From 6b7322d846c85a05166e91a5993167cd43724c86 Mon Sep 17 00:00:00 2001 From: Ales Hvezda Date: Sun, 8 Aug 2004 02:46:43 +0000 Subject: [PATCH] Initial import of gattrib 20040806 --- gattrib/AUTHORS | 17 + gattrib/COPYING | 340 ++ gattrib/ChangeLog | 67 + gattrib/INSTALL | 229 + gattrib/Makefile.am | 19 + gattrib/NEWS | 1 + gattrib/NOTES | 389 ++ gattrib/README | 129 + gattrib/README.gEDA | 135 + gattrib/ToDos | 64 + gattrib/autogen.sh | 128 + gattrib/configure.ac | 289 ++ gattrib/include/Makefile.am | 10 + gattrib/include/gettext.h | 71 + gattrib/include/globals.h | 165 + gattrib/include/gtkextra-marshal.h | 199 + gattrib/include/gtkitementry_1_2.h | 75 + gattrib/include/gtkitementry_2_2.h | 76 + gattrib/include/gtksheet_1_2.h | 808 ++++ gattrib/include/gtksheet_2_2.h | 871 ++++ gattrib/include/i_vars.h | 30 + gattrib/include/pixmaps.h | 243 + gattrib/include/prototype.h | 229 + gattrib/include/struct.h | 159 + gattrib/include/x_menu.h | 88 + gattrib/include/x_states.h | 36 + gattrib/lib/Makefile.am | 10 + gattrib/lib/system-gattribrc.in | 26 + gattrib/src/Makefile.am | 80 + gattrib/src/g_rc.c | 89 + gattrib/src/g_register.c | 71 + gattrib/src/gattrib.c | 374 ++ gattrib/src/globals.c | 72 + gattrib/src/gtkextra-marshal.c | 849 ++++ gattrib/src/gtkitementry_1_2.c | 1982 ++++++++ gattrib/src/gtkitementry_2_2.c | 2448 ++++++++++ gattrib/src/gtksheet_1_2.c | 8465 ++++++++++++++++++++++++++++++++++ gattrib/src/gtksheet_2_2.c | 8744 ++++++++++++++++++++++++++++++++++++ gattrib/src/i_basic.c | 123 + gattrib/src/i_vars.c | 88 + gattrib/src/listsort.c | 173 + gattrib/src/parsecmd.c | 91 + gattrib/src/s_misc.c | 71 + gattrib/src/s_object.c | 329 ++ gattrib/src/s_rename.c | 270 ++ gattrib/src/s_sheet_data.c | 456 ++ gattrib/src/s_string_list.c | 336 ++ gattrib/src/s_table.c | 557 +++ gattrib/src/s_toplevel.c | 717 +++ gattrib/src/x_dialog.c | 217 + gattrib/src/x_fileselect.c | 1433 ++++++ gattrib/src/x_gtksheet.c | 854 ++++ gattrib/src/x_window.c | 355 ++ 53 files changed, 34147 insertions(+) create mode 100644 gattrib/AUTHORS create mode 100644 gattrib/COPYING create mode 100644 gattrib/ChangeLog create mode 100644 gattrib/INSTALL create mode 100644 gattrib/Makefile.am create mode 100644 gattrib/NEWS create mode 100644 gattrib/NOTES create mode 100644 gattrib/README create mode 100644 gattrib/README.gEDA create mode 100644 gattrib/ToDos create mode 100755 gattrib/autogen.sh create mode 100644 gattrib/configure.ac create mode 100644 gattrib/include/Makefile.am create mode 100644 gattrib/include/gettext.h create mode 100644 gattrib/include/globals.h create mode 100644 gattrib/include/gtkextra-marshal.h create mode 100644 gattrib/include/gtkitementry_1_2.h create mode 100644 gattrib/include/gtkitementry_2_2.h create mode 100644 gattrib/include/gtksheet_1_2.h create mode 100644 gattrib/include/gtksheet_2_2.h create mode 100644 gattrib/include/i_vars.h create mode 100644 gattrib/include/pixmaps.h create mode 100644 gattrib/include/prototype.h create mode 100644 gattrib/include/struct.h create mode 100644 gattrib/include/x_menu.h create mode 100644 gattrib/include/x_states.h create mode 100644 gattrib/lib/Makefile.am create mode 100644 gattrib/lib/system-gattribrc.in create mode 100644 gattrib/src/Makefile.am create mode 100644 gattrib/src/g_rc.c create mode 100644 gattrib/src/g_register.c create mode 100644 gattrib/src/gattrib.c create mode 100644 gattrib/src/globals.c create mode 100644 gattrib/src/gtkextra-marshal.c create mode 100644 gattrib/src/gtkitementry_1_2.c create mode 100644 gattrib/src/gtkitementry_2_2.c create mode 100644 gattrib/src/gtksheet_1_2.c create mode 100644 gattrib/src/gtksheet_2_2.c create mode 100644 gattrib/src/i_basic.c create mode 100644 gattrib/src/i_vars.c create mode 100644 gattrib/src/listsort.c create mode 100644 gattrib/src/parsecmd.c create mode 100644 gattrib/src/s_misc.c create mode 100644 gattrib/src/s_object.c create mode 100644 gattrib/src/s_rename.c create mode 100644 gattrib/src/s_sheet_data.c create mode 100644 gattrib/src/s_string_list.c create mode 100644 gattrib/src/s_table.c create mode 100644 gattrib/src/s_toplevel.c create mode 100644 gattrib/src/x_dialog.c create mode 100644 gattrib/src/x_fileselect.c create mode 100644 gattrib/src/x_gtksheet.c create mode 100644 gattrib/src/x_window.c diff --git a/gattrib/AUTHORS b/gattrib/AUTHORS new file mode 100644 index 000000000..ec4d3d3ab --- /dev/null +++ b/gattrib/AUTHORS @@ -0,0 +1,17 @@ +gEDA-gattrib: A spreadsheet based attribute editor for gEDA. + +GPL Electronic Design Automation +------------------------------------------------------------------------------ + +gattrib AUTHORS + +Primary author: Stuart Brorson mailto:sdb@cloud9.net + +Large sections of code "borrowed" from geda-gnetlist, as well as gtksheet. +Credit to the authors of those programs is gratefully extended. + +geda-getnetlist: http://www.geda.seul.org/ + +gtksheet: http://gtkextra.sourceforge.net/ + + diff --git a/gattrib/COPYING b/gattrib/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/gattrib/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/gattrib/ChangeLog b/gattrib/ChangeLog new file mode 100644 index 000000000..74fe65843 --- /dev/null +++ b/gattrib/ChangeLog @@ -0,0 +1,67 @@ +2004-07-27 Stuart Brorson + + * Ported code to GTK+-2.X. Re-wrote Makefile.am & configure.ac. Split + gtksheet.c into two versions, one for GTK-1.2 and one for GTK-2.X. + (Both copied from gtkextra.sf.net.) + + * Many fixes related to moving to GTK-2.X + + * Incorporated Ales' fix for "for" loop which had i=i++ construct. + +2004-07-13 Stuart Brorson + + * Eliminated spurious GTK warnings by testing for NULL in arguments + before calling functions in gtksheet.c. + + * Removed several extraneous files originally bundled with GtkSheet. + +2004-06-25 Ales Hvezda (Noted by SDB) + + * Modified gattrib to work with soon-to-be-released libgeda 20040710. + +2004-03-13 Stuart Brorson + + * Implemented alphabetic sort of master_comp_list. + + * Placed references to g_rc_reset_component_library and other + Guile fcns into g_register to enable Sri's RC customized files. + +2004-03-11 Stuart Brorson + + * Fixed bug in which a loop was terminating early, causing + large portions of some designs to be not processed. + + * Added code to catch a cond where a design with no components + (i.e. no refdeses) or no modifiable attributes (i.e. no attribs + other than refdes) would cause a segfault. Now program just + exits with an error message. + +2004-03-06 Stuart Brorson + + * Fixed bug in which graphical objects were not discarded + during the "add master list" loop. This generated a + spurious warning message. + + * Fixed configure.ac to fix the GTK-1.2 vs. GTK-2.X problem + originally identified by Dan, and seen again by Sribalan + Santhanam in rel 20040304. Hopefully I fixed the bug this + time! + +2004-03-04 Stuart Brorson + + * Fixed some compilation issues related to GTK-1.2 vs. GTK-2.X. + Dan McMahill discovered the problem, and also provided the + patches fixing it. Thanks, Dan! + + * "make install" now correctly installs system-gattribrc into + the place where the gEDA RC files live. I had forgotten to + include this into the last release, and you had to do it by + hand. + +2004-02-21 Stuart Brorson + + * Initial release. + +2003-12-05 Stuart Brorson + + * Started hacking. diff --git a/gattrib/INSTALL b/gattrib/INSTALL new file mode 100644 index 000000000..54caf7c19 --- /dev/null +++ b/gattrib/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/gattrib/Makefile.am b/gattrib/Makefile.am new file mode 100644 index 000000000..af29de70c --- /dev/null +++ b/gattrib/Makefile.am @@ -0,0 +1,19 @@ + +AM_CFLAGS = -g -O2 + +SUBDIRS = src include lib design + +EXTRA_DIST = BUGS NOTES AlesCorrespondance ProgramArchitecture.gnumeric \ + NotesAndToDos.abw ChangeLog AUTHORS COPYING INSTALL + +distclean-local: + -rm -rf autom4te*.cache + +maintainer-clean-local: + -rm -rf autom4te*.cache + +MOSTLYCLEANFILES = *.log core FILE *~ #*# +CLEANFILES = *.log core FILE *~ #*# +DISTCLEANFILES = *.log core FILE *~ prototype.bak #*# +MAINTAINERCLEANFILES = *.log core FILE *~ Makefile.in configure \ + config.status aclocal.m4 #*# diff --git a/gattrib/NEWS b/gattrib/NEWS new file mode 100644 index 000000000..7165ea6f2 --- /dev/null +++ b/gattrib/NEWS @@ -0,0 +1 @@ +See ChangeLog for most recent updates. diff --git a/gattrib/NOTES b/gattrib/NOTES new file mode 100644 index 000000000..e7f94de9e --- /dev/null +++ b/gattrib/NOTES @@ -0,0 +1,389 @@ +----------------------------------------------------------------------- +This file contains a few notes I made while creating +gattrib. These notes were useful to me in coming to grips with +understanding gEDA's data structures and gnetlist's operation. +The were also useful when architecting the workings of gattrib. + +A spreadsheet holding function names is held in the file +called "ProgramArchitecture.gnumeric", which should live in the +toplevel directory. This spreadsheet should help you wade +through the various fcns used during processing. + +No guarantee is made that these notes are accurate, useful, +or even understandable to anybody else. -- SDB 12.5.2003 + sdb@cloud9.net + +Updated on 7.17.2004. SDB. + + +----------------------------------------------------------------------- +gattrib algorithm: +1. Input sheets as in gnetlist. Use fcns from libgeda. Result is + linked list of TOPLEVEL entries. (*pr_current) This data structure + comes from libgeda. + + The file is opened and read using: + libgeda/noweb/f_basic.nw:f_open(TOPLEVEL *w_current, char *filename) + + f_open invokes o_read, which fills out the objects: + libgeda/noweb/a_basic.nw:o_read(TOPLEVEL *w_current, + OBJECT *object_list, char *filename) + + At the end of this process, we have filled out (TOPLEVEL pr_current). + +2. Create (SHEET_DATA *sheet_head), which is the data structure intermediating + between gEDA's TOPLEVEL and GtkSheet's gtk_sheet. It holds all info + important to drive the spreadsheet app in a form easy to access. + +3. Build master lists which are part of SHEET_DATA. The master lists + are used when building the row and column label cells, and for + keeping track of which components and attribtues are in play. + + Master lists are built for each of these objects: + * Component refdeses found in the design. + sheet_head->master_comp_list_head + * Component attributes found in the design. + sheet_head->master_comp_attrib_list_head + * Netnames found in the design. + sheet_head->master_net_list_head (TBD) + * Net attributes found in the design. + sheet_head->master_net_attrib_list_head (TBD) + * Component pins found in the design (TBD) + + * Component pins attributes found in the design (TBD) + + Each master list is stored using a STRING_DATA struct. + Attach pointers to these master lists inside SHEET_DATA. + This stage of processing also counts up the number of each + item. These lists are used to index the table entries, and form the + row and column labels on the gtk_sheet. + +4. Create TABLE, which is the data structure holding the info to be displayed + in the gtk_sheet cells. TABLE is basically a 2D array of structs. + + Tables are built for the following types of objects: + * Component refdeses & attached attributes. + sheet_head->component_table + * Netnames and attached attributes. (TBD) + sheet_head->net_table + * Component pins and attached attributes. (TBD) + + After creation, attach TABLE inside SHEET_DATA. The fcns which add components + and nets to the internal data structs are called s_*_set_*" Items in the table + are referenced using a construct like: + sheet_head->component_table)[i][j].attrib_value + +5. Then build gtk_sheet using fcns available in x_gtksheet. They + take the data from SHEET_DATA and stick them into the gtk_sheet + widget for display on the spreadsheet. + +6. Run gtk_main. Wait for user clicks & invoke appropriate callbacks. + Callback fcns are specified in include/menu.h + +7. The save callback is x_fileselect_*. Upon a save/saveas event invoked + from the menu, take all data out of gtk_sheet and stick it into SHEET_DATA + using various fcns named "s_*_update_*". Then take data out of SHEET_DATA + and stick it into (TOPLEVEL *pr_current). Then use standard gEDA + fcns to save out the data in .sch format. + + + +----------------------------------------------------------------------- +gattrib data structures: + +TOPLEVEL: + Toplevel holds page info, and is a linked list with one entry per page/window. + Defined in libgeda/include/struct.h, fcns to manipulate TOPLEVEL data live in + libgeda/src/s_project.c + + +SHEET_DATA: + Holds data taken from TOPLEVEL in form relevant for display in spreadsheet. + Definition: + +struct st_sheet_data { + STRING_LIST *master_comp_list_head; /* Sorted list of all components used in design */ + int comp_count; /* number of components in design */ + STRING_LIST *master_comp_attrib_list_head; /* Sorted list of all component attribs used in design */ + int comp_attrib_count; /* number of component attribs in design */ + + STRING_LIST *master_net_list_head; /* Sorted list of all nets used in design */ + int net_count; /* number of nets in design */ + STRING_LIST *master_net_attrib_list_head; /* Sorted list of all net attribss used in design */ + int net_attrib_count; /* number of net attribss in design */ + + STRING_LIST *master_pin_list_head; /* Sorted list of all pins used in design */ + int pin_count; /* number of pins in design */ + STRING_LIST *master_pin_attrib_list_head; /* Sorted list of all pin attribss used in design */ + int pin_attrib_count; /* number of pin attribss in design */ + + TABLE **component_table; /* points to 2d array of component attribs */ + TABLE **net_table; /* points to 2d array of net attribs */ + TABLE **pin_table; /* points to 2d array of pin attribs */ +}; + + +TABLE: + Is 2D array of structs. Each struct member holds info about + one particular attrib. + +STRING_LIST: + A linked list of strings. Used to create the component and net + master lists. + +GtkWidget **sheets: + These are the spreadsheet widgets themselves. There are thre of them: + 1. Comp attribs + 2. Net attribs (currently TBD) + 3. Pin attribs (i.e. pinseq and so on). + + +--------------------------------------------------------------------------------- +******** How data gets from TOPLEVEL into GtkSheet ******** +--------------------------------------------------------------------------------- +Here's what happens when the program starts, or file->save is invoked from the menu +and the data is read in. (I handle data structure initialization elsewhere.) Keep +in mind that gattrib is a thin layer between GtkSheet and TOPLEVEL. When you read +in a design, libgeda functions are used to fill out the TOPLEVEL data structure. The +the purpose of gattrib is to take all teh stuff out of TOPLEVEL and stick it into +GtkSheet for user manipulation. + +1. Loop on each page. For each page in the design, read it in using + s_toplevel_read_page. Then invoke s_sheet_data_add_master_*_list items. + +2. Create the tables using s_table_new. + +3. Load the tables using s_sheet_data_add_*_table_items. + + + + + + +--------------------------------------------------------------------------------- +******** How data gets from GtkSheet into TOPLEVEL upon saving a sheet ******** +--------------------------------------------------------------------------------- +Here's what happens when file->save is invoked from the menu. Please keep in +mind that gattrib is a thin layer between GtkSheet and TOPLEVEL. When you save +out a design, the purpose of gattrib is to take all the stuff out of GtkSheet +and stick it into TOPLEVEL and then save it. + +1. Selecting "save" from the menubar invokes the callback s_toplevel_menubar_file_save. + (callbacks are defined in includes/x_menu.h) + This calls s_toplevel_gtksheet_to_toplevel. + +2. s_toplevel_gtksheet_to_toplevel calls s_sheet_data_gtksheet_to_sheetdata. + Then it loops on all pages and calls s_toplevel_sheetdata_to_toplevel. (Described + starting in item 6 below.) + +3. s_sheet_data_gtksheet_to_sheetdata calls s_table_gtksheet_to_all_tables. + +4. s_table_gtksheet_to_all_tables calls s_table_gtksheet_to_table individually + for each sheet (comps, nets, pins) + +5. s_table_gtksheet_to_table loops on the gtksheet and loads all the stuff it + finds back into TABLE (part of sheet_data). + +6. Back in s_toplevel_gtksheet_to_toplevel. Now that the tables are loaded, + we loop on all pages and call s_toplevel_sheetdata_to_toplevel with a pointer + to the first obj on the page as the calling arg. + +7. Inside s_toplevel_sheetdata_to_toplevel, we loop on all objs in teh page, + find the corresponding entry in TABLE, and then copy all attribs out of + TABLE and back into the object's attrib list. + +8. Finally, the libgeda fcn s_page_save_all is called. This saves out all + pages in the design. + + +----------------------------------------------------------------------- +Data structures used in gEDA. + +These notes attempt to document the data structures used in gEDA. I +present them here because they may not be familiar to those interested +in hacking gattrib. I also want to record these findings somewhere +because it took me some time to figure this out. + +TOPLEVEL: The basic data structure in gschem. Basically holds info + about the entire design. +PAGE: Self explanatory. The entire design is held as a + doubly linked list of PAGEs under TOPLEVEL. +OBJECT: A graphical object on the gschem page. Can be a net + segment, a component complex, a bit of text, a + component's pin, or whatever. Each page holds a + doubly linked list of OBJECTs present on that page. +TILE: A section of the graphical page. Important in + gschem, a TILE holds a single linked list of OBJECTs + present in its subsection of the page. Each page + holds a two dimensional doubly linked list of TILEs. + A TILE is used to quickly locate a particular object + when handling the graphical drawing of a schematic. +NETLIST: Important in gnetlist. This is a doubly linked list + of component objects and pins. + + +----------------------------------------------------------------------------- +Represenatation of components, component attributes, & pins in gattrib & gEDA. + + +From the top down to a particular graphical object on a schematic page: + +(TOPLEVEL *pr_current)->(PAGE page_current)->(OBJECT o_current) + +That is, all objects in the design are graphical objects, and are held +in a linked list. Note that an object is any graphical object, +such as text, a component (COMPLEX), an attribute, a line, a net, etc. +Stuff attached below the OBJECT level determine +what kind of object the graphic is, its visibility, its name, etc. etc. etc. + + +---- Components with attributes ---- +Starting from a component (COMPLEX), an attribute is represented like this: +(OBJECT *component)->(ATTRIB *attribs)->(OBJECT *attrib_graphic)->(TEXT *text)->(char *string) // name=value string + ->(int x, y) // which coords? + ->(int screen_x) // ??? + etc . . . + ->(int type) // types defined in o_types.h + ->(int sid) // unique identifier + ->(char *name) // something like text.3732 + +Note that ATTRIB is a linked list of (pointers to) attributes. +(OBJECT *component) points to the ATTRIB list, and "attrib_graphic" points +to the attrib OBJECT itself (which is a graphical element like everything else). + + +---- Attributes ---- +Since each attribute is itself a graphical object present in the design, +an attribute graphic is represented like this: +(OBJECT *attribute)->(TEXT *text)->(char *string) // name=value string + ->(int x, y) // which coords? + ->(int screen_x) // ??? + etc . . . + ->(int type) // for attribute, type is text + // #define OBJ_TEXT 'T' + ->(int sid) + ->(char *name) + ->(ATTRIB *attached_to) // points back to (OBJECT *component) + ->(int attribute) // = 1 for attribute, = 0 otherwise. + ->(int show_name_value) // #define SHOW_NAME_VALUE 0 + // #define SHOW_VALUE 1 + // #define SHOW_NAME 2 + ->(int visibility) // #define INVISIBLE 0 + // #define VISIBLE 1 + +Attrib_graphic is stored internally as a "name=value" string, and is disassembled +by various fcns when needed. + + +---- Pins ---- +As for pins, they are attached to components (COMPLEX) in the following way. +Starting from the component: +(OBJECT *component)->(COMPLEX *complex)->(OBJECT *prim_objs)->(int type) // #define OBJ_PIN 'P' + ->(ATTRIB *attribs) // pin attrib list. + ->(int sid) // unique identifier + ->(char *name) // something like pin.3372 + + +Pins also are held in the NETLIST data structure, which is a separate data structure. +NETLIST is more important in gnetlist than in gschem. A pin is held in the following +way: +(NETLIST *node)->(OBJECT *object_ptr) + ->(CPINLIST *cpins)->(char *pin_number) + ->(char *net_name) + ->(char *pin_label) + + + +---- Example ---- +Component objects (COMPLEX) have lots of other other fields in teh struct. +Here's an example taken from a real design: + +(OBJECT *component)->name = complex.188 // unique ID of component + + ->attribs->object->name = complex.188 // first "attribs" points back to component + ->attribs->next->object->name = text.272 // attrib text + ->attribs->next->object->text->string = refdes=C1 // actual attrib + + ->complex->prim_objs->name = complex-head.189 // head of prim_objs list + ->complex->prim_objs->next->name = pin.190 // actual object + +Each list attached to a component has a head, followed by the actual list +items. + + +----------------------------------------------------------------------- +From include/struct.h: + +/* ----------- SDB note about philosophy behind globals -------------- * + * I made the "TOPLEVEL project" and all the GTK window stuff into + * global variables. I know that this is supposedly bad programming form. + * However, here are some observations: + * -- I wanted to use gEDA's TOPLEVEL structure as much as possible, at + * least to hold info about the design's netlist & components. + * The TOPLEVEL strucuture is architected to hold info about gschem's + * window also. HOwever, gschem's windows are architected differently + * than mine in gattrib. This is because my windowing system does + * completely different things, and also uses the GtkSheet widget, which + * is architected completely differently from TOPLEVEL. + * -- Since I couldn't easily or naturally cram my windowing scheme into + * TOPLEVEL (or so I think), I decided to use a separate set of windows + * from those defined under TOPLEVEL for my application. + * -- The problem arises when using callbacks. Callbacks from GTK allow + * only one argument to be passed. Given the way I set up the menu bar, + * I didn't have easy acces to the information inside both the GtkSHeet + * objects *and* the TOPLEVEL stuff while only having one callback + * argument. This makes it hard to have access to e.g. a GtkSheet window + * and a list of files (in TOPLEVEL) simultaneously. + * -- Therefore, I decided to make both the window stuff and TOPLEVEL + * globals. + * -- Similarly, because I couldn't cram the SHEET_DATA struct into any + * hook in TOPLEVEL, I just made it a global also. + * -- Finally, in my defense, in gschem and gnetlist, (TOPLEVEL *w_current + * or pr_current) is passed to almost every function. Since it + * is just a pointer to a huge struct of stuff, manipulating + * the stuff in the struct has a global + * effect. That is, manipulating w_current (or pr_current) has side + * effects, so it is basically a global anyway. The real problem with + * globals occurs when you have a global variable caled "i" or "temp" + * which conflicts with a global in a module written by somebody else. + * Since pr_current is a very uncommon name, this should not be a + * problem here. Therefore, I decided + * to make life easy for myself dealing with callbacks by making both + * the windows and TOPLEVEL global variables. + * If there is a better way to solve this problem, I'd like to hear it. + * ------------------------------------------------------------------ */ + + +----------------------------------------------------------------------- +To be done: +1. Before building spreadsheet, create and sort component and net + master lists. They are used to index components in the TABLE, + and also label the gtk_sheet rows and cols. +2. How to handle attribute visibility? Maybe right-click on cell & set + vis. Default = invisible and "both name and value". +3. Need fcns to allow for adding and deleting of entire attrib + columns. Don't need to allow for adding/deleting components + -- users should use gschem to add/delete components because + gattrib can't do anything about the graphical position data + required for this. + + + +----------------------------------------------------------------------- +Here's what the filename prefix letters mean: + + a_ : Action (this was a really poor choice, and will be removed) + g_ : Guile related functions + i_ : Interface related functions + o_ : graphic Object related functions + x_ : X related functions + f_ : File related functions + m_ : Math related functions + s_ : data Structure related functions + + I've tried to keep the function names in the files correct, but + sometimes I failed. (look at libgeda/a_basic.c for an example of + a file which has a bunch of functions which shouldn't be in there) + + -Ales +----------------------------------------------------------------------- \ No newline at end of file diff --git a/gattrib/README b/gattrib/README new file mode 100644 index 000000000..01f1126fe --- /dev/null +++ b/gattrib/README @@ -0,0 +1,129 @@ +****************************************************************** +************* DANGER!! Read the entirety of this ************** +************ README before you try runing gattrib! ************* +****************************************************************** + + +INTRODUCTION + +Gattrib is gEDA's attribute editor. It reads a set of gschem .sch +files (schematic files), and creates a spreadsheet showing all +components in rows, with the associated component attributes listed in +the columns. It allows the user to add, modify, or delete component +attributes outside of gschem, and then save the .sch files back +out. When it is completed, it will allow the user to edit attributes +attached to components, nets, and pins. (Currently, only component +attribute editing is implemented; pin attributes are displayed only, +and net attributes are TBD.) + +Gattrib is useful in situations where you need to view, add, modify, +or delete a number of attributes all at once. It saves you the pain +of clicking on each component and repeatedly using gschem's attribute +editor to modify component attributes. For example, if you create a +large design with generic components, and then later want to attach +"footprint" attributes to your components, it is a real hassle to do +so using gschem. In this situation, you can run gattrib on your +design files to attach the "footprint" attributes all at once using an +easy-to-use spreadsheet. + +WARNING NOTE: Gattrib is currently PRE-ALPHA SOFTWARE! +It has been tested on several designs, but is not garuanteed to work. +It may even trash your design! Therefore, if you wish to try gattrib +out, please create a backup copy of your design before you run it! + +------------------------------------------------------------------ +USAGE + +You can invoke gattrib on your design from the command line in the +following way: + +gattrib my_design_*.sch + +(This assumes you have a multi-page design with file names +"my_design_1.sch", "my_design_2.sch", etc.) Gattrib will then read in +your design, and present you with a spreadsheet showing all components +and associated attributes. + +You can also just run gattrib, and specify the input files using the +file->open dialog box. + +To edit your attributes, just edit the cells in the spreadsheet. + +To save your design, just select "save" from the command menu. Note +that no checks are currently done when you select save. Be careful! + +To quit, just select "quit" from the command menu. Note that +currently no checks thet you have saved your design are done when you +quit. + +If you want to add an entirely new attribute to your design (i.e. one +which doesn't exist on any component), you must first attach at least +one instance of that attribute to a component using gschem. Then you +can use gattrib to attach the attribute to the remaining components. +(The reason for this is that gattrib creates its spreadsheet columns +based upon all attributes it finds when reading in the design. +Therefore, to create a column for a new attribute, you need to make +sure that that new attribute pre-exists in the design.) In the future, +you will be able to add new attribute columns directly from gattrib, +but this feature is currently unimplemented. + +------------------------------------------------------------------ +FEATURES + +The following features are currently implemented: + +* .sch file read in from command line. +* .sch file read in from menu. +* .sch file save out from menu. +* Component attribute editing (of course). +* Pin attribute viewing. +* Quit from menu. + +The following features are currently unimplemented, but will be +incorporated at some future date: + +* Throw up "Are you sure" dialog boxes upon selecting "save" and + "quit" from menu. +* Alphabetic sort of rows. (Should happen automatically upon read-in + of design.) +* Editing of net attributes (important for setting routing + attributes). +* Adding/deleting attribute columns (to add/delete entire sets of + attributes from a design.) +* Search/replace of individual attributes. +* Search for component refdeses & netnames. +* Set/view component & net visibility (through options pull-down + menu). Currently, visibility is "invisible", and both name & value + are displayed (if you turn on the visibility). + +Note that if you select unimplemented features from the menu, nothing +will happen. + +------------------------------------------------------------------ +INSTALLATION + +To install gattrib, place the tarball in the directory where your gEDA +sources live. Then do: + +tar -zxvf geda-gattrib-20040806.tar.gz +cd geda-gattrib-20040806 +./configure --prefix=/path/to/your/geda/stuff +make +make install + +Note that you may need to set some environment variables first. If +your compilation barfs, try setting these (for csh): + +setenv LD_LIBRARY_PATH /usr/local/geda/lib:$LD_LIBRARY_PATH +setenv PATH /usr/local/geda/bin:$PATH +setenv PKG_CONFIG_PATH /usr/local/geda/lib/pkgconfig:$PKG_CONFIG_PATH + +------------------------------------------------------------------ +CREDITS/CONTACT: + +Gattrib was cobbled together by Stuart Brorson starting in December +2003 from parts culled from GtkSheet (GTK+Extra) and gEDA. Please +mail bug reports to: sdb@cloud9.net + + + diff --git a/gattrib/README.gEDA b/gattrib/README.gEDA new file mode 100644 index 000000000..5f54a490f --- /dev/null +++ b/gattrib/README.gEDA @@ -0,0 +1,135 @@ + +gEDA + +GPL Electronic Design Automation +------------------------------------------------------------------------------ + + +README for gnetlist version 2003* +(This software is ALPHA) + + +IMPORTANT + Please read the file NEWS for details on specifics for this release + Please read the file BUGS for more info on existing bugs + Please read the file TODO for things which are in the works + + + +- Installation + + See the file INSTALL for more instructions on how to build gnetlist + + + +- System Requirements + + A computer: + - at least the processing power of a i486/66 + - Enough RAM for X (usually 16 Mbytes but 32 Mbytes recommended) + + Running UNIX: + - Semi recent version of UNIX + - Compiled on: + FreeBSD (2.2.5) + Linux (2.0, 2.2) (and highly tested) + Solaris (2.5) + SunOS (4.1.3) + HP-UX (9.07) + + The X window system + - A resolution of at least 800x600 + - A color display + - 3 button mouse (2 button works, but 3 button is better) + + A C compiler (that understands ANSI, like gcc) + + GLIB version 1.2.10 or higher (and anything glib requires) + Glib can be found at ftp://ftp.gtk.org/pub/gtk + + GTK+ version 1.2.10 or higher (and anything gtk requires) + Gtk can be found at: ftp://ftp.gtk.org/pub/gtk + + libgeda (same version as gnetlist) + This can be found on the gEDA website/ftpsite + + symbols (same version as gnetlist) + This can be found on the gEDA website/ftpsite + + GUILE - guile-1.4 or 1.6 is required (and anything guile requires). + Guile can be found at: ftp://ftp.gnu.org/pub/gnu/guile + + LibStroke 0.5.1 [OPTIONAL] - if you want stroke support. The homepage + for LibStroke is: http://www.etla.net/libstroke/ + + libgdgeda 2.0.9 [OPTIONAL] - if you want the ability to write png + images. libgdgeda is based on gd 2.0.9 The homepage for gd 2.0.x is + http://www.boutell.com/gd You can find libgdgeda on the gEDA + website: http://www.geda.seul.org/dist + + If you use libgdgeda then you also need zlib. zlib can be found at: + http://www.cdrom.com/pub/infozip/zlib zlib can also be found on + the gEDA website: http://www.geda.seul.org/dist + + If you use libgdgeda then you also need libpng. libpng can be found + at: http://www.libpng.org/pub/png/pngcode.html libpng can also be + found on the gEDA website: http://www.geda.seul.org/dist + + An understanding that this software is ALPHA release quality :) + + + +- License + + gEDA (libgeda/gschem/gnetlist/geda/gsymcheck/gschcheck/gpcb/utils + and all associated files (such as component symbols)) is placed + under the GNU Public License (GPL) version 2.0. See the toplevel + COPYING file for more information. + + Programs and associated files are: + Copyright 1998-2003 by Ales V. Hvezda and the respective original + authors (which are listed on the respective files) + + + +- gEDA Internet resources + + Homepage: http://www.geda.seul.org + + Mailing list: + To subscribe send an email to: majordomo@geda.seul.org + with subscribe geda-user in the body. + + To unsubscribe send an email to: majordomo@geda.seul.org + with unsubscribe geda-user in the body. + + To send email to the list use the address: + + geda-user@geda.seul.org + + Remember, geda-user@geda.seul.org is for the actual traffic. + For administrative commands, use majordomo@geda.seul.org. + + The mailing list archive is at: + + http://www.geda.seul.org/mailinglist + + Many thanks to the SEUL project for hosting gEDA! + + + +- Notes + + * For anybody developing guile scripts using guile 1.6.x really should + set GUILE_WARN_DEPRECATED=detailed to see if they are using any + deprecated guile functions in their scripts. gnetlist disables the + guile deprecated function warnings by default. + + + +- Contact information + + Ales V. Hvezda + E-mail: ahvezda@geda.seul.org + Web: http://www.geda.seul.org/ + diff --git a/gattrib/ToDos b/gattrib/ToDos new file mode 100644 index 000000000..82989ef50 --- /dev/null +++ b/gattrib/ToDos @@ -0,0 +1,64 @@ +Gattrib todos. + +Updated 8.06.2004 -- SDB + +-------------------------------------------------------------------- +This is a running list of things left to be done with gattrib. Items +already completed are marked with #####. + +* Fix key_press event handling so that arrow keys work intelligently + when using gtk-2.2. +* Decide what to do about "pins" editing: How to save out chages? + Right now "pins" are only displayed, they cannot be changed. +* ##### Add "component pins" page to allow users to edit component pin attributes. + This is useful to make sure that the pin attributess are correctly + set for use with PCB & other layout packages. +* ##### Create separate comp_attrib and net_attrib variables. +* ##### Handle saving out of multi-page design so that it correctly saves + individual pages back to their destination (i.e. page1.sch -> + page1.sch, page2.sch -> page2.sch.) Currently smashes all pages + together. +* Find out why it leaves "xterm" on the command prompt when you + invoke the prog with no files on the cmd line. +* Change save menus to: save project, save as, which cycles through + pages and saves objects out on a page by page basis. +* Incorporate a "close" item on the menu to close out the current + project without quitting the whole application. +* flesh out net handling functions. Need to more fully understand + how nets work. Note that they must be read in differently from + components -- you need to read the whole project in before handling + nets because each net segment is separate, but the net itself can span + many components and pages. +* Add info about NETS and net attribs into NOTES. +* ##### Go over OBJECT struct data in NOTES -- is (ATTRIB + *attached_to) info correct? +* ##### Implement sort of master lists. +* Install text_entry widget below menu & above spreadsheet (like in Excel). + Use this widget for text entry into cell, like in Excel. +* ##### Fix problem where pages read at different times are not merged. +* ##### re-write NOTES and documentation for project. +* ##### Include gnumeric spreadsheet into DIST_EXTRA +* make sure comments in each fcn are true +* ##### Make sure all DEBUG printfs are accurate & give the correct fcn name. +* Re-introduce I18N stuff where I took it out. +* ##### clean out i_basic.c -- there are too many "if 0" regions in there. +* ##### Change attrib update algorithm to write out both old, updated + attribs as well as new ones for each component. +* ##### Fix segfault when run on OpticalReceiver without valgrind or + GDB. (Fix: free in wrong place was moved to correct place in + gattrib.c) +* Eventually re-introduce hierarchy. Important for nets. +* Add in fcns dealing with nets spanning pages. +* ##### Incorporate Dan's changes, including Makefile changes, warning + stuff, and make sure prog doesn't segfault if no attributes are + found. +* ##### gattribrc should be installed in RC dir as part of "make install". +* ##### Enable horizontal resizing of window. Currently can't shrink window + to less than initial width. +* Fix cells so that a long attrib string doesn't bleed into/overwrite + adjacent cells. Maybe automatically size cell to fit text? Problem is that + "file" attributes can be very long. +* Per Ales, use the gtksheet stuff as a library instead of a bunch of source + files. Is this necessary? +* User should be able to set attrib visibility. How to implement & show? +* Incorporate ability to add attrib columns. diff --git a/gattrib/autogen.sh b/gattrib/autogen.sh new file mode 100755 index 000000000..938b00937 --- /dev/null +++ b/gattrib/autogen.sh @@ -0,0 +1,128 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +# This file came from glade-2.0.0 with modifications for gEDA/gaf. +# Ales Hvezda 11/09/2003 + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. +configure_script=configure.ac + +DIE=0 + +(test -f $srcdir/$configure_script) || { + 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 "^AM_PROG_LIBTOOL" $srcdir/$configure_script >/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_GNU_GETTEXT" $srcdir/$configure_script >/dev/null) && { + (grep "sed.*POTFILES" $srcdir/$configure_script) > /dev/null || \ + (autopoint --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`gettext' installed." + echo "You can get it from: http://www.gnu.org/software/gettext" + 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 + +# Don't run configure automatically. +#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_script -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_GNU_GETTEXT" $configure_script >/dev/null; then + echo "autogen.sh running: autopoint ..." + echo "no" | autopoint --force + #echo "Creating $dr/po/Makevars ..." + #mv -f $dr/po/Makevars.template $dr/po/Makevars + fi + if grep "^AM_PROG_LIBTOOL" $configure_script >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "autogen.sh running: libtoolize ..." + libtoolize --force --copy + fi + fi + echo "autogen.sh running: aclocal $aclocalinclude ..." + aclocal $aclocalinclude + if grep "^AM_CONFIG_HEADER" $configure_script >/dev/null; then + echo "autogen.sh running: autoheader ..." + autoheader + fi + echo "autogen.sh running: automake $am_opt ..." + automake --copy --add-missing --gnu $am_opt + echo "autogen.sh running: autoconf ..." + autoconf + ) + fi +done + + +# Don't run configure. +#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/gattrib/configure.ac b/gattrib/configure.ac new file mode 100644 index 000000000..8931db145 --- /dev/null +++ b/gattrib/configure.ac @@ -0,0 +1,289 @@ +dr Process this file with autoconf to produce a configure script. +AC_INIT +AC_CONFIG_SRCDIR([src/gattrib.c]) +AC_PREREQ(2.53) + +PACKAGE=geda-gattrib +GEDA_VERSION=20040710 +GATTRIB_VERSION=20040806 +echo Configuring $PACKAGE version $GATTRIB_VERSION +echo Part of gEDA version $GEDA_VERSION + +# Initialize automake -- use base gEDA version here +AM_INIT_AUTOMAKE($PACKAGE, $GEDA_VERSION) +AM_CONFIG_HEADER([config.h]) + +# Initialize maintainer mode +AM_MAINTAINER_MODE + + +######################################################################### +# Command line flags start +# +# Change default location for rc files +AC_ARG_WITH(rcdir, [ --with-rcdir=path Change where the system-*rc files are installed], [opt_rcdir=$withval]) + +# Force a specific version of gtk+ +AC_ARG_WITH(gtk12, [ --with-gtk12 Force the use of gtk+ 1.2.x], [opt_gtkver=1.2]) + +# +# Command line flags end +######################################################################### + +# Checks for programs. +AC_PROG_CC +AC_PROG_MAKE_SET +AC_PATH_PROGS(AWK, nawk gawk mawk awk, ) + +######################################################################### +# +# Misc win32 / mingw checks and variables start +# Win32 stuff currently unsupported by SDB. I left it in because we need +# to set the PATHSEP var. +AC_CANONICAL_HOST + +# Figure out if we are building on win32 and what environment. +case $host_os in + *cygwin* ) AC_MSG_ERROR([Building gEDA/gaf under cygwin is not supported]) ;; + *mingw32* ) echo "Configuring for mingw"; MINGW=yes ;; +esac + +if ! test "$MINGW" = "no" -o "$MINGW"x = x; then + MINGW="yes" + PATHSEP=\\\\ + OTHERPATHSEP=/ +else + # Unix host + MINGW="no" + PATHSEP=/ + OTHERPATHSEP=\\\\ +fi + +# +# Misc win32 / mingw checks and variables end +######################################################################### + + +############################################################################ +# Check for misc things. . . . +# +# Checking for rint in math library +AC_CHECK_LIB(m, rint, AC_DEFINE(HAS_RINT, 1, [If your math library has rint in it, define this]), no_RINT="yes") + +# Checking for dynamic lib +AC_CHECK_LIB(dl, dlopen, DL_LIB="-ldl", DL_LIB="") +# +############################################################################ + + +############################################################################ +## This is supposed to first look for GTK2.2 and then for GTK1.2. Right now, +## gattrib doesn't work with GTK2.X, therefore, most of this is commented out. +## Reconfigure this when GTK-2.X actually works with gattrib. +## Copied from configure.ac in geda. +# +# Check for pkg-config +AC_PATH_PROG(PKG_CONFIG, pkg-config, no) +if test $PKG_CONFIG = no; then + AC_MSG_ERROR([Cannot find pkg-config, make sure it is installed and in your PATH]) +fi + +# Search for gtk+ 2.2.x first (only if we are not forcing the gtk+ to 1.2) +if test "$opt_gtkver" != "1.2" +then + PKG_CHECK_MODULES(GTK22, gtk+-2.0 >= 2.2.0, GTK22="yes", no_GTK22="yes") +fi + +# This next bit of code figures out what gtk we need to use. +if test "$GTK22" = "yes" -a "$opt_gtkver" != "1.2" +then + + AC_DEFINE(HAS_GTK22, 1, [If gtk+ 2.2.x has been installed, define this]) + GTK_CFLAGS=$GTK22_CFLAGS + GTK_LIBS=$GTK22_LIBS + GTK_VERSION=`$PKG_CONFIG gtk+-2.0 --modversion` + + # Search for glib + PKG_CHECK_MODULES(GLIB22, glib-2.0 >= 2.2.0, GLIB22="yes", no_GLIB22="yes") + if test "$GLIB22" != "yes" + then + AC_MSG_ERROR([Cannot find glib 2.2.x, install it and rerun ./configure.]) + fi + GLIB_CFLAGS=$GLIB22_CFLAGS + GLIB_LIBS=$GLIB22_LIBS + GLIB_VERSION=`$PKG_CONFIG glib-2.0 --modversion` + +## GTKITEMENTRY_SOURCE=gtkitementry_2_2.c gtkitementry_2_2.h + +else + + # Search for gtk+ 1.2 now + PKG_CHECK_MODULES(GTK12, gtk+ >= 1.2, GTK12="yes", no_GTK12="yes") + + AC_DEFINE(HAS_GTK12, 1, [If gtk+ 1.2.x has been installed, define this]) + GTK_CFLAGS=$GTK12_CFLAGS + GTK_LIBS=$GTK12_LIBS + GTK_VERSION=`$PKG_CONFIG gtk+ --modversion` + + # Search for glib + PKG_CHECK_MODULES(GLIB12, glib >= 1.2, GLIB12="yes", no_GLIB12="yes") + if test "$GLIB12" != "yes" + then + AC_MSG_ERROR([Cannot find glib >= 1.2, install it and rerun ./configure.]) + fi + GLIB_CFLAGS=$GLIB12_CFLAGS + GLIB_LIBS=$GLIB12_LIBS + GLIB_VERSION=`$PKG_CONFIG glib --modversion` + +## GTKITEMENTRY_SOURCE=gtkitementry_1_2.c gtkitementry_1_2.h + +fi + + +if test "$GTK_VERSION" = "" +then + AC_MSG_ERROR([Cannot find gtk+ 2.2.x or gtk+ 1.2.x, install one of them.]) +fi + +## These tell Makefile.am which package to compile +AM_CONDITIONAL(GTK12_SOURCE, test "$GLIB12" = "yes") +AM_CONDITIONAL(GTK22_SOURCE, test "$GLIB22" = "yes" -a "$opt_gtkver" != "1.2") + +# +# Check for gtk+ 1.2 and 2.2 end +############################################################################ + + +############################################################################ +# Check for libgeda start +# +PKG_CHECK_MODULES(LIBGEDA, libgeda >= $GEDA_VERSION, LIBGEDA="yes", + no_LIBGEDA="yes") + +if test "$LIBGEDA" = "yes" +then + LIBGEDA_VERSION=`$PKG_CONFIG libgeda --modversion` +else + AC_MSG_ERROR([Cannot find libgeda (or libgeda.pc), please install libgeda]) +fi + +# +# Check for libgeda end +############################################################################ + +######################################################################### +# Checks for header files start +# +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_HEADER_DIRENT +AC_CHECK_HEADERS(unistd.h string.h stdlib.h \ + stdarg.h assert.h fcntl.h errno.h \ + dirent.h sys/param.h) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST + +# Checks for library functions. +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(getcwd strstr vsnprintf snprintf) + + +AC_MSG_CHECKING([for optarg in unistd.h]) +AC_TRY_COMPILE( +[#include ], +[ char *string = optarg; int i = optind; ], +optarg_found=yes, +optarg_found=no) +AC_MSG_RESULT($optarg_found) + +if test $optarg_found = yes; then + AC_DEFINE(OPTARG_IN_UNISTD, 1, [Define if you have optarg in unistd.h]) +fi + +# +# Checks for header files start +######################################################################### + +######################################################################### +# gEDA/gaf specify setup start +# + +DATADIR=gEDA +GEDADATADIR=$datadir/$DATADIR + +if eval "test x$opt_rcdir = x"; then + # path not was specified with --with-rcdir + AC_DEFINE_UNQUOTED(GEDARCDIR, "none", [gEDA/gaf's rc directory]) + GEDARCDIR=$GEDADATADIR +else + # path WAS specified with --with-rcdir + AC_DEFINE_UNQUOTED(GEDARCDIR, "$opt_rcdir", [gEDA/gaf's rc directory]) + GEDARCDIR="$opt_rcdir" +fi + +# Expand the prefix variable +# I don't like the way this is done, but it works (I hope). +if eval "test x$prefix = xNONE"; then + dprefix=$ac_default_prefix +else + dprefix=$prefix +fi + +gedatopdir=$dprefix/share/$DATADIR +expandgedadatadir=`echo $gedatopdir` + +# this has to be expanded ( no ${prefix} ) -- +AC_DEFINE_UNQUOTED(GEDADATADIR, "$gedatopdir", [gEDA/gaf's data directory]) +AC_DEFINE_UNQUOTED(VERSION, "$GEDA_VERSION", [Currently running version of gEDA/gaf]) + +# +# gEDA/gaf specify things which need to setup +######################################################################### + +# Finally create the final CFLAGS and LDFLAGS for use in the makefiles +GATTRIB_CFLAGS="$GTK_CFLAGS $GLIB_CFLAGS $LIBGEDA_CFLAGS" +GATTRIB_LDFLAGS="$GTK_LIBS $GLIB_LIBS $LIBGEDA_LIBS" + +# Makefile.in variable substitution +AC_SUBST(GATTRIB_CFLAGS) +AC_SUBST(GATTRIB_LDFLAGS) +AC_SUBST(GEDARCDIR) +AC_SUBST(GEDADATADIR) +AC_SUBST(PATHSEP) +AC_SUBST(OTHERPATHSEP) + +# Create all the necessary derived files +AC_CONFIG_FILES([Makefile + src/Makefile + lib/Makefile + design/Makefile + include/Makefile + lib/system-gattribrc + ]) + +AC_OUTPUT + +expandedGEDADATADIR=`eval "echo $GEDADATADIR"` +expandedGEDARCDIR=`eval "echo $GEDARCDIR"` + +AC_MSG_RESULT([ +***** Configuration summary for $PACKAGE $GATTRIB_VERSION ***** + +== gattrib version: $GATTRIB_VERSION +== base gEDA version: $GEDA_VERSION +== libgeda library version: $LIBGEDA_VERSION +== data directory: $expandedGEDADATADIR +== rc directory: $expandedGEDARCDIR +== compiler flags: $GATTRIB_CFLAGS +== linker flags: $GATTRIB_LDFLAGS +== glib compiler flags: $GLIB_CFLAGS +== glib linker libs: $GLIB_LIBS +== glib library version: $GLIB_VERSION +== gtk compiler flags: $GTK_CFLAGS +== gtk linker libs: $GTK_LIBS +== gtk library version: $GTK_VERSION + +************************************************************ +]) + diff --git a/gattrib/include/Makefile.am b/gattrib/include/Makefile.am new file mode 100644 index 000000000..902f0db4e --- /dev/null +++ b/gattrib/include/Makefile.am @@ -0,0 +1,10 @@ +## Process this file with automake to produce Makefile.in + +noinst_HEADERS = \ + globals.h prototype.h + +MOSTLYCLEANFILES = *.log core FILE *~ #*# +CLEANFILES = *.log core FILE *~ #*# +DISTCLEANFILES = *.log core FILE *~ prototype.bak #*# +MAINTAINERCLEANFILES = *.log core FILE *~ Makefile.in configure #*# + diff --git a/gattrib/include/gettext.h b/gattrib/include/gettext.h new file mode 100644 index 000000000..673b3bc8f --- /dev/null +++ b/gattrib/include/gettext.h @@ -0,0 +1,71 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((const char *) (Msgid)) +# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +#define _(String) gettext(String) + +#endif /* _LIBGETTEXT_H */ diff --git a/gattrib/include/globals.h b/gattrib/include/globals.h new file mode 100644 index 000000000..30868b6fa --- /dev/null +++ b/gattrib/include/globals.h @@ -0,0 +1,165 @@ +/* gEDA - GNU Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + + +/* ----------- SDB note about philosophy behind globals -------------- * + * I made the "TOPLEVEL project" and all the GTK window stuff into + * global variables. I know that this is supposedly bad programming form. + * However, here are some observations: + * -- I wanted to use gEDA's TOPLEVEL structure as much as possible, at + * least to hold info about the design's netlist & components. + * The TOPLEVEL strucuture is architected to hold info about gschem's + * window also. HOwever, gschem's windows are architected differently + * than mine in gattrib. This is because my windowing system does + * completely different things, and also uses the GtkSheet widget, which + * is architected completely differently from TOPLEVEL. + * -- Since I couldn't easily or naturally cram my windowing scheme into + * TOPLEVEL (or so I think), I decided to use a separate set of windows + * from those defined under TOPLEVEL for my application. + * -- The problem arises when using callbacks. Callbacks from GTK allow + * only one argument to be passed. Given the way I set up the menu bar, + * I didn't have easy acces to the information inside both the GtkSHeet + * objects *and* the TOPLEVEL stuff while only having one callback + * argument. This makes it hard to have access to e.g. a GtkSheet window + * and a list of files (in TOPLEVEL) simultaneously. + * -- Therefore, I decided to make both the window stuff and TOPLEVEL + * globals. + * -- Similarly, because I couldn't cram the SHEET_DATA struct into any + * hook in TOPLEVEL, I just made it a global also. + * -- Finally, in my defense, in gschem and gnetlist, (TOPLEVEL *w_current + * or pr_current) is passed to almost every function. Since it + * is just a pointer to a huge struct of stuff, manipulating + * the stuff in the struct has a global + * effect. That is, manipulating w_current (or pr_current) has side + * effects, so it is basically a global anyway. The real problem with + * globals occurs when you have a global variable caled "i" or "temp" + * which conflicts with a global in a module written by somebody else. + * Since pr_current is a very uncommon name, this should not be a + * problem here. Therefore, I decided + * to make life easy for myself dealing with callbacks by making both + * the windows and TOPLEVEL global variables. + * If there is a better way to solve this problem, I'd like to hear it. + * ------------------------------------------------------------------ */ + +#ifndef __GLOBALS__ +#define __GLOBALS__ + + +/*------------------------------------------------------------------ + * pr_current -- the main data structure from gEDA. I made it a + * global since it was treated that way anyway. It is defined in + * structs.h + *------------------------------------------------------------------*/ +TOPLEVEL *pr_current; + +/*------------------------------------------------------------------ + * first_page -- this will eventually get folded into the TOPLEVEL + * struct. + *------------------------------------------------------------------*/ +int first_page; /* set to zero after first page is read in. */ + +/*------------------------------------------------------------------ + * (SHEET_DATA *sheet_head) -- my own data structure which I made + * a global because it was easier to deal with when handing + * callbacks. It is defined in structs.h + *------------------------------------------------------------------*/ +SHEET_DATA *sheet_head; + +/*------------------------------------------------------------------ + * GTKsheet includes: stuff for dealing with windows. + *------------------------------------------------------------------*/ +#define DEFAULT_PRECISION 2 +#define DEFAULT_SPACE 8 +#define NUM_SHEETS 3 /* Components, Nets, and Pins */ + +GtkWidget *window; /* Main window */ +GtkWidget *main_vbox; /* Container which vertically aligns children */ +GtkWidget *menu_bar; /* Menu bar (holding file, edit, etc . . . */ + +GtkWidget *notebook; +GtkSheet **sheets; /* These are the spreadsheet widgets themselves */ + +GtkWidget **scrolled_windows; +GtkWidget *show_hide_box; +GtkWidget *status_box; +GtkWidget *location; +GtkWidget *entry; +GtkWidget *fgcolorcombo; +GtkWidget *bgcolorcombo; +GtkWidget *bordercombo; +GdkPixmap *pixmap; +GdkBitmap *mask; +GtkWidget *bg_pixmap; +GtkWidget *fg_pixmap; +GtkWidget *toolbar; +GtkWidget *left_button; +GtkWidget *center_button; +GtkWidget *right_button; +GtkWidget *tpixmap; +GtkWidget *bullet[10]; +GtkWidget *smile; +GtkWidget *curve; +GtkWidget *popup; + +GtkWidget *label; +GtkWidget *font_combo; +GtkWidget *toggle_combo; + +/*------------------------------------------------------------------ + * This stuff copied over from gschem + *------------------------------------------------------------------*/ +/* color stuff */ +extern GdkColormap *colormap; +extern GdkVisual *visual; + +/* colors */ +extern GdkColor white; +extern GdkColor black; +extern GdkColor red; +extern GdkColor green; +extern GdkColor blue; +extern GdkColor cyan; +extern GdkColor yellow; +extern GdkColor grey; +extern GdkColor grey90; +extern GdkColor darkgreen; +extern GdkColor darkred; +extern GdkColor darkyellow; +extern GdkColor darkcyan; +extern GdkColor darkblue; +extern GdkColor darkgrey; + +extern char *rc_filename; + +extern int logfile_fd; +extern int do_logging; +extern int logging_dest; + +/* Internationalization */ +#include "gettext.h" + +/* gattrib specific stuff */ +extern char *guile_proc; /* Needed for read-in of rc files */ + +/* command line switch settings */ +extern int verbose_mode; +extern int quiet_mode; + + +#endif diff --git a/gattrib/include/gtkextra-marshal.h b/gattrib/include/gtkextra-marshal.h new file mode 100644 index 000000000..82dfe5cc2 --- /dev/null +++ b/gattrib/include/gtkextra-marshal.h @@ -0,0 +1,199 @@ + +#ifndef __gtkextra_MARSHAL_H__ +#define __gtkextra_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* BOOL:INT,INT,POINTER,POINTER (gtkextra-marshal.list:1) */ +extern void gtkextra_BOOLEAN__INT_INT_POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__INT_INT_POINTER_POINTER gtkextra_BOOLEAN__INT_INT_POINTER_POINTER + +/* BOOL:BOXED,POINTER (gtkextra-marshal.list:2) */ +extern void gtkextra_BOOLEAN__BOXED_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__BOXED_POINTER gtkextra_BOOLEAN__BOXED_POINTER + +/* BOOL:BOXED,STRING (gtkextra-marshal.list:3) */ +extern void gtkextra_BOOLEAN__BOXED_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__BOXED_STRING gtkextra_BOOLEAN__BOXED_STRING + +/* BOOL:BOXED,BOXED (gtkextra-marshal.list:4) */ +extern void gtkextra_BOOLEAN__BOXED_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__BOXED_BOXED gtkextra_BOOLEAN__BOXED_BOXED + +/* BOOL:BOXED,DOUBLE,DOUBLE (gtkextra-marshal.list:5) */ +extern void gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__BOXED_DOUBLE_DOUBLE gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE + +/* BOOL:POINTER,POINTER (gtkextra-marshal.list:6) */ +extern void gtkextra_BOOLEAN__POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__POINTER_POINTER gtkextra_BOOLEAN__POINTER_POINTER + +/* BOOL:POINTER,BOXED (gtkextra-marshal.list:7) */ +extern void gtkextra_BOOLEAN__POINTER_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__POINTER_BOXED gtkextra_BOOLEAN__POINTER_BOXED + +/* BOOL:POINTER,STRING (gtkextra-marshal.list:8) */ +extern void gtkextra_BOOLEAN__POINTER_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__POINTER_STRING gtkextra_BOOLEAN__POINTER_STRING + +/* BOOL:POINTER (gtkextra-marshal.list:9) */ +extern void gtkextra_BOOLEAN__POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__POINTER gtkextra_BOOLEAN__POINTER + +/* BOOL:BOXED (gtkextra-marshal.list:10) */ +extern void gtkextra_BOOLEAN__BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__BOXED gtkextra_BOOLEAN__BOXED + +/* BOOL:INT,INT (gtkextra-marshal.list:11) */ +extern void gtkextra_BOOLEAN__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +#define gtkextra_BOOL__INT_INT gtkextra_BOOLEAN__INT_INT + +/* VOID:INT (gtkextra-marshal.list:12) */ +#define gtkextra_VOID__INT g_cclosure_marshal_VOID__INT + +/* VOID:INT,STRING (gtkextra-marshal.list:13) */ +extern void gtkextra_VOID__INT_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:BOXED (gtkextra-marshal.list:14) */ +#define gtkextra_VOID__BOXED g_cclosure_marshal_VOID__BOXED + +/* VOID:VOID (gtkextra-marshal.list:15) */ +#define gtkextra_VOID__VOID g_cclosure_marshal_VOID__VOID + +/* VOID:BOOL (gtkextra-marshal.list:16) */ +#define gtkextra_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN +#define gtkextra_VOID__BOOL gtkextra_VOID__BOOLEAN + +/* VOID:POINTER (gtkextra-marshal.list:17) */ +#define gtkextra_VOID__POINTER g_cclosure_marshal_VOID__POINTER + +/* VOID:INT,INT (gtkextra-marshal.list:18) */ +extern void gtkextra_VOID__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:INT,POINTER (gtkextra-marshal.list:19) */ +extern void gtkextra_VOID__INT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:INT,BOXED (gtkextra-marshal.list:20) */ +extern void gtkextra_VOID__INT_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:POINTER,POINTER (gtkextra-marshal.list:21) */ +extern void gtkextra_VOID__POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:BOXED,POINTER (gtkextra-marshal.list:22) */ +extern void gtkextra_VOID__BOXED_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:BOXED,BOXED (gtkextra-marshal.list:23) */ +extern void gtkextra_VOID__BOXED_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:OBJECT,OBJECT (gtkextra-marshal.list:24) */ +extern void gtkextra_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:DOUBLE,DOUBLE,DOUBLE,DOUBLE (gtkextra-marshal.list:25) */ +extern void gtkextra_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __gtkextra_MARSHAL_H__ */ + diff --git a/gattrib/include/gtkitementry_1_2.h b/gattrib/include/gtkitementry_1_2.h new file mode 100644 index 000000000..28402f5e2 --- /dev/null +++ b/gattrib/include/gtkitementry_1_2.h @@ -0,0 +1,75 @@ +/* GtkItemEntry - widget for gtk+ + * Copyright (C) 1999-2001 Adrian E. Feiguin + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GtkItemEntry widget by Adrian E. Feiguin + * Based on GtkEntry widget + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __GTK_ITEM_ENTRY_H__ +#define __GTK_ITEM_ENTRY_H__ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_TYPE_ITEM_ENTRY (gtk_item_entry_get_type ()) +#define GTK_ITEM_ENTRY(obj) (GTK_CHECK_CAST (obj, gtk_item_entry_get_type (), GtkItemEntry)) +#define GTK_ITEM_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, gtk_item_entry_get_type (), GtkItemEntryClass)) +#define GTK_IS_ITEM_ENTRY(obj) (GTK_CHECK_TYPE (obj, gtk_item_entry_get_type ())) +#define GTK_IS_ITEM_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY)) + + +typedef struct _GtkItemEntry GtkItemEntry; +typedef struct _GtkItemEntryClass GtkItemEntryClass; + +struct _GtkItemEntry +{ + GtkEntry parent; + + gint text_max_size; + + GdkGC *fg_gc; + GdkGC *bg_gc; + + GtkJustification justification; +}; + +struct _GtkItemEntryClass +{ + GtkEntryClass parent_class; +}; + +GtkType gtk_item_entry_get_type (void); +GtkWidget* gtk_item_entry_new (void); +GtkWidget* gtk_item_entry_new_with_max_length (guint16 max); +void gtk_item_entry_construct_with_max_length (GtkItemEntry *item_entry, + guint16 max); +void gtk_item_entry_set_text (GtkItemEntry *item_entry, + const gchar *text, + GtkJustification justification); +void gtk_item_entry_set_justification (GtkItemEntry *item_entry, + GtkJustification justification); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_ITEM_ENTRY_H__ */ diff --git a/gattrib/include/gtkitementry_2_2.h b/gattrib/include/gtkitementry_2_2.h new file mode 100644 index 000000000..839c2a879 --- /dev/null +++ b/gattrib/include/gtkitementry_2_2.h @@ -0,0 +1,76 @@ +/* GtkItemEntry - widget for gtk+ + * Copyright (C) 1999-2001 Adrian E. Feiguin + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GtkItemEntry widget by Adrian E. Feiguin + * Based on GtkEntry widget + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __GTK_ITEM_ENTRY_H__ +#define __GTK_ITEM_ENTRY_H__ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_TYPE_ITEM_ENTRY (gtk_item_entry_get_type ()) +#define GTK_ITEM_ENTRY(obj) (GTK_CHECK_CAST (obj, gtk_item_entry_get_type (), GtkItemEntry)) +#define GTK_ITEM_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, gtk_item_entry_get_type (), GtkItemEntryClass)) +#define GTK_IS_ITEM_ENTRY(obj) (GTK_CHECK_TYPE (obj, gtk_item_entry_get_type ())) +#define GTK_IS_ITEM_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY)) + + +typedef struct _GtkItemEntry GtkItemEntry; +typedef struct _GtkItemEntryClass GtkItemEntryClass; + +struct _GtkItemEntry +{ + GtkEntry parent; + + gint text_max_size; + + GtkJustification justification; +}; + +struct _GtkItemEntryClass +{ + GtkEntryClass parent_class; +}; + +GtkType gtk_item_entry_get_type (void); +GtkWidget* gtk_item_entry_new (void); +GtkWidget* gtk_item_entry_new_with_max_length (gint max); +void gtk_item_entry_set_text (GtkItemEntry *item_entry, + const gchar *text, + GtkJustification justification); + +void gtk_item_entry_set_justification (GtkItemEntry *item_entry, + GtkJustification justification); + +void gtk_item_entry_set_cursor_visible (GtkItemEntry *entry, + gboolean visible); +gboolean gtk_item_entry_get_cursor_visible (GtkItemEntry *entry); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_ITEM_ENTRY_H__ */ diff --git a/gattrib/include/gtksheet_1_2.h b/gattrib/include/gtksheet_1_2.h new file mode 100644 index 000000000..8b640ba6a --- /dev/null +++ b/gattrib/include/gtksheet_1_2.h @@ -0,0 +1,808 @@ +/* GtkSheet widget for Gtk+. + * Copyright (C) 1999-2001 Adrian E. Feiguin + * + * Based on GtkClist widget by Jay Painter, but major changes. + * Memory allocation routines inspired on SC (Spreadsheet Calculator) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_SHEET_H__ +#define __GTK_SHEET_H__ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +typedef enum{GTK_SHEET_FOREGROUND, + GTK_SHEET_BACKGROUND, + GTK_SHEET_FONT, + GTK_SHEET_JUSTIFICATION, + GTK_SHEET_BORDER, + GTK_SHEET_BORDER_COLOR, + GTK_SHEET_IS_EDITABLE, + GTK_SHEET_IS_VISIBLE} GtkSheetAttrType; + +/* sheet->state */ + +enum {GTK_SHEET_NORMAL, + GTK_SHEET_ROW_SELECTED, + GTK_SHEET_COLUMN_SELECTED, + GTK_SHEET_RANGE_SELECTED}; + +enum +{ + GTK_SHEET_LEFT_BORDER = 1 << 0, + GTK_SHEET_RIGHT_BORDER = 1 << 1, + GTK_SHEET_TOP_BORDER = 1 << 2, + GTK_SHEET_BOTTOM_BORDER = 1 << 3 +}; + +/* sheet flags */ +enum +{ + GTK_SHEET_IS_LOCKED = 1 << 0, + GTK_SHEET_IS_FROZEN = 1 << 1, + GTK_SHEET_IN_XDRAG = 1 << 2, + GTK_SHEET_IN_YDRAG = 1 << 3, + GTK_SHEET_IN_DRAG = 1 << 4, + GTK_SHEET_IN_SELECTION = 1 << 5, + GTK_SHEET_IN_RESIZE = 1 << 6, + GTK_SHEET_IN_CLIP = 1 << 7, + GTK_SHEET_ROW_FROZEN = 1 << 8, /* set rows to be resizeable */ + GTK_SHEET_COLUMN_FROZEN = 1 << 9, /* set cols to be resizeable */ + GTK_SHEET_AUTORESIZE = 1 << 10, /* resize column if text width is great than column width */ + GTK_SHEET_CLIP_TEXT = 1 << 11, /* clip text to cell */ + GTK_SHEET_ROW_TITLES_VISIBLE = 1 << 12, + GTK_SHEET_COL_TITLES_VISIBLE = 1 << 13, + GTK_SHEET_AUTO_SCROLL = 1 << 14, + GTK_SHEET_JUSTIFY_ENTRY = 1 << 15 +}; + +#define GTK_TYPE_SHEET_RANGE (gtk_sheet_range_get_type ()) +#define GTK_TYPE_SHEET (gtk_sheet_get_type ()) + +#define GTK_SHEET(obj) GTK_CHECK_CAST (obj, gtk_sheet_get_type (), GtkSheet) +#define GTK_SHEET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_sheet_get_type (), GtkSheetClass) +#define GTK_IS_SHEET(obj) GTK_CHECK_TYPE (obj, gtk_sheet_get_type ()) + +#define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags) +#define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag)) +#define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~(flag)) + +#define GTK_SHEET_IS_LOCKED(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IS_LOCKED) +#define GTK_SHEET_IS_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IS_FROZEN) +#define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG) +#define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG) +#define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG) +#define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION) +#define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE) +#define GTK_SHEET_IN_CLIP(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_CLIP) +#define GTK_SHEET_ROW_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_ROW_FROZEN) +#define GTK_SHEET_COLUMN_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_COLUMN_FROZEN) +#define GTK_SHEET_AUTORESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_AUTORESIZE) +#define GTK_SHEET_CLIP_TEXT(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_CLIP_TEXT) +#define GTK_SHEET_ROW_TITLES_VISIBLE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_ROW_TITLES_VISIBLE) +#define GTK_SHEET_COL_TITLES_VISIBLE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_COL_TITLES_VISIBLE) +#define GTK_SHEET_AUTO_SCROLL(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_AUTO_SCROLL) +#define GTK_SHEET_JUSTIFY_ENTRY(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_JUSTIFY_ENTRY) + +typedef struct _GtkSheet GtkSheet; +typedef struct _GtkSheetClass GtkSheetClass; +typedef struct _GtkSheetChild GtkSheetChild; +typedef struct _GtkSheetRow GtkSheetRow; +typedef struct _GtkSheetColumn GtkSheetColumn; +typedef struct _GtkSheetCell GtkSheetCell; +typedef struct _GtkSheetRange GtkSheetRange; +typedef struct _GtkSheetButton GtkSheetButton; +typedef struct _GtkSheetCellAttr GtkSheetCellAttr; +typedef struct _GtkSheetCellBorder GtkSheetCellBorder; + +struct _GtkSheetChild +{ + GtkWidget *widget; + GdkWindow *window; + gint x,y ; + gboolean attached_to_cell; + gint row, col; + gfloat x_align, y_align; +}; + +struct _GtkSheetButton +{ + GtkStateType state; + gchar *label; + + gboolean label_visible; + GtkSheetChild *child; + + GtkJustification justification; +}; + +struct _GtkSheetCellBorder +{ + gint8 mask; + guint width; + GdkLineStyle line_style; + GdkCapStyle cap_style; + GdkJoinStyle join_style; + GdkColor color; +}; + +struct _GtkSheetCellAttr +{ + GtkJustification justification; + GdkFont *font; + GdkColor foreground; + GdkColor background; + GtkSheetCellBorder border; + gboolean is_editable; + gboolean is_visible; +}; + +struct _GtkSheetCell +{ + GdkRectangle area; + gint row; + gint col; + + GtkSheetCellAttr *attributes; + + gchar *text; + gpointer link; +}; + +struct _GtkSheetRange +{ + gint row0,col0; /* upper-left cell */ + gint rowi,coli; /* lower-right cell */ +}; + + +struct _GtkSheetRow +{ + gchar *name; + gint height; + gint top_ypixel; + + GtkSheetButton button; + gboolean is_sensitive; + gboolean is_visible; +}; + +struct _GtkSheetColumn +{ + gchar *name; + gint width; + gint left_xpixel; + + GtkSheetButton button; + + gint left_text_column; /* min left column displaying text on this column */ + gint right_text_column; /* max right column displaying text on this column */ + + GtkJustification justification; + gboolean is_sensitive; + gboolean is_visible; +}; + + +struct _GtkSheet{ + GtkContainer container; + + guint16 flags; + + GtkSelectionMode selection_mode; + + guint freeze_count; + + /* Background colors */ + GdkColor bg_color; + GdkColor grid_color; + gboolean show_grid; + + /* sheet children */ + GList *children; + + /* allocation rectangle after the container_border_width + and the width of the shadow border */ + GdkRectangle internal_allocation; + + gchar *name; + + GtkSheetRow *row; + GtkSheetColumn *column; + + /* max number of diplayed cells */ + gint maxrow; + gint maxcol; + + /* Displayed range */ + + GtkSheetRange view; + + /* sheet data: dynamically allocated array of cell pointers */ + GtkSheetCell ***data; + + /* max number of allocated cells */ + gint maxallocrow; + gint maxalloccol; + + /* active cell */ + GtkSheetCell active_cell; + GtkWidget *sheet_entry; + GdkWindow *sheet_entry_window; /* for NO_WINDOW entry widgets(ala GtkLayout) */ + + GtkType entry_type; + + /* expanding selection */ + GtkSheetCell selection_cell; + + /* timer for automatic scroll during selection */ + gint32 timer; + /* timer for flashing clipped range */ + gint32 clip_timer; + gint interval; + + /* global selection button */ + GtkWidget *button; + + /* sheet state */ + gint state; + + /* selected range */ + GtkSheetRange range; + + /*the scrolling window and it's height and width to + * make things a little speedier */ + GdkWindow *sheet_window; + guint sheet_window_width; + guint sheet_window_height; + + /* sheet backing pixmap */ + GdkWindow *pixmap; + + /* offsets for scrolling */ + gint hoffset; + gint voffset; + gfloat old_hadjustment; + gfloat old_vadjustment; + + /* border shadow style */ + GtkShadowType shadow_type; + + /* Column Titles */ + GdkRectangle column_title_area; + GdkWindow *column_title_window; + + /* Row Titles */ + GdkRectangle row_title_area; + GdkWindow *row_title_window; + + /*scrollbars*/ + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + /* xor GC for the verticle drag line */ + GdkGC *xor_gc; + + /* gc for drawing unselected cells */ + GdkGC *fg_gc; + GdkGC *bg_gc; + + /* cursor used to indicate dragging */ + GdkCursor *cursor_drag; + + /* the current x-pixel location of the xor-drag vline */ + gint x_drag; + + /* the current y-pixel location of the xor-drag hline */ + gint y_drag; + + /* current cell being dragged */ + GtkSheetCell drag_cell; + /* current range being dragged */ + GtkSheetRange drag_range; + + /* clipped range */ + GtkSheetRange clip_range; +}; + +struct _GtkSheetClass +{ + GtkContainerClass parent_class; + + void (*set_scroll_adjustments) (GtkSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + + void (*select_row) (GtkSheet *sheet, gint row); + + void (*select_column) (GtkSheet *sheet, gint column); + + void (*select_range) (GtkSheet *sheet, GtkSheetRange *range); + + void (*clip_range) (GtkSheet *sheet, GtkSheetRange *clip_range); + + void (*resize_range) (GtkSheet *sheet, + GtkSheetRange *old_range, + GtkSheetRange *new_range); + + void (*move_range) (GtkSheet *sheet, + GtkSheetRange *old_range, + GtkSheetRange *new_range); + + gboolean (*traverse) (GtkSheet *sheet, + gint row, gint column, + gint *new_row, gint *new_column); + + gboolean (*deactivate) (GtkSheet *sheet, + gint row, gint column); + + gboolean (*activate) (GtkSheet *sheet, + gint row, gint column); + + void (*set_cell) (GtkSheet *sheet, + gint row, gint column); + + void (*clear_cell) (GtkSheet *sheet, + gint row, gint column); + + void (*changed) (GtkSheet *sheet, + gint row, gint column); + + void (*new_column_width) (GtkSheet *sheet, + gint col, + guint width); + + void (*new_row_height) (GtkSheet *sheet, + gint row, + guint height); + +}; + +GtkType gtk_sheet_get_type (void); +GtkType gtk_sheet_range_get_type (void); + +/* create a new sheet */ +GtkSheet * +gtk_sheet_new (guint rows, guint columns, const gchar *title); + +void +gtk_sheet_construct (GtkSheet *sheet, + guint rows, guint columns, const gchar *title); + +/* create a new browser sheet. It cells can not be edited */ +GtkWidget * +gtk_sheet_new_browser (guint rows, guint columns, const gchar *title); + +void +gtk_sheet_construct_browser (GtkSheet *sheet, + guint rows, guint columns, const gchar *title); + +/* create a new sheet with custom entry */ +GtkWidget * +gtk_sheet_new_with_custom_entry (guint rows, guint columns, + const gchar *title, + GtkType entry_type); +void +gtk_sheet_construct_with_custom_entry (GtkSheet *sheet, + guint rows, guint columns, + const gchar *title, + GtkType entry_type); +/* change scroll adjustments */ +void +gtk_sheet_set_hadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment); +void +gtk_sheet_set_vadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment); +/* Change entry */ +void +gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type); + +/* Returns sheet's entry widget */ +GtkWidget * +gtk_sheet_get_entry (GtkSheet *sheet); +GtkWidget * +gtk_sheet_get_entry_widget (GtkSheet *sheet); + +/* Returns sheet->state + * Added by Steven Rostedt */ +gint +gtk_sheet_get_state (GtkSheet *sheet); + +/* Returns sheet's ranges + * Added by Murray Cumming */ +guint +gtk_sheet_get_columns_count (GtkSheet *sheet); + +guint +gtk_sheet_get_rows_count (GtkSheet *sheet); + +void +gtk_sheet_get_visible_range (GtkSheet *sheet, + GtkSheetRange *range); +void +gtk_sheet_set_selection_mode (GtkSheet *sheet, gint mode); +/* set sheet title */ +void +gtk_sheet_set_title (GtkSheet *sheet, const gchar *title); + +/* freeze all visual updates of the sheet. + * Then thaw the sheet after you have made a number of changes. + * The updates will occure in a more efficent way than if + * you made them on a unfrozen sheet */ +void +gtk_sheet_freeze (GtkSheet *sheet); +void +gtk_sheet_thaw (GtkSheet *sheet); +/* Background colors */ +void +gtk_sheet_set_background (GtkSheet *sheet, + GdkColor *bg_color); +void +gtk_sheet_set_grid (GtkSheet *sheet, + GdkColor *grid_color); +void +gtk_sheet_show_grid (GtkSheet *sheet, + gboolean show); +gboolean +gtk_sheet_grid_visible (GtkSheet *sheet); + +/* set column title */ +void +gtk_sheet_set_column_title (GtkSheet * sheet, + gint column, + const gchar * title); + +/* set row title */ +void +gtk_sheet_set_row_title (GtkSheet * sheet, + gint row, + const gchar * title); + +/* set button label */ +void +gtk_sheet_row_button_add_label (GtkSheet *sheet, + gint row, const gchar *label); +void +gtk_sheet_column_button_add_label (GtkSheet *sheet, + gint column, const gchar *label); +void +gtk_sheet_row_button_justify (GtkSheet *sheet, + gint row, GtkJustification justification); +void +gtk_sheet_column_button_justify (GtkSheet *sheet, + gint column, GtkJustification justification); + +/* scroll the viewing area of the sheet to the given column + * and row; row_align and col_align are between 0-1 representing the + * location the row should appear on the screnn, 0.0 being top or left, + * 1.0 being bottom or right; if row or column is negative then there + * is no change */ +void +gtk_sheet_moveto (GtkSheet * sheet, + gint row, + gint column, + gfloat row_align, + gfloat col_align); + +/* resize column/row titles window */ +void +gtk_sheet_set_row_titles_width(GtkSheet *sheet, guint width); +void +gtk_sheet_set_column_titles_height(GtkSheet *sheet, guint height); + +/* show/hide column/row titles window */ +void +gtk_sheet_show_column_titles (GtkSheet *sheet); +void +gtk_sheet_show_row_titles (GtkSheet *sheet); +void +gtk_sheet_hide_column_titles (GtkSheet *sheet); +void +gtk_sheet_hide_row_titles (GtkSheet *sheet); + +/* set column button sensitivity. If sensitivity is TRUE it can be toggled, + * otherwise it acts as a title */ +void +gtk_sheet_column_set_sensitivity (GtkSheet *sheet, + gint column, gboolean sensitive); + +/* set sensitivity for all column buttons */ +void +gtk_sheet_columns_set_sensitivity (GtkSheet *sheet, gboolean sensitive); + + +/* set row button sensitivity. If sensitivity is TRUE can be toggled, + * otherwise it acts as a title */ +void +gtk_sheet_row_set_sensitivity (GtkSheet *sheet, + gint row, gboolean sensitive); + +/* set sensitivity for all row buttons */ +void +gtk_sheet_rows_set_sensitivity (GtkSheet *sheet, gboolean sensitive); + +/* set column visibility. The default value is TRUE. If FALSE, the + * column is hidden */ +void +gtk_sheet_column_set_visibility (GtkSheet *sheet, + gint column, gboolean visible); +void +gtk_sheet_column_label_set_visibility (GtkSheet *sheet, + gint column, gboolean visible); +void +gtk_sheet_columns_labels_set_visibility (GtkSheet *sheet, gboolean visible); + +/* set row visibility. The default value is TRUE. If FALSE, the + * row is hidden */ +void +gtk_sheet_row_set_visibility (GtkSheet *sheet, + gint row, gboolean visible); +void +gtk_sheet_row_label_set_visibility (GtkSheet *sheet, + gint row, gboolean visible); +void +gtk_sheet_rows_labels_set_visibility (GtkSheet *sheet, gboolean visible); + + +/* select the row. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void +gtk_sheet_select_row (GtkSheet * sheet, + gint row); + +/* select the column. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void +gtk_sheet_select_column (GtkSheet * sheet, + gint column); + +/* save selected range to "clipboard" */ +void +gtk_sheet_clip_range (GtkSheet *sheet, const GtkSheetRange *range); +/* free clipboard */ +void +gtk_sheet_unclip_range (GtkSheet *sheet); + +/* get scrollbars adjustment */ +GtkAdjustment * +gtk_sheet_get_vadjustment (GtkSheet * sheet); +GtkAdjustment * +gtk_sheet_get_hadjustment (GtkSheet * sheet); + +/* highlight the selected range and store bounds in sheet->range */ +void gtk_sheet_select_range (GtkSheet *sheet, + const GtkSheetRange *range); + +/* obvious */ +void gtk_sheet_unselect_range (GtkSheet *sheet); + +/* set active cell where the entry will be displayed + * returns FALSE if current cell can't be deactivated or + * requested cell can't be activated */ +gboolean +gtk_sheet_set_active_cell (GtkSheet *sheet, + gint row, gint column); +void +gtk_sheet_get_active_cell (GtkSheet *sheet, + gint *row, gint *column); + +/* set cell contents and allocate memory if needed */ +void +gtk_sheet_set_cell (GtkSheet *sheet, + gint row, gint col, + GtkJustification justification, + const gchar *text); +void +gtk_sheet_set_cell_text (GtkSheet *sheet, + gint row, gint col, + const gchar *text); + +/* get cell contents */ +gchar * +gtk_sheet_cell_get_text (GtkSheet *sheet, gint row, gint col); + + +/* clear cell contents */ +void +gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint col); +/* clear cell contents and remove links */ +void +gtk_sheet_cell_delete (GtkSheet *sheet, gint row, gint col); + +/* clear range contents. If range==NULL the whole sheet will be cleared */ +void +gtk_sheet_range_clear (GtkSheet *sheet, + const GtkSheetRange *range); +/* clear range contents and remove links */ +void +gtk_sheet_range_delete (GtkSheet *sheet, + const GtkSheetRange *range); + +/* get cell state: GTK_STATE_NORMAL, GTK_STATE_SELECTED */ +GtkStateType +gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col); + +/* Handles cell links */ +void +gtk_sheet_link_cell (GtkSheet *sheet, gint row, gint col, + gpointer link); + +gpointer +gtk_sheet_get_link (GtkSheet *sheet, gint row, gint col); +void +gtk_sheet_remove_link (GtkSheet *sheet, gint row, gint col); + +/* get row and column correspondig to the given position in the screen */ +gboolean +gtk_sheet_get_pixel_info (GtkSheet * sheet, + gint x, + gint y, + gint * row, + gint * column); + +/* get area of a given cell */ +gboolean +gtk_sheet_get_cell_area (GtkSheet *sheet, + gint row, + gint column, + GdkRectangle *area); + +/* set column width */ +void +gtk_sheet_set_column_width (GtkSheet * sheet, + gint column, + guint width); + +/* set row height */ +void +gtk_sheet_set_row_height (GtkSheet * sheet, + gint row, + guint height); + +/* append ncols columns to the end of the sheet */ +void +gtk_sheet_add_column (GtkSheet *sheet, guint ncols); + +/* append nrows row to the end of the sheet */ +void +gtk_sheet_add_row (GtkSheet *sheet, guint nrows); + +/* insert nrows rows before the given row and pull right */ +void +gtk_sheet_insert_rows (GtkSheet *sheet, guint row, guint nrows); + +/* insert ncols columns before the given col and pull down */ + void +gtk_sheet_insert_columns (GtkSheet *sheet, guint col, guint ncols); + +/* delete nrows rows starting in row */ +void +gtk_sheet_delete_rows (GtkSheet *sheet, guint row, guint nrows); + +/* delete ncols columns starting in col */ +void +gtk_sheet_delete_columns (GtkSheet *sheet, guint col, guint ncols); + +/* set abckground color of the given range */ +void +gtk_sheet_range_set_background (GtkSheet *sheet, + const GtkSheetRange *range, + const GdkColor *color); + +/* set foreground color (text color) of the given range */ +void +gtk_sheet_range_set_foreground (GtkSheet *sheet, + const GtkSheetRange *range, + const GdkColor *color); + +/* set text justification (GTK_JUSTIFY_LEFT, RIGHT, CENTER) of the given range. + * The default value is GTK_JUSTIFY_LEFT. If autoformat is on, the + * default justification for numbers is GTK_JUSTIFY_RIGHT */ +void +gtk_sheet_range_set_justification (GtkSheet *sheet, + const GtkSheetRange *range, + GtkJustification justification); +void +gtk_sheet_column_set_justification (GtkSheet *sheet, + gint column, + GtkJustification justification); +/* set if cell contents can be edited or not in the given range: + * accepted values are TRUE or FALSE. */ +void +gtk_sheet_range_set_editable (GtkSheet *sheet, + const GtkSheetRange *range, + gint editable); + +/* set if cell contents are visible or not in the given range: + * accepted values are TRUE or FALSE.*/ +void +gtk_sheet_range_set_visible (GtkSheet *sheet, + const GtkSheetRange *range, + gboolean visible); + +/* set cell border style in the given range. + * mask values are CELL_LEFT_BORDER, CELL_RIGHT_BORDER, CELL_TOP_BORDER, + * CELL_BOTTOM_BORDER + * width is the width of the border line in pixels + * line_style is the line_style for the border line */ +void +gtk_sheet_range_set_border (GtkSheet *sheet, + const GtkSheetRange *range, + gint mask, + guint width, + gint line_style); + +/* set border color for the given range */ +void +gtk_sheet_range_set_border_color (GtkSheet *sheet, + const GtkSheetRange *range, + const GdkColor *color); + +/* set font for the given range */ +void +gtk_sheet_range_set_font (GtkSheet *sheet, + const GtkSheetRange *range, + GdkFont *font); + +/* get cell attributes of the given cell */ +/* TRUE means that the cell is currently allocated */ +gboolean +gtk_sheet_get_attributes (GtkSheet *sheet, + gint row, gint col, + GtkSheetCellAttr *attributes); + + +GtkSheetChild * +gtk_sheet_put (GtkSheet *sheet, + GtkWidget *widget, + gint x, gint y); + +void +gtk_sheet_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col, + gfloat x_align, gfloat y_align); + +void +gtk_sheet_move_child (GtkSheet *sheet, + GtkWidget *widget, + gint x, gint y); + +GtkSheetChild * +gtk_sheet_get_child_at (GtkSheet *sheet, + gint row, gint col); + +void +gtk_sheet_button_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col, + gfloat x_align, gfloat y_align); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_SHEET_H__ */ + + diff --git a/gattrib/include/gtksheet_2_2.h b/gattrib/include/gtksheet_2_2.h new file mode 100644 index 000000000..f261c9b12 --- /dev/null +++ b/gattrib/include/gtksheet_2_2.h @@ -0,0 +1,871 @@ +/* GtkSheet widget for Gtk+. + * Copyright (C) 1999-2001 Adrian E. Feiguin + * + * Based on GtkClist widget by Jay Painter, but major changes. + * Memory allocation routines inspired on SC (Spreadsheet Calculator) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_SHEET_H__ +#define __GTK_SHEET_H__ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +typedef enum +{ + GTK_SHEET_FOREGROUND, + GTK_SHEET_BACKGROUND, + GTK_SHEET_FONT, + GTK_SHEET_JUSTIFICATION, + GTK_SHEET_BORDER, + GTK_SHEET_BORDER_COLOR, + GTK_SHEET_IS_EDITABLE, + GTK_SHEET_IS_VISIBLE +} GtkSheetAttrType; + +/* sheet->state */ + +enum +{ + GTK_SHEET_NORMAL, + GTK_SHEET_ROW_SELECTED, + GTK_SHEET_COLUMN_SELECTED, + GTK_SHEET_RANGE_SELECTED +}; + +enum +{ + GTK_SHEET_LEFT_BORDER = 1 << 0, + GTK_SHEET_RIGHT_BORDER = 1 << 1, + GTK_SHEET_TOP_BORDER = 1 << 2, + GTK_SHEET_BOTTOM_BORDER = 1 << 3 +}; + +#define GTK_TYPE_SHEET_RANGE (gtk_sheet_range_get_type ()) +#define GTK_TYPE_SHEET (gtk_sheet_get_type ()) + +#define GTK_SHEET(obj) GTK_CHECK_CAST (obj, gtk_sheet_get_type (), GtkSheet) +#define GTK_SHEET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_sheet_get_type (), GtkSheetClass) +#define GTK_IS_SHEET(obj) GTK_CHECK_TYPE (obj, gtk_sheet_get_type ()) + +/* Public flags, for compatibility */ + +#define GTK_SHEET_IS_LOCKED(sheet) gtk_sheet_locked(sheet) +#define GTK_SHEET_ROW_FROZEN(sheet) !gtk_sheet_rows_resizable(sheet) +#define GTK_SHEET_COLUMN_FROZEN(sheet) !gtk_sheet_columns_resizable(sheet) +#define GTK_SHEET_AUTORESIZE(sheet) gtk_sheet_autoresize(sheet) +#define GTK_SHEET_CLIP_TEXT(sheet) gtk_sheet_clip_text(sheet) +#define GTK_SHEET_ROW_TITLES_VISIBLE(sheet) gtk_sheet_row_titles_visible(sheet) +#define GTK_SHEET_COL_TITLES_VISIBLE(sheet) gtk_sheet_column_titles_visible(sheet) +#define GTK_SHEET_AUTO_SCROLL(sheet) gtk_sheet_autoscroll(sheet) +#define GTK_SHEET_JUSTIFY_ENTRY(sheet) gtk_sheet_justify_entry(sheet) + +typedef struct _GtkSheet GtkSheet; +typedef struct _GtkSheetClass GtkSheetClass; +typedef struct _GtkSheetChild GtkSheetChild; +typedef struct _GtkSheetRow GtkSheetRow; +typedef struct _GtkSheetColumn GtkSheetColumn; +typedef struct _GtkSheetCell GtkSheetCell; +typedef struct _GtkSheetRange GtkSheetRange; +typedef struct _GtkSheetButton GtkSheetButton; +typedef struct _GtkSheetCellAttr GtkSheetCellAttr; +typedef struct _GtkSheetCellBorder GtkSheetCellBorder; + +struct _GtkSheetChild +{ + GtkWidget *widget; + gint x,y ; + gboolean attached_to_cell; + gboolean floating; + gint row, col; + guint16 xpadding; + guint16 ypadding; + gboolean xexpand; + gboolean yexpand; + gboolean xshrink; + gboolean yshrink; + gboolean xfill; + gboolean yfill; +}; + +struct _GtkSheetButton +{ + GtkStateType state; + gchar *label; + + gboolean label_visible; + GtkSheetChild *child; + + GtkJustification justification; +}; + +struct _GtkSheetCellBorder +{ + gint8 mask; + guint width; + GdkLineStyle line_style; + GdkCapStyle cap_style; + GdkJoinStyle join_style; + GdkColor color; +}; + +struct _GtkSheetCellAttr +{ + GtkJustification justification; + GdkFont *font; + PangoFontDescription *font_desc; + GdkColor foreground; + GdkColor background; + GtkSheetCellBorder border; + gboolean is_editable; + gboolean is_visible; +}; + +struct _GtkSheetCell +{ + GdkRectangle area; + gint row; + gint col; + + GtkSheetCellAttr *attributes; + + gchar *text; + gpointer link; +}; + +struct _GtkSheetRange +{ + gint row0,col0; /* upper-left cell */ + gint rowi,coli; /* lower-right cell */ +}; + + +struct _GtkSheetRow +{ + gchar *name; + gint height; + gint top_ypixel; + guint16 requisition; + + GtkSheetButton button; + gboolean is_sensitive; + gboolean is_visible; +}; + +struct _GtkSheetColumn +{ + gchar *name; + gint width; + gint left_xpixel; + guint16 requisition; + + GtkSheetButton button; + + gint left_text_column; /* min left column displaying text on this column */ + gint right_text_column; /* max right column displaying text on this column */ + + GtkJustification justification; + gboolean is_sensitive; + gboolean is_visible; +}; + + +struct _GtkSheet{ + GtkContainer container; + + guint16 flags; + + GtkSelectionMode selection_mode; + gboolean autoresize; + gboolean autoscroll; + gboolean clip_text; + gboolean justify_entry; + gboolean locked; + + guint freeze_count; + + /* Background colors */ + GdkColor bg_color; + GdkColor grid_color; + gboolean show_grid; + + /* sheet children */ + GList *children; + + /* allocation rectangle after the container_border_width + and the width of the shadow border */ + GdkRectangle internal_allocation; + + gchar *name; + + GtkSheetRow *row; + GtkSheetColumn *column; + + gboolean rows_resizable; + gboolean columns_resizable; + + /* max number of diplayed cells */ + gint maxrow; + gint maxcol; + + /* Displayed range */ + + GtkSheetRange view; + + /* sheet data: dynamically allocated array of cell pointers */ + GtkSheetCell ***data; + + /* max number of allocated cells */ + gint maxallocrow; + gint maxalloccol; + + /* active cell */ + GtkSheetCell active_cell; + GtkWidget *sheet_entry; + + GtkType entry_type; + + /* expanding selection */ + GtkSheetCell selection_cell; + + /* timer for automatic scroll during selection */ + gint32 timer; + /* timer for flashing clipped range */ + gint32 clip_timer; + gint interval; + + /* global selection button */ + GtkWidget *button; + + /* sheet state */ + gint state; + + /* selected range */ + GtkSheetRange range; + + /*the scrolling window and it's height and width to + * make things a little speedier */ + GdkWindow *sheet_window; + guint sheet_window_width; + guint sheet_window_height; + + /* sheet backing pixmap */ + GdkWindow *pixmap; + + /* offsets for scrolling */ + gint hoffset; + gint voffset; + gfloat old_hadjustment; + gfloat old_vadjustment; + + /* border shadow style */ + GtkShadowType shadow_type; + + /* Column Titles */ + GdkRectangle column_title_area; + GdkWindow *column_title_window; + gboolean column_titles_visible; + + /* Row Titles */ + GdkRectangle row_title_area; + GdkWindow *row_title_window; + gboolean row_titles_visible; + + /*scrollbars*/ + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + /* xor GC for the verticle drag line */ + GdkGC *xor_gc; + + /* gc for drawing unselected cells */ + GdkGC *fg_gc; + GdkGC *bg_gc; + + /* cursor used to indicate dragging */ + GdkCursor *cursor_drag; + + /* the current x-pixel location of the xor-drag vline */ + gint x_drag; + + /* the current y-pixel location of the xor-drag hline */ + gint y_drag; + + /* current cell being dragged */ + GtkSheetCell drag_cell; + /* current range being dragged */ + GtkSheetRange drag_range; + + /* clipped range */ + GtkSheetRange clip_range; +}; + +struct _GtkSheetClass +{ + GtkContainerClass parent_class; + + void (*set_scroll_adjustments) (GtkSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + + void (*select_row) (GtkSheet *sheet, gint row); + + void (*select_column) (GtkSheet *sheet, gint column); + + void (*select_range) (GtkSheet *sheet, GtkSheetRange *range); + + void (*clip_range) (GtkSheet *sheet, GtkSheetRange *clip_range); + + void (*resize_range) (GtkSheet *sheet, + GtkSheetRange *old_range, + GtkSheetRange *new_range); + + void (*move_range) (GtkSheet *sheet, + GtkSheetRange *old_range, + GtkSheetRange *new_range); + + gboolean (*traverse) (GtkSheet *sheet, + gint row, gint column, + gint *new_row, gint *new_column); + + gboolean (*deactivate) (GtkSheet *sheet, + gint row, gint column); + + gboolean (*activate) (GtkSheet *sheet, + gint row, gint column); + + void (*set_cell) (GtkSheet *sheet, + gint row, gint column); + + void (*clear_cell) (GtkSheet *sheet, + gint row, gint column); + + void (*changed) (GtkSheet *sheet, + gint row, gint column); + + void (*new_column_width) (GtkSheet *sheet, + gint col, + guint width); + + void (*new_row_height) (GtkSheet *sheet, + gint row, + guint height); + +}; + +GType gtk_sheet_get_type (void); +GtkType gtk_sheet_range_get_type (void); + +/* create a new sheet */ +GtkWidget * +gtk_sheet_new (guint rows, guint columns, const gchar *title); + +void +gtk_sheet_construct (GtkSheet *sheet, + guint rows, guint columns, const gchar *title); + +/* create a new browser sheet. It cells can not be edited */ +GtkWidget * +gtk_sheet_new_browser (guint rows, guint columns, const gchar *title); + +void +gtk_sheet_construct_browser (GtkSheet *sheet, + guint rows, guint columns, const gchar *title); + +/* create a new sheet with custom entry */ +GtkWidget * +gtk_sheet_new_with_custom_entry (guint rows, guint columns, + const gchar *title, + GtkType entry_type); +void +gtk_sheet_construct_with_custom_entry (GtkSheet *sheet, + guint rows, guint columns, + const gchar *title, + GtkType entry_type); +/* change scroll adjustments */ +void +gtk_sheet_set_hadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment); +void +gtk_sheet_set_vadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment); +/* Change entry */ +void +gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type); + +/* Returns sheet's entry widget */ +GtkWidget * +gtk_sheet_get_entry (GtkSheet *sheet); +GtkWidget * +gtk_sheet_get_entry_widget (GtkSheet *sheet); + +/* Returns sheet->state + * Added by Steven Rostedt */ +gint +gtk_sheet_get_state (GtkSheet *sheet); + +/* Returns sheet's ranges + * Added by Murray Cumming */ +guint +gtk_sheet_get_columns_count (GtkSheet *sheet); + +guint +gtk_sheet_get_rows_count (GtkSheet *sheet); + +void +gtk_sheet_get_visible_range (GtkSheet *sheet, + GtkSheetRange *range); +void +gtk_sheet_set_selection_mode (GtkSheet *sheet, gint mode); + +void +gtk_sheet_set_autoresize (GtkSheet *sheet, gboolean autoresize); + +gboolean +gtk_sheet_autoresize (GtkSheet *sheet); + +void +gtk_sheet_set_autoscroll (GtkSheet *sheet, gboolean autoscroll); + +gboolean +gtk_sheet_autoscroll (GtkSheet *sheet); + +void +gtk_sheet_set_clip_text (GtkSheet *sheet, gboolean clip_text); + +gboolean +gtk_sheet_clip_text (GtkSheet *sheet); + +void +gtk_sheet_set_justify_entry (GtkSheet *sheet, gboolean justify); + +gboolean +gtk_sheet_justify_entry (GtkSheet *sheet); + +void +gtk_sheet_set_locked (GtkSheet *sheet, gboolean lock); + +gboolean +gtk_sheet_locked (GtkSheet *sheet); + +/* set sheet title */ +void +gtk_sheet_set_title (GtkSheet *sheet, const gchar *title); + +/* freeze all visual updates of the sheet. + * Then thaw the sheet after you have made a number of changes. + * The updates will occure in a more efficent way than if + * you made them on a unfrozen sheet */ +void +gtk_sheet_freeze (GtkSheet *sheet); +void +gtk_sheet_thaw (GtkSheet *sheet); +/* Background colors */ +void +gtk_sheet_set_background (GtkSheet *sheet, + GdkColor *bg_color); +void +gtk_sheet_set_grid (GtkSheet *sheet, + GdkColor *grid_color); +void +gtk_sheet_show_grid (GtkSheet *sheet, + gboolean show); +gboolean +gtk_sheet_grid_visible (GtkSheet *sheet); + +/* set/get column title */ +void +gtk_sheet_set_column_title (GtkSheet * sheet, + gint column, + const gchar * title); + +const gchar * +gtk_sheet_get_column_title (GtkSheet * sheet, + gint column); + +/* set/get row title */ +void +gtk_sheet_set_row_title (GtkSheet * sheet, + gint row, + const gchar * title); +const gchar * +gtk_sheet_get_row_title (GtkSheet * sheet, + gint row); + +/* set/get button label */ +void +gtk_sheet_row_button_add_label (GtkSheet *sheet, + gint row, const gchar *label); +void +gtk_sheet_column_button_add_label (GtkSheet *sheet, + gint column, const gchar *label); +const gchar * +gtk_sheet_row_button_get_label (GtkSheet *sheet, + gint row); +const gchar * +gtk_sheet_column_button_get_label (GtkSheet *sheet, + gint column); +void +gtk_sheet_row_button_justify (GtkSheet *sheet, + gint row, GtkJustification justification); +void +gtk_sheet_column_button_justify (GtkSheet *sheet, + gint column, GtkJustification justification); + +/* scroll the viewing area of the sheet to the given column + * and row; row_align and col_align are between 0-1 representing the + * location the row should appear on the screnn, 0.0 being top or left, + * 1.0 being bottom or right; if row or column is negative then there + * is no change */ +void +gtk_sheet_moveto (GtkSheet * sheet, + gint row, + gint column, + gfloat row_align, + gfloat col_align); + +/* resize column/row titles window */ +void +gtk_sheet_set_row_titles_width(GtkSheet *sheet, guint width); +void +gtk_sheet_set_column_titles_height(GtkSheet *sheet, guint height); + +/* show/hide column/row titles window */ +void +gtk_sheet_show_column_titles (GtkSheet *sheet); +void +gtk_sheet_show_row_titles (GtkSheet *sheet); +void +gtk_sheet_hide_column_titles (GtkSheet *sheet); +void +gtk_sheet_hide_row_titles (GtkSheet *sheet); +gboolean +gtk_sheet_column_titles_visible (GtkSheet *sheet); +gboolean +gtk_sheet_row_titles_visible (GtkSheet *sheet); + +/* set column button sensitivity. If sensitivity is TRUE it can be toggled, + * otherwise it acts as a title */ +void +gtk_sheet_column_set_sensitivity (GtkSheet *sheet, + gint column, gboolean sensitive); + +/* set sensitivity for all column buttons */ +void +gtk_sheet_columns_set_sensitivity (GtkSheet *sheet, gboolean sensitive); +void +gtk_sheet_columns_set_resizable (GtkSheet *sheet, gboolean resizable); +gboolean +gtk_sheet_columns_resizable (GtkSheet *sheet); + +/* set row button sensitivity. If sensitivity is TRUE can be toggled, + * otherwise it acts as a title */ +void +gtk_sheet_row_set_sensitivity (GtkSheet *sheet, + gint row, gboolean sensitive); + +/* set sensitivity for all row buttons */ +void +gtk_sheet_rows_set_sensitivity (GtkSheet *sheet, gboolean sensitive); +void +gtk_sheet_rows_set_resizable (GtkSheet *sheet, gboolean resizable); +gboolean +gtk_sheet_rows_resizable (GtkSheet *sheet); + +/* set column visibility. The default value is TRUE. If FALSE, the + * column is hidden */ +void +gtk_sheet_column_set_visibility (GtkSheet *sheet, + gint column, gboolean visible); +void +gtk_sheet_column_label_set_visibility (GtkSheet *sheet, + gint column, gboolean visible); +void +gtk_sheet_columns_labels_set_visibility (GtkSheet *sheet, gboolean visible); + +/* set row visibility. The default value is TRUE. If FALSE, the + * row is hidden */ +void +gtk_sheet_row_set_visibility (GtkSheet *sheet, + gint row, gboolean visible); +void +gtk_sheet_row_label_set_visibility (GtkSheet *sheet, + gint row, gboolean visible); +void +gtk_sheet_rows_labels_set_visibility (GtkSheet *sheet, gboolean visible); + + +/* select the row. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void +gtk_sheet_select_row (GtkSheet * sheet, + gint row); + +/* select the column. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void +gtk_sheet_select_column (GtkSheet * sheet, + gint column); + +/* save selected range to "clipboard" */ +void +gtk_sheet_clip_range (GtkSheet *sheet, const GtkSheetRange *range); +/* free clipboard */ +void +gtk_sheet_unclip_range (GtkSheet *sheet); + +gboolean +gtk_sheet_in_clip (GtkSheet *sheet); + +/* get scrollbars adjustment */ +GtkAdjustment * +gtk_sheet_get_vadjustment (GtkSheet * sheet); +GtkAdjustment * +gtk_sheet_get_hadjustment (GtkSheet * sheet); + +/* highlight the selected range and store bounds in sheet->range */ +void gtk_sheet_select_range (GtkSheet *sheet, + const GtkSheetRange *range); + +/* obvious */ +void gtk_sheet_unselect_range (GtkSheet *sheet); + +/* set active cell where the entry will be displayed + * returns FALSE if current cell can't be deactivated or + * requested cell can't be activated */ +gboolean +gtk_sheet_set_active_cell (GtkSheet *sheet, + gint row, gint column); +void +gtk_sheet_get_active_cell (GtkSheet *sheet, + gint *row, gint *column); + +/* set cell contents and allocate memory if needed */ +void +gtk_sheet_set_cell (GtkSheet *sheet, + gint row, gint col, + GtkJustification justification, + const gchar *text); +void +gtk_sheet_set_cell_text (GtkSheet *sheet, + gint row, gint col, + const gchar *text); + +/* get cell contents */ +gchar * +gtk_sheet_cell_get_text (GtkSheet *sheet, gint row, gint col); + + +/* clear cell contents */ +void +gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint col); +/* clear cell contents and remove links */ +void +gtk_sheet_cell_delete (GtkSheet *sheet, gint row, gint col); + +/* clear range contents. If range==NULL the whole sheet will be cleared */ +void +gtk_sheet_range_clear (GtkSheet *sheet, + const GtkSheetRange *range); +/* clear range contents and remove links */ +void +gtk_sheet_range_delete (GtkSheet *sheet, + const GtkSheetRange *range); + +/* get cell state: GTK_STATE_NORMAL, GTK_STATE_SELECTED */ +GtkStateType +gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col); + +/* Handles cell links */ +void +gtk_sheet_link_cell (GtkSheet *sheet, gint row, gint col, + gpointer link); + +gpointer +gtk_sheet_get_link (GtkSheet *sheet, gint row, gint col); +void +gtk_sheet_remove_link (GtkSheet *sheet, gint row, gint col); + +/* get row and column correspondig to the given position in the screen */ +gboolean +gtk_sheet_get_pixel_info (GtkSheet * sheet, + gint x, + gint y, + gint * row, + gint * column); + +/* get area of a given cell */ +gboolean +gtk_sheet_get_cell_area (GtkSheet *sheet, + gint row, + gint column, + GdkRectangle *area); + +/* set column width */ +void +gtk_sheet_set_column_width (GtkSheet * sheet, + gint column, + guint width); + +/* set row height */ +void +gtk_sheet_set_row_height (GtkSheet * sheet, + gint row, + guint height); + +/* append ncols columns to the end of the sheet */ +void +gtk_sheet_add_column (GtkSheet *sheet, guint ncols); + +/* append nrows row to the end of the sheet */ +void +gtk_sheet_add_row (GtkSheet *sheet, guint nrows); + +/* insert nrows rows before the given row and pull right */ +void +gtk_sheet_insert_rows (GtkSheet *sheet, guint row, guint nrows); + +/* insert ncols columns before the given col and pull down */ + void +gtk_sheet_insert_columns (GtkSheet *sheet, guint col, guint ncols); + +/* delete nrows rows starting in row */ +void +gtk_sheet_delete_rows (GtkSheet *sheet, guint row, guint nrows); + +/* delete ncols columns starting in col */ +void +gtk_sheet_delete_columns (GtkSheet *sheet, guint col, guint ncols); + +/* set abckground color of the given range */ +void +gtk_sheet_range_set_background (GtkSheet *sheet, + const GtkSheetRange *range, + const GdkColor *color); + +/* set foreground color (text color) of the given range */ +void +gtk_sheet_range_set_foreground (GtkSheet *sheet, + const GtkSheetRange *range, + const GdkColor *color); + +/* set text justification (GTK_JUSTIFY_LEFT, RIGHT, CENTER) of the given range. + * The default value is GTK_JUSTIFY_LEFT. If autoformat is on, the + * default justification for numbers is GTK_JUSTIFY_RIGHT */ +void +gtk_sheet_range_set_justification (GtkSheet *sheet, + const GtkSheetRange *range, + GtkJustification justification); +void +gtk_sheet_column_set_justification (GtkSheet *sheet, + gint column, + GtkJustification justification); +/* set if cell contents can be edited or not in the given range: + * accepted values are TRUE or FALSE. */ +void +gtk_sheet_range_set_editable (GtkSheet *sheet, + const GtkSheetRange *range, + gint editable); + +/* set if cell contents are visible or not in the given range: + * accepted values are TRUE or FALSE.*/ +void +gtk_sheet_range_set_visible (GtkSheet *sheet, + const GtkSheetRange *range, + gboolean visible); + +/* set cell border style in the given range. + * mask values are CELL_LEFT_BORDER, CELL_RIGHT_BORDER, CELL_TOP_BORDER, + * CELL_BOTTOM_BORDER + * width is the width of the border line in pixels + * line_style is the line_style for the border line */ +void +gtk_sheet_range_set_border (GtkSheet *sheet, + const GtkSheetRange *range, + gint mask, + guint width, + gint line_style); + +/* set border color for the given range */ +void +gtk_sheet_range_set_border_color (GtkSheet *sheet, + const GtkSheetRange *range, + const GdkColor *color); + +/* set font for the given range */ +void +gtk_sheet_range_set_font (GtkSheet *sheet, + const GtkSheetRange *range, + PangoFontDescription *font); + +/* get cell attributes of the given cell */ +/* TRUE means that the cell is currently allocated */ +gboolean +gtk_sheet_get_attributes (GtkSheet *sheet, + gint row, gint col, + GtkSheetCellAttr *attributes); + + +GtkSheetChild * +gtk_sheet_put (GtkSheet *sheet, + GtkWidget *widget, + gint x, gint y); +void +gtk_sheet_attach_floating (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col); +void +gtk_sheet_attach_default (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col); +void +gtk_sheet_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col, + gint xoptions, + gint yoptions, + gint xpadding, + gint ypadding); + + +void +gtk_sheet_move_child (GtkSheet *sheet, + GtkWidget *widget, + gint x, gint y); + +GtkSheetChild * +gtk_sheet_get_child_at (GtkSheet *sheet, + gint row, gint col); + +void +gtk_sheet_button_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_SHEET_H__ */ + + diff --git a/gattrib/include/i_vars.h b/gattrib/include/i_vars.h new file mode 100644 index 000000000..8deddd4d2 --- /dev/null +++ b/gattrib/include/i_vars.h @@ -0,0 +1,30 @@ +/* gEDA - GPL Electronic Design Automation + * gnetlist - gEDA Schematic Capture + * Copyright (C) 1998-2000 Ales V. Hvezda + * + * 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 USA + */ + +extern int default_graphic_color; +extern int default_text_color; +extern int default_text_size; +extern int default_text_caps; + +extern int default_attribute_color; +extern int default_paper_width; +extern int default_paper_height; +extern int default_init_right; +extern int default_init_bottom; + diff --git a/gattrib/include/pixmaps.h b/gattrib/include/pixmaps.h new file mode 100644 index 000000000..5fb65e35a --- /dev/null +++ b/gattrib/include/pixmaps.h @@ -0,0 +1,243 @@ +#ifndef __pixmap_h__ +#define __pixmap_h__ + +/* XPM */ +static char * bullet_xpm[] = { +"16 16 26 1", +" c #None", +". c #000000000000", +"X c #0000E38D0000", +"o c #0000EBAD0000", +"O c #0000F7DE0000", +"+ c #0000FFFF0000", +"@ c #0000CF3C0000", +"# c #0000D75C0000", +"$ c #0000B6DA0000", +"% c #0000C30B0000", +"& c #0000A2890000", +"* c #00009A690000", +"= c #0000AEBA0000", +"- c #00008E380000", +"; c #000086170000", +": c #000079E70000", +"> c #000071C60000", +", c #000065950000", +"< c #000059650000", +"1 c #000051440000", +"2 c #000045140000", +"3 c #00003CF30000", +"4 c #000030C20000", +"5 c #000028A20000", +"6 c #00001C710000", +"7 c #000014510000", +" ...... ", +" .XooO++. ", +" ..@@@#XoO+.. ", +" .$$$$$%@#XO++. ", +" .&&*&&=$%@XO+. ", +".*-;;;-*&=%@XO+.", +".;:>>>:;-&=%#o+.", +".>,<<<,>:-&$@XO.", +".<12321<>;*=%#o.", +".1345431,:-&$@o.", +".2467642<>;&$@X.", +" .57.753<>;*$@. ", +" .467642<>;&$@. ", +" ..5431,:-&.. ", +" .21<>;*. ", +" ...... "}; + +/* XPM */ +static char *center_just [] = +{ + "28 26 2 1", + ". c #None", + "X c #000000000000", + " ", + " ", + " ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXX ", + " XXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXX ", + " XXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXX ", + " XXXXXXXXXXXX ", + " ", + " ", + " ", + " ", + " ", +}; +/* XPM */ +static char * font[] = { +"26 26 3 1", +" c #None", +". c #000000000000", +"X c #000000000000", +" ", +" ", +" ", +" . ", +" ... ", +" ... ", +" ..... ", +" ..... ", +" .. .... ", +" .. .... ", +" .. .... ", +" ......... ", +" ........... ", +" .. .... ", +" .. .... ", +" .. .... ", +" ..... ....... ", +" ", +" ", +" ", +" XXXXXXXXXXXXXXXX ", +" XXXXXXXXXXXXXXXX ", +" XXXXXXXXXXXXXXXX ", +" XXXXXXXXXXXXXXXX ", +" ", +" ", +" "}; +/* XPM */ +static char *left_just [] = +{ + "28 26 2 1", + ". c #None", + "X c #000000000000", + " ", + " ", + " ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXX ", + " XXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXX ", + " XXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXX ", + " XXXXXXXXXXXXX ", + " ", + " ", + " ", + " ", + " ", +}; +/* XPM */ +static char *paint [] = +{ + "26 26 6 1", + ". c #None", + "a c #000000000000", + "e c #929292929292", + "g c #DBDBDBDBDBDB", + "h c #FFFFFFFFFFFF", + "X c #FFFFFFFFFFFF", + "..........................", + "...........ee.............", + "..........eeee............", + ".........eeggee...........", + ".........eegaee...........", + ".........eeahee...........", + ".........aahheeaa.........", + ".........ahhgeegaaa.......", + "........ahhghaeggaaa......", + ".......ahhghagaggeaaa.....", + "......ahhghggaggeeaaae....", + ".....ahhghgggggeeaaaae....", + ".....ahghgggggeeaeaaae....", + "......ahgggggeeaeeaaae....", + ".......ahgggeeaee.aaae....", + "........aggeeaee..aaee....", + ".........aeeaee...aee.....", + "..........aaee.....e......", + "...........ee.............", + "..........................", + "....XXXXXXXXXXXXXXXXXX....", + "....XXXXXXXXXXXXXXXXXX....", + "....XXXXXXXXXXXXXXXXXX....", + "....XXXXXXXXXXXXXXXXXX....", + "..........................", + "..........................", +}; +/* XPM */ +static char *right_just [] = +{ + "28 26 2 1", + ". c #None", + "X c #000000000000", + " ", + " ", + " ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXX ", + " XXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXX ", + " XXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXXXXXXX ", + " XXXXXXXXXXXXXXXXXX ", + " ", + " XXXXXXXXXXXXX ", + " XXXXXXXXXXXXX ", + " ", + " ", + " ", + " ", + " ", +}; + +/* XPM */ +static char * smile_xpm[] = { +"16 16 3 1", +" c #None", +". c #000000000000", +"X c #FFFFFFFF0000", +" ...... ", +" .XXXXXX. ", +" ..XXXXXXXX.. ", +" .XXXXXXXXXXXX. ", +" .XXX..XX..XXX. ", +".XXXX..XX..XXXX.", +".XXXX..XX..XXXX.", +".XXXXXXXXXXXXXX.", +".XX..XXXXXX..XX.", +".XX..XXXXXX..XX.", +".XXX.XXXXXX.XXX.", +" .XXX.XXXX.XXX. ", +" .XXXX....XXXX. ", +" ..XXXXXXXX.. ", +" .XXXXXX. ", +" ...... "}; + +#endif diff --git a/gattrib/include/prototype.h b/gattrib/include/prototype.h new file mode 100644 index 000000000..bdfc085a0 --- /dev/null +++ b/gattrib/include/prototype.h @@ -0,0 +1,229 @@ +/* + * This file holds all function prototypes for the entire gattrib + * project. It should be #include'ed after struct.h. + */ + +/* ---------------- gattrib.c ---------------- */ +void gattrib_quit(void); +void gattrib_main(int argc, char *argv[]); +int main(int argc, char *argv[]); + + +/* -------------- parsecmd.c ----------------- */ +void usage(char *cmd); + /* output usage string */ +int parse_commandline(int argc, char *argv[]); + /* run through cmd line options and set mode switches. */ + +/* -------------- listsort.c ----------------- */ +int cmp(STRING_LIST *a, STRING_LIST *b); +STRING_LIST *listsort(STRING_LIST *list, int is_circular, int is_double); + +/* ------------- g_register.c ------------- */ +void g_register_funcs(void); +SCM g_quit(void); + + +/* ------------- g_rc.c ------------- */ +SCM g_rc_gattrib_version(SCM version); + +/* ------------- s_sheet_data.c ------------- */ +SHEET_DATA *s_sheet_data_new(); + +void s_sheet_data_add_master_comp_list_items(OBJECT *start_obj); +void s_sheet_data_add_master_comp_attrib_list_items(OBJECT *start_obj); +void s_sheet_data_add_master_net_list_items(OBJECT *start_obj); +void s_sheet_data_add_master_net_attrib_list_items(OBJECT *start_obj); +void s_sheet_data_add_master_pin_list_items(OBJECT *start_obj); +void s_sheet_data_add_master_pin_attrib_list_items(OBJECT *start_obj); + +void s_sheet_data_gtksheet_to_sheetdata(); + + +/* ------------- s_string_list.c ------------- */ +STRING_LIST *s_string_list_new(); +void s_string_list_add_item(STRING_LIST *list, int *count, char *item); +int s_string_list_in_list(STRING_LIST *list, char *item); +void s_string_list_sort_master_comp_list(); +void s_string_list_sort_master_comp_attrib_list(); +void s_string_list_sort_master_net_list(); +void s_string_list_sort_master_net_attrib_list(); +void s_string_list_sort_master_pin_list(); +void s_string_list_sort_master_pin_attrib_list(); + + +/* ------------- s_table.c ------------- */ +TABLE **s_table_new(int rows, int cols); +void s_table_destroy(TABLE **table, int row_count, int col_count); +int s_table_get_index(STRING_LIST *list, char *string); + +void s_table_add_toplevel_comp_items_to_comp_table(OBJECT *start_obj); +void s_table_add_toplevel_net_items_to_net_table(OBJECT *start_obj); +void s_table_add_toplevel_pin_items_to_pin_table(OBJECT *start_obj); + +int s_table_gtksheet_to_all_tables(); +int s_table_gtksheet_to_table(GtkSheet *local_gtk_sheet, + STRING_LIST *master_row_list, STRING_LIST *master_col_list, + TABLE **local_table, int num_rows, int num_cols); + +/* ------------- s_toplevel.c ------------- */ +void s_toplevel_init(); +int s_toplevel_read_page(char *filename); +int s_toplevel_empty_project(); +void s_toplevel_gtksheet_to_toplevel(); +void s_toplevel_update_page(OBJECT *start_obj); +void s_toplevel_menubar_file_open(TOPLEVEL *pr_current); +void s_toplevel_menubar_file_save(TOPLEVEL *pr_current); +void s_toplevel_select_object(); +void s_toplevel_sheetdata_to_toplevel(OBJECT *start_obj); + +STRING_LIST *s_toplevel_get_component_attribs_in_sheet(char *refdes); +void s_toplevel_update_component_attribs_in_toplevel(OBJECT *o_current, + STRING_LIST *new_comp_attrib_list); +STRING_LIST *s_toplevel_get_net_attribs_in_sheet(char *netname); +void s_toplevel_update_net_attribs_in_toplevel(OBJECT *o_current, + STRING_LIST *new_net_attrib_list); +STRING_LIST *s_toplevel_get_pin_attribs_in_sheet(char *refdes, OBJECT *pin); +void s_toplevel_update_pin_attribs_in_toplevel(char *refdes, OBJECT *pin, + STRING_LIST *new_pin_attrib_list); + + +/* ------------- s_object.c ------------- */ +void s_object_add_comp_attrib_to_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value); +void s_object_add_net_attrib_to_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value); +void s_object_add_pin_attrib_to_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value); + +void s_object_replace_attrib_in_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value); +void s_object_remove_attrib_in_object(OBJECT *o_current, char *new_attrib_name); + +OBJECT *s_object_attrib_add_attrib_in_object(TOPLEVEL * w_current, char *text_string, + int visibility, int show_name_value, + OBJECT * object); +void s_object_delete_text_object_in_object(TOPLEVEL *w_current, OBJECT *test_object); + + +/* ------------- s_rename.c ------------- */ +void s_rename_init(void); +void s_rename_destroy_all(void); +void s_rename_next_set(void); +void s_rename_print(void); +int s_rename_search(char *src, char *dest, int quiet_flag); +void s_rename_add(char *src, char *dest); +void s_rename_all_lowlevel(NETLIST * netlist_head, char *src, char *dest); +void s_rename_all(TOPLEVEL * pr_current, NETLIST * netlist_head); + +/* ------------- s_misc.c ------------- */ +void verbose_print(char *string); +void verbose_done(void); +void verbose_reset_index(void); + +/* ------------- i_vars.c ------------- */ +void i_vars_set(TOPLEVEL * pr_current); +void i_window_vars_set(TOPLEVEL * w_current); +void i_vars_setnames(TOPLEVEL * w_current); + + +/* ------------- x_dialog.c ------------- */ +int x_dialog_about_keypress(GtkWidget * widget, GdkEventKey * event, + GtkWidget * window); +int x_dialog_about_close_callback(GtkWidget * widget, GtkWidget *window); +void x_dialog_about_dialog(); +GtkWidget *x_dialog_create_dialog_box(GtkWidget ** out_vbox, + GtkWidget ** out_action_area); +void x_dialog_close_window(GtkWidget * window); + + +/* ------------- x_gtksheet.c ------------- */ +void x_gtksheet_init(); +void x_notebook_init(); +void x_gtksheet_add_row_labels(GtkSheet *sheet, int count, STRING_LIST *list_head); +void x_gtksheet_add_col_labels(GtkSheet *sheet, int count, STRING_LIST *list_head); +void x_gtksheet_add_cell_item(GtkSheet *sheet, int i, int j, char *text); + +void format_text (GtkSheet *sheet, gchar *text, gint *justification, char *label); +void alarm_change(GtkWidget *widget, gint row, gint col, + gpointer data); +void alarm_activate(GtkWidget *widget, gint row, gint col, + gpointer data); +void alarm_deactivate(GtkWidget *widget, gint row, gint col, + gpointer data); +gint alarm_traverse(GtkWidget *widget, + gint row, gint col, gint *new_row, gint *new_col, + gpointer data); +void clipboard_handler(GtkWidget *widget, GdkEventKey *key); +void parse_numbers(GtkWidget *widget, gpointer data); +void resize_handler(GtkWidget *widget, GtkSheetRange *old_range, + GtkSheetRange *new_range, + gpointer data); +void move_handler(GtkWidget *widget, GtkSheetRange *old_range, + GtkSheetRange *new_range, + gpointer data); +gint change_entry(GtkWidget *widget, + gint row, gint col, gint *new_row, gint *new_col, + gpointer data); +void set_cell(GtkWidget *widget, gchar *insert, gint text_legth, gint position, + gpointer data); +void show_sheet_entry(GtkWidget *widget, gpointer data); +void activate_sheet_entry(GtkWidget *widget, gpointer data); +void show_entry(GtkWidget *widget, gpointer data); +void justify_left(GtkWidget *widget); +void justify_center(GtkWidget *widget); +void justify_right(GtkWidget *widget); +gint activate_sheet_cell(GtkWidget *widget, gint row, + gint column, gpointer data); +void change_border (GtkWidget *widget, gint border); +void change_fg(GtkWidget *widget, gint i, gchar *color_name); +void change_bg(GtkWidget *widget, gint i, gchar *color_name); +void do_hide_row_titles(GtkWidget *widget); +void do_hide_column_titles(GtkWidget *widget); +void do_show_row_titles(GtkWidget *widget); +void do_show_column_titles(GtkWidget *widget); + + +/* ------------- x_fileselect.c ------------- */ +void x_fileselect_destroy_window(GtkWidget * widget, FILEDIALOG * f_current); +int x_fileselect_keypress(GtkWidget * widget, GdkEventKey * event, + FILEDIALOG * f_current); +void x_fileselect_init_list_buffers(FILEDIALOG * f_current); +void x_fileselect_free_list_buffers(FILEDIALOG * f_current); +void x_fileselect_update_dirfile(FILEDIALOG * f_current, char *filename); +void x_fileselect_setup_list_buffers(FILEDIALOG * f_current, + int num_files, int num_directories); +int x_fileselect_include_file(char *filename, int filter_type); +void x_fileselect_fill_lists(FILEDIALOG * f_current); +gint x_fileselect_sch_files(GtkWidget * w, FILEDIALOG * f_current); +gint x_fileselect_sym_files(GtkWidget * w, FILEDIALOG * f_current); +gint x_fileselect_both_files(GtkWidget * w, FILEDIALOG * f_current); +gint x_fileselect_all_files(GtkWidget * w, FILEDIALOG * f_current); +static GtkWidget *x_fileselect_filter_menu(FILEDIALOG * f_current); +int +x_fileselect_preview_checkbox(GtkWidget * widget, FILEDIALOG * f_current); +void x_fileselect_saveas_close(GtkWidget * w, FILEDIALOG * f_current); +void x_fileselect_saveas(GtkWidget * w, FILEDIALOG * f_current); +void x_fileselect_change_dir(FILEDIALOG * f_current, char *new_directory); +void x_fileselect_set_filename(TOPLEVEL * w_current, const char *string); +void x_fileselect_open_file(GtkWidget * w, FILEDIALOG * f_current); +void +x_fileselect_dir_button(GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, FILEDIALOG * f_current); +void +x_fileselect_file_button(GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, FILEDIALOG * f_current); +void +x_fileselect_update_dirfile_saveas(FILEDIALOG * f_current, + char *new_filename); +void x_fileselect_close(GtkWidget * w, FILEDIALOG * f_current); +void x_fileselect_search(GtkWidget * w, FILEDIALOG * f_current); +void x_fileselect_setup(TOPLEVEL *pr_current, int filesel_type); + +/* ------------- x_window.c ------------- */ +void x_window_init(); +void x_window_create_menu(GtkWidget **menubar); +void x_window_add_items(); + + + diff --git a/gattrib/include/struct.h b/gattrib/include/struct.h new file mode 100644 index 000000000..019413694 --- /dev/null +++ b/gattrib/include/struct.h @@ -0,0 +1,159 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/* ----------------------------------------------------------------- * + * This file holds definitions of the structures used in gattrib. + * ----------------------------------------------------------------- */ + + +#ifndef SHEET_DATA_STRUCT +#define SHEET_DATA_STRUCT + +#include +#include +#include + +#include +#ifdef HAS_GTK22 +#include +#endif + +/* ------- Includes needed to make the GTK stuff work ------ */ + +#ifdef HAS_GTK22 +#include "gtksheet_2_2.h" +#include "gtkitementry_2_2.h" +#else +#include "gtksheet_1_2.h" +#include "gtkitementry_1_2.h" +#endif + +#include "pixmaps.h" + +/* ======== Data structures used in processing below here ========== */ + + +/* ----------------------------------------------------------------- * + * This struct used in dealing with guile's read-in of the rc files. + * ----------------------------------------------------------------- */ +typedef struct { + int m_val; + char *m_str; +} vstbl_entry; + + +/* ----------------------------------------------------------------- * + * The sheet data hierarchy built by the prog should look like this: + * SHEET_DATA->(STRING_LIST *master_XXX_list) // list of comps/nets/pins (row labels) + * ->(STRING_LIST *master_XXX_attrib_list) // list of attached names (column labels) + * ->(TABLE *XXX_table) // table of attrib values (table entries) + * ----------------------------------------------------------------- */ +typedef struct st_sheet_data SHEET_DATA; +typedef struct st_table TABLE; +typedef struct st_string_list STRING_LIST; +typedef struct st_pin_list PIN_LIST; +typedef struct st_main_window MAIN_WINDOW; + + +/* -------------------------------------------------------------------- * + * st_sheet_data defines SHEET_DATA, and holds master lists holding + * sorted lists of comp/netlist names. Also holds pointers to the heads + * of the attribute-holding component and net structures. + * -------------------------------------------------------------------- */ +struct st_sheet_data { + STRING_LIST *master_comp_list_head; /* Sorted list of all component refdeses used in design */ + STRING_LIST *master_comp_attrib_list_head; /* Sorted list of all component attribs used in design */ + int comp_count; /* This cannnot change -- user must edit design using gschem */ + int comp_attrib_count; /* This can change in this prog if the user adds attribs */ + + STRING_LIST *master_net_list_head; /* Sorted list of all net names used in design */ + STRING_LIST *master_net_attrib_list_head; /* Sorted list of all net attribs used in design */ + int net_count; /* This cannnot change -- user must edit design using gschem */ + int net_attrib_count; /* This can change in this prog if the user adds attribs */ + + STRING_LIST *master_pin_list_head; /* Sorted list of all refdes:pin items used in design. */ + STRING_LIST *master_pin_attrib_list_head; /* Sorted list of all pin attribs used in design */ + int pin_count; /* This cannnot change -- user must edit design using gschem */ + int pin_attrib_count; /* This can change in this prog if the user adds attribs */ + + TABLE **component_table; /* points to 2d array of component attribs */ + TABLE **net_table; /* points to 2d array of net attribs */ + TABLE **pin_table; /* points to 2d array of pin attribs */ +}; + + + +/* -------------------------------------------------------------------- * + * st_table defined what is held in a spreadsheet cell for both + * comp and net spreadsheets. Holds pointer to individual comp/net name, and + * pointer to attrib list. Ideally, the name pointer points to the + * refdes/netname string held in the TOPLEVEL data structure, so that + * when SHEET_DATA is manipulated, so is TOPLEVEL. + * -------------------------------------------------------------------- */ +#define ATTRIB_VIS_INVISIBLE 0 +#define ATTRIB_VIS_VALUE_ONLY 1 +#define ATTRIB_VIS_NAME_ONLY 2 +#define ATTRIB_VIS_BOTH 3 +struct st_table { + int row; /* location on spreadsheet */ + int col; /* location on spreadsheet */ + gchar *row_name; /* comp, net, or refdes:pin name */ + gchar *col_name; /* attrib name */ + gchar *attrib_value; /* attrib value */ + int visibility; /* 0 = invisible, 1 = value only, 2 = name only, + * 3 = both name & value visible */ + +}; + + +/* -------------------------------------------------------------------- * + * STRING_LIST is a list of strings. This struct is used for several + * different jobs, including serving as base class for master lists. + * -------------------------------------------------------------------- */ +struct st_string_list { + gchar *data; /* holds string */ + int pos; /* pos on spreadsheet */ + int length; /* number of items in list */ + STRING_LIST *prev; + STRING_LIST *next; +}; + +/* -------------------------------------------------------------------- * + * PIN_LIST is a special struct used for keeping track of pins. Since + * the master_pin_list must keep track of both refdes and pin, we need a + * special struct for pins. Later processing will for a STRING_LIST + * of refdes:pinnumber pairs for insertion in the spreadsheet. + * -------------------------------------------------------------------- */ +struct st_pin_list { + gchar *refdes; /* holds refdes string */ + gint pinnumber; + gchar *pinlabel; /* holds pin label string */ + int pos; /* pos on spreadsheet */ + int length; /* number of items in list */ + PIN_LIST *prev; + PIN_LIST *next; +}; + +#endif + + + + + + diff --git a/gattrib/include/x_menu.h b/gattrib/include/x_menu.h new file mode 100644 index 000000000..3204b2734 --- /dev/null +++ b/gattrib/include/x_menu.h @@ -0,0 +1,88 @@ +/* This is the stuff to define the graphical widgets used + * by gattrib. I had to separate them off from the other + * structs in order to define the callbacks first. The + * include order required is: + #include + #include "../include/struct.h" + #include "../include/prototype.h" + #include "../include/globals.h" + #include "../include/x_menu.h" + */ + + +#ifndef X_MENU_H +#define X_MENU_H + +#include +#include +#include + +#include +#ifdef HAS_GTK22 +#include +#endif + + +/* ------- Includes needed to make the GTK stuff work ------ */ + +#ifdef HAS_GTK22 +#include "gtksheet_2_2.h" +#include "gtkitementry_2_2.h" +#else +#include "gtksheet_1_2.h" +#include "gtkitementry_1_2.h" +#endif + +#include "pixmaps.h" + +/* ---------- Menu definition -- copied from the GTK tutorial file ---------- */ +/* ---------- and edited/adapted by SDB for gattrib. ---------- */ +/* This is the GtkItemFactoryEntry structure used to generate new menus. + Item 1: The menu path. The letter after the underscore indicates an + accelerator key once the menu is open. + Item 2: The accelerator key for the entry + Item 3: The callback function. + Item 4: The callback action. This changes the parameters with + which the function is called. The default is 0. + Item 5: The item type, used to define what kind of an item it is. + Here are the possible values: + + NULL -> "" + "" -> "" + "" -> create a title item + "<Item>" -> create a simple item + "<CheckItem>" -> create a check item + "<ToggleItem>" -> create a toggle item + "<RadioItem>" -> create a radio item + <path> -> path of a radio item to link against + "<Separator>" -> create a separator + "<Branch>" -> create an item to hold sub items (optional) + "<LastBranch>" -> create a right justified branch +*/ + +static GtkItemFactoryEntry menu_items[] = { + { "/_File", NULL, NULL, 0, "<Branch>" }, + { "/File/_Open", "<control>O", s_toplevel_menubar_file_open, 0, NULL }, + { "/File/_Save", "<control>S", s_toplevel_menubar_file_save, 0, NULL }, + { "/File/sep1", NULL, NULL, 0, "<Separator>" }, + { "/File/Quit", "<control>Q", gattrib_quit, 0, NULL }, + { "/_Edit", NULL, NULL, 0, "<Branch>" }, + { "/Edit/Add new attrib column", NULL, NULL, 0, NULL }, + { "/Edit/Delete attrib column", NULL, NULL, 0, NULL }, + { "/Edit/Search for attrib value", NULL, NULL, 0, NULL }, + { "/Edit/Search and replace attrib value", NULL, NULL, 0, NULL }, + { "/Edit/Search for refdes", NULL, NULL, 0, NULL }, + { "/_Options", NULL, NULL, 0, "<Branch>" }, + { "/Options/Set attrib visibility", NULL, NULL, 0, "<Branch>" }, + { "/Options/Set attrib visibility/invisible", NULL, NULL, 0, NULL }, + { "/Options/Set attrib visibility/value only", NULL, NULL, 0, NULL }, + { "/Options/Set attrib visibility/name only", NULL, NULL, 0, NULL }, + { "/Options/Set attrib visibility/name and value visible", NULL, NULL, 0, NULL }, + { "/_Help", NULL, NULL, 0, "<LastBranch>" }, + { "/_Help/About", NULL, x_dialog_about_dialog, 0, NULL }, +}; + + +#endif + + diff --git a/gattrib/include/x_states.h b/gattrib/include/x_states.h new file mode 100644 index 000000000..4e06a6158 --- /dev/null +++ b/gattrib/include/x_states.h @@ -0,0 +1,36 @@ +/* gEDA - GPL Electronic Design Automation + * gschem - gEDA Schematic Capture + * Copyright (C) 1998-2004 Ales V. Hvezda + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA + */ + +#ifndef X_STATES_H +#define X_STATES_H + +#undef NONE + +/* NOTE: when adding states, also update i_status_string() function */ + +enum x_states { NONE, SELECT, DRAWLINE, DRAWBOX, MOVE, COPY, DRAWCIRCLE, ZOOM, + PAN, DRAWNET, NETCONT, TEXTENTRY, DRAWPIN, DRAWARC, STARTDRAWNET, DRAWCOMP, + SBOX, STARTPAN, STARTSELECT, STARTCOPY, STARTMOVE, ENDCOPY, ENDMOVE, + ENDLINE, ENDBOX, ENDCIRCLE, ENDARC, ENDPIN, ENDCOMP, DRAWATTRIB, ENDATTRIB, + DRAWTEXT, ENDTEXT, ENDROTATEP, ENDMIRROR, ZOOMBOXSTART, ZOOMBOXEND, + STARTROUTENET, ENDROUTENET, MOUSEPAN, DRAWBUS, BUSCONT, STARTDRAWBUS, + STARTPASTE, ENDPASTE, GRIPS}; + + +#endif diff --git a/gattrib/lib/Makefile.am b/gattrib/lib/Makefile.am new file mode 100644 index 000000000..34ff2b26d --- /dev/null +++ b/gattrib/lib/Makefile.am @@ -0,0 +1,10 @@ + +rcdatadir = @GEDARCDIR@ +rcdata_DATA = system-gattribrc + +EXTRA_DIST = system-gattribrc.in + +MOSTLYCLEANFILES = *.log *~ +CLEANFILES = *.log *~ +DISTCLEANFILES = *.log core FILE *~ +MAINTAINERCLEANFILES = system-gattribrc *.log *~ Makefile.in configure diff --git a/gattrib/lib/system-gattribrc.in b/gattrib/lib/system-gattribrc.in new file mode 100644 index 000000000..e384b4ba5 --- /dev/null +++ b/gattrib/lib/system-gattribrc.in @@ -0,0 +1,26 @@ + +; +; Init file for gattrib +; + +; ;'s are comments +; keywords are case sensitive (guile feature) +; mode strings are case sensitive +; colors are not case sensitive +; + +; gattrib-version string +; +; Specifies the version of this file. This number is used to make sure +; that the rc file is compatible with the version of gschem that is +; being run. The end user should *not* change this value. +; +(gattrib-version "@VERSION@") + +; Load up commonrc +; +; Contains all paths needed for gattrib +(define gedadata (getenv "GEDADATA")) +(define gedadatarc (getenv "GEDADATARC")) +(load (string-append gedadatarc "@PATHSEP@system-commonrc")) + diff --git a/gattrib/src/Makefile.am b/gattrib/src/Makefile.am new file mode 100644 index 000000000..d2e5cd9ec --- /dev/null +++ b/gattrib/src/Makefile.am @@ -0,0 +1,80 @@ +## Process this file with automake to produce Makefile.in +## This Makefile.am created for gattrib by SDB 12.13.2003 + +AM_CFLAGS = -g -O2 + +bin_PROGRAMS = gattrib + +## don't forget all *.h files */ +if GTK22_SOURCE +gattrib_SOURCES = \ + gtksheet_2_2.c gtksheet_2_2.h \ + gtkextra-marshal.c gtkextra-marshal.h \ + gtkitementry_2_2.c gtkitementry_2_2.h \ + globals.c globals.h \ + prototype.h \ + struct.h \ + listsort.c \ + parsecmd.c \ + g_register.c \ + g_rc.c \ + i_vars.c i_vars.h \ + i_basic.c \ + x_window.c \ + x_gtksheet.c \ + x_dialog.c \ + x_fileselect.c \ + s_rename.c \ + s_misc.c \ + s_string_list.c \ + s_sheet_data.c \ + s_table.c \ + s_object.c \ + s_toplevel.c \ + gattrib.c +else +gattrib_SOURCES = \ + gtksheet_1_2.c gtksheet_1_2.h \ + gtkitementry_1_2.c gtkitementry_1_2.h \ + globals.c globals.h \ + prototype.h \ + struct.h \ + listsort.c \ + parsecmd.c \ + g_register.c \ + g_rc.c \ + i_vars.c i_vars.h \ + i_basic.c \ + x_window.c \ + x_gtksheet.c \ + x_dialog.c \ + x_fileselect.c \ + s_rename.c \ + s_misc.c \ + s_string_list.c \ + s_sheet_data.c \ + s_table.c \ + s_object.c \ + s_toplevel.c \ + gattrib.c +endif + + +INCLUDES = -I$(top_srcdir)/include @GATTRIB_CFLAGS@ +gattrib_LDFLAGS = @GATTRIB_LDFLAGS@ +gattrib_LDADD = @GATTRIB_LDFLAGS@ + +## Don't need these right now. +## man_MANS = gattrib.1 +## EXTRA_DIST = gattrib.1 + +MOSTLYCLEANFILES = *.log *.ps core FILE *~ #*# +CLEANFILES = *.log *.ps core FILE *~ #*# +DISTCLEANFILES = *.log core FILE *~ #*# +MAINTAINERCLEANFILES = *.log *.ps core FILE *~ #*# + + + + + + diff --git a/gattrib/src/g_rc.c b/gattrib/src/g_rc.c new file mode 100644 index 000000000..e4b7c2170 --- /dev/null +++ b/gattrib/src/g_rc.c @@ -0,0 +1,89 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +#include <config.h> + +#include <stdio.h> +#include <sys/stat.h> +#include <ctype.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_DIRENT_H +#include <dirent.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" +#include "../include/i_vars.h" /* This holds all the guile variable defs */ + +#define RETURN_G_RC_MODE(rc, var, size) \ + return g_rc_mode_general(mode, \ + (rc), \ + &(var), \ + mode_table, \ + size) + +/* a random int, used only as a place holder */ +int default_dummy; + + +/*------------------------------------------------------------------ + * + *------------------------------------------------------------------*/ +SCM g_rc_gattrib_version(SCM version) +{ + char *string = gh_scm2newstr(version, NULL); + + if (string == NULL) { + fprintf(stderr, + "%s requires a string as a parameter\n", "gattrib-version"); + return SCM_BOOL_F; + } + + if (strcmp(string, VERSION) != 0) { + fprintf(stderr, + "You are running gEDA version [%s],\n", VERSION); + fprintf(stderr, + "but you have a version [%s] gattribrc file:\n[%s]\n", + string, rc_filename); + fprintf(stderr, + "While gattrib is in ALPHA, " + "please be sure that you have the latest rc file.\n"); + free(string); + return SCM_BOOL_F; + } + + free(string); + return SCM_BOOL_T; +} + + + diff --git a/gattrib/src/g_register.c b/gattrib/src/g_register.c new file mode 100644 index 000000000..3cb552d25 --- /dev/null +++ b/gattrib/src/g_register.c @@ -0,0 +1,71 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +#include <config.h> + +#include <stdio.h> +#include <sys/stat.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ASSERT_H +#include <assert.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" +#include "../include/i_vars.h" + + +/* GtkWidget *w_main; */ + + +/* ---------------------------------------------------------------------- * + * g_register_funcs -- this registers the Scheme functions required to use + * gattrib. They are mostly unnecessary, except for reading in the gattribrc + * file at the beginning of the prog which gives the library search paths. + * ---------------------------------------------------------------------- */ +void g_register_funcs(void) +{ + /* general functions */ + gh_new_procedure0_0("quit", g_quit); + gh_new_procedure0_0("exit", g_quit); + + /* gattrib functions */ + gh_new_procedure1_0("gattrib-version", g_rc_gattrib_version); +} + +SCM g_quit(void) +{ +#ifdef DEBUG + printf("In g_quit, calling exit(0)\n"); +#endif + + gattrib_quit(); + exit(0); +} + diff --git a/gattrib/src/gattrib.c b/gattrib/src/gattrib.c new file mode 100644 index 000000000..57f2d822f --- /dev/null +++ b/gattrib/src/gattrib.c @@ -0,0 +1,374 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/***************************************************************************** + * In the spirit of open source/free software, major sections of * + * gattrib's code were borrowed from other sources, and hacked * + * together by SDB in Dec. 2003. Particularly rich sources for code were * + * gEDA/gnetlist, and the gtkextra program testgtksheet.c. Thanks to their * + * authors for providing the foundation upon which this is built. * + * * + * Of course, I *did* write major portions of the code too . . . . . * + * Some documentation about the internal operation of this program can be * + * found in the "NOTES" file in the gattrib top-level directory. * + * -- SDB December 2003 - * + *****************************************************************************/ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/*------------------------------------------------------------------ + * Includes originally from testgtksheet -- stuff needed to deal with + * spreadsheet widget. + *------------------------------------------------------------------*/ +#include <stdio.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include <glib.h> +#ifdef HAS_GTK22 +#include <glib-object.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes -- stuff dealing with gattrib data structs. + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" +/* #include "../include/x_menu.h" */ + + +/*------------------------------------------------------------------ + * gattrib_quit -- wrap up and quit fcn. + *------------------------------------------------------------------*/ +void gattrib_quit(void) +{ + s_clib_cache_free(); + s_clib_free(); + s_slib_free(); + /* s_rename_destroy_all(); */ +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_quit, calling gtk_main_quit()\n"); +#endif + gtk_main_quit(); + return; +} + +/*------------------------------------------------------------------ + * gattrib_main -- main gattrib fcn. + *------------------------------------------------------------------*/ +void gattrib_main(int argc, char *argv[]) +{ + /* TOPLEVEL *pr_current is a global */ + /* SHEET_DATA *sheet_head is a global */ + /* GtkWidget *main_window is a global */ + + int i; + int return_code = 0; /* used when invoking s_toplevel_read_page */ + int argv_index; + char *cwd; + char *full_filename; + PAGE *p_local; + + + + /* Initialize gEDA stuff */ + libgeda_init(); + + /* Note that argv_index holds index to first non-flag command line option + * (that is, to the first file name) */ + argv_index = parse_commandline(argc, argv); + cwd = u_basic_strdup(getcwd(NULL, 1024)); + + /* ---------- create log file right away ---------- */ + /* ---------- even if logging is enabled ---------- */ + s_log_init(cwd, "gattrib.log"); + + s_log_message + ("gEDA/gattrib version %s\n", VERSION); + s_log_message + ("gEDA/gattrib comes with ABSOLUTELY NO WARRANTY; see COPYING for more details.\n"); + s_log_message + ("This is free software, and you are welcome to redistribute it under certain\n"); + s_log_message + ("conditions; please see the COPYING file for more details.\n\n"); + + if (!quiet_mode) { + fflush(stderr); + fflush(stdout); + fprintf(stderr, + "gEDA/gattrib version %s\n", VERSION); + fprintf(stderr, + "gEDA/gattrib comes with ABSOLUTELY NO WARRANTY; see COPYING for more details.\n"); + fprintf(stderr, + "This is free software, and you are welcome to redistribute it under certain\n"); + fprintf(stderr, + "conditions; please see the COPYING file for more details.\n\n"); + } + + + /* ------ register guile (scheme) functions. Necessary to parse RC file. ------ */ + g_register_funcs(); +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just registered the guile functions.\n"); +#endif + + + /* ------ These libraries are defined in libgeda ------ */ +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just initialized the component and source libs.\n"); +#endif + + /* ----- Read in all rc files and set up relevant directory pointers ----- */ + g_rc_parse("gattribrc", rc_filename); +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just read in and parsed the RC files.\n"); +#endif + + /* ---------- This creates pointer to new project: (TOPLEVEL *pr_current) ---------- */ + pr_current = s_project_create_new(); /* pr_current declared in globals.h */ + s_toplevel_init(pr_current); /* This finishes initialization of pr_current */ +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just created and init'ed a new pr_current\n"); +#endif + + + /* -------- Initialize main_window. -------- */ +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- calling gtk_init. . . ..\n"); +#endif + gtk_init(&argc, &argv); + + x_window_init(); +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just initialized the main_window.\n"); +#endif + + + /* ---------- Initialize SHEET_DATA data structure ---------- */ + sheet_head = s_sheet_data_new(); /* sheet_head was declared in globals.h */ + + + /* ---------- Now loop on the files specified on the cmd line & read them in ---------- */ + /* argv[0] = name of this prog (gattrib). argv_index holds the + * position of the first filename */ + i = argv_index; + while(argv[i]) { + full_filename = u_basic_strdup(argv[i]); + +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main, opening full_filename = %s\n", full_filename); +#endif + + if (first_page == 1) { + if (pr_current->page_current->page_filename) { + /* Page structure & first page has already been created in + * s_project_create_new. Therefore, just rename the first page + * and open the project. First free "unknown" name. */ + free(pr_current->page_current->page_filename); + } + return_code |= s_toplevel_read_page(full_filename); /* read in first page */ + first_page = 0; + } else { + return_code |= s_toplevel_read_page(full_filename); /* read in secondary page */ + } + free(full_filename); + + /* Now add all items found to the master lists */ + s_sheet_data_add_master_comp_list_items(pr_current->page_current->object_head); + s_sheet_data_add_master_comp_attrib_list_items(pr_current->page_current->object_head); + +#if 0 + /* Note that this must be changed. We need to input the entire project + * before doing anything with the nets because we need to first + * determine where they are all connected! */ + s_sheet_data_add_master_net_list_items(pr_current->page_current->object_head); + s_sheet_data_add_master_net_attrib_list_items(pr_current->page_current->object_head); +#endif + + s_sheet_data_add_master_pin_list_items(pr_current->page_current->object_head); + s_sheet_data_add_master_pin_attrib_list_items(pr_current->page_current->object_head); + + i++; + + } /* while(argv[i]) */ + free(cwd); + + + /* ---------- Now complete read-in of project ---------- */ + if (first_page != 1) { + + + /* ---------- Sort the master lists ---------- */ + s_string_list_sort_master_comp_list(); + s_string_list_sort_master_comp_attrib_list(); + +#if 0 + /* Note that this must be changed. We need to input the entire project + * before doing anything with the nets because we need to first + * determine where they are all connected! */ + s_string_list_sort_master_net_list(); + s_string_list_sort_master_net_attrib_list(); +#endif + + s_string_list_sort_master_pin_list(); + s_string_list_sort_master_pin_attrib_list(); + + /* ---------- Create and load the tables ---------- */ + sheet_head->component_table = s_table_new(sheet_head->comp_count, sheet_head->comp_attrib_count); + sheet_head->net_table = s_table_new(sheet_head->net_count, sheet_head->net_attrib_count); + sheet_head->pin_table = s_table_new(sheet_head->pin_count, sheet_head->pin_attrib_count); + + p_local = pr_current->page_head; /* must iterate over all pages in design */ + while (p_local != NULL) { + if (p_local->pid != -1) { /* only traverse pages which are toplevel */ + if (p_local->object_head && p_local->page_control == 0) { + s_table_add_toplevel_comp_items_to_comp_table(p_local->object_head); /* adds all components from page to comp_table */ +#if 0 + /* Note that this must be changed. We need to input the entire project + * before doing anything with the nets because we need to first + * determine where they are all connected! */ + s_table_add_toplevel_net_items_to_net_table(p_local->object_head); /* adds all nets from page to net_table */ +#endif + + s_table_add_toplevel_pin_items_to_pin_table(p_local->object_head); /* adds all pins from page to pin_table */ + + } + } + p_local = p_local->next; /* iterate to next schematic page */ + } +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just returned from adding to the tables.\n"); + fflush(stderr); + fflush(stdout); +#endif + + +#if DEBUG + /* ----- Make debug printout of entire object list ----- */ + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just read in the project and filled out pr_current\n"); + printf("---------------------------- Object list -----------------------------\n"); + s_page_print_all(pr_current); + printf("-----------------------------------------------------------------------\n"); +#endif + + + /* -------------- Next, update windows --------------- */ + x_window_add_items(); /* This updates the top level stuff, + * and then calls another fcn to update + * the GtkSheet itself. */ +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- we have just returned from x_window_add_items.\n"); +#endif + + } /* if (first_page != 1) */ + else { + /* no filename found on command line, therefore we are still on the first page */ +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main -- no files specified on command line. Throw up filedialog.\n"); +#endif + x_fileselect_setup(pr_current, OPEN); + } + + /* ---------- Now make sure that there are attributes to display. ---------- */ + + + /* ---------- Now enter main event loop for spreadsheet. ---------- */ + gtk_widget_show( GTK_WIDGET(window) ); /* One final show for good measure */ + gtk_main(); + + /* ---------- Spreadsheet has been killed; we are quitting. ---------- */ +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In gattrib_main, we have exited gtk_main. \n"); +#endif + + return; +} + +/*------------------------------------------------------------------ + * main -- entry point to gattrib. This is just a wrapper which + * invokes the guile stuff, and points to the real main prog, + * gattrib_main. Note that I still need some vestigal + * guile stuff in order to read the rc files. + *------------------------------------------------------------------*/ +int main(int argc, char *argv[]) +{ + + /* This is i18n stuff */ +#if ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#ifdef HAS_GTK22 + bind_textdomain_codeset(PACKAGE, "UTF-8"); +#endif +#endif + + /* disable the deprecated warnings in guile 1.6.3 */ + /* Eventually the warnings will need to be fixed */ + if(getenv("GUILE_WARN_DEPRECATED")==NULL) + putenv("GUILE_WARN_DEPRECATED=no"); + + gh_enter(argc, argv, gattrib_main); + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("Now exiting main . . . Bye!\n"); +#endif + /* return 0 */ + exit(0); +} diff --git a/gattrib/src/globals.c b/gattrib/src/globals.c new file mode 100644 index 000000000..df5290a1b --- /dev/null +++ b/gattrib/src/globals.c @@ -0,0 +1,72 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +#include <config.h> +#include <stdio.h> + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + +char *rc_filename = NULL; + +/* color stuff */ +GdkColormap *colormap; +GdkVisual *visual; + +/* colors */ +GdkColor white; +GdkColor black; + +int logfile_fd=-1; +int do_logging=TRUE; +int logging_dest=LOG_WINDOW; + +/* these are required by libgeda */ +/* I have made most of these NULL because they aren't needed + * for gattrib -- no drawing is done. */ +void (*arc_draw_func)() = NULL; +void (*box_draw_func)() = NULL; +void (*circle_draw_func)() = NULL; +void (*complex_draw_func)() = NULL; +void (*line_draw_func)() = NULL; +void (*net_draw_func)() = NULL; +void (*bus_draw_func)() = NULL; +void (*text_draw_func)() = NULL; +void (*pin_draw_func)() = NULL; +void (*select_func)() = s_toplevel_select_object; +void (*x_log_update_func)() = NULL; +void (*quit_func)() = gattrib_quit; +void (*variable_set_func)() = i_vars_set; + + +/* gattrib specific variables */ +int first_page = 1; + +/* command line arguments */ +int verbose_mode=FALSE; +int quiet_mode=FALSE; + + + diff --git a/gattrib/src/gtkextra-marshal.c b/gattrib/src/gtkextra-marshal.c new file mode 100644 index 000000000..31cf6e3d7 --- /dev/null +++ b/gattrib/src/gtkextra-marshal.c @@ -0,0 +1,849 @@ + +#include <glib-object.h> + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_int +#define g_marshal_value_peek_flags(v) (v)->data[0].v_uint +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* BOOL:INT,INT,POINTER,POINTER (gtkextra-marshal.list:1) */ +void +gtkextra_BOOLEAN__INT_INT_POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER) (gpointer data1, + gint arg_1, + gint arg_2, + gpointer arg_3, + gpointer arg_4, + gpointer data2); + register GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + g_marshal_value_peek_pointer (param_values + 3), + g_marshal_value_peek_pointer (param_values + 4), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:BOXED,POINTER (gtkextra-marshal.list:2) */ +void +gtkextra_BOOLEAN__BOXED_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__BOXED_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:BOXED,STRING (gtkextra-marshal.list:3) */ +void +gtkextra_BOOLEAN__BOXED_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_STRING) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__BOXED_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__BOXED_STRING) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:BOXED,BOXED (gtkextra-marshal.list:4) */ +void +gtkextra_BOOLEAN__BOXED_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_BOXED) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__BOXED_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:BOXED,DOUBLE,DOUBLE (gtkextra-marshal.list:5) */ +void +gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE) (gpointer data1, + gpointer arg_1, + gdouble arg_2, + gdouble arg_3, + gpointer data2); + register GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_double (param_values + 2), + g_marshal_value_peek_double (param_values + 3), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:POINTER,POINTER (gtkextra-marshal.list:6) */ +void +gtkextra_BOOLEAN__POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__POINTER_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:POINTER,BOXED (gtkextra-marshal.list:7) */ +void +gtkextra_BOOLEAN__POINTER_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_BOXED) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__POINTER_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__POINTER_BOXED) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:POINTER,STRING (gtkextra-marshal.list:8) */ +void +gtkextra_BOOLEAN__POINTER_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_STRING) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__POINTER_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__POINTER_STRING) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:POINTER (gtkextra-marshal.list:9) */ +void +gtkextra_BOOLEAN__POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1, + gpointer arg_1, + gpointer data2); + register GMarshalFunc_BOOLEAN__POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:BOXED (gtkextra-marshal.list:10) */ +void +gtkextra_BOOLEAN__BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer data1, + gpointer arg_1, + gpointer data2); + register GMarshalFunc_BOOLEAN__BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOL:INT,INT (gtkextra-marshal.list:11) */ +void +gtkextra_BOOLEAN__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT) (gpointer data1, + gint arg_1, + gint arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__INT_INT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_INT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* VOID:INT (gtkextra-marshal.list:12) */ + +/* VOID:INT,STRING (gtkextra-marshal.list:13) */ +void +gtkextra_VOID__INT_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_STRING) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__INT_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); +} + +/* VOID:BOXED (gtkextra-marshal.list:14) */ + +/* VOID:VOID (gtkextra-marshal.list:15) */ + +/* VOID:BOOL (gtkextra-marshal.list:16) */ + +/* VOID:POINTER (gtkextra-marshal.list:17) */ + +/* VOID:INT,INT (gtkextra-marshal.list:18) */ +void +gtkextra_VOID__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer data1, + gint arg_1, + gint arg_2, + gpointer data2); + register GMarshalFunc_VOID__INT_INT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); +} + +/* VOID:INT,POINTER (gtkextra-marshal.list:19) */ +void +gtkextra_VOID__INT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_POINTER) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__INT_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + +/* VOID:INT,BOXED (gtkextra-marshal.list:20) */ +void +gtkextra_VOID__INT_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_BOXED) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__INT_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); +} + +/* VOID:POINTER,POINTER (gtkextra-marshal.list:21) */ +void +gtkextra_VOID__POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__POINTER_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + +/* VOID:BOXED,POINTER (gtkextra-marshal.list:22) */ +void +gtkextra_VOID__BOXED_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__BOXED_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + +/* VOID:BOXED,BOXED (gtkextra-marshal.list:23) */ +void +gtkextra_VOID__BOXED_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__BOXED_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); +} + +/* VOID:OBJECT,OBJECT (gtkextra-marshal.list:24) */ +void +gtkextra_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); +} + +/* VOID:DOUBLE,DOUBLE,DOUBLE,DOUBLE (gtkextra-marshal.list:25) */ +void +gtkextra_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE) (gpointer data1, + gdouble arg_1, + gdouble arg_2, + gdouble arg_3, + gdouble arg_4, + gpointer data2); + register GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_double (param_values + 1), + g_marshal_value_peek_double (param_values + 2), + g_marshal_value_peek_double (param_values + 3), + g_marshal_value_peek_double (param_values + 4), + data2); +} + diff --git a/gattrib/src/gtkitementry_1_2.c b/gattrib/src/gtkitementry_1_2.c new file mode 100644 index 000000000..947b9cea1 --- /dev/null +++ b/gattrib/src/gtkitementry_1_2.c @@ -0,0 +1,1982 @@ +/* GtkItemEntry - widget for gtk+ + * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar> + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GtkItemEntry widget by Adrian E. Feiguin + * Based on GtkEntry widget + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <ctype.h> +#include <string.h> +#include <gdk/gdkkeysyms.h> +#include <gdk/gdki18n.h> +#include <gtk/gtkmain.h> +#include <gtk/gtkselection.h> +#include <gtk/gtksignal.h> +#include <gtk/gtkentry.h> +#include "gtkitementry_1_2.h" + +#define MIN_ENTRY_WIDTH 150 +#define DRAW_TIMEOUT 20 +#define INNER_BORDER 4 + +enum { + ARG_0, + ARG_MAX_LENGTH, + ARG_VISIBILITY +}; + + +static void gtk_entry_class_init (GtkItemEntryClass *klass); +static void gtk_entry_init (GtkItemEntry *entry); +static void gtk_entry_set_position_from_editable + (GtkEditable *editable, + gint position); +static void gtk_entry_realize (GtkWidget *widget); +static void gtk_entry_unrealize (GtkWidget *widget); +static void gtk_entry_draw_focus (GtkWidget *widget); +static void gtk_entry_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gtk_entry_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_entry_make_backing_pixmap (GtkEntry *entry, + gint width, gint height); +static void gtk_entry_draw (GtkWidget *widget, + GdkRectangle *area); +static gint gtk_entry_expose (GtkWidget *widget, + GdkEventExpose *event); +static gint gtk_entry_button_press (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_entry_button_release (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_entry_motion_notify (GtkWidget *widget, + GdkEventMotion *event); +static gint gtk_entry_key_press (GtkWidget *widget, + GdkEventKey *event); +static void gtk_entry_draw_text (GtkEntry *entry); +static void gtk_entry_draw_cursor (GtkEntry *entry); +static void gtk_entry_draw_cursor_on_drawable + (GtkEntry *entry, + GdkDrawable *drawable); +#ifdef USE_XIM +static void gtk_entry_update_ic_attr (GtkWidget *widget); +#endif +static void gtk_entry_queue_draw (GtkEntry *entry); +static gint gtk_entry_timer (gpointer data); +static gint gtk_entry_position (GtkEntry *entry, + gint x); +static void entry_adjust_scroll (GtkEntry *entry); +static void gtk_entry_grow_text (GtkEntry *entry); +static void gtk_entry_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position); +static void gtk_entry_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos); +static void gtk_entry_update_text (GtkEditable *editable, + gint start_pos, + gint end_pos); +static gchar *gtk_entry_get_chars (GtkEditable *editable, + gint start_pos, + gint end_pos); + +/* Binding actions */ +static void gtk_entry_move_cursor (GtkEditable *editable, + gint x, + gint y); +static void gtk_entry_move_word (GtkEditable *editable, + gint n); +static void gtk_entry_move_to_column (GtkEditable *editable, + gint row); +static void gtk_entry_kill_char (GtkEditable *editable, + gint direction); +static void gtk_entry_kill_word (GtkEditable *editable, + gint direction); +static void gtk_entry_kill_line (GtkEditable *editable, + gint direction); + +/* To be removed */ +static void gtk_move_forward_character (GtkEntry *entry); +static void gtk_move_backward_character (GtkEntry *entry); +static void gtk_move_forward_word (GtkEntry *entry); +static void gtk_move_backward_word (GtkEntry *entry); +static void gtk_move_beginning_of_line (GtkEntry *entry); +static void gtk_move_end_of_line (GtkEntry *entry); +static void gtk_delete_forward_character (GtkEntry *entry); +static void gtk_delete_backward_character (GtkEntry *entry); +static void gtk_delete_forward_word (GtkEntry *entry); +static void gtk_delete_backward_word (GtkEntry *entry); +static void gtk_delete_line (GtkEntry *entry); +static void gtk_delete_to_line_end (GtkEntry *entry); +static void gtk_select_word (GtkEntry *entry, + guint32 time); +static void gtk_select_line (GtkEntry *entry, + guint32 time); + + +static void gtk_entry_set_selection (GtkEditable *editable, + gint start, + gint end); + +static gint gtk_entry_find_position (GtkEntry *entry, + gint position); +static void gtk_entry_set_position_from_editable (GtkEditable *editable, + gint position); + +static GtkWidgetClass *parent_class = NULL; +static GdkAtom ctext_atom = GDK_NONE; + + +static const GtkTextFunction control_keys[26] = +{ + (GtkTextFunction)gtk_move_beginning_of_line, /* a */ + (GtkTextFunction)gtk_move_backward_character, /* b */ + (GtkTextFunction)gtk_editable_copy_clipboard, /* c */ + (GtkTextFunction)gtk_delete_forward_character, /* d */ + (GtkTextFunction)gtk_move_end_of_line, /* e */ + (GtkTextFunction)gtk_move_forward_character, /* f */ + NULL, /* g */ + (GtkTextFunction)gtk_delete_backward_character, /* h */ + NULL, /* i */ + NULL, /* j */ + (GtkTextFunction)gtk_delete_to_line_end, /* k */ + NULL, /* l */ + NULL, /* m */ + NULL, /* n */ + NULL, /* o */ + NULL, /* p */ + NULL, /* q */ + NULL, /* r */ + NULL, /* s */ + NULL, /* t */ + (GtkTextFunction)gtk_delete_line, /* u */ + (GtkTextFunction)gtk_editable_paste_clipboard, /* v */ + (GtkTextFunction)gtk_delete_backward_word, /* w */ + (GtkTextFunction)gtk_editable_cut_clipboard, /* x */ + NULL, /* y */ + NULL, /* z */ +}; + +static const GtkTextFunction alt_keys[26] = +{ + NULL, /* a */ + (GtkTextFunction)gtk_move_backward_word, /* b */ + NULL, /* c */ + (GtkTextFunction)gtk_delete_forward_word, /* d */ + NULL, /* e */ + (GtkTextFunction)gtk_move_forward_word, /* f */ + NULL, /* g */ + NULL, /* h */ + NULL, /* i */ + NULL, /* j */ + NULL, /* k */ + NULL, /* l */ + NULL, /* m */ + NULL, /* n */ + NULL, /* o */ + NULL, /* p */ + NULL, /* q */ + NULL, /* r */ + NULL, /* s */ + NULL, /* t */ + NULL, /* u */ + NULL, /* v */ + NULL, /* w */ + NULL, /* x */ + NULL, /* y */ + NULL, /* z */ +}; + + + +GtkType +gtk_item_entry_get_type (void) +{ + static GtkType entry_type = 0; + + if (!entry_type) + { + static const GtkTypeInfo entry_info = + { + "GtkItemEntry", + sizeof (GtkItemEntry), + sizeof (GtkItemEntryClass), + (GtkClassInitFunc) gtk_entry_class_init, + (GtkObjectInitFunc) gtk_entry_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + entry_type = gtk_type_unique (GTK_TYPE_ENTRY, &entry_info); + } + + return entry_type; +} + +static void +gtk_entry_class_init (GtkItemEntryClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkEditableClass *editable_class; + + object_class = (GtkObjectClass*) klass; + widget_class = (GtkWidgetClass*) klass; + editable_class = (GtkEditableClass*) klass; + parent_class = gtk_type_class (GTK_TYPE_ENTRY); + + widget_class->realize = gtk_entry_realize; + widget_class->unrealize = gtk_entry_unrealize; + widget_class->draw_focus = gtk_entry_draw_focus; + widget_class->size_allocate = gtk_entry_size_allocate; + widget_class->size_request = gtk_entry_size_request; + widget_class->draw = gtk_entry_draw; + widget_class->expose_event = gtk_entry_expose; + widget_class->button_press_event = gtk_entry_button_press; + widget_class->button_release_event = gtk_entry_button_release; + widget_class->motion_notify_event = gtk_entry_motion_notify; + widget_class->key_press_event = gtk_entry_key_press; + + editable_class->insert_text = gtk_entry_insert_text; + editable_class->delete_text = gtk_entry_delete_text; + editable_class->changed = (void (*)(GtkEditable *)) entry_adjust_scroll; + + editable_class->move_cursor = gtk_entry_move_cursor; + editable_class->move_word = gtk_entry_move_word; + editable_class->move_to_column = gtk_entry_move_to_column; + + editable_class->kill_char = gtk_entry_kill_char; + editable_class->kill_word = gtk_entry_kill_word; + editable_class->kill_line = gtk_entry_kill_line; + + editable_class->update_text = gtk_entry_update_text; + editable_class->get_chars = gtk_entry_get_chars; + editable_class->set_selection = gtk_entry_set_selection; + editable_class->set_position = gtk_entry_set_position_from_editable; + +} + + +static void +gtk_entry_init (GtkItemEntry *entry) +{ +} + +GtkWidget* +gtk_item_entry_new () +{ + GtkWidget *item_entry; + + item_entry = GTK_WIDGET (gtk_type_new (gtk_item_entry_get_type ())); + + return item_entry; +} + +GtkWidget* +gtk_item_entry_new_with_max_length (guint16 max) +{ + GtkItemEntry *item_entry; + item_entry = gtk_type_new (gtk_item_entry_get_type ()); + + gtk_item_entry_construct_with_max_length(item_entry, max); + + return GTK_WIDGET (item_entry); +} + +void +gtk_item_entry_construct_with_max_length (GtkItemEntry *item_entry, guint16 max) +{ + GTK_ENTRY (item_entry)->text_max_length = max; +} + + +void +gtk_item_entry_set_text (GtkItemEntry *item_entry, + const gchar *text, + GtkJustification justification ) +{ + gint tmp_pos; + + GtkEditable *editable; + GtkEntry *entry; + + g_return_if_fail (item_entry != NULL); + g_return_if_fail (GTK_IS_ITEM_ENTRY (item_entry)); + g_return_if_fail (text != NULL); + + editable = GTK_EDITABLE (item_entry); + entry = GTK_ENTRY(item_entry); + + item_entry->justification = justification; + + gtk_entry_delete_text (GTK_EDITABLE(entry), 0, entry->text_length); + + tmp_pos = 0; + gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos); + editable->current_pos = tmp_pos; + + editable->selection_start_pos = 0; + editable->selection_end_pos = 0; + + if (GTK_WIDGET_DRAWABLE (entry)) + gtk_entry_draw_text (entry); +} + +static void +gtk_entry_set_position_from_editable (GtkEditable *editable, + gint position) +{ + GtkEntry *entry; + + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_EDITABLE (editable)); + + entry = GTK_ENTRY(editable); + + if ((position == -1) || (position > entry->text_length)) + GTK_EDITABLE(entry)->current_pos = entry->text_length; + else + GTK_EDITABLE(entry)->current_pos = position; + entry_adjust_scroll (entry); +} + +void +gtk_item_entry_set_justification (GtkItemEntry *item_entry, GtkJustification justification) +{ + g_return_if_fail (item_entry != NULL); + g_return_if_fail (GTK_IS_ITEM_ENTRY (item_entry)); + + item_entry->justification = justification; + entry_adjust_scroll (GTK_ENTRY(item_entry)); + gtk_widget_draw(GTK_WIDGET(item_entry), NULL); +} + + +static void +gtk_entry_realize (GtkWidget *widget) +{ + GtkItemEntry *item_entry; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ITEM_ENTRY (widget)); + + if (GTK_WIDGET_CLASS (parent_class)->realize) + (* GTK_WIDGET_CLASS (parent_class)->realize) (widget); + + item_entry = GTK_ITEM_ENTRY (widget); + + item_entry->fg_gc = gdk_gc_new (widget->window); + item_entry->bg_gc = gdk_gc_new (widget->window); + + gdk_gc_set_foreground (item_entry->fg_gc, &widget->style->white); + gdk_gc_set_foreground (item_entry->bg_gc, &widget->style->black); +} + +static void +gtk_entry_unrealize (GtkWidget *widget) +{ + GtkItemEntry *item_entry; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ITEM_ENTRY (widget)); + + item_entry = GTK_ITEM_ENTRY (widget); + + gdk_gc_destroy (item_entry->fg_gc); + gdk_gc_destroy (item_entry->bg_gc); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +gtk_entry_draw_focus (GtkWidget *widget) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ITEM_ENTRY (widget)); + + if (GTK_WIDGET_DRAWABLE (widget)) + { + gtk_entry_draw_cursor (GTK_ENTRY (widget)); + } +} + + +static void +gtk_entry_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkEntry *entry; + GtkItemEntry *ientry; + GtkEditable *editable; + gint width = 0, nwidth = 0, height = 0, nheight = 0; + + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ITEM_ENTRY (widget)); + g_return_if_fail (allocation != NULL); + + entry = GTK_ENTRY (widget); + ientry = GTK_ITEM_ENTRY (widget); + editable = GTK_EDITABLE (widget); + + if(ientry->text_max_size > 0) + allocation->width = MIN(ientry->text_max_size, allocation->width); + + widget->allocation = *allocation; + + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_get_size (entry->text_area, &width, &height); + gdk_window_move_resize (widget->window, + allocation->x + INNER_BORDER, + allocation->y + INNER_BORDER, + allocation->width - 2 * INNER_BORDER, + allocation->height - 2 * INNER_BORDER); + gdk_window_move_resize (entry->text_area, + 0, + 0, + allocation->width - 2 * INNER_BORDER, + allocation->height - 2 * INNER_BORDER); + + + gdk_window_get_size (entry->text_area, &nwidth, &nheight); + if(nwidth != width || nheight != height){ + entry->scroll_offset = 0; + entry_adjust_scroll (entry); + } + +#ifdef USE_XIM + if (editable->ic && + (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION)) + { + editable->ic_attr->preedit_area.width = nwidth; + editable->ic_attr->preedit_area.height = nheight; + gdk_ic_set_attr (editable->ic, editable->ic_attr, + GDK_IC_PREEDIT_AREA); + } +#endif + + } + +} + +static void +gtk_entry_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkItemEntry *entry; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ITEM_ENTRY (widget)); + g_return_if_fail (requisition != NULL); + + requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + INNER_BORDER) * 2; + requisition->height = (widget->style->font->ascent + + widget->style->font->descent + + (widget->style->klass->ythickness + INNER_BORDER) * 2); + + entry = GTK_ITEM_ENTRY(widget); + + if(entry->text_max_size > 0 && + requisition->width > entry->text_max_size) + requisition->width = entry->text_max_size; +} + +static void +gtk_entry_draw (GtkWidget *widget, + GdkRectangle *area) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ENTRY (widget)); + g_return_if_fail (area != NULL); + + if (GTK_WIDGET_DRAWABLE (widget)) + { + gtk_widget_draw_focus (widget); + gtk_entry_draw_text (GTK_ENTRY (widget)); + } +} + +static gint +gtk_entry_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkEntry *entry; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + entry = GTK_ENTRY (widget); + + if (widget->window == event->window) + gtk_widget_draw_focus (widget); + else if (entry->text_area == event->window) + gtk_entry_draw_text (GTK_ENTRY (widget)); + + return FALSE; +} + +static gint +gtk_entry_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + GtkEntry *entry; + GtkEditable *editable; + GdkModifierType mods; + gint tmp_pos; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (ctext_atom == GDK_NONE) + ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (widget); + + if (entry->button && (event->button != entry->button)) + return FALSE; + + gdk_window_get_pointer(widget->window, NULL, NULL, &mods); + if (mods & GDK_BUTTON3_MASK) + return FALSE; + + entry->button = event->button; + + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + + if (event->button == 1) + { + switch (event->type) + { + case GDK_BUTTON_PRESS: + gtk_grab_add (widget); + + tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset); + /* Set it now, so we display things right. We'll unset it + * later if things don't work out */ + editable->has_selection = TRUE; + gtk_entry_set_selection (editable, tmp_pos, tmp_pos); + editable->current_pos = editable->selection_start_pos; + break; + + case GDK_2BUTTON_PRESS: + gtk_select_word (entry, event->time); + break; + + case GDK_3BUTTON_PRESS: + gtk_select_line (entry, event->time); + break; + + default: + break; + } + } + else if (event->type == GDK_BUTTON_PRESS) + { + if ((event->button == 2) && editable->editable) + { + if (editable->selection_start_pos == editable->selection_end_pos || + editable->has_selection) + editable->current_pos = gtk_entry_position (entry, event->x + entry->scroll_offset); + gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, + ctext_atom, event->time); + } + else + { + gtk_grab_add (widget); + + tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset); + gtk_entry_set_selection (editable, tmp_pos, tmp_pos); + editable->has_selection = FALSE; + editable->current_pos = editable->selection_start_pos; + + if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window) + gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time); + } + } + + return FALSE; +} + +static gint +gtk_entry_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GtkEntry *entry; + GtkEditable *editable; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (widget); + + if (entry->button != event->button) + return FALSE; + + entry->button = 0; + + if (event->button == 1) + { + gtk_grab_remove (widget); + + editable->has_selection = FALSE; + if (editable->selection_start_pos != editable->selection_end_pos) + { + if (gtk_selection_owner_set (widget, + GDK_SELECTION_PRIMARY, + event->time)) + editable->has_selection = TRUE; + else + gtk_entry_queue_draw (entry); + } + else + { + if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window) + gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time); + } + } + else if (event->button == 3) + { + gtk_grab_remove (widget); + } + + return FALSE; +} + +static gint +gtk_entry_motion_notify (GtkWidget *widget, + GdkEventMotion *event) +{ + GtkEntry *entry; + gint x; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + entry = GTK_ENTRY (widget); + + if (entry->button == 0) + return FALSE; + + x = event->x; + if (event->is_hint || (entry->text_area != event->window)) + gdk_window_get_pointer (entry->text_area, &x, NULL, NULL); + + GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_position (entry, x + entry->scroll_offset); + GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(entry)->selection_end_pos; + entry_adjust_scroll (entry); + gtk_entry_queue_draw (entry); + + return FALSE; +} + +static gint +gtk_entry_key_press (GtkWidget *widget, + GdkEventKey *event) +{ + GtkEntry *entry; + GtkEditable *editable; + + gint return_val; + gint key; + guint initial_pos; + gint extend_selection; + gint extend_start; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (widget); + return_val = FALSE; + + if(!GTK_WIDGET_HAS_FOCUS(widget)) return FALSE; + + if(editable->editable == FALSE) + switch(event->keyval){ + case GDK_Left: case GDK_Right: case GDK_Up: case GDK_Down: + case GDK_Tab: case GDK_Return: + break; + default: + return TRUE; + } + + initial_pos = editable->current_pos; + + extend_selection = event->state & GDK_SHIFT_MASK; + extend_start = FALSE; + + if (extend_selection) + { + if (editable->selection_start_pos == editable->selection_end_pos) + { + editable->selection_start_pos = editable->current_pos; + editable->selection_end_pos = editable->current_pos; + } + + extend_start = (editable->current_pos == editable->selection_start_pos); + } + + switch (event->keyval) + { + case GDK_BackSpace: + return_val = TRUE; + if (event->state & GDK_CONTROL_MASK) + gtk_delete_backward_word (entry); + else + gtk_delete_backward_character (entry); + break; + case GDK_Clear: + return_val = TRUE; + gtk_delete_line (entry); + break; + case GDK_Insert: + return_val = TRUE; + if (event->state & GDK_SHIFT_MASK) + { + extend_selection = FALSE; + gtk_editable_paste_clipboard (editable); + } + else if (event->state & GDK_CONTROL_MASK) + { + gtk_editable_copy_clipboard (editable); + } + else + { + /* gtk_toggle_insert(entry) -- IMPLEMENT */ + } + break; + case GDK_Delete: + return_val = TRUE; + if (event->state & GDK_CONTROL_MASK) + gtk_delete_forward_word (entry); + else if (event->state & GDK_SHIFT_MASK) + { + extend_selection = FALSE; + gtk_editable_cut_clipboard (editable); + } + else + gtk_delete_forward_character (entry); + break; + case GDK_Home: + return_val = TRUE; + gtk_move_beginning_of_line (entry); + break; + case GDK_End: + return_val = TRUE; + gtk_move_end_of_line (entry); + break; + case GDK_Left: + return_val = TRUE; + if (event->state & GDK_CONTROL_MASK) + gtk_move_backward_word (entry); + else + gtk_move_backward_character (entry); + break; + case GDK_Right: + return_val = TRUE; + if (event->state & GDK_CONTROL_MASK) + gtk_move_forward_word (entry); + else + gtk_move_forward_character (entry); + break; + case GDK_Shift_L: + case GDK_Shift_R: + case GDK_Tab: + case GDK_Escape: + case GDK_Up: + case GDK_Down: + case GDK_Return: + return_val = TRUE; + gtk_signal_emit_by_name (GTK_OBJECT (entry), "activate"); + break; + default: + if ((event->keyval >= 0x20) && (event->keyval <= 0xFF)) + { + key = event->keyval; + + if (event->state & GDK_CONTROL_MASK) + { + if ((key >= 'A') && (key <= 'Z')) + key -= 'A' - 'a'; + + if ((key >= 'a') && (key <= 'z') && control_keys[key - 'a']) + { + (* control_keys[key - 'a']) (editable, event->time); + return_val = TRUE; + } + break; + } + else if (event->state & GDK_MOD1_MASK) + { + if ((key >= 'A') && (key <= 'Z')) + key -= 'A' - 'a'; + + if ((key >= 'a') && (key <= 'z') && alt_keys[key - 'a']) + { + (* alt_keys[key - 'a']) (editable, event->time); + return_val = TRUE; + } + break; + } + } + if (event->length > 0) + { + gint tmp_pos; + + extend_selection = FALSE; + gtk_editable_delete_selection (editable); + + tmp_pos = editable->current_pos; + gtk_editable_insert_text (editable, event->string, event->length, &tmp_pos); + editable->current_pos = tmp_pos; + + return_val = TRUE; + } + break; + } + + /* since we emit signals from within the above code, + * the widget might already be destroyed or at least + * unrealized. + */ + if (GTK_WIDGET_REALIZED (editable) && + return_val && (editable->current_pos != initial_pos)) + { + if (extend_selection) + { + if (editable->current_pos < editable->selection_start_pos) + editable->selection_start_pos = editable->current_pos; + else if (editable->current_pos > editable->selection_end_pos) + editable->selection_end_pos = editable->current_pos; + else + { + if (extend_start) + editable->selection_start_pos = editable->current_pos; + else + editable->selection_end_pos = editable->current_pos; + } + } + else + { + editable->selection_start_pos = 0; + editable->selection_end_pos = 0; + } + + gtk_editable_claim_selection (editable, + editable->selection_start_pos != editable->selection_end_pos, + event->time); + + entry_adjust_scroll (entry); + gtk_entry_queue_draw (entry); + } + + return return_val; +} + + +static void +gtk_entry_make_backing_pixmap (GtkEntry *entry, gint width, gint height) +{ + gint pixmap_width, pixmap_height; + + if (!entry->backing_pixmap) + { + /* allocate */ + entry->backing_pixmap = gdk_pixmap_new (entry->text_area, + width, height, + -1); + } + else + { + /* reallocate if sizes don't match */ + gdk_window_get_size (entry->backing_pixmap, + &pixmap_width, &pixmap_height); + if ((pixmap_width != width) || (pixmap_height != height)) + { + gdk_pixmap_unref (entry->backing_pixmap); + entry->backing_pixmap = gdk_pixmap_new (entry->text_area, + width, height, + -1); + } + } +} + +static void +gtk_entry_draw_text (GtkEntry *entry) +{ + GtkWidget *widget; + GtkItemEntry *item_entry; + GtkEditable *editable; + GtkStateType selected_state; + gint start_pos; + gint end_pos; + gint start_xoffset; + gint selection_start_pos; + gint selection_end_pos; + gint selection_start_xoffset; + gint selection_end_xoffset; + gint width, height; + gint y; + GdkDrawable *drawable; + gint use_backing_pixmap; + GdkWChar *stars; + GdkWChar *toprint; + + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + item_entry = GTK_ITEM_ENTRY (entry); + + if (entry->timer) + { + gtk_timeout_remove (entry->timer); + entry->timer = 0; + } + + if (GTK_WIDGET_DRAWABLE (entry)) + { + widget = GTK_WIDGET (entry); + editable = GTK_EDITABLE (entry); + + gdk_window_get_size (entry->text_area, &width, &height); + + if (!entry->text) + { + gdk_draw_rectangle (entry->text_area, + item_entry->bg_gc, + TRUE, + 0, 0, + width, + height); + + if (editable->editable) + gtk_entry_draw_cursor (entry); + return; + } + + + /* + If the widget has focus, draw on a backing pixmap to avoid flickering + and copy it to the text_area. + Otherwise draw to text_area directly for better speed. + */ + use_backing_pixmap = GTK_WIDGET_HAS_FOCUS (widget) && (entry->text != NULL); + + if (use_backing_pixmap) + { + gtk_entry_make_backing_pixmap (entry, width, height); + drawable = entry->backing_pixmap; + gdk_draw_rectangle (drawable, + item_entry->bg_gc, + TRUE, + 0, 0, + width, + height); + } + else + { + drawable = entry->text_area; + gdk_draw_rectangle (entry->text_area, + item_entry->bg_gc, + TRUE, + 0, 0, + width, + height); + + } + + +/* CENTER */ +/* y = height; + y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2; + y += widget->style->font->ascent; +*/ +/* BOTTOM */ + y = height; + y = (y - widget->style->font->ascent - widget->style->font->descent); + y += widget->style->font->ascent; + + start_pos = gtk_entry_find_position (entry, entry->scroll_offset); + start_xoffset = entry->char_offset[start_pos] - entry->scroll_offset; + + end_pos = gtk_entry_find_position (entry, entry->scroll_offset + width); + if (end_pos < entry->text_length) + end_pos += 1; + + selected_state = GTK_STATE_SELECTED; + if (!editable->has_selection) + selected_state = GTK_STATE_ACTIVE; + + selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos); + selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos); + + selection_start_pos = CLAMP (selection_start_pos, start_pos, end_pos); + selection_end_pos = CLAMP (selection_end_pos, start_pos, end_pos); + + selection_start_xoffset = + entry->char_offset[selection_start_pos] - entry->scroll_offset; + selection_end_xoffset = + entry->char_offset[selection_end_pos] -entry->scroll_offset; + + /* if entry->visible, print a bunch of stars. If not, print the standard text. */ + if (entry->visible) + { + toprint = entry->text + start_pos; + } + else + { + gint i; + + stars = g_new (GdkWChar, end_pos - start_pos); + for (i = 0; i < end_pos - start_pos; i++) + stars[i] = '*'; + toprint = stars; + } + + if (selection_start_pos > start_pos) + gdk_draw_text_wc (drawable, widget->style->font, + item_entry->fg_gc, + start_xoffset, y, + toprint, + selection_start_pos - start_pos); + + if ((selection_end_pos >= start_pos) && + (selection_start_pos < end_pos) && + (selection_start_pos != selection_end_pos)) + { + gint yoffset = + (height - + (widget->style->font->ascent + widget->style->font->descent)); + gtk_paint_flat_box (widget->style, drawable, + selected_state, GTK_SHADOW_NONE, + NULL, widget, "text", + selection_start_xoffset, + yoffset, + selection_end_xoffset - selection_start_xoffset, + widget->style->font->ascent + widget->style->font->descent); + gdk_draw_text_wc (drawable, widget->style->font, + widget->style->fg_gc[selected_state], + selection_start_xoffset, y, + toprint + selection_start_pos - start_pos, + selection_end_pos - selection_start_pos); + } + + if (selection_end_pos < end_pos) + gdk_draw_text_wc (drawable, widget->style->font, + item_entry->fg_gc, + selection_end_xoffset, y, + toprint + selection_end_pos - start_pos, + end_pos - selection_end_pos); + /* free the space allocated for the stars if it's neccessary. */ + if (!entry->visible) + g_free (toprint); + + if (editable->editable) + gtk_entry_draw_cursor_on_drawable (entry, drawable); + + if (use_backing_pixmap) + gdk_draw_pixmap(entry->text_area, + item_entry->fg_gc, + entry->backing_pixmap, + 0, 0, 0, 0, width, height); + } + + +} + +static void +gtk_entry_draw_cursor (GtkEntry *entry) +{ + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + + gtk_entry_draw_cursor_on_drawable (entry, entry->text_area); +} + +static void +gtk_entry_draw_cursor_on_drawable (GtkEntry *entry, GdkDrawable *drawable) +{ + GtkWidget *widget; + GtkEditable *editable; + gint xoffset; + gint text_area_height; + + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + if (GTK_WIDGET_DRAWABLE (entry)) + { + widget = GTK_WIDGET (entry); + editable = GTK_EDITABLE (entry); + + xoffset = entry->char_offset[editable->current_pos]; + xoffset -= entry->scroll_offset; + + gdk_window_get_size (entry->text_area, NULL, &text_area_height); + + if (GTK_WIDGET_HAS_FOCUS (widget) && + (editable->selection_start_pos == editable->selection_end_pos)) + { + gint yoffset = + (text_area_height - + (widget->style->font->ascent + widget->style->font->descent)); + + gdk_draw_line (drawable, widget->style->fg_gc[GTK_STATE_NORMAL], + xoffset, yoffset, + xoffset, text_area_height); + } + else + { + gint yoffset = + (text_area_height - + (widget->style->font->ascent + widget->style->font->descent)); + + gtk_paint_flat_box (widget->style, drawable, + GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE, + NULL, widget, "entry_bg", + xoffset, yoffset, + 1, text_area_height); + /* Draw the character under the cursor again */ +/* + gdk_draw_text_wc (drawable, widget->style->font, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + xoffset, yoffset, + entry->text + editable->current_pos, 1); +*/ + } + + +#ifdef USE_XIM + if (GTK_WIDGET_HAS_FOCUS(widget) && gdk_im_ready() && editable->ic && + (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION)) + { + editable->ic_attr->spot_location.x = xoffset; + editable->ic_attr->spot_location.y = + (text_area_height + (widget->style->font->ascent + - widget->style->font->descent) + 1) / 2; + + gdk_ic_set_attr (editable->ic, + editable->ic_attr, GDK_IC_SPOT_LOCATION); + } +#endif + } +} + +static void +gtk_entry_queue_draw (GtkEntry *entry) +{ + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + if (!entry->timer) + entry->timer = gtk_timeout_add (DRAW_TIMEOUT, gtk_entry_timer, (gpointer) entry); +} + +static gint +gtk_entry_timer (gpointer data) +{ + GtkEntry *entry; + + GDK_THREADS_ENTER (); + + entry = GTK_ENTRY (data); + entry->timer = 0; + gtk_entry_draw_text (entry); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gtk_entry_find_position (GtkEntry *entry, + gint x) +{ + gint start = 0; + gint end = entry->text_length; + gint half; + + if (x <= 0) + return 0; + if (x >= entry->char_offset[end]) + return end; + + /* invariant - char_offset[start] <= x < char_offset[end] */ + + while (start != end) + { + half = (start+end)/2; + if (half == start) + return half; + else if (entry->char_offset[half] <= x) + start = half; + else + end = half; + } + + return start; +} + +static gint +gtk_entry_position (GtkEntry *entry, + gint x) +{ + return gtk_entry_find_position(entry, x); +} + + +static void +entry_adjust_scroll (GtkEntry *entry) +{ + GtkItemEntry *item_entry; + gint xoffset; + gint text_area_width, text_area_height; + gint char_width, text_width; + + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + item_entry = GTK_ITEM_ENTRY (entry); + + if (!entry->text_area) + return; + + gdk_window_get_size (entry->text_area, &text_area_width, &text_area_height); + + char_width = gdk_char_width (GTK_WIDGET(item_entry)->style->font,(gchar)'X'); + + entry->scroll_offset = 0 ; + + switch(item_entry->justification){ + + case GTK_JUSTIFY_FILL: + case GTK_JUSTIFY_LEFT: + +/* LEFT JUSTIFICATION */ + + if (GTK_EDITABLE(entry)->current_pos > 0) + xoffset = gdk_text_width (GTK_WIDGET (entry)->style->font, gtk_entry_get_text(entry), GTK_EDITABLE(entry)->current_pos); + else + xoffset = 0; + + xoffset -= entry->scroll_offset; + if (xoffset < 0) + entry->scroll_offset += xoffset; + else if (xoffset > text_area_width){ + if(item_entry->text_max_size != 0 && + text_area_width + char_width <= item_entry->text_max_size){ + + text_width = gdk_text_width (GTK_WIDGET (item_entry)->style->font, + gtk_entry_get_text(entry), strlen(gtk_entry_get_text(entry))); + GTK_WIDGET(item_entry)->allocation.width = text_width + + 2 * INNER_BORDER + 1; + gtk_entry_size_allocate(GTK_WIDGET(item_entry), + (GtkAllocation *)(>K_WIDGET(item_entry)->allocation)); + gtk_widget_queue_draw(GTK_WIDGET(item_entry)); + }else{ + entry->scroll_offset += (xoffset - text_area_width) + 1; + } + } + + break; + + case GTK_JUSTIFY_RIGHT: + +/* RIGHT JUSTIFICATION FOR NUMBERS */ + if(entry->text){ + + text_width = gdk_text_width (GTK_WIDGET (item_entry)->style->font, gtk_entry_get_text(entry), strlen(gtk_entry_get_text(entry))); + + entry->scroll_offset= -(text_area_width - text_width) + 1; + if(entry->scroll_offset > 0){ + if(item_entry->text_max_size != 0 && + text_area_width + char_width <= item_entry->text_max_size){ + GTK_WIDGET(item_entry)->allocation.x = + GTK_WIDGET(item_entry)->allocation.x+ + GTK_WIDGET(item_entry)->allocation.width- + (text_area_width+char_width); + GTK_WIDGET(item_entry)->allocation.width = text_area_width + char_width; + gtk_entry_size_allocate(GTK_WIDGET(item_entry), + (GtkAllocation *)(>K_WIDGET(item_entry)->allocation)); + gtk_widget_queue_draw(GTK_WIDGET(item_entry)); + } + else + { + entry->scroll_offset= -(text_area_width + - gdk_text_width (GTK_WIDGET (item_entry)->style->font, gtk_entry_get_text(entry), GTK_EDITABLE(entry)->current_pos)) + 1; + if(entry->scroll_offset < 0) entry->scroll_offset = 0; + } + } + } + else + entry->scroll_offset=0; + + break; + case GTK_JUSTIFY_CENTER: + + if(entry->text){ + + text_width = gdk_text_width (GTK_WIDGET (item_entry)->style->font, gtk_entry_get_text(entry), strlen(gtk_entry_get_text(entry))); + + entry->scroll_offset= -(text_area_width - text_width)/2; + if(entry->scroll_offset > 0){ + if(item_entry->text_max_size != 0 && + text_area_width+char_width<=item_entry->text_max_size){ + GTK_WIDGET(item_entry)->allocation.x=GTK_WIDGET(item_entry)->allocation.x+ + GTK_WIDGET(item_entry)->allocation.width/2- + (text_area_width+char_width)/2; + GTK_WIDGET(item_entry)->allocation.width=text_area_width+char_width; + gtk_entry_size_allocate(GTK_WIDGET(item_entry), + (GtkAllocation *)(>K_WIDGET(item_entry)->allocation)); + gtk_widget_queue_draw(GTK_WIDGET(item_entry)); + } + else + { + entry->scroll_offset= -(text_area_width + - gdk_text_width (GTK_WIDGET (item_entry)->style->font, gtk_entry_get_text(entry), GTK_EDITABLE(entry)->current_pos)) + 1; + if(entry->scroll_offset < 0) entry->scroll_offset = 0; + } + } + } + else + entry->scroll_offset=0; + + break; + + } + +} + +static void +gtk_entry_grow_text (GtkEntry *entry) +{ + gint previous_size; + gint i; + + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + previous_size = entry->text_size; + if (!entry->text_size) + entry->text_size = 128; + else + entry->text_size *= 2; + entry->text = g_realloc (entry->text, entry->text_size * sizeof(GdkWChar)); + entry->char_offset = g_realloc (entry->char_offset, + entry->text_size * sizeof(guint)); + + if (entry->text_length == 0) /* initial allocation */ + { + entry->char_offset[0] = 0; + } + + for (i = previous_size; i < entry->text_size; i++) + entry->text[i] = '\0'; +} + +static void +gtk_entry_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position) +{ + GdkWChar *text; + gint start_pos; + gint end_pos; + gint last_pos; + gint max_length; + gint i; + + guchar *new_text_nt; + gint insertion_length; + GdkWChar *insertion_text; + + GtkEntry *entry; + GtkWidget *widget; + + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_ENTRY (editable)); + + entry = GTK_ENTRY (editable); + widget = GTK_WIDGET (editable); + + if ((entry->text_length == 0) && (entry->use_wchar == FALSE)) + { + if (!GTK_WIDGET_REALIZED (widget)) + gtk_widget_ensure_style (widget); + if ((widget->style) && (widget->style->font->type == GDK_FONT_FONTSET)) + entry->use_wchar = TRUE; + } + + if (new_text_length < 0) + { + new_text_nt = (gchar *)new_text; + new_text_length = strlen (new_text); + if (new_text_length <= 0) return; + } + else if (new_text_length == 0) + { + return; + } + else + { + /* make a null-terminated copy of new_text */ + new_text_nt = g_new (gchar, new_text_length + 1); + memcpy (new_text_nt, new_text, new_text_length); + new_text_nt[new_text_length] = 0; + } + + /* The algorithms here will work as long as, the text size (a + * multiple of 2), fits into a guint16 but we specify a shorter + * maximum length so that if the user pastes a very long text, there + * is not a long hang from the slow X_LOCALE functions. */ + + if (entry->text_max_length == 0) + max_length = 2047; + else + max_length = MIN (2047, entry->text_max_length); + + /* Convert to wide characters */ + insertion_text = g_new (GdkWChar, new_text_length); + if (entry->use_wchar) + insertion_length = gdk_mbstowcs (insertion_text, new_text_nt, + new_text_length); + else + for (insertion_length=0; new_text_nt[insertion_length]; insertion_length++) + insertion_text[insertion_length] = new_text_nt[insertion_length]; + if (new_text_nt != (guchar *)new_text) + g_free (new_text_nt); + + /* Make sure we do not exceed the maximum size of the entry. */ + if (insertion_length + entry->text_length > max_length) + insertion_length = max_length - entry->text_length; + + /* Don't insert anything, if there was nothing to insert. */ + if (insertion_length <= 0) + { + g_free(insertion_text); + return; + } + + /* Make sure we are inserting at integral character position */ + start_pos = *position; + if (start_pos < 0) + start_pos = 0; + else if (start_pos > entry->text_length) + start_pos = entry->text_length; + + end_pos = start_pos + insertion_length; + last_pos = insertion_length + entry->text_length; + + if (editable->selection_start_pos >= *position) + editable->selection_start_pos += insertion_length; + if (editable->selection_end_pos >= *position) + editable->selection_end_pos += insertion_length; + + while (last_pos >= entry->text_size) + gtk_entry_grow_text (entry); + + text = entry->text; + for (i = last_pos - 1; i >= end_pos; i--) + text[i] = text[i- (end_pos - start_pos)]; + for (i = start_pos; i < end_pos; i++) + text[i] = insertion_text[i - start_pos]; + g_free (insertion_text); + + /* Fix up the the character offsets */ + + if (GTK_WIDGET_REALIZED (entry)) + { + gint offset = 0; + + for (i = last_pos; i >= end_pos; i--) + entry->char_offset[i] + = entry->char_offset[i - insertion_length]; + + for (i=start_pos; i<end_pos; i++) + { + entry->char_offset[i] = entry->char_offset[start_pos] + offset; + if (entry->visible) + { + offset += gdk_char_width_wc (GTK_WIDGET (entry)->style->font, + entry->text[i]); + } + else + { + offset += gdk_char_width (GTK_WIDGET (entry)->style->font, '*'); + } + } + for (i = end_pos; i <= last_pos; i++) + entry->char_offset[i] += offset; + } + + entry->text_length += insertion_length; + *position = end_pos; + + entry->text_mb_dirty = 1; + gtk_entry_queue_draw (entry); +} + +static void +gtk_entry_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + GdkWChar *text; + gint deletion_length; + gint i; + + GtkEntry *entry; + + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_ENTRY (editable)); + + entry = GTK_ENTRY (editable); + + if (end_pos < 0) + end_pos = entry->text_length; + + if (editable->selection_start_pos > start_pos) + editable->selection_start_pos -= MIN(end_pos, editable->selection_start_pos) - start_pos; + if (editable->selection_end_pos > start_pos) + editable->selection_end_pos -= MIN(end_pos, editable->selection_end_pos) - start_pos; + + if ((start_pos < end_pos) && + (start_pos >= 0) && + (end_pos <= entry->text_length)) + { + text = entry->text; + deletion_length = end_pos - start_pos; + + /* Fix up the character offsets */ + if (GTK_WIDGET_REALIZED (entry)) + { + gint deletion_width = + entry->char_offset[end_pos] - entry->char_offset[start_pos]; + + for (i = 0 ; i <= entry->text_length - end_pos; i++) + entry->char_offset[start_pos+i] = entry->char_offset[end_pos+i] - deletion_width; + } + + for (i = end_pos; i < entry->text_length; i++) + text[i - deletion_length] = text[i]; + + for (i = entry->text_length - deletion_length; i < entry->text_length; i++) + text[i] = '\0'; + + entry->text_length -= deletion_length; + editable->current_pos = start_pos; + } + + entry->text_mb_dirty = 1; + gtk_entry_queue_draw (entry); +} + +static void +gtk_entry_update_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + gtk_entry_queue_draw (GTK_ENTRY(editable)); +} + +static gchar * +gtk_entry_get_chars (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry; + + g_return_val_if_fail (editable != NULL, NULL); + g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL); + + entry = GTK_ENTRY (editable); + + if (end_pos < 0) + end_pos = entry->text_length; + + start_pos = MIN(entry->text_length, start_pos); + end_pos = MIN(entry->text_length, end_pos); + + if (start_pos <= end_pos) + { + guchar *mbstr; + if (entry->use_wchar) + { + GdkWChar ch; + if (end_pos >= entry->text_size) + gtk_entry_grow_text(entry); + ch = entry->text[end_pos]; + entry->text[end_pos] = 0; + mbstr = gdk_wcstombs (entry->text + start_pos); + entry->text[end_pos] = ch; + return (gchar *)mbstr; + } + else + { + gint i; + mbstr = g_new (gchar, end_pos - start_pos + 1); + for (i=0; i<end_pos-start_pos; i++) + mbstr[i] = entry->text[start_pos + i]; + mbstr[i] = 0; + return (gchar *)mbstr; + } + } + else + return NULL; +} + +static void +gtk_entry_move_cursor (GtkEditable *editable, + gint x, + gint y) +{ + GtkEntry *entry; + entry = GTK_ENTRY (editable); + + /* Horizontal motion */ + if ((gint)editable->current_pos < -x) + editable->current_pos = 0; + else if (editable->current_pos + x > entry->text_length) + editable->current_pos = entry->text_length; + else + editable->current_pos += x; + + /* Ignore vertical motion */ +} + +static void +gtk_move_forward_character (GtkEntry *entry) +{ + gtk_entry_move_cursor (GTK_EDITABLE (entry), 1, 0); +} + +static void +gtk_move_backward_character (GtkEntry *entry) +{ + gtk_entry_move_cursor (GTK_EDITABLE (entry), -1, 0); +} + +static void +gtk_entry_move_word (GtkEditable *editable, + gint n) +{ + while (n-- > 0) + gtk_move_forward_word (GTK_ENTRY (editable)); + while (n++ < 0) + gtk_move_backward_word (GTK_ENTRY (editable)); +} + +static void +gtk_move_forward_word (GtkEntry *entry) +{ + GtkEditable *editable; + GdkWChar *text; + gint i; + + editable = GTK_EDITABLE (entry); + + if (entry->text && (editable->current_pos < entry->text_length)) + { + text = entry->text; + i = editable->current_pos; + + if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) + for (; i < entry->text_length; i++) + { + if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i])) + break; + } + + for (; i < entry->text_length; i++) + { + if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) + break; + } + + editable->current_pos = i; + } +} + +static void +gtk_move_backward_word (GtkEntry *entry) +{ + GtkEditable *editable; + GdkWChar *text; + gint i; + + editable = GTK_EDITABLE (entry); + + if (entry->text && editable->current_pos > 0) + { + text = entry->text; + i = editable->current_pos - 1; + if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) + for (; i >= 0; i--) + { + if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i])) + break; + } + for (; i >= 0; i--) + { + if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) + { + i++; + break; + } + } + + if (i < 0) + i = 0; + + editable->current_pos = i; + } +} + +static void +gtk_entry_move_to_column (GtkEditable *editable, gint column) +{ + GtkEntry *entry; + + entry = GTK_ENTRY (editable); + + if (column < 0 || column > entry->text_length) + editable->current_pos = entry->text_length; + else + editable->current_pos = column; +} + +static void +gtk_move_beginning_of_line (GtkEntry *entry) +{ + gtk_entry_move_to_column (GTK_EDITABLE (entry), 0); +} + +static void +gtk_move_end_of_line (GtkEntry *entry) +{ + gtk_entry_move_to_column (GTK_EDITABLE (entry), -1); +} + +static void +gtk_entry_kill_char (GtkEditable *editable, + gint direction) +{ + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_delete_selection (editable); + else + { + gint old_pos = editable->current_pos; + if (direction >= 0) + { + gtk_entry_move_cursor (editable, 1, 0); + gtk_editable_delete_text (editable, old_pos, editable->current_pos); + } + else + { + gtk_entry_move_cursor (editable, -1, 0); + gtk_editable_delete_text (editable, editable->current_pos, old_pos); + } + } +} + +static void +gtk_delete_forward_character (GtkEntry *entry) +{ + gtk_entry_kill_char (GTK_EDITABLE (entry), 1); +} + +static void +gtk_delete_backward_character (GtkEntry *entry) +{ + gtk_entry_kill_char (GTK_EDITABLE (entry), -1); +} + +static void +gtk_entry_kill_word (GtkEditable *editable, + gint direction) +{ + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_delete_selection (editable); + else + { + gint old_pos = editable->current_pos; + if (direction >= 0) + { + gtk_entry_move_word (editable, 1); + gtk_editable_delete_text (editable, old_pos, editable->current_pos); + } + else + { + gtk_entry_move_word (editable, -1); + gtk_editable_delete_text (editable, editable->current_pos, old_pos); + } + } +} + +static void +gtk_delete_forward_word (GtkEntry *entry) +{ + gtk_entry_kill_word (GTK_EDITABLE (entry), 1); +} + +static void +gtk_delete_backward_word (GtkEntry *entry) +{ + gtk_entry_kill_word (GTK_EDITABLE (entry), -1); +} + +static void +gtk_entry_kill_line (GtkEditable *editable, + gint direction) +{ + gint old_pos = editable->current_pos; + if (direction >= 0) + { + gtk_entry_move_to_column (editable, -1); + gtk_editable_delete_text (editable, old_pos, editable->current_pos); + } + else + { + gtk_entry_move_to_column (editable, 0); + gtk_editable_delete_text (editable, editable->current_pos, old_pos); + } +} + +static void +gtk_delete_line (GtkEntry *entry) +{ + gtk_entry_move_to_column (GTK_EDITABLE (entry), 0); + gtk_entry_kill_line (GTK_EDITABLE (entry), 1); +} + +static void +gtk_delete_to_line_end (GtkEntry *entry) +{ + gtk_editable_delete_text (GTK_EDITABLE(entry), GTK_EDITABLE(entry)->current_pos, entry->text_length); +} + +static void +gtk_select_word (GtkEntry *entry, + guint32 time) +{ + GtkEditable *editable; + gint start_pos; + gint end_pos; + + editable = GTK_EDITABLE (entry); + + gtk_move_backward_word (entry); + start_pos = editable->current_pos; + + gtk_move_forward_word (entry); + end_pos = editable->current_pos; + + editable->has_selection = TRUE; + gtk_entry_set_selection (editable, start_pos, end_pos); + gtk_editable_claim_selection (editable, start_pos != end_pos, time); +} + +static void +gtk_select_line (GtkEntry *entry, + guint32 time) +{ + GtkEditable *editable; + + editable = GTK_EDITABLE (entry); + + editable->has_selection = TRUE; + gtk_entry_set_selection (editable, 0, entry->text_length); + gtk_editable_claim_selection (editable, entry->text_length != 0, time); + + editable->current_pos = editable->selection_end_pos; +} + +static void +gtk_entry_set_selection (GtkEditable *editable, + gint start, + gint end) +{ + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_ENTRY (editable)); + + if (end < 0) + end = GTK_ENTRY (editable)->text_length; + + editable->selection_start_pos = start; + editable->selection_end_pos = end; + + gtk_entry_queue_draw (GTK_ENTRY (editable)); +} + +#ifdef USE_XIM +static void +gtk_entry_update_ic_attr (GtkWidget *widget) +{ + GtkEditable *editable = (GtkEditable *) widget; + GdkICAttributesType mask = 0; + + gdk_ic_get_attr (editable->ic, editable->ic_attr, + GDK_IC_PREEDIT_FOREGROUND | + GDK_IC_PREEDIT_BACKGROUND | + GDK_IC_PREEDIT_FONTSET); + + if (editable->ic_attr->preedit_foreground.pixel != + widget->style->fg[GTK_STATE_NORMAL].pixel) + { + mask |= GDK_IC_PREEDIT_FOREGROUND; + editable->ic_attr->preedit_foreground + = widget->style->fg[GTK_STATE_NORMAL]; + } + if (editable->ic_attr->preedit_background.pixel != + widget->style->base[GTK_STATE_NORMAL].pixel) + { + mask |= GDK_IC_PREEDIT_BACKGROUND; + editable->ic_attr->preedit_background + = widget->style->base[GTK_STATE_NORMAL]; + } + if ((gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION) && + !gdk_font_equal (editable->ic_attr->preedit_fontset, + widget->style->font)) + { + mask |= GDK_IC_PREEDIT_FONTSET; + editable->ic_attr->preedit_fontset = widget->style->font; + } + + if (mask) + gdk_ic_set_attr (editable->ic, editable->ic_attr, mask); +} +#endif /* USE_XIM */ + + diff --git a/gattrib/src/gtkitementry_2_2.c b/gattrib/src/gtkitementry_2_2.c new file mode 100644 index 000000000..9c743f1c5 --- /dev/null +++ b/gattrib/src/gtkitementry_2_2.c @@ -0,0 +1,2448 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include <string.h> + +#include <glib.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <gtk/gtkcelleditable.h> +/* #include <gtk/gtkintl.h> */ +/* #include <gtk/gtkmarshalers.h> */ +/* #include <gtk/gtktextutil.h> */ +#include <gtk/gtkcelllayout.h> + + +#include <pango/pango.h> + +#include "gtkitementry_2_2.h" + +#define MIN_ENTRY_WIDTH 150 +#define DRAW_TIMEOUT 20 +#define INNER_BORDER 0 + +/* Initial size of buffer, in bytes */ +#define MIN_SIZE 16 + +/* Maximum size of text buffer, in bytes */ +#define MAX_SIZE G_MAXUSHORT + +typedef enum { + CURSOR_STANDARD, + CURSOR_DND +} CursorType; + +/* GObject, GtkObject methods + */ +static void gtk_item_entry_class_init (GtkItemEntryClass *klass); +static void gtk_item_entry_init (GtkItemEntry *entry); +static void gtk_item_entry_editable_init (GtkEditableClass *iface); + +/* GtkWidget methods + */ +static void gtk_entry_realize (GtkWidget *widget); +static void gtk_entry_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_entry_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gtk_entry_draw_frame (GtkWidget *widget); +static gint gtk_entry_expose (GtkWidget *widget, + GdkEventExpose *event); +static void gtk_entry_grab_focus (GtkWidget *widget); +static void gtk_entry_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void gtk_entry_direction_changed (GtkWidget *widget, + GtkTextDirection previous_dir); +static void gtk_entry_state_changed (GtkWidget *widget, + GtkStateType previous_state); + +/* GtkEditable method implementations + */ +static void gtk_entry_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position); +static void gtk_entry_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos); + +static void gtk_entry_real_set_position (GtkEditable *editable, + gint position); +static gint gtk_entry_get_position (GtkEditable *editable); + +/* Default signal handlers + */ +static void gtk_entry_real_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position); +static void gtk_entry_real_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos); +static void gtk_entry_move_cursor (GtkEntry *entry, + GtkMovementStep step, + gint count, + gboolean extend_selection); +static void gtk_entry_insert_at_cursor (GtkEntry *entry, + const gchar *str); +static void gtk_entry_delete_from_cursor (GtkEntry *entry, + GtkDeleteType type, + gint count); + +/* IM Context Callbacks + */ +static void gtk_entry_commit_cb (GtkIMContext *context, + const gchar *str, + GtkEntry *entry); +static void gtk_entry_preedit_changed_cb (GtkIMContext *context, + GtkEntry *entry); +static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, + GtkEntry *entry); +static gboolean gtk_entry_delete_surrounding_cb (GtkIMContext *context, + gint offset, + gint n_chars, + GtkEntry *entry); + +/* Internal routines + */ +static void gtk_entry_enter_text (GtkEntry *entry, + const gchar *str); +static void gtk_entry_set_positions (GtkEntry *entry, + gint current_pos, + gint selection_bound); +static void gtk_entry_draw_text (GtkEntry *entry); +static void gtk_entry_draw_cursor (GtkEntry *entry, + CursorType type); +static PangoLayout *gtk_entry_ensure_layout (GtkEntry *entry, + gboolean include_preedit); +static void gtk_entry_queue_draw (GtkEntry *entry); +static void gtk_entry_reset_im_context (GtkEntry *entry); +static void gtk_entry_recompute (GtkEntry *entry); +static void gtk_entry_get_cursor_locations (GtkEntry *entry, + CursorType type, + gint *strong_x, + gint *weak_x); +static void gtk_entry_adjust_scroll (GtkEntry *entry); +static gint gtk_entry_move_visually (GtkEntry *editable, + gint start, + gint count); +static gint gtk_entry_move_logically (GtkEntry *entry, + gint start, + gint count); +static gint gtk_entry_move_forward_word (GtkEntry *entry, + gint start); +static gint gtk_entry_move_backward_word (GtkEntry *entry, + gint start); +static void gtk_entry_delete_whitespace (GtkEntry *entry); +static char * gtk_entry_get_public_chars (GtkEntry *entry, + gint start, + gint end); +static void gtk_entry_update_primary_selection (GtkEntry *entry); +static void gtk_entry_state_changed (GtkWidget *widget, + GtkStateType previous_state); +static void gtk_entry_check_cursor_blink (GtkEntry *entry); +static void gtk_entry_pend_cursor_blink (GtkEntry *entry); +static void get_text_area_size (GtkEntry *entry, + gint *x, + gint *y, + gint *width, + gint *height); +static void get_widget_window_size (GtkEntry *entry, + gint *x, + gint *y, + gint *width, + gint *height); + + +static GtkEntryClass *parent_class = NULL; + +/* =============================== Fcns ========================== */ + +GtkType +gtk_item_entry_get_type (void) +{ + static GtkType item_entry_type = 0; + + if (!item_entry_type) + { + static const GtkTypeInfo item_entry_info = + { + "GtkItemEntry", + sizeof (GtkItemEntry), + sizeof (GtkItemEntryClass), + (GtkClassInitFunc) gtk_item_entry_class_init, + (GtkObjectInitFunc) gtk_item_entry_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + static const GInterfaceInfo item_editable_info = + { + (GInterfaceInitFunc) gtk_item_entry_editable_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + + item_entry_type = gtk_type_unique (GTK_TYPE_ENTRY, &item_entry_info); + + g_type_add_interface_static (item_entry_type, + GTK_TYPE_EDITABLE, + &item_editable_info); + + } + + return item_entry_type; +} + +static void +gtk_item_entry_class_init (GtkItemEntryClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkEntryClass *entry_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + parent_class = gtk_type_class (GTK_TYPE_ENTRY); + entry_class = (GtkEntryClass *) class; + + widget_class->realize = gtk_entry_realize; + widget_class->size_request = gtk_entry_size_request; + widget_class->size_allocate = gtk_entry_size_allocate; + widget_class->expose_event = gtk_entry_expose; + widget_class->grab_focus = gtk_entry_grab_focus; + widget_class->style_set = gtk_entry_style_set; + widget_class->direction_changed = gtk_entry_direction_changed; + widget_class->state_changed = gtk_entry_state_changed; + + entry_class->move_cursor = gtk_entry_move_cursor; + entry_class->insert_at_cursor = gtk_entry_insert_at_cursor; + entry_class->delete_from_cursor = gtk_entry_delete_from_cursor; + +} + +static void +gtk_item_entry_editable_init (GtkEditableClass *iface) +{ + iface->do_insert_text = gtk_entry_insert_text; + iface->do_delete_text = gtk_entry_delete_text; + iface->insert_text = gtk_entry_real_insert_text; + iface->delete_text = gtk_entry_real_delete_text; + iface->set_position = gtk_entry_real_set_position; + iface->get_position = gtk_entry_get_position; +} + +static void +gtk_item_entry_init (GtkItemEntry *entry) +{ + +#ifdef DEBUG + printf("In gtk_item_entry_init. . . . .\n"); +#endif + + entry->justification = GTK_JUSTIFY_LEFT; + entry->text_max_size = 0; + GTK_ENTRY(entry)->has_frame = FALSE; + + g_object_unref(G_OBJECT(GTK_ENTRY(entry)->im_context)); + GTK_ENTRY(entry)->im_context = gtk_im_multicontext_new(); + + /* GTK_ENTRY(entry)->im_context = gtk_im_context_simple_new(); */ + + + g_signal_connect (G_OBJECT(GTK_ENTRY(entry)->im_context), + "commit", + G_CALLBACK(gtk_entry_commit_cb), + entry); + g_signal_connect (G_OBJECT(GTK_ENTRY(entry)->im_context), + "preedit_changed", + G_CALLBACK(gtk_entry_preedit_changed_cb), + entry); + g_signal_connect (G_OBJECT(GTK_ENTRY(entry)->im_context), + "retrieve_surrounding", + G_CALLBACK(gtk_entry_retrieve_surrounding_cb), + entry); + g_signal_connect (G_OBJECT(GTK_ENTRY(entry)->im_context), + "delete_surrounding", + G_CALLBACK(gtk_entry_delete_surrounding_cb), + entry); + +} + +static void +gtk_entry_realize (GtkWidget *widget) +{ + GtkEntry *entry; + GtkEditable *editable; + GdkWindowAttr attributes; + gint attributes_mask; + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (widget); + + attributes.window_type = GDK_WINDOW_CHILD; + + get_widget_window_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height); + + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON3_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK | + GDK_POINTER_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK); + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); + gdk_window_set_user_data (widget->window, entry); + + get_text_area_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height); + + attributes.cursor = gdk_cursor_new (GDK_XTERM); + attributes_mask |= GDK_WA_CURSOR; + + entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (entry->text_area, entry); + + gdk_cursor_unref (attributes.cursor); + + widget->style = gtk_style_attach (widget->style, widget->window); + + gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]); + gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]); + + gdk_window_show (entry->text_area); + + gtk_im_context_set_client_window (entry->im_context, entry->text_area); + + gtk_entry_adjust_scroll (entry); +} + +static void +get_borders (GtkEntry *entry, + gint *xborder, + gint *yborder) +{ + GtkWidget *widget = GTK_WIDGET (entry); + gint focus_width; + gboolean interior_focus; + + gtk_widget_style_get (widget, + "interior-focus", &interior_focus, + "focus-line-width", &focus_width, + NULL); + + if (entry->has_frame) + { + *xborder = widget->style->xthickness; + *yborder = widget->style->ythickness; + } + else + { + *xborder = 0; + *yborder = 0; + } + + if (!interior_focus) + { + *xborder += focus_width; + *yborder += focus_width; + } + +} + +static void +gtk_entry_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkEntry *entry = GTK_ENTRY (widget); + PangoFontMetrics *metrics; + gint xborder, yborder; + PangoContext *context; + + context = gtk_widget_get_pango_context (widget); + metrics = pango_context_get_metrics (context, + widget->style->font_desc, + pango_context_get_language (context)); + + entry->ascent = pango_font_metrics_get_ascent (metrics); + entry->descent = pango_font_metrics_get_descent (metrics); + + get_borders (entry, &xborder, &yborder); + + xborder += INNER_BORDER; + yborder += INNER_BORDER; + + if (entry->width_chars < 0) + requisition->width = MIN_ENTRY_WIDTH + xborder * 2; + else + { + gint char_width = pango_font_metrics_get_approximate_char_width (metrics); + requisition->width = PANGO_PIXELS (char_width) * entry->width_chars + xborder * 2; + } + + requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2; + + pango_font_metrics_unref (metrics); +} + +static void +get_text_area_size (GtkEntry *entry, + gint *x, + gint *y, + gint *width, + gint *height) +{ + gint xborder, yborder; + GtkRequisition requisition; + GtkWidget *widget = GTK_WIDGET (entry); + + gtk_widget_get_child_requisition (widget, &requisition); + + get_borders (entry, &xborder, &yborder); + + if (x) + *x = xborder; + + if (y) + *y = yborder; + + if (width) + *width = GTK_WIDGET (entry)->allocation.width - xborder * 2; + + if (height) + *height = requisition.height - yborder * 2; +} + +static void +get_widget_window_size (GtkEntry *entry, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GtkRequisition requisition; + GtkWidget *widget = GTK_WIDGET (entry); + + gtk_widget_get_child_requisition (widget, &requisition); + + if (x) + *x = widget->allocation.x; + + if (y) + { + if (entry->is_cell_renderer) + *y = widget->allocation.y; + else + *y = widget->allocation.y + (widget->allocation.height - requisition.height) / 2; + } + + if (width) + *width = widget->allocation.width; + + if (height) + { + if (entry->is_cell_renderer) + *height = widget->allocation.height; + else + *height = requisition.height; + } +} + +static void +gtk_entry_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkEntry *entry = GTK_ENTRY (widget); + GtkItemEntry *ientry = GTK_ITEM_ENTRY (widget); + + if(ientry->text_max_size > 0) + allocation->width = MIN(ientry->text_max_size, allocation->width); + + widget->allocation = *allocation; + + if (GTK_WIDGET_REALIZED (widget)) + { + /* We call gtk_widget_get_child_requisition, since we want (for + * backwards compatibility reasons) the realization here to + * be affected by the usize of the entry, if set + */ + gint x, y, width, height; + + get_widget_window_size (entry, &x, &y, &width, &height); + + gdk_window_move_resize (widget->window, + allocation->x, allocation->y, allocation->width, allocation->height); + + get_text_area_size (entry, &x, &y, &width, &height); + + gdk_window_move_resize (entry->text_area, + 0, allocation->height - height, allocation->width, height); + + gtk_entry_recompute (entry); + } +} + +static void +gtk_entry_draw_frame (GtkWidget *widget) +{ +} + +static gint +gtk_entry_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkEntry *entry = GTK_ENTRY (widget); + + if (widget->window == event->window) + gtk_entry_draw_frame (widget); + else if (entry->text_area == event->window) + { + gint area_width, area_height; + + get_text_area_size (entry, NULL, NULL, &area_width, &area_height); + + gdk_draw_rectangle (entry->text_area, + widget->style->bg_gc[GTK_WIDGET_STATE(widget)], + TRUE, + 0, 0, area_width, area_height); + + if ((entry->visible || entry->invisible_char != 0) && + GTK_WIDGET_HAS_FOCUS (widget) && + entry->selection_bound == entry->current_pos && entry->cursor_visible) + gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD); + + if (entry->dnd_position != -1) + gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND); + + gtk_entry_draw_text (GTK_ENTRY (widget)); + } + + return FALSE; +} + +static void +gtk_entry_grab_focus (GtkWidget *widget) +{ + GtkEntry *entry = GTK_ENTRY (widget); + gboolean select_on_focus; + + GTK_WIDGET_CLASS (parent_class)->grab_focus (widget); + + g_object_get (G_OBJECT (gtk_settings_get_default ()), + "gtk-entry-select-on-focus", + &select_on_focus, + NULL); + + if (select_on_focus && entry->editable && !entry->in_click) + gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1); +} + +static void +gtk_entry_direction_changed (GtkWidget *widget, + GtkTextDirection previous_dir) +{ + GtkEntry *entry = GTK_ENTRY (widget); + + gtk_entry_recompute (entry); + + GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir); +} + +static void +gtk_entry_state_changed (GtkWidget *widget, + GtkStateType previous_state) +{ + GtkEntry *entry = GTK_ENTRY (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE (widget)]); + gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]); + } + + if (!GTK_WIDGET_IS_SENSITIVE (widget)) + { + /* Clear any selection */ + gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos); + } + + gtk_widget_queue_clear (widget); +} + +/* GtkEditable method implementations + */ +static void +gtk_entry_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position) +{ + GtkEntry *entry = GTK_ENTRY (editable); + gchar buf[64]; + gchar *text; + + if (*position < 0 || *position > entry->text_length) + *position = entry->text_length; + + g_object_ref (G_OBJECT (editable)); + + if (new_text_length <= 63) + text = buf; + else + text = g_new (gchar, new_text_length + 1); + + text[new_text_length] = '\0'; + strncpy (text, new_text, new_text_length); + + g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position); + + if (new_text_length > 63) + g_free (text); + + g_object_unref (G_OBJECT (editable)); +} + +static void +gtk_entry_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry = GTK_ENTRY (editable); + + if (end_pos < 0 || end_pos > entry->text_length) + end_pos = entry->text_length; + if (start_pos < 0) + start_pos = 0; + if (start_pos > end_pos) + start_pos = end_pos; + + g_object_ref (G_OBJECT (editable)); + + g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos); + + g_object_unref (G_OBJECT (editable)); +} + +static void +gtk_entry_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + GtkEntry *entry = GTK_ENTRY (widget); + + if (previous_style && GTK_WIDGET_REALIZED (widget)) + { + gtk_entry_recompute (entry); + + gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]); + gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]); + } +} + +static void +gtk_entry_real_set_position (GtkEditable *editable, + gint position) +{ + GtkEntry *entry = GTK_ENTRY (editable); + + if (position < 0 || position > entry->text_length) + position = entry->text_length; + + if (position != entry->current_pos || + position != entry->selection_bound) + { + gtk_entry_reset_im_context (entry); + gtk_entry_set_positions (entry, position, position); + } +} + +static gint +gtk_entry_get_position (GtkEditable *editable) +{ + return GTK_ENTRY (editable)->current_pos; +} + + +/* Default signal handlers + */ +static void +gtk_entry_real_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position) +{ + gint index; + gint n_chars; + + GtkEntry *entry = GTK_ENTRY (editable); + + if (new_text_length < 0) + new_text_length = strlen (new_text); + + n_chars = g_utf8_strlen (new_text, new_text_length); + if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length) + { + gdk_beep (); + n_chars = entry->text_max_length - entry->text_length; + new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text; + } + + if (new_text_length + entry->n_bytes + 1 > entry->text_size) + { + while (new_text_length + entry->n_bytes + 1 > entry->text_size) + { + if (entry->text_size == 0) + entry->text_size = MIN_SIZE; + else + { + if (2 * (guint)entry->text_size < MAX_SIZE && + 2 * (guint)entry->text_size > entry->text_size) + entry->text_size *= 2; + else + { + entry->text_size = MAX_SIZE; + if (new_text_length > (gint)entry->text_size - (gint)entry->n_bytes - 1) + { + new_text_length = (gint)entry->text_size - (gint)entry->n_bytes - 1; + new_text_length = g_utf8_find_prev_char (new_text, new_text + new_text_length + 1) - new_text; + n_chars = g_utf8_strlen (new_text, new_text_length); + } + break; + } + } + } + + entry->text = g_realloc (entry->text, entry->text_size); + } + + index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text; + + g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index); + memcpy (entry->text + index, new_text, new_text_length); + + entry->n_bytes += new_text_length; + entry->text_length += n_chars; + + /* NUL terminate for safety and convenience */ + entry->text[entry->n_bytes] = '\0'; + + if (entry->current_pos > *position) + entry->current_pos += n_chars; + + if (entry->selection_bound > *position) + entry->selection_bound += n_chars; + + *position += n_chars; + + gtk_entry_recompute (entry); + + g_signal_emit_by_name (editable, "changed"); + g_object_notify (G_OBJECT (editable), "text"); +} + +static void +gtk_entry_real_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry = GTK_ENTRY (editable); + + if (start_pos < 0) + start_pos = 0; + if (end_pos < 0 || end_pos > entry->text_length) + end_pos = entry->text_length; + + if (start_pos < end_pos) + { + gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text; + gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text; + + g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index); + entry->text_length -= (end_pos - start_pos); + entry->n_bytes -= (end_index - start_index); + + if (entry->current_pos > start_pos) + entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos; + + if (entry->selection_bound > start_pos) + entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos; + /* We might have deleted the selection + */ + gtk_entry_update_primary_selection (entry); + + gtk_entry_recompute (entry); + + g_signal_emit_by_name (editable, "changed"); + g_object_notify (G_OBJECT (editable), "text"); + } +} + +/* Compute the X position for an offset that corresponds to the "more important + * cursor position for that offset. We use this when trying to guess to which + * end of the selection we should go to when the user hits the left or + * right arrow key. + */ +static gint +get_better_cursor_x (GtkEntry *entry, + gint offset) +{ + GtkTextDirection keymap_direction = + (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? + GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry)); + gboolean split_cursor; + + PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); + gint index = g_utf8_offset_to_pointer (entry->text, offset) - entry->text; + + PangoRectangle strong_pos, weak_pos; + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)), + "gtk-split-cursor", &split_cursor, + NULL); + + pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos); + + if (split_cursor) + return strong_pos.x / PANGO_SCALE; + else + return (keymap_direction == widget_direction) ? strong_pos.x / PANGO_SCALE : weak_pos.x / PANGO_SCALE; +} + +static void +gtk_entry_move_cursor (GtkEntry *entry, + GtkMovementStep step, + gint count, + gboolean extend_selection) +{ + gint new_pos = entry->current_pos; + + gtk_entry_reset_im_context (entry); + + if (entry->current_pos != entry->selection_bound && !extend_selection) + { + /* If we have a current selection and aren't extending it, move to the + * start/or end of the selection as appropriate + */ + switch (step) + { + case GTK_MOVEMENT_VISUAL_POSITIONS: + { + gint current_x = get_better_cursor_x (entry, entry->current_pos); + gint bound_x = get_better_cursor_x (entry, entry->selection_bound); + + if (count < 0) + new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound; + else + new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound; + + break; + } + case GTK_MOVEMENT_LOGICAL_POSITIONS: + case GTK_MOVEMENT_WORDS: + if (count < 0) + new_pos = MIN (entry->current_pos, entry->selection_bound); + else + new_pos = MAX (entry->current_pos, entry->selection_bound); + break; + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: + case GTK_MOVEMENT_BUFFER_ENDS: + new_pos = count < 0 ? 0 : entry->text_length; + break; + case GTK_MOVEMENT_DISPLAY_LINES: + case GTK_MOVEMENT_PARAGRAPHS: + case GTK_MOVEMENT_PAGES: + break; + } + } + else + { + switch (step) + { + case GTK_MOVEMENT_LOGICAL_POSITIONS: + new_pos = gtk_entry_move_logically (entry, new_pos, count); + break; + case GTK_MOVEMENT_VISUAL_POSITIONS: + new_pos = gtk_entry_move_visually (entry, new_pos, count); + break; + case GTK_MOVEMENT_WORDS: + while (count > 0) + { + new_pos = gtk_entry_move_forward_word (entry, new_pos); + count--; + } + while (count < 0) + { + new_pos = gtk_entry_move_backward_word (entry, new_pos); + count++; + } + break; + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: + case GTK_MOVEMENT_BUFFER_ENDS: + new_pos = count < 0 ? 0 : entry->text_length; + break; + case GTK_MOVEMENT_DISPLAY_LINES: + case GTK_MOVEMENT_PARAGRAPHS: + case GTK_MOVEMENT_PAGES: + break; + } + } + + if (extend_selection) + gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos); + else + gtk_editable_set_position (GTK_EDITABLE (entry), new_pos); + + gtk_entry_pend_cursor_blink (entry); +} + +static void +gtk_entry_insert_at_cursor (GtkEntry *entry, + const gchar *str) +{ + GtkEditable *editable = GTK_EDITABLE (entry); + gint pos = entry->current_pos; + + if (entry->editable) + { + gtk_entry_reset_im_context (entry); + + gtk_editable_insert_text (editable, str, -1, &pos); + gtk_editable_set_position (editable, pos); + } +} + +static void +gtk_entry_delete_from_cursor (GtkEntry *entry, + GtkDeleteType type, + gint count) +{ + GtkEditable *editable = GTK_EDITABLE (entry); + gint start_pos = entry->current_pos; + gint end_pos = entry->current_pos; + + gtk_entry_reset_im_context (entry); + + if (!entry->editable) + return; + + if (entry->selection_bound != entry->current_pos) + { + gtk_editable_delete_selection (editable); + return; + } + + switch (type) + { + case GTK_DELETE_CHARS: + end_pos = gtk_entry_move_logically (entry, entry->current_pos, count); + gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos)); + break; + case GTK_DELETE_WORDS: + if (count < 0) + { + /* Move to end of current word, or if not on a word, end of previous word */ + end_pos = gtk_entry_move_backward_word (entry, end_pos); + end_pos = gtk_entry_move_forward_word (entry, end_pos); + } + else if (count > 0) + { + /* Move to beginning of current word, or if not on a word, begining of next word */ + start_pos = gtk_entry_move_forward_word (entry, start_pos); + start_pos = gtk_entry_move_backward_word (entry, start_pos); + } + + /* Fall through */ + case GTK_DELETE_WORD_ENDS: + while (count < 0) + { + start_pos = gtk_entry_move_backward_word (entry, start_pos); + count++; + } + while (count > 0) + { + end_pos = gtk_entry_move_forward_word (entry, end_pos); + count--; + } + gtk_editable_delete_text (editable, start_pos, end_pos); + break; + case GTK_DELETE_DISPLAY_LINE_ENDS: + case GTK_DELETE_PARAGRAPH_ENDS: + if (count < 0) + gtk_editable_delete_text (editable, 0, entry->current_pos); + else + gtk_editable_delete_text (editable, entry->current_pos, -1); + break; + case GTK_DELETE_DISPLAY_LINES: + case GTK_DELETE_PARAGRAPHS: + gtk_editable_delete_text (editable, 0, -1); + break; + case GTK_DELETE_WHITESPACE: + gtk_entry_delete_whitespace (entry); + break; + } + + gtk_entry_pend_cursor_blink (entry); +} + +/* IM Context Callbacks + */ + +static void +gtk_entry_commit_cb (GtkIMContext *context, + const gchar *str, + GtkEntry *entry) +{ + +#ifdef DEBUG + printf("In gtk_entry_commit_cb. . . .\n"); +#endif + + gtk_entry_enter_text (entry, str); +} + +static void +gtk_entry_preedit_changed_cb (GtkIMContext *context, + GtkEntry *entry) +{ + gchar *preedit_string; + gint cursor_pos; + +#ifdef DEBUG + printf("In gtk_entry_preedit_changed_cb. . . .\n"); +#endif + + gtk_im_context_get_preedit_string (entry->im_context, + &preedit_string, NULL, + &cursor_pos); + entry->preedit_length = strlen (preedit_string); + cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1)); + entry->preedit_cursor = cursor_pos; + g_free (preedit_string); + + gtk_entry_recompute (entry); +} + +static gboolean +gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, + GtkEntry *entry) +{ + gtk_im_context_set_surrounding (context, + entry->text, + entry->n_bytes, + g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text); + + return TRUE; +} + +static gboolean +gtk_entry_delete_surrounding_cb (GtkIMContext *slave, + gint offset, + gint n_chars, + GtkEntry *entry) +{ + gtk_editable_delete_text (GTK_EDITABLE (entry), + entry->current_pos + offset, + entry->current_pos + offset + n_chars); + + return TRUE; +} + + +/* Internal functions + */ + +/* Used for im_commit_cb and inserting Unicode chars */ +static void +gtk_entry_enter_text (GtkEntry *entry, + const gchar *str) +{ + GtkEditable *editable = GTK_EDITABLE (entry); + gint tmp_pos; + +#ifdef DEBUG + printf("In gtk_entry_enter_text, inserting text . . . \n"); +#endif + + if (gtk_editable_get_selection_bounds (editable, NULL, NULL)) + gtk_editable_delete_selection (editable); + else + { + if (entry->overwrite_mode) + gtk_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1); + } + + tmp_pos = entry->current_pos; + gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos); + gtk_editable_set_position (editable, tmp_pos); +} + +/* All changes to entry->current_pos and entry->selection_bound + * should go through this function. + */ +static void +gtk_entry_set_positions (GtkEntry *entry, + gint current_pos, + gint selection_bound) +{ + gboolean changed = FALSE; + + g_object_freeze_notify (G_OBJECT (entry)); + + if (current_pos != -1 && + entry->current_pos != current_pos) + { + entry->current_pos = current_pos; + changed = TRUE; + + g_object_notify (G_OBJECT (entry), "cursor_position"); + } + + if (selection_bound != -1 && + entry->selection_bound != selection_bound) + { + entry->selection_bound = selection_bound; + changed = TRUE; + + g_object_notify (G_OBJECT (entry), "selection_bound"); + } + + g_object_thaw_notify (G_OBJECT (entry)); + + if (changed) + gtk_entry_recompute (entry); +} + +static void +gtk_entry_reset_layout (GtkEntry *entry) +{ + if (entry->cached_layout) + { + g_object_unref (G_OBJECT (entry->cached_layout)); + entry->cached_layout = NULL; + } +} + +static void +update_im_cursor_location (GtkEntry *entry) +{ + GdkRectangle area; + gint strong_x; + gint strong_xoffset; + gint x, y, area_width, area_height; + + gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL) +; + get_text_area_size (entry, &x, &y, &area_width, &area_height); + + strong_xoffset = strong_x - entry->scroll_offset; + if (strong_xoffset < 0) + { + strong_xoffset = 0; + } + else if (strong_xoffset > area_width) + { + strong_xoffset = area_width; + } + area.x = x + strong_xoffset; + area.y = y + area_height; + area.width = area_width; + area.height = area_height; + + gtk_im_context_set_cursor_location (entry->im_context, &area); +} + +static gboolean +recompute_idle_func (gpointer data) +{ + GtkEntry *entry; + + GDK_THREADS_ENTER (); + + entry = GTK_ENTRY (data); + + gtk_entry_adjust_scroll (entry); + gtk_entry_queue_draw (entry); + + entry->recompute_idle = FALSE; + + update_im_cursor_location (entry); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +gtk_entry_recompute (GtkEntry *entry) +{ + gtk_entry_reset_layout (entry); + gtk_entry_check_cursor_blink (entry); + + + if (!entry->recompute_idle) + { + entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */ + recompute_idle_func, entry, NULL); + } +} + +static void +append_char (GString *str, + gunichar ch, + gint count) +{ + gint i; + gint char_len; + gchar buf[7]; + + char_len = g_unichar_to_utf8 (ch, buf); + + i = 0; + while (i < count) + { + g_string_append_len (str, buf, char_len); + ++i; + } +} + +static PangoLayout * +gtk_entry_create_layout (GtkEntry *entry, + gboolean include_preedit) +{ + PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL); + PangoAttrList *tmp_attrs = pango_attr_list_new (); + + gchar *preedit_string = NULL; + gint preedit_length = 0; + PangoAttrList *preedit_attrs = NULL; + + pango_layout_set_single_paragraph_mode (layout, TRUE); + + if (include_preedit) + { + gtk_im_context_get_preedit_string (entry->im_context, + &preedit_string, &preedit_attrs, NULL); + preedit_length = entry->preedit_length; + } + + if (preedit_length) + { + GString *tmp_string = g_string_new (NULL); + + gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text; + + if (entry->visible) + { + g_string_prepend_len (tmp_string, entry->text, entry->n_bytes); + g_string_insert (tmp_string, cursor_index, preedit_string); + } + else + { + gint ch_len; + gint preedit_len_chars; + gunichar invisible_char; + + ch_len = g_utf8_strlen (entry->text, entry->n_bytes); + preedit_len_chars = g_utf8_strlen (preedit_string, -1); + ch_len += preedit_len_chars; + + if (entry->invisible_char != 0) + invisible_char = entry->invisible_char; + else + invisible_char = ' '; /* just pick a char */ + + append_char (tmp_string, invisible_char, ch_len); + + /* Fix cursor index to point to invisible char corresponding + * to the preedit, fix preedit_length to be the length of + * the invisible chars representing the preedit + */ + cursor_index = + g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) - + tmp_string->str; + preedit_length = + preedit_len_chars * + g_unichar_to_utf8 (invisible_char, NULL); + } + + pango_layout_set_text (layout, tmp_string->str, tmp_string->len); + + pango_attr_list_splice (tmp_attrs, preedit_attrs, + cursor_index, preedit_length); + + g_string_free (tmp_string, TRUE); + } + else + { + if (entry->visible) + { + pango_layout_set_text (layout, entry->text, entry->n_bytes); + } + else + { + GString *str = g_string_new (NULL); + gunichar invisible_char; + + if (entry->invisible_char != 0) + invisible_char = entry->invisible_char; + else + invisible_char = ' '; /* just pick a char */ + + append_char (str, invisible_char, entry->text_length); + pango_layout_set_text (layout, str->str, str->len); + g_string_free (str, TRUE); + } + } + + pango_layout_set_attributes (layout, tmp_attrs); + + if (preedit_string) + g_free (preedit_string); + if (preedit_attrs) + pango_attr_list_unref (preedit_attrs); + + pango_attr_list_unref (tmp_attrs); + + return layout; +} + +static PangoLayout * +gtk_entry_ensure_layout (GtkEntry *entry, + gboolean include_preedit) +{ + if (entry->preedit_length > 0 && + !include_preedit != !entry->cache_includes_preedit) + gtk_entry_reset_layout (entry); + + if (!entry->cached_layout) + { + entry->cached_layout = gtk_entry_create_layout (entry, include_preedit); + entry->cache_includes_preedit = include_preedit; + } + + return entry->cached_layout; +} + +static void +get_layout_position (GtkEntry *entry, + gint *x, + gint *y) +{ + PangoLayout *layout; + PangoRectangle logical_rect; + gint area_width, area_height; + gint y_pos; + PangoLayoutLine *line; + + layout = gtk_entry_ensure_layout (entry, TRUE); + + get_text_area_size (entry, NULL, NULL, &area_width, &area_height); + + area_height = PANGO_SCALE * (area_height); + + line = pango_layout_get_lines (layout)->data; + pango_layout_line_get_extents (line, NULL, &logical_rect); + + /* Align primarily for locale's ascent/descent */ + + y_pos = ((area_height - entry->ascent - entry->descent) / 2 + + entry->ascent + logical_rect.y); + + + /* Now see if we need to adjust to fit in actual drawn string */ + + if (logical_rect.height > area_height) + y_pos = (area_height - logical_rect.height) / 2; + else if (y_pos < 0) + y_pos = 0; + else if (y_pos + logical_rect.height > area_height) + y_pos = area_height - logical_rect.height; + + y_pos = y_pos / PANGO_SCALE; + + if (x) + *x = - entry->scroll_offset; + + if (y) + *y = y_pos; +} + +static void +gtk_entry_draw_text (GtkEntry *entry) +{ + GtkWidget *widget; + PangoLayoutLine *line; + + if (!entry->visible && entry->invisible_char == 0) + return; + + if (GTK_WIDGET_DRAWABLE (entry)) + { + PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); + gint area_width, area_height; + + gint x, y; + gint start_pos, end_pos; + + widget = GTK_WIDGET (entry); + + get_layout_position (entry, &x, &y); + + get_text_area_size (entry, NULL, NULL, &area_width, &area_height); + + + gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], + x, y, + layout); + + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos)) + { + gint *ranges; + gint n_ranges, i; + PangoRectangle logical_rect; + const gchar *text = pango_layout_get_text (layout); + gint start_index = g_utf8_offset_to_pointer (text, start_pos) - text; + gint end_index = g_utf8_offset_to_pointer (text, end_pos) - text; + GdkRegion *clip_region = gdk_region_new (); + GdkGC *text_gc; + GdkGC *selection_gc; + + line = pango_layout_get_lines (layout)->data; + + pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); + + pango_layout_get_extents (layout, NULL, &logical_rect); + + if (GTK_WIDGET_HAS_FOCUS (entry)) + { + selection_gc = widget->style->base_gc [GTK_STATE_SELECTED]; + text_gc = widget->style->text_gc [GTK_STATE_SELECTED]; + } + else + { + selection_gc = widget->style->base_gc [GTK_STATE_ACTIVE]; + text_gc = widget->style->text_gc [GTK_STATE_ACTIVE]; + } + + for (i=0; i < n_ranges; i++) + { + GdkRectangle rect; + + rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE; + rect.y = y; + rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE; + rect.height = logical_rect.height / PANGO_SCALE; + + gdk_draw_rectangle (entry->text_area, selection_gc, TRUE, + rect.x, rect.y, rect.width, rect.height); + + gdk_region_union_with_rect (clip_region, &rect); + } + + gdk_gc_set_clip_region (text_gc, clip_region); + gdk_draw_layout (entry->text_area, text_gc, + x, y, + layout); + gdk_gc_set_clip_region (text_gc, NULL); + + gdk_region_destroy (clip_region); + g_free (ranges); + } + } +} + +/* + * From _gtk_get_insertion_cursor_gc + */ + +typedef struct _CursorInfo CursorInfo; + +struct _CursorInfo +{ + GType for_type; + GdkGC *primary_gc; + GdkGC *secondary_gc; +}; + +static GdkGC * +make_cursor_gc (GtkWidget *widget, + const gchar *property_name, + GdkColor *fallback) +{ + GdkGCValues gc_values; + GdkGCValuesMask gc_values_mask; + GdkColor *cursor_color; + + gtk_widget_style_get (widget, property_name, &cursor_color, NULL); + + gc_values_mask = GDK_GC_FOREGROUND; + if (cursor_color) + { + gc_values.foreground = *cursor_color; + gdk_color_free (cursor_color); + } + else + gc_values.foreground = *fallback; + + gdk_rgb_find_color (widget->style->colormap, &gc_values.foreground); + return gtk_gc_get (widget->style->depth, widget->style->colormap, + &gc_values, gc_values_mask); +} + +static GdkGC * +_gtkextra_get_insertion_cursor_gc (GtkWidget *widget, + gboolean is_primary) +{ + CursorInfo *cursor_info; + + cursor_info = g_object_get_data (G_OBJECT (widget->style), "gtk-style-cursor-info"); + if (!cursor_info) + { + cursor_info = g_new (CursorInfo, 1); + g_object_set_data (G_OBJECT (widget->style), "gtk-style-cursor-info", cursor_info); + cursor_info->primary_gc = NULL; + cursor_info->secondary_gc = NULL; + cursor_info->for_type = G_TYPE_INVALID; + } + + /* We have to keep track of the type because gtk_widget_style_get() + * can return different results when called on the same property and + * same style but for different widgets. :-(. That is, + * GtkEntry::cursor-color = "red" in a style will modify the cursor + * color for entries but not for text view. + */ + if (cursor_info->for_type != G_OBJECT_TYPE (widget)) + { + cursor_info->for_type = G_OBJECT_TYPE (widget); + if (cursor_info->primary_gc) + { + gtk_gc_release (cursor_info->primary_gc); + cursor_info->primary_gc = NULL; + } + if (cursor_info->secondary_gc) + { + gtk_gc_release (cursor_info->secondary_gc); + cursor_info->secondary_gc = NULL; + } + } + + if (is_primary) + { + if (!cursor_info->primary_gc) + cursor_info->primary_gc = make_cursor_gc (widget, + "cursor-color", + &widget->style->black); + + return g_object_ref (cursor_info->primary_gc); + } + else + { + static GdkColor gray = { 0, 0x8888, 0x8888, 0x8888 }; + + if (!cursor_info->secondary_gc) + cursor_info->secondary_gc = make_cursor_gc (widget, + "secondary-cursor-color", + &gray); + + return g_object_ref (cursor_info->secondary_gc); + } +} + +/* + * From _gtk_draw_insertion_cursor + */ +static void +_gtkextra_draw_insertion_cursor (GtkWidget *widget, + GdkDrawable *drawable, + GdkGC *gc, + GdkRectangle *location, + GtkTextDirection direction, + gboolean draw_arrow) +{ + gint stem_width; + gint arrow_width; + gint x, y; + gint i; + gfloat cursor_aspect_ratio; + gint offset; + + g_return_if_fail (direction != GTK_TEXT_DIR_NONE); + + gtk_widget_style_get (widget, "cursor-aspect-ratio", &cursor_aspect_ratio, NULL); + + stem_width = location->height * cursor_aspect_ratio + 1; + arrow_width = stem_width + 1; + + /* put (stem_width % 2) on the proper side of the cursor */ + if (direction == GTK_TEXT_DIR_LTR) + offset = stem_width / 2; + else + offset = stem_width - stem_width / 2; + + for (i = 0; i < stem_width; i++) + gdk_draw_line (drawable, gc, + location->x + i - offset, location->y, + location->x + i - offset, location->y + location->height - 1); + + if (draw_arrow) + { + if (direction == GTK_TEXT_DIR_RTL) + { + x = location->x - offset - 1; + y = location->y + location->height - arrow_width * 2 - arrow_width + 1; + + for (i = 0; i < arrow_width; i++) + { + gdk_draw_line (drawable, gc, + x, y + i + 1, + x, y + 2 * arrow_width - i - 1); + x --; + } + } + else if (direction == GTK_TEXT_DIR_LTR) + { + x = location->x + stem_width - offset; + y = location->y + location->height - arrow_width * 2 - arrow_width + 1; + + for (i = 0; i < arrow_width; i++) + { + gdk_draw_line (drawable, gc, + x, y + i + 1, + x, y + 2 * arrow_width - i - 1); + x++; + } + } + } +} + +static void +gtk_entry_draw_cursor (GtkEntry *entry, + CursorType type) +{ + GtkTextDirection keymap_direction = + (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? + GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry)); + + if (GTK_WIDGET_DRAWABLE (entry) && GTK_ENTRY(entry)->cursor_visible) + { + GtkWidget *widget = GTK_WIDGET (entry); + GdkRectangle cursor_location; + gboolean split_cursor; + + gint xoffset = INNER_BORDER - entry->scroll_offset; + gint strong_x, weak_x; + gint text_area_height; + GtkTextDirection dir1 = GTK_TEXT_DIR_NONE; + GtkTextDirection dir2 = GTK_TEXT_DIR_NONE; + gint x1 = 0; + gint x2 = 0; + GdkGC *gc; + + gdk_window_get_size (entry->text_area, NULL, &text_area_height); + + gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x); + + g_object_get (gtk_widget_get_settings (widget), + "gtk-split-cursor", &split_cursor, + NULL); + + dir1 = widget_direction; + + if (split_cursor) + { + x1 = strong_x; + + if (weak_x != strong_x) + { + dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR; + x2 = weak_x; + } + } + else + { + if (keymap_direction == widget_direction) + x1 = strong_x; + else + x1 = weak_x; + } + + cursor_location.x = xoffset + x1; + cursor_location.y = INNER_BORDER; + cursor_location.width = 0; + cursor_location.height = text_area_height - 2 * INNER_BORDER ; + + gc = _gtkextra_get_insertion_cursor_gc (widget, TRUE); + _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc, + &cursor_location, dir1, + dir2 != GTK_TEXT_DIR_NONE); + g_object_unref (gc); + + if (dir2 != GTK_TEXT_DIR_NONE) + { + cursor_location.x = xoffset + x2; + gc = _gtkextra_get_insertion_cursor_gc (widget, FALSE); + _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc, + &cursor_location, dir2, + TRUE); + g_object_unref (gc); + } + } +} + +static void +gtk_entry_queue_draw (GtkEntry *entry) +{ + if (GTK_WIDGET_REALIZED (entry)) + gdk_window_invalidate_rect (entry->text_area, NULL, FALSE); +} + +static void +gtk_entry_reset_im_context (GtkEntry *entry) +{ + if (entry->need_im_reset) + { + entry->need_im_reset = 0; + gtk_im_context_reset (entry->im_context); + } +} + +static void +gtk_entry_get_cursor_locations (GtkEntry *entry, + CursorType type, + gint *strong_x, + gint *weak_x) +{ + PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); + const gchar *text; + PangoRectangle strong_pos, weak_pos; + gint index; + + if (type == CURSOR_STANDARD) + { + text = pango_layout_get_text (layout); + index = g_utf8_offset_to_pointer (text, entry->current_pos + entry->preedit_cursor) - text; + } + else /* type == CURSOR_DND */ + { + index = g_utf8_offset_to_pointer (entry->text, entry->dnd_position) - entry->text; + if (entry->dnd_position > entry->current_pos) + index += entry->preedit_length; + } + + pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos); + + if (strong_x) + *strong_x = strong_pos.x / PANGO_SCALE; + + if (weak_x) + *weak_x = weak_pos.x / PANGO_SCALE; +} + +static void +gtk_entry_adjust_scroll (GtkEntry *entry) +{ + gint min_offset, max_offset; + gint text_area_width; + gint strong_x, weak_x; + PangoLayout *layout; + PangoLayoutLine *line; + PangoRectangle logical_rect; + GtkItemEntry *item_entry; + gint text_width; + + if (!GTK_WIDGET_REALIZED (entry)) + return; + + item_entry = GTK_ITEM_ENTRY(entry); + + gdk_window_get_size (entry->text_area, &text_area_width, NULL); + text_area_width -= 2 * INNER_BORDER; + + layout = gtk_entry_ensure_layout (entry, TRUE); + line = pango_layout_get_lines (layout)->data; + + pango_layout_line_get_extents (line, NULL, &logical_rect); + text_width = logical_rect.width / PANGO_SCALE + 2; /* 2 for cursor */ + + gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x); + + /* Display as much text as we can */ + + if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_LTR) + { + entry->scroll_offset = 0; + switch(item_entry->justification){ + + case GTK_JUSTIFY_FILL: + case GTK_JUSTIFY_LEFT: + +/* LEFT JUSTIFICATION */ + + strong_x -= entry->scroll_offset; + if (strong_x < 0) + entry->scroll_offset += strong_x; + else if (strong_x > text_area_width){ + if(item_entry->text_max_size != 0 && + text_area_width + 2 <= item_entry->text_max_size){ + GtkAllocation allocation; + allocation = GTK_WIDGET(entry)->allocation; + allocation.width += text_width - text_area_width; + entry->scroll_offset = 0; + gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation); + }else{ + entry->scroll_offset += (strong_x - text_area_width) + 1; + } + } + + break; + + case GTK_JUSTIFY_RIGHT: + + /* RIGHT JUSTIFICATION FOR NUMBERS */ + if(entry->text){ + + entry->scroll_offset= -(text_area_width - text_width) + 1; + if(entry->scroll_offset > 0){ + if(item_entry->text_max_size != 0 && + text_area_width + 2 <= item_entry->text_max_size){ + GtkAllocation allocation; + allocation = GTK_WIDGET(entry)->allocation; + allocation.x -= text_width - text_area_width; + allocation.width += text_width - text_area_width; + entry->scroll_offset = 0; + gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation); + } + else + { + entry->scroll_offset= -(text_area_width - strong_x) + 1; + if(entry->scroll_offset < 0) entry->scroll_offset = 0; + } + } + } + else + entry->scroll_offset=0; + + break; + case GTK_JUSTIFY_CENTER: + + if(entry->text){ + + entry->scroll_offset= -(text_area_width - text_width)/2; + if(entry->scroll_offset > 0){ + if(item_entry->text_max_size != 0 && + text_area_width+1<=item_entry->text_max_size){ + GtkAllocation allocation; + allocation = GTK_WIDGET(entry)->allocation; + allocation.x += (text_area_width/2 - text_width/2); + allocation.width += text_width - text_area_width; + entry->scroll_offset = 0; + gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation); + } + else + { + entry->scroll_offset= -(text_area_width - strong_x) + 1; + if(entry->scroll_offset < 0) entry->scroll_offset = 0; + } + } + } + else + entry->scroll_offset=0; + + break; + + } + + } + else + { + max_offset = text_width - text_area_width; + min_offset = MIN (0, max_offset); + entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset); + } + + g_object_notify (G_OBJECT (entry), "scroll_offset"); +} + +static gint +gtk_entry_move_visually (GtkEntry *entry, + gint start, + gint count) +{ + gint index; + PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); + const gchar *text; + + text = pango_layout_get_text (layout); + + index = g_utf8_offset_to_pointer (text, start) - text; + + while (count != 0) + { + int new_index, new_trailing; + gboolean split_cursor; + gboolean strong; + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)), + "gtk-split-cursor", &split_cursor, + NULL); + + if (split_cursor) + strong = TRUE; + else + { + GtkTextDirection keymap_direction = + (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? + GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + + strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (entry)); + } + + if (count > 0) + { + pango_layout_move_cursor_visually (layout, strong, index, 0, 1, &new_index, &new_trailing); + count--; + } + else + { + pango_layout_move_cursor_visually (layout, strong, index, 0, -1, &new_index, &new_trailing); + count++; + } + + if (new_index < 0 || new_index == G_MAXINT) + break; + + index = new_index; + + while (new_trailing--) + index = g_utf8_next_char (entry->text + new_index) - entry->text; + } + + return g_utf8_pointer_to_offset (text, text + index); +} + +static gint +gtk_entry_move_logically (GtkEntry *entry, + gint start, + gint count) +{ + gint new_pos = start; + + /* Prevent any leak of information */ + if (!entry->visible) + { + new_pos = CLAMP (start + count, 0, entry->text_length); + } + else if (entry->text) + { + PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); + PangoLogAttr *log_attrs; + gint n_attrs; + + pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); + + while (count > 0 && new_pos < entry->text_length) + { + do + new_pos++; + while (new_pos < entry->text_length && !log_attrs[new_pos].is_cursor_position); + + count--; + } + while (count < 0 && new_pos > 0) + { + do + new_pos--; + while (new_pos > 0 && !log_attrs[new_pos].is_cursor_position); + + count++; + } + + g_free (log_attrs); + } + + return new_pos; +} + +static gint +gtk_entry_move_forward_word (GtkEntry *entry, + gint start) +{ + gint new_pos = start; + + /* Prevent any leak of information */ + if (!entry->visible) + { + new_pos = entry->text_length; + } + else if (entry->text && (new_pos < entry->text_length)) + { + PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); + PangoLogAttr *log_attrs; + gint n_attrs; + + pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); + + /* Find the next word end */ + new_pos++; + while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end) + new_pos++; + + g_free (log_attrs); + } + + return new_pos; +} + + +static gint +gtk_entry_move_backward_word (GtkEntry *entry, + gint start) +{ + gint new_pos = start; + + /* Prevent any leak of information */ + if (!entry->visible) + { + new_pos = 0; + } + else if (entry->text && start > 0) + { + PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); + PangoLogAttr *log_attrs; + gint n_attrs; + + pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); + + new_pos = start - 1; + + /* Find the previous word beginning */ + while (new_pos > 0 && !log_attrs[new_pos].is_word_start) + new_pos--; + + g_free (log_attrs); + } + + return new_pos; +} + +static void +gtk_entry_delete_whitespace (GtkEntry *entry) +{ + PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); + PangoLogAttr *log_attrs; + gint n_attrs; + gint start, end; + + pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); + + start = end = entry->current_pos; + + while (start > 0 && log_attrs[start-1].is_white) + start--; + + while (end < n_attrs && log_attrs[end].is_white) + end++; + + g_free (log_attrs); + + if (start != end) + gtk_editable_delete_text (GTK_EDITABLE (entry), start, end); +} + + +/* + * Like gtk_editable_get_chars, but if the editable is not + * visible, return asterisks; also convert result to UTF-8. + */ +static char * +gtk_entry_get_public_chars (GtkEntry *entry, + gint start, + gint end) +{ + if (end < 0) + end = entry->text_length; + + if (entry->visible) + return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end); + else + { + gchar *str; + gint i; + gint n_chars = end - start; + + str = g_malloc (n_chars + 1); + for (i = 0; i < n_chars; i++) + str[i] = '*'; + str[i] = '\0'; + + return str; + } + +} + +static void +primary_get_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + GtkEntry *entry = GTK_ENTRY (data); + gint start, end; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) + { + gchar *str = gtk_entry_get_public_chars (entry, start, end); + gtk_selection_data_set_text (selection_data, str, -1); + g_free (str); + } +} + +static void +primary_clear_cb (GtkClipboard *clipboard, + gpointer data) +{ + GtkEntry *entry = GTK_ENTRY (data); + + gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos); +} + +static void +gtk_entry_update_primary_selection (GtkEntry *entry) +{ + static const GtkTargetEntry targets[] = { + { "UTF8_STRING", 0, 0 }, + { "STRING", 0, 0 }, + { "TEXT", 0, 0 }, + { "COMPOUND_TEXT", 0, 0 } + }; + + GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); + gint start, end; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) + { + if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets), + primary_get_cb, primary_clear_cb, G_OBJECT (entry))) + primary_clear_cb (clipboard, entry); + } + else + { + if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry)) + gtk_clipboard_clear (clipboard); + } +} + +/* Public API + */ + +GtkWidget* +gtk_item_entry_new (void) +{ + +#if 0 + /* old stuff removed by SDB */ + GtkWidget *widget; + + widget = GTK_WIDGET (gtk_type_new (GTK_TYPE_ITEM_ENTRY)); + return widget; +#endif + + /* Copied from GtkEntry by SDB */ + return g_object_new (GTK_TYPE_ITEM_ENTRY, NULL); + +} + +GtkWidget* +gtk_item_entry_new_with_max_length (gint max) +{ + GtkItemEntry *entry; + + entry = gtk_type_new (GTK_TYPE_ITEM_ENTRY); + gtk_entry_set_max_length(GTK_ENTRY(entry), max); + + return GTK_WIDGET (entry); +} + +void +gtk_item_entry_set_text (GtkItemEntry *entry, + const gchar *text, + GtkJustification justification) +{ + gint tmp_pos; + + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + g_return_if_fail (text != NULL); + + entry->justification = justification; + + /* Actually setting the text will affect the cursor and selection; + * if the contents don't actually change, this will look odd to the user. + */ + if (strcmp (GTK_ENTRY(entry)->text, text) == 0) + return; + + if (GTK_ENTRY(entry)->recompute_idle){ + g_source_remove (GTK_ENTRY(entry)->recompute_idle); + GTK_ENTRY(entry)->recompute_idle = 0; + } + if (GTK_ENTRY(entry)->blink_timeout){ + g_source_remove (GTK_ENTRY(entry)->blink_timeout); + GTK_ENTRY(entry)->blink_timeout = 0; + } + + gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1); + + tmp_pos = 0; + gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos); +} + +/** + * gtk_entry_get_layout_offsets: + * @entry: a #GtkEntry + * @x: location to store X offset of layout, or %NULL + * @y: location to store Y offset of layout, or %NULL + * + * + * Obtains the position of the #PangoLayout used to render text + * in the entry, in widget coordinates. Useful if you want to line + * up the text in an entry with some other text, e.g. when using the + * entry to implement editable cells in a sheet widget. + * + * Also useful to convert mouse events into coordinates inside the + * #PangoLayout, e.g. to take some action if some part of the entry text + * is clicked. + * + * Note that as the user scrolls around in the entry the offsets will + * change; you'll need to connect to the "notify::scroll_offset" + * signal to track this. Remember when using the #PangoLayout + * functions you need to convert to and from pixels using + * PANGO_PIXELS() or #PANGO_SCALE. + * + * Keep in mind that the layout text may contain a preedit string, so + * gtk_entry_layout_index_to_text_index() and + * gtk_entry_text_index_to_layout_index() are needed to convert byte + * indices in the layout to byte indices in the entry contents. + * + **/ +void +gtk_item_entry_get_layout_offsets (GtkItemEntry *entry, + gint *x, + gint *y) +{ + gint text_area_x, text_area_y; + + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + /* this gets coords relative to text area */ + get_layout_position (GTK_ENTRY(entry), x, y); + + /* convert to widget coords */ + get_text_area_size (GTK_ENTRY(entry), &text_area_x, &text_area_y, NULL, NULL); + + if (x) + *x += text_area_x; + + if (y) + *y += text_area_y; +} + +void +gtk_item_entry_set_justification(GtkItemEntry *entry, GtkJustification just) +{ + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + entry->justification = just; +} + + +/* We display the cursor when + * + * - the selection is empty, AND + * - the widget has focus + */ + +#define CURSOR_ON_MULTIPLIER 0.66 +#define CURSOR_OFF_MULTIPLIER 0.34 +#define CURSOR_PEND_MULTIPLIER 1.0 + +static gboolean +cursor_blinks (GtkEntry *entry) +{ + GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry)); + gboolean blink; + + if (GTK_WIDGET_HAS_FOCUS (entry) && + entry->selection_bound == entry->current_pos) + { + g_object_get (G_OBJECT (settings), "gtk-cursor-blink", &blink, NULL); + return blink; + } + else + return FALSE; +} + +static gint +get_cursor_time (GtkEntry *entry) +{ + GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry)); + gint time; + + g_object_get (G_OBJECT (settings), "gtk-cursor-blink-time", &time, NULL); + + return time; +} + +static void +show_cursor (GtkEntry *entry) +{ + if (!entry->cursor_visible) + { + entry->cursor_visible = TRUE; + + if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos) + gtk_widget_queue_draw (GTK_WIDGET (entry)); + } +} + +static void +hide_cursor (GtkEntry *entry) +{ + if (entry->cursor_visible) + { + entry->cursor_visible = FALSE; + + if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos) + gtk_widget_queue_draw (GTK_WIDGET (entry)); + } +} + +/* + * Blink! + */ +static gint +blink_cb (gpointer data) +{ + GtkEntry *entry; + + GDK_THREADS_ENTER (); + + entry = GTK_ENTRY (data); + + g_assert (GTK_WIDGET_HAS_FOCUS (entry)); + g_assert (entry->selection_bound == entry->current_pos); + + if (entry->cursor_visible) + { + hide_cursor (entry); + entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER, + blink_cb, + entry); + } + else + { + show_cursor (entry); + entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER, + blink_cb, + entry); + } + + GDK_THREADS_LEAVE (); + + /* Remove ourselves */ + return FALSE; +} + +static void +gtk_entry_check_cursor_blink (GtkEntry *entry) +{ + if (cursor_blinks (entry)) + { + if (!entry->blink_timeout) + { + entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER, + blink_cb, + entry); + show_cursor (entry); + } + } + else + { + if (entry->blink_timeout) + { + gtk_timeout_remove (entry->blink_timeout); + entry->blink_timeout = 0; + } + + entry->cursor_visible = TRUE; + } + +} + +static void +gtk_entry_pend_cursor_blink (GtkEntry *entry) +{ + if (cursor_blinks (entry)) + { + if (entry->blink_timeout != 0) + gtk_timeout_remove (entry->blink_timeout); + + entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER, + blink_cb, + entry); + show_cursor (entry); + } +} + +void +gtk_item_entry_set_cursor_visible(GtkItemEntry *entry, gboolean visible) +{ + g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); + + GTK_ENTRY(entry)->cursor_visible = visible; +} + +gboolean +gtk_item_entry_get_cursor_visible(GtkItemEntry *entry) +{ + g_return_val_if_fail (GTK_IS_ITEM_ENTRY (entry), FALSE); + + return(GTK_ENTRY(entry)->cursor_visible); +} diff --git a/gattrib/src/gtksheet_1_2.c b/gattrib/src/gtksheet_1_2.c new file mode 100644 index 000000000..62230397e --- /dev/null +++ b/gattrib/src/gtksheet_1_2.c @@ -0,0 +1,8465 @@ +/* GtkSheet widget for Gtk+. + * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar> + * + * Based on GtkClist widget by Jay Painter, but major changes. + * Memory allocation routines inspired on SC (Spreadsheet Calculator) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#include <glib.h> + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtksignal.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkadjustment.h> +#include <gtk/gtktable.h> +#include <gtk/gtkbox.h> +#include <gtk/gtkmain.h> +#include <gtk/gtktypeutils.h> +#include <gtk/gtkentry.h> +#include <gtk/gtkcontainer.h> +#include <gtk/gtkpixmap.h> + + +#include "gtksheet_1_2.h" +#include "gtkitementry_1_2.h" + + +#define CELL_SPACING 1 +#define DRAG_WIDTH 6 +#define TIMEOUT_SCROLL 20 +#define TIMEOUT_FLASH 200 +#define TIME_INTERVAL 8 +#define COLUMN_MIN_WIDTH 10 +#define MINROWS 1 +#define MINCOLS 1 +#define MAXLENGTH 30 +#define CELLOFFSET 4 +#define DEFAULT_COLUMN_WIDTH 80 + + +#define DEFAULT_ROW_HEIGHT(widget) (widget->style->font->ascent + 2*widget->style->font->descent + 2*CELLOFFSET) +#define DEFAULT_LABEL_HEIGHT(widget) (widget->style->font->ascent+\ + 2*widget->style->font->descent) +#define DEFAULT_FONT_ASCENT(widget) (widget->style->font->ascent) +#define DEFAULT_FONT_DESCENT(widget) (widget->style->font->descent) + +/*-----------------------------------------------------------------------------*/ +/* scrollbar spacing class macro */ +#define SCROLLBAR_SPACING(w) (GTK_SHEET_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing) + + +/*-----------------------------------------------------------------------------*/ +/* gives the top pixel of the given row in context of + * the sheet's voffset */ +static inline gint +ROW_TOP_YPIXEL(GtkSheet *sheet, gint nrow) +{ + return (sheet->voffset + sheet->row[nrow].top_ypixel); +} + +/*-----------------------------------------------------------------------------*/ +/* returns the row index from a y pixel location in the + * context of the sheet's voffset */ +static inline gint +ROW_FROM_YPIXEL(GtkSheet *sheet, gint y) +{ + gint i, cy; + + cy = sheet->voffset; + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) cy += sheet->column_title_area.height; + if(y < cy) return 0; + for (i = 0; i <= sheet->maxrow; i++) + { + if (y >= cy && y <= (cy + sheet->row[i].height) && sheet->row[i].is_visible) + return i; + if(sheet->row[i].is_visible) cy += sheet->row[i].height; + + } + + /* no match */ + return sheet->maxrow; +} + + +/*-----------------------------------------------------------------------------*/ +/* gives the left pixel of the given column in context of + * the sheet's hoffset */ +static inline gint +COLUMN_LEFT_XPIXEL(GtkSheet *sheet, gint ncol) +{ + return (sheet->hoffset + sheet->column[ncol].left_xpixel); +} + + +/*-----------------------------------------------------------------------------*/ +/* returns the column index from a x pixel location in the + * context of the sheet's hoffset */ +static inline gint +COLUMN_FROM_XPIXEL (GtkSheet * sheet, + gint x) +{ + gint i, cx; + + cx = sheet->hoffset; + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) cx += sheet->row_title_area.width; + if(x < cx) return 0; + for (i = 0; i <= sheet->maxcol; i++) + { + if (x >= cx && x <= (cx + sheet->column[i].width) && sheet->column[i].is_visible) + return i; + if(sheet->column[i].is_visible) cx += sheet->column[i].width; + + } + + /* no match */ + return sheet->maxcol; +} + + +/*-----------------------------------------------------------------------------*/ +/* returns the total height of the sheet */ +static inline gint SHEET_HEIGHT(GtkSheet *sheet) +{ + gint i,cx; + + cx = 0; + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) cx += sheet->column_title_area.height; + for (i=0;i<=sheet->maxrow; i++) + if(sheet->row[i].is_visible) cx += sheet->row[i].height; + + return cx; +} + + +/*-----------------------------------------------------------------------------*/ +/* returns the total width of the sheet */ +static inline gint SHEET_WIDTH(GtkSheet *sheet) +{ + gint i,cx; + + cx = 0; + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) cx += sheet->row_title_area.width; + for (i=0;i<=sheet->maxcol; i++) + if(sheet->column[i].is_visible) cx += sheet->column[i].width; + + return cx; +} + +#define MIN_VISIBLE_ROW(sheet) sheet->view.row0 +#define MAX_VISIBLE_ROW(sheet) sheet->view.rowi +#define MIN_VISIBLE_COLUMN(sheet) sheet->view.col0 +#define MAX_VISIBLE_COLUMN(sheet) sheet->view.coli + +/*-----------------------------------------------------------------------------*/ +static inline gint +POSSIBLE_XDRAG(GtkSheet *sheet, gint x, gint *drag_column) +{ + gint column, xdrag; + + column=COLUMN_FROM_XPIXEL(sheet, x); + *drag_column=column; + + xdrag=COLUMN_LEFT_XPIXEL(sheet,column)+CELL_SPACING; + if(x <= xdrag+DRAG_WIDTH/2 && column != 0){ + while(!sheet->column[column-1].is_visible && column>0) column--; + *drag_column=column-1; + return sheet->column[column-1].is_sensitive; + } + + xdrag+=sheet->column[column].width; + if(x >= xdrag-DRAG_WIDTH/2 && x <= xdrag+DRAG_WIDTH/2) + return sheet->column[column].is_sensitive; + + return FALSE; +} + + +/*-----------------------------------------------------------------------------*/ +static inline gint +POSSIBLE_YDRAG(GtkSheet *sheet, gint y, gint *drag_row) +{ + gint row, ydrag; + + row=ROW_FROM_YPIXEL(sheet, y); + *drag_row=row; + + ydrag=ROW_TOP_YPIXEL(sheet,row)+CELL_SPACING; + if(y <= ydrag+DRAG_WIDTH/2 && row != 0){ + while(!sheet->row[row-1].is_visible && row>0) row--; + *drag_row=row-1; + return sheet->row[row-1].is_sensitive; + } + + ydrag+=sheet->row[row].height; + + if(y >= ydrag-DRAG_WIDTH/2 && y <= ydrag+DRAG_WIDTH/2) + return sheet->row[row].is_sensitive; + + + return FALSE; +} + + +/*-----------------------------------------------------------------------------*/ +static inline gint POSSIBLE_DRAG(GtkSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint ydrag, xdrag; + + *drag_column=COLUMN_FROM_XPIXEL(sheet,x); + *drag_row=ROW_FROM_YPIXEL(sheet,y); + + if(x>=COLUMN_LEFT_XPIXEL(sheet,sheet->range.col0)-DRAG_WIDTH/2 && + x<=COLUMN_LEFT_XPIXEL(sheet,sheet->range.coli)+ + sheet->column[sheet->range.coli].width+DRAG_WIDTH/2){ + ydrag=ROW_TOP_YPIXEL(sheet,sheet->range.row0); + if(y>=ydrag-DRAG_WIDTH/2 && y<=ydrag+DRAG_WIDTH/2){ + *drag_row=sheet->range.row0; + return TRUE; + } + ydrag=ROW_TOP_YPIXEL(sheet,sheet->range.rowi)+ + sheet->row[sheet->range.rowi].height; + if(y>=ydrag-DRAG_WIDTH/2 && y<=ydrag+DRAG_WIDTH/2){ + *drag_row=sheet->range.rowi; + return TRUE; + } + } + + if(y>=ROW_TOP_YPIXEL(sheet,sheet->range.row0)-DRAG_WIDTH/2 && + y<=ROW_TOP_YPIXEL(sheet,sheet->range.rowi)+ + sheet->row[sheet->range.rowi].height+DRAG_WIDTH/2){ + xdrag=COLUMN_LEFT_XPIXEL(sheet,sheet->range.col0); + if(x>=xdrag-DRAG_WIDTH/2 && x<=xdrag+DRAG_WIDTH/2){ + *drag_column=sheet->range.col0; + return TRUE; + } + xdrag=COLUMN_LEFT_XPIXEL(sheet,sheet->range.coli)+ + sheet->column[sheet->range.coli].width; + if(x>=xdrag-DRAG_WIDTH/2 && x<=xdrag+DRAG_WIDTH/2){ + *drag_column=sheet->range.coli; + return TRUE; + } + } + return FALSE; +} + + +/*-----------------------------------------------------------------------------*/ +static inline gint POSSIBLE_RESIZE(GtkSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint xdrag, ydrag; + + xdrag=COLUMN_LEFT_XPIXEL(sheet,sheet->range.coli)+ + sheet->column[sheet->range.coli].width; + + ydrag=ROW_TOP_YPIXEL(sheet,sheet->range.rowi)+ + sheet->row[sheet->range.rowi].height; + + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + ydrag = ROW_TOP_YPIXEL(sheet, sheet->view.row0); + + if(sheet->state == GTK_SHEET_ROW_SELECTED) + xdrag = COLUMN_LEFT_XPIXEL(sheet, sheet->view.col0); + + *drag_column=COLUMN_FROM_XPIXEL(sheet,x); + *drag_row=ROW_FROM_YPIXEL(sheet,y); + + if(x>=xdrag-DRAG_WIDTH/2 && x<=xdrag+DRAG_WIDTH/2 && + y>=ydrag-DRAG_WIDTH/2 && y<=ydrag+DRAG_WIDTH/2) return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------------------*/ +/* Typedefs and function prototypes */ +/*-----------------------------------------------------------------------------*/ +typedef gboolean (*GtkSheetSignal1) (GtkObject *object, + gint arg1, gint arg2, gint *arg3, gint *arg4, + gpointer user_data); + +typedef gboolean (*GtkSheetSignal2) (GtkObject *object, + gint arg1, gint arg2, + gpointer user_data); + +static void gtk_sheet_class_init (GtkSheetClass * klass); +static void gtk_sheet_init (GtkSheet * sheet); +static void gtk_sheet_destroy (GtkObject * object); +static void gtk_sheet_finalize (GtkObject * object); +static void gtk_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void gtk_sheet_realize (GtkWidget * widget); +static void gtk_sheet_unrealize (GtkWidget * widget); +static void gtk_sheet_map (GtkWidget * widget); +static void gtk_sheet_unmap (GtkWidget * widget); +static void gtk_sheet_draw (GtkWidget * widget, + GdkRectangle * area); +static gint gtk_sheet_expose (GtkWidget * widget, + GdkEventExpose * event); +/*static void gtk_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +*/ +static void gtk_sheet_set_scroll_adjustments (GtkSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + +static gint gtk_sheet_button_press (GtkWidget * widget, + GdkEventButton * event); +static gint gtk_sheet_button_release (GtkWidget * widget, + GdkEventButton * event); +static gint gtk_sheet_motion (GtkWidget * widget, + GdkEventMotion * event); +static gint gtk_sheet_entry_key_press (GtkWidget *widget, + GdkEventKey *key); +static gint gtk_sheet_key_press (GtkWidget *widget, + GdkEventKey *key); +static void gtk_sheet_size_request (GtkWidget * widget, + GtkRequisition * requisition); +static void gtk_sheet_size_allocate (GtkWidget * widget, + GtkAllocation * allocation); + +/* Sheet queries */ + +static gint gtk_sheet_range_isvisible (GtkSheet * sheet, + GtkSheetRange range); +static gint gtk_sheet_cell_isvisible (GtkSheet * sheet, + gint row, gint column); +/* Clipped Range */ + +static gint gtk_sheet_scroll (gpointer data); +static gint gtk_sheet_flash (gpointer data); + +/* Drawing Routines */ + +/* draw cell background and frame */ +static void gtk_sheet_cell_draw_default (GtkSheet *sheet, + gint row, gint column); + +/* draw cell border */ +static void gtk_sheet_cell_draw_border (GtkSheet *sheet, + gint row, gint column, + gint mask); + +/* draw cell contents */ +static void gtk_sheet_cell_draw_label (GtkSheet *sheet, + gint row, gint column); + +/* draw visible part of range. If range==NULL then draw the whole screen */ +static void gtk_sheet_range_draw (GtkSheet *sheet, + const GtkSheetRange *range); + +/* highlight the visible part of the selected range */ +static void gtk_sheet_range_draw_selection (GtkSheet *sheet, + GtkSheetRange range); + +/* Selection */ + +static gint gtk_sheet_move_query (GtkSheet *sheet, + gint row, gint column); +static void gtk_sheet_real_select_range (GtkSheet * sheet, + GtkSheetRange * range); +static void gtk_sheet_real_unselect_range (GtkSheet * sheet, + const GtkSheetRange * range); +static void gtk_sheet_extend_selection (GtkSheet *sheet, + gint row, gint column); +static void gtk_sheet_new_selection (GtkSheet *sheet, + GtkSheetRange *range); +static void gtk_sheet_draw_border (GtkSheet *sheet, + GtkSheetRange range); +static void gtk_sheet_draw_corners (GtkSheet *sheet, + GtkSheetRange range); + + +/* Active Cell handling */ + +static void gtk_sheet_entry_changed (GtkWidget *widget, + gpointer data); +static gboolean gtk_sheet_deactivate_cell (GtkSheet *sheet); +static void gtk_sheet_hide_active_cell (GtkSheet *sheet); +static gboolean gtk_sheet_activate_cell (GtkSheet *sheet, + gint row, gint col); +static void gtk_sheet_draw_active_cell (GtkSheet *sheet); +static void gtk_sheet_show_active_cell (GtkSheet *sheet); +static void gtk_sheet_click_cell (GtkSheet *sheet, + gint row, + gint column, + gboolean *veto); + +/* Backing Pixmap */ + +static void gtk_sheet_make_backing_pixmap (GtkSheet *sheet, + guint width, guint height); +static void gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, + GtkSheetRange range); +/* Scrollbars */ + +static void adjust_scrollbars (GtkSheet * sheet); +static void vadjustment_changed (GtkAdjustment * adjustment, + gpointer data); +static void hadjustment_changed (GtkAdjustment * adjustment, + gpointer data); +static void vadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data); +static void hadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data); + + +static void draw_xor_vline (GtkSheet * sheet); +static void draw_xor_hline (GtkSheet * sheet); +static void draw_xor_rectangle (GtkSheet *sheet, + GtkSheetRange range); +static void gtk_sheet_draw_flashing_range (GtkSheet *sheet, + GtkSheetRange range); +static guint new_column_width (GtkSheet * sheet, + gint column, + gint * x); +static guint new_row_height (GtkSheet * sheet, + gint row, + gint * y); +/* Sheet Button */ + +static void create_global_button (GtkSheet *sheet); +static void global_button_clicked (GtkWidget *widget, + gpointer data); +/* Sheet Entry */ + +static void create_sheet_entry (GtkSheet *sheet); +static void gtk_sheet_size_allocate_entry (GtkSheet *sheet); +static void gtk_sheet_entry_set_max_size (GtkSheet *sheet); + +/* Sheet button gadgets */ + +static void size_allocate_column_title_buttons (GtkSheet * sheet); +static void size_allocate_row_title_buttons (GtkSheet * sheet); +static void gtk_sheet_recalc_top_ypixels (GtkSheet *sheet, + gint row); +static void gtk_sheet_recalc_left_xpixels (GtkSheet *sheet, + gint column); +static void row_button_set (GtkSheet *sheet, + gint row); +static void column_button_set (GtkSheet *sheet, + gint column); +static void row_button_release (GtkSheet *sheet, + gint row); +static void column_button_release (GtkSheet *sheet, + gint column); +static void gtk_sheet_button_draw (GtkSheet *sheet, + gint row, gint column); +static void size_allocate_global_button (GtkSheet *sheet); +static void label_size_request (GtkSheet *sheet, gchar *label, + GtkRequisition *req); +static void gtk_sheet_button_size_request (GtkSheet *sheet, + GtkSheetButton *button, + GtkRequisition *requisition); + +/* Attributes routines */ + +static void gtk_sheet_set_cell_attributes (GtkSheet *sheet, + gint row, gint col, + GtkSheetCellAttr attributes); + +static void init_attributes (GtkSheet *sheet, gint col, + GtkSheetCellAttr *attributes); +/* Memory allocation routines */ +static void gtk_sheet_real_range_clear (GtkSheet *sheet, + const GtkSheetRange *range, + gboolean delete); +static void gtk_sheet_real_cell_clear (GtkSheet *sheet, + gint row, + gint column, + gboolean delete); +static GtkSheetCell * gtk_sheet_cell_new (void); +static gint AddRow (GtkSheet *sheet, gint nrows); +static gint AddColumn (GtkSheet *sheet, gint ncols); +static gint InsertRow (GtkSheet *sheet, gint row, gint nrows); +static gint InsertColumn (GtkSheet *sheet, gint col, gint ncols); +static gint DeleteRow (GtkSheet *sheet, gint row, gint nrows); +static gint DeleteColumn (GtkSheet *sheet, gint col, gint ncols); +static gint GrowSheet (GtkSheet *sheet, + gint newrows, gint newcols); +static gint CheckBounds (GtkSheet *sheet, + gint row, gint col); + +/* Container Functions */ +static void gtk_sheet_remove (GtkContainer *container, + GtkWidget *widget); +static void gtk_sheet_realize_child (GtkSheet *sheet, + GtkSheetChild *child); +static void gtk_sheet_position_child (GtkSheet *sheet, + GtkSheetChild *child); +static void gtk_sheet_position_children (GtkSheet *sheet); +static void gtk_sheet_child_show (GtkSheetChild *child); +static void gtk_sheet_child_hide (GtkSheetChild *child); + +/* Signals */ + +enum { + SELECT_ROW, + SELECT_COLUMN, + SELECT_RANGE, + CLIP_RANGE, + RESIZE_RANGE, + MOVE_RANGE, + TRAVERSE, + DEACTIVATE, + ACTIVATE, + SET_CELL, + CLEAR_CELL, + CHANGED, + NEW_COL_WIDTH, + NEW_ROW_HEIGHT, + LAST_SIGNAL +}; + +static void +gtk_sheet_marshal_BOOL__INT_INT (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg * args); +static void +gtk_sheet_marshal_BOOL__INT_INT_POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg * args); + +static GtkContainerClass *parent_class = NULL; +static guint sheet_signals[LAST_SIGNAL] = {0}; + +GtkType gtk_sheet_get_type () +{ + static guint sheet_type = 0; + if(!sheet_type){ + GtkTypeInfo sheet_info = + { + "GtkSheet", + sizeof(GtkSheet), + sizeof(GtkSheetClass), + (GtkClassInitFunc) gtk_sheet_class_init, + (GtkObjectInitFunc) gtk_sheet_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + sheet_type = gtk_type_unique (GTK_TYPE_CONTAINER, &sheet_info); + } + return sheet_type; +} + + +GtkType +gtk_sheet_range_get_type (void) +{ + static GtkType sheet_range_type = 0; + if (!sheet_range_type){ + static const GtkTypeInfo sheet_range_info = + { + "GtkSheetRange", + 0, + 0, + (GtkClassInitFunc) NULL, + (GtkObjectInitFunc) NULL, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + sheet_range_type = gtk_type_unique(GTK_TYPE_BOXED, &sheet_range_info); + } + return sheet_range_type; +} + +static void +gtk_sheet_class_init (GtkSheetClass * klass) +{ + + GtkType object_class_type; /* SDB addition for GTK-2.X */ + + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + object_class = (GtkObjectClass *) klass; + widget_class = (GtkWidgetClass *) klass; + container_class = (GtkContainerClass *) klass; + + /* This was introduced by SDB in order to enable compilation under GTK-2.X */ + + object_class_type = object_class->type; + parent_class = gtk_type_class (GTK_TYPE_CONTAINER); + + sheet_signals[SELECT_ROW] = + gtk_signal_new ("select_row", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, select_row), + gtk_marshal_NONE__INT, + GTK_TYPE_NONE, 1, GTK_TYPE_INT); + sheet_signals[SELECT_COLUMN] = + gtk_signal_new ("select_column", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, select_column), + gtk_marshal_NONE__INT, + GTK_TYPE_NONE, 1, GTK_TYPE_INT); + sheet_signals[SELECT_RANGE] = + gtk_signal_new ("select_range", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, select_range), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, GTK_TYPE_SHEET_RANGE); + sheet_signals[CLIP_RANGE] = + gtk_signal_new ("clip_range", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, clip_range), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, GTK_TYPE_SHEET_RANGE); + sheet_signals[RESIZE_RANGE] = + gtk_signal_new ("resize_range", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, resize_range), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE); + sheet_signals[MOVE_RANGE] = + gtk_signal_new ("move_range", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, move_range), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE); + + sheet_signals[TRAVERSE] = + gtk_signal_new ("traverse", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, traverse), + gtk_sheet_marshal_BOOL__INT_INT_POINTER_POINTER, + GTK_TYPE_BOOL, 4, GTK_TYPE_INT, GTK_TYPE_INT, + GTK_TYPE_POINTER, GTK_TYPE_POINTER); + + sheet_signals[DEACTIVATE] = + gtk_signal_new ("deactivate", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, deactivate), + gtk_sheet_marshal_BOOL__INT_INT, + GTK_TYPE_BOOL, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[ACTIVATE] = + gtk_signal_new ("activate", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, activate), + gtk_sheet_marshal_BOOL__INT_INT, + GTK_TYPE_BOOL, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[SET_CELL] = + gtk_signal_new ("set_cell", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, set_cell), + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + sheet_signals[CLEAR_CELL] = + gtk_signal_new ("clear_cell", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, clear_cell), + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + sheet_signals[CHANGED] = + gtk_signal_new ("changed", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, changed), + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + sheet_signals[NEW_COL_WIDTH] = + gtk_signal_new ("new_column_width", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, changed), + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + sheet_signals[NEW_ROW_HEIGHT] = + gtk_signal_new ("new_row_height", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, changed), + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + widget_class->set_scroll_adjustments_signal = + gtk_signal_new ("set_scroll_adjustments", + GTK_RUN_LAST, + object_class_type, + GTK_SIGNAL_OFFSET (GtkSheetClass, set_scroll_adjustments), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + + container_class->add = NULL; + container_class->remove = gtk_sheet_remove; + container_class->forall = NULL; + + object_class->destroy = gtk_sheet_destroy; + + widget_class->realize = gtk_sheet_realize; + widget_class->unrealize = gtk_sheet_unrealize; + widget_class->map = gtk_sheet_map; + widget_class->unmap = gtk_sheet_unmap; + widget_class->draw = gtk_sheet_draw; + widget_class->style_set = gtk_sheet_style_set; + widget_class->button_press_event = gtk_sheet_button_press; + widget_class->button_release_event = gtk_sheet_button_release; + widget_class->motion_notify_event = gtk_sheet_motion; + widget_class->key_press_event = gtk_sheet_key_press; + widget_class->expose_event = gtk_sheet_expose; + widget_class->size_request = gtk_sheet_size_request; + widget_class->size_allocate = gtk_sheet_size_allocate; + widget_class->focus_in_event = NULL; + widget_class->focus_out_event = NULL; + + klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments; + klass->select_row = NULL; + klass->select_column = NULL; + klass->select_range = NULL; + klass->clip_range = NULL; + klass->resize_range = NULL; + klass->move_range = NULL; + klass->traverse = NULL; + klass->deactivate = NULL; + klass->activate = NULL; + klass->set_cell = NULL; + klass->clear_cell = NULL; + klass->changed = NULL; + +} + +static void +gtk_sheet_marshal_BOOL__INT_INT_POINTER_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg * args) +{ + GtkSheetSignal1 rfunc; + gboolean *veto; + veto = GTK_RETLOC_BOOL (args[4]); + + rfunc = (GtkSheetSignal1) func; + + *veto = (*rfunc) (object, GTK_VALUE_INT (args[0]), + GTK_VALUE_INT (args[1]), + GTK_VALUE_POINTER (args[2]), + GTK_VALUE_POINTER (args[3]), + func_data); +} + +static void +gtk_sheet_marshal_BOOL__INT_INT (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg * args) +{ + GtkSheetSignal2 rfunc; + gboolean *veto; + veto = GTK_RETLOC_BOOL (args[2]); + + rfunc = (GtkSheetSignal2) func; + + *veto = (*rfunc) (object, GTK_VALUE_INT (args[0]), + GTK_VALUE_INT (args[1]), + func_data); +} + + + +static void +gtk_sheet_init (GtkSheet *sheet) +{ + sheet->children = NULL; + + sheet->flags = 0; + sheet->selection_mode = GTK_SELECTION_BROWSE; + sheet->freeze_count = 0; + sheet->state = GTK_SHEET_NORMAL; + + GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW); + GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS); + + sheet->maxrow = 0; + sheet->maxcol = 0; + + sheet->view.row0 = 0; + sheet->view.col0 = 0; + sheet->view.rowi = 0; + sheet->view.coli = 0; + + sheet->maxallocrow = 0; + sheet->maxalloccol = 0; + + sheet->column_title_window=NULL; + sheet->column_title_area.x=0; + sheet->column_title_area.y=0; + sheet->column_title_area.width=0; + sheet->column_title_area.height=DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + + sheet->row_title_window=NULL; + sheet->row_title_area.x=0; + sheet->row_title_area.y=0; + sheet->row_title_area.width=DEFAULT_COLUMN_WIDTH; + sheet->row_title_area.height=0; + + sheet->active_cell.row=0; + sheet->active_cell.col=0; + sheet->selection_cell.row=0; + sheet->selection_cell.col=0; + + sheet->sheet_entry=NULL; + sheet->pixmap=NULL; + + sheet->range.row0=0; + sheet->range.rowi=0; + sheet->range.col0=0; + sheet->range.coli=0; + + sheet->state=GTK_SHEET_NORMAL; + + sheet->sheet_window = NULL; + sheet->sheet_window_width = 0; + sheet->sheet_window_height = 0; + sheet->sheet_entry = NULL; + sheet->button = NULL; + + sheet->hoffset = 0; + sheet->voffset = 0; + + sheet->hadjustment = NULL; + sheet->vadjustment = NULL; + + sheet->cursor_drag = gdk_cursor_new(GDK_PLUS); + sheet->xor_gc = NULL; + sheet->fg_gc = NULL; + sheet->bg_gc = NULL; + sheet->x_drag = 0; + sheet->y_drag = 0; + + gdk_color_white(gdk_colormap_get_system(), &sheet->bg_color); + gdk_color_parse("gray", &sheet->grid_color); + gdk_color_alloc(gdk_colormap_get_system(), &sheet->grid_color); + sheet->show_grid = TRUE; +} + +GtkSheet * +gtk_sheet_new (guint rows, guint columns, const gchar *title) +{ + GtkWidget *widget; + + /* sanity check */ + g_return_val_if_fail(columns >= MINCOLS, NULL); + g_return_val_if_fail(rows >= MINROWS, NULL); + + widget = GTK_WIDGET( gtk_type_new(gtk_sheet_get_type ()) ); + + gtk_sheet_construct(GTK_SHEET(widget), rows, columns, title); + + return GTK_SHEET(widget); +} + +void +gtk_sheet_construct (GtkSheet *sheet, guint rows, guint columns, const gchar *title) +{ + sheet->row=(GtkSheetRow *)g_malloc(sizeof(GtkSheetRow)); + sheet->column=(GtkSheetColumn *)g_malloc(sizeof(GtkSheetColumn)); + sheet->data=(GtkSheetCell ***)g_malloc(sizeof(GtkSheetCell **)); + + sheet->data[0] = (GtkSheetCell **)g_malloc(sizeof(GtkSheetCell *)+sizeof(gdouble)); + sheet->data[0][0] = NULL; + + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_ROW_TITLES_VISIBLE); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_COL_TITLES_VISIBLE); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_AUTO_SCROLL); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_JUSTIFY_ENTRY); + + /* set number of rows and columns */ + GrowSheet(sheet, MINROWS, MINCOLS); + + /* Init row an column zero */ + AddRow(sheet,-1); + AddColumn(sheet,-1); + + /* Add rows and columns */ + AddRow(sheet,rows-1); + AddColumn(sheet,columns-1); + + /* create sheet entry */ + sheet->entry_type = 0; + create_sheet_entry (sheet); + + /* create global selection button */ + create_global_button(sheet); + + if(title) + sheet->name = g_strdup(title); + +} + + +GtkWidget * +gtk_sheet_new_with_custom_entry (guint rows, guint columns, const gchar *title, + GtkType entry_type) +{ + GtkWidget *widget; + + widget = gtk_type_new (gtk_sheet_get_type ()); + + gtk_sheet_construct_with_custom_entry(GTK_SHEET(widget), + rows, columns, title, entry_type); + + return widget; +} + +void +gtk_sheet_construct_with_custom_entry (GtkSheet *sheet, + guint rows, guint columns, + const gchar *title, + GtkType entry_type) +{ + gtk_sheet_construct(sheet, rows, columns, title); + + sheet->entry_type = entry_type; + create_sheet_entry(sheet); +} + + +void +gtk_sheet_change_entry(GtkSheet *sheet, GtkType entry_type) +{ + gint state; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + state = sheet->state; + + if(sheet->state == GTK_SHEET_NORMAL) + gtk_sheet_hide_active_cell(sheet); + + sheet->entry_type = entry_type; + + create_sheet_entry(sheet); + + if(state == GTK_SHEET_NORMAL) + { + gtk_sheet_show_active_cell(sheet); + gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "changed", + (GtkSignalFunc)gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + } + +} + +void +gtk_sheet_show_grid(GtkSheet *sheet, gboolean show) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(show == sheet->show_grid) return; + + sheet->show_grid = show; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, NULL); +} + +gboolean +gtk_sheet_grid_visible(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return sheet->show_grid; +} + +void +gtk_sheet_set_background(GtkSheet *sheet, GdkColor *color) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!color) + gdk_color_white(gdk_colormap_get_system(), &sheet->bg_color); + else + sheet->bg_color = *color; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, NULL); +} + +void +gtk_sheet_set_grid(GtkSheet *sheet, GdkColor *color) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!color) + gdk_color_black(gdk_colormap_get_system(), &sheet->grid_color); + else + sheet->grid_color = *color; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, NULL); +} + +guint +gtk_sheet_get_columns_count(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return sheet->maxcol + 1; +} + +guint +gtk_sheet_get_rows_count(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return sheet->maxrow + 1; +} + +gint +gtk_sheet_get_state(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return (sheet->state); +} + +void +gtk_sheet_set_selection_mode(GtkSheet *sheet, gint mode) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + sheet->selection_mode = mode; +} + +/* This routine has problems with gtk+-1.2 related with the + * label/button drawing - I think it's a bug in gtk+-1.2 */ +void +gtk_sheet_set_title(GtkSheet *sheet, const gchar *title) +{ +/* GtkWidget *old_widget; +*/ GtkWidget *label; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (title != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (sheet->name) + g_free (sheet->name); + + sheet->name = g_strdup (title); + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) || !title) return; + + if(GTK_BIN(sheet->button)->child) + label = GTK_BIN(sheet->button)->child; +/* + gtk_label_set_text(GTK_LABEL(label), title); +*/ + size_allocate_global_button(sheet); + + /* remove and destroy the old widget */ +/* + old_widget = GTK_BIN (sheet->button)->child; + if (old_widget) + { + gtk_container_remove (GTK_CONTAINER (sheet->button), old_widget); + } + + label = gtk_label_new (title); + gtk_misc_set_alignment(GTK_MISC(label), 0.5 , 0.5 ); + + gtk_container_add (GTK_CONTAINER (sheet->button), label); + gtk_widget_show (label); + + size_allocate_global_button(sheet); + + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, -1); + + if(old_widget) + gtk_widget_destroy (old_widget); +*/ +} + +void +gtk_sheet_freeze (GtkSheet *sheet) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->freeze_count++; + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); +} + +void +gtk_sheet_thaw(GtkSheet *sheet) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(sheet->freeze_count == 0) return; + + sheet->freeze_count--; + if(sheet->freeze_count > 0) return; + + adjust_scrollbars(sheet); + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + sheet->old_vadjustment = -1.; + sheet->old_hadjustment = -1.; + + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + + if(sheet->state == GTK_STATE_NORMAL) + if(sheet->sheet_entry && GTK_WIDGET_MAPPED(sheet->sheet_entry)){ + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col); +/* + gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "changed", + (GtkSignalFunc)gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + gtk_sheet_show_active_cell(sheet); +*/ + } + +} + +void +gtk_sheet_set_row_titles_width(GtkSheet *sheet, guint width) +{ + if(width < COLUMN_MIN_WIDTH) return; + + sheet->row_title_area.width = width; + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + adjust_scrollbars(sheet); + + sheet->old_hadjustment = -1.; + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_set_column_titles_height(GtkSheet *sheet, guint height) +{ + if(height < DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet))) return; + + sheet->column_title_area.height = height; + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_show_column_titles(GtkSheet *sheet) +{ + gint col; + + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) return; + + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_COL_TITLES_VISIBLE); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + gdk_window_show(sheet->column_title_window); + + for(col = MIN_VISIBLE_COLUMN(sheet); col <= MAX_VISIBLE_COLUMN(sheet); col++){ + GtkSheetChild *child; + child = sheet->column[col].button.child; + if(child){ + gtk_sheet_child_show(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_vadjustment = -1.; + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_show_row_titles(GtkSheet *sheet) +{ + gint row; + + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) return; + + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_ROW_TITLES_VISIBLE); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + gdk_window_show(sheet->row_title_window); + + for(row = MIN_VISIBLE_ROW(sheet); row <= MAX_VISIBLE_ROW(sheet); row++){ + GtkSheetChild *child; + child = sheet->row[row].button.child; + if(child){ + gtk_sheet_child_show(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_hadjustment = -1.; + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_hide_column_titles(GtkSheet *sheet) +{ + gint col; + + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) return; + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_COL_TITLES_VISIBLE); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + if(sheet->column_title_window) gdk_window_hide(sheet->column_title_window); + if(GTK_WIDGET_VISIBLE(sheet->button)) gtk_widget_hide(sheet->button); + + for(col = MIN_VISIBLE_COLUMN(sheet); col <= MAX_VISIBLE_COLUMN(sheet); col++){ + GtkSheetChild *child; + child = sheet->column[col].button.child; + if(child){ + gtk_sheet_child_hide(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_vadjustment = -1.; + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); +} + +void +gtk_sheet_hide_row_titles(GtkSheet *sheet) +{ + gint row; + + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) return; + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_ROW_TITLES_VISIBLE); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + if(sheet->row_title_window) gdk_window_hide(sheet->row_title_window); + if(GTK_WIDGET_VISIBLE(sheet->button)) gtk_widget_hide(sheet->button); + for(row = MIN_VISIBLE_ROW(sheet); row <= MAX_VISIBLE_ROW(sheet); row++){ + GtkSheetChild *child; + child = sheet->row[row].button.child; + if(child){ + gtk_sheet_child_hide(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_hadjustment = -1.; + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); +} + +void +gtk_sheet_set_column_title (GtkSheet * sheet, + gint column, + const gchar * title) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (sheet->column[column].name) + g_free (sheet->column[column].name); + + sheet->column[column].name = g_strdup(title); +} + +void +gtk_sheet_set_row_title (GtkSheet * sheet, + gint row, + const gchar * title) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (sheet->row[row].name) + g_free (sheet->row[row].name); + + sheet->row[row].name = g_strdup (title); +} + +void +gtk_sheet_row_button_add_label(GtkSheet *sheet, gint row, const gchar *label) +{ + GtkSheetButton *button; + gint label_height; + gchar *words; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + + if (sheet->row[row].button.label) + g_free (sheet->row[row].button.label); + + sheet->row[row].button.label = g_strdup (label); + + button = &sheet->row[row].button; + label_height = 0; + if(button->label && strlen(button->label)>0){ + words=button->label; + while(words && *words != '\0'){ + if(*words == '\n' || *(words+1) == '\0'){ + label_height += DEFAULT_LABEL_HEIGHT(GTK_WIDGET(sheet)); + } + words++; + } + } + + if(label_height+2*CELLOFFSET > sheet->column_title_area.height) + gtk_sheet_set_row_height(sheet, row, label_height+2*CELLOFFSET); + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, row, -1); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, -1); + } +} + +void +gtk_sheet_row_label_set_visibility(GtkSheet *sheet, gint row, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + sheet->row[row].button.label_visible = visible; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, row, -1); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, -1); + } +} + +void +gtk_sheet_rows_labels_set_visibility(GtkSheet *sheet, gboolean visible) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i = 0; i <= sheet->maxrow; i++) + gtk_sheet_row_label_set_visibility(sheet, i, visible); +} + + +void +gtk_sheet_column_button_add_label(GtkSheet *sheet, gint column, const gchar *label) +{ + GtkSheetButton *button; + gint label_height = 0; + gchar *words; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column >sheet->maxcol) return; + + if (sheet->column[column].button.label) + g_free (sheet->column[column].button.label); + + + sheet->column[column].button.label = g_strdup (label); + + button = &sheet->column[column].button; + if(button->label && strlen(button->label)>0){ + words=button->label; + while(words && *words != '\0'){ + if(*words == '\n' || *(words+1) == '\0'){ + label_height += DEFAULT_LABEL_HEIGHT(GTK_WIDGET(sheet)); + } + words++; + } + } + + if(label_height+2*CELLOFFSET > sheet->column_title_area.height) + gtk_sheet_set_column_titles_height(sheet, label_height+2*CELLOFFSET); + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, -1, column); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, column); + } +} + +void +gtk_sheet_column_label_set_visibility(GtkSheet *sheet, gint col, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(col < 0 || col > sheet->maxcol) return; + + sheet->column[col].button.label_visible = visible; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, -1, col); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, col); + } +} + +void +gtk_sheet_columns_labels_set_visibility(GtkSheet *sheet, gboolean visible) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i = 0; i <= sheet->maxcol; i++) + gtk_sheet_column_label_set_visibility(sheet, i, visible); +} + +void +gtk_sheet_row_button_justify(GtkSheet *sheet, gint row, + GtkJustification justification) +{ + GtkSheetButton *button; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + button = &sheet->row[row].button; + button->justification = justification; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, row, -1); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, -1); + } +} + +void +gtk_sheet_column_button_justify(GtkSheet *sheet, gint column, + GtkJustification justification) +{ + GtkSheetButton *button; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column > sheet->maxcol) return; + + button = &sheet->column[column].button; + button->justification = justification; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, -1, column); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, column); + } +} + + +void +gtk_sheet_moveto (GtkSheet * sheet, + gint row, + gint column, + gfloat row_align, + gfloat col_align) +{ + gint x, y; + guint width, height; + gint adjust; + gint min_row, min_col; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + g_return_if_fail (sheet->hadjustment != NULL); + g_return_if_fail (sheet->vadjustment != NULL); + + if (row < 0 || row > sheet->maxrow) + return; + if (column < 0 || column > sheet->maxcol) + return; + + height = sheet->sheet_window_height; + width = sheet->sheet_window_width; + + /* adjust vertical scrollbar */ + + if (row >= 0 && row_align >=0.) + { + y = ROW_TOP_YPIXEL(sheet, row) - sheet->voffset - + row_align*height- + (1.-row_align)*sheet->row[row].height; + + /* This forces the sheet to scroll when you don't see the entire cell */ + min_row = row; + adjust = 0; + if(row_align == 1.){ + while(min_row >= 0 && min_row > MIN_VISIBLE_ROW(sheet)){ + if(sheet->row[min_row].is_visible) + adjust += sheet->row[min_row].height; + if(adjust >= height){ + break; + } + min_row--; + } + min_row = MAX(min_row, 0); + y = ROW_TOP_YPIXEL(sheet, min_row) - sheet->voffset + + sheet->row[min_row].height - 1; + } + + if (y < 0) + sheet->vadjustment->value = 0.0; + else + sheet->vadjustment->value = y; + + sheet->old_vadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + + } + + /* adjust horizontal scrollbar */ + if (column >= 0 && col_align >= 0.) + { + x = COLUMN_LEFT_XPIXEL (sheet, column) - sheet->hoffset - + col_align*width - + (1.-col_align)*sheet->column[column].width; + + /* This forces the sheet to scroll when you don't see the entire cell */ + min_col = column; + adjust = 0; + if(col_align == 1.){ + while(min_col >= 0 && min_col > MIN_VISIBLE_COLUMN(sheet)){ + if(sheet->column[min_col].is_visible) + adjust += sheet->column[min_col].width; + if(adjust >= width){ + break; + } + min_col--; + } + min_col = MAX(min_col, 0); + x = COLUMN_LEFT_XPIXEL(sheet, min_col) - sheet->hoffset + + sheet->column[min_col].width - 1; + } + + if (x < 0) + sheet->hadjustment->value = 0.0; + else + sheet->hadjustment->value = x; + + sheet->old_vadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + + } +} + +void +gtk_sheet_column_set_sensitivity(GtkSheet *sheet, gint column, gboolean sensitive) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column > sheet->maxcol) return; + + sheet->column[column].is_sensitive=sensitive; + if(!sensitive) + sheet->column[column].button.state=GTK_STATE_INSENSITIVE; + else + sheet->column[column].button.state=GTK_STATE_NORMAL; + + if(GTK_WIDGET_REALIZED(sheet) && !GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_button_draw(sheet, -1, column); +} + +void +gtk_sheet_columns_set_sensitivity(GtkSheet *sheet, gboolean sensitive) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i=0; i<=sheet->maxcol; i++) + gtk_sheet_column_set_sensitivity(sheet, i, sensitive); +} + +void +gtk_sheet_row_set_sensitivity(GtkSheet *sheet, gint row, gboolean sensitive) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + sheet->row[row].is_sensitive=sensitive; + if(!sensitive) + sheet->row[row].button.state=GTK_STATE_INSENSITIVE; + else + sheet->row[row].button.state=GTK_STATE_NORMAL; + + if(GTK_WIDGET_REALIZED(sheet) && !GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_button_draw(sheet, row, -1); +} + +void +gtk_sheet_rows_set_sensitivity(GtkSheet *sheet, gboolean sensitive) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i=0; i<=sheet->maxrow; i++) + gtk_sheet_row_set_sensitivity(sheet, i, sensitive); +} + +void +gtk_sheet_column_set_visibility(GtkSheet *sheet, gint column, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column > sheet->maxcol) return; + if(sheet->column[column].is_visible == visible) return; + + sheet->column[column].is_visible = visible; + + gtk_sheet_recalc_left_xpixels(sheet, column); + + if(!GTK_SHEET_IS_FROZEN(sheet) && + gtk_sheet_cell_isvisible(sheet, MIN_VISIBLE_ROW(sheet), column)){ + gtk_sheet_range_draw(sheet, NULL); + size_allocate_column_title_buttons(sheet); + } +} + +void +gtk_sheet_row_set_visibility(GtkSheet *sheet, gint row, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + if(sheet->row[row].is_visible == visible) return; + + sheet->row[row].is_visible = visible; + + gtk_sheet_recalc_top_ypixels(sheet, row); + + if(!GTK_SHEET_IS_FROZEN(sheet) && + gtk_sheet_cell_isvisible(sheet, row, MIN_VISIBLE_COLUMN(sheet))){ + gtk_sheet_range_draw(sheet, NULL); + size_allocate_row_title_buttons(sheet); + } +} + +void +gtk_sheet_select_row (GtkSheet * sheet, + gint row) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (row < 0 || row > sheet->maxrow) + return; + + if(sheet->state != GTK_SHEET_NORMAL) + gtk_sheet_real_unselect_range(sheet, NULL); + else + { + gboolean veto; + veto = gtk_sheet_deactivate_cell(sheet); + if(!veto) return; + } + + sheet->state=GTK_SHEET_ROW_SELECTED; + sheet->range.row0=row; + sheet->range.col0=0; + sheet->range.rowi=row; + sheet->range.coli=sheet->maxcol; + sheet->active_cell.row=row; + sheet->active_cell.col=0; + + gtk_signal_emit (GTK_OBJECT (sheet), sheet_signals[SELECT_ROW], row); + gtk_sheet_real_select_range(sheet, NULL); + +} + + +void +gtk_sheet_select_column (GtkSheet * sheet, + gint column) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (column < 0 || column > sheet->maxcol) + return; + + if(sheet->state != GTK_SHEET_NORMAL) + gtk_sheet_real_unselect_range(sheet, NULL); + else + { + gboolean veto; + veto = gtk_sheet_deactivate_cell(sheet); + if(!veto) return; + } + + sheet->state=GTK_SHEET_COLUMN_SELECTED; + sheet->range.row0=0; + sheet->range.col0=column; + sheet->range.rowi=sheet->maxrow; + sheet->range.coli=column; + sheet->active_cell.row=0; + sheet->active_cell.col=column; + + gtk_signal_emit (GTK_OBJECT (sheet), sheet_signals[SELECT_COLUMN], column); + gtk_sheet_real_select_range(sheet, NULL); + +} + +void +gtk_sheet_clip_range (GtkSheet *sheet, const GtkSheetRange *range) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_SHEET_IN_CLIP(sheet)) return; + + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_CLIP); + + if(range == NULL) + sheet->clip_range = sheet->range; + else + sheet->clip_range=*range; + + sheet->interval=0; + sheet->clip_timer=gtk_timeout_add(TIMEOUT_FLASH, gtk_sheet_flash, (gpointer) sheet); + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[CLIP_RANGE], + &sheet->clip_range); + +} + +void +gtk_sheet_unclip_range(GtkSheet *sheet) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!GTK_SHEET_IN_CLIP(sheet)) return; + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_CLIP); + gtk_timeout_remove(sheet->clip_timer); + gtk_sheet_range_draw(sheet, &sheet->clip_range); + + if(gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_range_draw(sheet, &sheet->range); +} + +static gint +gtk_sheet_flash(gpointer data) +{ + GtkSheet *sheet; + gint x,y,width,height; + GdkRectangle clip_area; + + sheet=GTK_SHEET(data); + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return TRUE; + if(!GTK_WIDGET_DRAWABLE(GTK_WIDGET(sheet))) return TRUE; + if(!gtk_sheet_range_isvisible(sheet, sheet->clip_range)) return TRUE; + if(GTK_SHEET_IN_XDRAG(sheet)) return TRUE; + if(GTK_SHEET_IN_YDRAG(sheet)) return TRUE; + + GDK_THREADS_ENTER(); + + x=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.col0)+1; + y=ROW_TOP_YPIXEL(sheet,sheet->clip_range.row0)+1; + width=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.coli)-x+ + sheet->column[sheet->clip_range.coli].width-1; + height=ROW_TOP_YPIXEL(sheet,sheet->clip_range.rowi)-y+ + sheet->row[sheet->clip_range.rowi].height-1; + + clip_area.x=COLUMN_LEFT_XPIXEL(sheet, MIN_VISIBLE_COLUMN(sheet)); + clip_area.y=ROW_TOP_YPIXEL(sheet, MIN_VISIBLE_ROW(sheet)); + clip_area.width=sheet->sheet_window_width; + clip_area.height=sheet->sheet_window_height; + + if(x<0) { + width=width+x+1; + x=-1; + } + if(width>clip_area.width) width=clip_area.width+10; + if(y<0) { + height=height+y+1; + y=-1; + } + if(height>clip_area.height) height=clip_area.height+10; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, y, + x, y, + 1, height); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, y, + x, y, + width, 1); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, y+height, + x, y+height, + width, 1); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x+width, y, + x+width, y, + 1, height); + + + sheet->interval=sheet->interval+1; + if(sheet->interval==TIME_INTERVAL) sheet->interval=0; + + gdk_gc_set_dashes(sheet->xor_gc, sheet->interval, "\4\4", 2); + gtk_sheet_draw_flashing_range(sheet, sheet->clip_range); + gdk_gc_set_dashes(sheet->xor_gc, 0, "\4\4", 2); + + GDK_THREADS_LEAVE(); + + return TRUE; + +} + +static void +gtk_sheet_draw_flashing_range(GtkSheet *sheet, GtkSheetRange range) +{ + GdkRectangle clip_area; + gint x,y,width,height; + + if(!gtk_sheet_range_isvisible(sheet, sheet->clip_range)) return; + + clip_area.x=COLUMN_LEFT_XPIXEL(sheet, MIN_VISIBLE_COLUMN(sheet)); + clip_area.y=ROW_TOP_YPIXEL(sheet, MIN_VISIBLE_ROW(sheet)); + clip_area.width=sheet->sheet_window_width; + clip_area.height=sheet->sheet_window_height; + + gdk_gc_set_clip_rectangle(sheet->xor_gc, &clip_area); + + x=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.col0)+1; + y=ROW_TOP_YPIXEL(sheet,sheet->clip_range.row0)+1; + width=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.coli)-x+ + sheet->column[sheet->clip_range.coli].width-1; + height=ROW_TOP_YPIXEL(sheet,sheet->clip_range.rowi)-y+ + sheet->row[sheet->clip_range.rowi].height-1; + + if(x<0) { + width=width+x+1; + x=-1; + } + if(width>clip_area.width) width=clip_area.width+10; + if(y<0) { + height=height+y+1; + y=-1; + } + if(height>clip_area.height) height=clip_area.height+10; + + gdk_gc_set_line_attributes(sheet->xor_gc, 1, 1, 0 ,0 ); + + gdk_draw_rectangle(sheet->sheet_window, sheet->xor_gc, FALSE, + x, y, + width, height); + + gdk_gc_set_line_attributes (sheet->xor_gc, 1, 0, 0, 0); + + gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL); + +} + +static gint +gtk_sheet_range_isvisible (GtkSheet * sheet, + GtkSheetRange range) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + + if (range.row0 < 0 || range.row0 > sheet->maxrow) + return FALSE; + + if (range.rowi < 0 || range.rowi > sheet->maxrow) + return FALSE; + + if (range.col0 < 0 || range.col0 > sheet->maxcol) + return FALSE; + + if (range.coli < 0 || range.coli > sheet->maxcol) + return FALSE; + + if (range.rowi < MIN_VISIBLE_ROW (sheet)) + return FALSE; + + if (range.row0 > MAX_VISIBLE_ROW (sheet)) + return FALSE; + + if (range.coli < MIN_VISIBLE_COLUMN (sheet)) + return FALSE; + + if (range.col0 > MAX_VISIBLE_COLUMN (sheet)) + return FALSE; + + return TRUE; +} + +static gint +gtk_sheet_cell_isvisible (GtkSheet * sheet, + gint row, gint column) +{ + GtkSheetRange range; + + range.row0 = row; + range.col0 = column; + range.rowi = row; + range.coli = column; + + return gtk_sheet_range_isvisible(sheet, range); +} + +void +gtk_sheet_get_visible_range(GtkSheet *sheet, GtkSheetRange *range) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)) ; + g_return_if_fail (range != NULL); + + range->row0 = MIN_VISIBLE_ROW(sheet); + range->col0 = MIN_VISIBLE_COLUMN(sheet); + range->rowi = MAX_VISIBLE_ROW(sheet); + range->coli = MAX_VISIBLE_COLUMN(sheet); + +} + +GtkAdjustment * +gtk_sheet_get_vadjustment (GtkSheet * sheet) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + return sheet->vadjustment; +} + +GtkAdjustment * +gtk_sheet_get_hadjustment (GtkSheet * sheet) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + return sheet->hadjustment; +} + +void +gtk_sheet_set_vadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment) +{ + GtkAdjustment *old_adjustment; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (adjustment) + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + + if (sheet->vadjustment == adjustment) + return; + + old_adjustment = sheet->vadjustment; + + if (sheet->vadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->vadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->vadjustment)); + } + + sheet->vadjustment = adjustment; + + if (sheet->vadjustment) + { + gtk_object_ref (GTK_OBJECT (sheet->vadjustment)); + gtk_object_sink (GTK_OBJECT (sheet->vadjustment)); + + gtk_signal_connect (GTK_OBJECT (sheet->vadjustment), "changed", + (GtkSignalFunc) vadjustment_changed, + (gpointer) sheet); + gtk_signal_connect (GTK_OBJECT (sheet->vadjustment), "value_changed", + (GtkSignalFunc) vadjustment_value_changed, + (gpointer) sheet); + } + + if (!sheet->vadjustment || !old_adjustment) + { + gtk_widget_queue_resize (GTK_WIDGET (sheet)); + return; + } + + sheet->old_vadjustment = sheet->vadjustment->value; +} + +void +gtk_sheet_set_hadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment) +{ + GtkAdjustment *old_adjustment; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (adjustment) + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + + if (sheet->hadjustment == adjustment) + return; + + old_adjustment = sheet->hadjustment; + + if (sheet->hadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->hadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->hadjustment)); + } + + sheet->hadjustment = adjustment; + + if (sheet->hadjustment) + { + gtk_object_ref (GTK_OBJECT (sheet->hadjustment)); + gtk_object_sink (GTK_OBJECT (sheet->hadjustment)); + + gtk_signal_connect (GTK_OBJECT (sheet->hadjustment), "changed", + (GtkSignalFunc) hadjustment_changed, + (gpointer) sheet); + gtk_signal_connect (GTK_OBJECT (sheet->hadjustment), "value_changed", + (GtkSignalFunc) hadjustment_value_changed, + (gpointer) sheet); + } + + if (!sheet->hadjustment || !old_adjustment) + { + gtk_widget_queue_resize (GTK_WIDGET (sheet)); + return; + } + + sheet->old_hadjustment = sheet->hadjustment->value; +} + +static void +gtk_sheet_set_scroll_adjustments (GtkSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + if(sheet->hadjustment != hadjustment) + gtk_sheet_set_hadjustment (sheet, hadjustment); + if(sheet->vadjustment != vadjustment) + gtk_sheet_set_vadjustment (sheet, vadjustment); +} + +static void +gtk_sheet_destroy (GtkObject * object) +{ + GtkSheet *sheet; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_SHEET (object)); + + sheet = GTK_SHEET (object); + + /* get rid of all the cells */ + gtk_sheet_range_clear (sheet, NULL); + + /* destroy the entry */ + gtk_widget_destroy (sheet->sheet_entry); + + /* destroy the global selection button */ + gtk_widget_destroy (sheet->button); + + if(sheet->timer){ + gtk_timeout_remove(sheet->timer); + sheet->timer = 0; + } + + if(sheet->clip_timer){ + gtk_timeout_remove(sheet->clip_timer); + sheet->clip_timer = 0; + } + + /* unref adjustments */ + if (sheet->hadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->hadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->hadjustment)); + sheet->hadjustment = NULL; + } + if (sheet->vadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->vadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->vadjustment)); + sheet->vadjustment = NULL; + } + + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (*GTK_OBJECT_CLASS (parent_class)->destroy) (object); + + g_list_free(sheet->children); +} + +#if 0 +static void +gtk_sheet_finalize (GtkObject * object) +{ + GtkSheet *sheet; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_SHEET (object)); + + sheet = GTK_SHEET(object); + + gtk_sheet_range_delete(sheet, NULL); + + DeleteRow (sheet, 0, sheet->maxrow + 1); + DeleteColumn (sheet, 0, sheet->maxcol + 1); + + g_free(sheet->row); + g_free(sheet->column); + g_free(sheet->data); + + if(sheet->name) + g_free(sheet->name); + + if (GTK_OBJECT_CLASS (parent_class)->finalize) + (*GTK_OBJECT_CLASS (parent_class)->finalize) (object); + +} +#endif + +static void +gtk_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + GtkSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + if (GTK_WIDGET_CLASS (parent_class)->style_set) + (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style); + + sheet = GTK_SHEET (widget); + + if(GTK_WIDGET_REALIZED(widget)) + { + gtk_style_set_background (widget->style, widget->window, widget->state); + } + +} + +static void +gtk_sheet_realize (GtkWidget * widget) +{ + GtkSheet *sheet; + GdkWindowAttr attributes; + gint attributes_mask; + GdkGCValues values, auxvalues; + GdkColormap *colormap; + gchar *name; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | + GDK_WA_CURSOR; + + attributes.cursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW); + + /* main window */ + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); + + gdk_window_set_user_data (widget->window, sheet); + + widget->style = gtk_style_attach (widget->style, widget->window); + + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + + attributes.x = 0; + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + attributes.x = sheet->row_title_area.width; + attributes.y = 0; + attributes.width = sheet->column_title_area.width; + attributes.height = sheet->column_title_area.height; + + /* column-title window */ + sheet->column_title_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (sheet->column_title_window, sheet); + gtk_style_set_background (widget->style, sheet->column_title_window, GTK_STATE_NORMAL); + + attributes.x = 0; + attributes.y = 0; + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + attributes.y = sheet->column_title_area.height; + attributes.width = sheet->row_title_area.width; + attributes.height = sheet->row_title_area.height; + + /* row-title window */ + sheet->row_title_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (sheet->row_title_window, sheet); + gtk_style_set_background (widget->style, sheet->row_title_window, GTK_STATE_NORMAL); + + /* sheet-window */ + attributes.cursor = gdk_cursor_new(GDK_PLUS); + + attributes.x = 0; + attributes.y = 0; + attributes.width = sheet->sheet_window_width, + attributes.height = sheet->sheet_window_height; + + sheet->sheet_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (sheet->sheet_window, sheet); + + gdk_window_set_background (sheet->sheet_window, &widget->style->white); + gdk_window_show (sheet->sheet_window); + + /* backing_pixmap */ + gtk_sheet_make_backing_pixmap(sheet, 0, 0); + + /* GCs */ + if(sheet->fg_gc) + gdk_gc_unref(sheet->fg_gc); + if(sheet->bg_gc) + gdk_gc_unref(sheet->bg_gc); + sheet->fg_gc = gdk_gc_new (widget->window); + sheet->bg_gc = gdk_gc_new (widget->window); + + colormap = gtk_widget_get_colormap(widget); + + gdk_color_white(colormap, &widget->style->white); + gdk_color_black(colormap, &widget->style->black); + + gdk_gc_get_values(sheet->fg_gc, &auxvalues); + + values.foreground = widget->style->white; + values.function = GDK_INVERT; + values.subwindow_mode = GDK_INCLUDE_INFERIORS; + if(sheet->xor_gc) + gdk_gc_unref(sheet->xor_gc); + sheet->xor_gc = gdk_gc_new_with_values (widget->window, + &values, + GDK_GC_FOREGROUND | + GDK_GC_FUNCTION | + GDK_GC_SUBWINDOW); + +/* create sheet_entry_window */ + + if(GTK_WIDGET_NO_WINDOW(sheet->sheet_entry)){ + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = 0; + attributes.y = 0; + attributes.width = sheet->sheet_entry->requisition.width; + attributes.height = sheet->sheet_entry->requisition.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_EXPOSURE_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + sheet->sheet_entry_window = gdk_window_new (sheet->sheet_window, + &attributes, attributes_mask); + gdk_window_set_user_data (sheet->sheet_entry_window, widget); + } + + if(sheet->sheet_entry->parent){ + gtk_widget_ref(sheet->sheet_entry); + gtk_widget_unparent(sheet->sheet_entry); + } + gtk_widget_set_parent(sheet->sheet_entry, GTK_WIDGET(sheet)); + gtk_widget_set_parent_window (sheet->sheet_entry, + sheet->sheet_entry_window ? + sheet->sheet_entry_window : + sheet->sheet_window); + + if(sheet->button && sheet->button->parent){ + gtk_widget_ref(sheet->button); + gtk_widget_unparent(sheet->button); + } + gtk_widget_set_parent(sheet->button, GTK_WIDGET(sheet)); + gtk_widget_set_parent_window(sheet->button, sheet->sheet_window); +/* + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col); +*/ + if(!sheet->cursor_drag) + sheet->cursor_drag = gdk_cursor_new(GDK_PLUS); + + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->column_title_window); + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->row_title_window); + + size_allocate_row_title_buttons(sheet); + size_allocate_column_title_buttons(sheet); + + name = g_strdup(sheet->name); + gtk_sheet_set_title(sheet, name); + + g_free(name); + + children = sheet->children; + while(children) + { + child = children->data; + children = children->next; + + gtk_sheet_realize_child(sheet, child); + } +} + +static void +create_global_button(GtkSheet *sheet) +{ + sheet->button = gtk_button_new_with_label(" "); + + gtk_widget_ensure_style(sheet->button); + gtk_widget_show(sheet->button); + + gtk_signal_connect (GTK_OBJECT (sheet->button), + "pressed", + (GtkSignalFunc) global_button_clicked, + (gpointer) sheet); +} + +static void +size_allocate_global_button(GtkSheet *sheet) +{ + GtkAllocation allocation; + + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) return; + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) return; + + gtk_widget_size_request(sheet->button, NULL); + + allocation.x=0; + allocation.y=0; + allocation.width=sheet->row_title_area.width; + allocation.height=sheet->column_title_area.height; + + gtk_widget_size_allocate(sheet->button, &allocation); + gtk_widget_show(sheet->button); +} + +static void +global_button_clicked(GtkWidget *widget, gpointer data) +{ + gboolean veto; + + gtk_sheet_click_cell(GTK_SHEET(data), -1, -1, &veto); + gtk_widget_grab_focus(GTK_WIDGET(data)); +} + + +static void +gtk_sheet_unrealize (GtkWidget * widget) +{ + GtkSheet *sheet; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED | GTK_MAPPED); + + gdk_cursor_destroy (sheet->cursor_drag); + + gdk_gc_destroy (sheet->xor_gc); + gdk_gc_destroy (sheet->fg_gc); + gdk_gc_destroy (sheet->bg_gc); + + gtk_style_detach (widget->style); + + gdk_window_destroy (sheet->sheet_window); + gdk_window_destroy (sheet->column_title_window); + gdk_window_destroy (sheet->row_title_window); + gdk_window_set_user_data (widget->window, NULL); + gdk_window_destroy (widget->window); + + if (sheet->pixmap){ + g_free (sheet->pixmap); + sheet->pixmap = NULL; + } + + widget->window = NULL; + sheet->column_title_window=NULL; + sheet->sheet_window = NULL; + sheet->sheet_entry_window = NULL; + sheet->cursor_drag = NULL; + sheet->xor_gc = NULL; + sheet->fg_gc = NULL; + sheet->bg_gc = NULL; + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + if(child->window) + { + gdk_window_set_user_data(child->window, NULL); + gdk_window_destroy(child->window); + child->window = NULL; + } + + } + +} + +static void +gtk_sheet_map (GtkWidget * widget) +{ + GtkSheet *sheet; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + + if (!GTK_WIDGET_MAPPED (widget)) + { + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + + if(!sheet->cursor_drag) sheet->cursor_drag=gdk_cursor_new(GDK_PLUS); + + gdk_window_show (widget->window); + + gdk_window_show (sheet->sheet_window); + + if(sheet->sheet_entry_window) + gdk_window_show(sheet->sheet_entry_window); + + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)){ + gdk_window_show (sheet->column_title_window); + } + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)){ + gdk_window_show (sheet->row_title_window); + } + + if(!GTK_WIDGET_MAPPED (sheet->sheet_entry)){ + gtk_widget_show (sheet->sheet_entry); + gtk_widget_map (sheet->sheet_entry); + } + + if (GTK_WIDGET_VISIBLE (sheet->button) && + !GTK_WIDGET_MAPPED (sheet->button)){ + gtk_widget_show(sheet->button); + gtk_widget_map (sheet->button); + } + + if(GTK_BIN(sheet->button)->child) + if (GTK_WIDGET_VISIBLE (GTK_BIN(sheet->button)->child) && + !GTK_WIDGET_MAPPED (GTK_BIN(sheet->button)->child)) + gtk_widget_map (GTK_BIN(sheet->button)->child); + + gtk_sheet_range_draw(sheet, NULL); + gtk_sheet_activate_cell(sheet, + sheet->active_cell.row, + sheet->active_cell.col); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget) && + !GTK_WIDGET_MAPPED (child->widget)){ + gtk_widget_map (child->widget); + gtk_sheet_position_child(sheet, child); + if (GTK_WIDGET_NO_WINDOW(child->widget) && child->window) + gdk_window_show(child->window); + } + } + + } +} + +static void +gtk_sheet_unmap (GtkWidget * widget) +{ + GtkSheet *sheet; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + + if (GTK_WIDGET_MAPPED (widget)) + { + GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); + + gdk_window_hide (sheet->sheet_window); + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + gdk_window_hide (sheet->column_title_window); + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + gdk_window_hide (sheet->row_title_window); + gdk_window_hide (widget->window); + + if(sheet->sheet_entry_window) + gdk_window_hide (sheet->sheet_entry_window); + + if (GTK_WIDGET_MAPPED (sheet->sheet_entry)) + gtk_widget_unmap (sheet->sheet_entry); + + if (GTK_WIDGET_MAPPED (sheet->button)) + gtk_widget_unmap (sheet->button); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget) && + GTK_WIDGET_MAPPED (child->widget)) + { + gtk_widget_unmap (child->widget); + if(child->window) gdk_window_hide(child->window); + } + } + + } +} + + +static void +gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col) +{ + GtkWidget *widget; + GdkGC *fg_gc, *bg_gc; + GtkSheetCellAttr attributes; + GdkRectangle area; + + g_return_if_fail (sheet != NULL); + + /* bail now if we arn't drawable yet */ + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + if (row < 0 || row > sheet->maxrow) return; + if (col < 0 || col > sheet->maxcol) return; + if (!sheet->column[col].is_visible) return; + if (!sheet->row[row].is_visible) return; + + widget = GTK_WIDGET (sheet); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + /* select GC for background rectangle */ + gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground); + gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); + + fg_gc = sheet->fg_gc; + bg_gc = sheet->bg_gc; + + area.x=COLUMN_LEFT_XPIXEL(sheet,col); + area.y=ROW_TOP_YPIXEL(sheet,row); + area.width=sheet->column[col].width; + area.height=sheet->row[row].height; + + gdk_draw_rectangle (sheet->pixmap, + bg_gc, + TRUE, + area.x, + area.y, + area.width, + area.height); + + gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0); + + if(sheet->show_grid && attributes.background.pixel == sheet->bg_color.pixel){ + gdk_gc_set_foreground (sheet->bg_gc, &sheet->grid_color); + gdk_draw_rectangle (sheet->pixmap, + sheet->bg_gc, + FALSE, + area.x, area.y, + area.width, area.height); + } +} + +static void +gtk_sheet_cell_draw_border (GtkSheet *sheet, gint row, gint col, gint mask) +{ + GtkWidget *widget; + GdkGC *fg_gc, *bg_gc; + GtkSheetCellAttr attributes; + GdkRectangle area; + guint width; + + g_return_if_fail (sheet != NULL); + + /* bail now if we arn't drawable yet */ + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + if (row < 0 || row > sheet->maxrow) return; + if (col < 0 || col > sheet->maxcol) return; + if (!sheet->column[col].is_visible) return; + if (!sheet->row[row].is_visible) return; + + widget = GTK_WIDGET (sheet); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + /* select GC for background rectangle */ + gdk_gc_set_foreground (sheet->fg_gc, &attributes.border.color); + gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); + + fg_gc = sheet->fg_gc; + bg_gc = sheet->bg_gc; + + area.x=COLUMN_LEFT_XPIXEL(sheet,col); + area.y=ROW_TOP_YPIXEL(sheet,row); + area.width=sheet->column[col].width; + area.height=sheet->row[row].height; + + width = attributes.border.width; + gdk_gc_set_line_attributes(sheet->fg_gc, attributes.border.width, + attributes.border.line_style, + attributes.border.cap_style, + attributes.border.join_style); + + if(width>0){ + if(attributes.border.mask & GTK_SHEET_LEFT_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x, area.y-width/2, + area.x, area.y+area.height+width/2+1); + + if(attributes.border.mask & GTK_SHEET_RIGHT_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x+area.width, area.y-width/2, + area.x+area.width, + area.y+area.height+width/2+1); + + if(attributes.border.mask & GTK_SHEET_TOP_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x-width/2,area.y, + area.x+area.width+width/2+1, + area.y); + + if(attributes.border.mask & GTK_SHEET_BOTTOM_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x-width/2, area.y+area.height, + area.x+area.width+width/2+1, + area.y+area.height); + + } + +} + + +static void +gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col) +{ + GtkWidget *widget; + GdkRectangle area, clip_area; + gint i; + gint text_width, text_height, y; + gint xoffset=0; + gint size, sizel, sizer; + GdkGC *fg_gc, *bg_gc; + GtkSheetCellAttr attributes; + char *label; + + g_return_if_fail (sheet != NULL); + + /* bail now if we arn't drawable yet */ + if (!GTK_WIDGET_DRAWABLE (sheet)) + return; + + if (row > sheet->maxallocrow) return; + if (col > sheet->maxalloccol) return; + if (!sheet->data[row]) return; + if (!sheet->data[row][col]) return; + if (!sheet->data[row][col]->text || strlen(sheet->data[row][col]->text)==0) + return; + + if (row < 0 || row > sheet->maxrow) return; + if (col < 0 || col > sheet->maxcol) return; + if (!sheet->column[col].is_visible) return; + if (!sheet->row[row].is_visible) return; + + + widget = GTK_WIDGET(sheet); + + label = sheet->data[row][col]->text; + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + /* select GC for background rectangle */ + gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground); + gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); + gdk_gc_set_font(sheet->fg_gc, attributes.font); + + fg_gc = sheet->fg_gc; + bg_gc = sheet->bg_gc; + + area.x=COLUMN_LEFT_XPIXEL(sheet,col); + area.y=ROW_TOP_YPIXEL(sheet,row); + area.width=sheet->column[col].width; + area.height=sheet->row[row].height; + + clip_area = area; + + text_width = gdk_string_width (attributes.font, label); + text_height = attributes.font->ascent + attributes.font->descent; + y = area.y + area.height - CELLOFFSET; + y = y - text_height + attributes.font->ascent; + + switch(attributes.justification){ + case GTK_JUSTIFY_RIGHT: + size=area.width; + area.x+=area.width; + if(!GTK_SHEET_CLIP_TEXT(sheet)){ + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(size>=text_width+CELLOFFSET) break; + size+=sheet->column[i].width; + sheet->column[i].right_text_column = MAX(col, sheet->column[i].right_text_column); + } + area.width=size; + } + area.x-=size; + xoffset+=area.width-text_width - 2 * CELLOFFSET - + attributes.border.width/2; + break; + case GTK_JUSTIFY_CENTER: + sizel=area.width/2; + sizer=area.width/2; + area.x+=area.width/2; + if(!GTK_SHEET_CLIP_TEXT(sheet)){ + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(sizer>=text_width/2) break; + sizer+=sheet->column[i].width; + sheet->column[i].left_text_column = MIN(col, sheet->column[i].left_text_column); + } + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(sizel>=text_width/2) break; + sizel+=sheet->column[i].width; + sheet->column[i].right_text_column = MAX(col, sheet->column[i].right_text_column); + } + size=MIN(sizel, sizer); + } + area.x-=sizel; + xoffset+= sizel - text_width/2 - CELLOFFSET; + area.width=sizel+sizer; + break; + case GTK_JUSTIFY_LEFT: + default: + size=area.width; + if(!GTK_SHEET_CLIP_TEXT(sheet)){ + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(size>=text_width+CELLOFFSET) break; + size+=sheet->column[i].width; + sheet->column[i].left_text_column = MIN(col, sheet->column[i].left_text_column); + } + area.width=size; + } + xoffset += attributes.border.width/2; + break; + } + + if(!GTK_SHEET_CLIP_TEXT(sheet)) clip_area = area; + gdk_gc_set_clip_rectangle(fg_gc, &clip_area); + + gdk_draw_string (sheet->pixmap, + attributes.font, + fg_gc, + area.x + xoffset + CELLOFFSET, + y, + label); + + gdk_gc_set_clip_rectangle(fg_gc, NULL); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + area.x, + area.y, + area.x, + area.y, + area.width, + area.height); + + +} + + + +static void +gtk_sheet_range_draw(GtkSheet *sheet, const GtkSheetRange *range) +{ + gint i,j; + GtkSheetRange drawing_range; + GdkRectangle area; + + g_return_if_fail(sheet != NULL); + g_return_if_fail(GTK_SHEET(sheet)); + + if(!GTK_WIDGET_DRAWABLE(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_MAPPED(GTK_WIDGET(sheet))) return; + + if(range == NULL) + { + drawing_range.row0=MIN_VISIBLE_ROW(sheet); + drawing_range.col0=MIN_VISIBLE_COLUMN(sheet); + drawing_range.rowi=MAX_VISIBLE_ROW(sheet); + drawing_range.coli=MAX_VISIBLE_COLUMN(sheet); +/* + gdk_draw_rectangle (sheet->pixmap, + GTK_WIDGET(sheet)->style->white_gc, + TRUE, + 0,0, + sheet->sheet_window_width,sheet->sheet_window_height); +*/ + } + else + { + drawing_range.row0=MAX(range->row0, MIN_VISIBLE_ROW(sheet)); + drawing_range.col0=MAX(range->col0, MIN_VISIBLE_COLUMN(sheet)); + drawing_range.rowi=MIN(range->rowi, MAX_VISIBLE_ROW(sheet)); + drawing_range.coli=MIN(range->coli, MAX_VISIBLE_COLUMN(sheet)); + } + + if(drawing_range.coli == sheet->maxcol){ + area.x=COLUMN_LEFT_XPIXEL(sheet,sheet->maxcol)+ + sheet->column[sheet->maxcol].width+1; + area.y=0; + + gdk_gc_set_foreground(sheet->fg_gc, &sheet->bg_color); + gdk_draw_rectangle (sheet->pixmap, + sheet->fg_gc, + TRUE, + area.x,area.y, + sheet->sheet_window_width - area.x, + sheet->sheet_window_height); + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + area.x, + area.y, + area.x, + area.y, + sheet->sheet_window_width - area.x, + sheet->sheet_window_height); + + } + if(drawing_range.rowi == sheet->maxrow){ + area.x=0; + area.y=ROW_TOP_YPIXEL(sheet,sheet->maxrow)+sheet->row[sheet->maxrow].height+1; + + gdk_gc_set_foreground(sheet->fg_gc, &sheet->bg_color); + gdk_draw_rectangle (sheet->pixmap, + sheet->fg_gc, + TRUE, + area.x,area.y, + sheet->sheet_window_width, + sheet->sheet_window_height - area.y); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + area.x, + area.y, + area.x, + area.y, + sheet->sheet_window_width, + sheet->sheet_window_height - area.y); + } + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.col0; j<=drawing_range.coli; j++){ + gtk_sheet_cell_draw_default(sheet, i, j); + } + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.col0; j<=drawing_range.coli; j++){ + gtk_sheet_cell_draw_border(sheet, i-1, j, GTK_SHEET_BOTTOM_BORDER); + gtk_sheet_cell_draw_border(sheet, i+1, j, GTK_SHEET_TOP_BORDER); + gtk_sheet_cell_draw_border(sheet, i, j-1, GTK_SHEET_RIGHT_BORDER); + gtk_sheet_cell_draw_border(sheet, i, j+1, GTK_SHEET_LEFT_BORDER); + gtk_sheet_cell_draw_border(sheet, i, j, 15); + } + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.col0; j<=drawing_range.coli; j++) + if(i<=sheet->maxallocrow && j<=sheet->maxalloccol && + sheet->data[i] && sheet->data[i][j]) + gtk_sheet_cell_draw_label (sheet, i, j); + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=sheet->column[drawing_range.col0].left_text_column; j<drawing_range.col0; j++) + if(i<=sheet->maxallocrow && j<=sheet->maxalloccol && + sheet->data[i] && sheet->data[i][j]) + gtk_sheet_cell_draw_label (sheet, i, j); + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.coli+1; j<=sheet->column[drawing_range.coli].right_text_column; j++) + if(i<=sheet->maxallocrow && j<=sheet->maxalloccol && + sheet->data[i] && sheet->data[i][j]) + gtk_sheet_cell_draw_label (sheet, i, j); + + gtk_sheet_draw_backing_pixmap(sheet, drawing_range); + + if(sheet->state != GTK_SHEET_NORMAL && gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_range_draw_selection(sheet, drawing_range); + + if(sheet->state == GTK_STATE_NORMAL && + sheet->active_cell.row >= drawing_range.row0 && + sheet->active_cell.row <= drawing_range.rowi && + sheet->active_cell.col >= drawing_range.col0 && + sheet->active_cell.col <= drawing_range.coli) + gtk_sheet_show_active_cell(sheet); +} + +static void +gtk_sheet_range_draw_selection(GtkSheet *sheet, GtkSheetRange range) +{ + GdkRectangle area; + gint i,j; + GtkSheetRange aux; + + if(range.col0 > sheet->range.coli || range.coli < sheet->range.col0 || + range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0) + return; + + if(!gtk_sheet_range_isvisible(sheet, range)) return; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + aux=range; + + range.col0=MAX(sheet->range.col0, range.col0); + range.coli=MIN(sheet->range.coli, range.coli); + range.row0=MAX(sheet->range.row0, range.row0); + range.rowi=MIN(sheet->range.rowi, range.rowi); + + range.col0=MAX(range.col0, MIN_VISIBLE_COLUMN(sheet)); + range.coli=MIN(range.coli, MAX_VISIBLE_COLUMN(sheet)); + range.row0=MAX(range.row0, MIN_VISIBLE_ROW(sheet)); + range.rowi=MIN(range.rowi, MAX_VISIBLE_ROW(sheet)); + + for(i=range.row0; i<=range.rowi; i++){ + for(j=range.col0; j<=range.coli; j++){ + + if(gtk_sheet_cell_get_state(sheet, i, j)==GTK_STATE_SELECTED && + sheet->column[j].is_visible && sheet->row[i].is_visible){ + + row_button_set(sheet, i); + column_button_set(sheet, j); + + area.x=COLUMN_LEFT_XPIXEL(sheet,j); + area.y=ROW_TOP_YPIXEL(sheet,i); + area.width=sheet->column[j].width; + area.height=sheet->row[i].height; + + if(i==sheet->range.row0){ + area.y=area.y+2; + area.height=area.height-2; + } + if(i==sheet->range.rowi) area.height=area.height-3; + if(j==sheet->range.col0){ + area.x=area.x+2; + area.width=area.width-2; + } + if(j==sheet->range.coli) area.width=area.width-3; + + if(i!=sheet->active_cell.row || j!=sheet->active_cell.col){ + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + area.x+1,area.y+1, + area.width,area.height); + } + } + + } + } + + gtk_sheet_draw_border(sheet, sheet->range); + +} + +static void +gtk_sheet_draw_backing_pixmap(GtkSheet *sheet, GtkSheetRange range) +{ + gint x,y,width,height; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + x=COLUMN_LEFT_XPIXEL(sheet,range.col0); + y=ROW_TOP_YPIXEL(sheet, range.row0); + width=COLUMN_LEFT_XPIXEL(sheet, range.coli)-x+sheet->column[range.coli].width; + height=ROW_TOP_YPIXEL(sheet, range.rowi)-y+sheet->row[range.rowi].height; + + if(range.row0==sheet->range.row0){ + y=y-5; + height=height+5; + } + if(range.rowi==sheet->range.rowi) height=height+5; + if(range.col0==sheet->range.col0){ + x=x-5; + width=width+5; + } + if(range.coli==sheet->range.coli) width=width+5; + + + width=MIN(width, sheet->sheet_window_width-x); + height=MIN(height, sheet->sheet_window_height-y); + + x--; + y--; + width+=2; + height+=2; + + x = (GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + ? MAX(x, sheet->row_title_area.width) : MAX(x, 0); + y = (GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + ? MAX(y, sheet->column_title_area.height) : MAX(y, 0); + + if(range.coli==sheet->maxcol) width=sheet->sheet_window_width-x; + if(range.rowi==sheet->maxrow) height=sheet->sheet_window_height-y; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, + y, + x, + y, + width+1, + height+1); + +} + +static GtkSheetCell * +gtk_sheet_cell_new() +{ + GtkSheetCell *cell; + cell = g_new(GtkSheetCell, 1); + cell->text = NULL; + cell->link = NULL; + cell->attributes = NULL; + return cell; +} + +void +gtk_sheet_set_cell_text(GtkSheet *sheet, gint row, gint col, const gchar *text) +{ + GtkSheetCellAttr attributes; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (col > sheet->maxcol || row > sheet->maxrow) return; + if (col < 0 || row < 0) return; + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + gtk_sheet_set_cell(sheet, row, col, attributes.justification, text); +} + +void +gtk_sheet_set_cell(GtkSheet *sheet, gint row, gint col, + GtkJustification justification, + const gchar *text) +{ + GtkSheetCell **cell; + GtkSheetRange range; + gint text_width; + GtkSheetCellAttr attributes; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (col > sheet->maxcol || row > sheet->maxrow) return; + if (col < 0 || row < 0) return; + + CheckBounds(sheet, row, col); + + cell=&sheet->data[row][col]; + + if(*cell==NULL) + (*cell) = gtk_sheet_cell_new(); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + (*cell)->row = row; + (*cell)->col = col; + + attributes.justification = justification; + gtk_sheet_set_cell_attributes(sheet, row, col, attributes); + + if((*cell)->text){ + g_free((*cell)->text); + (*cell)->text = NULL; + } + + if(text) + (*cell)->text=g_strdup(text); + + if(attributes.is_visible){ + + text_width = 0; + if((*cell)->text && strlen((*cell)->text) > 0) { + text_width = gdk_string_width (attributes.font, (*cell)->text); + } + + range.row0 = row; + range.rowi = row; + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + if(GTK_SHEET_AUTORESIZE(sheet) && + text_width > sheet->column[col].width-2*CELLOFFSET-attributes.border.width){ + gtk_sheet_set_column_width(sheet, col, text_width+2*CELLOFFSET+attributes.border.width); + } + else + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + } + + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, col); + +} + +void +gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column) +{ + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (column > sheet->maxcol || row > sheet->maxrow) return; + if (column > sheet->maxalloccol || row > sheet->maxallocrow) return; + if (column < 0 || row < 0) return; + + range.row0 = row; + range.rowi = row; + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + gtk_sheet_real_cell_clear(sheet, row, column, FALSE); + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_range_draw(sheet, &range); + } +} + +void +gtk_sheet_cell_delete (GtkSheet *sheet, gint row, gint column) +{ + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (column > sheet->maxcol || row > sheet->maxrow) return; + if (column > sheet->maxalloccol || row > sheet->maxallocrow) return; + if (column < 0 || row < 0) return; + + range.row0 = row; + range.rowi = row; + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + gtk_sheet_real_cell_clear(sheet, row, column, TRUE); + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_range_draw(sheet, &range); + } +} + +static void +gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column, gboolean delete) +{ + gchar *text; + gpointer link; + + if(row > sheet->maxallocrow || column > sheet->maxalloccol) return; + if(!sheet->data[row]) return; + if(!sheet->data[row][column]) return; + + text = gtk_sheet_cell_get_text(sheet, row, column); + link = gtk_sheet_get_link(sheet, row, column); + + if(text){ + g_free(sheet->data[row][column]->text); + sheet->data[row][column]->text = NULL; + + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CLEAR_CELL], row, column); + } + + if(delete){ + if(sheet->data[row][column]->attributes){ + g_free(sheet->data[row][column]->attributes); + sheet->data[row][column]->attributes = NULL; + } + sheet->data[row][column]->link = NULL; + + if(sheet->data[row][column]) g_free(sheet->data[row][column]); + + sheet->data[row][column] = NULL; + } + +} + +void +gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + gtk_sheet_real_range_clear(sheet, range, FALSE); +} + +void +gtk_sheet_range_delete (GtkSheet *sheet, const GtkSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + gtk_sheet_real_range_clear(sheet, range, TRUE); +} + +static void +gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range, + gboolean delete) +{ + gint i, j; + GtkSheetRange clear; + + if(!range){ + clear.row0=0; + clear.rowi=sheet->maxallocrow; + clear.col0=0; + clear.coli=sheet->maxalloccol; + }else + clear=*range; + + clear.row0=MAX(clear.row0, 0); + clear.col0=MAX(clear.col0, 0); + clear.rowi=MIN(clear.rowi, sheet->maxallocrow); + clear.coli=MIN(clear.coli, sheet->maxalloccol); + + for(i=clear.row0; i<=clear.rowi; i++) + for(j=clear.col0; j<=clear.coli; j++){ + gtk_sheet_real_cell_clear(sheet, i, j, delete); + } + + gtk_sheet_range_draw(sheet, NULL); +} + + +gchar * +gtk_sheet_cell_get_text (GtkSheet *sheet, gint row, gint col) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + if(col > sheet->maxcol || row > sheet->maxrow) return NULL; + if(col < 0 || row < 0) return NULL; + if(row > sheet->maxallocrow || col > sheet->maxalloccol) return NULL; + if(!sheet->data[row]) return NULL; + if(!sheet->data[row][col]) return NULL; + if(!sheet->data[row][col]->text) return NULL; + if(strlen(sheet->data[row][col]->text) == 0) return NULL; + + return (sheet->data[row][col]->text); +} + +void +gtk_sheet_link_cell(GtkSheet *sheet, gint row, gint col, gpointer link) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if(col > sheet->maxcol || row > sheet->maxrow) return; + if(col < 0 || row < 0) return; + + if(row > sheet->maxallocrow || col > sheet->maxalloccol || + !sheet->data[row] || !sheet->data[row][col]) + gtk_sheet_set_cell_text(sheet, row, col, ""); + + sheet->data[row][col]->link = link; +} + +gpointer +gtk_sheet_get_link(GtkSheet *sheet, gint row, gint col) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + if(col > sheet->maxcol || row > sheet->maxrow) return NULL; + if(col < 0 || row < 0) return NULL; + + if (row > sheet->maxallocrow || col > sheet->maxalloccol) return NULL; + if (!sheet->data[row]) return NULL; /* Added by Chris Howell */ + if (!sheet->data[row][col]) return NULL; /* Added by Bob Lissner */ + + return(sheet->data[row][col]->link); +} + +void +gtk_sheet_remove_link(GtkSheet *sheet, gint row, gint col) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if(col > sheet->maxcol || row > sheet->maxrow) return; + if(col < 0 || row < 0) return; + + /* Fixed by Andreas Voegele */ + if(row < sheet->maxallocrow && col < sheet->maxalloccol && + sheet->data[row] && sheet->data[row][col] && + sheet->data[row][col]->link) + sheet->data[row][col]->link = NULL; +} + + +GtkStateType +gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col) +{ + gint state; + GtkSheetRange *range; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + if(col > sheet->maxcol || row > sheet->maxrow) return 0; + if(col < 0 || row < 0) return 0; + + state = sheet->state; + range = &sheet->range; + + switch (state){ + case GTK_SHEET_NORMAL: + return GTK_STATE_NORMAL; + break; + case GTK_SHEET_ROW_SELECTED: + if(row>=range->row0 && row<=range->rowi) + return GTK_STATE_SELECTED; + break; + case GTK_SHEET_COLUMN_SELECTED: + if(col>=range->col0 && col<=range->coli) + return GTK_STATE_SELECTED; + break; + case GTK_SHEET_RANGE_SELECTED: + if(row >= range->row0 && row <= range->rowi && \ + col >= range->col0 && col <= range->coli) + return GTK_STATE_SELECTED; + break; + } + return GTK_STATE_NORMAL; +} + +gboolean +gtk_sheet_get_pixel_info (GtkSheet * sheet, + gint x, + gint y, + gint * row, + gint * column) +{ + gint trow, tcol; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + /* bounds checking, return false if the user clicked + * on a blank area */ + trow = ROW_FROM_YPIXEL (sheet, y); + if (trow > sheet->maxrow) + return FALSE; + + *row = trow; + + tcol = COLUMN_FROM_XPIXEL (sheet, x); + if (tcol > sheet->maxcol) + return FALSE; + + *column = tcol; + + return TRUE; +} + +gboolean +gtk_sheet_get_cell_area (GtkSheet * sheet, + gint row, + gint column, + GdkRectangle *area) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + if(row > sheet->maxrow || column > sheet->maxcol) return FALSE; + + area->x = (column == -1) ? 0 : (COLUMN_LEFT_XPIXEL(sheet, column) - + ((GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + ? sheet->row_title_area.width + : 0)); + area->y = (row == -1) ? 0 : (ROW_TOP_YPIXEL(sheet, row) - + ((GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + ? sheet->column_title_area.height + : 0)); + area->width= (column == -1) ? sheet->row_title_area.width + : sheet->column[column].width; + area->height= (row == -1) ? sheet->column_title_area.height + : sheet->row[row].height; + +/* + if(row < 0 || column < 0) return FALSE; + + area->x = COLUMN_LEFT_XPIXEL(sheet, column); + area->y = ROW_TOP_YPIXEL(sheet, row); + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + area->x -= sheet->row_title_area.width; + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + area->y -= sheet->column_title_area.height; + + area->width=sheet->column[column].width; + area->height=sheet->row[row].height; +*/ + return TRUE; +} + +gboolean +gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + if(row < 0 || column < 0) return FALSE; + if(row > sheet->maxrow || column > sheet->maxcol) return FALSE; + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) + { + if(!gtk_sheet_deactivate_cell(sheet)) return FALSE; + } + + sheet->active_cell.row=row; + sheet->active_cell.col=column; + + if(!gtk_sheet_activate_cell(sheet, row, column)) return FALSE; + + return TRUE; +} + +void +gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + *row = sheet->active_cell.row; + *column = sheet->active_cell.col; +} + +static void +gtk_sheet_entry_changed(GtkWidget *widget, gpointer data) +{ + GtkSheet *sheet; + gint row,col; + char *text; + GtkJustification justification; + GtkSheetCellAttr attributes; + + g_return_if_fail (data != NULL); + g_return_if_fail (GTK_IS_SHEET (data)); + + sheet=GTK_SHEET(data); + + if(!GTK_WIDGET_VISIBLE(widget)) return; + if(sheet->state != GTK_STATE_NORMAL) return; + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + if(row<0 || col<0) return; + + sheet->active_cell.row=-1; + sheet->active_cell.col=-1; + + text = (gchar *) gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + if(text && strlen(text)!=0){ + gtk_sheet_get_attributes(sheet, row, col, &attributes); + justification=attributes.justification; + gtk_sheet_set_cell(sheet, row, col, justification, text); + } + else + { + /* Added by Matias Mutchinick */ + gtk_sheet_cell_clear(sheet, row, col); + } + + if(sheet->freeze_count == 0) + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + sheet->active_cell.row=row;; + sheet->active_cell.col=col; + +} + + +static gboolean +gtk_sheet_deactivate_cell(GtkSheet *sheet) +{ + gboolean veto = TRUE; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return FALSE; + if(sheet->state != GTK_SHEET_NORMAL) return FALSE; + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[DEACTIVATE], + sheet->active_cell.row, + sheet->active_cell.col, &veto); + + if(!veto) return FALSE; + + gtk_signal_disconnect_by_func(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + (GtkSignalFunc) gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + + gtk_sheet_hide_active_cell(sheet); + + sheet->active_cell.row=-1; + sheet->active_cell.col=-1; + + return TRUE; +} + +static void +gtk_sheet_hide_active_cell(GtkSheet *sheet) +{ + char *text; + gint row,col; + GtkJustification justification; + GtkSheetCellAttr attributes; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + if(row < 0 || col < 0) return; + + if(sheet->freeze_count == 0) + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + text = (gchar *) gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + justification=attributes.justification; + + if(text && strlen(text)!=0){ + gtk_sheet_set_cell(sheet, row, col, justification, text); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[SET_CELL], row, col); + } + else + { + gtk_sheet_cell_clear(sheet, row, col); + } + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + column_button_release(sheet, col); + row_button_release(sheet, row); + + if(sheet->sheet_entry_window) + gdk_window_hide(sheet->sheet_entry_window); + else + gdk_window_hide(sheet->sheet_entry->window); + + if(row != -1 && col != -1) + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + COLUMN_LEFT_XPIXEL(sheet,col)-1, + ROW_TOP_YPIXEL(sheet,row)-1, + COLUMN_LEFT_XPIXEL(sheet,col)-1, + ROW_TOP_YPIXEL(sheet,row)-1, + sheet->column[col].width+4, + sheet->row[row].height+4); + + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_WIDGET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(sheet->sheet_entry), GTK_VISIBLE); + +} + +static gboolean +gtk_sheet_activate_cell(GtkSheet *sheet, gint row, gint col) +{ + gboolean veto = TRUE; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + if(row < 0 || col < 0) return FALSE; + if(row > sheet->maxrow || col > sheet->maxcol) return FALSE; + +/* gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[ACTIVATE], row, col, &veto); + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return veto; +*/ + + if(!veto) return FALSE; + if(sheet->state != GTK_SHEET_NORMAL){ + sheet->state=GTK_SHEET_NORMAL; + gtk_sheet_real_unselect_range(sheet, NULL); + } + + sheet->range.row0=row; + sheet->range.col0=col; + sheet->range.rowi=row; + sheet->range.coli=col; + sheet->active_cell.row=row; + sheet->active_cell.col=col; + sheet->selection_cell.row=row; + sheet->selection_cell.col=col; + row_button_set(sheet, row); + column_button_set(sheet, col); + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + gtk_sheet_show_active_cell(sheet); + + gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "changed", + (GtkSignalFunc)gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[ACTIVATE], row, col, &veto); + + return TRUE; +} + +static void +gtk_sheet_show_active_cell(GtkSheet *sheet) +{ + GtkSheetCell *cell; + GtkEntry *sheet_entry; + GtkSheetCellAttr attributes; + gchar *text = NULL; + GtkJustification justification; + gint row, col; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + /* Don't show the active cell, if there is no active cell: */ + if(!(row >= 0 && col >= 0)) /* e.g row or coll == -1. */ + return; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + if(sheet->state != GTK_SHEET_NORMAL) return; + if(GTK_SHEET_IN_SELECTION(sheet)) return; + + GTK_WIDGET_SET_FLAGS(GTK_WIDGET(sheet->sheet_entry), GTK_VISIBLE); + + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry(sheet)); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + justification = GTK_JUSTIFY_LEFT; + + if(GTK_SHEET_JUSTIFY_ENTRY(sheet)) + justification = attributes.justification; + + if(row <= sheet->maxallocrow && col <= sheet->maxalloccol) { + if(sheet->data[row]) { + if(sheet->data[row][col]) { + cell = sheet->data[row][col]; + if(cell->text) + text = g_strdup(cell->text); + } + } + } + + if(!text) text = (gchar *) g_strdup(""); + + if(!GTK_IS_ITEM_ENTRY(sheet_entry)) + gtk_entry_set_text(GTK_ENTRY(sheet_entry), text); + else + gtk_item_entry_set_text(GTK_ITEM_ENTRY(sheet_entry), (const gchar *) text, justification); + + if(GTK_SHEET_IS_LOCKED(sheet) || !attributes.is_editable) + gtk_entry_set_editable(GTK_ENTRY(sheet_entry), FALSE); + else + gtk_entry_set_editable(GTK_ENTRY(sheet_entry), TRUE); + + gtk_entry_set_visibility(GTK_ENTRY(sheet_entry), attributes.is_visible); + + gtk_sheet_entry_set_max_size(sheet); + gtk_sheet_size_allocate_entry(sheet); + + if(GTK_WIDGET_REALIZED(sheet->sheet_entry)){ + if(sheet->sheet_entry_window) + gdk_window_show(sheet->sheet_entry_window); + else + gdk_window_show(sheet->sheet_entry->window); + gtk_widget_queue_draw(sheet->sheet_entry); + } + gtk_sheet_draw_active_cell(sheet); + + gtk_widget_grab_focus(GTK_WIDGET(sheet_entry)); + GTK_WIDGET_SET_FLAGS(GTK_WIDGET(sheet_entry), GTK_HAS_FOCUS); + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(sheet), GTK_HAS_FOCUS); + + g_free(text); +} + +static void +gtk_sheet_draw_active_cell(GtkSheet *sheet) +{ + gint row, col; + + if(!GTK_WIDGET_DRAWABLE(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + if(row<0 || col<0) return; + + if(!gtk_sheet_cell_isvisible(sheet, row, col)) return; + + row_button_set(sheet, row); + column_button_set(sheet, col); + + gtk_sheet_draw_backing_pixmap(sheet, sheet->range); + gtk_sheet_draw_border(sheet, sheet->range); + +} + + +static void +gtk_sheet_make_backing_pixmap (GtkSheet *sheet, guint width, guint height) +{ + gint pixmap_width, pixmap_height; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + if(width == 0 && height == 0){ + width=sheet->sheet_window_width+80; + height=sheet->sheet_window_height+80; + } + + if (!sheet->pixmap) + { + /* allocate */ + sheet->pixmap = gdk_pixmap_new (sheet->sheet_window, + width, height, + -1); + if(!GTK_SHEET_IS_FROZEN(sheet)) gtk_sheet_range_draw(sheet, NULL); + } + else + { + /* reallocate if sizes don't match */ + gdk_window_get_size (sheet->pixmap, + &pixmap_width, &pixmap_height); + if ((pixmap_width != width) || (pixmap_height != height)) + { + g_free(sheet->pixmap); + sheet->pixmap = gdk_pixmap_new (sheet->sheet_window, + width, height, + -1); + if(!GTK_SHEET_IS_FROZEN(sheet)) gtk_sheet_range_draw(sheet, NULL); + } + } +} + +static void +gtk_sheet_new_selection(GtkSheet *sheet, GtkSheetRange *range) +{ + gint i,j, mask1, mask2; + gint state, selected; + gint x,y,width,height; + GtkSheetRange new_range, aux_range; + + g_return_if_fail (sheet != NULL); + + if(range==NULL) range=&sheet->range; + + new_range=*range; + + range->row0=MIN(range->row0, sheet->range.row0); + range->rowi=MAX(range->rowi, sheet->range.rowi); + range->col0=MIN(range->col0, sheet->range.col0); + range->coli=MAX(range->coli, sheet->range.coli); + + range->row0=MAX(range->row0, MIN_VISIBLE_ROW(sheet)); + range->rowi=MIN(range->rowi, MAX_VISIBLE_ROW(sheet)); + range->col0=MAX(range->col0, MIN_VISIBLE_COLUMN(sheet)); + range->coli=MIN(range->coli, MAX_VISIBLE_COLUMN(sheet)); + + aux_range.row0=MAX(new_range.row0, MIN_VISIBLE_ROW(sheet)); + aux_range.rowi=MIN(new_range.rowi, MAX_VISIBLE_ROW(sheet)); + aux_range.col0=MAX(new_range.col0, MIN_VISIBLE_COLUMN(sheet)); + aux_range.coli=MIN(new_range.coli, MAX_VISIBLE_COLUMN(sheet)); + + for(i=range->row0; i<=range->rowi; i++){ + for(j=range->col0; j<=range->coli; j++){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + selected=(i<=new_range.rowi && i>=new_range.row0 && + j<=new_range.coli && j>=new_range.col0) ? TRUE : FALSE; + + if(state==GTK_STATE_SELECTED && selected && + sheet->column[j].is_visible && sheet->row[i].is_visible && + (i==sheet->range.row0 || i==sheet->range.rowi || + j==sheet->range.col0 || j==sheet->range.coli || + i==new_range.row0 || i==new_range.rowi || + j==new_range.col0 || j==new_range.coli)){ + + mask1 = i==sheet->range.row0 ? 1 : 0; + mask1 = i==sheet->range.rowi ? mask1+2 : mask1; + mask1 = j==sheet->range.col0 ? mask1+4 : mask1; + mask1 = j==sheet->range.coli ? mask1+8 : mask1; + + mask2 = i==new_range.row0 ? 1 : 0; + mask2 = i==new_range.rowi ? mask2+2 : mask2; + mask2 = j==new_range.col0 ? mask2+4 : mask2; + mask2 = j==new_range.coli ? mask2+8 : mask2; + + if(mask1 != mask2){ + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==sheet->range.row0){ + y=y-3; + height=height+3; + } + if(i==sheet->range.rowi) height=height+3; + if(j==sheet->range.col0){ + x=x-3; + width=width+3; + } + if(j==sheet->range.coli) width=width+3; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x+1, + y+1, + x+1, + y+1, + width, + height); + + if(i != sheet->active_cell.row || j != sheet->active_cell.col){ + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==new_range.row0){ + y=y+2; + height=height-2; + } + if(i==new_range.rowi) height=height-3; + if(j==new_range.col0){ + x=x+2; + width=width-2; + } + if(j==new_range.coli) width=width-3; + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y+1, + width,height); + } + } + } + } + } + + for(i=range->row0; i<=range->rowi; i++){ + for(j=range->col0; j<=range->coli; j++){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + selected=(i<=new_range.rowi && i>=new_range.row0 && + j<=new_range.coli && j>=new_range.col0) ? TRUE : FALSE; + + if(state==GTK_STATE_SELECTED && !selected && + sheet->column[j].is_visible && sheet->row[i].is_visible){ + + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==sheet->range.row0){ + y=y-3; + height=height+3; + } + if(i==sheet->range.rowi) height=height+3; + if(j==sheet->range.col0){ + x=x-3; + width=width+3; + } + if(j==sheet->range.coli) width=width+3; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x+1, + y+1, + x+1, + y+1, + width, + height); + } + } + } + + for(i=range->row0; i<=range->rowi; i++){ + for(j=range->col0; j<=range->coli; j++){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + selected=(i<=new_range.rowi && i>=new_range.row0 && + j<=new_range.coli && j>=new_range.col0) ? TRUE : FALSE; + + if(state!=GTK_STATE_SELECTED && selected && + sheet->column[j].is_visible && sheet->row[i].is_visible && + (i != sheet->active_cell.row || j != sheet->active_cell.col)){ + + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==new_range.row0){ + y=y+2; + height=height-2; + } + if(i==new_range.rowi) height=height-3; + if(j==new_range.col0){ + x=x+2; + width=width-2; + } + if(j==new_range.coli) width=width-3; + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y+1, + width,height); + + } + + } + } + + for(i=aux_range.row0; i<=aux_range.rowi; i++){ + for(j=aux_range.col0; j<=aux_range.coli; j++){ + + if(sheet->column[j].is_visible && sheet->row[i].is_visible){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + + mask1 = i==sheet->range.row0 ? 1 : 0; + mask1 = i==sheet->range.rowi ? mask1+2 : mask1; + mask1 = j==sheet->range.col0 ? mask1+4 : mask1; + mask1 = j==sheet->range.coli ? mask1+8 : mask1; + + mask2 = i==new_range.row0 ? 1 : 0; + mask2 = i==new_range.rowi ? mask2+2 : mask2; + mask2 = j==new_range.col0 ? mask2+4 : mask2; + mask2 = j==new_range.coli ? mask2+8 : mask2; + if(mask2!=mask1 || (mask2==mask1 && state!=GTK_STATE_SELECTED)){ + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=sheet->column[j].width; + height=sheet->row[i].height; + if(mask2 & 1) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y-1, + width,3); + + + if(mask2 & 2) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y+height-1, + width,3); + + if(mask2 & 4) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-1,y+1, + 3,height); + + + if(mask2 & 8) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+width-1,y+1, + 3,height); + + + + } + + } + + } + } + + + *range=new_range; + gtk_sheet_draw_corners(sheet, new_range); + +} + +static void +gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range) +{ + GtkWidget *widget; + GdkRectangle area; + gint i; + gint x,y,width,height; + + widget = GTK_WIDGET(sheet); + + x=COLUMN_LEFT_XPIXEL(sheet,new_range.col0); + y=ROW_TOP_YPIXEL(sheet,new_range.row0); + width=COLUMN_LEFT_XPIXEL(sheet,new_range.coli)-x+ + sheet->column[new_range.coli].width; + height=ROW_TOP_YPIXEL(sheet,new_range.rowi)-y+ + sheet->row[new_range.rowi].height; + + area.x=COLUMN_LEFT_XPIXEL(sheet, MIN_VISIBLE_COLUMN(sheet)); + area.y=ROW_TOP_YPIXEL(sheet, MIN_VISIBLE_ROW(sheet)); + area.width=sheet->sheet_window_width; + area.height=sheet->sheet_window_height; + + if(x<0) { + width=width+x; + x=0; + } + if(width>area.width) width=area.width+10; + if(y<0) { + height=height+y; + y=0; + } + if(height>area.height) height=area.height+10; + + gdk_gc_set_clip_rectangle(sheet->xor_gc, &area); + + for(i=-1; i<=1; i++) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + FALSE, + x+i,y+i, + width-2*i,height-2*i); + + gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL); + + gtk_sheet_draw_corners(sheet, new_range); + +} + +static void +gtk_sheet_draw_corners(GtkSheet *sheet, GtkSheetRange range) +{ + gint x,y; + guint width = 1; + + if(gtk_sheet_cell_isvisible(sheet, range.row0, range.col0)){ + x=COLUMN_LEFT_XPIXEL(sheet,range.col0); + y=ROW_TOP_YPIXEL(sheet,range.row0); + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-1, + y-1, + x-1, + y-1, + 3, + 3); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-1,y-1, + 3,3); + } + + if(gtk_sheet_cell_isvisible(sheet, range.row0, range.coli) || + sheet->state == GTK_SHEET_COLUMN_SELECTED){ + x=COLUMN_LEFT_XPIXEL(sheet,range.coli)+ + sheet->column[range.coli].width; + y=ROW_TOP_YPIXEL(sheet,range.row0); + width = 1; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + { + y = ROW_TOP_YPIXEL(sheet, sheet->view.row0)+3; + width = 3; + } + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-width, + y-width, + x-width, + y-width, + 2*width+1, + 2*width+1); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-width+width/2,y-width+width/2, + 2+width,2+width); + } + + if(gtk_sheet_cell_isvisible(sheet, range.rowi, range.col0) || + sheet->state == GTK_SHEET_ROW_SELECTED){ + x=COLUMN_LEFT_XPIXEL(sheet,range.col0); + y=ROW_TOP_YPIXEL(sheet,range.rowi)+ + sheet->row[range.rowi].height; + width = 1; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + { + x = COLUMN_LEFT_XPIXEL(sheet, sheet->view.col0)+3; + width = 3; + } + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-width, + y-width, + x-width, + y-width, + 2*width+1, + 2*width+1); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-width+width/2,y-width+width/2, + 2+width,2+width); + } + + if(gtk_sheet_cell_isvisible(sheet, range.rowi, range.coli)){ + x=COLUMN_LEFT_XPIXEL(sheet,range.coli)+ + sheet->column[range.coli].width; + y=ROW_TOP_YPIXEL(sheet,range.rowi)+ + sheet->row[range.rowi].height; + width = 1; + if(sheet->state == GTK_SHEET_RANGE_SELECTED) width = 3; + if(sheet->state == GTK_SHEET_NORMAL) width = 3; + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-width, + y-width, + x-width, + y-width, + 2*width+1, + 2*width+1); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-width+width/2,y-width+width/2, + 2+width,2+width); + + } + +} + + +static void +gtk_sheet_real_select_range (GtkSheet * sheet, + GtkSheetRange * range) +{ + gint i; + gint state; + + g_return_if_fail (sheet != NULL); + + if(range==NULL) range=&sheet->range; + + if(range->row0 < 0 || range->rowi < 0) return; + if(range->col0 < 0 || range->coli < 0) return; + + state=sheet->state; + + if(state==GTK_SHEET_COLUMN_SELECTED || state==GTK_SHEET_RANGE_SELECTED){ + for(i=sheet->range.col0; i< range->col0; i++) + column_button_release(sheet, i); + for(i=range->coli+1; i<= sheet->range.coli; i++) + column_button_release(sheet, i); + for(i=range->col0; i<=range->coli; i++){ + column_button_set(sheet, i); + } + } + + if(state==GTK_SHEET_ROW_SELECTED || state==GTK_SHEET_RANGE_SELECTED){ + for(i=sheet->range.row0; i< range->row0; i++) + row_button_release(sheet, i); + for(i=range->rowi+1; i<= sheet->range.rowi; i++) + row_button_release(sheet, i); + for(i=range->row0; i<=range->rowi; i++){ + row_button_set(sheet, i); + } + } + + if(range->coli != sheet->range.coli || range->col0 != sheet->range.col0 || + range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0) + { + + gtk_sheet_new_selection(sheet, range); + + sheet->range.col0=range->col0; + sheet->range.coli=range->coli; + sheet->range.row0=range->row0; + sheet->range.rowi=range->rowi; + + } + else + { + gtk_sheet_draw_backing_pixmap(sheet, sheet->range); + gtk_sheet_range_draw_selection(sheet, sheet->range); + } + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[SELECT_RANGE], range); +} + +void +gtk_sheet_select_range(GtkSheet * sheet, const GtkSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + + if(range==NULL) range=&sheet->range; + + if(range->row0 < 0 || range->rowi < 0) return; + if(range->col0 < 0 || range->coli < 0) return; + + if(sheet->state != GTK_SHEET_NORMAL) + gtk_sheet_real_unselect_range(sheet, NULL); + else + { + gboolean veto; + veto = gtk_sheet_deactivate_cell(sheet); + if(!veto) return; + } + + sheet->range.row0=range->row0; + sheet->range.rowi=range->rowi; + sheet->range.col0=range->col0; + sheet->range.coli=range->coli; + sheet->active_cell.row=range->row0; + sheet->active_cell.col=range->col0; + sheet->selection_cell.row=range->rowi; + sheet->selection_cell.col=range->coli; + + sheet->state = GTK_SHEET_RANGE_SELECTED; + gtk_sheet_real_select_range(sheet, NULL); + +} + +void +gtk_sheet_unselect_range (GtkSheet * sheet) +{ + gtk_sheet_real_unselect_range(sheet, NULL); + sheet->state = GTK_STATE_NORMAL; + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col); +} + + +static void +gtk_sheet_real_unselect_range (GtkSheet * sheet, + const GtkSheetRange *range) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))); + + if(range==NULL){ + range=&sheet->range; + } + + if(range->row0 < 0 || range->rowi < 0) return; + if(range->col0 < 0 || range->coli < 0) return; + + if (gtk_sheet_range_isvisible (sheet, *range)){ + gtk_sheet_draw_backing_pixmap(sheet, *range); + } + + for(i=range->col0; i<=range->coli; i++){ + column_button_release(sheet, i); + } + + for(i=range->row0; i<=range->rowi; i++){ + row_button_release(sheet, i); + } + +} + + +static void +gtk_sheet_draw (GtkWidget * widget, + GdkRectangle * area) +{ + GtkSheet *sheet; + GtkSheetRange range; + GtkSheetChild *child; + GdkRectangle child_area; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + g_return_if_fail (area != NULL); + + if (GTK_WIDGET_DRAWABLE (widget)) + { + sheet = GTK_SHEET (widget); + + range.row0=ROW_FROM_YPIXEL(sheet, area->y); + range.rowi=ROW_FROM_YPIXEL(sheet, area->y+area->height); + range.col0=COLUMN_FROM_XPIXEL(sheet, area->x); + range.coli=COLUMN_FROM_XPIXEL(sheet, area->x+area->width); + + gtk_sheet_range_draw (sheet, &range); + + if(sheet->state != GTK_SHEET_NORMAL && gtk_sheet_range_isvisible(sheet, sheet->range)){ + gtk_sheet_draw_backing_pixmap(sheet, sheet->range); + gtk_sheet_range_draw_selection(sheet, sheet->range); + } + + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->row_title_window); + + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->column_title_window); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + if (gtk_widget_intersect (child->widget, area, &child_area)) + gtk_widget_draw (child->widget, &child_area); + } + + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet) + && GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + gtk_widget_draw(sheet->button, NULL); + + } + +} + + +static gint +gtk_sheet_expose (GtkWidget * widget, + GdkEventExpose * event) +{ + GtkSheet *sheet; + GtkSheetRange range; + GtkSheetChild *child; + GList *children; + GdkEventExpose child_event; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + sheet = GTK_SHEET (widget); + + if (GTK_WIDGET_DRAWABLE (widget)) + { + range.row0=ROW_FROM_YPIXEL(sheet,event->area.y); + range.col0=COLUMN_FROM_XPIXEL(sheet,event->area.x); + range.rowi=ROW_FROM_YPIXEL(sheet,event->area.y+event->area.height); + range.coli=COLUMN_FROM_XPIXEL(sheet,event->area.x+event->area.width); + + /* exposure events on the sheet */ + + if(event->window == sheet->row_title_window){ + size_allocate_row_title_buttons(sheet); + gdk_window_show(sheet->row_title_window); + } + + if(event->window == sheet->column_title_window){ + size_allocate_column_title_buttons(sheet); + gdk_window_show(sheet->column_title_window); + } + + if (event->window == sheet->sheet_window){ + gtk_sheet_draw_backing_pixmap(sheet, range); + + if(sheet->state != GTK_SHEET_NORMAL){ + if(gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_draw_backing_pixmap(sheet, sheet->range); + if(GTK_SHEET_IN_RESIZE(sheet) || GTK_SHEET_IN_DRAG(sheet)) + gtk_sheet_draw_backing_pixmap(sheet, sheet->drag_range); + + if(gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_range_draw_selection(sheet, sheet->range); + if(GTK_SHEET_IN_RESIZE(sheet) || GTK_SHEET_IN_DRAG(sheet)) + draw_xor_rectangle(sheet, sheet->drag_range); + } + + if((!GTK_SHEET_IN_XDRAG(sheet)) && (!GTK_SHEET_IN_YDRAG(sheet))){ + if(sheet->state == GTK_SHEET_NORMAL){ + gtk_sheet_draw_active_cell(sheet); + if(!GTK_SHEET_IN_SELECTION(sheet)) + gtk_widget_queue_draw(sheet->sheet_entry); + } + } + + /* sheet children events */ + child_event = *event; + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_NO_WINDOW (child->widget)) + { + GdkRectangle child_area; + + child_area.x = child->x; + child_area.y = child->y; + child_area.width = child->widget->allocation.width; + child_area.height = child->widget->allocation.height; + gdk_rectangle_intersect (&child_area, &event->area, &child_event.area); + child_event.window = event->window; + if(child->window) child_event.window = child->window; + gtk_widget_event (child->widget, (GdkEvent*) &child_event); + } + } + + } + + } + + + if(sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION(sheet)) + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + return FALSE; +} + +/* +static void +gtk_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkSheet *sheet; + + sheet = GTK_SHEET(container); + + (*callback) (sheet->button, callback_data); +} +*/ + +static gint +gtk_sheet_button_press (GtkWidget * widget, + GdkEventButton * event) +{ + GtkSheet *sheet; + GdkModifierType mods; + gint x, y, row, column; + gboolean veto; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if(event->type != GDK_BUTTON_PRESS) return TRUE; + gdk_window_get_pointer(widget->window, NULL, NULL, &mods); + if(!(mods & GDK_BUTTON1_MASK)) return TRUE; + + sheet = GTK_SHEET (widget); + + /* press on resize windows */ + if (event->window == sheet->column_title_window && + !GTK_SHEET_COLUMN_FROZEN(sheet)) + { + gtk_widget_get_pointer (widget, &sheet->x_drag, NULL); + if(POSSIBLE_XDRAG(sheet, sheet->x_drag, &sheet->drag_cell.col)){ + + GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG); + gdk_pointer_grab (sheet->column_title_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + + draw_xor_vline (sheet); + return TRUE; + } + } + + if (event->window == sheet->row_title_window && !GTK_SHEET_ROW_FROZEN(sheet)) + { + gtk_widget_get_pointer (widget, NULL, &sheet->y_drag); + + if(POSSIBLE_YDRAG(sheet, sheet->y_drag, &sheet->drag_cell.row)){ + GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG); + gdk_pointer_grab (sheet->row_title_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + + draw_xor_hline (sheet); + return TRUE; + } + } + + /* selections on the sheet */ + if(event->window == sheet->sheet_window){ + gtk_widget_get_pointer (widget, &x, &y); + gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + gdk_pointer_grab (sheet->sheet_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + gtk_grab_add(GTK_WIDGET(sheet)); + sheet->timer=gtk_timeout_add(TIMEOUT_SCROLL, gtk_sheet_scroll, (gpointer) sheet); + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_SHEET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + if(sheet->selection_mode != GTK_SELECTION_SINGLE && + sheet->cursor_drag->type==GDK_SIZING && + !GTK_SHEET_IN_SELECTION(sheet) && !GTK_SHEET_IN_RESIZE(sheet)){ + if(sheet->state==GTK_STATE_NORMAL) { + row=sheet->active_cell.row; + column=sheet->active_cell.col; + if(!gtk_sheet_deactivate_cell(sheet)) return FALSE; + sheet->active_cell.row=row; + sheet->active_cell.col=column; + sheet->drag_range=sheet->range; + sheet->state=GTK_SHEET_RANGE_SELECTED; + gtk_sheet_select_range(sheet, &sheet->drag_range); + } + sheet->x_drag=x; + sheet->y_drag=y; + if(row > sheet->range.rowi) row--; + if(column > sheet->range.coli) column--; + sheet->drag_cell.row = row; + sheet->drag_cell.col = column; + sheet->drag_range=sheet->range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_RESIZE); + } + else if(sheet->cursor_drag->type==GDK_TOP_LEFT_ARROW && + !GTK_SHEET_IN_SELECTION(sheet) && !GTK_SHEET_IN_DRAG(sheet)) { + if(sheet->state==GTK_STATE_NORMAL) { + row=sheet->active_cell.row; + column=sheet->active_cell.col; + if(!gtk_sheet_deactivate_cell(sheet)) return FALSE; + sheet->active_cell.row=row; + sheet->active_cell.col=column; + sheet->drag_range=sheet->range; + sheet->state=GTK_SHEET_RANGE_SELECTED; + gtk_sheet_select_range(sheet, &sheet->drag_range); + } + sheet->x_drag=x; + sheet->y_drag=y; + if(row < sheet->range.row0) row++; + if(row > sheet->range.rowi) row--; + if(column < sheet->range.col0) column++; + if(column > sheet->range.coli) column--; + sheet->drag_cell.row=row; + sheet->drag_cell.col=column; + sheet->drag_range=sheet->range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_DRAG); + } + else + { + gtk_sheet_click_cell(sheet, row, column, &veto); + if(veto) GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + } + + } + + if(event->window == sheet->column_title_window){ + gtk_widget_get_pointer (widget, &x, &y); + column = COLUMN_FROM_XPIXEL(sheet, x); + if(sheet->column[column].is_sensitive){; + gtk_sheet_click_cell(sheet, -1, column, &veto); + gtk_grab_add(GTK_WIDGET(sheet)); + sheet->timer=gtk_timeout_add(TIMEOUT_SCROLL, gtk_sheet_scroll, (gpointer) sheet); + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_SHEET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + } + } + + if(event->window == sheet->row_title_window){ + gtk_widget_get_pointer (widget, &x, &y); + row = ROW_FROM_YPIXEL(sheet, y); + if(sheet->row[row].is_sensitive){ + gtk_sheet_click_cell(sheet, row, -1, &veto); + gtk_grab_add(GTK_WIDGET(sheet)); + sheet->timer=gtk_timeout_add(TIMEOUT_SCROLL, gtk_sheet_scroll, (gpointer) sheet); + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_SHEET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + } + } + + return TRUE; +} + +static gint +gtk_sheet_scroll(gpointer data) +{ + GtkSheet *sheet; + gint x,y,row,column; + gint move; + + sheet=GTK_SHEET(data); + + GDK_THREADS_ENTER(); + + gtk_widget_get_pointer (GTK_WIDGET(sheet), &x, &y); + gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + + move=TRUE; + + if(GTK_SHEET_IN_SELECTION(sheet)) + gtk_sheet_extend_selection(sheet, row, column); + + if(GTK_SHEET_IN_DRAG(sheet) || GTK_SHEET_IN_RESIZE(sheet)){ + move=gtk_sheet_move_query(sheet, row, column); + if(move) draw_xor_rectangle(sheet, sheet->drag_range); + } + + GDK_THREADS_LEAVE(); + + return TRUE; + +} + +static void +gtk_sheet_click_cell(GtkSheet *sheet, gint row, gint column, gboolean *veto) +{ + *veto = TRUE; + + if(row > sheet->maxrow || column > sheet->maxcol){ + veto = FALSE; + return; + } + + if(column >= 0 && row >= 0) + if(!sheet->column[column].is_visible || !sheet->row[row].is_visible) + { + veto = FALSE; + return; + } + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[TRAVERSE], + sheet->active_cell.row, + sheet->active_cell.col, + &row, + &column, + veto); + if(!*veto){ + if(sheet->state == GTK_STATE_NORMAL) return; + + row = sheet->active_cell.row; + column = sheet->active_cell.col; + gtk_sheet_activate_cell(sheet, row, column); + return; + } + + if(row == -1 && column >= 0){ + if(GTK_SHEET_AUTO_SCROLL(sheet)) + gtk_sheet_move_query(sheet, row, column); + gtk_sheet_select_column(sheet, column); + return; + } + if(column == -1 && row >= 0){ + if(GTK_SHEET_AUTO_SCROLL(sheet)) + gtk_sheet_move_query(sheet, row, column); + gtk_sheet_select_row(sheet, row); + return; + } + + if(row!=-1 && column !=-1){ + if(sheet->state != GTK_SHEET_NORMAL){ + sheet->state = GTK_SHEET_NORMAL; + gtk_sheet_real_unselect_range(sheet, NULL); + } + else + { + if(!gtk_sheet_deactivate_cell(sheet)){ + *veto = FALSE; + return; + } + } + + if(GTK_SHEET_AUTO_SCROLL(sheet)) + gtk_sheet_move_query(sheet, row, column); + sheet->active_cell.row=row; + sheet->active_cell.col=column; + sheet->selection_cell.row=row; + sheet->selection_cell.col=column; + sheet->range.row0=row; + sheet->range.col0=column; + sheet->range.rowi=row; + sheet->range.coli=column; + sheet->state=GTK_SHEET_NORMAL; + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + gtk_sheet_draw_active_cell(sheet); + return; + } + + if(row==-1 && column ==-1){ + sheet->state=GTK_SHEET_RANGE_SELECTED; + sheet->range.row0=0; + sheet->range.col0=0; + sheet->range.rowi=sheet->maxrow; + sheet->range.coli=sheet->maxcol; + sheet->active_cell.row=0; + sheet->active_cell.col=0; + gtk_sheet_select_range(sheet, NULL); + return; + } + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); +} + +static gint +gtk_sheet_button_release (GtkWidget * widget, + GdkEventButton * event) +{ + GtkSheet *sheet; + gint x,y; + + sheet=GTK_SHEET(widget); + + /* release on resize windows */ + if (GTK_SHEET_IN_XDRAG (sheet)){ + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG); + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); + gtk_widget_get_pointer (widget, &x, NULL); + gdk_pointer_ungrab (event->time); + draw_xor_vline (sheet); + + gtk_sheet_set_column_width (sheet, sheet->drag_cell.col, new_column_width (sheet, sheet->drag_cell.col, &x)); + sheet->old_hadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), "value_changed"); + return TRUE; + } + + if (GTK_SHEET_IN_YDRAG (sheet)){ + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG); + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); + gtk_widget_get_pointer (widget, NULL, &y); + gdk_pointer_ungrab (event->time); + draw_xor_hline (sheet); + + gtk_sheet_set_row_height (sheet, sheet->drag_cell.row, new_row_height (sheet, sheet->drag_cell.row, &y)); + sheet->old_vadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), "value_changed"); + return TRUE; + } + + + if (GTK_SHEET_IN_DRAG(sheet)){ + GtkSheetRange old_range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_DRAG); + gdk_pointer_ungrab (event->time); + + gtk_sheet_real_unselect_range(sheet, NULL); + + sheet->active_cell.row = sheet->active_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->active_cell.col = sheet->active_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + sheet->selection_cell.row = sheet->selection_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->selection_cell.col = sheet->selection_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + old_range=sheet->range; + sheet->range=sheet->drag_range; + sheet->drag_range=old_range; + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[MOVE_RANGE], + &sheet->drag_range, &sheet->range); + gtk_sheet_select_range(sheet, &sheet->range); + } + + if (GTK_SHEET_IN_RESIZE(sheet)){ + GtkSheetRange old_range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_RESIZE); + gdk_pointer_ungrab (event->time); + + gtk_sheet_real_unselect_range(sheet, NULL); + + sheet->active_cell.row = sheet->active_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->active_cell.col = sheet->active_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + if(sheet->drag_range.row0 < sheet->range.row0) + sheet->selection_cell.row = sheet->drag_range.row0; + if(sheet->drag_range.rowi >= sheet->range.rowi) + sheet->selection_cell.row = sheet->drag_range.rowi; + if(sheet->drag_range.col0 < sheet->range.col0) + sheet->selection_cell.col = sheet->drag_range.col0; + if(sheet->drag_range.coli >= sheet->range.coli) + sheet->selection_cell.col = sheet->drag_range.coli; + old_range = sheet->range; + sheet->range = sheet->drag_range; + sheet->drag_range = old_range; + + if(sheet->state==GTK_STATE_NORMAL) sheet->state=GTK_SHEET_RANGE_SELECTED; + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[RESIZE_RANGE], + &sheet->drag_range, &sheet->range); + gtk_sheet_select_range(sheet, &sheet->range); + } + + if(sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION(sheet)){ + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + gdk_pointer_ungrab (event->time); + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + } + + if(GTK_SHEET_IN_SELECTION) + gdk_pointer_ungrab (event->time); + if(sheet->timer) + gtk_timeout_remove(sheet->timer); + gtk_grab_remove(GTK_WIDGET(sheet)); + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + + return TRUE; +} + +static gint +gtk_sheet_motion (GtkWidget * widget, + GdkEventMotion * event) +{ + GtkSheet *sheet; + GdkModifierType mods; + GdkCursorType new_cursor; + gint x, y, row, column; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + + sheet = GTK_SHEET (widget); + + + /* selections on the sheet */ + x = event->x; + y = event->y; + + if(event->window == sheet->column_title_window && !GTK_SHEET_COLUMN_FROZEN(sheet)){ + gtk_widget_get_pointer(widget, &x, &y); + if(!GTK_SHEET_IN_SELECTION(sheet) && POSSIBLE_XDRAG(sheet, x, &column)){ + new_cursor=GDK_SB_H_DOUBLE_ARROW; + if(new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW); + gdk_window_set_cursor(sheet->column_title_window,sheet->cursor_drag); + } + }else{ + new_cursor=GDK_TOP_LEFT_ARROW; + if(!GTK_SHEET_IN_XDRAG(sheet) && new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor(sheet->column_title_window,sheet->cursor_drag); + } + } + } + + if(event->window == sheet->row_title_window && !GTK_SHEET_ROW_FROZEN(sheet)){ + gtk_widget_get_pointer(widget, &x, &y); + if(!GTK_SHEET_IN_SELECTION(sheet) && POSSIBLE_YDRAG(sheet,y, &column)){ + new_cursor=GDK_SB_V_DOUBLE_ARROW; + if(new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW); + gdk_window_set_cursor(sheet->row_title_window,sheet->cursor_drag); + } + }else{ + new_cursor=GDK_TOP_LEFT_ARROW; + if(!GTK_SHEET_IN_YDRAG(sheet) && new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor(sheet->row_title_window,sheet->cursor_drag); + } + } + } + + new_cursor=GDK_PLUS; + if(!POSSIBLE_DRAG(sheet,x,y,&row,&column) && !GTK_SHEET_IN_DRAG(sheet) && + !POSSIBLE_RESIZE(sheet,x,y,&row,&column) && !GTK_SHEET_IN_RESIZE(sheet) && + event->window == sheet->sheet_window && + new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_PLUS); + gdk_window_set_cursor(sheet->sheet_window,sheet->cursor_drag); + } + + new_cursor=GDK_TOP_LEFT_ARROW; + if(!(POSSIBLE_RESIZE(sheet,x,y,&row,&column) || GTK_SHEET_IN_RESIZE(sheet)) && + (POSSIBLE_DRAG(sheet, x,y,&row,&column) || GTK_SHEET_IN_DRAG(sheet)) && + event->window == sheet->sheet_window && + new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor(sheet->sheet_window,sheet->cursor_drag); + } + + new_cursor=GDK_SIZING; + if(!GTK_SHEET_IN_DRAG(sheet) && + (POSSIBLE_RESIZE(sheet,x,y,&row,&column) || GTK_SHEET_IN_RESIZE(sheet)) && + event->window == sheet->sheet_window && + new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_SIZING); + gdk_window_set_cursor(sheet->sheet_window,sheet->cursor_drag); + } + + gdk_window_get_pointer (widget->window, &x, &y, &mods); + if(!(mods & GDK_BUTTON1_MASK)) return FALSE; + + if (GTK_SHEET_IN_XDRAG (sheet)){ + if (event->is_hint || event->window != widget->window) + gtk_widget_get_pointer (widget, &x, NULL); + else + x = event->x; + + new_column_width (sheet, sheet->drag_cell.col, &x); + if (x != sheet->x_drag) + { + draw_xor_vline (sheet); + sheet->x_drag = x; + draw_xor_vline (sheet); + } + return TRUE; + } + + if (GTK_SHEET_IN_YDRAG (sheet)){ + if (event->is_hint || event->window != widget->window) + gtk_widget_get_pointer (widget, NULL, &y); + else + y = event->y; + + new_row_height (sheet, sheet->drag_cell.row, &y); + if (y != sheet->y_drag) + { + draw_xor_hline (sheet); + sheet->y_drag = y; + draw_xor_hline (sheet); + } + return TRUE; + } + + if (GTK_SHEET_IN_DRAG(sheet)){ + GtkSheetRange aux; + column=COLUMN_FROM_XPIXEL(sheet,x)-sheet->drag_cell.col; + row=ROW_FROM_YPIXEL(sheet,y)-sheet->drag_cell.row; + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) row=0; + if(sheet->state==GTK_SHEET_ROW_SELECTED) column=0; + sheet->x_drag=x; + sheet->y_drag=y; + aux=sheet->range; + if(aux.row0+row >= 0 && aux.rowi+row <= sheet->maxrow && + aux.col0+column >= 0 && aux.coli+column <= sheet->maxcol){ + aux=sheet->drag_range; + sheet->drag_range.row0=sheet->range.row0+row; + sheet->drag_range.col0=sheet->range.col0+column; + sheet->drag_range.rowi=sheet->range.rowi+row; + sheet->drag_range.coli=sheet->range.coli+column; + if(aux.row0 != sheet->drag_range.row0 || + aux.col0 != sheet->drag_range.col0){ + draw_xor_rectangle (sheet, aux); + draw_xor_rectangle (sheet, sheet->drag_range); + } + } + return TRUE; + } + + if (GTK_SHEET_IN_RESIZE(sheet)){ + GtkSheetRange aux; + gint v_h; + v_h=1; + if(abs(x-COLUMN_LEFT_XPIXEL(sheet,sheet->drag_cell.col)) > + abs(y-ROW_TOP_YPIXEL(sheet,sheet->drag_cell.row))) v_h=2; + + column=COLUMN_FROM_XPIXEL(sheet,x)-sheet->drag_cell.col; + row=ROW_FROM_YPIXEL(sheet,y)-sheet->drag_cell.row; + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) row=0; + if(sheet->state==GTK_SHEET_ROW_SELECTED) column=0; + sheet->x_drag=x; + sheet->y_drag=y; + aux=sheet->range; + + if(row < sheet->range.row0 - sheet->range.rowi - 1) + row=row+(sheet->range.rowi-sheet->range.row0 + 1); + else if(row<0) row=0; + + if(column < sheet->range.col0 - sheet->range.coli - 1) + column=column+(sheet->range.coli-sheet->range.col0 + 1); + else if(column<0) column=0; + + if(v_h==1) + column=0; + else + row=0; + + if(aux.row0+row >= 0 && aux.rowi+row <= sheet->maxrow && + aux.col0+column >= 0 && aux.coli+column <= sheet->maxcol){ + + aux=sheet->drag_range; + sheet->drag_range=sheet->range; + + if(row<0) sheet->drag_range.row0=sheet->range.row0+row; + if(row>0) sheet->drag_range.rowi=sheet->range.rowi+row; + if(column<0) sheet->drag_range.col0=sheet->range.col0+column; + if(column>0) sheet->drag_range.coli=sheet->range.coli+column; + + if(aux.row0 != sheet->drag_range.row0 || + aux.rowi != sheet->drag_range.rowi || + aux.col0 != sheet->drag_range.col0 || + aux.coli != sheet->drag_range.coli){ + draw_xor_rectangle (sheet, aux); + draw_xor_rectangle (sheet, sheet->drag_range); + } + } + return TRUE; + } + + + + gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + + if(sheet->state==GTK_SHEET_NORMAL && row==sheet->active_cell.row && + column==sheet->active_cell.col) return TRUE; + + if(GTK_SHEET_IN_SELECTION(sheet) && mods&GDK_BUTTON1_MASK) + gtk_sheet_extend_selection(sheet, row, column); + + return TRUE; +} + +static gint +gtk_sheet_move_query(GtkSheet *sheet, gint row, gint column) +{ + gint row_move, column_move; + gfloat row_align, col_align; + guint height, width; + gint new_row = row; + gint new_col = column; + + row_move=FALSE; + column_move=FALSE; + row_align=-1.; + col_align=-1.; + + height = sheet->sheet_window_height; + width = sheet->sheet_window_width; + + if(row>=MAX_VISIBLE_ROW(sheet) && sheet->state!=GTK_SHEET_COLUMN_SELECTED) { + row_align = 1.; + new_row = MIN(sheet->maxrow, row + 1); + row_move = TRUE; + if(MAX_VISIBLE_ROW(sheet) == sheet->maxrow && + ROW_TOP_YPIXEL(sheet, sheet->maxrow) + + sheet->row[sheet->maxrow].height < height){ + row_move = FALSE; + row_align = -1.; + } + } + if(row<MIN_VISIBLE_ROW(sheet) && sheet->state!=GTK_SHEET_COLUMN_SELECTED) { + row_align= 0.; + row_move = TRUE; + } + if(column>=MAX_VISIBLE_COLUMN(sheet) && sheet->state!=GTK_SHEET_ROW_SELECTED) { + col_align = 1.; + new_col = MIN(sheet->maxcol, column + 1); + column_move = TRUE; + if(MAX_VISIBLE_COLUMN(sheet) == sheet->maxcol && + COLUMN_LEFT_XPIXEL(sheet, sheet->maxcol) + + sheet->column[sheet->maxcol].width < width){ + column_move = FALSE; + col_align = -1.; + } + } + if(column<MIN_VISIBLE_COLUMN(sheet) && sheet->state!=GTK_SHEET_ROW_SELECTED) { + col_align = 0.; + column_move = TRUE; + } + + if(row_move || column_move){ + gtk_sheet_moveto(sheet, new_row, new_col, row_align, col_align); + } + + return(row_move || column_move); +} + +static void +gtk_sheet_extend_selection(GtkSheet *sheet, gint row, gint column) +{ + GtkSheetRange range; + gint state; + gint r,c; + + if(row == sheet->selection_cell.row && column == sheet->selection_cell.col) + return; + + if(sheet->selection_mode == GTK_SELECTION_SINGLE) return; + + gtk_sheet_move_query(sheet, row, column); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + if(GTK_SHEET_IN_DRAG(sheet)) return; + + state=sheet->state; + + switch(sheet->state){ + case GTK_SHEET_ROW_SELECTED: + column = sheet->maxcol; + break; + case GTK_SHEET_COLUMN_SELECTED: + row = sheet->maxrow; + break; + case GTK_SHEET_NORMAL: + sheet->state=GTK_SHEET_RANGE_SELECTED; + r=sheet->active_cell.row; + c=sheet->active_cell.col; + sheet->range.col0=c; + sheet->range.row0=r; + sheet->range.coli=c; + sheet->range.rowi=r; + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + COLUMN_LEFT_XPIXEL(sheet,c)-1, + ROW_TOP_YPIXEL(sheet,r)-1, + COLUMN_LEFT_XPIXEL(sheet,c)-1, + ROW_TOP_YPIXEL(sheet,r)-1, + sheet->column[c].width+4, + sheet->row[r].height+4); + gtk_sheet_range_draw_selection(sheet, sheet->range); + case GTK_SHEET_RANGE_SELECTED: + sheet->state=GTK_SHEET_RANGE_SELECTED; + } + + sheet->selection_cell.row = row; + sheet->selection_cell.col = column; + + range.col0=MIN(column,sheet->active_cell.col); + range.coli=MAX(column,sheet->active_cell.col); + range.row0=MIN(row,sheet->active_cell.row); + range.rowi=MAX(row,sheet->active_cell.row); + + if(range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi || + range.col0 != sheet->range.col0 || range.coli != sheet->range.coli || + state==GTK_SHEET_NORMAL) + gtk_sheet_real_select_range(sheet, &range); + +} + +static gint +gtk_sheet_entry_key_press(GtkWidget *widget, + GdkEventKey *key) +{ + gboolean focus; + gtk_signal_emit_by_name(GTK_OBJECT(widget), "key_press_event", key, &focus); + return focus; +} + +static gint +gtk_sheet_key_press(GtkWidget *widget, + GdkEventKey *key) +{ + GtkSheet *sheet; + gint row, col; + gint state; + gboolean extend_selection = FALSE; + gboolean force_move = FALSE; + gboolean in_selection = FALSE; + gboolean veto = TRUE; + gint scroll = 1; + + sheet = GTK_SHEET(widget); + + if(key->state & GDK_CONTROL_MASK || key->keyval==GDK_Control_L || + key->keyval==GDK_Control_R) return FALSE; + +/* + { + if(key->keyval=='c' || key->keyval == 'C' && sheet->state != GTK_STATE_NORMAL) + gtk_sheet_clip_range(sheet, sheet->range); + if(key->keyval=='x' || key->keyval == 'X') + gtk_sheet_unclip_range(sheet); + return FALSE; + } +*/ + + extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval==GDK_Shift_L +|| key->keyval==GDK_Shift_R; + + state=sheet->state; + in_selection = GTK_SHEET_IN_SELECTION(sheet); + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + + switch(key->keyval){ + case GDK_Return: case GDK_KP_Enter: + if(sheet->state == GTK_SHEET_NORMAL && + !GTK_SHEET_IN_SELECTION(sheet)) + gtk_signal_emit_stop_by_name(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "key_press_event"); + row = sheet->active_cell.row; + col = sheet->active_cell.col; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet)-1; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet); + if(row < sheet->maxrow){ + row = row + scroll; + while(!sheet->row[row].is_visible && row<sheet->maxrow) row++; + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_ISO_Left_Tab: + row = sheet->active_cell.row; + col = sheet->active_cell.col; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + if(col > 0){ + col = col - scroll; + while(!sheet->column[col].is_visible && col>0) col--; + col=MAX(0, col); + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_Tab: + row = sheet->active_cell.row; + col = sheet->active_cell.col; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + if(col < sheet->maxcol){ + col = col + scroll; + while(!sheet->column[col].is_visible && col<sheet->maxcol) col++; + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; +/* case GDK_BackSpace: + if(sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0){ + if(sheet->active_cell.col > 0){ + col = sheet->active_cell.col - scroll; + row = sheet->active_cell.row; + while(!sheet->column[col].is_visible && col > 0) col--; + } + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; +*/ + case GDK_Page_Up: + scroll=MAX_VISIBLE_ROW(sheet)-MIN_VISIBLE_ROW(sheet)+1; + case GDK_Up: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.row > 0){ + row = sheet->selection_cell.row - scroll; + while(!sheet->row[row].is_visible && row > 0) row--; + row = MAX(0, row); + gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col); + } + return TRUE; + } + col = sheet->active_cell.col; + row = sheet->active_cell.row; + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet); + row = row - scroll; + while(!sheet->row[row].is_visible && row > 0) row--; + row = MAX(0,row); + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_Page_Down: + scroll=MAX_VISIBLE_ROW(sheet)-MIN_VISIBLE_ROW(sheet)+1; + case GDK_Down: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.row < sheet->maxrow){ + row = sheet->selection_cell.row + scroll; + while(!sheet->row[row].is_visible && row < sheet->maxrow) row++; + row = MIN(sheet->maxrow, row); + gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col); + } + return TRUE; + } + col = sheet->active_cell.col; + row = sheet->active_cell.row; + if(sheet->active_cell.row < sheet->maxrow){ + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet)-1; + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet); + row = row + scroll; + while(!sheet->row[row].is_visible && row < sheet->maxrow) row++; + row = MIN(sheet->maxrow, row); + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_Right: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.col < sheet->maxcol){ + col = sheet->selection_cell.col + 1; + while(!sheet->column[col].is_visible && col < sheet->maxcol) col++; + gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col); + } + return TRUE; + } + col = sheet->active_cell.col; + row = sheet->active_cell.row; + if(sheet->active_cell.col < sheet->maxcol){ + col ++; + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + while(!sheet->column[col].is_visible && col < sheet->maxcol) col++; + if(strlen(gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet)))) == 0 + || force_move) { + gtk_sheet_click_cell(sheet, row, col, &veto); + } + else + return FALSE; + } + extend_selection = FALSE; + break; + case GDK_Left: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.col > 0){ + col = sheet->selection_cell.col - 1; + while(!sheet->column[col].is_visible && col > 0) col--; + gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col); + } + return TRUE; + } + col = sheet->active_cell.col - 1; + row = sheet->active_cell.row; + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + while(!sheet->column[col].is_visible && col > 0) col--; + col = MAX(0, col); + + if(strlen(gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet)))) == 0 + || force_move){ + gtk_sheet_click_cell(sheet, row, col, &veto); + } + else + return FALSE; + extend_selection = FALSE; + break; + case GDK_Home: + row=0; + while(!sheet->row[row].is_visible && row < sheet->maxrow) row++; + gtk_sheet_click_cell(sheet, row, sheet->active_cell.col, &veto); + extend_selection = FALSE; + break; + case GDK_End: + row=sheet->maxrow; + while(!sheet->row[row].is_visible && row > 0) row--; + gtk_sheet_click_cell(sheet, row, sheet->active_cell.col, &veto); + extend_selection = FALSE; + break; + default: + if(in_selection) GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + if(extend_selection) return TRUE; + if(state == GTK_SHEET_ROW_SELECTED) + sheet->active_cell.col=MIN_VISIBLE_COLUMN(sheet); + if(state == GTK_SHEET_COLUMN_SELECTED) + sheet->active_cell.row=MIN_VISIBLE_ROW(sheet); + return FALSE; + } + + if(extend_selection) return TRUE; + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + + return TRUE; +} + +static void +gtk_sheet_size_request (GtkWidget * widget, + GtkRequisition * requisition) +{ + GtkSheet *sheet; + GList *children; + GtkSheetChild *child; + GtkRequisition child_requisition; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + g_return_if_fail (requisition != NULL); + + sheet = GTK_SHEET (widget); + + requisition->width = 3*DEFAULT_COLUMN_WIDTH; + requisition->height = 3*DEFAULT_ROW_HEIGHT(widget); + + /* compute the size of the column title area */ + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + requisition->height += sheet->column_title_area.height; + + /* compute the size of the row title area */ + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + requisition->width += sheet->row_title_area.width; + + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + sheet->view.row0=ROW_FROM_YPIXEL(sheet, 1); + + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, 1); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + gtk_widget_size_request(child->widget, &child_requisition); + } +} + + +static void +gtk_sheet_size_allocate (GtkWidget * widget, + GtkAllocation * allocation) +{ + GtkSheet *sheet; + GtkAllocation sheet_allocation; + gint border_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + g_return_if_fail (allocation != NULL); + + sheet = GTK_SHEET (widget); + widget->allocation = *allocation; + border_width = GTK_CONTAINER(widget)->border_width; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (widget->window, + allocation->x + border_width, + allocation->y + border_width, + allocation->width - 2*border_width, + allocation->height - 2*border_width); + + /* use internal allocation structure for all the math + * because it's easier than always subtracting the container + * border width */ + sheet->internal_allocation.x = 0; + sheet->internal_allocation.y = 0; + sheet->internal_allocation.width = allocation->width - 2*border_width; + sheet->internal_allocation.height = allocation->height - 2*border_width; + + sheet_allocation.x = 0; + sheet_allocation.y = 0; + sheet_allocation.width = allocation->width - 2*border_width; + sheet_allocation.height = allocation->height - 2*border_width; + + sheet->sheet_window_width = sheet_allocation.width; + sheet->sheet_window_height = sheet_allocation.height; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (sheet->sheet_window, + sheet_allocation.x, + sheet_allocation.y, + sheet_allocation.width, + sheet_allocation.height); + + /* position the window which holds the column title buttons */ + sheet->column_title_area.x = 0; + sheet->column_title_area.y = 0; + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + sheet->column_title_area.x = sheet->row_title_area.width; + sheet->column_title_area.width = sheet_allocation.width - + sheet->column_title_area.x; + if(GTK_WIDGET_REALIZED(widget) && GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + + sheet->sheet_window_width = sheet_allocation.width; + sheet->sheet_window_height = sheet_allocation.height; + + /* column button allocation */ + size_allocate_column_title_buttons (sheet); + + /* position the window which holds the row title buttons */ + sheet->row_title_area.x = 0; + sheet->row_title_area.y = 0; + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + sheet->row_title_area.y = sheet->column_title_area.height; + sheet->row_title_area.height = sheet_allocation.height - + sheet->row_title_area.y; + + if(GTK_WIDGET_REALIZED(widget) && GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + + + /* row button allocation */ + size_allocate_row_title_buttons (sheet); + + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + sheet->view.row0=ROW_FROM_YPIXEL(sheet, 1); + + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, 1); + + size_allocate_column_title_buttons(sheet); + size_allocate_row_title_buttons(sheet); + + /* re-scale backing pixmap */ + gtk_sheet_make_backing_pixmap(sheet, 0, 0); + gtk_sheet_position_children(sheet); + + /* set the scrollbars adjustments */ + adjust_scrollbars (sheet); +} + +static void +size_allocate_column_title_buttons (GtkSheet * sheet) +{ + gint i; + gint x,width; + + if (!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + width = sheet->sheet_window_width; + x = 0; + + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + { + width -= sheet->row_title_area.width; + x = sheet->row_title_area.width; + } + + if(sheet->column_title_area.width != width || sheet->column_title_area.x != x) + { + sheet->column_title_area.width = width; + sheet->column_title_area.x = x; + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + } + + + if(MAX_VISIBLE_COLUMN(sheet) == sheet->maxcol) + gdk_window_clear_area (sheet->column_title_window, + 0,0, + sheet->column_title_area.width, + sheet->column_title_area.height); + + if(!GTK_WIDGET_DRAWABLE(sheet)) return; + + for (i = MIN_VISIBLE_COLUMN(sheet); i <= MAX_VISIBLE_COLUMN(sheet); i++) + gtk_sheet_button_draw(sheet,-1,i); +} + +static void +size_allocate_row_title_buttons (GtkSheet * sheet) +{ + gint i; + gint y, height; + + if (!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + height = sheet->sheet_window_height; + y = 0; + + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + { + height -= sheet->column_title_area.height; + y = sheet->column_title_area.height; + } + + if(sheet->row_title_area.height != height || sheet->row_title_area.y != y){ + sheet->row_title_area.y = y; + sheet->row_title_area.height = height; + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + } + if(MAX_VISIBLE_ROW(sheet) == sheet->maxrow) + gdk_window_clear_area (sheet->row_title_window, + 0,0, + sheet->row_title_area.width, + sheet->row_title_area.height); + + if(!GTK_WIDGET_DRAWABLE(sheet)) return; + + for(i = MIN_VISIBLE_ROW(sheet); i <= MAX_VISIBLE_ROW(sheet); i++) + gtk_sheet_button_draw(sheet,i,-1); +} + +static void +gtk_sheet_recalc_top_ypixels(GtkSheet *sheet, gint row) +{ + gint i, cy; + + cy = sheet->column_title_area.height; + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) cy = 0; + for(i=0; i<=sheet->maxrow; i++){ + sheet->row[i].top_ypixel=cy; + if(sheet->row[i].is_visible) cy+=sheet->row[i].height; + } +} + +static void +gtk_sheet_recalc_left_xpixels(GtkSheet *sheet, gint column) +{ + gint i, cx; + + cx = sheet->row_title_area.width; + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) cx = 0; + for(i=0; i<=sheet->maxcol; i++){ + sheet->column[i].left_xpixel=cx; + if(sheet->column[i].is_visible) cx+=sheet->column[i].width; + } + +} + + + +static void +gtk_sheet_size_allocate_entry(GtkSheet *sheet) +{ + GtkAllocation shentry_allocation; + GtkSheetCellAttr attributes; + GtkEntry *sheet_entry; + GtkStyle *style = NULL, *previous_style = NULL; + gint row, col; + gint size, max_size, text_size, column_width; + const gchar *text; /* Needed for Gtk-2.X */ + + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_MAPPED(GTK_WIDGET(sheet))) return; + + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry(sheet)); + + gtk_sheet_get_attributes(sheet, sheet->active_cell.row, sheet->active_cell.col, &attributes); + + if(GTK_WIDGET_REALIZED(sheet->sheet_entry)){ + + if(!GTK_WIDGET(sheet_entry)->style) + gtk_widget_ensure_style(GTK_WIDGET(sheet_entry)); + + previous_style = GTK_WIDGET(sheet_entry)->style; + + style = gtk_style_copy(previous_style); + + style->bg[GTK_STATE_NORMAL] = attributes.background; + style->fg[GTK_STATE_NORMAL] = attributes.foreground; + style->text[GTK_STATE_NORMAL] = attributes.foreground; + style->bg[GTK_STATE_ACTIVE] = attributes.background; + style->fg[GTK_STATE_ACTIVE] = attributes.foreground; + style->text[GTK_STATE_ACTIVE] = attributes.foreground; + + if(style->font != attributes.font) { + gdk_font_unref(style->font); + style->font = attributes.font; + gdk_font_ref(style->font); + } + +/* I'm emulating gtk_widget_size_request to avoid the redraw */ + GTK_WIDGET(sheet_entry)->style = style; + gtk_widget_size_request(sheet->sheet_entry, NULL); + GTK_WIDGET(sheet_entry)->style = previous_style; + + if(style != previous_style) + gtk_widget_set_style(GTK_WIDGET(sheet_entry), style); + + if(GTK_IS_ITEM_ENTRY(sheet_entry)){ + gdk_gc_set_foreground(GTK_ITEM_ENTRY(sheet_entry)->fg_gc, + &attributes.foreground); + gdk_gc_set_foreground(GTK_ITEM_ENTRY(sheet_entry)->bg_gc, + &attributes.background); + } + + gdk_window_set_background(GTK_ENTRY(sheet_entry)->text_area, + &attributes.background); + + if(sheet->sheet_entry_window) + gdk_window_set_background(sheet->sheet_entry_window, + &attributes.background); + else + gdk_window_set_background(GTK_WIDGET(sheet_entry)->window, + &attributes.background); + } + + + if(GTK_IS_ITEM_ENTRY(sheet_entry)) + max_size = GTK_ITEM_ENTRY(sheet_entry)->text_max_size; + else + max_size = 0; + + text_size=GTK_ENTRY(sheet_entry)->text == NULL ? 0 : + gdk_string_width(attributes.font, + gtk_entry_get_text(GTK_ENTRY(sheet_entry))); + + column_width=sheet->column[sheet->active_cell.col].width; + + size=MIN(text_size, max_size); + size=MAX(size,column_width); + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + shentry_allocation.x=COLUMN_LEFT_XPIXEL(sheet,sheet->active_cell.col); + shentry_allocation.y=ROW_TOP_YPIXEL(sheet,sheet->active_cell.row); + shentry_allocation.width=column_width; + shentry_allocation.height=sheet->row[sheet->active_cell.row].height; + + /* SDB says: we may need to edit this for gtk-2 */ + if(GTK_IS_ITEM_ENTRY(sheet->sheet_entry) && !GTK_SHEET_CLIP_TEXT(sheet)){ + switch(GTK_ITEM_ENTRY(sheet_entry)->justification){ + case GTK_JUSTIFY_CENTER: + shentry_allocation.width=size; + shentry_allocation.x=shentry_allocation.x+ + column_width/2-size/2; + break; + case GTK_JUSTIFY_RIGHT: + shentry_allocation.x=shentry_allocation.x+shentry_allocation.width-size+1; + shentry_allocation.width=size; + break; + case GTK_JUSTIFY_LEFT: + case GTK_JUSTIFY_FILL: + shentry_allocation.width=size; + break; + } /* switch */ + } + + + if(!GTK_IS_ITEM_ENTRY(sheet->sheet_entry)){ + shentry_allocation.x += 2; + shentry_allocation.y += 2; + shentry_allocation.width -= MIN(shentry_allocation.width, 3); + shentry_allocation.height -= MIN(shentry_allocation.height, 3); + } + + /* SDB says: We may remove this (or rewrite it) for GTK22 */ + if(sheet->sheet_entry_window){ + gdk_window_move_resize(sheet->sheet_entry_window, + shentry_allocation.x, + shentry_allocation.y, + shentry_allocation.width, + shentry_allocation.height); + shentry_allocation.x = 0; + shentry_allocation.y = 0; + gtk_widget_size_allocate(sheet->sheet_entry, &shentry_allocation); + } else { + gtk_widget_size_allocate(sheet->sheet_entry, &shentry_allocation); + } + + + if(previous_style == style) gtk_style_unref(previous_style); +} + + + +static void +gtk_sheet_entry_set_max_size(GtkSheet *sheet) +{ + gint i; + gint size=0; + gint sizel=0, sizer=0; + gint row,col; + GtkJustification justification; + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + if(!GTK_IS_ITEM_ENTRY(sheet->sheet_entry) || GTK_SHEET_CLIP_TEXT(sheet)) return; + + justification = GTK_ITEM_ENTRY(sheet->sheet_entry)->justification; + + switch(justification){ + case GTK_JUSTIFY_FILL: + case GTK_JUSTIFY_LEFT: + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + size+=sheet->column[i].width; + } + size = MIN(size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL(sheet, col)); + break; + case GTK_JUSTIFY_RIGHT: + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + size+=sheet->column[i].width; + } + break; + case GTK_JUSTIFY_CENTER: + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ +/* if(gtk_sheet_cell_get_text(sheet, row, i)) break; +*/ + sizer+=sheet->column[i].width; + } + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + sizel+=sheet->column[i].width; + } + size=2*MIN(sizel, sizer); + break; + } + + if(size!=0) size+=sheet->column[col].width; + GTK_ITEM_ENTRY(sheet->sheet_entry)->text_max_size=size; + +} + + +static void +create_sheet_entry(GtkSheet *sheet) +{ + GtkWidget *widget; + GtkWidget *parent; + GtkWidget *entry; + GtkStyle *style; + gint found_entry = FALSE; + + widget = GTK_WIDGET(sheet); + + style = gtk_style_copy(GTK_WIDGET(sheet)->style); + + gtk_widget_push_style(style); + + if(sheet->sheet_entry){ + if(sheet->sheet_entry_window){ + gdk_window_set_user_data(sheet->sheet_entry_window, NULL); + gdk_window_destroy(sheet->sheet_entry_window); + sheet->sheet_entry_window = NULL; + } + /* avoids warnings */ + gtk_widget_ref(sheet->sheet_entry); + gtk_widget_unparent(sheet->sheet_entry); + gtk_widget_destroy(sheet->sheet_entry); + } + + if(sheet->entry_type){ + + if(!gtk_type_is_a (sheet->entry_type, GTK_TYPE_ENTRY)){ + + parent = GTK_WIDGET(gtk_type_new(sheet->entry_type)); + + sheet->sheet_entry = parent; + + entry = gtk_sheet_get_entry (sheet); + if(GTK_IS_ENTRY(entry)) found_entry = TRUE; + + } else { + + parent = GTK_WIDGET(gtk_type_new(sheet->entry_type)); + entry = parent; + found_entry = TRUE; + + } + + if(!found_entry){ + + g_warning ("Entry type must be GtkEntry subclass, using default"); + entry=GTK_WIDGET(gtk_type_new(GTK_TYPE_ITEM_ENTRY)); + sheet->sheet_entry = entry; + + } else { + + sheet->sheet_entry = parent; + + } + + + } else { + + entry=GTK_WIDGET(gtk_type_new(GTK_TYPE_ITEM_ENTRY)); + sheet->sheet_entry = entry; + + } + + gtk_widget_size_request(sheet->sheet_entry, NULL); + + /* --- SDB says: This doesn't appear in the Gtk-2.X version --- */ + if (GTK_WIDGET_REALIZED(sheet) && GTK_WIDGET_NO_WINDOW (sheet->sheet_entry)) + { + GdkWindowAttr attributes; + gint attributes_mask; + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = 0; + attributes.y = 0; + attributes.width = sheet->sheet_entry->requisition.width; + attributes.height = sheet->sheet_entry->requisition.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_EXPOSURE_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + sheet->sheet_entry_window = gdk_window_new (sheet->sheet_window, + &attributes, attributes_mask); + gdk_window_set_user_data (sheet->sheet_entry_window, widget); + + if (sheet->sheet_entry_window) + gtk_style_set_background (widget->style, + sheet->sheet_entry_window, + GTK_STATE_NORMAL); + } + + if(GTK_WIDGET_REALIZED(sheet)) + { + gtk_widget_set_parent(sheet->sheet_entry, GTK_WIDGET(sheet)); + gtk_widget_set_parent_window (sheet->sheet_entry, + sheet->sheet_entry_window ? + sheet->sheet_entry_window : + sheet->sheet_window); + gtk_widget_realize(sheet->sheet_entry); + } + + gtk_signal_connect_object(GTK_OBJECT(entry),"key_press_event", + (GtkSignalFunc) gtk_sheet_entry_key_press, + GTK_OBJECT(sheet)); + + gtk_widget_show (sheet->sheet_entry); +} + + +GtkWidget * +gtk_sheet_get_entry(GtkSheet *sheet) +{ + GtkWidget *parent; + GtkWidget *entry = NULL; + GtkTableChild *table_child; + GtkBoxChild *box_child; + GList *children = NULL; + + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + g_return_val_if_fail (sheet->sheet_entry != NULL, NULL); + + if(GTK_IS_ENTRY(sheet->sheet_entry)) return (sheet->sheet_entry); + + parent = GTK_WIDGET(sheet->sheet_entry); + + if(GTK_IS_TABLE(parent)) children = GTK_TABLE(parent)->children; + if(GTK_IS_BOX(parent)) children = GTK_BOX(parent)->children; + + if(!children) return NULL; + + while(children){ + if(GTK_IS_TABLE(parent)) { + table_child = children->data; + entry = table_child->widget; + } + if(GTK_IS_BOX(parent)){ + box_child = children->data; + entry = box_child->widget; + } + + if(GTK_IS_ENTRY(entry)) + break; + children = children->next; + } + + + if(!GTK_IS_ENTRY(entry)) return NULL; + + return (entry); + +} + +GtkWidget * +gtk_sheet_get_entry_widget(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + g_return_val_if_fail (sheet->sheet_entry != NULL, NULL); + + return (sheet->sheet_entry); +} + +/* BUTTONS */ +static void +row_button_set (GtkSheet *sheet, gint row) +{ + if(sheet->row[row].button.state == GTK_STATE_ACTIVE) return; + + sheet->row[row].button.state = GTK_STATE_ACTIVE; + gtk_sheet_button_draw(sheet, row, -1); + +} + +static void +column_button_set (GtkSheet *sheet, gint column) +{ + if(sheet->column[column].button.state == GTK_STATE_ACTIVE) return; + + sheet->column[column].button.state = GTK_STATE_ACTIVE; + gtk_sheet_button_draw(sheet, -1, column); + +} + +static void +row_button_release (GtkSheet *sheet, gint row) +{ + if(sheet->row[row].button.state == GTK_STATE_NORMAL) return; + + sheet->row[row].button.state = GTK_STATE_NORMAL; + gtk_sheet_button_draw(sheet, row, -1); +} + +static void +column_button_release (GtkSheet *sheet, gint column) +{ + if(sheet->column[column].button.state == GTK_STATE_NORMAL) return; + + sheet->column[column].button.state = GTK_STATE_NORMAL; + gtk_sheet_button_draw(sheet, -1, column); +} + +static void +gtk_sheet_button_draw (GtkSheet *sheet, gint row, gint column) +{ + GdkWindow *window = NULL; + GtkShadowType shadow_type; + guint width = 0, height = 0; + gint x = 0, y = 0; + gint index = 0; + gint text_width = 0, text_height = 0; + GtkSheetButton *button = NULL; + GtkSheetChild *child = NULL; + GdkRectangle allocation; + gboolean is_sensitive = FALSE; + gint state = 0; + gint len = 0; + gchar *line = 0; + gchar *words = 0; + gchar label[10]; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + if(row >= 0 && !sheet->row[row].is_visible) return; + if(column >= 0 && !sheet->column[column].is_visible) return; + if(row >= 0 && !GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) return; + if(column >= 0 && !GTK_SHEET_COL_TITLES_VISIBLE(sheet)) return; + if(column>=0 && column <MIN_VISIBLE_COLUMN(sheet)) return; + if(column>=0 && column >MAX_VISIBLE_COLUMN(sheet)) return; + if(row>=0 && row <MIN_VISIBLE_ROW(sheet)) return; + if(row>=0 && row >MAX_VISIBLE_ROW(sheet)) return; + if( (row == -1) && (column == -1) ) return; //This seems to cause a crash. + + if(row==-1){ + window=sheet->column_title_window; + button=&sheet->column[column].button; + index=column; + x = COLUMN_LEFT_XPIXEL(sheet, column)+CELL_SPACING; + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) x -= sheet->row_title_area.width; + y = 0; + width = sheet->column[column].width; + height = sheet->column_title_area.height; + is_sensitive=sheet->column[column].is_sensitive; + } + else if(column==-1){ + window=sheet->row_title_window; + button=&sheet->row[row].button; + index=row; + x = 0; + y = ROW_TOP_YPIXEL(sheet, row)+CELL_SPACING; + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) y-=sheet->column_title_area.height; + width = sheet->row_title_area.width; + height = sheet->row[row].height; + is_sensitive=sheet->row[row].is_sensitive; + } + + allocation.x = x; + allocation.y = y; + allocation.width = width; + allocation.height = height; + + gdk_window_clear_area (window, + x, y, + width, height); + + gtk_paint_box (sheet->button->style, window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + &allocation, GTK_WIDGET(sheet), + "buttondefault", x, y, width, height); + + state = button->state; + if(!is_sensitive) state=GTK_STATE_INSENSITIVE; + + if (state == GTK_STATE_ACTIVE) + shadow_type = GTK_SHADOW_IN; + else + shadow_type = GTK_SHADOW_OUT; + + if(state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE) + gtk_paint_box (sheet->button->style, window, + button->state, shadow_type, + &allocation, GTK_WIDGET(sheet), + "button", x, y, width, height); + + if(button->label_visible){ + + text_height=DEFAULT_LABEL_HEIGHT(GTK_WIDGET(sheet)); + + y += DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet))/2+ + DEFAULT_FONT_ASCENT(GTK_WIDGET(sheet))/2; + + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->fg_gc[button->state], + &allocation); + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->white_gc, &allocation); + + if(button->label && strlen(button->label)>0){ + words=button->label; + line = g_new(gchar, 1); + line[0]='\0'; + + while(words && *words != '\0'){ + if(*words != '\n'){ + len=strlen(line); + line=g_realloc(line, len+2); + line[len]=*words; + line[len+1]='\0'; + } + if(*words == '\n' || *(words+1) == '\0'){ + text_width = gdk_string_width (GTK_WIDGET (sheet)->style->font, line); + + switch(button->justification){ + case GTK_JUSTIFY_LEFT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + CELLOFFSET, y, + line); + break; + case GTK_JUSTIFY_RIGHT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + width - text_width - CELLOFFSET, y, + line); + break; + case GTK_JUSTIFY_CENTER: + default: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + (width - text_width) /2, y, + line); + } + + y += DEFAULT_FONT_ASCENT(GTK_WIDGET(sheet))+ + DEFAULT_FONT_DESCENT(GTK_WIDGET(sheet)) + 2; + + g_free(line); + line = g_new(gchar, 1); + line[0]='\0'; + } + words++; + } + g_free(line); + } else { + sprintf(label,"%d",index); + + text_width = gdk_string_width (GTK_WIDGET (sheet)->style->font, label); + + switch(button->justification){ + case GTK_JUSTIFY_LEFT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + CELLOFFSET, y, + label); + break; + case GTK_JUSTIFY_RIGHT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + width - text_width - CELLOFFSET, y, + label); + break; + case GTK_JUSTIFY_CENTER: + default: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + (width - text_width) /2, y, + label); + } + + } + + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->fg_gc[button->state], + NULL); + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->white_gc, NULL); + + } + + if((child = button->child) && (child->widget)){ + child->x = allocation.x; + child->y = allocation.y; + + child->x += (width - child->widget->requisition.width) * child->x_align; + child->y += (height - child->widget->requisition.height) * child->y_align; + child->widget->allocation.width = child->widget->requisition.width; + child->widget->allocation.height = child->widget->requisition.height; + + x = child->x; + y = child->y; + + gtk_widget_set_state(child->widget, button->state); + if(GTK_WIDGET_NO_WINDOW(child->widget)) + { + child->widget->allocation.x = 0; + child->widget->allocation.y = 0; + } + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + GTK_WIDGET_MAPPED(child->widget)) + { + gtk_widget_size_allocate(child->widget, + &child->widget->allocation); + if(GTK_WIDGET_NO_WINDOW(child->widget) && child->window) + { + gdk_window_move_resize(child->window, + x, y, + child->widget->allocation.width, + child->widget->allocation.height); + gtk_widget_draw(child->widget, NULL); + } + } + + } + +} + + +/* SCROLLBARS + * + * functions: + * adjust_scrollbars + * vadjustment_changed + * hadjustment_changed + * vadjustment_value_changed + * hadjustment_value_changed */ + +static void +adjust_scrollbars (GtkSheet * sheet) +{ + + if(sheet->vadjustment){ + sheet->vadjustment->page_size = sheet->sheet_window_height; + sheet->vadjustment->page_increment = sheet->sheet_window_height / 2; + sheet->vadjustment->step_increment = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + sheet->vadjustment->lower = 0; + sheet->vadjustment->upper = SHEET_HEIGHT (sheet) + 80; +/* + if (sheet->sheet_window_height - sheet->voffset > SHEET_HEIGHT (sheet)) + { + sheet->vadjustment->value = MAX(0, SHEET_HEIGHT (sheet) - + sheet->sheet_window_height); + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + } +*/ + gtk_signal_emit_by_name (GTK_OBJECT(sheet->vadjustment), "changed"); + + } + + if(sheet->hadjustment){ + sheet->hadjustment->page_size = sheet->sheet_window_width; + sheet->hadjustment->page_increment = sheet->sheet_window_width / 2; + sheet->hadjustment->step_increment = DEFAULT_COLUMN_WIDTH; + sheet->hadjustment->lower = 0; + sheet->hadjustment->upper = SHEET_WIDTH (sheet)+ 80; +/* + if (sheet->sheet_window_width - sheet->hoffset > SHEET_WIDTH (sheet)) + { + sheet->hadjustment->value = MAX(0, SHEET_WIDTH (sheet) - + sheet->sheet_window_width); + gtk_signal_emit_by_name (GTK_OBJECT(sheet->hadjustment), + "value_changed"); + } +*/ + gtk_signal_emit_by_name (GTK_OBJECT(sheet->hadjustment), "changed"); + + } +/* + if(GTK_WIDGET_REALIZED(sheet)) + { + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)){ + size_allocate_row_title_buttons(sheet); + gdk_window_show(sheet->row_title_window); + } + + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)){ + size_allocate_column_title_buttons(sheet); + gdk_window_show(sheet->column_title_window); + } + + gtk_sheet_range_draw(sheet, NULL); + } +*/ +} + + +static void +vadjustment_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + sheet = GTK_SHEET (data); + +} + +static void +hadjustment_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + sheet = GTK_SHEET (data); + +} + + +static void +vadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + gint diff, value, old_value; + gint i; + gint row, new_row; + gint y=0; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + g_return_if_fail (GTK_IS_SHEET (data)); + + sheet = GTK_SHEET (data); + + if(GTK_SHEET_IS_FROZEN(sheet)) return; + + row=ROW_FROM_YPIXEL(sheet,sheet->column_title_area.height + CELL_SPACING); + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + row=ROW_FROM_YPIXEL(sheet,CELL_SPACING); + + old_value = -sheet->voffset; + + for(i=0; i<= sheet->maxrow; i++){ + if(sheet->row[i].is_visible) y+=sheet->row[i].height; + if(y > adjustment->value) break; + } + y-=sheet->row[i].height; + new_row=i; + + if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. && + sheet->row[i].height > sheet->vadjustment->step_increment){ +/* This avoids embarrassing twitching */ + if(row == new_row && row != sheet->maxrow && + adjustment->value - sheet->old_vadjustment >= + sheet->vadjustment->step_increment && + new_row + 1 != MIN_VISIBLE_ROW(sheet)){ + new_row+=1; + y=y+sheet->row[row].height; + } + } + +/* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */ + if(sheet->old_vadjustment >= 0. && row == new_row){ + sheet->old_vadjustment = sheet->vadjustment->value; + return; + } + + sheet->old_vadjustment = sheet->vadjustment->value; + adjustment->value=y; + + + if(new_row == 0){ + sheet->vadjustment->step_increment= + sheet->row[0].height; + }else{ + sheet->vadjustment->step_increment= + MIN(sheet->row[new_row].height, sheet->row[new_row-1].height); + } + + sheet->vadjustment->value=adjustment->value; + + value = adjustment->value; + + if (value >= -sheet->voffset) + { + /* scroll down */ + diff = value + sheet->voffset; + } + else + { + /* scroll up */ + diff = -sheet->voffset - value; + } + + sheet->voffset = -value; + + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + sheet->view.row0=ROW_FROM_YPIXEL(sheet, 1); + + if(GTK_WIDGET_REALIZED(sheet->sheet_entry) && + sheet->state == GTK_SHEET_NORMAL && + sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 && + !gtk_sheet_cell_isvisible(sheet, sheet->active_cell.row, + sheet->active_cell.col)) + { + gchar *text; + + text = (gchar *) gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + + if(!text || strlen(text)==0) + gtk_sheet_cell_clear(sheet, + sheet->active_cell.row, + sheet->active_cell.col); + + if(sheet->sheet_entry_window) + gdk_window_hide(sheet->sheet_entry_window); + else + gdk_window_hide(sheet->sheet_entry->window); + } + + gtk_sheet_position_children(sheet); + + gtk_sheet_range_draw(sheet, NULL); + size_allocate_row_title_buttons(sheet); + size_allocate_global_button(sheet); +} + +static void +hadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + gint i, diff, value, old_value; + gint column, new_column; + gint x=0; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + g_return_if_fail (GTK_IS_SHEET (data)); + + sheet = GTK_SHEET (data); + + if(GTK_SHEET_IS_FROZEN(sheet)) return; + + column=COLUMN_FROM_XPIXEL(sheet,sheet->row_title_area.width + CELL_SPACING); + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + column=COLUMN_FROM_XPIXEL(sheet, CELL_SPACING); + + old_value = -sheet->hoffset; + + for(i=0; i<= sheet->maxcol; i++){ + if(sheet->column[i].is_visible) x+=sheet->column[i].width; + if(x > adjustment->value) break; + } + x-=sheet->column[i].width; + new_column=i; + + if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 && + sheet->column[i].width > sheet->hadjustment->step_increment){ +/* This avoids embarrassing twitching */ + if(column == new_column && column != sheet->maxcol && + adjustment->value - sheet->old_hadjustment >= + sheet->hadjustment->step_increment && + new_column + 1 != MIN_VISIBLE_COLUMN(sheet)){ + new_column+=1; + x=x+sheet->column[column].width; + } + } + +/* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */ + if(sheet->old_hadjustment >= 0. && new_column == column){ + sheet->old_hadjustment = sheet->hadjustment->value; + return; + } + + sheet->old_hadjustment = sheet->hadjustment->value; + adjustment->value=x; + + if(new_column == 0){ + sheet->hadjustment->step_increment= + sheet->column[0].width; + }else{ + sheet->hadjustment->step_increment= + MIN(sheet->column[new_column].width, sheet->column[new_column-1].width); + } + + + sheet->hadjustment->value=adjustment->value; + + value = adjustment->value; + + if (value >= -sheet->hoffset) + { + /* scroll right */ + diff = value + sheet->hoffset; + } + else + { + /* scroll left */ + diff = -sheet->hoffset - value; + } + + sheet->hoffset = -value; + + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, 1); + + if(GTK_WIDGET_REALIZED(sheet->sheet_entry) && + sheet->state == GTK_SHEET_NORMAL && + sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 && + !gtk_sheet_cell_isvisible(sheet, sheet->active_cell.row, + sheet->active_cell.col)) + { + gchar *text; + + text = (gchar *) gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + if(!text || strlen(text)==0) + gtk_sheet_cell_clear(sheet, + sheet->active_cell.row, + sheet->active_cell.col); + + if(sheet->sheet_entry_window) + gdk_window_hide(sheet->sheet_entry_window); + else + gdk_window_hide(sheet->sheet_entry->window); + } + + gtk_sheet_position_children(sheet); + + gtk_sheet_range_draw(sheet, NULL); + size_allocate_column_title_buttons(sheet); +} + + +/* COLUMN RESIZING */ +static void +draw_xor_vline (GtkSheet * sheet) +{ + GtkWidget *widget; + + g_return_if_fail (sheet != NULL); + + widget = GTK_WIDGET (sheet); + + gdk_draw_line (widget->window, sheet->xor_gc, + sheet->x_drag, + sheet->column_title_area.height, + sheet->x_drag, + sheet->sheet_window_height + 1); +} + +/* ROW RESIZING */ +static void +draw_xor_hline (GtkSheet * sheet) +{ + GtkWidget *widget; + + g_return_if_fail (sheet != NULL); + + widget = GTK_WIDGET (sheet); + + gdk_draw_line (widget->window, sheet->xor_gc, + sheet->row_title_area.width, + sheet->y_drag, + + sheet->sheet_window_width + 1, + sheet->y_drag); +} + +/* SELECTED RANGE */ +static void +draw_xor_rectangle(GtkSheet *sheet, GtkSheetRange range) +{ + gint i; + GdkRectangle clip_area, area; + GdkGCValues values; + + area.x=COLUMN_LEFT_XPIXEL(sheet, range.col0); + area.y=ROW_TOP_YPIXEL(sheet, range.row0); + area.width=COLUMN_LEFT_XPIXEL(sheet, range.coli)-area.x+ + sheet->column[range.coli].width; + area.height=ROW_TOP_YPIXEL(sheet, range.rowi)-area.y+ + sheet->row[range.rowi].height; + + clip_area.x=sheet->row_title_area.width; + clip_area.y=sheet->column_title_area.height; + clip_area.width=sheet->sheet_window_width; + clip_area.height=sheet->sheet_window_height; + + if(!GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) clip_area.x = 0; + if(!GTK_SHEET_COL_TITLES_VISIBLE(sheet)) clip_area.y = 0; + + if(area.x<0) { + area.width=area.width+area.x; + area.x=0; + } + if(area.width>clip_area.width) area.width=clip_area.width+10; + if(area.y<0) { + area.height=area.height+area.y; + area.y=0; + } + if(area.height>clip_area.height) area.height=clip_area.height+10; + + clip_area.x--; + clip_area.y--; + clip_area.width+=3; + clip_area.height+=3; + + gdk_gc_get_values(sheet->xor_gc, &values); + + gdk_gc_set_clip_rectangle(sheet->xor_gc, &clip_area); + + for(i=-1; i<=1; i++) + gdk_draw_rectangle(sheet->sheet_window, + sheet->xor_gc, + FALSE, + area.x+i, area.y+i, + area.width-2*i, area.height-2*i); + + + gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL); + + gdk_gc_set_foreground(sheet->xor_gc, &values.foreground); + +} + + +/* this function returns the new width of the column being resized given + * the column and x position of the cursor; the x cursor position is passed + * in as a pointer and automaticaly corrected if it's beyond min/max limits */ +static guint +new_column_width (GtkSheet * sheet, + gint column, + gint * x) +{ + gint cx, width; + GtkRequisition requisition; + + cx = *x; + + gtk_sheet_button_size_request(sheet, &sheet->column[column].button, + &requisition); + + /* you can't shrink a column to less than its minimum width */ + if (cx < COLUMN_LEFT_XPIXEL (sheet, column) + requisition.width) + { + *x = cx = COLUMN_LEFT_XPIXEL (sheet, column) + requisition.width; + } + + /* don't grow past the end of the window */ + /* + if (cx > sheet->sheet_window_width) + { + *x = cx = sheet->sheet_window_width; + } + */ + /* calculate new column width making sure it doesn't end up + * less than the minimum width */ + width = cx - COLUMN_LEFT_XPIXEL (sheet, column); + if (width < requisition.width) + width = requisition.width; + + sheet->column[column].width = width; + gtk_sheet_recalc_left_xpixels(sheet, column+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + size_allocate_column_title_buttons (sheet); + + return width; +} + +/* this function returns the new height of the row being resized given + * the row and y position of the cursor; the y cursor position is passed + * in as a pointer and automaticaly corrected if it's beyond min/max limits */ +static guint +new_row_height (GtkSheet * sheet, + gint row, + gint * y) +{ + GtkRequisition requisition; + gint cy, height; + + cy = *y; + + gtk_sheet_button_size_request(sheet, &sheet->row[row].button, + &requisition); + + /* you can't shrink a row to less than its minimum height */ + if (cy < ROW_TOP_YPIXEL (sheet, row) + requisition.height) + + { + *y = cy = ROW_TOP_YPIXEL (sheet, row) + requisition.height; + } + + /* don't grow past the end of the window */ + /* + if (cy > sheet->sheet_window_height) + { + *y = cy = sheet->sheet_window_height; + } + */ + /* calculate new row height making sure it doesn't end up + * less than the minimum height */ + height = (cy - ROW_TOP_YPIXEL (sheet, row)); + if (height < requisition.height) + height = requisition.height; + + sheet->row[row].height = height; + gtk_sheet_recalc_top_ypixels(sheet, row); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + size_allocate_row_title_buttons (sheet); + + return height; +} + +void +gtk_sheet_set_column_width (GtkSheet * sheet, + gint column, + guint width) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (column < 0 || column > sheet->maxcol) + return; + + sheet->column[column].width = width; + + gtk_sheet_recalc_left_xpixels(sheet, column+1); + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && !GTK_SHEET_IS_FROZEN(sheet)){ + size_allocate_column_title_buttons (sheet); + adjust_scrollbars (sheet); + gtk_sheet_size_allocate_entry(sheet); + gtk_sheet_range_draw (sheet, NULL); + } + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[CHANGED], -1, column); + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[NEW_COL_WIDTH], column, width); + +} + +void +gtk_sheet_set_row_height (GtkSheet * sheet, + gint row, + guint height) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (row < 0 || row > sheet->maxrow) + return; + + sheet->row[row].height = height; + + gtk_sheet_recalc_top_ypixels(sheet, row+1); + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && !GTK_SHEET_IS_FROZEN(sheet)){ + size_allocate_row_title_buttons (sheet); + adjust_scrollbars (sheet); + gtk_sheet_size_allocate_entry(sheet); + gtk_sheet_range_draw (sheet, NULL); + } + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[CHANGED], row, -1); + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[NEW_ROW_HEIGHT], row, height); + +} + + +void +gtk_sheet_add_column(GtkSheet *sheet, guint ncols) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + AddColumn(sheet, ncols); + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + adjust_scrollbars(sheet); + + if(sheet->state==GTK_SHEET_ROW_SELECTED) sheet->range.coli+=ncols; + + sheet->old_hadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); +} + +void +gtk_sheet_add_row(GtkSheet *sheet, guint nrows) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + AddRow(sheet, nrows); + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) sheet->range.rowi+=nrows; + + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); +} + +void +gtk_sheet_insert_rows(GtkSheet *sheet, guint row, guint nrows) +{ + GList *children; + GtkSheetChild *child; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + InsertRow(sheet, row, nrows); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell) + if(child->row >= row) child->row += nrows; + + children = children->next; + } + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) sheet->range.rowi+=nrows; + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + +} + +void +gtk_sheet_insert_columns(GtkSheet *sheet, guint col, guint ncols) +{ + GList *children; + GtkSheetChild *child; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + InsertColumn(sheet, col, ncols); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell) + if(child->col >= col) child->col += ncols; + + children = children->next; + } + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + if(sheet->state==GTK_SHEET_ROW_SELECTED) sheet->range.coli+=ncols; + adjust_scrollbars(sheet); + + sheet->old_hadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + +} + +void +gtk_sheet_delete_rows(GtkSheet *sheet, guint row, guint nrows) +{ + GList *children; + GtkSheetChild *child; + gint irow, icol; + gboolean veto; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + nrows = MIN(nrows, sheet->maxrow-row+1); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + DeleteRow(sheet, row, nrows); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell){ + if(child->row >= row && child->row < row+nrows){ + gtk_container_remove(GTK_CONTAINER(sheet), child->widget); + children = sheet->children; + } + else + { + if(child->row >= row) child->row -= nrows; + children = children->next; + } + } + else + children = children->next; + } + + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + irow = sheet->active_cell.row; + icol = sheet->active_cell.col; + + sheet->active_cell.row = -1; + sheet->active_cell.col = -1; + +/* if(sheet->state == GTK_SHEET_ROW_SELECTED) +*/ + + irow = MIN(irow, sheet->maxrow); + irow = MAX(irow, 0); + gtk_sheet_click_cell(sheet, irow, icol, &veto); + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + +} + +void +gtk_sheet_delete_columns(GtkSheet *sheet, guint col, guint ncols) +{ + GList *children; + GtkSheetChild *child; + gint irow, icol; + gboolean veto; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + ncols = MIN(ncols, sheet->maxcol-col+1); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + DeleteColumn(sheet, col, ncols); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell){ + if(child->col >= col && child->col < col+ncols){ + gtk_container_remove(GTK_CONTAINER(sheet), child->widget); + children = sheet->children; + } + else + { + if(child->col >= col) child->col -= ncols; + children = children->next; + } + } + else + children = children->next; + } + + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + irow = sheet->active_cell.row; + icol = sheet->active_cell.col; + + sheet->active_cell.row = -1; + sheet->active_cell.col = -1; + +/* if(sheet->state == GTK_SHEET_COLUMN_SELECTED) +*/ + + icol = MIN(icol, sheet->maxcol); + icol = MAX(icol, 0); + gtk_sheet_click_cell(sheet, irow, icol, &veto); + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + + adjust_scrollbars(sheet); + + sheet->old_hadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + +} + +void +gtk_sheet_range_set_background(GtkSheet *sheet, const GtkSheetRange *urange, const GdkColor *color) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + if(color != NULL) + attributes.background = *color; + else + attributes.background = sheet->bg_color; + + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + range.row0--; + range.col0--; + range.rowi++; + range.coli++; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_foreground(GtkSheet *sheet, const GtkSheetRange *urange, const GdkColor *color) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + + if(color != NULL) + attributes.foreground = *color; + else + gdk_color_black(gdk_colormap_get_system(), &attributes.foreground); + + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_justification(GtkSheet *sheet, const GtkSheetRange *urange, + GtkJustification just) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.justification = just; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_column_set_justification(GtkSheet *sheet, gint col, + GtkJustification justification) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(col > sheet->maxcol) return; + + sheet->column[col].justification = justification; + + if(GTK_WIDGET_REALIZED(sheet) && !GTK_SHEET_IS_FROZEN(sheet) && + col >= MIN_VISIBLE_COLUMN(sheet) && col <= MAX_VISIBLE_COLUMN(sheet)) + gtk_sheet_range_draw(sheet, NULL); + +} + +void +gtk_sheet_range_set_editable(GtkSheet *sheet, const GtkSheetRange *urange, gboolean editable) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.is_editable = editable; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_visible(GtkSheet *sheet, const GtkSheetRange *urange, gboolean visible) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.is_visible=visible; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_border(GtkSheet *sheet, const GtkSheetRange *urange, gint mask, +guint width, gint line_style) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.border.mask = mask; + attributes.border.width = width; + attributes.border.line_style=line_style; + attributes.border.cap_style=GDK_CAP_NOT_LAST; + attributes.border.join_style=GDK_JOIN_MITER; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + range.row0--; + range.col0--; + range.rowi++; + range.coli++; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_border_color(GtkSheet *sheet, const GtkSheetRange *urange, const GdkColor *color) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.border.color = *color; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +/* TODO: Should this really store the font pointer instead of the font value. +*/ + +#if 0 +/* SDB says: Remove this . . . . . */ +void +gtk_sheet_range_set_font(GtkSheet *sheet, const GtkSheetRange *urange, GdkFont *font) +{ + gint i, j; + gint font_height; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + gtk_sheet_freeze(sheet); + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.font = font; /* TODO: See comment above this function.*/ + font_height=attributes.font->ascent + + 2 * attributes.font->descent + 2*CELLOFFSET; + if(font_height > sheet->row[i].height){ + sheet->row[i].height = font_height; + gtk_sheet_recalc_top_ypixels(sheet, i); + } + + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + gtk_sheet_thaw(sheet); +} +#endif + +static void +gtk_sheet_set_cell_attributes(GtkSheet *sheet, gint row, gint col, GtkSheetCellAttr attributes) +{ + GtkSheetCell **cell; + + if(row > sheet->maxrow || col >sheet->maxcol) return; + + CheckBounds(sheet, row, col); + + cell = &sheet->data[row][col]; + + if(*cell==NULL){ + (*cell) = gtk_sheet_cell_new(); + (*cell)->row = row; + (*cell)->col = col; + } + + if((*cell)->attributes == NULL) + (*cell)->attributes = g_new(GtkSheetCellAttr, 1); + + *((*cell)->attributes) = attributes; +} + +gboolean +gtk_sheet_get_attributes(GtkSheet *sheet, gint row, gint col, GtkSheetCellAttr *attributes) +{ + GtkSheetCell **cell = NULL; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + if(row < 0 || col < 0) return FALSE; + + if(row > sheet->maxallocrow || col > sheet->maxalloccol){ + init_attributes(sheet, col, attributes); + return FALSE; + } + + if(row <= sheet->maxallocrow && col <= sheet->maxalloccol){ + if(sheet->data[row] && sheet->data[row][col]) + cell = &sheet->data[row][col]; + if(cell == NULL || *cell == NULL){ + init_attributes(sheet, col, attributes); + return FALSE; + } else + if((*cell)->attributes == NULL){ + init_attributes(sheet, col, attributes); + return FALSE; + }else{ + *attributes = *(sheet->data[row][col]->attributes); + if(sheet->column[col].justification != GTK_JUSTIFY_FILL) + attributes->justification = sheet->column[col].justification; + } + } + + return TRUE; +} + +static void +init_attributes(GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes) +{ + /* DEFAULT VALUES */ + attributes->foreground = GTK_WIDGET(sheet)->style->black; + attributes->background = sheet->bg_color; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + GdkColormap *colormap; + colormap=gdk_colormap_get_system(); + gdk_color_black(colormap, &attributes->foreground); + attributes->background = sheet->bg_color; + } + attributes->justification = sheet->column[col].justification; + attributes->border.width = 0; + attributes->border.line_style = GDK_LINE_SOLID; + attributes->border.cap_style = GDK_CAP_NOT_LAST; + attributes->border.join_style = GDK_JOIN_MITER; + attributes->border.mask = 0; + attributes->border.color = GTK_WIDGET(sheet)->style->black; + attributes->is_editable = TRUE; + attributes->is_visible = TRUE; + attributes->font = GTK_WIDGET(sheet)->style->font; + +} + +/********************************************************************** + * Memory allocation routines: + * AddRow & AddColumn allocate memory for GtkSheetColumn & GtkSheetRow structs. + * InsertRow + * InsertColumn + * DeleteRow + * DeleteColumn + * GrowSheet allocates memory for the sheet cells contents using an array of + * pointers. Alternative to this could be a linked list or a hash table. + * CheckBounds checks whether the given cell is currently allocated or not. + * If not, it calls to GrowSheet. + **********************************************************************/ + +static gint +AddColumn(GtkSheet *tbl, gint ncols) +{ + gint i; + + if(ncols == -1 && tbl->maxcol == 0) + { + ncols = 1; + } + else + { + tbl->maxcol += ncols; + tbl->column = (GtkSheetColumn *)g_realloc(tbl->column,(tbl->maxcol+1)* + sizeof(GtkSheetColumn)); + } + + for(i=tbl->maxcol-ncols+1; i<= tbl->maxcol; i++){ + tbl->column[i].width=DEFAULT_COLUMN_WIDTH; + tbl->column[i].button.label=NULL; + tbl->column[i].button.child=NULL; + tbl->column[i].button.state=GTK_STATE_NORMAL; + tbl->column[i].button.justification=GTK_JUSTIFY_CENTER; + tbl->column[i].button.label_visible = TRUE; + tbl->column[i].name=NULL; + tbl->column[i].is_visible=TRUE; + tbl->column[i].is_sensitive=TRUE; + tbl->column[i].left_text_column=i; + tbl->column[i].right_text_column=i; + tbl->column[i].justification=GTK_JUSTIFY_FILL; + if(i>0) + { + tbl->column[i].left_text_column=tbl->column[i-1].left_text_column; + tbl->column[i].left_xpixel=tbl->column[i-1].left_xpixel + + tbl->column[i-1].width; + } + else + { + tbl->column[i].left_xpixel=tbl->row_title_area.width; + if(!GTK_SHEET_ROW_TITLES_VISIBLE(tbl)) + tbl->column[i].left_xpixel=0; + } + } + return TRUE; +} + +static gint +AddRow(GtkSheet *tbl, gint nrows) +{ + gint i; + + if(nrows == -1 && tbl->maxrow == 0) + { + nrows = 1; + } + else + { + tbl->maxrow += nrows; + tbl->row = (GtkSheetRow *)g_realloc(tbl->row,(tbl->maxrow+1)* + sizeof(GtkSheetRow)); + } + + for(i=tbl->maxrow-nrows+1; i<= tbl->maxrow; i++){ + tbl->row[i].height=DEFAULT_ROW_HEIGHT(GTK_WIDGET(tbl)); + tbl->row[i].button.label=NULL; + tbl->row[i].button.child=NULL; + tbl->row[i].button.state=GTK_STATE_NORMAL; + tbl->row[i].button.justification=GTK_JUSTIFY_CENTER; + tbl->row[i].button.label_visible = TRUE; + tbl->row[i].name=NULL; + tbl->row[i].is_visible=TRUE; + tbl->row[i].is_sensitive=TRUE; + if(i>0) + tbl->row[i].top_ypixel=tbl->row[i-1].top_ypixel+tbl->row[i-1].height; + else + { + tbl->row[i].top_ypixel=tbl->column_title_area.height; + if(!GTK_SHEET_COL_TITLES_VISIBLE(tbl)) + tbl->row[i].top_ypixel=0; + } + } + return TRUE; +} + +static gint +InsertRow(GtkSheet *tbl, gint row, gint nrows) +{ + GtkSheetCell **pp; + gint i,j; + GtkSheetCell **auxdata; + GtkSheetRow auxrow; + + AddRow(tbl,nrows); + + for(i=tbl->maxrow; i>=row+nrows; i--){ + auxrow = tbl->row[i]; + tbl->row[i]=tbl->row[i-nrows]; + tbl->row[i].is_visible=tbl->row[i-nrows].is_visible; + tbl->row[i].is_sensitive=tbl->row[i-nrows].is_sensitive; + if(auxrow.is_visible) + tbl->row[i].top_ypixel+=nrows*DEFAULT_ROW_HEIGHT(GTK_WIDGET(tbl)); + tbl->row[i-nrows]=auxrow; + } + + if(row <= tbl->maxallocrow){ + + GrowSheet(tbl,nrows,0); + + for(i=tbl->maxallocrow; i>=row+nrows; i--){ + auxdata = tbl->data[i]; + tbl->data[i]=tbl->data[i-nrows]; + + pp= tbl->data[i]; + for(j=0; j<=tbl->maxalloccol; j++,pp++){ + if(*pp!=(GtkSheetCell *)NULL) + (*pp)->row=i; + + } + tbl->data[i-nrows]=auxdata; + } + } + gtk_sheet_recalc_top_ypixels(tbl, 0); + return TRUE; +} + +static gint +InsertColumn(GtkSheet *tbl, gint col, gint ncols) +{ + gint i,j; + GtkSheetColumn auxcol; + + AddColumn(tbl,ncols); + + for(i=tbl->maxcol; i>=col+ncols; i--){ + auxcol = tbl->column[i]; + tbl->column[i]=tbl->column[i-ncols]; + tbl->column[i].is_visible=tbl->column[i-ncols].is_visible; + tbl->column[i].is_sensitive=tbl->column[i-ncols].is_sensitive; + tbl->column[i].left_text_column=tbl->column[i-ncols].left_text_column; + tbl->column[i].right_text_column=tbl->column[i-ncols].right_text_column; + tbl->column[i].justification=tbl->column[i-ncols].justification; + if(auxcol.is_visible) tbl->column[i].left_xpixel+=ncols*DEFAULT_COLUMN_WIDTH; + tbl->column[i-ncols]=auxcol; + } + + if(col <= tbl->maxalloccol){ + + GrowSheet(tbl,0,ncols); + + for(i=0; i<=tbl->maxallocrow; i++){ + for(j=tbl->maxalloccol; j>=col+ncols; j--){ + gtk_sheet_real_cell_clear(tbl, i, j, TRUE); + tbl->data[i][j]=tbl->data[i][j-ncols]; + if(tbl->data[i][j]) tbl->data[i][j]->col=j; + tbl->data[i][j-ncols]=NULL; + } + } + } + gtk_sheet_recalc_left_xpixels(tbl, 0); + return TRUE; +} + +static gint +DeleteRow(GtkSheet *tbl, gint row, gint nrows) +{ + GtkSheetCell **auxdata = NULL; + gint i,j; + + if(nrows <= 0 || row > tbl->maxrow) return TRUE; + + nrows=MIN(nrows,tbl->maxrow-row+1); + + for(i=row; i<row+nrows; i++){ + if(tbl->row[i].name){ + g_free(tbl->row[i].name); + tbl->row[i].name = NULL; + } + if(tbl->row[i].button.label){ + g_free(tbl->row[i].button.label); + tbl->row[i].button.label = NULL; + } + } + + for(i=row; i<=tbl->maxrow-nrows; i++){ + if(i+nrows <= tbl->maxrow){ + tbl->row[i]=tbl->row[i+nrows]; + } + } + + if(row <= tbl->maxallocrow){ + + for(i=row; i<=tbl->maxrow-nrows; i++){ + if(i<=tbl->maxallocrow){ + auxdata=tbl->data[i]; + for(j=0; j<=tbl->maxalloccol; j++){ + gtk_sheet_real_cell_clear(tbl, i, j, TRUE); + } + } + if(i+nrows<=tbl->maxallocrow){ + tbl->data[i]=tbl->data[i+nrows]; + tbl->data[i+nrows]=auxdata; + for(j=0; j<=tbl->maxalloccol; j++){ + if(tbl->data[i][j]) tbl->data[i][j]->row=i; + } + } + } + + for(i=tbl->maxrow-nrows+1; i<=tbl->maxallocrow; i++){ + if(i > 0 && tbl->data[i]){ + g_free(tbl->data[i]); + tbl->data[i] = NULL; + } + } + + tbl->maxallocrow-=MIN(nrows,tbl->maxallocrow-row+1); + tbl->maxallocrow = MIN(tbl->maxallocrow, tbl->maxrow); + + } + + tbl->maxrow-=nrows; + gtk_sheet_recalc_top_ypixels(tbl, 0); + return TRUE; +} + +static gint +DeleteColumn(GtkSheet *tbl, gint column, gint ncols) +{ + gint i,j; + GtkSheetColumn auxcol; + + ncols = MIN(ncols,tbl->maxcol-column+1); + + if(ncols <= 0 || column > tbl->maxcol) return TRUE; + + for(i=column; i<column+ncols; i++){ + auxcol=tbl->column[i]; + if(tbl->column[i].name){ + g_free(tbl->column[i].name); + tbl->column[i].name = NULL; + } + if(tbl->column[i].button.label){ + g_free(tbl->column[i].button.label); + tbl->column[i].button.label = NULL; + } + } + + for(i=column; i<=tbl->maxcol-ncols; i++){ + if(i+ncols <= tbl->maxcol){ + tbl->column[i]=tbl->column[i+ncols]; + } + } + + if(column <= tbl->maxalloccol){ + + for(i=column; i<=tbl->maxcol-ncols; i++){ + if(i<=tbl->maxalloccol){ + for(j=0; j<=tbl->maxallocrow; j++){ + gtk_sheet_real_cell_clear(tbl, j, i, TRUE); + if(i+ncols <= tbl->maxalloccol){ + tbl->data[j][i] = tbl->data[j][i+ncols]; + tbl->data[j][i+ncols] = NULL; + if(tbl->data[j][i]) tbl->data[j][i]->col=i; + } + } + } + + } + + tbl->maxalloccol-=MIN(ncols,tbl->maxalloccol-column+1); + tbl->maxalloccol = MIN(tbl->maxalloccol, tbl->maxcol); + } + tbl->maxcol-=ncols; + gtk_sheet_recalc_left_xpixels(tbl, 0); + return TRUE; +} + +static gint +GrowSheet(GtkSheet *tbl, gint newrows, gint newcols) +{ + gint i,j; + gint inirow, inicol; + + inirow = tbl->maxallocrow + 1; + inicol = tbl->maxalloccol + 1; + + tbl->maxalloccol = tbl->maxalloccol + newcols; + tbl->maxallocrow = tbl->maxallocrow + newrows; + + if(newrows>0){ + tbl->data = (GtkSheetCell***) + g_realloc(tbl->data,(tbl->maxallocrow+1)*sizeof(GtkSheetCell **)+sizeof(double)); + + for(i=inirow; i <= tbl->maxallocrow; i++){ + tbl->data[i] = (GtkSheetCell **) \ + g_malloc((tbl->maxcol+1)*sizeof(GtkSheetCell *)+sizeof(double)); + for(j=0; j<inicol; j++) { + tbl->data[i][j] = NULL; + } + } + + } + + if(newcols>0){ + for(i=0; i <= tbl->maxallocrow; i++) { + tbl->data[i] = (GtkSheetCell **) \ + g_realloc(tbl->data[i],(tbl->maxalloccol+1)*sizeof(GtkSheetCell *)+sizeof(double)); + for(j=inicol; j <= tbl->maxalloccol; j++) { + tbl->data[i][j] = NULL; + } + } + } + + return(0); +} + +static gint +CheckBounds(GtkSheet *tbl, gint row, gint col) +{ + gint newrows=0,newcols=0; + + if(col>tbl->maxalloccol) newcols=col-tbl->maxalloccol; + if(row>tbl->maxallocrow) newrows=row-tbl->maxallocrow; + if(newrows>0 || newcols>0) GrowSheet(tbl, newrows, newcols); + return(0); +} + +/******************************************************************** + * Container Functions: + * gtk_sheet_add + * gtk_sheet_put + * gtk_sheet_attach + * gtk_sheet_remove + * gtk_sheet_move_child + * gtk_sheet_position_child + * gtk_sheet_position_children + * gtk_sheet_realize_child + * gtk_sheet_get_child_at + ********************************************************************/ + +GtkSheetChild * +gtk_sheet_put(GtkSheet *sheet, GtkWidget *child, gint x, gint y) +{ + GtkRequisition child_requisition; + GtkSheetChild *child_info; + + g_return_val_if_fail(sheet != NULL, NULL); + g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL); + g_return_val_if_fail(child != NULL, NULL); + g_return_val_if_fail(child->parent == NULL, NULL); + + child_info = g_new (GtkSheetChild, 1); + child_info->widget = child; + child_info->x = x; + child_info->y = y; + child_info->window = NULL; + child_info->attached_to_cell = FALSE; + + sheet->children = g_list_append(sheet->children, child_info); + + gtk_widget_set_parent (child, GTK_WIDGET(sheet)); + + gtk_widget_size_request(child, &child_requisition); + + if (GTK_WIDGET_VISIBLE(GTK_WIDGET(sheet))) + { + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + !GTK_WIDGET_REALIZED(child)) + gtk_sheet_realize_child(sheet, child_info); + + if(GTK_WIDGET_MAPPED(GTK_WIDGET(sheet)) && + !GTK_WIDGET_MAPPED(child)) + gtk_widget_map(child); + } + + gtk_sheet_position_child(sheet, child_info); + +/* This will avoid drawing on the titles */ + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) + { + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->row_title_window); + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->column_title_window); + } + + return (child_info); +} + +void +gtk_sheet_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col, + gfloat x_align, gfloat y_align) +{ + GdkRectangle area; + GtkSheetChild *child; + + if(row < 0 || col < 0){ + gtk_sheet_button_attach(sheet, widget, row, col, x_align, y_align); + return; + } + + gtk_sheet_get_cell_area(sheet, row, col, &area); + + child = gtk_sheet_put(sheet, widget, area.x, area.y); + + child->attached_to_cell = TRUE; + child->row = row; + child->col = col; + child->x_align = x_align; + child->y_align = y_align; +} + +void +gtk_sheet_button_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col, + gfloat x_align, gfloat y_align) +{ + GtkSheetButton *button; + GtkSheetChild *child; + GtkRequisition button_requisition; + + if(row >= 0 && col >= 0) return; + if(row < 0 && col < 0) return; + + child = g_new (GtkSheetChild, 1); + child->widget = widget; + child->x = 0; + child->y = 0; + child->window = NULL; + child->attached_to_cell = TRUE; + child->row = row; + child->col = col; + child->x_align = x_align; + child->y_align = y_align; + + if(row == -1){ + button = &sheet->column[col].button; + button->child = child; + } + else + { + button = &sheet->row[row].button; + button->child = child; + } + + sheet->children = g_list_append(sheet->children, child); + + gtk_widget_set_parent (widget, GTK_WIDGET(sheet)); + gtk_sheet_button_size_request(sheet, button, &button_requisition); + + if(row == -1){ + if(button_requisition.height > sheet->column_title_area.height) + sheet->column_title_area.height = button_requisition.height; + if(button_requisition.width > sheet->column[col].width) + sheet->column[col].width = button_requisition.width; + } + + if(col == -1){ + if(button_requisition.width > sheet->row_title_area.width) + sheet->row_title_area.width = button_requisition.width; + if(button_requisition.height > sheet->row[row].height) + sheet->row[row].height = button_requisition.height; + } + + if (GTK_WIDGET_VISIBLE(GTK_WIDGET(sheet))) + { + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + !GTK_WIDGET_REALIZED(widget)) + gtk_sheet_realize_child(sheet, child); + + if(GTK_WIDGET_MAPPED(GTK_WIDGET(sheet)) && + !GTK_WIDGET_MAPPED(widget)) + gtk_widget_map(widget); + } + + if(row == -1) size_allocate_column_title_buttons(sheet); + if(col == -1) size_allocate_row_title_buttons(sheet); + +} + + +static void +label_size_request(GtkSheet *sheet, gchar *label, GtkRequisition *req) +{ + gchar *words; + gchar word[1000]; + gint n = 0; + gint row_height = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)) - 2*CELLOFFSET + 2; + + req->height = 0; + req->width = 0; + words=label; + + while(words && *words != '\0'){ + if(*words == '\n' || *(words+1) == '\0'){ + req->height += row_height; + + word[n] = '\0'; + req->width = MAX( req->width, gdk_string_width (GTK_WIDGET (sheet)->style->font, word) ); + n = 0; + } else { + word[n++] = *words; + } + words++; + } + + if(n > 0) req->height -= 2; +} + + + +static void +gtk_sheet_button_size_request (GtkSheet *sheet, + GtkSheetButton *button, + GtkRequisition *button_requisition) +{ + GtkRequisition requisition; + GtkRequisition label_requisition; + + /* Taken from 2.2 */ + if(GTK_SHEET_AUTORESIZE(sheet) && button->label && strlen(button->label) > 0){ + label_size_request(sheet, button->label, &label_requisition); + label_requisition.width += 2*CELLOFFSET; + label_requisition.height += 2*CELLOFFSET; + } else { + label_requisition.height = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + label_requisition.width = COLUMN_MIN_WIDTH; + } + + if(button->child) + { + gtk_widget_size_request(button->child->widget, &requisition); + /* taken from 2.2 */ + requisition.width += 2; /* 2*button->child->xpadding; */ + requisition.height += 2; /* 2*button->child->ypadding; */ + requisition.width += 2*sheet->button->style->klass->xthickness; + requisition.height += 2*sheet->button->style->klass->ythickness; + } else { + requisition.height = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + requisition.width = COLUMN_MIN_WIDTH; + } + + *button_requisition = requisition; + + button_requisition->width += sheet->button->style->klass->xthickness; + button_requisition->height += sheet->button->style->klass->ythickness; +} + +void +gtk_sheet_move_child(GtkSheet *sheet, GtkWidget *widget, gint x, gint y) +{ + GtkSheetChild *child; + GList *children; + + g_return_if_fail(sheet != NULL); + g_return_if_fail(GTK_IS_SHEET(sheet)); + + children = sheet->children; + while(children) + { + child = children->data; + + if(child->widget == widget){ + child->x = x; + child->y = y; + child->row = ROW_FROM_YPIXEL(sheet, y); + child->col = COLUMN_FROM_XPIXEL(sheet, x); + gtk_sheet_position_child(sheet, child); + return; + } + + children = children->next; + } + + g_warning("Widget must be a GtkSheet child"); + +} + +static void +gtk_sheet_position_child(GtkSheet *sheet, GtkSheetChild *child) +{ + GtkRequisition child_requisition; + gint xoffset = 0; + gint yoffset = 0; + gint x = 0, y = 0; + GdkRectangle area; + + gtk_widget_get_child_requisition(child->widget, &child_requisition); + + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + yoffset = sheet->column_title_area.height; + + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + xoffset = sheet->row_title_area.width; + + if(child->attached_to_cell){ +/* + child->x = COLUMN_LEFT_XPIXEL(sheet, child->col); + child->y = ROW_TOP_YPIXEL(sheet, child->row); + + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + child->x-=sheet->row_title_area.width; + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + child->y-=sheet->column_title_area.height; + + width = sheet->column[child->col].width; + height = sheet->row[child->row].height; +*/ + + gtk_sheet_get_cell_area(sheet, child->row, child->col, &area); + child->x = area.x; + child->y = area.y; + child->x += (area.width - child_requisition.width) * child->x_align; + child->y += (area.height - child_requisition.height) * child->y_align; + x = child->widget->allocation.x = child->x + xoffset; + y = child->widget->allocation.y = child->y + yoffset; + } + else + { + x = child->widget->allocation.x = child->x + sheet->hoffset + xoffset; + x = child->widget->allocation.x = child->x + xoffset; + y = child->widget->allocation.y = child->y + sheet->voffset + yoffset; + y = child->widget->allocation.y = child->y + yoffset; + } + + child->widget->allocation.width = child_requisition.width; + child->widget->allocation.height = child_requisition.height; + + if(GTK_WIDGET_NO_WINDOW(child->widget)) + { + child->widget->allocation.x = 0; + child->widget->allocation.y = 0; + } + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + GTK_WIDGET_MAPPED(child->widget)) + { + gtk_widget_size_allocate(child->widget, + &child->widget->allocation); + if(GTK_WIDGET_NO_WINDOW(child->widget) && child->window) + { + gdk_window_move_resize(child->window, + x, y, + child->widget->allocation.width, + child->widget->allocation.height); + gtk_widget_draw(child->widget, NULL); + } + } + +} + +static void +gtk_sheet_position_children(GtkSheet *sheet) +{ + GList *children; + GtkSheetChild *child; + + children = sheet->children; + + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->col !=-1 && child->row != -1) + gtk_sheet_position_child(sheet, child); + + if(child->row == -1){ + if(child->col < MIN_VISIBLE_COLUMN(sheet) || + child->col > MAX_VISIBLE_COLUMN(sheet)) + gtk_sheet_child_hide(child); + else + gtk_sheet_child_show(child); + } + if(child->col == -1){ + if(child->row < MIN_VISIBLE_ROW(sheet) || + child->row > MAX_VISIBLE_ROW(sheet)) + gtk_sheet_child_hide(child); + else + gtk_sheet_child_show(child); + } + + children = children->next; + } + +} + +static void +gtk_sheet_remove (GtkContainer *container, GtkWidget *widget) +{ + GtkSheet *sheet; + GList *children; + GtkSheetChild *child = 0; + + g_return_if_fail(container != NULL); + g_return_if_fail(GTK_IS_SHEET(container)); + + sheet = GTK_SHEET(container); + + children = sheet->children; + + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->widget == widget) break; + + children = children->next; + } + + if (children) + { + if(child->row == -1) + sheet->row[child->col].button.child = NULL; + + if(child->col == -1) + sheet->column[child->row].button.child = NULL; + + if(child->window) { + gdk_window_hide(child->window); + gdk_window_unref(child->window); + } + + gtk_widget_unparent (widget); + child->widget = NULL; + + sheet->children = g_list_remove_link (sheet->children, children); + g_list_free_1 (children); + } + +} + +static void +gtk_sheet_realize_child(GtkSheet *sheet, GtkSheetChild *child) +{ + gint attributes_mask; + GtkWidget *widget; + + widget = GTK_WIDGET(sheet); + + if (GTK_WIDGET_NO_WINDOW (child->widget)) + { + GdkWindowAttr attributes; + + gint x = child->x - sheet->hoffset; + gint y = child->y - sheet->voffset; + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = x; + attributes.y = y; + attributes.width = child->widget->requisition.width; + attributes.height = child->widget->requisition.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_EXPOSURE_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + + if(child->row == -1) + child->window = gdk_window_new (sheet->column_title_window, + &attributes, attributes_mask); + else if(child->col == -1) + child->window = gdk_window_new (sheet->row_title_window, + &attributes, attributes_mask); + else + child->window = gdk_window_new (widget->window, + &attributes, attributes_mask); + + if(GTK_IS_PIXMAP(child->widget)){ + gdk_window_shape_combine_mask(child->window, + GTK_PIXMAP(child->widget)->mask, + 0, 0); + } + + gdk_window_set_user_data (child->window, widget); + + if (child->window) + gtk_style_set_background (widget->style, + child->window, + GTK_STATE_NORMAL); + + gtk_widget_set_parent_window(child->widget, child->window); + gdk_window_show(child->window); + + } + + gtk_widget_realize(child->widget); + +} + + + +GtkSheetChild * +gtk_sheet_get_child_at(GtkSheet *sheet, gint row, gint col) +{ + GList *children; + GtkSheetChild *child = 0; + + g_return_val_if_fail(sheet != NULL, NULL); + g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL); + + children = sheet->children; + + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell) + if(child->row == row && child->col == col) break; + + children = children->next; + } + + if(children) return child; + + return NULL; +} + +static void +gtk_sheet_child_hide(GtkSheetChild *child) +{ + g_return_if_fail(child != NULL); + gtk_widget_hide(child->widget); + if(child->window) + gdk_window_hide(child->window); +} + +static void +gtk_sheet_child_show(GtkSheetChild *child) +{ + g_return_if_fail(child != NULL); + + gtk_widget_show(child->widget); + if(child->window) + gdk_window_show(child->window); +} diff --git a/gattrib/src/gtksheet_2_2.c b/gattrib/src/gtksheet_2_2.c new file mode 100644 index 000000000..f5f6d2543 --- /dev/null +++ b/gattrib/src/gtksheet_2_2.c @@ -0,0 +1,8744 @@ +/* GtkSheet widget for Gtk+. + * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar> + * + * Based on GtkClist widget by Jay Painter, but major changes. + * Memory allocation routines inspired on SC (Spreadsheet Calculator) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <glib.h> +#include <glib-object.h> +#include <gobject/gvaluecollector.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtksignal.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkadjustment.h> +#include <gtk/gtktable.h> +#include <gtk/gtkbox.h> +#include <gtk/gtkmain.h> +#include <gtk/gtktypeutils.h> +#include <gtk/gtkentry.h> +#include <gtk/gtkcontainer.h> +#include <gtk/gtkpixmap.h> +#include <pango/pango.h> +#include "gtkitementry_2_2.h" +#include "gtksheet_2_2.h" +#include "gtkextra-marshal.h" + +/* sheet flags */ +enum +{ + GTK_SHEET_IS_LOCKED = 1 << 0, + GTK_SHEET_IS_FROZEN = 1 << 1, + GTK_SHEET_IN_XDRAG = 1 << 2, + GTK_SHEET_IN_YDRAG = 1 << 3, + GTK_SHEET_IN_DRAG = 1 << 4, + GTK_SHEET_IN_SELECTION = 1 << 5, + GTK_SHEET_IN_RESIZE = 1 << 6, + GTK_SHEET_IN_CLIP = 1 << 7, + GTK_SHEET_REDRAW_PENDING = 1 << 8, +}; + +#define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags) +#define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag)) +#define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~(flag)) + +#define GTK_SHEET_IS_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IS_FROZEN) +#define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG) +#define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG) +#define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG) +#define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION) +#define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE) +#define GTK_SHEET_IN_CLIP(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_CLIP) +#define GTK_SHEET_REDRAW_PENDING(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_REDRAW_PENDING) + +#define CELL_SPACING 1 +#define DRAG_WIDTH 6 +#define TIMEOUT_SCROLL 20 +#define TIMEOUT_FLASH 200 +#define TIME_INTERVAL 8 +#define COLUMN_MIN_WIDTH 10 +#define MINROWS 1 +#define MINCOLS 1 +#define MAXLENGTH 30 +#define CELLOFFSET 4 +#define DEFAULT_COLUMN_WIDTH 80 + +static inline guint DEFAULT_ROW_HEIGHT(GtkWidget *widget) +{ + if(!widget->style->font_desc) return 24; + else { + PangoContext *context = gtk_widget_get_pango_context(widget); + PangoFontMetrics *metrics = pango_context_get_metrics(context, + widget->style->font_desc, + pango_context_get_language(context)); + guint val = pango_font_metrics_get_descent(metrics) + + pango_font_metrics_get_ascent(metrics); + pango_font_metrics_unref(metrics); + return PANGO_PIXELS(val)+2*CELLOFFSET; + } +} +static inline guint DEFAULT_FONT_ASCENT(GtkWidget *widget) +{ + if(!widget->style->font_desc) return 12; + else { + PangoContext *context = gtk_widget_get_pango_context(widget); + PangoFontMetrics *metrics = pango_context_get_metrics(context, + widget->style->font_desc, + pango_context_get_language(context)); + guint val = pango_font_metrics_get_ascent(metrics); + pango_font_metrics_unref(metrics); + return PANGO_PIXELS(val); + } +} +static inline guint STRING_WIDTH(GtkWidget *widget, + PangoFontDescription *font, const gchar *text) +{ + PangoRectangle rect; + PangoLayout *layout; + + layout = gtk_widget_create_pango_layout (widget, text); + pango_layout_set_font_description (layout, font); + + pango_layout_get_extents (layout, NULL, &rect); + + g_object_unref(G_OBJECT(layout)); + return PANGO_PIXELS(rect.width); +} + +static inline guint DEFAULT_FONT_DESCENT(GtkWidget *widget) +{ + if(!widget->style->font_desc) return 12; + else { + PangoContext *context = gtk_widget_get_pango_context(widget); + PangoFontMetrics *metrics = pango_context_get_metrics(context, + widget->style->font_desc, + pango_context_get_language(context)); + guint val = pango_font_metrics_get_descent(metrics); + pango_font_metrics_unref(metrics); + return PANGO_PIXELS(val); + } +} + +/* gives the top pixel of the given row in context of + * the sheet's voffset */ +static inline gint +ROW_TOP_YPIXEL(GtkSheet *sheet, gint nrow) +{ + return (sheet->voffset + sheet->row[nrow].top_ypixel); +} + + +/* returns the row index from a y pixel location in the + * context of the sheet's voffset */ +static inline gint +ROW_FROM_YPIXEL(GtkSheet *sheet, gint y) +{ + gint i, cy; + + cy = sheet->voffset; + if(sheet->column_titles_visible) cy += sheet->column_title_area.height; + if(y < cy) return 0; + for (i = 0; i <= sheet->maxrow; i++) + { + if (y >= cy && y <= (cy + sheet->row[i].height) && sheet->row[i].is_visible) + return i; + if(sheet->row[i].is_visible) cy += sheet->row[i].height; + + } + + /* no match */ + return sheet->maxrow; +} + + +/* gives the left pixel of the given column in context of + * the sheet's hoffset */ +static inline gint +COLUMN_LEFT_XPIXEL(GtkSheet *sheet, gint ncol) +{ + return (sheet->hoffset + sheet->column[ncol].left_xpixel); +} + +/* returns the column index from a x pixel location in the + * context of the sheet's hoffset */ +static inline gint +COLUMN_FROM_XPIXEL (GtkSheet * sheet, + gint x) +{ + gint i, cx; + + cx = sheet->hoffset; + if(sheet->row_titles_visible) cx += sheet->row_title_area.width; + if(x < cx) return 0; + for (i = 0; i <= sheet->maxcol; i++) + { + if (x >= cx && x <= (cx + sheet->column[i].width) && sheet->column[i].is_visible) + return i; + if(sheet->column[i].is_visible) cx += sheet->column[i].width; + + } + + /* no match */ + return sheet->maxcol; +} + +/* returns the total height of the sheet */ +static inline gint SHEET_HEIGHT(GtkSheet *sheet) +{ + gint i,cx; + + cx = 0; + if(sheet->column_titles_visible) cx += sheet->column_title_area.height; + for (i=0;i<=sheet->maxrow; i++) + if(sheet->row[i].is_visible) cx += sheet->row[i].height; + + return cx; +} + + +/* returns the total width of the sheet */ +static inline gint SHEET_WIDTH(GtkSheet *sheet) +{ + gint i,cx; + + cx = 0; + if(sheet->row_titles_visible) cx += sheet->row_title_area.width; + for (i=0;i<=sheet->maxcol; i++) + if(sheet->column[i].is_visible) cx += sheet->column[i].width; + + return cx; +} + +#define MIN_VISIBLE_ROW(sheet) sheet->view.row0 +#define MAX_VISIBLE_ROW(sheet) sheet->view.rowi +#define MIN_VISIBLE_COLUMN(sheet) sheet->view.col0 +#define MAX_VISIBLE_COLUMN(sheet) sheet->view.coli + + +static inline gint +POSSIBLE_XDRAG(GtkSheet *sheet, gint x, gint *drag_column) +{ + gint column, xdrag; + + column=COLUMN_FROM_XPIXEL(sheet, x); + *drag_column=column; + + xdrag=COLUMN_LEFT_XPIXEL(sheet,column)+CELL_SPACING; + if(x <= xdrag+DRAG_WIDTH/2 && column != 0){ + while(!sheet->column[column-1].is_visible && column>0) column--; + *drag_column=column-1; + return sheet->column[column-1].is_sensitive; + } + + xdrag+=sheet->column[column].width; + if(x >= xdrag-DRAG_WIDTH/2 && x <= xdrag+DRAG_WIDTH/2) + return sheet->column[column].is_sensitive; + + return FALSE; +} + +static inline gint +POSSIBLE_YDRAG(GtkSheet *sheet, gint y, gint *drag_row) +{ + gint row, ydrag; + + row=ROW_FROM_YPIXEL(sheet, y); + *drag_row=row; + + ydrag=ROW_TOP_YPIXEL(sheet,row)+CELL_SPACING; + if(y <= ydrag+DRAG_WIDTH/2 && row != 0){ + while(!sheet->row[row-1].is_visible && row>0) row--; + *drag_row=row-1; + return sheet->row[row-1].is_sensitive; + } + + ydrag+=sheet->row[row].height; + + if(y >= ydrag-DRAG_WIDTH/2 && y <= ydrag+DRAG_WIDTH/2) + return sheet->row[row].is_sensitive; + + + return FALSE; +} + +static inline gint POSSIBLE_DRAG(GtkSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint ydrag, xdrag; + + *drag_column=COLUMN_FROM_XPIXEL(sheet,x); + *drag_row=ROW_FROM_YPIXEL(sheet,y); + + if(x>=COLUMN_LEFT_XPIXEL(sheet,sheet->range.col0)-DRAG_WIDTH/2 && + x<=COLUMN_LEFT_XPIXEL(sheet,sheet->range.coli)+ + sheet->column[sheet->range.coli].width+DRAG_WIDTH/2){ + ydrag=ROW_TOP_YPIXEL(sheet,sheet->range.row0); + if(y>=ydrag-DRAG_WIDTH/2 && y<=ydrag+DRAG_WIDTH/2){ + *drag_row=sheet->range.row0; + return TRUE; + } + ydrag=ROW_TOP_YPIXEL(sheet,sheet->range.rowi)+ + sheet->row[sheet->range.rowi].height; + if(y>=ydrag-DRAG_WIDTH/2 && y<=ydrag+DRAG_WIDTH/2){ + *drag_row=sheet->range.rowi; + return TRUE; + } + } + + if(y>=ROW_TOP_YPIXEL(sheet,sheet->range.row0)-DRAG_WIDTH/2 && + y<=ROW_TOP_YPIXEL(sheet,sheet->range.rowi)+ + sheet->row[sheet->range.rowi].height+DRAG_WIDTH/2){ + xdrag=COLUMN_LEFT_XPIXEL(sheet,sheet->range.col0); + if(x>=xdrag-DRAG_WIDTH/2 && x<=xdrag+DRAG_WIDTH/2){ + *drag_column=sheet->range.col0; + return TRUE; + } + xdrag=COLUMN_LEFT_XPIXEL(sheet,sheet->range.coli)+ + sheet->column[sheet->range.coli].width; + if(x>=xdrag-DRAG_WIDTH/2 && x<=xdrag+DRAG_WIDTH/2){ + *drag_column=sheet->range.coli; + return TRUE; + } + } + return FALSE; +} + +static inline gint POSSIBLE_RESIZE(GtkSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint xdrag, ydrag; + + xdrag=COLUMN_LEFT_XPIXEL(sheet,sheet->range.coli)+ + sheet->column[sheet->range.coli].width; + + ydrag=ROW_TOP_YPIXEL(sheet,sheet->range.rowi)+ + sheet->row[sheet->range.rowi].height; + + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + ydrag = ROW_TOP_YPIXEL(sheet, sheet->view.row0); + + if(sheet->state == GTK_SHEET_ROW_SELECTED) + xdrag = COLUMN_LEFT_XPIXEL(sheet, sheet->view.col0); + + *drag_column=COLUMN_FROM_XPIXEL(sheet,x); + *drag_row=ROW_FROM_YPIXEL(sheet,y); + + if(x>=xdrag-DRAG_WIDTH/2 && x<=xdrag+DRAG_WIDTH/2 && + y>=ydrag-DRAG_WIDTH/2 && y<=ydrag+DRAG_WIDTH/2) return TRUE; + + return FALSE; +} + +static void gtk_sheet_class_init (GtkSheetClass * klass); +static void gtk_sheet_init (GtkSheet * sheet); +static void gtk_sheet_destroy (GtkObject * object); +static void gtk_sheet_finalize (GObject * object); +static void gtk_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void gtk_sheet_realize (GtkWidget * widget); +static void gtk_sheet_unrealize (GtkWidget * widget); +static void gtk_sheet_map (GtkWidget * widget); +static void gtk_sheet_unmap (GtkWidget * widget); +static gint gtk_sheet_expose (GtkWidget * widget, + GdkEventExpose * event); +static void gtk_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +static void gtk_sheet_set_scroll_adjustments (GtkSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + +static gint gtk_sheet_button_press (GtkWidget * widget, + GdkEventButton * event); +static gint gtk_sheet_button_release (GtkWidget * widget, + GdkEventButton * event); +static gint gtk_sheet_motion (GtkWidget * widget, + GdkEventMotion * event); +/* + *static gint gtk_sheet_entry_key_press (GtkWidget *widget, + * GdkEventKey *key); + */ +static gint gtk_sheet_key_press (GtkWidget *widget, + GdkEventKey *key); + +static void gtk_sheet_size_request (GtkWidget * widget, + GtkRequisition * requisition); +static void gtk_sheet_size_allocate (GtkWidget * widget, + GtkAllocation * allocation); + +/* Sheet queries */ + +static gint gtk_sheet_range_isvisible (GtkSheet * sheet, + GtkSheetRange range); +static gint gtk_sheet_cell_isvisible (GtkSheet * sheet, + gint row, gint column); +/* Clipped Range */ + +static gint gtk_sheet_scroll (gpointer data); +static gint gtk_sheet_flash (gpointer data); + +/* Drawing Routines */ + +/* draw cell background and frame */ +static void gtk_sheet_cell_draw_default (GtkSheet *sheet, + gint row, gint column); + +/* draw cell border */ +static void gtk_sheet_cell_draw_border (GtkSheet *sheet, + gint row, gint column, + gint mask); + +/* draw cell contents */ +static void gtk_sheet_cell_draw_label (GtkSheet *sheet, + gint row, gint column); + +/* draw visible part of range. If range==NULL then draw the whole screen */ +static void gtk_sheet_range_draw (GtkSheet *sheet, + const GtkSheetRange *range); + +/* highlight the visible part of the selected range */ +static void gtk_sheet_range_draw_selection (GtkSheet *sheet, + GtkSheetRange range); + +/* Selection */ + +static gint gtk_sheet_move_query (GtkSheet *sheet, + gint row, gint column); +static void gtk_sheet_real_select_range (GtkSheet * sheet, + GtkSheetRange * range); +static void gtk_sheet_real_unselect_range (GtkSheet * sheet, + const GtkSheetRange * range); +static void gtk_sheet_extend_selection (GtkSheet *sheet, + gint row, gint column); +static void gtk_sheet_new_selection (GtkSheet *sheet, + GtkSheetRange *range); +static void gtk_sheet_draw_border (GtkSheet *sheet, + GtkSheetRange range); +static void gtk_sheet_draw_corners (GtkSheet *sheet, + GtkSheetRange range); + + +/* Active Cell handling */ + +static void gtk_sheet_entry_changed (GtkWidget *widget, + gpointer data); +static gboolean gtk_sheet_deactivate_cell (GtkSheet *sheet); +static void gtk_sheet_hide_active_cell (GtkSheet *sheet); +static gboolean gtk_sheet_activate_cell (GtkSheet *sheet, + gint row, gint col); +static void gtk_sheet_draw_active_cell (GtkSheet *sheet); +static void gtk_sheet_show_active_cell (GtkSheet *sheet); +static void gtk_sheet_click_cell (GtkSheet *sheet, + gint row, + gint column, + gboolean *veto); + +/* Backing Pixmap */ + +static void gtk_sheet_make_backing_pixmap (GtkSheet *sheet, + guint width, guint height); +static void gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, + GtkSheetRange range); +/* Scrollbars */ + +static void adjust_scrollbars (GtkSheet * sheet); +static void vadjustment_changed (GtkAdjustment * adjustment, + gpointer data); +static void hadjustment_changed (GtkAdjustment * adjustment, + gpointer data); +static void vadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data); +static void hadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data); + + +static void draw_xor_vline (GtkSheet * sheet); +static void draw_xor_hline (GtkSheet * sheet); +static void draw_xor_rectangle (GtkSheet *sheet, + GtkSheetRange range); +static void gtk_sheet_draw_flashing_range (GtkSheet *sheet, + GtkSheetRange range); +static guint new_column_width (GtkSheet * sheet, + gint column, + gint * x); +static guint new_row_height (GtkSheet * sheet, + gint row, + gint * y); +/* Sheet Button */ + +static void create_global_button (GtkSheet *sheet); +static void global_button_clicked (GtkWidget *widget, + gpointer data); +/* Sheet Entry */ + +static void create_sheet_entry (GtkSheet *sheet); +static void gtk_sheet_size_allocate_entry (GtkSheet *sheet); +static void gtk_sheet_entry_set_max_size (GtkSheet *sheet); + +/* Sheet button gadgets */ + +static void size_allocate_column_title_buttons (GtkSheet * sheet); +static void size_allocate_row_title_buttons (GtkSheet * sheet); +static void gtk_sheet_recalc_top_ypixels (GtkSheet *sheet, + gint row); +static void gtk_sheet_recalc_left_xpixels (GtkSheet *sheet, + gint column); +static void row_button_set (GtkSheet *sheet, + gint row); +static void column_button_set (GtkSheet *sheet, + gint column); +static void row_button_release (GtkSheet *sheet, + gint row); +static void column_button_release (GtkSheet *sheet, + gint column); +static void gtk_sheet_button_draw (GtkSheet *sheet, + gint row, gint column); +static void size_allocate_global_button (GtkSheet *sheet); +static void gtk_sheet_button_size_request (GtkSheet *sheet, + GtkSheetButton *button, + GtkRequisition *requisition); + +/* Attributes routines */ + +static void gtk_sheet_set_cell_attributes (GtkSheet *sheet, + gint row, gint col, + GtkSheetCellAttr attributes); + +static void init_attributes (GtkSheet *sheet, gint col, + GtkSheetCellAttr *attributes); +/* Memory allocation routines */ +static void gtk_sheet_real_range_clear (GtkSheet *sheet, + const GtkSheetRange *range, + gboolean delete); +static void gtk_sheet_real_cell_clear (GtkSheet *sheet, + gint row, + gint column, + gboolean delete); +static GtkSheetCell * gtk_sheet_cell_new (void); +static gint AddRow (GtkSheet *sheet, gint nrows); +static gint AddColumn (GtkSheet *sheet, gint ncols); +static gint InsertRow (GtkSheet *sheet, gint row, gint nrows); +static gint InsertColumn (GtkSheet *sheet, gint col, gint ncols); +static gint DeleteRow (GtkSheet *sheet, gint row, gint nrows); +static gint DeleteColumn (GtkSheet *sheet, gint col, gint ncols); +static gint GrowSheet (GtkSheet *sheet, + gint newrows, gint newcols); +static gint CheckBounds (GtkSheet *sheet, + gint row, gint col); + +/* Container Functions */ +static void gtk_sheet_remove (GtkContainer *container, + GtkWidget *widget); +static void gtk_sheet_realize_child (GtkSheet *sheet, + GtkSheetChild *child); +static void gtk_sheet_position_child (GtkSheet *sheet, + GtkSheetChild *child); +static void gtk_sheet_position_children (GtkSheet *sheet); +static void gtk_sheet_child_show (GtkSheetChild *child); +static void gtk_sheet_child_hide (GtkSheetChild *child); +static void gtk_sheet_column_size_request (GtkSheet *sheet, + gint col, + guint *requisition); +static void gtk_sheet_row_size_request (GtkSheet *sheet, + gint row, + guint *requisition); + + +/* Signals */ + +/* Imported from gtkextra.c by SDB 7.22.2004 */ +void +_gtkextra_signal_emit(GtkObject *object, guint signal_id, ...) +{ + gboolean *result; + GValue ret = { 0, }; + GValue instance_and_params [10] = { {0, }, }; + va_list var_args; + GSignalQuery query; + gchar *error; + int i; + + va_start (var_args, signal_id); + + g_value_init(instance_and_params + 0, GTK_OBJECT_TYPE(object)); + g_value_set_instance (instance_and_params + 0, G_OBJECT(object)); + + g_signal_query(signal_id, &query); + + for (i = 0; i < query.n_params; i++) + { + gboolean static_scope = query.param_types[i]&~G_SIGNAL_TYPE_STATIC_SCOPE; + g_value_init(instance_and_params + i + 1, query.param_types[i]); + + + G_VALUE_COLLECT (instance_and_params + i + 1, + var_args, + static_scope ? G_VALUE_NOCOPY_CONTENTS : 0, + &error); + + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + while (i-- > 0) + g_value_unset (instance_and_params + i); + + va_end (var_args); + return; + } + + + } + + g_value_init(&ret, query.return_type); + result = va_arg(var_args,gboolean *); + g_value_set_boolean(&ret, *result); + g_signal_emitv(instance_and_params, signal_id, 0, &ret); + *result = g_value_get_boolean(&ret); + g_value_unset (&ret); + + for (i = 0; i < query.n_params; i++) + g_value_unset (instance_and_params + 1 + i); + g_value_unset (instance_and_params + 0); + + va_end (var_args); +} + + +enum { + SELECT_ROW, + SELECT_COLUMN, + SELECT_RANGE, + CLIP_RANGE, + RESIZE_RANGE, + MOVE_RANGE, + TRAVERSE, + DEACTIVATE, + ACTIVATE, + SET_CELL, + CLEAR_CELL, + CHANGED, + NEW_COL_WIDTH, + NEW_ROW_HEIGHT, + LAST_SIGNAL +}; + +static GtkContainerClass *parent_class = NULL; +static guint sheet_signals[LAST_SIGNAL] = {0}; + + +GType +gtk_sheet_get_type () +{ + static GType sheet_type = 0; + + if (!sheet_type) + { + static const GTypeInfo sheet_info = + { + sizeof (GtkSheetClass), + NULL, + NULL, + (GClassInitFunc) gtk_sheet_class_init, + NULL, + NULL, + sizeof (GtkSheet), + 0, + (GInstanceInitFunc) gtk_sheet_init, + NULL, + }; + sheet_type = + g_type_register_static (GTK_TYPE_CONTAINER, "GtkSheet", + &sheet_info, 0); + } + return sheet_type; +} + +static GtkSheetRange* +gtk_sheet_range_copy (const GtkSheetRange *range) +{ + GtkSheetRange *new_range; + + g_return_val_if_fail (range != NULL, NULL); + + new_range = g_new (GtkSheetRange, 1); + + *new_range = *range; + + return new_range; +} + +static void +gtk_sheet_range_free (GtkSheetRange *range) +{ + g_return_if_fail (range != NULL); + + g_free (range); +} + +GType +gtk_sheet_range_get_type (void) +{ + static GType sheet_range_type; + + if(!sheet_range_type) + { + sheet_range_type = g_boxed_type_register_static("GtkSheetRange", (GBoxedCopyFunc)gtk_sheet_range_copy, (GBoxedFreeFunc)gtk_sheet_range_free); + } + return sheet_range_type; + +} + +static void +gtk_sheet_class_init (GtkSheetClass * klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + object_class = (GtkObjectClass *) klass; + widget_class = (GtkWidgetClass *) klass; + container_class = (GtkContainerClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + sheet_signals[SELECT_ROW] = + gtk_signal_new ("select_row", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, select_row), + gtkextra_VOID__INT, + GTK_TYPE_NONE, 1, GTK_TYPE_INT); + + sheet_signals[SELECT_COLUMN] = + gtk_signal_new ("select_column", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, select_column), + gtkextra_VOID__INT, + GTK_TYPE_NONE, 1, GTK_TYPE_INT); + + sheet_signals[SELECT_RANGE] = + gtk_signal_new ("select_range", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, select_range), + gtkextra_VOID__BOXED, + GTK_TYPE_NONE, 1, GTK_TYPE_SHEET_RANGE); + + sheet_signals[CLIP_RANGE] = + gtk_signal_new ("clip_range", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, clip_range), + gtkextra_VOID__BOXED, + GTK_TYPE_NONE, 1, GTK_TYPE_SHEET_RANGE); + + sheet_signals[RESIZE_RANGE] = + gtk_signal_new ("resize_range", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, resize_range), + gtkextra_VOID__BOXED_BOXED, + GTK_TYPE_NONE, 2, GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE); + sheet_signals[MOVE_RANGE] = + gtk_signal_new ("move_range", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, move_range), + gtkextra_VOID__BOXED_BOXED, + GTK_TYPE_NONE, 2, GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE); + sheet_signals[TRAVERSE] = + gtk_signal_new ("traverse", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, traverse), + gtkextra_BOOLEAN__INT_INT_POINTER_POINTER, + GTK_TYPE_BOOL, 4, GTK_TYPE_INT, GTK_TYPE_INT, + GTK_TYPE_POINTER, GTK_TYPE_POINTER); + + sheet_signals[DEACTIVATE] = + gtk_signal_new ("deactivate", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, deactivate), + gtkextra_BOOLEAN__INT_INT, + GTK_TYPE_BOOL, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[ACTIVATE] = + gtk_signal_new ("activate", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, activate), + gtkextra_BOOLEAN__INT_INT, + GTK_TYPE_BOOL, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[SET_CELL] = + gtk_signal_new ("set_cell", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, set_cell), + gtkextra_VOID__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[CLEAR_CELL] = + gtk_signal_new ("clear_cell", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, clear_cell), + gtkextra_VOID__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[CHANGED] = + gtk_signal_new ("changed", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, changed), + gtkextra_VOID__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[NEW_COL_WIDTH] = + gtk_signal_new ("new_column_width", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, changed), + gtkextra_VOID__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + sheet_signals[NEW_ROW_HEIGHT] = + gtk_signal_new ("new_row_height", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, changed), + gtkextra_VOID__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + + + widget_class->set_scroll_adjustments_signal = + gtk_signal_new ("set_scroll_adjustments", + GTK_RUN_LAST, + GTK_CLASS_TYPE(object_class), + GTK_SIGNAL_OFFSET (GtkSheetClass, set_scroll_adjustments), + gtkextra_VOID__OBJECT_OBJECT, + GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + + + container_class->add = NULL; + container_class->remove = gtk_sheet_remove; + container_class->forall = gtk_sheet_forall; + + object_class->destroy = gtk_sheet_destroy; + gobject_class->finalize = gtk_sheet_finalize; + + widget_class->realize = gtk_sheet_realize; + widget_class->unrealize = gtk_sheet_unrealize; + widget_class->map = gtk_sheet_map; + widget_class->unmap = gtk_sheet_unmap; + widget_class->style_set = gtk_sheet_style_set; + widget_class->button_press_event = gtk_sheet_button_press; + widget_class->button_release_event = gtk_sheet_button_release; + widget_class->motion_notify_event = gtk_sheet_motion; + widget_class->key_press_event = gtk_sheet_key_press; + widget_class->expose_event = gtk_sheet_expose; + widget_class->size_request = gtk_sheet_size_request; + widget_class->size_allocate = gtk_sheet_size_allocate; + widget_class->focus_in_event = NULL; + widget_class->focus_out_event = NULL; + + klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments; + klass->select_row = NULL; + klass->select_column = NULL; + klass->select_range = NULL; + klass->clip_range = NULL; + klass->resize_range = NULL; + klass->move_range = NULL; + klass->traverse = NULL; + klass->deactivate = NULL; + klass->activate = NULL; + klass->set_cell = NULL; + klass->clear_cell = NULL; + klass->changed = NULL; + +} + +static void +gtk_sheet_init (GtkSheet *sheet) +{ + sheet->children = NULL; + + sheet->flags = 0; + sheet->selection_mode = GTK_SELECTION_BROWSE; + sheet->freeze_count = 0; + sheet->state = GTK_SHEET_NORMAL; + + GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW); + GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS); + + sheet->maxrow = 0; + sheet->maxcol = 0; + + sheet->view.row0 = 0; + sheet->view.col0 = 0; + sheet->view.rowi = 0; + sheet->view.coli = 0; + + sheet->maxallocrow = 0; + sheet->maxalloccol = 0; + + sheet->column_title_window=NULL; + sheet->column_title_area.x=0; + sheet->column_title_area.y=0; + sheet->column_title_area.width=0; + sheet->column_title_area.height=DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + + sheet->row_title_window=NULL; + sheet->row_title_area.x=0; + sheet->row_title_area.y=0; + sheet->row_title_area.width=DEFAULT_COLUMN_WIDTH; + sheet->row_title_area.height=0; + + sheet->active_cell.row=0; + sheet->active_cell.col=0; + sheet->selection_cell.row=0; + sheet->selection_cell.col=0; + + sheet->sheet_entry=NULL; + sheet->pixmap=NULL; + + sheet->range.row0=0; + sheet->range.rowi=0; + sheet->range.col0=0; + sheet->range.coli=0; + + sheet->state=GTK_SHEET_NORMAL; + + sheet->sheet_window = NULL; + sheet->sheet_window_width = 0; + sheet->sheet_window_height = 0; + sheet->sheet_entry = NULL; + sheet->button = NULL; + + sheet->hoffset = 0; + sheet->voffset = 0; + + sheet->hadjustment = NULL; + sheet->vadjustment = NULL; + + sheet->cursor_drag = gdk_cursor_new(GDK_PLUS); + sheet->xor_gc = NULL; + sheet->fg_gc = NULL; + sheet->bg_gc = NULL; + sheet->x_drag = 0; + sheet->y_drag = 0; + + gdk_color_white(gdk_colormap_get_system(), &sheet->bg_color); + gdk_color_parse("gray", &sheet->grid_color); + gdk_color_alloc(gdk_colormap_get_system(), &sheet->grid_color); + sheet->show_grid = TRUE; +} + +GtkWidget * +gtk_sheet_new (guint rows, guint columns, const gchar *title) +{ + GtkWidget *widget; + + /* sanity check */ + g_return_val_if_fail (columns >= MINCOLS, NULL); + g_return_val_if_fail (rows >= MINROWS, NULL); + + widget = gtk_type_new (gtk_sheet_get_type ()); + + gtk_sheet_construct(GTK_SHEET(widget), rows, columns, title); + + return widget; +} + +void +gtk_sheet_construct (GtkSheet *sheet, guint rows, guint columns, const gchar *title) +{ + sheet->row=(GtkSheetRow *)g_malloc(sizeof(GtkSheetRow)); + sheet->column=(GtkSheetColumn *)g_malloc(sizeof(GtkSheetColumn)); + sheet->data=(GtkSheetCell ***)g_malloc(sizeof(GtkSheetCell **)); + + sheet->data[0] = (GtkSheetCell **)g_malloc(sizeof(GtkSheetCell *)+sizeof(gdouble)); + sheet->data[0][0] = NULL; + + sheet->columns_resizable = TRUE; + sheet->rows_resizable = TRUE; + sheet->row_titles_visible = TRUE; + sheet->column_titles_visible = TRUE; + sheet->autoscroll = TRUE; + sheet->justify_entry = TRUE; + sheet->locked = FALSE; + + /* set number of rows and columns */ + GrowSheet(sheet, MINROWS, MINCOLS); + + /* Init row an column zero */ + AddRow(sheet,-1); + AddColumn(sheet,-1); + + /* Add rows and columns */ + AddRow(sheet,rows-1); + AddColumn(sheet,columns-1); + + /* create sheet entry */ + sheet->entry_type = 0; + create_sheet_entry (sheet); + + /* create global selection button */ + create_global_button(sheet); + + if(title) + sheet->name = g_strdup(title); + +} + + +GtkWidget * +gtk_sheet_new_browser(guint rows, guint columns, const gchar *title) +{ + GtkWidget *widget; + + widget = gtk_type_new (gtk_sheet_get_type ()); + + gtk_sheet_construct_browser(GTK_SHEET(widget), rows, columns, title); + + return widget; +} + +void +gtk_sheet_construct_browser(GtkSheet *sheet, guint rows, guint columns, + const gchar *title) +{ + gtk_sheet_construct(sheet, rows, columns, title); + + gtk_sheet_set_locked(sheet, TRUE); + sheet->autoresize = TRUE; +} + +GtkWidget * +gtk_sheet_new_with_custom_entry (guint rows, guint columns, const gchar *title, + GtkType entry_type) +{ + GtkWidget *widget; + + widget = gtk_type_new (gtk_sheet_get_type ()); + + gtk_sheet_construct_with_custom_entry(GTK_SHEET(widget), + rows, columns, title, entry_type); + + return widget; +} + +void +gtk_sheet_construct_with_custom_entry (GtkSheet *sheet, + guint rows, guint columns, + const gchar *title, + GtkType entry_type) +{ + gtk_sheet_construct(sheet, rows, columns, title); + + sheet->entry_type = entry_type; + create_sheet_entry(sheet); +} + + +void +gtk_sheet_change_entry(GtkSheet *sheet, GtkType entry_type) +{ + gint state; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + state = sheet->state; + + if(sheet->state == GTK_SHEET_NORMAL) + gtk_sheet_hide_active_cell(sheet); + + sheet->entry_type = entry_type; + + create_sheet_entry(sheet); + + if(state == GTK_SHEET_NORMAL) + { + gtk_sheet_show_active_cell(sheet); + gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "changed", + (GtkSignalFunc)gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + } + +} + +void +gtk_sheet_show_grid(GtkSheet *sheet, gboolean show) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(show == sheet->show_grid) return; + + sheet->show_grid = show; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, NULL); +} + +gboolean +gtk_sheet_grid_visible(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return sheet->show_grid; +} + +void +gtk_sheet_set_background(GtkSheet *sheet, GdkColor *color) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!color) + gdk_color_white(gdk_colormap_get_system(), &sheet->bg_color); + else + sheet->bg_color = *color; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, NULL); +} + +void +gtk_sheet_set_grid(GtkSheet *sheet, GdkColor *color) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!color) + gdk_color_black(gdk_colormap_get_system(), &sheet->grid_color); + else + sheet->grid_color = *color; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, NULL); +} + +guint +gtk_sheet_get_columns_count(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return sheet->maxcol + 1; +} + +guint +gtk_sheet_get_rows_count(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return sheet->maxrow + 1; +} + +gint +gtk_sheet_get_state(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + return (sheet->state); +} + +void +gtk_sheet_set_selection_mode(GtkSheet *sheet, gint mode) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + sheet->selection_mode = mode; +} + +void +gtk_sheet_set_autoresize (GtkSheet *sheet, gboolean autoresize) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->autoresize = autoresize; +} + +gboolean +gtk_sheet_autoresize (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return sheet->autoresize; +} + +void +gtk_sheet_set_autoscroll (GtkSheet *sheet, gboolean autoscroll) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->autoscroll = autoscroll; +} + +gboolean +gtk_sheet_autoscroll (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return sheet->autoscroll; +} + +void +gtk_sheet_set_clip_text (GtkSheet *sheet, gboolean clip_text) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->clip_text = clip_text; +} + +gboolean +gtk_sheet_clip_text (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return sheet->clip_text; +} + +void +gtk_sheet_set_justify_entry (GtkSheet *sheet, gboolean justify) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->justify_entry = justify; +} + +gboolean +gtk_sheet_justify_entry (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return sheet->justify_entry; +} + +void +gtk_sheet_set_locked (GtkSheet *sheet, gboolean locked) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->locked = locked; +} + +gboolean +gtk_sheet_locked (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return sheet->locked; +} + +/* This routine has problems with gtk+-1.2 related with the + * label/button drawing - I think it's a bug in gtk+-1.2 */ + +void +gtk_sheet_set_title(GtkSheet *sheet, const gchar *title) +{ +/* GtkWidget *old_widget; +*/ GtkWidget *label; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (title != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (sheet->name) + g_free (sheet->name); + + sheet->name = g_strdup (title); + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) || !title) return; + + if(GTK_BIN(sheet->button)->child) + label = GTK_BIN(sheet->button)->child; +/* + gtk_label_set_text(GTK_LABEL(label), title); +*/ + size_allocate_global_button(sheet); + + /* remove and destroy the old widget */ +/* + old_widget = GTK_BIN (sheet->button)->child; + if (old_widget) + { + gtk_container_remove (GTK_CONTAINER (sheet->button), old_widget); + } + + label = gtk_label_new (title); + gtk_misc_set_alignment(GTK_MISC(label), 0.5 , 0.5 ); + + gtk_container_add (GTK_CONTAINER (sheet->button), label); + gtk_widget_show (label); + + size_allocate_global_button(sheet); + + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, -1); + + if(old_widget) + gtk_widget_destroy (old_widget); +*/ +} + +void +gtk_sheet_freeze (GtkSheet *sheet) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->freeze_count++; + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); +} + +void +gtk_sheet_thaw(GtkSheet *sheet) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(sheet->freeze_count == 0) return; + + sheet->freeze_count--; + if(sheet->freeze_count > 0) return; + + adjust_scrollbars(sheet); + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + sheet->old_vadjustment = -1.; + sheet->old_hadjustment = -1.; + + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + + if(sheet->state == GTK_STATE_NORMAL) + if(sheet->sheet_entry && GTK_WIDGET_MAPPED(sheet->sheet_entry)){ + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col); +/* + gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "changed", + (GtkSignalFunc)gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + gtk_sheet_show_active_cell(sheet); +*/ + } + +} + +void +gtk_sheet_set_row_titles_width(GtkSheet *sheet, guint width) +{ + if(width < COLUMN_MIN_WIDTH) return; + + sheet->row_title_area.width = width; + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + adjust_scrollbars(sheet); + + sheet->old_hadjustment = -1.; + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_set_column_titles_height(GtkSheet *sheet, guint height) +{ + if(height < DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet))) return; + + sheet->column_title_area.height = height; + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_show_column_titles(GtkSheet *sheet) +{ + gint col; + + if(sheet->column_titles_visible) return; + + sheet->column_titles_visible = TRUE; + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + gdk_window_show(sheet->column_title_window); + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + + for(col = MIN_VISIBLE_COLUMN(sheet); col <= MAX_VISIBLE_COLUMN(sheet); col++){ + GtkSheetChild *child; + child = sheet->column[col].button.child; + if(child){ + gtk_sheet_child_show(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_vadjustment = -1.; + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_show_row_titles(GtkSheet *sheet) +{ + gint row; + + if(sheet->row_titles_visible) return; + + sheet->row_titles_visible = TRUE; + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + gdk_window_show(sheet->row_title_window); + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + + for(row = MIN_VISIBLE_ROW(sheet); row <= MAX_VISIBLE_ROW(sheet); row++){ + GtkSheetChild *child; + child = sheet->row[row].button.child; + if(child){ + gtk_sheet_child_show(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_hadjustment = -1.; + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + size_allocate_global_button(sheet); +} + +void +gtk_sheet_hide_column_titles(GtkSheet *sheet) +{ + gint col; + + if(!sheet->column_titles_visible) return; + + sheet->column_titles_visible = FALSE; + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + if(sheet->column_title_window) + gdk_window_hide(sheet->column_title_window); + if(GTK_WIDGET_VISIBLE(sheet->button)) + gtk_widget_hide(sheet->button); + + for(col = MIN_VISIBLE_COLUMN(sheet); col <= MAX_VISIBLE_COLUMN(sheet); col++){ + GtkSheetChild *child; + child = sheet->column[col].button.child; + if(child){ + gtk_sheet_child_hide(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_vadjustment = -1.; + if(sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); +} + +void +gtk_sheet_hide_row_titles(GtkSheet *sheet) +{ + gint row; + + if(!sheet->row_titles_visible) return; + + sheet->row_titles_visible = FALSE; + gtk_sheet_recalc_top_ypixels(sheet, 0); + gtk_sheet_recalc_left_xpixels(sheet, 0); + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + if(sheet->row_title_window) + gdk_window_hide(sheet->row_title_window); + if(GTK_WIDGET_VISIBLE(sheet->button)) + gtk_widget_hide(sheet->button); + for(row = MIN_VISIBLE_ROW(sheet); row <= MAX_VISIBLE_ROW(sheet); row++){ + GtkSheetChild *child; + child = sheet->row[row].button.child; + if(child){ + gtk_sheet_child_hide(child); + } + } + adjust_scrollbars(sheet); + } + + sheet->old_hadjustment = -1.; + if(sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); +} + +gboolean +gtk_sheet_column_titles_visible(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + return sheet->column_titles_visible; +} + +gboolean +gtk_sheet_row_titles_visible(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + return sheet->row_titles_visible; +} + +void +gtk_sheet_set_column_title (GtkSheet * sheet, + gint column, + const gchar * title) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (sheet->column[column].name) + g_free (sheet->column[column].name); + + sheet->column[column].name = g_strdup(title); +} + +void +gtk_sheet_set_row_title (GtkSheet * sheet, + gint row, + const gchar * title) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (sheet->row[row].name) + g_free (sheet->row[row].name); + + sheet->row[row].name = g_strdup (title); +} + +const gchar * +gtk_sheet_get_row_title (GtkSheet * sheet, + gint row) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + return(sheet->row[row].name); +} + +const gchar * +gtk_sheet_get_column_title (GtkSheet * sheet, + gint column) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + return(sheet->column[column].name); +} + +void +gtk_sheet_row_button_add_label(GtkSheet *sheet, gint row, const gchar *label) +{ + GtkSheetButton *button; + GtkRequisition req; + gboolean aux; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + button = &sheet->row[row].button; + if (button->label) g_free (button->label); + button->label = g_strdup (label); + + aux = gtk_sheet_autoresize(sheet); + gtk_sheet_set_autoresize(sheet, TRUE); + gtk_sheet_button_size_request(sheet, button, &req); + gtk_sheet_set_autoresize(sheet, aux); + + if(req.height > sheet->row[row].height) + gtk_sheet_set_row_height(sheet, row, req.height); + + if(req.width > sheet->row_title_area.width){ + gtk_sheet_set_row_titles_width(sheet, req.width); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, row, -1); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, -1); + } +} + +const gchar * +gtk_sheet_row_button_get_label(GtkSheet *sheet, gint row) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + if(row < 0 || row > sheet->maxrow) return NULL; + + return (sheet->row[row].button.label); +} + +void +gtk_sheet_row_label_set_visibility(GtkSheet *sheet, gint row, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + sheet->row[row].button.label_visible = visible; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, row, -1); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, -1); + } +} + +void +gtk_sheet_rows_labels_set_visibility(GtkSheet *sheet, gboolean visible) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i = 0; i <= sheet->maxrow; i++) + gtk_sheet_row_label_set_visibility(sheet, i, visible); +} + + +void +gtk_sheet_column_button_add_label(GtkSheet *sheet, gint column, const gchar *label) +{ + GtkSheetButton *button; + GtkRequisition req; + gboolean aux; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column >sheet->maxcol) return; + + button = &sheet->column[column].button; + if (button->label) g_free (button->label); + button->label = g_strdup (label); + + aux = gtk_sheet_autoresize(sheet); + gtk_sheet_set_autoresize(sheet, TRUE); + gtk_sheet_button_size_request(sheet, button, &req); + gtk_sheet_set_autoresize(sheet, aux); + + if(req.width > sheet->column[column].width) + gtk_sheet_set_column_width(sheet, column, req.width); + + if(req.height > sheet->column_title_area.height) + gtk_sheet_set_column_titles_height(sheet, req.height); + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, -1, column); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, column); + } +} + +const gchar * +gtk_sheet_column_button_get_label(GtkSheet *sheet, gint column) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + if(column < 0 || column >sheet->maxcol) return NULL; + + return(sheet->column[column].button.label); +} + +void +gtk_sheet_column_label_set_visibility(GtkSheet *sheet, gint col, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(col < 0 || col > sheet->maxcol) return; + + sheet->column[col].button.label_visible = visible; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, -1, col); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, col); + } +} + +void +gtk_sheet_columns_labels_set_visibility(GtkSheet *sheet, gboolean visible) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i = 0; i <= sheet->maxcol; i++) + gtk_sheet_column_label_set_visibility(sheet, i, visible); +} + +void +gtk_sheet_row_button_justify(GtkSheet *sheet, gint row, + GtkJustification justification) +{ + GtkSheetButton *button; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + button = &sheet->row[row].button; + button->justification = justification; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, row, -1); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, -1); + } +} + +void +gtk_sheet_column_button_justify(GtkSheet *sheet, gint column, + GtkJustification justification) +{ + GtkSheetButton *button; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column > sheet->maxcol) return; + + button = &sheet->column[column].button; + button->justification = justification; + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_button_draw(sheet, -1, column); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], -1, column); + } +} + + +void +gtk_sheet_moveto (GtkSheet * sheet, + gint row, + gint column, + gfloat row_align, + gfloat col_align) +{ + gint x, y; + guint width, height; + gint adjust; + gint min_row, min_col; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + g_return_if_fail (sheet->hadjustment != NULL); + g_return_if_fail (sheet->vadjustment != NULL); + + if (row < 0 || row > sheet->maxrow) + return; + if (column < 0 || column > sheet->maxcol) + return; + + height = sheet->sheet_window_height; + width = sheet->sheet_window_width; + + /* adjust vertical scrollbar */ + + if (row >= 0 && row_align >=0.) + { + y = ROW_TOP_YPIXEL(sheet, row) - sheet->voffset - + row_align*height- + (1.-row_align)*sheet->row[row].height; + + /* This forces the sheet to scroll when you don't see the entire cell */ + min_row = row; + adjust = 0; + if(row_align == 1.){ + while(min_row >= 0 && min_row > MIN_VISIBLE_ROW(sheet)){ + if(sheet->row[min_row].is_visible) + adjust += sheet->row[min_row].height; + if(adjust >= height){ + break; + } + min_row--; + } + min_row = MAX(min_row, 0); + y = ROW_TOP_YPIXEL(sheet, min_row) - sheet->voffset + + sheet->row[min_row].height - 1; + } + + if (y < 0) + sheet->vadjustment->value = 0.0; + else + sheet->vadjustment->value = y; + + sheet->old_vadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + + } + + /* adjust horizontal scrollbar */ + if (column >= 0 && col_align >= 0.) + { + x = COLUMN_LEFT_XPIXEL (sheet, column) - sheet->hoffset - + col_align*width - + (1.-col_align)*sheet->column[column].width; + + /* This forces the sheet to scroll when you don't see the entire cell */ + min_col = column; + adjust = 0; + if(col_align == 1.){ + while(min_col >= 0 && min_col > MIN_VISIBLE_COLUMN(sheet)){ + if(sheet->column[min_col].is_visible) + adjust += sheet->column[min_col].width; + if(adjust >= width){ + break; + } + min_col--; + } + min_col = MAX(min_col, 0); + x = COLUMN_LEFT_XPIXEL(sheet, min_col) - sheet->hoffset + + sheet->column[min_col].width - 1; + } + + if (x < 0) + sheet->hadjustment->value = 0.0; + else + sheet->hadjustment->value = x; + + sheet->old_vadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + + } +} + +void +gtk_sheet_column_set_sensitivity(GtkSheet *sheet, gint column, gboolean sensitive) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column > sheet->maxcol) return; + + sheet->column[column].is_sensitive=sensitive; + if(!sensitive) + sheet->column[column].button.state=GTK_STATE_INSENSITIVE; + else + sheet->column[column].button.state=GTK_STATE_NORMAL; + + if(GTK_WIDGET_REALIZED(sheet) && !GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_button_draw(sheet, -1, column); +} + + +void +gtk_sheet_columns_set_sensitivity(GtkSheet *sheet, gboolean sensitive) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i=0; i<=sheet->maxcol; i++) + gtk_sheet_column_set_sensitivity(sheet, i, sensitive); +} + +void +gtk_sheet_columns_set_resizable (GtkSheet *sheet, gboolean resizable) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->columns_resizable = resizable; +} + +gboolean +gtk_sheet_columns_resizable (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return sheet->columns_resizable; +} + +void +gtk_sheet_row_set_sensitivity(GtkSheet *sheet, gint row, gboolean sensitive) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + + sheet->row[row].is_sensitive=sensitive; + if(!sensitive) + sheet->row[row].button.state=GTK_STATE_INSENSITIVE; + else + sheet->row[row].button.state=GTK_STATE_NORMAL; + + if(GTK_WIDGET_REALIZED(sheet) && !GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_button_draw(sheet, row, -1); +} + +void +gtk_sheet_rows_set_sensitivity(GtkSheet *sheet, gboolean sensitive) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + for(i=0; i<=sheet->maxrow; i++) + gtk_sheet_row_set_sensitivity(sheet, i, sensitive); +} + + +void +gtk_sheet_rows_set_resizable (GtkSheet *sheet, gboolean resizable) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + sheet->rows_resizable = resizable; +} + +gboolean +gtk_sheet_rows_resizable (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return sheet->rows_resizable; +} + +void +gtk_sheet_column_set_visibility(GtkSheet *sheet, gint column, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(column < 0 || column > sheet->maxcol) return; + if(sheet->column[column].is_visible == visible) return; + + sheet->column[column].is_visible = visible; + + gtk_sheet_recalc_left_xpixels(sheet, column); + + if(!GTK_SHEET_IS_FROZEN(sheet) && + gtk_sheet_cell_isvisible(sheet, MIN_VISIBLE_ROW(sheet), column)){ + gtk_sheet_range_draw(sheet, NULL); + size_allocate_column_title_buttons(sheet); + } +} + +void +gtk_sheet_row_set_visibility(GtkSheet *sheet, gint row, gboolean visible) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(row < 0 || row > sheet->maxrow) return; + if(sheet->row[row].is_visible == visible) return; + + sheet->row[row].is_visible = visible; + + gtk_sheet_recalc_top_ypixels(sheet, row); + + if(!GTK_SHEET_IS_FROZEN(sheet) && + gtk_sheet_cell_isvisible(sheet, row, MIN_VISIBLE_COLUMN(sheet))){ + gtk_sheet_range_draw(sheet, NULL); + size_allocate_row_title_buttons(sheet); + } +} + +void +gtk_sheet_select_row (GtkSheet * sheet, + gint row) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (row < 0 || row > sheet->maxrow) + return; + + if(sheet->state != GTK_SHEET_NORMAL) + gtk_sheet_real_unselect_range(sheet, NULL); + else + { + gboolean veto = TRUE; + veto = gtk_sheet_deactivate_cell(sheet); + if(!veto) return; + } + + sheet->state=GTK_SHEET_ROW_SELECTED; + sheet->range.row0=row; + sheet->range.col0=0; + sheet->range.rowi=row; + sheet->range.coli=sheet->maxcol; + sheet->active_cell.row=row; + sheet->active_cell.col=0; + + gtk_signal_emit (GTK_OBJECT (sheet), sheet_signals[SELECT_ROW], row); + gtk_sheet_real_select_range(sheet, NULL); + +} + + +void +gtk_sheet_select_column (GtkSheet * sheet, + gint column) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (column < 0 || column > sheet->maxcol) + return; + + if(sheet->state != GTK_SHEET_NORMAL) + gtk_sheet_real_unselect_range(sheet, NULL); + else + { + gboolean veto = TRUE; + veto = gtk_sheet_deactivate_cell(sheet); + if(!veto) return; + } + + sheet->state=GTK_SHEET_COLUMN_SELECTED; + sheet->range.row0=0; + sheet->range.col0=column; + sheet->range.rowi=sheet->maxrow; + sheet->range.coli=column; + sheet->active_cell.row=0; + sheet->active_cell.col=column; + + gtk_signal_emit (GTK_OBJECT (sheet), sheet_signals[SELECT_COLUMN], column); + gtk_sheet_real_select_range(sheet, NULL); + +} + +void +gtk_sheet_clip_range (GtkSheet *sheet, const GtkSheetRange *range) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_SHEET_IN_CLIP(sheet)) return; + + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_CLIP); + + if(range == NULL) + sheet->clip_range = sheet->range; + else + sheet->clip_range=*range; + + sheet->interval=0; + sheet->clip_timer=gtk_timeout_add(TIMEOUT_FLASH, gtk_sheet_flash, sheet); + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[CLIP_RANGE], + &sheet->clip_range); + +} + +void +gtk_sheet_unclip_range(GtkSheet *sheet) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!GTK_SHEET_IN_CLIP(sheet)) return; + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_CLIP); + gtk_timeout_remove(sheet->clip_timer); + gtk_sheet_range_draw(sheet, &sheet->clip_range); + + if(gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_range_draw(sheet, &sheet->range); +} + +gboolean +gtk_sheet_in_clip (GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + return GTK_SHEET_IN_CLIP(sheet); +} + +static gint +gtk_sheet_flash(gpointer data) +{ + GtkSheet *sheet; + gint x,y,width,height; + GdkRectangle clip_area; + + sheet=GTK_SHEET(data); + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return TRUE; + if(!GTK_WIDGET_DRAWABLE(GTK_WIDGET(sheet))) return TRUE; + if(!gtk_sheet_range_isvisible(sheet, sheet->clip_range)) return TRUE; + if(GTK_SHEET_IN_XDRAG(sheet)) return TRUE; + if(GTK_SHEET_IN_YDRAG(sheet)) return TRUE; + + GDK_THREADS_ENTER(); + + x=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.col0)+1; + y=ROW_TOP_YPIXEL(sheet,sheet->clip_range.row0)+1; + width=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.coli)-x+ + sheet->column[sheet->clip_range.coli].width-1; + height=ROW_TOP_YPIXEL(sheet,sheet->clip_range.rowi)-y+ + sheet->row[sheet->clip_range.rowi].height-1; + + clip_area.x=COLUMN_LEFT_XPIXEL(sheet, MIN_VISIBLE_COLUMN(sheet)); + clip_area.y=ROW_TOP_YPIXEL(sheet, MIN_VISIBLE_ROW(sheet)); + clip_area.width=sheet->sheet_window_width; + clip_area.height=sheet->sheet_window_height; + + if(x<0) { + width=width+x+1; + x=-1; + } + if(width>clip_area.width) width=clip_area.width+10; + if(y<0) { + height=height+y+1; + y=-1; + } + if(height>clip_area.height) height=clip_area.height+10; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, y, + x, y, + 1, height); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, y, + x, y, + width, 1); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, y+height, + x, y+height, + width, 1); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x+width, y, + x+width, y, + 1, height); + + + sheet->interval=sheet->interval+1; + if(sheet->interval==TIME_INTERVAL) sheet->interval=0; + + gdk_gc_set_dashes(sheet->xor_gc, sheet->interval, (gint8*)"\4\4", 2); + gtk_sheet_draw_flashing_range(sheet,sheet->clip_range); + gdk_gc_set_dashes(sheet->xor_gc, 0, (gint8*)"\4\4", 2); + + GDK_THREADS_LEAVE(); + + return TRUE; + +} + +static void +gtk_sheet_draw_flashing_range(GtkSheet *sheet, GtkSheetRange range) +{ + GdkRectangle clip_area; + gint x,y,width,height; + + if(!gtk_sheet_range_isvisible(sheet, sheet->clip_range)) return; + + clip_area.x=COLUMN_LEFT_XPIXEL(sheet, MIN_VISIBLE_COLUMN(sheet)); + clip_area.y=ROW_TOP_YPIXEL(sheet, MIN_VISIBLE_ROW(sheet)); + clip_area.width=sheet->sheet_window_width; + clip_area.height=sheet->sheet_window_height; + + gdk_gc_set_clip_rectangle(sheet->xor_gc, &clip_area); + + x=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.col0)+1; + y=ROW_TOP_YPIXEL(sheet,sheet->clip_range.row0)+1; + width=COLUMN_LEFT_XPIXEL(sheet,sheet->clip_range.coli)-x+ + sheet->column[sheet->clip_range.coli].width-1; + height=ROW_TOP_YPIXEL(sheet,sheet->clip_range.rowi)-y+ + sheet->row[sheet->clip_range.rowi].height-1; + + if(x<0) { + width=width+x+1; + x=-1; + } + if(width>clip_area.width) width=clip_area.width+10; + if(y<0) { + height=height+y+1; + y=-1; + } + if(height>clip_area.height) height=clip_area.height+10; + + gdk_gc_set_line_attributes(sheet->xor_gc, 1, 1, 0 ,0 ); + + gdk_draw_rectangle(sheet->sheet_window, sheet->xor_gc, FALSE, + x, y, + width, height); + + gdk_gc_set_line_attributes (sheet->xor_gc, 1, 0, 0, 0); + + gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL); + +} + +static gint +gtk_sheet_range_isvisible (GtkSheet * sheet, + GtkSheetRange range) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + + if (range.row0 < 0 || range.row0 > sheet->maxrow) + return FALSE; + + if (range.rowi < 0 || range.rowi > sheet->maxrow) + return FALSE; + + if (range.col0 < 0 || range.col0 > sheet->maxcol) + return FALSE; + + if (range.coli < 0 || range.coli > sheet->maxcol) + return FALSE; + + if (range.rowi < MIN_VISIBLE_ROW (sheet)) + return FALSE; + + if (range.row0 > MAX_VISIBLE_ROW (sheet)) + return FALSE; + + if (range.coli < MIN_VISIBLE_COLUMN (sheet)) + return FALSE; + + if (range.col0 > MAX_VISIBLE_COLUMN (sheet)) + return FALSE; + + return TRUE; +} + +static gint +gtk_sheet_cell_isvisible (GtkSheet * sheet, + gint row, gint column) +{ + GtkSheetRange range; + + range.row0 = row; + range.col0 = column; + range.rowi = row; + range.coli = column; + + return gtk_sheet_range_isvisible(sheet, range); +} + +void +gtk_sheet_get_visible_range(GtkSheet *sheet, GtkSheetRange *range) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)) ; + g_return_if_fail (range != NULL); + + range->row0 = MIN_VISIBLE_ROW(sheet); + range->col0 = MIN_VISIBLE_COLUMN(sheet); + range->rowi = MAX_VISIBLE_ROW(sheet); + range->coli = MAX_VISIBLE_COLUMN(sheet); + +} + +GtkAdjustment * +gtk_sheet_get_vadjustment (GtkSheet * sheet) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + return sheet->vadjustment; +} + +GtkAdjustment * +gtk_sheet_get_hadjustment (GtkSheet * sheet) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + return sheet->hadjustment; +} + +void +gtk_sheet_set_vadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment) +{ + GtkAdjustment *old_adjustment; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (adjustment) + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + + if (sheet->vadjustment == adjustment) + return; + + old_adjustment = sheet->vadjustment; + + if (sheet->vadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->vadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->vadjustment)); + } + + sheet->vadjustment = adjustment; + + if (sheet->vadjustment) + { + gtk_object_ref (GTK_OBJECT (sheet->vadjustment)); + gtk_object_sink (GTK_OBJECT (sheet->vadjustment)); + + gtk_signal_connect (GTK_OBJECT (sheet->vadjustment), "changed", + (GtkSignalFunc) vadjustment_changed, + (gpointer) sheet); + gtk_signal_connect (GTK_OBJECT (sheet->vadjustment), "value_changed", + (GtkSignalFunc) vadjustment_value_changed, + (gpointer) sheet); + } + + if (!sheet->vadjustment || !old_adjustment) + { + gtk_widget_queue_resize (GTK_WIDGET (sheet)); + return; + } + + sheet->old_vadjustment = sheet->vadjustment->value; +} + +void +gtk_sheet_set_hadjustment (GtkSheet *sheet, + GtkAdjustment *adjustment) +{ + GtkAdjustment *old_adjustment; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (adjustment) + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + + if (sheet->hadjustment == adjustment) + return; + + old_adjustment = sheet->hadjustment; + + if (sheet->hadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->hadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->hadjustment)); + } + + sheet->hadjustment = adjustment; + + if (sheet->hadjustment) + { + gtk_object_ref (GTK_OBJECT (sheet->hadjustment)); + gtk_object_sink (GTK_OBJECT (sheet->hadjustment)); + + gtk_signal_connect (GTK_OBJECT (sheet->hadjustment), "changed", + (GtkSignalFunc) hadjustment_changed, + (gpointer) sheet); + gtk_signal_connect (GTK_OBJECT (sheet->hadjustment), "value_changed", + (GtkSignalFunc) hadjustment_value_changed, + (gpointer) sheet); + } + + if (!sheet->hadjustment || !old_adjustment) + { + gtk_widget_queue_resize (GTK_WIDGET (sheet)); + return; + } + + sheet->old_hadjustment = sheet->hadjustment->value; +} + +static void +gtk_sheet_set_scroll_adjustments (GtkSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + if(sheet->hadjustment != hadjustment) + gtk_sheet_set_hadjustment (sheet, hadjustment); + if(sheet->vadjustment != vadjustment) + gtk_sheet_set_vadjustment (sheet, vadjustment); +} + +static void +gtk_sheet_finalize (GObject * object) +{ + GtkSheet *sheet; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_SHEET (object)); + + sheet = GTK_SHEET (object); + + /* get rid of all the cells */ + gtk_sheet_range_clear (sheet, NULL); + gtk_sheet_range_delete(sheet, NULL); + + gtk_sheet_delete_rows (sheet, 0, sheet->maxrow + 1); + gtk_sheet_delete_columns (sheet, 0, sheet->maxcol + 1); + + DeleteRow (sheet, 0, sheet->maxrow + 1); + DeleteColumn (sheet, 0, sheet->maxcol + 1); + + g_free(sheet->row); + sheet->row = NULL; + g_free(sheet->column); + sheet->column = NULL; + g_free(sheet->data); + sheet->data = NULL; + + if(sheet->name){ + g_free(sheet->name); + sheet->name = NULL; + } + + if (G_OBJECT_CLASS (parent_class)->finalize) + (*G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +gtk_sheet_destroy (GtkObject * object) +{ + GtkSheet *sheet; + GList *children; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_SHEET (object)); + + sheet = GTK_SHEET (object); + + /* destroy the entry */ + if(sheet->sheet_entry && GTK_IS_WIDGET(sheet->sheet_entry)){ + gtk_widget_destroy (sheet->sheet_entry); + sheet->sheet_entry = NULL; + } + + /* destroy the global selection button */ + if(sheet->button && GTK_IS_WIDGET(sheet->button)){ + gtk_widget_destroy (sheet->button); + sheet->button = NULL; + } + + if(sheet->timer){ + gtk_timeout_remove(sheet->timer); + sheet->timer = 0; + } + + if(sheet->clip_timer){ + gtk_timeout_remove(sheet->clip_timer); + sheet->clip_timer = 0; + } + + /* unref adjustments */ + if (sheet->hadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->hadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->hadjustment)); + sheet->hadjustment = NULL; + } + if (sheet->vadjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (sheet->vadjustment), sheet); + gtk_object_unref (GTK_OBJECT (sheet->vadjustment)); + sheet->vadjustment = NULL; + } + + children = sheet->children; + while(children){ + GtkSheetChild *child = (GtkSheetChild *)children->data; + if(child && child->widget) + gtk_sheet_remove(GTK_CONTAINER(sheet), child->widget); + children = sheet->children; + } + sheet->children = NULL; + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (*GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +static void +gtk_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + GtkSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + if (GTK_WIDGET_CLASS (parent_class)->style_set) + (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style); + + sheet = GTK_SHEET (widget); + + if(GTK_WIDGET_REALIZED(widget)) + { + gtk_style_set_background (widget->style, widget->window, widget->state); + } + +} + +static void +gtk_sheet_realize (GtkWidget * widget) +{ + GtkSheet *sheet; + GdkWindowAttr attributes; + gint attributes_mask; + GdkGCValues values, auxvalues; + GdkColormap *colormap; + gchar *name; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | + GDK_WA_CURSOR; + + attributes.cursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW); + + /* main window */ + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); + + gdk_window_set_user_data (widget->window, sheet); + + widget->style = gtk_style_attach (widget->style, widget->window); + + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + + attributes.x = 0; + if(sheet->row_titles_visible) + attributes.x = sheet->row_title_area.width; + attributes.y = 0; + attributes.width = sheet->column_title_area.width; + attributes.height = sheet->column_title_area.height; + + /* column-title window */ + sheet->column_title_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (sheet->column_title_window, sheet); + gtk_style_set_background (widget->style, sheet->column_title_window, GTK_STATE_NORMAL); + + attributes.x = 0; + attributes.y = 0; + if(sheet->column_titles_visible) + attributes.y = sheet->column_title_area.height; + attributes.width = sheet->row_title_area.width; + attributes.height = sheet->row_title_area.height; + + /* row-title window */ + sheet->row_title_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (sheet->row_title_window, sheet); + gtk_style_set_background (widget->style, sheet->row_title_window, GTK_STATE_NORMAL); + + /* sheet-window */ + attributes.cursor = gdk_cursor_new(GDK_PLUS); + + attributes.x = 0; + attributes.y = 0; + attributes.width = sheet->sheet_window_width, + attributes.height = sheet->sheet_window_height; + + sheet->sheet_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (sheet->sheet_window, sheet); + + gdk_window_set_background (sheet->sheet_window, &widget->style->white); + gdk_window_show (sheet->sheet_window); + + /* backing_pixmap */ + gtk_sheet_make_backing_pixmap(sheet, 0, 0); + + /* GCs */ + if(sheet->fg_gc) + gdk_gc_unref(sheet->fg_gc); + if(sheet->bg_gc) + gdk_gc_unref(sheet->bg_gc); + sheet->fg_gc = gdk_gc_new (widget->window); + sheet->bg_gc = gdk_gc_new (widget->window); + + colormap = gtk_widget_get_colormap(widget); + + gdk_color_white(colormap, &widget->style->white); + gdk_color_black(colormap, &widget->style->black); + + gdk_gc_get_values(sheet->fg_gc, &auxvalues); + + values.foreground = widget->style->white; + values.function = GDK_INVERT; + values.subwindow_mode = GDK_INCLUDE_INFERIORS; + if(sheet->xor_gc) + gdk_gc_unref(sheet->xor_gc); + sheet->xor_gc = gdk_gc_new_with_values (widget->window, + &values, + GDK_GC_FOREGROUND | + GDK_GC_FUNCTION | + GDK_GC_SUBWINDOW); + + if(sheet->sheet_entry->parent){ + gtk_widget_ref(sheet->sheet_entry); + gtk_widget_unparent(sheet->sheet_entry); + } + gtk_widget_set_parent_window (sheet->sheet_entry, sheet->sheet_window); + gtk_widget_set_parent(sheet->sheet_entry, GTK_WIDGET(sheet)); + + if(sheet->button && sheet->button->parent){ + gtk_widget_ref(sheet->button); + gtk_widget_unparent(sheet->button); + } + gtk_widget_set_parent_window(sheet->button, sheet->sheet_window); + gtk_widget_set_parent(sheet->button, GTK_WIDGET(sheet)); + +/* + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col); +*/ + if(!sheet->cursor_drag) + sheet->cursor_drag = gdk_cursor_new(GDK_PLUS); + + if(sheet->column_titles_visible) + gdk_window_show(sheet->column_title_window); + if(sheet->row_titles_visible) + gdk_window_show(sheet->row_title_window); + + size_allocate_row_title_buttons(sheet); + size_allocate_column_title_buttons(sheet); + + name = g_strdup(sheet->name); + gtk_sheet_set_title(sheet, name); + + g_free(name); + + children = sheet->children; + while(children) + { + child = children->data; + children = children->next; + + gtk_sheet_realize_child(sheet, child); + } +} + +static void +create_global_button(GtkSheet *sheet) +{ + sheet->button = gtk_button_new_with_label(" "); + + gtk_signal_connect (GTK_OBJECT (sheet->button), + "pressed", + (GtkSignalFunc) global_button_clicked, + (gpointer) sheet); +} + +static void +size_allocate_global_button(GtkSheet *sheet) +{ + GtkAllocation allocation; + + if(!sheet->column_titles_visible) return; + if(!sheet->row_titles_visible) return; + + gtk_widget_size_request(sheet->button, NULL); + + allocation.x=0; + allocation.y=0; + allocation.width=sheet->row_title_area.width; + allocation.height=sheet->column_title_area.height; + + gtk_widget_size_allocate(sheet->button, &allocation); + gtk_widget_show(sheet->button); +} + +static void +global_button_clicked(GtkWidget *widget, gpointer data) +{ + gboolean veto; + + gtk_sheet_click_cell(GTK_SHEET(data), -1, -1, &veto); + gtk_widget_grab_focus(GTK_WIDGET(data)); +} + + +static void +gtk_sheet_unrealize (GtkWidget * widget) +{ + GtkSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + + gdk_cursor_destroy (sheet->cursor_drag); + + gdk_gc_destroy (sheet->xor_gc); + gdk_gc_destroy (sheet->fg_gc); + gdk_gc_destroy (sheet->bg_gc); + + gdk_window_destroy (sheet->sheet_window); + gdk_window_destroy (sheet->column_title_window); + gdk_window_destroy (sheet->row_title_window); + + if (sheet->pixmap){ + g_free (sheet->pixmap); + sheet->pixmap = NULL; + } + + sheet->column_title_window=NULL; + sheet->sheet_window = NULL; + sheet->cursor_drag = NULL; + sheet->xor_gc = NULL; + sheet->fg_gc = NULL; + sheet->bg_gc = NULL; + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +gtk_sheet_map (GtkWidget * widget) +{ + GtkSheet *sheet; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + + if (!GTK_WIDGET_MAPPED (widget)) + { + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + + if(!sheet->cursor_drag) sheet->cursor_drag=gdk_cursor_new(GDK_PLUS); + + gdk_window_show (widget->window); + + gdk_window_show (sheet->sheet_window); + + if(sheet->column_titles_visible){ + gdk_window_show (sheet->column_title_window); + } + if(sheet->row_titles_visible){ + gdk_window_show (sheet->row_title_window); + } + + if(!GTK_WIDGET_MAPPED (sheet->sheet_entry)){ + gtk_widget_show (sheet->sheet_entry); + gtk_widget_map (sheet->sheet_entry); + } + + if (GTK_WIDGET_VISIBLE (sheet->button) && + !GTK_WIDGET_MAPPED (sheet->button)){ + gtk_widget_show(sheet->button); + gtk_widget_map (sheet->button); + } + + if(GTK_BIN(sheet->button)->child) + if (GTK_WIDGET_VISIBLE (GTK_BIN(sheet->button)->child) && + !GTK_WIDGET_MAPPED (GTK_BIN(sheet->button)->child)) + gtk_widget_map (GTK_BIN(sheet->button)->child); + + gtk_sheet_range_draw(sheet, NULL); + gtk_sheet_activate_cell(sheet, + sheet->active_cell.row, + sheet->active_cell.col); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget) && + !GTK_WIDGET_MAPPED (child->widget)){ + gtk_widget_map (child->widget); + gtk_sheet_position_child(sheet, child); + } + } + + } +} + +static void +gtk_sheet_unmap (GtkWidget * widget) +{ + GtkSheet *sheet; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + + sheet = GTK_SHEET (widget); + + if (GTK_WIDGET_MAPPED (widget)) + { + GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); + + gdk_window_hide (sheet->sheet_window); + if(sheet->column_titles_visible) + gdk_window_hide (sheet->column_title_window); + if(sheet->row_titles_visible) + gdk_window_hide (sheet->row_title_window); + gdk_window_hide (widget->window); + + if (GTK_WIDGET_MAPPED (sheet->sheet_entry)) + gtk_widget_unmap (sheet->sheet_entry); + + if (GTK_WIDGET_MAPPED (sheet->button)) + gtk_widget_unmap (sheet->button); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget) && + GTK_WIDGET_MAPPED (child->widget)) + { + gtk_widget_unmap (child->widget); + } + } + + } +} + + +static void +gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col) +{ + GtkWidget *widget; + GdkGC *fg_gc, *bg_gc; + GtkSheetCellAttr attributes; + GdkRectangle area; + + g_return_if_fail (sheet != NULL); + + /* bail now if we arn't drawable yet */ + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + if (row < 0 || row > sheet->maxrow) return; + if (col < 0 || col > sheet->maxcol) return; + if (!sheet->column[col].is_visible) return; + if (!sheet->row[row].is_visible) return; + + widget = GTK_WIDGET (sheet); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + /* select GC for background rectangle */ + gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground); + gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); + + fg_gc = sheet->fg_gc; + bg_gc = sheet->bg_gc; + + area.x=COLUMN_LEFT_XPIXEL(sheet,col); + area.y=ROW_TOP_YPIXEL(sheet,row); + area.width=sheet->column[col].width; + area.height=sheet->row[row].height; + + gdk_draw_rectangle (sheet->pixmap, + bg_gc, + TRUE, + area.x, + area.y, + area.width, + area.height); + + gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0); + + if(sheet->show_grid){ + gdk_gc_set_foreground (sheet->bg_gc, &sheet->grid_color); + + gdk_draw_rectangle (sheet->pixmap, + sheet->bg_gc, + FALSE, + area.x, area.y, + area.width, area.height); + } +} + +static void +gtk_sheet_cell_draw_border (GtkSheet *sheet, gint row, gint col, gint mask) +{ + GtkWidget *widget; + GdkGC *fg_gc, *bg_gc; + GtkSheetCellAttr attributes; + GdkRectangle area; + guint width; + + g_return_if_fail (sheet != NULL); + + /* bail now if we arn't drawable yet */ + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + if (row < 0 || row > sheet->maxrow) return; + if (col < 0 || col > sheet->maxcol) return; + if (!sheet->column[col].is_visible) return; + if (!sheet->row[row].is_visible) return; + + widget = GTK_WIDGET (sheet); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + /* select GC for background rectangle */ + gdk_gc_set_foreground (sheet->fg_gc, &attributes.border.color); + gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); + + fg_gc = sheet->fg_gc; + bg_gc = sheet->bg_gc; + + area.x=COLUMN_LEFT_XPIXEL(sheet,col); + area.y=ROW_TOP_YPIXEL(sheet,row); + area.width=sheet->column[col].width; + area.height=sheet->row[row].height; + + width = attributes.border.width; + gdk_gc_set_line_attributes(sheet->fg_gc, attributes.border.width, + attributes.border.line_style, + attributes.border.cap_style, + attributes.border.join_style); + if(width>0){ + + if(attributes.border.mask & GTK_SHEET_LEFT_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x, area.y-width/2, + area.x, area.y+area.height+width/2+1); + + if(attributes.border.mask & GTK_SHEET_RIGHT_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x+area.width, area.y-width/2, + area.x+area.width, + area.y+area.height+width/2+1); + + if(attributes.border.mask & GTK_SHEET_TOP_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x-width/2,area.y, + area.x+area.width+width/2+1, + area.y); + + if(attributes.border.mask & GTK_SHEET_BOTTOM_BORDER & mask) + gdk_draw_line(sheet->pixmap, sheet->fg_gc, + area.x-width/2, area.y+area.height, + area.x+area.width+width/2+1, + area.y+area.height); + } + +} + + +static void +gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col) +{ + GtkWidget *widget; + GdkRectangle area, clip_area; + gint i; + gint text_width, text_height, y; + gint xoffset=0; + gint size, sizel, sizer; + GdkGC *fg_gc, *bg_gc; + GtkSheetCellAttr attributes; + PangoLayout *layout; + PangoRectangle rect; + PangoRectangle logical_rect; + PangoLayoutLine *line; + PangoFontMetrics *metrics; + PangoContext *context = gtk_widget_get_pango_context(GTK_WIDGET(sheet)); + gint ascent, descent, y_pos; + + char *label; + + g_return_if_fail (sheet != NULL); + + /* bail now if we aren't drawable yet */ + if (!GTK_WIDGET_DRAWABLE (sheet)) + return; + + if (row > sheet->maxallocrow) return; + if (col > sheet->maxalloccol) return; + if (!sheet->data[row]) return; + if (!sheet->data[row][col]) return; + if (!sheet->data[row][col]->text || strlen(sheet->data[row][col]->text)==0) + return; + + if (row < 0 || row > sheet->maxrow) return; + if (col < 0 || col > sheet->maxcol) return; + if (!sheet->column[col].is_visible) return; + if (!sheet->row[row].is_visible) return; + + + widget = GTK_WIDGET(sheet); + + label = sheet->data[row][col]->text; + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + /* select GC for background rectangle */ + gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground); + gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); + + fg_gc = sheet->fg_gc; + bg_gc = sheet->bg_gc; + + area.x=COLUMN_LEFT_XPIXEL(sheet,col); + area.y=ROW_TOP_YPIXEL(sheet,row); + area.width=sheet->column[col].width; + area.height=sheet->row[row].height; + + clip_area = area; + + layout = gtk_widget_create_pango_layout (GTK_WIDGET(sheet), label); + pango_layout_set_font_description (layout, attributes.font_desc); + + pango_layout_get_pixel_extents (layout, NULL, &rect); + + line = pango_layout_get_lines (layout)->data; + pango_layout_line_get_extents (line, NULL, &logical_rect); + + metrics = pango_context_get_metrics(context, + attributes.font_desc, + pango_context_get_language(context)); + + ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; + descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE; + + pango_font_metrics_unref(metrics); + + /* Align primarily for locale's ascent/descent */ + + logical_rect.height /= PANGO_SCALE; + logical_rect.y /= PANGO_SCALE; + y_pos = area.height - logical_rect.height; + + if (logical_rect.height > area.height) + y_pos = (logical_rect.height - area.height - 2*CELLOFFSET) / 2; + else if (y_pos < 0) + y_pos = 0; + else if (y_pos + logical_rect.height > area.height) + y_pos = area.height - logical_rect.height; + + text_width = rect.width; + text_height = rect.height; + y = area.y + y_pos - CELLOFFSET; + + switch(attributes.justification){ + case GTK_JUSTIFY_RIGHT: + size=area.width; + area.x+=area.width; + if(!gtk_sheet_clip_text(sheet)){ + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(size>=text_width+CELLOFFSET) break; + size+=sheet->column[i].width; + sheet->column[i].right_text_column = MAX(col, sheet->column[i].right_text_column); + } + area.width=size; + } + area.x-=size; + xoffset+=area.width-text_width - 2 * CELLOFFSET - + attributes.border.width/2; + break; + case GTK_JUSTIFY_CENTER: + sizel=area.width/2; + sizer=area.width/2; + area.x+=area.width/2; + if(!gtk_sheet_clip_text(sheet)){ + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(sizer>=text_width/2) break; + sizer+=sheet->column[i].width; + sheet->column[i].left_text_column = MIN(col, sheet->column[i].left_text_column); + } + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(sizel>=text_width/2) break; + sizel+=sheet->column[i].width; + sheet->column[i].right_text_column = MAX(col, sheet->column[i].right_text_column); + } + size=MIN(sizel, sizer); + } + area.x-=sizel; + xoffset+= sizel - text_width/2 - CELLOFFSET; + area.width=sizel+sizer; + break; + case GTK_JUSTIFY_LEFT: + default: + size=area.width; + if(!gtk_sheet_clip_text(sheet)){ + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + if(size>=text_width+CELLOFFSET) break; + size+=sheet->column[i].width; + sheet->column[i].left_text_column = MIN(col, sheet->column[i].left_text_column); + } + area.width=size; + } + xoffset += attributes.border.width/2; + break; + } + + if(!gtk_sheet_clip_text(sheet)) clip_area = area; + gdk_gc_set_clip_rectangle(fg_gc, &clip_area); + + + gdk_draw_layout (sheet->pixmap, fg_gc, + area.x + xoffset + CELLOFFSET, + y, + layout); + + gdk_gc_set_clip_rectangle(fg_gc, NULL); + g_object_unref(G_OBJECT(layout)); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + area.x, + area.y, + area.x, + area.y, + area.width, + area.height); + +} + + + +static void +gtk_sheet_range_draw(GtkSheet *sheet, const GtkSheetRange *range) +{ + gint i,j; + GtkSheetRange drawing_range; + GdkRectangle area; + + g_return_if_fail(sheet != NULL); + g_return_if_fail(GTK_SHEET(sheet)); + + if(!GTK_WIDGET_DRAWABLE(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_MAPPED(GTK_WIDGET(sheet))) return; + + if(range == NULL) + { + drawing_range.row0=MIN_VISIBLE_ROW(sheet); + drawing_range.col0=MIN_VISIBLE_COLUMN(sheet); + drawing_range.rowi=MAX_VISIBLE_ROW(sheet); + drawing_range.coli=MAX_VISIBLE_COLUMN(sheet); +/* + gdk_draw_rectangle (sheet->pixmap, + GTK_WIDGET(sheet)->style->white_gc, + TRUE, + 0,0, + sheet->sheet_window_width,sheet->sheet_window_height); +*/ + } + else + { + drawing_range.row0=MAX(range->row0, MIN_VISIBLE_ROW(sheet)); + drawing_range.col0=MAX(range->col0, MIN_VISIBLE_COLUMN(sheet)); + drawing_range.rowi=MIN(range->rowi, MAX_VISIBLE_ROW(sheet)); + drawing_range.coli=MIN(range->coli, MAX_VISIBLE_COLUMN(sheet)); + } + + if(drawing_range.coli == sheet->maxcol){ + area.x=COLUMN_LEFT_XPIXEL(sheet,sheet->maxcol)+ + sheet->column[sheet->maxcol].width+1; + area.y=0; + + gdk_gc_set_foreground(sheet->fg_gc, &sheet->bg_color); + + gdk_draw_rectangle (sheet->pixmap, + sheet->fg_gc, + TRUE, + area.x,area.y, + sheet->sheet_window_width - area.x, + sheet->sheet_window_height); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + area.x, + area.y, + area.x, + area.y, + sheet->sheet_window_width - area.x, + sheet->sheet_window_height); + } + if(drawing_range.rowi == sheet->maxrow){ + area.x=0; + area.y=ROW_TOP_YPIXEL(sheet,sheet->maxrow)+sheet->row[sheet->maxrow].height+1; + + gdk_gc_set_foreground(sheet->fg_gc, &sheet->bg_color); + + gdk_draw_rectangle (sheet->pixmap, + sheet->fg_gc, + TRUE, + area.x,area.y, + sheet->sheet_window_width, + sheet->sheet_window_height - area.y); + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + area.x, + area.y, + area.x, + area.y, + sheet->sheet_window_width, + sheet->sheet_window_height - area.y); + } + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.col0; j<=drawing_range.coli; j++){ + gtk_sheet_cell_draw_default(sheet, i, j); + } + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.col0; j<=drawing_range.coli; j++){ + gtk_sheet_cell_draw_border(sheet, i-1, j, GTK_SHEET_BOTTOM_BORDER); + gtk_sheet_cell_draw_border(sheet, i+1, j, GTK_SHEET_TOP_BORDER); + gtk_sheet_cell_draw_border(sheet, i, j-1, GTK_SHEET_RIGHT_BORDER); + gtk_sheet_cell_draw_border(sheet, i, j+1, GTK_SHEET_LEFT_BORDER); + gtk_sheet_cell_draw_border(sheet, i, j, 15); + } + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.col0; j<=drawing_range.coli; j++) + if(i<=sheet->maxallocrow && j<=sheet->maxalloccol && + sheet->data[i] && sheet->data[i][j]) + gtk_sheet_cell_draw_label (sheet, i, j); + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=sheet->column[drawing_range.col0].left_text_column; j<drawing_range.col0; j++) + if(i<=sheet->maxallocrow && j<=sheet->maxalloccol && + sheet->data[i] && sheet->data[i][j]) + gtk_sheet_cell_draw_label (sheet, i, j); + + for(i=drawing_range.row0; i<=drawing_range.rowi; i++) + for(j=drawing_range.coli+1; j<=sheet->column[drawing_range.coli].right_text_column; j++) + if(i<=sheet->maxallocrow && j<=sheet->maxalloccol && + sheet->data[i] && sheet->data[i][j]) + gtk_sheet_cell_draw_label (sheet, i, j); + + gtk_sheet_draw_backing_pixmap(sheet, drawing_range); + + if(sheet->state != GTK_SHEET_NORMAL && gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_range_draw_selection(sheet, drawing_range); + + if(sheet->state == GTK_STATE_NORMAL && + sheet->active_cell.row >= drawing_range.row0 && + sheet->active_cell.row <= drawing_range.rowi && + sheet->active_cell.col >= drawing_range.col0 && + sheet->active_cell.col <= drawing_range.coli) + gtk_sheet_show_active_cell(sheet); + +} + +static void +gtk_sheet_range_draw_selection(GtkSheet *sheet, GtkSheetRange range) +{ + GdkRectangle area; + gint i,j; + GtkSheetRange aux; + + if(range.col0 > sheet->range.coli || range.coli < sheet->range.col0 || + range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0) + return; + + if(!gtk_sheet_range_isvisible(sheet, range)) return; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + aux=range; + + range.col0=MAX(sheet->range.col0, range.col0); + range.coli=MIN(sheet->range.coli, range.coli); + range.row0=MAX(sheet->range.row0, range.row0); + range.rowi=MIN(sheet->range.rowi, range.rowi); + + range.col0=MAX(range.col0, MIN_VISIBLE_COLUMN(sheet)); + range.coli=MIN(range.coli, MAX_VISIBLE_COLUMN(sheet)); + range.row0=MAX(range.row0, MIN_VISIBLE_ROW(sheet)); + range.rowi=MIN(range.rowi, MAX_VISIBLE_ROW(sheet)); + + for(i=range.row0; i<=range.rowi; i++){ + for(j=range.col0; j<=range.coli; j++){ + + if(gtk_sheet_cell_get_state(sheet, i, j)==GTK_STATE_SELECTED && + sheet->column[j].is_visible && sheet->row[i].is_visible){ + + row_button_set(sheet, i); + column_button_set(sheet, j); + + area.x=COLUMN_LEFT_XPIXEL(sheet,j); + area.y=ROW_TOP_YPIXEL(sheet,i); + area.width=sheet->column[j].width; + area.height=sheet->row[i].height; + + if(i==sheet->range.row0){ + area.y=area.y+2; + area.height=area.height-2; + } + if(i==sheet->range.rowi) area.height=area.height-3; + if(j==sheet->range.col0){ + area.x=area.x+2; + area.width=area.width-2; + } + if(j==sheet->range.coli) area.width=area.width-3; + + if(i!=sheet->active_cell.row || j!=sheet->active_cell.col){ + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + area.x+1,area.y+1, + area.width,area.height); + } + } + + } + } + + gtk_sheet_draw_border(sheet, sheet->range); + +} + +static void +gtk_sheet_draw_backing_pixmap(GtkSheet *sheet, GtkSheetRange range) +{ + gint x,y,width,height; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + x=COLUMN_LEFT_XPIXEL(sheet,range.col0); + y=ROW_TOP_YPIXEL(sheet, range.row0); + width=COLUMN_LEFT_XPIXEL(sheet, range.coli)-x+sheet->column[range.coli].width; + height=ROW_TOP_YPIXEL(sheet, range.rowi)-y+sheet->row[range.rowi].height; + + if(range.row0==sheet->range.row0){ + y=y-5; + height=height+5; + } + if(range.rowi==sheet->range.rowi) height=height+5; + if(range.col0==sheet->range.col0){ + x=x-5; + width=width+5; + } + if(range.coli==sheet->range.coli) width=width+5; + + + width=MIN(width, sheet->sheet_window_width-x); + height=MIN(height, sheet->sheet_window_height-y); + + x--; + y--; + width+=2; + height+=2; + + x = (sheet->row_titles_visible) + ? MAX(x, sheet->row_title_area.width) : MAX(x, 0); + y = (sheet->column_titles_visible) + ? MAX(y, sheet->column_title_area.height) : MAX(y, 0); + + if(range.coli==sheet->maxcol) width=sheet->sheet_window_width-x; + if(range.rowi==sheet->maxrow) height=sheet->sheet_window_height-y; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x, + y, + x, + y, + width+1, + height+1); +} + +static GtkSheetCell * +gtk_sheet_cell_new() +{ + GtkSheetCell *cell; + cell = g_new(GtkSheetCell, 1); + cell->text = NULL; + cell->link = NULL; + cell->attributes = NULL; + return cell; +} + +void +gtk_sheet_set_cell_text(GtkSheet *sheet, gint row, gint col, const gchar *text) +{ + GtkSheetCellAttr attributes; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (col > sheet->maxcol || row > sheet->maxrow) return; + if (col < 0 || row < 0) return; + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + gtk_sheet_set_cell(sheet, row, col, attributes.justification, text); +} + +void +gtk_sheet_set_cell(GtkSheet *sheet, gint row, gint col, + GtkJustification justification, + const gchar *text) +{ + GtkSheetCell **cell; + GtkSheetRange range; + gint text_width; + GtkSheetCellAttr attributes; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (col > sheet->maxcol || row > sheet->maxrow) return; + if (col < 0 || row < 0) return; + + CheckBounds(sheet, row, col); + + cell=&sheet->data[row][col]; + + if(*cell==NULL) + (*cell) = gtk_sheet_cell_new(); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + (*cell)->row = row; + (*cell)->col = col; + + attributes.justification = justification; + gtk_sheet_set_cell_attributes(sheet, row, col, attributes); + + if((*cell)->text){ + g_free((*cell)->text); + (*cell)->text = NULL; + } + + if(text) + (*cell)->text=g_strdup(text); + + if(attributes.is_visible){ + + text_width = 0; + if((*cell)->text && strlen((*cell)->text) > 0) { + text_width = STRING_WIDTH(GTK_WIDGET(sheet), attributes.font_desc, (*cell)->text); + } + + range.row0 = row; + range.rowi = row; + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + if(gtk_sheet_autoresize(sheet) && + text_width > sheet->column[col].width-2*CELLOFFSET-attributes.border.width){ + gtk_sheet_set_column_width(sheet, col, text_width+2*CELLOFFSET+attributes.border.width); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_REDRAW_PENDING); + } + else + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + } + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], row, col); + + +} + +void +gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column) +{ + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (column > sheet->maxcol || row > sheet->maxrow) return; + if (column > sheet->maxalloccol || row > sheet->maxallocrow) return; + if (column < 0 || row < 0) return; + + range.row0 = row; + range.rowi = row; + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + gtk_sheet_real_cell_clear(sheet, row, column, FALSE); + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_range_draw(sheet, &range); + } +} + +void +gtk_sheet_cell_delete (GtkSheet *sheet, gint row, gint column) +{ + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if (column > sheet->maxcol || row > sheet->maxrow) return; + if (column > sheet->maxalloccol || row > sheet->maxallocrow) return; + if (column < 0 || row < 0) return; + + range.row0 = row; + range.rowi = row; + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + gtk_sheet_real_cell_clear(sheet, row, column, TRUE); + + if(!GTK_SHEET_IS_FROZEN(sheet)){ + gtk_sheet_range_draw(sheet, &range); + } +} + +static void +gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column, gboolean delete) +{ + gchar *text; + gpointer link; + + if(row > sheet->maxallocrow || column > sheet->maxalloccol) return; + if(!sheet->data[row]) return; + if(!sheet->data[row][column]) return; + + text = gtk_sheet_cell_get_text(sheet, row, column); + link = gtk_sheet_get_link(sheet, row, column); + + if(text){ + g_free(sheet->data[row][column]->text); + sheet->data[row][column]->text = NULL; + + if(GTK_IS_OBJECT(sheet) && G_OBJECT(sheet)->ref_count > 0) + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[CLEAR_CELL], row, column); + } + + if(delete){ + if(sheet->data[row][column]->attributes){ + g_free(sheet->data[row][column]->attributes); + sheet->data[row][column]->attributes = NULL; + } + sheet->data[row][column]->link = NULL; + + if(sheet->data[row][column]) g_free(sheet->data[row][column]); + + sheet->data[row][column] = NULL; + } + +} + +void +gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + gtk_sheet_real_range_clear(sheet, range, FALSE); +} + +void +gtk_sheet_range_delete (GtkSheet *sheet, const GtkSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + gtk_sheet_real_range_clear(sheet, range, TRUE); +} + +static void +gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range, + gboolean delete) +{ + gint i, j; + GtkSheetRange clear; + + if(!range){ + clear.row0=0; + clear.rowi=sheet->maxallocrow; + clear.col0=0; + clear.coli=sheet->maxalloccol; + }else + clear=*range; + + clear.row0=MAX(clear.row0, 0); + clear.col0=MAX(clear.col0, 0); + clear.rowi=MIN(clear.rowi, sheet->maxallocrow); + clear.coli=MIN(clear.coli, sheet->maxalloccol); + + for(i=clear.row0; i<=clear.rowi; i++) + for(j=clear.col0; j<=clear.coli; j++){ + gtk_sheet_real_cell_clear(sheet, i, j, delete); + } + + gtk_sheet_range_draw(sheet, NULL); +} + + +gchar * +gtk_sheet_cell_get_text (GtkSheet *sheet, gint row, gint col) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + + if(col > sheet->maxcol || row > sheet->maxrow) return NULL; + if(col < 0 || row < 0) return NULL; + if(row > sheet->maxallocrow || col > sheet->maxalloccol) return NULL; + if(!sheet->data[row]) return NULL; + if(!sheet->data[row][col]) return NULL; + if(!sheet->data[row][col]->text) return NULL; + if(strlen(sheet->data[row][col]->text) == 0) return NULL; + + return (sheet->data[row][col]->text); +} + +void +gtk_sheet_link_cell(GtkSheet *sheet, gint row, gint col, gpointer link) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if(col > sheet->maxcol || row > sheet->maxrow) return; + if(col < 0 || row < 0) return; + + if(row > sheet->maxallocrow || col > sheet->maxalloccol || + !sheet->data[row] || !sheet->data[row][col]) + gtk_sheet_set_cell_text(sheet, row, col, ""); + + sheet->data[row][col]->link = link; +} + +gpointer +gtk_sheet_get_link(GtkSheet *sheet, gint row, gint col) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + if(col > sheet->maxcol || row > sheet->maxrow) return NULL; + if(col < 0 || row < 0) return NULL; + + if (row > sheet->maxallocrow || col > sheet->maxalloccol) return NULL; + if (!sheet->data[row]) return NULL; /* Added by Chris Howell */ + if (!sheet->data[row][col]) return NULL; /* Added by Bob Lissner */ + + return(sheet->data[row][col]->link); +} + +void +gtk_sheet_remove_link(GtkSheet *sheet, gint row, gint col) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + if(col > sheet->maxcol || row > sheet->maxrow) return; + if(col < 0 || row < 0) return; + + /* Fixed by Andreas Voegele */ + if(row < sheet->maxallocrow && col < sheet->maxalloccol && + sheet->data[row] && sheet->data[row][col] && + sheet->data[row][col]->link) + sheet->data[row][col]->link = NULL; +} + + +GtkStateType +gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col) +{ + gint state; + GtkSheetRange *range; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + if(col > sheet->maxcol || row > sheet->maxrow) return 0; + if(col < 0 || row < 0) return 0; + + state = sheet->state; + range = &sheet->range; + + switch (state){ + case GTK_SHEET_NORMAL: + return GTK_STATE_NORMAL; + break; + case GTK_SHEET_ROW_SELECTED: + if(row>=range->row0 && row<=range->rowi) + return GTK_STATE_SELECTED; + break; + case GTK_SHEET_COLUMN_SELECTED: + if(col>=range->col0 && col<=range->coli) + return GTK_STATE_SELECTED; + break; + case GTK_SHEET_RANGE_SELECTED: + if(row >= range->row0 && row <= range->rowi && \ + col >= range->col0 && col <= range->coli) + return GTK_STATE_SELECTED; + break; + } + return GTK_STATE_NORMAL; +} + +gboolean +gtk_sheet_get_pixel_info (GtkSheet * sheet, + gint x, + gint y, + gint * row, + gint * column) +{ + gint trow, tcol; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + /* bounds checking, return false if the user clicked + * on a blank area */ + trow = ROW_FROM_YPIXEL (sheet, y); + if (trow > sheet->maxrow) + return FALSE; + + *row = trow; + + tcol = COLUMN_FROM_XPIXEL (sheet, x); + if (tcol > sheet->maxcol) + return FALSE; + + *column = tcol; + + return TRUE; +} + +gboolean +gtk_sheet_get_cell_area (GtkSheet * sheet, + gint row, + gint column, + GdkRectangle *area) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + if(row > sheet->maxrow || column > sheet->maxcol) return FALSE; + + area->x = (column == -1) ? 0 : (COLUMN_LEFT_XPIXEL(sheet, column) - + (sheet->row_titles_visible + ? sheet->row_title_area.width + : 0)); + area->y = (row == -1) ? 0 : (ROW_TOP_YPIXEL(sheet, row) - + (sheet->column_titles_visible + ? sheet->column_title_area.height + : 0)); + area->width= (column == -1) ? sheet->row_title_area.width + : sheet->column[column].width; + area->height= (row == -1) ? sheet->column_title_area.height + : sheet->row[row].height; + +/* + if(row < 0 || column < 0) return FALSE; + + area->x = COLUMN_LEFT_XPIXEL(sheet, column); + area->y = ROW_TOP_YPIXEL(sheet, row); + if(sheet->row_titles_visible) + area->x -= sheet->row_title_area.width; + if(sheet->column_titles_visible) + area->y -= sheet->column_title_area.height; + + area->width=sheet->column[column].width; + area->height=sheet->row[row].height; +*/ + return TRUE; +} + +gboolean +gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); + + if(row < 0 || column < 0) return FALSE; + if(row > sheet->maxrow || column > sheet->maxcol) return FALSE; + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) + { + if(!gtk_sheet_deactivate_cell(sheet)) return FALSE; + } + + sheet->active_cell.row=row; + sheet->active_cell.col=column; + + if(!gtk_sheet_activate_cell(sheet, row, column)) return FALSE; + + return TRUE; +} + +void +gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + *row = sheet->active_cell.row; + *column = sheet->active_cell.col; +} + +static void +gtk_sheet_entry_changed(GtkWidget *widget, gpointer data) +{ + GtkSheet *sheet; + gint row,col; + const char *text; + GtkJustification justification; + GtkSheetCellAttr attributes; + + g_return_if_fail (data != NULL); + g_return_if_fail (GTK_IS_SHEET (data)); + + sheet=GTK_SHEET(data); + + if(!GTK_WIDGET_VISIBLE(widget)) return; + if(sheet->state != GTK_STATE_NORMAL) return; + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + if(row<0 || col<0) return; + + sheet->active_cell.row=-1; + sheet->active_cell.col=-1; + + text=gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + if(text && strlen(text)!=0){ + gtk_sheet_get_attributes(sheet, row, col, &attributes); + justification=attributes.justification; + gtk_sheet_set_cell(sheet, row, col, justification, text); + } + else + { + /* Added by Matias Mutchinick */ + gtk_sheet_cell_clear(sheet, row, col); + } + + if(sheet->freeze_count == 0) + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + sheet->active_cell.row=row;; + sheet->active_cell.col=col; + +} + + +static gboolean +gtk_sheet_deactivate_cell(GtkSheet *sheet) +{ + gboolean veto = TRUE; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return FALSE; + if(sheet->state != GTK_SHEET_NORMAL) return FALSE; + + _gtkextra_signal_emit(GTK_OBJECT(sheet),sheet_signals[DEACTIVATE], + sheet->active_cell.row, + sheet->active_cell.col, &veto); + + if(!veto) return FALSE; + + gtk_signal_disconnect_by_func(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + (GtkSignalFunc) gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + + gtk_sheet_hide_active_cell(sheet); + sheet->active_cell.row=-1; + sheet->active_cell.col=-1; + + if(GTK_SHEET_REDRAW_PENDING(sheet)){ + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_REDRAW_PENDING); + gtk_sheet_range_draw(sheet, NULL); + } + + return TRUE; +} + +static void +gtk_sheet_hide_active_cell(GtkSheet *sheet) +{ + const char *text; + gint row,col; + GtkJustification justification; + GtkSheetCellAttr attributes; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + if(row < 0 || col < 0) return; + + if(sheet->freeze_count == 0) + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN); + + text=gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + justification=attributes.justification; + + if(text && strlen(text)!=0){ + gtk_sheet_set_cell(sheet, row, col, justification, text); + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[SET_CELL], row, col); + } + else + { + gtk_sheet_cell_clear(sheet, row, col); + } + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + column_button_release(sheet, col); + row_button_release(sheet, row); + + gtk_widget_unmap(sheet->sheet_entry); + + if(row != -1 && col != -1) + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + COLUMN_LEFT_XPIXEL(sheet,col)-1, + ROW_TOP_YPIXEL(sheet,row)-1, + COLUMN_LEFT_XPIXEL(sheet,col)-1, + ROW_TOP_YPIXEL(sheet,row)-1, + sheet->column[col].width+4, + sheet->row[row].height+4); + + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_WIDGET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(sheet->sheet_entry), GTK_VISIBLE); + +} + +static gboolean +gtk_sheet_activate_cell(GtkSheet *sheet, gint row, gint col) +{ + gboolean veto = TRUE; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + if(row < 0 || col < 0) return FALSE; + if(row > sheet->maxrow || col > sheet->maxcol) return FALSE; + +/* _gtkextra_signal_emit(GTK_OBJECT(sheet),sheet_signals[ACTIVATE], row, col, &veto); + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return veto; +*/ + + if(!veto) return FALSE; + if(sheet->state != GTK_SHEET_NORMAL){ + sheet->state=GTK_SHEET_NORMAL; + gtk_sheet_real_unselect_range(sheet, NULL); + } + + sheet->range.row0=row; + sheet->range.col0=col; + sheet->range.rowi=row; + sheet->range.coli=col; + sheet->active_cell.row=row; + sheet->active_cell.col=col; + sheet->selection_cell.row=row; + sheet->selection_cell.col=col; + row_button_set(sheet, row); + column_button_set(sheet, col); + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + gtk_sheet_show_active_cell(sheet); + + gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "changed", + (GtkSignalFunc)gtk_sheet_entry_changed, + GTK_OBJECT(GTK_WIDGET(sheet))); + + _gtkextra_signal_emit(GTK_OBJECT(sheet),sheet_signals[ACTIVATE], row, col, &veto); + + return TRUE; +} + +static void +gtk_sheet_show_active_cell(GtkSheet *sheet) +{ + GtkSheetCell *cell; + GtkEntry *sheet_entry; + GtkSheetCellAttr attributes; + gchar *text = NULL; + GtkJustification justification; + gint row, col; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + /* Don't show the active cell, if there is no active cell: */ + if(!(row >= 0 && col >= 0)) /* e.g row or coll == -1. */ + return; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + if(sheet->state != GTK_SHEET_NORMAL) return; + if(GTK_SHEET_IN_SELECTION(sheet)) return; + + GTK_WIDGET_SET_FLAGS(GTK_WIDGET(sheet->sheet_entry), GTK_VISIBLE); + + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry(sheet)); + + gtk_sheet_get_attributes(sheet, row, col, &attributes); + + justification = GTK_JUSTIFY_LEFT; + + if(gtk_sheet_justify_entry(sheet)) + justification = attributes.justification; + + if(row <= sheet->maxallocrow && col <= sheet->maxalloccol) { + if(sheet->data[row]) { + if(sheet->data[row][col]) { + cell = sheet->data[row][col]; + if(cell->text) + text = g_strdup(cell->text); + } + } + } + + if(!text) text = g_strdup(""); + + gtk_entry_set_visibility(GTK_ENTRY(sheet_entry), attributes.is_visible); + + if(gtk_sheet_locked(sheet) || !attributes.is_editable){ + gtk_entry_set_editable(GTK_ENTRY(sheet_entry), FALSE); + }else{ + gtk_entry_set_editable(GTK_ENTRY(sheet_entry), TRUE); + } + + if(!GTK_IS_ITEM_ENTRY(sheet_entry)) + gtk_entry_set_text(GTK_ENTRY(sheet_entry), text); + else + gtk_item_entry_set_text(GTK_ITEM_ENTRY(sheet_entry), text, justification); + + gtk_sheet_entry_set_max_size(sheet); + gtk_sheet_size_allocate_entry(sheet); + + gtk_widget_map(sheet->sheet_entry); + gtk_sheet_draw_active_cell(sheet); + + gtk_widget_grab_focus(GTK_WIDGET(sheet_entry)); + GTK_WIDGET_SET_FLAGS(GTK_WIDGET(sheet_entry), GTK_HAS_FOCUS); + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(sheet), GTK_HAS_FOCUS); + + g_free(text); +} + +static void +gtk_sheet_draw_active_cell(GtkSheet *sheet) +{ + gint row, col; + + if(!GTK_WIDGET_DRAWABLE(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + if(row<0 || col<0) return; + + if(!gtk_sheet_cell_isvisible(sheet, row, col)) return; + + row_button_set(sheet, row); + column_button_set(sheet, col); + + gtk_sheet_draw_backing_pixmap(sheet, sheet->range); + gtk_sheet_draw_border(sheet, sheet->range); + +} + + +static void +gtk_sheet_make_backing_pixmap (GtkSheet *sheet, guint width, guint height) +{ + gint pixmap_width, pixmap_height; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + if(width == 0 && height == 0){ + width=sheet->sheet_window_width+80; + height=sheet->sheet_window_height+80; + } + + if (!sheet->pixmap) + { + /* allocate */ + sheet->pixmap = gdk_pixmap_new (sheet->sheet_window, + width, height, + -1); + if(!GTK_SHEET_IS_FROZEN(sheet)) gtk_sheet_range_draw(sheet, NULL); + } + else + { + /* reallocate if sizes don't match */ + gdk_window_get_size (sheet->pixmap, + &pixmap_width, &pixmap_height); + if ((pixmap_width != width) || (pixmap_height != height)) + { + g_free(sheet->pixmap); + sheet->pixmap = gdk_pixmap_new (sheet->sheet_window, + width, height, + -1); + if(!GTK_SHEET_IS_FROZEN(sheet)) gtk_sheet_range_draw(sheet, NULL); + } + } +} + +static void +gtk_sheet_new_selection(GtkSheet *sheet, GtkSheetRange *range) +{ + gint i,j, mask1, mask2; + gint state, selected; + gint x,y,width,height; + GtkSheetRange new_range, aux_range; + + g_return_if_fail (sheet != NULL); + + if(range==NULL) range=&sheet->range; + + new_range=*range; + + range->row0=MIN(range->row0, sheet->range.row0); + range->rowi=MAX(range->rowi, sheet->range.rowi); + range->col0=MIN(range->col0, sheet->range.col0); + range->coli=MAX(range->coli, sheet->range.coli); + + range->row0=MAX(range->row0, MIN_VISIBLE_ROW(sheet)); + range->rowi=MIN(range->rowi, MAX_VISIBLE_ROW(sheet)); + range->col0=MAX(range->col0, MIN_VISIBLE_COLUMN(sheet)); + range->coli=MIN(range->coli, MAX_VISIBLE_COLUMN(sheet)); + + aux_range.row0=MAX(new_range.row0, MIN_VISIBLE_ROW(sheet)); + aux_range.rowi=MIN(new_range.rowi, MAX_VISIBLE_ROW(sheet)); + aux_range.col0=MAX(new_range.col0, MIN_VISIBLE_COLUMN(sheet)); + aux_range.coli=MIN(new_range.coli, MAX_VISIBLE_COLUMN(sheet)); + + for(i=range->row0; i<=range->rowi; i++){ + for(j=range->col0; j<=range->coli; j++){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + selected=(i<=new_range.rowi && i>=new_range.row0 && + j<=new_range.coli && j>=new_range.col0) ? TRUE : FALSE; + + if(state==GTK_STATE_SELECTED && selected && + sheet->column[j].is_visible && sheet->row[i].is_visible && + (i==sheet->range.row0 || i==sheet->range.rowi || + j==sheet->range.col0 || j==sheet->range.coli || + i==new_range.row0 || i==new_range.rowi || + j==new_range.col0 || j==new_range.coli)){ + + mask1 = i==sheet->range.row0 ? 1 : 0; + mask1 = i==sheet->range.rowi ? mask1+2 : mask1; + mask1 = j==sheet->range.col0 ? mask1+4 : mask1; + mask1 = j==sheet->range.coli ? mask1+8 : mask1; + + mask2 = i==new_range.row0 ? 1 : 0; + mask2 = i==new_range.rowi ? mask2+2 : mask2; + mask2 = j==new_range.col0 ? mask2+4 : mask2; + mask2 = j==new_range.coli ? mask2+8 : mask2; + + if(mask1 != mask2){ + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==sheet->range.row0){ + y=y-3; + height=height+3; + } + if(i==sheet->range.rowi) height=height+3; + if(j==sheet->range.col0){ + x=x-3; + width=width+3; + } + if(j==sheet->range.coli) width=width+3; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x+1, + y+1, + x+1, + y+1, + width, + height); + + if(i != sheet->active_cell.row || j != sheet->active_cell.col){ + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==new_range.row0){ + y=y+2; + height=height-2; + } + if(i==new_range.rowi) height=height-3; + if(j==new_range.col0){ + x=x+2; + width=width-2; + } + if(j==new_range.coli) width=width-3; + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y+1, + width,height); + } + } + } + } + } + + for(i=range->row0; i<=range->rowi; i++){ + for(j=range->col0; j<=range->coli; j++){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + selected=(i<=new_range.rowi && i>=new_range.row0 && + j<=new_range.coli && j>=new_range.col0) ? TRUE : FALSE; + + if(state==GTK_STATE_SELECTED && !selected && + sheet->column[j].is_visible && sheet->row[i].is_visible){ + + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==sheet->range.row0){ + y=y-3; + height=height+3; + } + if(i==sheet->range.rowi) height=height+3; + if(j==sheet->range.col0){ + x=x-3; + width=width+3; + } + if(j==sheet->range.coli) width=width+3; + + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x+1, + y+1, + x+1, + y+1, + width, + height); + } + } + } + + for(i=range->row0; i<=range->rowi; i++){ + for(j=range->col0; j<=range->coli; j++){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + selected=(i<=new_range.rowi && i>=new_range.row0 && + j<=new_range.coli && j>=new_range.col0) ? TRUE : FALSE; + + if(state!=GTK_STATE_SELECTED && selected && + sheet->column[j].is_visible && sheet->row[i].is_visible && + (i != sheet->active_cell.row || j != sheet->active_cell.col)){ + + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=COLUMN_LEFT_XPIXEL(sheet, j)-x+sheet->column[j].width; + height=ROW_TOP_YPIXEL(sheet, i)-y+sheet->row[i].height; + + if(i==new_range.row0){ + y=y+2; + height=height-2; + } + if(i==new_range.rowi) height=height-3; + if(j==new_range.col0){ + x=x+2; + width=width-2; + } + if(j==new_range.coli) width=width-3; + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y+1, + width,height); + + } + + } + } + + for(i=aux_range.row0; i<=aux_range.rowi; i++){ + for(j=aux_range.col0; j<=aux_range.coli; j++){ + + if(sheet->column[j].is_visible && sheet->row[i].is_visible){ + + state=gtk_sheet_cell_get_state(sheet, i, j); + + mask1 = i==sheet->range.row0 ? 1 : 0; + mask1 = i==sheet->range.rowi ? mask1+2 : mask1; + mask1 = j==sheet->range.col0 ? mask1+4 : mask1; + mask1 = j==sheet->range.coli ? mask1+8 : mask1; + + mask2 = i==new_range.row0 ? 1 : 0; + mask2 = i==new_range.rowi ? mask2+2 : mask2; + mask2 = j==new_range.col0 ? mask2+4 : mask2; + mask2 = j==new_range.coli ? mask2+8 : mask2; + if(mask2!=mask1 || (mask2==mask1 && state!=GTK_STATE_SELECTED)){ + x=COLUMN_LEFT_XPIXEL(sheet,j); + y=ROW_TOP_YPIXEL(sheet, i); + width=sheet->column[j].width; + height=sheet->row[i].height; + if(mask2 & 1) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y-1, + width,3); + + + if(mask2 & 2) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+1,y+height-1, + width,3); + + if(mask2 & 4) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-1,y+1, + 3,height); + + + if(mask2 & 8) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x+width-1,y+1, + 3,height); + + + + } + + } + + } + } + + + *range=new_range; + gtk_sheet_draw_corners(sheet, new_range); + +} + +static void +gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range) +{ + GtkWidget *widget; + GdkRectangle area; + gint i; + gint x,y,width,height; + + widget = GTK_WIDGET(sheet); + + x=COLUMN_LEFT_XPIXEL(sheet,new_range.col0); + y=ROW_TOP_YPIXEL(sheet,new_range.row0); + width=COLUMN_LEFT_XPIXEL(sheet,new_range.coli)-x+ + sheet->column[new_range.coli].width; + height=ROW_TOP_YPIXEL(sheet,new_range.rowi)-y+ + sheet->row[new_range.rowi].height; + + area.x=COLUMN_LEFT_XPIXEL(sheet, MIN_VISIBLE_COLUMN(sheet)); + area.y=ROW_TOP_YPIXEL(sheet, MIN_VISIBLE_ROW(sheet)); + area.width=sheet->sheet_window_width; + area.height=sheet->sheet_window_height; + + if(x<0) { + width=width+x; + x=0; + } + if(width>area.width) width=area.width+10; + if(y<0) { + height=height+y; + y=0; + } + if(height>area.height) height=area.height+10; + + gdk_gc_set_clip_rectangle(sheet->xor_gc, &area); + + for(i=-1; i<=1; ++i) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + FALSE, + x+i,y+i, + width-2*i,height-2*i); + + gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL); + + gtk_sheet_draw_corners(sheet, new_range); + +} + +static void +gtk_sheet_draw_corners(GtkSheet *sheet, GtkSheetRange range) +{ + gint x,y; + guint width = 1; + + if(gtk_sheet_cell_isvisible(sheet, range.row0, range.col0)){ + x=COLUMN_LEFT_XPIXEL(sheet,range.col0); + y=ROW_TOP_YPIXEL(sheet,range.row0); + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-1, + y-1, + x-1, + y-1, + 3, + 3); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-1,y-1, + 3,3); + } + + if(gtk_sheet_cell_isvisible(sheet, range.row0, range.coli) || + sheet->state == GTK_SHEET_COLUMN_SELECTED){ + x=COLUMN_LEFT_XPIXEL(sheet,range.coli)+ + sheet->column[range.coli].width; + y=ROW_TOP_YPIXEL(sheet,range.row0); + width = 1; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + { + y = ROW_TOP_YPIXEL(sheet, sheet->view.row0)+3; + width = 3; + } + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-width, + y-width, + x-width, + y-width, + 2*width+1, + 2*width+1); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-width+width/2,y-width+width/2, + 2+width,2+width); + } + + if(gtk_sheet_cell_isvisible(sheet, range.rowi, range.col0) || + sheet->state == GTK_SHEET_ROW_SELECTED){ + x=COLUMN_LEFT_XPIXEL(sheet,range.col0); + y=ROW_TOP_YPIXEL(sheet,range.rowi)+ + sheet->row[range.rowi].height; + width = 1; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + { + x = COLUMN_LEFT_XPIXEL(sheet, sheet->view.col0)+3; + width = 3; + } + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-width, + y-width, + x-width, + y-width, + 2*width+1, + 2*width+1); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-width+width/2,y-width+width/2, + 2+width,2+width); + } + + if(gtk_sheet_cell_isvisible(sheet, range.rowi, range.coli)){ + x=COLUMN_LEFT_XPIXEL(sheet,range.coli)+ + sheet->column[range.coli].width; + y=ROW_TOP_YPIXEL(sheet,range.rowi)+ + sheet->row[range.rowi].height; + width = 1; + if(sheet->state == GTK_SHEET_RANGE_SELECTED) width = 3; + if(sheet->state == GTK_SHEET_NORMAL) width = 3; + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + x-width, + y-width, + x-width, + y-width, + 2*width+1, + 2*width+1); + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x-width+width/2,y-width+width/2, + 2+width,2+width); + + } + +} + + +static void +gtk_sheet_real_select_range (GtkSheet * sheet, + GtkSheetRange * range) +{ + gint i; + gint state; + + g_return_if_fail (sheet != NULL); + + if(range==NULL) range=&sheet->range; + + if(range->row0 < 0 || range->rowi < 0) return; + if(range->col0 < 0 || range->coli < 0) return; + + state=sheet->state; + + if(state==GTK_SHEET_COLUMN_SELECTED || state==GTK_SHEET_RANGE_SELECTED){ + for(i=sheet->range.col0; i< range->col0; i++) + column_button_release(sheet, i); + for(i=range->coli+1; i<= sheet->range.coli; i++) + column_button_release(sheet, i); + for(i=range->col0; i<=range->coli; i++){ + column_button_set(sheet, i); + } + } + + if(state==GTK_SHEET_ROW_SELECTED || state==GTK_SHEET_RANGE_SELECTED){ + for(i=sheet->range.row0; i< range->row0; i++) + row_button_release(sheet, i); + for(i=range->rowi+1; i<= sheet->range.rowi; i++) + row_button_release(sheet, i); + for(i=range->row0; i<=range->rowi; i++){ + row_button_set(sheet, i); + } + } + + if(range->coli != sheet->range.coli || range->col0 != sheet->range.col0 || + range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0) + { + + gtk_sheet_new_selection(sheet, range); + + sheet->range.col0=range->col0; + sheet->range.coli=range->coli; + sheet->range.row0=range->row0; + sheet->range.rowi=range->rowi; + + } + else + { + gtk_sheet_draw_backing_pixmap(sheet, sheet->range); + gtk_sheet_range_draw_selection(sheet, sheet->range); + } + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[SELECT_RANGE], range); +} + +void +gtk_sheet_select_range(GtkSheet * sheet, const GtkSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + + if(range==NULL) range=&sheet->range; + + if(range->row0 < 0 || range->rowi < 0) return; + if(range->col0 < 0 || range->coli < 0) return; + + if(sheet->state != GTK_SHEET_NORMAL) + gtk_sheet_real_unselect_range(sheet, NULL); + else + { + gboolean veto = TRUE; + veto = gtk_sheet_deactivate_cell(sheet); + if(!veto) return; + } + + sheet->range.row0=range->row0; + sheet->range.rowi=range->rowi; + sheet->range.col0=range->col0; + sheet->range.coli=range->coli; + sheet->active_cell.row=range->row0; + sheet->active_cell.col=range->col0; + sheet->selection_cell.row=range->rowi; + sheet->selection_cell.col=range->coli; + + sheet->state = GTK_SHEET_RANGE_SELECTED; + gtk_sheet_real_select_range(sheet, NULL); + +} + +void +gtk_sheet_unselect_range (GtkSheet * sheet) +{ + gtk_sheet_real_unselect_range(sheet, NULL); + sheet->state = GTK_STATE_NORMAL; + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col); +} + + +static void +gtk_sheet_real_unselect_range (GtkSheet * sheet, + const GtkSheetRange *range) +{ + gint i; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))); + + if(range==NULL){ + range=&sheet->range; + } + + if(range->row0 < 0 || range->rowi < 0) return; + if(range->col0 < 0 || range->coli < 0) return; + + if (gtk_sheet_range_isvisible (sheet, *range)){ + gtk_sheet_draw_backing_pixmap(sheet, *range); + } + + for(i=range->col0; i<=range->coli; i++){ + column_button_release(sheet, i); + } + + for(i=range->row0; i<=range->rowi; i++){ + row_button_release(sheet, i); + } + +} + + +static gint +gtk_sheet_expose (GtkWidget * widget, + GdkEventExpose * event) +{ + GtkSheet *sheet; + GtkSheetRange range; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + sheet = GTK_SHEET (widget); + + if (GTK_WIDGET_DRAWABLE (widget)) + { + range.row0=ROW_FROM_YPIXEL(sheet,event->area.y); + range.col0=COLUMN_FROM_XPIXEL(sheet,event->area.x); + range.rowi=ROW_FROM_YPIXEL(sheet,event->area.y+event->area.height); + range.coli=COLUMN_FROM_XPIXEL(sheet,event->area.x+event->area.width); + + /* exposure events on the sheet */ + + if(event->window == sheet->row_title_window && sheet->row_titles_visible){ + size_allocate_row_title_buttons(sheet); + gdk_window_show(sheet->row_title_window); + } + + if(event->window == sheet->column_title_window && sheet->column_titles_visible){ + size_allocate_column_title_buttons(sheet); + gdk_window_show(sheet->column_title_window); + } + + if (event->window == sheet->sheet_window){ + gtk_sheet_draw_backing_pixmap(sheet, range); + + if(sheet->state != GTK_SHEET_NORMAL){ + if(gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_draw_backing_pixmap(sheet, sheet->range); + if(GTK_SHEET_IN_RESIZE(sheet) || GTK_SHEET_IN_DRAG(sheet)) + gtk_sheet_draw_backing_pixmap(sheet, sheet->drag_range); + + if(gtk_sheet_range_isvisible(sheet, sheet->range)) + gtk_sheet_range_draw_selection(sheet, sheet->range); + if(GTK_SHEET_IN_RESIZE(sheet) || GTK_SHEET_IN_DRAG(sheet)) + draw_xor_rectangle(sheet, sheet->drag_range); + } + + if((!GTK_SHEET_IN_XDRAG(sheet)) && (!GTK_SHEET_IN_YDRAG(sheet))){ + if(sheet->state == GTK_SHEET_NORMAL){ + gtk_sheet_draw_active_cell(sheet); + if(!GTK_SHEET_IN_SELECTION(sheet)) + gtk_widget_queue_draw(sheet->sheet_entry); + } + } + + + } + + } + + if(sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION(sheet)) + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); + + return FALSE; +} + + +static gint +gtk_sheet_button_press (GtkWidget * widget, + GdkEventButton * event) +{ + GtkSheet *sheet; + GdkModifierType mods; + gint x, y, row, column; + gboolean veto; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if(event->type != GDK_BUTTON_PRESS) return TRUE; + gdk_window_get_pointer(widget->window, NULL, NULL, &mods); + if(!(mods & GDK_BUTTON1_MASK)) return TRUE; + + sheet = GTK_SHEET (widget); + + /* press on resize windows */ + if (event->window == sheet->column_title_window && + gtk_sheet_columns_resizable(sheet)) + { + gtk_widget_get_pointer (widget, &sheet->x_drag, NULL); + if(POSSIBLE_XDRAG(sheet, sheet->x_drag, &sheet->drag_cell.col)){ + guint req; + gtk_sheet_column_size_request(sheet, sheet->drag_cell.col, &req); + GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG); + gdk_pointer_grab (sheet->column_title_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + + draw_xor_vline (sheet); + return TRUE; + } + } + + if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable(sheet)) + { + gtk_widget_get_pointer (widget, NULL, &sheet->y_drag); + + if(POSSIBLE_YDRAG(sheet, sheet->y_drag, &sheet->drag_cell.row)){ + guint req; + gtk_sheet_row_size_request(sheet, sheet->drag_cell.row, &req); + GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG); + gdk_pointer_grab (sheet->row_title_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + + draw_xor_hline (sheet); + return TRUE; + } + } + + /* selections on the sheet */ + if(event->window == sheet->sheet_window){ + gtk_widget_get_pointer (widget, &x, &y); + gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + gdk_pointer_grab (sheet->sheet_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + gtk_grab_add(GTK_WIDGET(sheet)); + sheet->timer=gtk_timeout_add(TIMEOUT_SCROLL, gtk_sheet_scroll, sheet); + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_SHEET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + if(sheet->selection_mode != GTK_SELECTION_SINGLE && + sheet->cursor_drag->type==GDK_SIZING && + !GTK_SHEET_IN_SELECTION(sheet) && !GTK_SHEET_IN_RESIZE(sheet)){ + if(sheet->state==GTK_STATE_NORMAL) { + row=sheet->active_cell.row; + column=sheet->active_cell.col; + if(!gtk_sheet_deactivate_cell(sheet)) return FALSE; + sheet->active_cell.row=row; + sheet->active_cell.col=column; + sheet->drag_range=sheet->range; + sheet->state=GTK_SHEET_RANGE_SELECTED; + gtk_sheet_select_range(sheet, &sheet->drag_range); + } + sheet->x_drag=x; + sheet->y_drag=y; + if(row > sheet->range.rowi) row--; + if(column > sheet->range.coli) column--; + sheet->drag_cell.row = row; + sheet->drag_cell.col = column; + sheet->drag_range=sheet->range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_RESIZE); + } + else if(sheet->cursor_drag->type==GDK_TOP_LEFT_ARROW && + !GTK_SHEET_IN_SELECTION(sheet) && !GTK_SHEET_IN_DRAG(sheet)) { + if(sheet->state==GTK_STATE_NORMAL) { + row=sheet->active_cell.row; + column=sheet->active_cell.col; + if(!gtk_sheet_deactivate_cell(sheet)) return FALSE; + sheet->active_cell.row=row; + sheet->active_cell.col=column; + sheet->drag_range=sheet->range; + sheet->state=GTK_SHEET_RANGE_SELECTED; + gtk_sheet_select_range(sheet, &sheet->drag_range); + } + sheet->x_drag=x; + sheet->y_drag=y; + if(row < sheet->range.row0) row++; + if(row > sheet->range.rowi) row--; + if(column < sheet->range.col0) column++; + if(column > sheet->range.coli) column--; + sheet->drag_cell.row=row; + sheet->drag_cell.col=column; + sheet->drag_range=sheet->range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_DRAG); + } + else + { + gtk_sheet_click_cell(sheet, row, column, &veto); + if(veto) GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + } + + } + + if(event->window == sheet->column_title_window){ + gtk_widget_get_pointer (widget, &x, &y); + column = COLUMN_FROM_XPIXEL(sheet, x); + if(sheet->column[column].is_sensitive){; + gtk_sheet_click_cell(sheet, -1, column, &veto); + gtk_grab_add(GTK_WIDGET(sheet)); + sheet->timer=gtk_timeout_add(TIMEOUT_SCROLL, gtk_sheet_scroll, sheet); + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_SHEET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + } + } + + if(event->window == sheet->row_title_window){ + gtk_widget_get_pointer (widget, &x, &y); + row = ROW_FROM_YPIXEL(sheet, y); + if(sheet->row[row].is_sensitive){ + gtk_sheet_click_cell(sheet, row, -1, &veto); + gtk_grab_add(GTK_WIDGET(sheet)); + sheet->timer=gtk_timeout_add(TIMEOUT_SCROLL, gtk_sheet_scroll, sheet); + GTK_WIDGET_UNSET_FLAGS(sheet->sheet_entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_SHEET(sheet), GTK_HAS_FOCUS); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + } + } + + return TRUE; +} + +static gint +gtk_sheet_scroll(gpointer data) +{ + GtkSheet *sheet; + gint x,y,row,column; + gint move; + + sheet=GTK_SHEET(data); + + GDK_THREADS_ENTER(); + + gtk_widget_get_pointer (GTK_WIDGET(sheet), &x, &y); + gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + + move=TRUE; + + if(GTK_SHEET_IN_SELECTION(sheet)) + gtk_sheet_extend_selection(sheet, row, column); + + if(GTK_SHEET_IN_DRAG(sheet) || GTK_SHEET_IN_RESIZE(sheet)){ + move=gtk_sheet_move_query(sheet, row, column); + if(move) draw_xor_rectangle(sheet, sheet->drag_range); + } + + GDK_THREADS_LEAVE(); + + return TRUE; + +} + +static void +gtk_sheet_click_cell(GtkSheet *sheet, gint row, gint column, gboolean *veto) +{ + *veto = TRUE; + + if(row > sheet->maxrow || column > sheet->maxcol){ + *veto = FALSE; + return; + } + + if(column >= 0 && row >= 0) + if(!sheet->column[column].is_visible || !sheet->row[row].is_visible) + { + *veto = FALSE; + return; + } + + _gtkextra_signal_emit(GTK_OBJECT(sheet), sheet_signals[TRAVERSE], + sheet->active_cell.row, sheet->active_cell.col, + &row, &column, veto); + + if(!*veto){ + if(sheet->state == GTK_STATE_NORMAL) return; + + row = sheet->active_cell.row; + column = sheet->active_cell.col; + gtk_sheet_activate_cell(sheet, row, column); + return; + } + + if(row == -1 && column >= 0){ + if(gtk_sheet_autoscroll(sheet)) + gtk_sheet_move_query(sheet, row, column); + gtk_sheet_select_column(sheet, column); + return; + } + if(column == -1 && row >= 0){ + if(gtk_sheet_autoscroll(sheet)) + gtk_sheet_move_query(sheet, row, column); + gtk_sheet_select_row(sheet, row); + return; + } + + if(row==-1 && column ==-1){ + sheet->range.row0=0; + sheet->range.col0=0; + sheet->range.rowi=sheet->maxrow; + sheet->range.coli=sheet->maxcol; + sheet->active_cell.row=0; + sheet->active_cell.col=0; + gtk_sheet_select_range(sheet, NULL); + return; + } + + if(row!=-1 && column !=-1){ + if(sheet->state != GTK_SHEET_NORMAL){ + sheet->state = GTK_SHEET_NORMAL; + gtk_sheet_real_unselect_range(sheet, NULL); + } + else + { + if(!gtk_sheet_deactivate_cell(sheet)){ + *veto = FALSE; + return; + } + } + + if(gtk_sheet_autoscroll(sheet)) + gtk_sheet_move_query(sheet, row, column); + sheet->active_cell.row=row; + sheet->active_cell.col=column; + sheet->selection_cell.row=row; + sheet->selection_cell.col=column; + sheet->range.row0=row; + sheet->range.col0=column; + sheet->range.rowi=row; + sheet->range.coli=column; + sheet->state=GTK_SHEET_NORMAL; + GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + gtk_sheet_draw_active_cell(sheet); + return; + } + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); +} + +static gint +gtk_sheet_button_release (GtkWidget * widget, + GdkEventButton * event) +{ + GtkSheet *sheet; + gint x,y; + + sheet=GTK_SHEET(widget); + + /* release on resize windows */ + if (GTK_SHEET_IN_XDRAG (sheet)){ + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG); + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); + gtk_widget_get_pointer (widget, &x, NULL); + gdk_pointer_ungrab (event->time); + draw_xor_vline (sheet); + + gtk_sheet_set_column_width (sheet, sheet->drag_cell.col, new_column_width (sheet, sheet->drag_cell.col, &x)); + sheet->old_hadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), "value_changed"); + return TRUE; + } + + if (GTK_SHEET_IN_YDRAG (sheet)){ + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG); + GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); + gtk_widget_get_pointer (widget, NULL, &y); + gdk_pointer_ungrab (event->time); + draw_xor_hline (sheet); + + gtk_sheet_set_row_height (sheet, sheet->drag_cell.row, new_row_height (sheet, sheet->drag_cell.row, &y)); + sheet->old_vadjustment = -1.; + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), "value_changed"); + return TRUE; + } + + + if (GTK_SHEET_IN_DRAG(sheet)){ + GtkSheetRange old_range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_DRAG); + gdk_pointer_ungrab (event->time); + + gtk_sheet_real_unselect_range(sheet, NULL); + + sheet->active_cell.row = sheet->active_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->active_cell.col = sheet->active_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + sheet->selection_cell.row = sheet->selection_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->selection_cell.col = sheet->selection_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + old_range=sheet->range; + sheet->range=sheet->drag_range; + sheet->drag_range=old_range; + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[MOVE_RANGE], + &sheet->drag_range, &sheet->range); + gtk_sheet_select_range(sheet, &sheet->range); + } + + if (GTK_SHEET_IN_RESIZE(sheet)){ + GtkSheetRange old_range; + draw_xor_rectangle(sheet, sheet->drag_range); + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_RESIZE); + gdk_pointer_ungrab (event->time); + + gtk_sheet_real_unselect_range(sheet, NULL); + + sheet->active_cell.row = sheet->active_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->active_cell.col = sheet->active_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + if(sheet->drag_range.row0 < sheet->range.row0) + sheet->selection_cell.row = sheet->drag_range.row0; + if(sheet->drag_range.rowi >= sheet->range.rowi) + sheet->selection_cell.row = sheet->drag_range.rowi; + if(sheet->drag_range.col0 < sheet->range.col0) + sheet->selection_cell.col = sheet->drag_range.col0; + if(sheet->drag_range.coli >= sheet->range.coli) + sheet->selection_cell.col = sheet->drag_range.coli; + old_range = sheet->range; + sheet->range = sheet->drag_range; + sheet->drag_range = old_range; + + if(sheet->state==GTK_STATE_NORMAL) sheet->state=GTK_SHEET_RANGE_SELECTED; + gtk_signal_emit(GTK_OBJECT(sheet),sheet_signals[RESIZE_RANGE], + &sheet->drag_range, &sheet->range); + gtk_sheet_select_range(sheet, &sheet->range); + } + + if(sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION(sheet)){ + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + gdk_pointer_ungrab (event->time); + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + } + + if(GTK_SHEET_IN_SELECTION) + gdk_pointer_ungrab (event->time); + if(sheet->timer) + gtk_timeout_remove(sheet->timer); + gtk_grab_remove(GTK_WIDGET(sheet)); + + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + + return TRUE; +} + +static gint +gtk_sheet_motion (GtkWidget * widget, + GdkEventMotion * event) +{ + GtkSheet *sheet; + GdkModifierType mods; + GdkCursorType new_cursor; + gint x, y, row, column; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + + sheet = GTK_SHEET (widget); + + + /* selections on the sheet */ + x = event->x; + y = event->y; + + if(event->window == sheet->column_title_window && gtk_sheet_columns_resizable(sheet)){ + gtk_widget_get_pointer(widget, &x, &y); + if(!GTK_SHEET_IN_SELECTION(sheet) && POSSIBLE_XDRAG(sheet, x, &column)){ + new_cursor=GDK_SB_H_DOUBLE_ARROW; + if(new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW); + gdk_window_set_cursor(sheet->column_title_window,sheet->cursor_drag); + } + }else{ + new_cursor=GDK_TOP_LEFT_ARROW; + if(!GTK_SHEET_IN_XDRAG(sheet) && new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor(sheet->column_title_window,sheet->cursor_drag); + } + } + } + + if(event->window == sheet->row_title_window && gtk_sheet_rows_resizable(sheet)){ + gtk_widget_get_pointer(widget, &x, &y); + if(!GTK_SHEET_IN_SELECTION(sheet) && POSSIBLE_YDRAG(sheet,y, &column)){ + new_cursor=GDK_SB_V_DOUBLE_ARROW; + if(new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW); + gdk_window_set_cursor(sheet->row_title_window,sheet->cursor_drag); + } + }else{ + new_cursor=GDK_TOP_LEFT_ARROW; + if(!GTK_SHEET_IN_YDRAG(sheet) && new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor(sheet->row_title_window,sheet->cursor_drag); + } + } + } + + new_cursor=GDK_PLUS; + if(!POSSIBLE_DRAG(sheet,x,y,&row,&column) && !GTK_SHEET_IN_DRAG(sheet) && + !POSSIBLE_RESIZE(sheet,x,y,&row,&column) && !GTK_SHEET_IN_RESIZE(sheet) && + event->window == sheet->sheet_window && + new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_PLUS); + gdk_window_set_cursor(sheet->sheet_window,sheet->cursor_drag); + } + + new_cursor=GDK_TOP_LEFT_ARROW; + if(!(POSSIBLE_RESIZE(sheet,x,y,&row,&column) || GTK_SHEET_IN_RESIZE(sheet)) && + (POSSIBLE_DRAG(sheet, x,y,&row,&column) || GTK_SHEET_IN_DRAG(sheet)) && + event->window == sheet->sheet_window && + new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor(sheet->sheet_window,sheet->cursor_drag); + } + + new_cursor=GDK_SIZING; + if(!GTK_SHEET_IN_DRAG(sheet) && + (POSSIBLE_RESIZE(sheet,x,y,&row,&column) || GTK_SHEET_IN_RESIZE(sheet)) && + event->window == sheet->sheet_window && + new_cursor != sheet->cursor_drag->type){ + gdk_cursor_destroy(sheet->cursor_drag); + sheet->cursor_drag=gdk_cursor_new(GDK_SIZING); + gdk_window_set_cursor(sheet->sheet_window,sheet->cursor_drag); + } + + gdk_window_get_pointer (widget->window, &x, &y, &mods); + if(!(mods & GDK_BUTTON1_MASK)) return FALSE; + + if (GTK_SHEET_IN_XDRAG (sheet)){ + if (event->is_hint || event->window != widget->window) + gtk_widget_get_pointer (widget, &x, NULL); + else + x = event->x; + + new_column_width (sheet, sheet->drag_cell.col, &x); + if (x != sheet->x_drag) + { + draw_xor_vline (sheet); + sheet->x_drag = x; + draw_xor_vline (sheet); + } + return TRUE; + } + + if (GTK_SHEET_IN_YDRAG (sheet)){ + if (event->is_hint || event->window != widget->window) + gtk_widget_get_pointer (widget, NULL, &y); + else + y = event->y; + + new_row_height (sheet, sheet->drag_cell.row, &y); + if (y != sheet->y_drag) + { + draw_xor_hline (sheet); + sheet->y_drag = y; + draw_xor_hline (sheet); + } + return TRUE; + } + + if (GTK_SHEET_IN_DRAG(sheet)){ + GtkSheetRange aux; + column=COLUMN_FROM_XPIXEL(sheet,x)-sheet->drag_cell.col; + row=ROW_FROM_YPIXEL(sheet,y)-sheet->drag_cell.row; + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) row=0; + if(sheet->state==GTK_SHEET_ROW_SELECTED) column=0; + sheet->x_drag=x; + sheet->y_drag=y; + aux=sheet->range; + if(aux.row0+row >= 0 && aux.rowi+row <= sheet->maxrow && + aux.col0+column >= 0 && aux.coli+column <= sheet->maxcol){ + aux=sheet->drag_range; + sheet->drag_range.row0=sheet->range.row0+row; + sheet->drag_range.col0=sheet->range.col0+column; + sheet->drag_range.rowi=sheet->range.rowi+row; + sheet->drag_range.coli=sheet->range.coli+column; + if(aux.row0 != sheet->drag_range.row0 || + aux.col0 != sheet->drag_range.col0){ + draw_xor_rectangle (sheet, aux); + draw_xor_rectangle (sheet, sheet->drag_range); + } + } + return TRUE; + } + + if (GTK_SHEET_IN_RESIZE(sheet)){ + GtkSheetRange aux; + gint v_h; + v_h=1; + if(abs(x-COLUMN_LEFT_XPIXEL(sheet,sheet->drag_cell.col)) > + abs(y-ROW_TOP_YPIXEL(sheet,sheet->drag_cell.row))) v_h=2; + + column=COLUMN_FROM_XPIXEL(sheet,x)-sheet->drag_cell.col; + row=ROW_FROM_YPIXEL(sheet,y)-sheet->drag_cell.row; + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) row=0; + if(sheet->state==GTK_SHEET_ROW_SELECTED) column=0; + sheet->x_drag=x; + sheet->y_drag=y; + aux=sheet->range; + + if(row < sheet->range.row0 - sheet->range.rowi - 1) + row=row+(sheet->range.rowi-sheet->range.row0 + 1); + else if(row<0) row=0; + + if(column < sheet->range.col0 - sheet->range.coli - 1) + column=column+(sheet->range.coli-sheet->range.col0 + 1); + else if(column<0) column=0; + + if(v_h==1) + column=0; + else + row=0; + + if(aux.row0+row >= 0 && aux.rowi+row <= sheet->maxrow && + aux.col0+column >= 0 && aux.coli+column <= sheet->maxcol){ + + aux=sheet->drag_range; + sheet->drag_range=sheet->range; + + if(row<0) sheet->drag_range.row0=sheet->range.row0+row; + if(row>0) sheet->drag_range.rowi=sheet->range.rowi+row; + if(column<0) sheet->drag_range.col0=sheet->range.col0+column; + if(column>0) sheet->drag_range.coli=sheet->range.coli+column; + + if(aux.row0 != sheet->drag_range.row0 || + aux.rowi != sheet->drag_range.rowi || + aux.col0 != sheet->drag_range.col0 || + aux.coli != sheet->drag_range.coli){ + draw_xor_rectangle (sheet, aux); + draw_xor_rectangle (sheet, sheet->drag_range); + } + } + return TRUE; + } + + + + gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + + if(sheet->state==GTK_SHEET_NORMAL && row==sheet->active_cell.row && + column==sheet->active_cell.col) return TRUE; + + if(GTK_SHEET_IN_SELECTION(sheet) && mods&GDK_BUTTON1_MASK) + gtk_sheet_extend_selection(sheet, row, column); + + return TRUE; +} + +static gint +gtk_sheet_move_query(GtkSheet *sheet, gint row, gint column) +{ + gint row_move, column_move; + gfloat row_align, col_align; + guint height, width; + gint new_row = row; + gint new_col = column; + + row_move=FALSE; + column_move=FALSE; + row_align=-1.; + col_align=-1.; + + height = sheet->sheet_window_height; + width = sheet->sheet_window_width; + + if(row>=MAX_VISIBLE_ROW(sheet) && sheet->state!=GTK_SHEET_COLUMN_SELECTED) { + row_align = 1.; + new_row = MIN(sheet->maxrow, row + 1); + row_move = TRUE; + if(MAX_VISIBLE_ROW(sheet) == sheet->maxrow && + ROW_TOP_YPIXEL(sheet, sheet->maxrow) + + sheet->row[sheet->maxrow].height < height){ + row_move = FALSE; + row_align = -1.; + } + } + if(row<MIN_VISIBLE_ROW(sheet) && sheet->state!=GTK_SHEET_COLUMN_SELECTED) { + row_align= 0.; + row_move = TRUE; + } + if(column>=MAX_VISIBLE_COLUMN(sheet) && sheet->state!=GTK_SHEET_ROW_SELECTED) { + col_align = 1.; + new_col = MIN(sheet->maxcol, column + 1); + column_move = TRUE; + if(MAX_VISIBLE_COLUMN(sheet) == sheet->maxcol && + COLUMN_LEFT_XPIXEL(sheet, sheet->maxcol) + + sheet->column[sheet->maxcol].width < width){ + column_move = FALSE; + col_align = -1.; + } + } + if(column<MIN_VISIBLE_COLUMN(sheet) && sheet->state!=GTK_SHEET_ROW_SELECTED) { + col_align = 0.; + column_move = TRUE; + } + + if(row_move || column_move){ + gtk_sheet_moveto(sheet, new_row, new_col, row_align, col_align); + } + + return(row_move || column_move); +} + +static void +gtk_sheet_extend_selection(GtkSheet *sheet, gint row, gint column) +{ + GtkSheetRange range; + gint state; + gint r,c; + + if(row == sheet->selection_cell.row && column == sheet->selection_cell.col) + return; + + if(sheet->selection_mode == GTK_SELECTION_SINGLE) return; + + gtk_sheet_move_query(sheet, row, column); + gtk_widget_grab_focus(GTK_WIDGET(sheet)); + + if(GTK_SHEET_IN_DRAG(sheet)) return; + + state=sheet->state; + + switch(sheet->state){ + case GTK_SHEET_ROW_SELECTED: + column = sheet->maxcol; + break; + case GTK_SHEET_COLUMN_SELECTED: + row = sheet->maxrow; + break; + case GTK_SHEET_NORMAL: + sheet->state=GTK_SHEET_RANGE_SELECTED; + r=sheet->active_cell.row; + c=sheet->active_cell.col; + sheet->range.col0=c; + sheet->range.row0=r; + sheet->range.coli=c; + sheet->range.rowi=r; + gdk_draw_pixmap(sheet->sheet_window, + GTK_WIDGET(sheet)->style->fg_gc[GTK_STATE_NORMAL], + sheet->pixmap, + COLUMN_LEFT_XPIXEL(sheet,c)-1, + ROW_TOP_YPIXEL(sheet,r)-1, + COLUMN_LEFT_XPIXEL(sheet,c)-1, + ROW_TOP_YPIXEL(sheet,r)-1, + sheet->column[c].width+4, + sheet->row[r].height+4); + gtk_sheet_range_draw_selection(sheet, sheet->range); + case GTK_SHEET_RANGE_SELECTED: + sheet->state=GTK_SHEET_RANGE_SELECTED; + } + + sheet->selection_cell.row = row; + sheet->selection_cell.col = column; + + range.col0=MIN(column,sheet->active_cell.col); + range.coli=MAX(column,sheet->active_cell.col); + range.row0=MIN(row,sheet->active_cell.row); + range.rowi=MAX(row,sheet->active_cell.row); + + if(range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi || + range.col0 != sheet->range.col0 || range.coli != sheet->range.coli || + state==GTK_SHEET_NORMAL) + gtk_sheet_real_select_range(sheet, &range); + +} + +#if 0 +static gint +gtk_sheet_entry_key_press(GtkWidget *widget, + GdkEventKey *key) +{ + gboolean focus; + gtk_signal_emit_by_name(GTK_OBJECT(widget), "key_press_event", key, &focus); + return focus; +} +#endif + +static gint +gtk_sheet_key_press(GtkWidget *widget, + GdkEventKey *key) +{ + GtkSheet *sheet; + gint row, col; + gint state; + gboolean extend_selection = FALSE; + gboolean force_move = FALSE; + gboolean in_selection = FALSE; + gboolean veto = TRUE; + gint scroll = 1; + + sheet = GTK_SHEET(widget); + + if(key->state & GDK_CONTROL_MASK || key->keyval==GDK_Control_L || + key->keyval==GDK_Control_R) return FALSE; + +/* + { + if(key->keyval=='c' || key->keyval == 'C' && sheet->state != GTK_STATE_NORMAL) + gtk_sheet_clip_range(sheet, sheet->range); + if(key->keyval=='x' || key->keyval == 'X') + gtk_sheet_unclip_range(sheet); + return FALSE; + } +*/ + + extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval==GDK_Shift_L +|| key->keyval==GDK_Shift_R; + + state=sheet->state; + in_selection = GTK_SHEET_IN_SELECTION(sheet); + GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + + switch(key->keyval){ + case GDK_Return: case GDK_KP_Enter: + if(sheet->state == GTK_SHEET_NORMAL && + !GTK_SHEET_IN_SELECTION(sheet)) + gtk_signal_emit_stop_by_name(GTK_OBJECT(gtk_sheet_get_entry(sheet)), + "key_press_event"); + row = sheet->active_cell.row; + col = sheet->active_cell.col; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet)-1; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet); + if(row < sheet->maxrow){ + row = row + scroll; + while(!sheet->row[row].is_visible && row<sheet->maxrow) row++; + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_ISO_Left_Tab: + row = sheet->active_cell.row; + col = sheet->active_cell.col; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + if(col > 0){ + col = col - scroll; + while(!sheet->column[col].is_visible && col>0) col--; + col=MAX(0, col); + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_Tab: + row = sheet->active_cell.row; + col = sheet->active_cell.col; + if(sheet->state == GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(sheet->state == GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + if(col < sheet->maxcol){ + col = col + scroll; + while(!sheet->column[col].is_visible && col<sheet->maxcol) col++; + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; +/* case GDK_BackSpace: + if(sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0){ + if(sheet->active_cell.col > 0){ + col = sheet->active_cell.col - scroll; + row = sheet->active_cell.row; + while(!sheet->column[col].is_visible && col > 0) col--; + } + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; +*/ + case GDK_Page_Up: + scroll=MAX_VISIBLE_ROW(sheet)-MIN_VISIBLE_ROW(sheet)+1; + case GDK_Up: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.row > 0){ + row = sheet->selection_cell.row - scroll; + while(!sheet->row[row].is_visible && row > 0) row--; + row = MAX(0, row); + gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col); + } + return TRUE; + } + col = sheet->active_cell.col; + row = sheet->active_cell.row; + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet); + row = row - scroll; + while(!sheet->row[row].is_visible && row > 0) row--; + row = MAX(0,row); + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_Page_Down: + scroll=MAX_VISIBLE_ROW(sheet)-MIN_VISIBLE_ROW(sheet)+1; + case GDK_Down: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.row < sheet->maxrow){ + row = sheet->selection_cell.row + scroll; + while(!sheet->row[row].is_visible && row < sheet->maxrow) row++; + row = MIN(sheet->maxrow, row); + gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col); + } + return TRUE; + } + col = sheet->active_cell.col; + row = sheet->active_cell.row; + if(sheet->active_cell.row < sheet->maxrow){ + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet)-1; + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet); + row = row + scroll; + while(!sheet->row[row].is_visible && row < sheet->maxrow) row++; + row = MIN(sheet->maxrow, row); + } + gtk_sheet_click_cell(sheet, row, col, &veto); + extend_selection = FALSE; + break; + case GDK_Right: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.col < sheet->maxcol){ + col = sheet->selection_cell.col + 1; + while(!sheet->column[col].is_visible && col < sheet->maxcol) col++; + gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col); + } + return TRUE; + } + col = sheet->active_cell.col; + row = sheet->active_cell.row; + if(sheet->active_cell.col < sheet->maxcol){ + col ++; + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + while(!sheet->column[col].is_visible && col < sheet->maxcol) col++; + if(strlen(gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet)))) == 0 + || force_move) { + gtk_sheet_click_cell(sheet, row, col, &veto); + } + else + return FALSE; + } + extend_selection = FALSE; + break; + case GDK_Left: + if(extend_selection){ + if(state==GTK_STATE_NORMAL){ + row=sheet->active_cell.row; + col=sheet->active_cell.col; + gtk_sheet_click_cell(sheet, row, col, &veto); + if(!veto) break; + } + if(sheet->selection_cell.col > 0){ + col = sheet->selection_cell.col - 1; + while(!sheet->column[col].is_visible && col > 0) col--; + gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col); + } + return TRUE; + } + col = sheet->active_cell.col - 1; + row = sheet->active_cell.row; + if(state==GTK_SHEET_ROW_SELECTED) + col = MIN_VISIBLE_COLUMN(sheet)-1; + if(state==GTK_SHEET_COLUMN_SELECTED) + row = MIN_VISIBLE_ROW(sheet); + while(!sheet->column[col].is_visible && col > 0) col--; + col = MAX(0, col); + + if(strlen(gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet)))) == 0 + || force_move){ + gtk_sheet_click_cell(sheet, row, col, &veto); + } + else + return FALSE; + extend_selection = FALSE; + break; + case GDK_Home: + row=0; + while(!sheet->row[row].is_visible && row < sheet->maxrow) row++; + gtk_sheet_click_cell(sheet, row, sheet->active_cell.col, &veto); + extend_selection = FALSE; + break; + case GDK_End: + row=sheet->maxrow; + while(!sheet->row[row].is_visible && row > 0) row--; + gtk_sheet_click_cell(sheet, row, sheet->active_cell.col, &veto); + extend_selection = FALSE; + break; + default: + if(in_selection) GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION); + if(extend_selection) return TRUE; + if(state == GTK_SHEET_ROW_SELECTED) + sheet->active_cell.col=MIN_VISIBLE_COLUMN(sheet); + if(state == GTK_SHEET_COLUMN_SELECTED) + sheet->active_cell.row=MIN_VISIBLE_ROW(sheet); + return FALSE; + } + + if(extend_selection) return TRUE; + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + + return TRUE; +} + +static void +gtk_sheet_size_request (GtkWidget * widget, + GtkRequisition * requisition) +{ + GtkSheet *sheet; + GList *children; + GtkSheetChild *child; + GtkRequisition child_requisition; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + g_return_if_fail (requisition != NULL); + + sheet = GTK_SHEET (widget); + + requisition->width = 3*DEFAULT_COLUMN_WIDTH; + requisition->height = 3*DEFAULT_ROW_HEIGHT(widget); + + /* compute the size of the column title area */ + if(sheet->column_titles_visible) + requisition->height += sheet->column_title_area.height; + + /* compute the size of the row title area */ + if(sheet->row_titles_visible) + requisition->width += sheet->row_title_area.width; + + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + + if(!sheet->column_titles_visible) + sheet->view.row0=ROW_FROM_YPIXEL(sheet, 1); + + if(!sheet->row_titles_visible) + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, 1); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + gtk_widget_size_request(child->widget, &child_requisition); + } +} + + +static void +gtk_sheet_size_allocate (GtkWidget * widget, + GtkAllocation * allocation) +{ + GtkSheet *sheet; + GtkAllocation sheet_allocation; + gint border_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_SHEET (widget)); + g_return_if_fail (allocation != NULL); + + sheet = GTK_SHEET (widget); + widget->allocation = *allocation; + border_width = GTK_CONTAINER(widget)->border_width; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (widget->window, + allocation->x + border_width, + allocation->y + border_width, + allocation->width - 2*border_width, + allocation->height - 2*border_width); + + /* use internal allocation structure for all the math + * because it's easier than always subtracting the container + * border width */ + sheet->internal_allocation.x = 0; + sheet->internal_allocation.y = 0; + sheet->internal_allocation.width = allocation->width - 2*border_width; + sheet->internal_allocation.height = allocation->height - 2*border_width; + + sheet_allocation.x = 0; + sheet_allocation.y = 0; + sheet_allocation.width = allocation->width - 2*border_width; + sheet_allocation.height = allocation->height - 2*border_width; + + sheet->sheet_window_width = sheet_allocation.width; + sheet->sheet_window_height = sheet_allocation.height; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (sheet->sheet_window, + sheet_allocation.x, + sheet_allocation.y, + sheet_allocation.width, + sheet_allocation.height); + + /* position the window which holds the column title buttons */ + sheet->column_title_area.x = 0; + sheet->column_title_area.y = 0; + if(sheet->row_titles_visible) + sheet->column_title_area.x = sheet->row_title_area.width; + sheet->column_title_area.width = sheet_allocation.width - + sheet->column_title_area.x; + if(GTK_WIDGET_REALIZED(widget) && sheet->column_titles_visible) + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + + sheet->sheet_window_width = sheet_allocation.width; + sheet->sheet_window_height = sheet_allocation.height; + + /* column button allocation */ + size_allocate_column_title_buttons (sheet); + + /* position the window which holds the row title buttons */ + sheet->row_title_area.x = 0; + sheet->row_title_area.y = 0; + if(sheet->column_titles_visible) + sheet->row_title_area.y = sheet->column_title_area.height; + sheet->row_title_area.height = sheet_allocation.height - + sheet->row_title_area.y; + + if(GTK_WIDGET_REALIZED(widget) && sheet->row_titles_visible) + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + + + /* row button allocation */ + size_allocate_row_title_buttons (sheet); + + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + + if(!sheet->column_titles_visible) + sheet->view.row0=ROW_FROM_YPIXEL(sheet, 1); + + if(!sheet->row_titles_visible) + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, 1); + + size_allocate_column_title_buttons(sheet); + size_allocate_row_title_buttons(sheet); + + /* re-scale backing pixmap */ + gtk_sheet_make_backing_pixmap(sheet, 0, 0); + gtk_sheet_position_children(sheet); + + /* set the scrollbars adjustments */ + adjust_scrollbars (sheet); +} + +static void +size_allocate_column_title_buttons (GtkSheet * sheet) +{ + gint i; + gint x,width; + + if (!sheet->column_titles_visible) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + width = sheet->sheet_window_width; + x = 0; + + if(sheet->row_titles_visible) + { + width -= sheet->row_title_area.width; + x = sheet->row_title_area.width; + } + + if(sheet->column_title_area.width != width || sheet->column_title_area.x != x) + { + sheet->column_title_area.width = width; + sheet->column_title_area.x = x; + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + } + + + if(MAX_VISIBLE_COLUMN(sheet) == sheet->maxcol) + gdk_window_clear_area (sheet->column_title_window, + 0,0, + sheet->column_title_area.width, + sheet->column_title_area.height); + + if(!GTK_WIDGET_DRAWABLE(sheet)) return; + + for (i = MIN_VISIBLE_COLUMN(sheet); i <= MAX_VISIBLE_COLUMN(sheet); i++) + gtk_sheet_button_draw(sheet,-1,i); +} + +static void +size_allocate_row_title_buttons (GtkSheet * sheet) +{ + gint i; + gint y, height; + + if (!sheet->row_titles_visible) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + height = sheet->sheet_window_height; + y = 0; + + if(sheet->column_titles_visible) + { + height -= sheet->column_title_area.height; + y = sheet->column_title_area.height; + } + + if(sheet->row_title_area.height != height || sheet->row_title_area.y != y){ + sheet->row_title_area.y = y; + sheet->row_title_area.height = height; + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + } + if(MAX_VISIBLE_ROW(sheet) == sheet->maxrow) + gdk_window_clear_area (sheet->row_title_window, + 0,0, + sheet->row_title_area.width, + sheet->row_title_area.height); + + if(!GTK_WIDGET_DRAWABLE(sheet)) return; + + for(i = MIN_VISIBLE_ROW(sheet); i <= MAX_VISIBLE_ROW(sheet); i++) + gtk_sheet_button_draw(sheet,i,-1); +} + +static void +gtk_sheet_recalc_top_ypixels(GtkSheet *sheet, gint row) +{ + gint i, cy; + + cy = sheet->column_title_area.height; + if(!sheet->column_titles_visible) cy = 0; + for(i=0; i<=sheet->maxrow; i++){ + sheet->row[i].top_ypixel=cy; + if(sheet->row[i].is_visible) cy+=sheet->row[i].height; + } +} + +static void +gtk_sheet_recalc_left_xpixels(GtkSheet *sheet, gint column) +{ + gint i, cx; + + cx = sheet->row_title_area.width; + if(!sheet->row_titles_visible) cx = 0; + for(i=0; i<=sheet->maxcol; i++){ + sheet->column[i].left_xpixel=cx; + if(sheet->column[i].is_visible) cx+=sheet->column[i].width; + } + +} + + + +static void +gtk_sheet_size_allocate_entry(GtkSheet *sheet) +{ + GtkAllocation shentry_allocation; + GtkSheetCellAttr attributes; + GtkEntry *sheet_entry; + GtkStyle *style = NULL, *previous_style = NULL; + gint row, col; + gint size, max_size, text_size, column_width; + const gchar *text; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + if(!GTK_WIDGET_MAPPED(GTK_WIDGET(sheet))) return; + + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry(sheet)); + + gtk_sheet_get_attributes(sheet, sheet->active_cell.row, sheet->active_cell.col, &attributes); + + if(GTK_WIDGET_REALIZED(sheet->sheet_entry)){ + + if(!GTK_WIDGET(sheet_entry)->style) + gtk_widget_ensure_style(GTK_WIDGET(sheet_entry)); + + previous_style = GTK_WIDGET(sheet_entry)->style; + + style = gtk_style_copy(previous_style); + style->bg[GTK_STATE_NORMAL] = attributes.background; + style->fg[GTK_STATE_NORMAL] = attributes.foreground; + style->text[GTK_STATE_NORMAL] = attributes.foreground; + style->bg[GTK_STATE_ACTIVE] = attributes.background; + style->fg[GTK_STATE_ACTIVE] = attributes.foreground; + style->text[GTK_STATE_ACTIVE] = attributes.foreground; + + pango_font_description_free(style->font_desc); + style->font_desc = pango_font_description_copy(attributes.font_desc); + + GTK_WIDGET(sheet_entry)->style = style; + gtk_widget_size_request(sheet->sheet_entry, NULL); + GTK_WIDGET(sheet_entry)->style = previous_style; + + if(style != previous_style){ + if(!GTK_IS_ITEM_ENTRY(sheet->sheet_entry)){ + style->bg[GTK_STATE_NORMAL] = previous_style->bg[GTK_STATE_NORMAL]; + style->fg[GTK_STATE_NORMAL] = previous_style->fg[GTK_STATE_NORMAL]; + style->bg[GTK_STATE_ACTIVE] = previous_style->bg[GTK_STATE_ACTIVE]; + style->fg[GTK_STATE_ACTIVE] = previous_style->fg[GTK_STATE_ACTIVE]; + } + gtk_widget_set_style(GTK_WIDGET(sheet_entry), style); + } + } + + if(GTK_IS_ITEM_ENTRY(sheet_entry)) + max_size = GTK_ITEM_ENTRY(sheet_entry)->text_max_size; + else + max_size = 0; + + text_size = 0; + text = gtk_entry_get_text(GTK_ENTRY(sheet_entry)); + if(text && strlen(text) > 0){ + text_size = STRING_WIDTH(GTK_WIDGET(sheet), attributes.font_desc, text); + } + + column_width=sheet->column[sheet->active_cell.col].width; + + size=MIN(text_size, max_size); + size=MAX(size,column_width-2*CELLOFFSET); + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + shentry_allocation.x = COLUMN_LEFT_XPIXEL(sheet,sheet->active_cell.col); + shentry_allocation.y = ROW_TOP_YPIXEL(sheet,sheet->active_cell.row); + shentry_allocation.width = column_width; + shentry_allocation.height = sheet->row[sheet->active_cell.row].height; + + if(GTK_IS_ITEM_ENTRY(sheet->sheet_entry)){ + + shentry_allocation.height -= 2*CELLOFFSET; + shentry_allocation.y += CELLOFFSET; + if(gtk_sheet_clip_text(sheet)) + shentry_allocation.width = column_width - 2*CELLOFFSET; + else + shentry_allocation.width = size; + + switch(GTK_ITEM_ENTRY(sheet_entry)->justification){ + case GTK_JUSTIFY_CENTER: + shentry_allocation.x += (column_width)/2 - size/2; + break; + case GTK_JUSTIFY_RIGHT: + shentry_allocation.x += column_width - size - CELLOFFSET; + break; + case GTK_JUSTIFY_LEFT: + case GTK_JUSTIFY_FILL: + shentry_allocation.x += CELLOFFSET; + break; + } + + } + + if(!GTK_IS_ITEM_ENTRY(sheet->sheet_entry)){ + shentry_allocation.x += 2; + shentry_allocation.y += 2; + shentry_allocation.width -= MIN(shentry_allocation.width, 3); + shentry_allocation.height -= MIN(shentry_allocation.height, 3); + } + + gtk_widget_size_allocate(sheet->sheet_entry, &shentry_allocation); + + if(previous_style == style) gtk_style_unref(previous_style); +} + +static void +gtk_sheet_entry_set_max_size(GtkSheet *sheet) +{ + gint i; + gint size=0; + gint sizel=0, sizer=0; + gint row,col; + GtkJustification justification; + + row=sheet->active_cell.row; + col=sheet->active_cell.col; + + if(!GTK_IS_ITEM_ENTRY(sheet->sheet_entry) || gtk_sheet_clip_text(sheet)) return; + + justification = GTK_ITEM_ENTRY(sheet->sheet_entry)->justification; + + switch(justification){ + case GTK_JUSTIFY_FILL: + case GTK_JUSTIFY_LEFT: + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + size+=sheet->column[i].width; + } + size = MIN(size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL(sheet, col)); + break; + case GTK_JUSTIFY_RIGHT: + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + size+=sheet->column[i].width; + } + break; + case GTK_JUSTIFY_CENTER: + for(i=col+1; i<=MAX_VISIBLE_COLUMN(sheet); i++){ +/* if(gtk_sheet_cell_get_text(sheet, row, i)) break; +*/ + sizer+=sheet->column[i].width; + } + for(i=col-1; i>=MIN_VISIBLE_COLUMN(sheet); i--){ + if(gtk_sheet_cell_get_text(sheet, row, i)) break; + sizel+=sheet->column[i].width; + } + size=2*MIN(sizel, sizer); + break; + } + + if(size!=0) size+=sheet->column[col].width; + GTK_ITEM_ENTRY(sheet->sheet_entry)->text_max_size=size; + +} + +static void +create_sheet_entry(GtkSheet *sheet) +{ + GtkWidget *widget; + GtkWidget *parent; + GtkWidget *entry; + GtkStyle *style; + gint found_entry = FALSE; + + widget = GTK_WIDGET(sheet); + + style = gtk_style_copy(GTK_WIDGET(sheet)->style); + + if(sheet->sheet_entry){ + /* avoids warnings */ + gtk_widget_ref(sheet->sheet_entry); + gtk_widget_unparent(sheet->sheet_entry); + gtk_widget_destroy(sheet->sheet_entry); + } + + if(sheet->entry_type){ + + if(!gtk_type_is_a (sheet->entry_type, GTK_TYPE_ENTRY)){ + + parent = GTK_WIDGET(gtk_type_new(sheet->entry_type)); + + sheet->sheet_entry = parent; + + entry = gtk_sheet_get_entry (sheet); + if(GTK_IS_ENTRY(entry)) found_entry = TRUE; + + } else { + + parent = GTK_WIDGET(gtk_type_new(sheet->entry_type)); + entry = parent; + found_entry = TRUE; + + } + + if(!found_entry){ + + g_warning ("Entry type must be GtkEntry subclass, using default"); + entry = gtk_item_entry_new(); + sheet->sheet_entry = entry; + + } else { + + sheet->sheet_entry = parent; + + } + + + } else { + + entry = gtk_item_entry_new(); + sheet->sheet_entry = entry; + + } + + gtk_widget_size_request(sheet->sheet_entry, NULL); + + if(GTK_WIDGET_REALIZED(sheet)) + { + gtk_widget_set_parent_window (sheet->sheet_entry, sheet->sheet_window); + gtk_widget_set_parent(sheet->sheet_entry, GTK_WIDGET(sheet)); + gtk_widget_realize(sheet->sheet_entry); + } + +#if 0 + /* SDB says: I need to work out the event passing system */ + gtk_signal_connect_object(GTK_OBJECT(entry),"key_press_event", + (GtkSignalFunc) gtk_sheet_entry_key_press, + GTK_OBJECT(sheet)); +#endif + + gtk_widget_show (sheet->sheet_entry); +} + + +GtkWidget * +gtk_sheet_get_entry(GtkSheet *sheet) +{ + GtkWidget *parent; + GtkWidget *entry = NULL; + GtkTableChild *table_child; + GtkBoxChild *box_child; + GList *children = NULL; + + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + g_return_val_if_fail (sheet->sheet_entry != NULL, NULL); + + if(GTK_IS_ENTRY(sheet->sheet_entry)) return (sheet->sheet_entry); + + parent = GTK_WIDGET(sheet->sheet_entry); + + if(GTK_IS_TABLE(parent)) children = GTK_TABLE(parent)->children; + if(GTK_IS_BOX(parent)) children = GTK_BOX(parent)->children; + + if(!children) return NULL; + + while(children){ + if(GTK_IS_TABLE(parent)) { + table_child = children->data; + entry = table_child->widget; + } + if(GTK_IS_BOX(parent)){ + box_child = children->data; + entry = box_child->widget; + } + + if(GTK_IS_ENTRY(entry)) + break; + children = children->next; + } + + + if(!GTK_IS_ENTRY(entry)) return NULL; + + return (entry); + +} + +GtkWidget * +gtk_sheet_get_entry_widget(GtkSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); + g_return_val_if_fail (sheet->sheet_entry != NULL, NULL); + + return (sheet->sheet_entry); +} + +/* BUTTONS */ +static void +row_button_set (GtkSheet *sheet, gint row) +{ + if(sheet->row[row].button.state == GTK_STATE_ACTIVE) return; + + sheet->row[row].button.state = GTK_STATE_ACTIVE; + gtk_sheet_button_draw(sheet, row, -1); + +} + +static void +column_button_set (GtkSheet *sheet, gint column) +{ + if(sheet->column[column].button.state == GTK_STATE_ACTIVE) return; + + sheet->column[column].button.state = GTK_STATE_ACTIVE; + gtk_sheet_button_draw(sheet, -1, column); + +} + +static void +row_button_release (GtkSheet *sheet, gint row) +{ + if(sheet->row[row].button.state == GTK_STATE_NORMAL) return; + + sheet->row[row].button.state = GTK_STATE_NORMAL; + gtk_sheet_button_draw(sheet, row, -1); +} + +static void +column_button_release (GtkSheet *sheet, gint column) +{ + if(sheet->column[column].button.state == GTK_STATE_NORMAL) return; + + sheet->column[column].button.state = GTK_STATE_NORMAL; + gtk_sheet_button_draw(sheet, -1, column); +} + +static void +gtk_sheet_button_draw (GtkSheet *sheet, gint row, gint column) +{ + GdkWindow *window = NULL; + GtkShadowType shadow_type; + guint width = 0, height = 0; + gint x = 0, y = 0; + gint index = 0; + gint text_width = 0, text_height = 0; + GtkSheetButton *button = NULL; + GtkSheetChild *child = NULL; + GdkRectangle allocation; + gboolean is_sensitive = FALSE; + gint state = 0; + gint len = 0; + gchar *line = 0; + gchar *words = 0; + gchar label[10]; + + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) return; + + if(row >= 0 && !sheet->row[row].is_visible) return; + if(column >= 0 && !sheet->column[column].is_visible) return; + if(row >= 0 && !sheet->row_titles_visible) return; + if(column >= 0 && !sheet->column_titles_visible) return; + if(column>=0 && column <MIN_VISIBLE_COLUMN(sheet)) return; + if(column>=0 && column >MAX_VISIBLE_COLUMN(sheet)) return; + if(row>=0 && row <MIN_VISIBLE_ROW(sheet)) return; + if(row>=0 && row >MAX_VISIBLE_ROW(sheet)) return; + if( (row == -1) && (column == -1) ) return; + + if(row==-1){ + window=sheet->column_title_window; + button=&sheet->column[column].button; + index=column; + x = COLUMN_LEFT_XPIXEL(sheet, column)+CELL_SPACING; + if(sheet->row_titles_visible) x -= sheet->row_title_area.width; + y = 0; + width = sheet->column[column].width; + height = sheet->column_title_area.height; + is_sensitive=sheet->column[column].is_sensitive; + } + else if(column==-1){ + window=sheet->row_title_window; + button=&sheet->row[row].button; + index=row; + x = 0; + y = ROW_TOP_YPIXEL(sheet, row)+CELL_SPACING; + if(sheet->column_titles_visible) y-=sheet->column_title_area.height; + width = sheet->row_title_area.width; + height = sheet->row[row].height; + is_sensitive=sheet->row[row].is_sensitive; + } + + allocation.x = x; + allocation.y = y; + allocation.width = width; + allocation.height = height; + + gdk_window_clear_area (window, + x, y, + width, height); + + gtk_paint_box (sheet->button->style, window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + &allocation, GTK_WIDGET(sheet), + "buttondefault", x, y, width, height); + + state = button->state; + if(!is_sensitive) state=GTK_STATE_INSENSITIVE; + + if (state == GTK_STATE_ACTIVE) + shadow_type = GTK_SHADOW_IN; + else + shadow_type = GTK_SHADOW_OUT; + + if(state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE) + gtk_paint_box (sheet->button->style, window, + button->state, shadow_type, + &allocation, GTK_WIDGET(sheet), + "button", x, y, width, height); + + if(button->label_visible){ + + text_height=DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet))-2*CELLOFFSET; + + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->fg_gc[button->state], + &allocation); + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->white_gc, &allocation); + + y += DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet))/2 + sheet->button->style->ythickness + DEFAULT_FONT_DESCENT(GTK_WIDGET(sheet)); + + if(button->label && strlen(button->label)>0){ + + words=button->label; + line = g_new(gchar, 1); + line[0]='\0'; + + while(words && *words != '\0'){ + if(*words != '\n'){ + len=strlen(line); + line=g_realloc(line, len+2); + line[len]=*words; + line[len+1]='\0'; + } + if(*words == '\n' || *(words+1) == '\0'){ + text_width = STRING_WIDTH(GTK_WIDGET(sheet), GTK_WIDGET(sheet)->style->font_desc, line); + + switch(button->justification){ + case GTK_JUSTIFY_LEFT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + CELLOFFSET, y, + line); + break; + case GTK_JUSTIFY_RIGHT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + width - text_width - CELLOFFSET, y, + line); + break; + case GTK_JUSTIFY_CENTER: + default: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + (width - text_width) /2, y, + line); + } + + y += text_height + 2; + + g_free(line); + line = g_new(gchar, 1); + line[0]='\0'; + } + words++; + } + g_free(line); + }else{ + sprintf(label,"%d",index); + text_width = STRING_WIDTH(GTK_WIDGET(sheet), GTK_WIDGET(sheet)->style->font_desc, label); + + switch(button->justification){ + case GTK_JUSTIFY_LEFT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + CELLOFFSET, y, + label); + break; + case GTK_JUSTIFY_RIGHT: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + width - text_width - CELLOFFSET, y, + label); + break; + case GTK_JUSTIFY_CENTER: + default: + gtk_paint_string (GTK_WIDGET(sheet)->style, window, state, + &allocation, GTK_WIDGET(sheet), "label", + x + (width - text_width) /2, y, + label); + } + + } + + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->fg_gc[button->state], + NULL); + gdk_gc_set_clip_rectangle(GTK_WIDGET(sheet)->style->white_gc, NULL); + + } + + if((child = button->child) && (child->widget)){ + child->x = allocation.x; + child->y = allocation.y; + + child->x += (width - child->widget->requisition.width) / 2; + child->y += (height - child->widget->requisition.height) / 2; + allocation.x = child->x; + allocation.y = child->y; + allocation.width = child->widget->requisition.width; + allocation.height = child->widget->requisition.height; + + x = child->x; + y = child->y; + + gtk_widget_set_state(child->widget, button->state); + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + GTK_WIDGET_MAPPED(child->widget)) + { + gtk_widget_size_allocate(child->widget, + &allocation); + gtk_widget_queue_draw(child->widget); + } + } + +} + + +/* SCROLLBARS + * + * functions: + * adjust_scrollbars + * vadjustment_changed + * hadjustment_changed + * vadjustment_value_changed + * hadjustment_value_changed */ + +static void +adjust_scrollbars (GtkSheet * sheet) +{ + + if(sheet->vadjustment){ + sheet->vadjustment->page_size = sheet->sheet_window_height; + sheet->vadjustment->page_increment = sheet->sheet_window_height / 2; + sheet->vadjustment->step_increment = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + sheet->vadjustment->lower = 0; + sheet->vadjustment->upper = SHEET_HEIGHT (sheet) + 80; +/* + if (sheet->sheet_window_height - sheet->voffset > SHEET_HEIGHT (sheet)) + { + sheet->vadjustment->value = MAX(0, SHEET_HEIGHT (sheet) - + sheet->sheet_window_height); + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + } +*/ + gtk_signal_emit_by_name (GTK_OBJECT(sheet->vadjustment), "changed"); + + } + + if(sheet->hadjustment){ + sheet->hadjustment->page_size = sheet->sheet_window_width; + sheet->hadjustment->page_increment = sheet->sheet_window_width / 2; + sheet->hadjustment->step_increment = DEFAULT_COLUMN_WIDTH; + sheet->hadjustment->lower = 0; + sheet->hadjustment->upper = SHEET_WIDTH (sheet)+ 80; +/* + if (sheet->sheet_window_width - sheet->hoffset > SHEET_WIDTH (sheet)) + { + sheet->hadjustment->value = MAX(0, SHEET_WIDTH (sheet) - + sheet->sheet_window_width); + gtk_signal_emit_by_name (GTK_OBJECT(sheet->hadjustment), + "value_changed"); + } +*/ + gtk_signal_emit_by_name (GTK_OBJECT(sheet->hadjustment), "changed"); + + } +/* + if(GTK_WIDGET_REALIZED(sheet)) + { + if(sheet->row_titles_visible){ + size_allocate_row_title_buttons(sheet); + gdk_window_show(sheet->row_title_window); + } + + if(sheet->column_titles_visible){ + size_allocate_column_title_buttons(sheet); + gdk_window_show(sheet->column_title_window); + } + + gtk_sheet_range_draw(sheet, NULL); + } +*/ +} + + +static void +vadjustment_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + sheet = GTK_SHEET (data); + +} + +static void +hadjustment_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + sheet = GTK_SHEET (data); + +} + + +static void +vadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + gint diff, value, old_value; + gint i; + gint row, new_row; + gint y=0; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + g_return_if_fail (GTK_IS_SHEET (data)); + + sheet = GTK_SHEET (data); + + if(GTK_SHEET_IS_FROZEN(sheet)) return; + + row=ROW_FROM_YPIXEL(sheet,sheet->column_title_area.height + CELL_SPACING); + if(!sheet->column_titles_visible) + row=ROW_FROM_YPIXEL(sheet,CELL_SPACING); + + old_value = -sheet->voffset; + + for(i=0; i<= sheet->maxrow; i++){ + if(sheet->row[i].is_visible) y+=sheet->row[i].height; + if(y > adjustment->value) break; + } + y-=sheet->row[i].height; + new_row=i; + + if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. && + sheet->row[i].height > sheet->vadjustment->step_increment){ +/* This avoids embarrassing twitching */ + if(row == new_row && row != sheet->maxrow && + adjustment->value - sheet->old_vadjustment >= + sheet->vadjustment->step_increment && + new_row + 1 != MIN_VISIBLE_ROW(sheet)){ + new_row+=1; + y=y+sheet->row[row].height; + } + } + +/* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */ + if(sheet->old_vadjustment >= 0. && row == new_row){ + sheet->old_vadjustment = sheet->vadjustment->value; + return; + } + + sheet->old_vadjustment = sheet->vadjustment->value; + adjustment->value=y; + + + if(new_row == 0){ + sheet->vadjustment->step_increment= + sheet->row[0].height; + }else{ + sheet->vadjustment->step_increment= + MIN(sheet->row[new_row].height, sheet->row[new_row-1].height); + } + + sheet->vadjustment->value=adjustment->value; + + value = adjustment->value; + + if (value >= -sheet->voffset) + { + /* scroll down */ + diff = value + sheet->voffset; + } + else + { + /* scroll up */ + diff = -sheet->voffset - value; + } + + sheet->voffset = -value; + + sheet->view.row0=ROW_FROM_YPIXEL(sheet, sheet->column_title_area.height+1); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + if(!sheet->column_titles_visible) + sheet->view.row0=ROW_FROM_YPIXEL(sheet, 1); + + if(GTK_WIDGET_REALIZED(sheet->sheet_entry) && + sheet->state == GTK_SHEET_NORMAL && + sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 && + !gtk_sheet_cell_isvisible(sheet, sheet->active_cell.row, + sheet->active_cell.col)) + { + const gchar *text; + + text = gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + + if(!text || strlen(text)==0) + gtk_sheet_cell_clear(sheet, + sheet->active_cell.row, + sheet->active_cell.col); + gtk_widget_unmap(sheet->sheet_entry); + } + + gtk_sheet_position_children(sheet); + + gtk_sheet_range_draw(sheet, NULL); + size_allocate_row_title_buttons(sheet); + size_allocate_global_button(sheet); +} + +static void +hadjustment_value_changed (GtkAdjustment * adjustment, + gpointer data) +{ + GtkSheet *sheet; + gint i, diff, value, old_value; + gint column, new_column; + gint x=0; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + g_return_if_fail (GTK_IS_SHEET (data)); + + sheet = GTK_SHEET (data); + + if(GTK_SHEET_IS_FROZEN(sheet)) return; + + column=COLUMN_FROM_XPIXEL(sheet,sheet->row_title_area.width + CELL_SPACING); + if(!sheet->row_titles_visible) + column=COLUMN_FROM_XPIXEL(sheet, CELL_SPACING); + + old_value = -sheet->hoffset; + + for(i=0; i<= sheet->maxcol; i++){ + if(sheet->column[i].is_visible) x+=sheet->column[i].width; + if(x > adjustment->value) break; + } + x-=sheet->column[i].width; + new_column=i; + + if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 && + sheet->column[i].width > sheet->hadjustment->step_increment){ +/* This avoids embarrassing twitching */ + if(column == new_column && column != sheet->maxcol && + adjustment->value - sheet->old_hadjustment >= + sheet->hadjustment->step_increment && + new_column + 1 != MIN_VISIBLE_COLUMN(sheet)){ + new_column+=1; + x=x+sheet->column[column].width; + } + } + +/* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */ + if(sheet->old_hadjustment >= 0. && new_column == column){ + sheet->old_hadjustment = sheet->hadjustment->value; + return; + } + + sheet->old_hadjustment = sheet->hadjustment->value; + adjustment->value=x; + + if(new_column == 0){ + sheet->hadjustment->step_increment= + sheet->column[0].width; + }else{ + sheet->hadjustment->step_increment= + MIN(sheet->column[new_column].width, sheet->column[new_column-1].width); + } + + + sheet->hadjustment->value=adjustment->value; + + value = adjustment->value; + + if (value >= -sheet->hoffset) + { + /* scroll right */ + diff = value + sheet->hoffset; + } + else + { + /* scroll left */ + diff = -sheet->hoffset - value; + } + + sheet->hoffset = -value; + + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, sheet->row_title_area.width+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + if(!sheet->row_titles_visible) + sheet->view.col0=COLUMN_FROM_XPIXEL(sheet, 1); + + if(GTK_WIDGET_REALIZED(sheet->sheet_entry) && + sheet->state == GTK_SHEET_NORMAL && + sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 && + !gtk_sheet_cell_isvisible(sheet, sheet->active_cell.row, + sheet->active_cell.col)) + { + const gchar *text; + + text = gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry(sheet))); + if(!text || strlen(text)==0) + gtk_sheet_cell_clear(sheet, + sheet->active_cell.row, + sheet->active_cell.col); + + gtk_widget_unmap(sheet->sheet_entry); + } + + gtk_sheet_position_children(sheet); + + gtk_sheet_range_draw(sheet, NULL); + size_allocate_column_title_buttons(sheet); +} + + +/* COLUMN RESIZING */ +static void +draw_xor_vline (GtkSheet * sheet) +{ + GtkWidget *widget; + + g_return_if_fail (sheet != NULL); + + widget = GTK_WIDGET (sheet); + + gdk_draw_line (widget->window, sheet->xor_gc, + sheet->x_drag, + sheet->column_title_area.height, + sheet->x_drag, + sheet->sheet_window_height + 1); +} + +/* ROW RESIZING */ +static void +draw_xor_hline (GtkSheet * sheet) +{ + GtkWidget *widget; + + g_return_if_fail (sheet != NULL); + + widget = GTK_WIDGET (sheet); + + gdk_draw_line (widget->window, sheet->xor_gc, + sheet->row_title_area.width, + sheet->y_drag, + + sheet->sheet_window_width + 1, + sheet->y_drag); +} + +/* SELECTED RANGE */ +static void +draw_xor_rectangle(GtkSheet *sheet, GtkSheetRange range) +{ + gint i; + GdkRectangle clip_area, area; + GdkGCValues values; + + area.x=COLUMN_LEFT_XPIXEL(sheet, range.col0); + area.y=ROW_TOP_YPIXEL(sheet, range.row0); + area.width=COLUMN_LEFT_XPIXEL(sheet, range.coli)-area.x+ + sheet->column[range.coli].width; + area.height=ROW_TOP_YPIXEL(sheet, range.rowi)-area.y+ + sheet->row[range.rowi].height; + + clip_area.x=sheet->row_title_area.width; + clip_area.y=sheet->column_title_area.height; + clip_area.width=sheet->sheet_window_width; + clip_area.height=sheet->sheet_window_height; + + if(!sheet->row_titles_visible) clip_area.x = 0; + if(!sheet->column_titles_visible) clip_area.y = 0; + + if(area.x<0) { + area.width=area.width+area.x; + area.x=0; + } + if(area.width>clip_area.width) area.width=clip_area.width+10; + if(area.y<0) { + area.height=area.height+area.y; + area.y=0; + } + if(area.height>clip_area.height) area.height=clip_area.height+10; + + clip_area.x--; + clip_area.y--; + clip_area.width+=3; + clip_area.height+=3; + + gdk_gc_get_values(sheet->xor_gc, &values); + + gdk_gc_set_clip_rectangle(sheet->xor_gc, &clip_area); + + for(i=-1;i<=1;i=++i) + gdk_draw_rectangle(sheet->sheet_window, + sheet->xor_gc, + FALSE, + area.x+i, area.y+i, + area.width-2*i, area.height-2*i); + + + gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL); + + gdk_gc_set_foreground(sheet->xor_gc, &values.foreground); + +} + + +/* this function returns the new width of the column being resized given + * the column and x position of the cursor; the x cursor position is passed + * in as a pointer and automaticaly corrected if it's beyond min/max limits */ +static guint +new_column_width (GtkSheet * sheet, + gint column, + gint * x) +{ + gint cx, width; + GtkRequisition requisition; + + cx = *x; + + requisition.width = sheet->column[column].requisition; + + /* you can't shrink a column to less than its minimum width */ + if (cx < COLUMN_LEFT_XPIXEL (sheet, column) + requisition.width) + { + *x = cx = COLUMN_LEFT_XPIXEL (sheet, column) + requisition.width; + } + + /* don't grow past the end of the window */ + /* + if (cx > sheet->sheet_window_width) + { + *x = cx = sheet->sheet_window_width; + } + */ + /* calculate new column width making sure it doesn't end up + * less than the minimum width */ + width = cx - COLUMN_LEFT_XPIXEL (sheet, column); + if (width < requisition.width) + width = requisition.width; + + sheet->column[column].width = width; + gtk_sheet_recalc_left_xpixels(sheet, column+1); + sheet->view.coli=COLUMN_FROM_XPIXEL(sheet, sheet->sheet_window_width); + size_allocate_column_title_buttons (sheet); + + return width; +} + +/* this function returns the new height of the row being resized given + * the row and y position of the cursor; the y cursor position is passed + * in as a pointer and automaticaly corrected if it's beyond min/max limits */ +static guint +new_row_height (GtkSheet * sheet, + gint row, + gint * y) +{ + GtkRequisition requisition; + gint cy, height; + + cy = *y; + + requisition.height = sheet->row[row].requisition; + + /* you can't shrink a row to less than its minimum height */ + if (cy < ROW_TOP_YPIXEL (sheet, row) + requisition.height) + + { + *y = cy = ROW_TOP_YPIXEL (sheet, row) + requisition.height; + } + + /* don't grow past the end of the window */ + /* + if (cy > sheet->sheet_window_height) + { + *y = cy = sheet->sheet_window_height; + } + */ + /* calculate new row height making sure it doesn't end up + * less than the minimum height */ + height = (cy - ROW_TOP_YPIXEL (sheet, row)); + if (height < requisition.height) + height = requisition.height; + + sheet->row[row].height = height; + gtk_sheet_recalc_top_ypixels(sheet, row); + sheet->view.rowi=ROW_FROM_YPIXEL(sheet, sheet->sheet_window_height-1); + size_allocate_row_title_buttons (sheet); + + return height; +} + +void +gtk_sheet_set_column_width (GtkSheet * sheet, + gint column, + guint width) +{ + guint min_width; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (column < 0 || column > sheet->maxcol) + return; + + gtk_sheet_column_size_request(sheet, column, &min_width); + if(width < min_width) return; + + sheet->column[column].width = width; + + gtk_sheet_recalc_left_xpixels(sheet, column+1); + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && !GTK_SHEET_IS_FROZEN(sheet)){ + size_allocate_column_title_buttons (sheet); + adjust_scrollbars (sheet); + gtk_sheet_size_allocate_entry(sheet); + gtk_sheet_range_draw (sheet, NULL); + } else + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[CHANGED], -1, column); + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[NEW_COL_WIDTH], column, width); + +} + +void +gtk_sheet_set_row_height (GtkSheet * sheet, + gint row, + guint height) +{ + guint min_height; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if (row < 0 || row > sheet->maxrow) + return; + + gtk_sheet_row_size_request(sheet, row, &min_height); + if(height < min_height) return; + + sheet->row[row].height = height; + + gtk_sheet_recalc_top_ypixels(sheet, row+1); + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && !GTK_SHEET_IS_FROZEN(sheet)){ + size_allocate_row_title_buttons (sheet); + adjust_scrollbars (sheet); + gtk_sheet_size_allocate_entry(sheet); + gtk_sheet_range_draw (sheet, NULL); + } + + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[CHANGED], row, -1); + gtk_signal_emit(GTK_OBJECT(sheet), sheet_signals[NEW_ROW_HEIGHT], row, height); + +} + + +void +gtk_sheet_add_column(GtkSheet *sheet, guint ncols) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + AddColumn(sheet, ncols); + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + adjust_scrollbars(sheet); + + if(sheet->state==GTK_SHEET_ROW_SELECTED) sheet->range.coli+=ncols; + + sheet->old_hadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); +} + +void +gtk_sheet_add_row(GtkSheet *sheet, guint nrows) +{ + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + AddRow(sheet, nrows); + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) sheet->range.rowi+=nrows; + + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); +} + +void +gtk_sheet_insert_rows(GtkSheet *sheet, guint row, guint nrows) +{ + GList *children; + GtkSheetChild *child; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + InsertRow(sheet, row, nrows); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell) + if(child->row >= row) child->row += nrows; + + children = children->next; + } + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + if(sheet->state==GTK_SHEET_COLUMN_SELECTED) sheet->range.rowi+=nrows; + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + +} + +void +gtk_sheet_insert_columns(GtkSheet *sheet, guint col, guint ncols) +{ + GList *children; + GtkSheetChild *child; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + InsertColumn(sheet, col, ncols); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell) + if(child->col >= col) child->col += ncols; + + children = children->next; + } + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + if(sheet->state==GTK_SHEET_ROW_SELECTED) sheet->range.coli+=ncols; + adjust_scrollbars(sheet); + + sheet->old_hadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + +} + +void +gtk_sheet_delete_rows(GtkSheet *sheet, guint row, guint nrows) +{ + GList *children; + GtkSheetChild *child; + gint irow, icol; + gboolean veto; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + nrows = MIN(nrows, sheet->maxrow-row+1); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + DeleteRow(sheet, row, nrows); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell && + child->row >= row && child->row < row+nrows){ + gtk_container_remove(GTK_CONTAINER(sheet), child->widget); + children = sheet->children; + } else + children = children->next; + } + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell && child->row > row) child->row -= nrows; + children = children->next; + } + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + irow = sheet->active_cell.row; + icol = sheet->active_cell.col; + + sheet->active_cell.row = -1; + sheet->active_cell.col = -1; + +/* if(sheet->state == GTK_SHEET_ROW_SELECTED) +*/ + + irow = MIN(irow, sheet->maxrow); + irow = MAX(irow, 0); + gtk_sheet_click_cell(sheet, irow, icol, &veto); + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + + adjust_scrollbars(sheet); + + sheet->old_vadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->vadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->vadjustment), + "value_changed"); + +} + +void +gtk_sheet_delete_columns(GtkSheet *sheet, guint col, guint ncols) +{ + GList *children; + GtkSheetChild *child; + gint irow, icol; + gboolean veto; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + ncols = MIN(ncols, sheet->maxcol-col+1); + + if(GTK_WIDGET_REALIZED(sheet)) + gtk_sheet_real_unselect_range(sheet, NULL); + + DeleteColumn(sheet, col, ncols); + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell && + child->col >= col && child->col < col+ncols){ + gtk_container_remove(GTK_CONTAINER(sheet), child->widget); + children = sheet->children; + } else + children = children->next; + } + + children = sheet->children; + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell && child->col > col) child->col -= ncols; + children = children->next; + } + + if(!GTK_WIDGET_REALIZED(sheet)) return; + + irow = sheet->active_cell.row; + icol = sheet->active_cell.col; + + sheet->active_cell.row = -1; + sheet->active_cell.col = -1; + +/* if(sheet->state == GTK_SHEET_COLUMN_SELECTED) +*/ + + icol = MIN(icol, sheet->maxcol); + icol = MAX(icol, 0); + gtk_sheet_click_cell(sheet, irow, icol, &veto); + + gtk_sheet_activate_cell(sheet, sheet->active_cell.row, + sheet->active_cell.col); + + adjust_scrollbars(sheet); + + sheet->old_hadjustment = -1.; + if(!GTK_SHEET_IS_FROZEN(sheet) && sheet->hadjustment) + gtk_signal_emit_by_name (GTK_OBJECT (sheet->hadjustment), + "value_changed"); + +} + +void +gtk_sheet_range_set_background(GtkSheet *sheet, const GtkSheetRange *urange, const GdkColor *color) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + if(color != NULL) + attributes.background = *color; + else + attributes.background = sheet->bg_color; + + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + range.row0--; + range.col0--; + range.rowi++; + range.coli++; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_foreground(GtkSheet *sheet, const GtkSheetRange *urange, const GdkColor *color) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + + if(color != NULL) + attributes.foreground = *color; + else + gdk_color_black(gdk_colormap_get_system(), &attributes.foreground); + + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_justification(GtkSheet *sheet, const GtkSheetRange *urange, + GtkJustification just) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.justification = just; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + range.col0 = sheet->view.col0; + range.coli = sheet->view.coli; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_column_set_justification(GtkSheet *sheet, gint col, + GtkJustification justification) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(col > sheet->maxcol) return; + + sheet->column[col].justification = justification; + + if(GTK_WIDGET_REALIZED(sheet) && !GTK_SHEET_IS_FROZEN(sheet) && + col >= MIN_VISIBLE_COLUMN(sheet) && col <= MAX_VISIBLE_COLUMN(sheet)) + gtk_sheet_range_draw(sheet, NULL); + +} + +void +gtk_sheet_range_set_editable(GtkSheet *sheet, const GtkSheetRange *urange, gboolean editable) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.is_editable = editable; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_visible(GtkSheet *sheet, const GtkSheetRange *urange, gboolean visible) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.is_visible=visible; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_border(GtkSheet *sheet, const GtkSheetRange *urange, gint mask, +guint width, gint line_style) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.border.mask = mask; + attributes.border.width = width; + attributes.border.line_style=line_style; + attributes.border.cap_style=GDK_CAP_NOT_LAST; + attributes.border.join_style=GDK_JOIN_MITER; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + range.row0--; + range.col0--; + range.rowi++; + range.coli++; + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_border_color(GtkSheet *sheet, const GtkSheetRange *urange, const GdkColor *color) +{ + gint i, j; + GtkSheetCellAttr attributes; + GtkSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.border.color = *color; + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + if(!GTK_SHEET_IS_FROZEN(sheet)) + gtk_sheet_range_draw(sheet, &range); + +} + +void +gtk_sheet_range_set_font(GtkSheet *sheet, const GtkSheetRange *urange, PangoFontDescription *font) +{ + gint i, j; + gint font_height; + GtkSheetCellAttr attributes; + GtkSheetRange range; + PangoContext *context; + PangoFontMetrics *metrics; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_IS_SHEET (sheet)); + + if(!urange) + range = sheet->range; + else + range = *urange; + + gtk_sheet_freeze(sheet); + + context = gtk_widget_get_pango_context(GTK_WIDGET(sheet)); + metrics = pango_context_get_metrics(context, + font, + pango_context_get_language(context)); + font_height = pango_font_metrics_get_descent(metrics) + + pango_font_metrics_get_ascent(metrics); + font_height = PANGO_PIXELS(font_height) + 2*CELLOFFSET; + + for (i=range.row0; i<=range.rowi; i++) + for (j=range.col0; j<=range.coli; j++){ + gtk_sheet_get_attributes(sheet, i, j, &attributes); + attributes.font_desc = font; + if(font_height > sheet->row[i].height){ + sheet->row[i].height = font_height; + gtk_sheet_recalc_top_ypixels(sheet, i); + } + + gtk_sheet_set_cell_attributes(sheet, i, j, attributes); + } + + gtk_sheet_thaw(sheet); + pango_font_metrics_unref(metrics); +} + +static void +gtk_sheet_set_cell_attributes(GtkSheet *sheet, gint row, gint col, GtkSheetCellAttr attributes) +{ + GtkSheetCell **cell; + + if(row > sheet->maxrow || col >sheet->maxcol) return; + + CheckBounds(sheet, row, col); + + cell = &sheet->data[row][col]; + + if(*cell==NULL){ + (*cell) = gtk_sheet_cell_new(); + (*cell)->row = row; + (*cell)->col = col; + } + + if((*cell)->attributes == NULL) + (*cell)->attributes = g_new(GtkSheetCellAttr, 1); + + *((*cell)->attributes) = attributes; +} + +gboolean +gtk_sheet_get_attributes(GtkSheet *sheet, gint row, gint col, GtkSheetCellAttr *attributes) +{ + GtkSheetCell **cell = NULL; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); + + if(row < 0 || col < 0) return FALSE; + + if(row > sheet->maxallocrow || col > sheet->maxalloccol){ + init_attributes(sheet, col, attributes); + return FALSE; + } + + if(row <= sheet->maxallocrow && col <= sheet->maxalloccol){ + if(sheet->data[row] && sheet->data[row][col]) + cell = &sheet->data[row][col]; + if(cell == NULL || *cell == NULL){ + init_attributes(sheet, col, attributes); + return FALSE; + } else + if((*cell)->attributes == NULL){ + init_attributes(sheet, col, attributes); + return FALSE; + }else{ + *attributes = *(sheet->data[row][col]->attributes); + if(sheet->column[col].justification != GTK_JUSTIFY_FILL) + attributes->justification = sheet->column[col].justification; + } + } + + return TRUE; +} + +static void +init_attributes(GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes) +{ + /* DEFAULT VALUES */ + attributes->foreground = GTK_WIDGET(sheet)->style->black; + attributes->background = sheet->bg_color; + if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))){ + GdkColormap *colormap; + colormap=gdk_colormap_get_system(); + gdk_color_black(colormap, &attributes->foreground); + attributes->background = sheet->bg_color; + } + attributes->justification = sheet->column[col].justification; + attributes->border.width = 0; + attributes->border.line_style = GDK_LINE_SOLID; + attributes->border.cap_style = GDK_CAP_NOT_LAST; + attributes->border.join_style = GDK_JOIN_MITER; + attributes->border.mask = 0; + attributes->border.color = GTK_WIDGET(sheet)->style->black; + attributes->is_editable = TRUE; + attributes->is_visible = TRUE; + attributes->font = GTK_WIDGET(sheet)->style->private_font; + attributes->font_desc = GTK_WIDGET(sheet)->style->font_desc; + +} + +/********************************************************************** + * Memory allocation routines: + * AddRow & AddColumn allocate memory for GtkSheetColumn & GtkSheetRow structs. + * InsertRow + * InsertColumn + * DeleteRow + * DeleteColumn + * GrowSheet allocates memory for the sheet cells contents using an array of + * pointers. Alternative to this could be a linked list or a hash table. + * CheckBounds checks whether the given cell is currently allocated or not. + * If not, it calls to GrowSheet. + **********************************************************************/ + +static gint +AddColumn(GtkSheet *tbl, gint ncols) +{ + gint i; + + if(ncols == -1 && tbl->maxcol == 0) + { + ncols = 1; + } + else + { + tbl->maxcol += ncols; + tbl->column = (GtkSheetColumn *)g_realloc(tbl->column,(tbl->maxcol+1)* + sizeof(GtkSheetColumn)); + } + + for(i=tbl->maxcol-ncols+1; i<= tbl->maxcol; i++){ + tbl->column[i].width=DEFAULT_COLUMN_WIDTH; + tbl->column[i].button.label=NULL; + tbl->column[i].button.child=NULL; + tbl->column[i].button.state=GTK_STATE_NORMAL; + tbl->column[i].button.justification=GTK_JUSTIFY_CENTER; + tbl->column[i].button.label_visible = TRUE; + tbl->column[i].name=NULL; + tbl->column[i].is_visible=TRUE; + tbl->column[i].is_sensitive=TRUE; + tbl->column[i].left_text_column=i; + tbl->column[i].right_text_column=i; + tbl->column[i].justification=GTK_JUSTIFY_FILL; + tbl->column[i].requisition=DEFAULT_COLUMN_WIDTH; + if(i>0) + { + tbl->column[i].left_text_column=tbl->column[i-1].left_text_column; + tbl->column[i].left_xpixel=tbl->column[i-1].left_xpixel + + tbl->column[i-1].width; + } + else + { + tbl->column[i].left_xpixel=tbl->row_title_area.width; + if(!tbl->row_titles_visible) + tbl->column[i].left_xpixel=0; + } + } + return TRUE; +} + +static gint +AddRow(GtkSheet *tbl, gint nrows) +{ + gint i; + + if(nrows == -1 && tbl->maxrow == 0) + { + nrows = 1; + } + else + { + tbl->maxrow += nrows; + tbl->row = (GtkSheetRow *)g_realloc(tbl->row,(tbl->maxrow+1)* + sizeof(GtkSheetRow)); + } + + for(i=tbl->maxrow-nrows+1; i<= tbl->maxrow; i++){ + tbl->row[i].requisition=tbl->row[i].height=DEFAULT_ROW_HEIGHT(GTK_WIDGET(tbl)); + tbl->row[i].button.label=NULL; + tbl->row[i].button.child=NULL; + tbl->row[i].button.state=GTK_STATE_NORMAL; + tbl->row[i].button.justification=GTK_JUSTIFY_CENTER; + tbl->row[i].button.label_visible = TRUE; + tbl->row[i].name=NULL; + tbl->row[i].is_visible=TRUE; + tbl->row[i].is_sensitive=TRUE; + if(i>0) + tbl->row[i].top_ypixel=tbl->row[i-1].top_ypixel+tbl->row[i-1].height; + else + { + tbl->row[i].top_ypixel=tbl->column_title_area.height; + if(!tbl->column_titles_visible) + tbl->row[i].top_ypixel=0; + } + } + return TRUE; +} + +static gint +InsertRow(GtkSheet *tbl, gint row, gint nrows) +{ + GtkSheetCell **pp; + gint i,j; + GtkSheetCell **auxdata; + GtkSheetRow auxrow; + + AddRow(tbl,nrows); + + for(i=tbl->maxrow; i>=row+nrows; i--){ + auxrow = tbl->row[i]; + tbl->row[i]=tbl->row[i-nrows]; + tbl->row[i].is_visible=tbl->row[i-nrows].is_visible; + tbl->row[i].is_sensitive=tbl->row[i-nrows].is_sensitive; + if(auxrow.is_visible) + tbl->row[i].top_ypixel+=nrows*DEFAULT_ROW_HEIGHT(GTK_WIDGET(tbl)); + tbl->row[i-nrows]=auxrow; + } + + if(row <= tbl->maxallocrow){ + + GrowSheet(tbl,nrows,0); + + for(i=tbl->maxallocrow; i>=row+nrows; i--){ + auxdata = tbl->data[i]; + tbl->data[i]=tbl->data[i-nrows]; + + pp= tbl->data[i]; + for(j=0; j<=tbl->maxalloccol; j++,pp++){ + if(*pp!=(GtkSheetCell *)NULL) + (*pp)->row=i; + + } + tbl->data[i-nrows]=auxdata; + } + } + gtk_sheet_recalc_top_ypixels(tbl, 0); + return TRUE; +} + +static gint +InsertColumn(GtkSheet *tbl, gint col, gint ncols) +{ + gint i,j; + GtkSheetColumn auxcol; + + AddColumn(tbl,ncols); + + for(i=tbl->maxcol; i>=col+ncols; i--){ + auxcol = tbl->column[i]; + tbl->column[i]=tbl->column[i-ncols]; + tbl->column[i].is_visible=tbl->column[i-ncols].is_visible; + tbl->column[i].is_sensitive=tbl->column[i-ncols].is_sensitive; + tbl->column[i].left_text_column=tbl->column[i-ncols].left_text_column; + tbl->column[i].right_text_column=tbl->column[i-ncols].right_text_column; + tbl->column[i].justification=tbl->column[i-ncols].justification; + if(auxcol.is_visible) tbl->column[i].left_xpixel+=ncols*DEFAULT_COLUMN_WIDTH; + tbl->column[i-ncols]=auxcol; + } + + if(col <= tbl->maxalloccol){ + + GrowSheet(tbl,0,ncols); + + for(i=0; i<=tbl->maxallocrow; i++){ + for(j=tbl->maxalloccol; j>=col+ncols; j--){ + gtk_sheet_real_cell_clear(tbl, i, j, TRUE); + tbl->data[i][j]=tbl->data[i][j-ncols]; + if(tbl->data[i][j]) tbl->data[i][j]->col=j; + tbl->data[i][j-ncols]=NULL; + } + } + } + gtk_sheet_recalc_left_xpixels(tbl, 0); + return TRUE; +} + +static gint +DeleteRow(GtkSheet *tbl, gint row, gint nrows) +{ + GtkSheetCell **auxdata = NULL; + gint i,j; + + if(nrows <= 0 || row > tbl->maxrow) return TRUE; + + nrows=MIN(nrows,tbl->maxrow-row+1); + + for(i=row; i<row+nrows; i++){ + if(tbl->row[i].name){ + g_free(tbl->row[i].name); + tbl->row[i].name = NULL; + } + if(tbl->row[i].button.label){ + g_free(tbl->row[i].button.label); + tbl->row[i].button.label = NULL; + } + } + + for(i=row; i<=tbl->maxrow-nrows; i++){ + if(i+nrows <= tbl->maxrow){ + tbl->row[i]=tbl->row[i+nrows]; + } + } + + if(row <= tbl->maxallocrow){ + + for(i=row; i<=tbl->maxrow-nrows; i++){ + if(i<=tbl->maxallocrow){ + auxdata=tbl->data[i]; + for(j=0; j<=tbl->maxalloccol; j++){ + gtk_sheet_real_cell_clear(tbl, i, j, TRUE); + } + } + if(i+nrows<=tbl->maxallocrow){ + tbl->data[i]=tbl->data[i+nrows]; + tbl->data[i+nrows]=auxdata; + for(j=0; j<=tbl->maxalloccol; j++){ + if(tbl->data[i][j]) tbl->data[i][j]->row=i; + } + } + } + + for(i=tbl->maxrow-nrows+1; i<=tbl->maxallocrow; i++){ + if(i > 0 && tbl->data[i]){ + g_free(tbl->data[i]); + tbl->data[i] = NULL; + } + } + + tbl->maxallocrow-=MIN(nrows,tbl->maxallocrow-row+1); + tbl->maxallocrow = MIN(tbl->maxallocrow, tbl->maxrow); + + } + + tbl->maxrow-=nrows; + gtk_sheet_recalc_top_ypixels(tbl, 0); + return TRUE; +} + +static gint +DeleteColumn(GtkSheet *tbl, gint column, gint ncols) +{ + gint i,j; + GtkSheetColumn auxcol; + + ncols = MIN(ncols,tbl->maxcol-column+1); + + if(ncols <= 0 || column > tbl->maxcol) return TRUE; + + for(i=column; i<column+ncols; i++){ + auxcol=tbl->column[i]; + if(tbl->column[i].name){ + g_free(tbl->column[i].name); + tbl->column[i].name = NULL; + } + if(tbl->column[i].button.label){ + g_free(tbl->column[i].button.label); + tbl->column[i].button.label = NULL; + } + } + + for(i=column; i<=tbl->maxcol-ncols; i++){ + if(i+ncols <= tbl->maxcol){ + tbl->column[i]=tbl->column[i+ncols]; + } + } + + if(column <= tbl->maxalloccol){ + + for(i=column; i<=tbl->maxcol-ncols; i++){ + if(i<=tbl->maxalloccol){ + for(j=0; j<=tbl->maxallocrow; j++){ + gtk_sheet_real_cell_clear(tbl, j, i, TRUE); + if(i+ncols <= tbl->maxalloccol){ + tbl->data[j][i] = tbl->data[j][i+ncols]; + tbl->data[j][i+ncols] = NULL; + if(tbl->data[j][i]) tbl->data[j][i]->col=i; + } + } + } + + } + + tbl->maxalloccol-=MIN(ncols,tbl->maxalloccol-column+1); + tbl->maxalloccol = MIN(tbl->maxalloccol, tbl->maxcol); + } + tbl->maxcol-=ncols; + gtk_sheet_recalc_left_xpixels(tbl, 0); + return TRUE; +} + +static gint +GrowSheet(GtkSheet *tbl, gint newrows, gint newcols) +{ + gint i,j; + gint inirow, inicol; + + inirow = tbl->maxallocrow + 1; + inicol = tbl->maxalloccol + 1; + + tbl->maxalloccol = tbl->maxalloccol + newcols; + tbl->maxallocrow = tbl->maxallocrow + newrows; + + if(newrows>0){ + tbl->data = (GtkSheetCell***) + g_realloc(tbl->data,(tbl->maxallocrow+1)*sizeof(GtkSheetCell **)+sizeof(double)); + + for(i=inirow; i <= tbl->maxallocrow; i++){ + tbl->data[i] = (GtkSheetCell **) \ + g_malloc((tbl->maxcol+1)*sizeof(GtkSheetCell *)+sizeof(double)); + for(j=0; j<inicol; j++) { + tbl->data[i][j] = NULL; + } + } + + } + + if(newcols>0){ + for(i=0; i <= tbl->maxallocrow; i++) { + tbl->data[i] = (GtkSheetCell **) \ + g_realloc(tbl->data[i],(tbl->maxalloccol+1)*sizeof(GtkSheetCell *)+sizeof(double)); + for(j=inicol; j <= tbl->maxalloccol; j++) { + tbl->data[i][j] = NULL; + } + } + } + + return(0); +} + +static gint +CheckBounds(GtkSheet *tbl, gint row, gint col) +{ + gint newrows=0,newcols=0; + + if(col>tbl->maxalloccol) newcols=col-tbl->maxalloccol; + if(row>tbl->maxallocrow) newrows=row-tbl->maxallocrow; + if(newrows>0 || newcols>0) GrowSheet(tbl, newrows, newcols); + return(0); +} + +/******************************************************************** + * Container Functions: + * gtk_sheet_add + * gtk_sheet_put + * gtk_sheet_attach + * gtk_sheet_remove + * gtk_sheet_move_child + * gtk_sheet_position_child + * gtk_sheet_position_children + * gtk_sheet_realize_child + * gtk_sheet_get_child_at + ********************************************************************/ + +GtkSheetChild * +gtk_sheet_put(GtkSheet *sheet, GtkWidget *child, gint x, gint y) +{ + GtkRequisition child_requisition; + GtkSheetChild *child_info; + + g_return_val_if_fail(sheet != NULL, NULL); + g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL); + g_return_val_if_fail(child != NULL, NULL); + g_return_val_if_fail(child->parent == NULL, NULL); + + child_info = g_new (GtkSheetChild, 1); + child_info->widget = child; + child_info->x = x; + child_info->y = y; + child_info->attached_to_cell = FALSE; + child_info->floating = TRUE; + child_info->xpadding = child_info->ypadding = 0; + child_info->xexpand = child_info->yexpand = FALSE; + child_info->xshrink = child_info->yshrink = FALSE; + child_info->xfill = child_info->yfill = FALSE; + + sheet->children = g_list_append(sheet->children, child_info); + + gtk_widget_set_parent (child, GTK_WIDGET(sheet)); + + gtk_widget_size_request(child, &child_requisition); + + if (GTK_WIDGET_VISIBLE(GTK_WIDGET(sheet))) + { + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + (!GTK_WIDGET_REALIZED(child) || GTK_WIDGET_NO_WINDOW(child))) + gtk_sheet_realize_child(sheet, child_info); + + if(GTK_WIDGET_MAPPED(GTK_WIDGET(sheet)) && + !GTK_WIDGET_MAPPED(child)) + gtk_widget_map(child); + } + + gtk_sheet_position_child(sheet, child_info); + +/* This will avoid drawing on the titles */ + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) + { + if(sheet->row_titles_visible) + gdk_window_show(sheet->row_title_window); + if(sheet->column_titles_visible) + gdk_window_show(sheet->column_title_window); + } + + return (child_info); +} + +void +gtk_sheet_attach_floating (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col) +{ + GdkRectangle area; + GtkSheetChild *child; + + if(row < 0 || col < 0){ + gtk_sheet_button_attach(sheet, widget, row, col); + return; + } + + gtk_sheet_get_cell_area(sheet, row, col, &area); + child = gtk_sheet_put(sheet, widget, area.x, area.y); + child->attached_to_cell = TRUE; + child->row = row; + child->col = col; +} + +void +gtk_sheet_attach_default (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col) +{ + if(row < 0 || col < 0){ + gtk_sheet_button_attach(sheet, widget, row, col); + return; + } + + gtk_sheet_attach(sheet, widget, row, col, GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); +} + +void +gtk_sheet_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col, + gint xoptions, + gint yoptions, + gint xpadding, + gint ypadding) +{ + GdkRectangle area; + GtkSheetChild *child = NULL; + + if(row < 0 || col < 0){ + gtk_sheet_button_attach(sheet, widget, row, col); + return; + } + + child = g_new0(GtkSheetChild, 1); + child->attached_to_cell = TRUE; + child->floating = FALSE; + child->widget = widget; + child->row = row; + child->col = col; + child->xpadding = xpadding; + child->ypadding = ypadding; + child->xexpand = (xoptions & GTK_EXPAND) != 0; + child->yexpand = (yoptions & GTK_EXPAND) != 0; + child->xshrink = (xoptions & GTK_SHRINK) != 0; + child->yshrink = (yoptions & GTK_SHRINK) != 0; + child->xfill = (xoptions & GTK_FILL) != 0; + child->yfill = (yoptions & GTK_FILL) != 0; + + sheet->children = g_list_append(sheet->children, child); + + gtk_sheet_get_cell_area(sheet, row, col, &area); + + child->x = area.x + child->xpadding; + child->y = area.y + child->ypadding; + + if (GTK_WIDGET_VISIBLE(GTK_WIDGET(sheet))) + { + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + (!GTK_WIDGET_REALIZED(widget) || GTK_WIDGET_NO_WINDOW(widget))) + gtk_sheet_realize_child(sheet, child); + + if(GTK_WIDGET_MAPPED(GTK_WIDGET(sheet)) && + !GTK_WIDGET_MAPPED(widget)) + gtk_widget_map(widget); + } + + gtk_sheet_position_child(sheet, child); + +/* This will avoid drawing on the titles */ + + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet))) + { + if(GTK_SHEET_ROW_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->row_title_window); + if(GTK_SHEET_COL_TITLES_VISIBLE(sheet)) + gdk_window_show(sheet->column_title_window); + } + +} + +void +gtk_sheet_button_attach (GtkSheet *sheet, + GtkWidget *widget, + gint row, gint col) +{ + GtkSheetButton *button; + GtkSheetChild *child; + GtkRequisition button_requisition; + + if(row >= 0 && col >= 0) return; + if(row < 0 && col < 0) return; + + child = g_new (GtkSheetChild, 1); + child->widget = widget; + child->x = 0; + child->y = 0; + child->attached_to_cell = TRUE; + child->floating = FALSE; + child->row = row; + child->col = col; + child->xpadding = child->ypadding = 0; + child->xshrink = child->yshrink = FALSE; + child->xfill = child->yfill = FALSE; + + if(row == -1){ + button = &sheet->column[col].button; + button->child = child; + } + else + { + button = &sheet->row[row].button; + button->child = child; + } + + sheet->children = g_list_append(sheet->children, child); + + gtk_sheet_button_size_request(sheet, button, &button_requisition); + + if(row == -1){ + if(button_requisition.height > sheet->column_title_area.height) + sheet->column_title_area.height = button_requisition.height; + if(button_requisition.width > sheet->column[col].width) + sheet->column[col].width = button_requisition.width; + } + + if(col == -1){ + if(button_requisition.width > sheet->row_title_area.width) + sheet->row_title_area.width = button_requisition.width; + if(button_requisition.height > sheet->row[row].height) + sheet->row[row].height = button_requisition.height; + } + + if (GTK_WIDGET_VISIBLE(GTK_WIDGET(sheet))) + { + if(GTK_WIDGET_REALIZED(GTK_WIDGET(sheet)) && + (!GTK_WIDGET_REALIZED(widget) || GTK_WIDGET_NO_WINDOW(widget))) + gtk_sheet_realize_child(sheet, child); + + if(GTK_WIDGET_MAPPED(GTK_WIDGET(sheet)) && + !GTK_WIDGET_MAPPED(widget)) + gtk_widget_map(widget); + } + + if(row == -1) size_allocate_column_title_buttons(sheet); + if(col == -1) size_allocate_row_title_buttons(sheet); + +} + +static void +label_size_request(GtkSheet *sheet, gchar *label, GtkRequisition *req) +{ + gchar *words; + gchar word[1000]; + gint n = 0; + gint row_height = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)) - 2*CELLOFFSET + 2; + + req->height = 0; + req->width = 0; + words=label; + + while(words && *words != '\0'){ + if(*words == '\n' || *(words+1) == '\0'){ + req->height += row_height; + + word[n] = '\0'; + req->width = MAX(req->width, STRING_WIDTH(GTK_WIDGET(sheet), GTK_WIDGET(sheet)->style->font_desc, word)); + n = 0; + } else { + word[n++] = *words; + } + words++; + } + + if(n > 0) req->height -= 2; +} + +static void +gtk_sheet_button_size_request (GtkSheet *sheet, + GtkSheetButton *button, + GtkRequisition *button_requisition) +{ + GtkRequisition requisition; + GtkRequisition label_requisition; + + if(gtk_sheet_autoresize(sheet) && button->label && strlen(button->label) > 0){ + label_size_request(sheet, button->label, &label_requisition); + label_requisition.width += 2*CELLOFFSET; + label_requisition.height += 2*CELLOFFSET; + } else { + label_requisition.height = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + label_requisition.width = COLUMN_MIN_WIDTH; + } + + if(button->child) + { + gtk_widget_size_request(button->child->widget, &requisition); + requisition.width += 2*button->child->xpadding; + requisition.height += 2*button->child->ypadding; + requisition.width += 2*sheet->button->style->xthickness; + requisition.height += 2*sheet->button->style->ythickness; + } + else + { + requisition.height = DEFAULT_ROW_HEIGHT(GTK_WIDGET(sheet)); + requisition.width = COLUMN_MIN_WIDTH; + } + + *button_requisition = requisition; + button_requisition->width = MAX(requisition.width, label_requisition.width); + button_requisition->height = MAX(requisition.height, label_requisition.height); + +} + +static void +gtk_sheet_row_size_request (GtkSheet *sheet, + gint row, + guint *requisition) +{ + GtkRequisition button_requisition; + GList *children; + + gtk_sheet_button_size_request(sheet, &sheet->row[row].button, &button_requisition); + + *requisition = button_requisition.height; + + children = sheet->children; + while(children){ + GtkSheetChild *child = (GtkSheetChild *)children->data; + GtkRequisition child_requisition; + + if(child->attached_to_cell && child->row == row && child->col != -1 && !child->floating && !child->yshrink){ + gtk_widget_get_child_requisition(child->widget, &child_requisition); + + if(child_requisition.height + 2 * child->ypadding > *requisition) + *requisition = child_requisition.height + 2 * child->ypadding; + } + children = children->next; + } + + sheet->row[row].requisition = *requisition; +} + +static void +gtk_sheet_column_size_request (GtkSheet *sheet, + gint col, + guint *requisition) +{ + GtkRequisition button_requisition; + GList *children; + + gtk_sheet_button_size_request(sheet, &sheet->column[col].button, &button_requisition); + + *requisition = button_requisition.width; + + children = sheet->children; + while(children){ + GtkSheetChild *child = (GtkSheetChild *)children->data; + GtkRequisition child_requisition; + + if(child->attached_to_cell && child->col == col && child->row != -1 && !child->floating && !child->xshrink){ + gtk_widget_get_child_requisition(child->widget, &child_requisition); + + if(child_requisition.width + 2 * child->xpadding > *requisition) + *requisition = child_requisition.width + 2 * child->xpadding; + } + children = children->next; + } + + sheet->column[col].requisition = *requisition; +} + +void +gtk_sheet_move_child(GtkSheet *sheet, GtkWidget *widget, gint x, gint y) +{ + GtkSheetChild *child; + GList *children; + + g_return_if_fail(sheet != NULL); + g_return_if_fail(GTK_IS_SHEET(sheet)); + + children = sheet->children; + while(children) + { + child = children->data; + + if(child->widget == widget){ + child->x = x; + child->y = y; + child->row = ROW_FROM_YPIXEL(sheet, y); + child->col = COLUMN_FROM_XPIXEL(sheet, x); + gtk_sheet_position_child(sheet, child); + return; + } + + children = children->next; + } + + g_warning("Widget must be a GtkSheet child"); + +} + +static void +gtk_sheet_position_child(GtkSheet *sheet, GtkSheetChild *child) +{ + GtkRequisition child_requisition; + GtkAllocation child_allocation; + gint xoffset = 0; + gint yoffset = 0; + gint x = 0, y = 0; + GdkRectangle area; + + gtk_widget_get_child_requisition(child->widget, &child_requisition); + + if(sheet->column_titles_visible) + yoffset = sheet->column_title_area.height; + + if(sheet->row_titles_visible) + xoffset = sheet->row_title_area.width; + + if(child->attached_to_cell){ +/* + child->x = COLUMN_LEFT_XPIXEL(sheet, child->col); + child->y = ROW_TOP_YPIXEL(sheet, child->row); + + if(sheet->row_titles_visible) + child->x-=sheet->row_title_area.width; + if(sheet->column_titles_visible) + child->y-=sheet->column_title_area.height; + + width = sheet->column[child->col].width; + height = sheet->row[child->row].height; +*/ + + gtk_sheet_get_cell_area(sheet, child->row, child->col, &area); + child->x = area.x + child->xpadding; + child->y = area.y + child->ypadding; + + if(!child->floating){ + if(child_requisition.width + 2*child->xpadding <= sheet->column[child->col].width){ + if(child->xfill){ + child_requisition.width = child_allocation.width = sheet->column[child->col].width - 2*child->xpadding; + } else { + if(child->xexpand){ + child->x = area.x + sheet->column[child->col].width / 2 - + child_requisition.width / 2; + } + child_allocation.width = child_requisition.width; + } + } else { + if(!child->xshrink){ + gtk_sheet_set_column_width(sheet, child->col, child_requisition.width + 2 * child->xpadding); + } + child_allocation.width = sheet->column[child->col].width - 2*child->xpadding; + } + + if(child_requisition.height + 2*child->ypadding <= sheet->row[child->row].height){ + if(child->yfill){ + child_requisition.height = child_allocation.height = sheet->row[child->row].height - 2*child->ypadding; + } else { + if(child->yexpand){ + child->y = area.y + sheet->row[child->row].height / 2 - + child_requisition.height / 2; + } + child_allocation.height = child_requisition.height; + } + } else { + if(!child->yshrink){ + gtk_sheet_set_row_height(sheet, child->row, child_requisition.height + 2 * child->ypadding); + } + child_allocation.height = sheet->row[child->row].height - 2*child->ypadding; + } + } else { + child_allocation.width = child_requisition.width; + child_allocation.height = child_requisition.height; + } + + x = child_allocation.x = child->x + xoffset; + y = child_allocation.y = child->y + yoffset; + } + else + { + x = child_allocation.x = child->x + sheet->hoffset + xoffset; + x = child_allocation.x = child->x + xoffset; + y = child_allocation.y = child->y + sheet->voffset + yoffset; + y = child_allocation.y = child->y + yoffset; + child_allocation.width = child_requisition.width; + child_allocation.height = child_requisition.height; + } + + gtk_widget_size_allocate(child->widget, &child_allocation); + gtk_widget_queue_draw(child->widget); +} + +static void +gtk_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkSheet *sheet; + GtkSheetChild *child; + GList *children; + + g_return_if_fail (GTK_IS_SHEET (container)); + g_return_if_fail (callback != NULL); + + sheet = GTK_SHEET (container); + + children = sheet->children; + while (children) + { + child = children->data; + children = children->next; + + (* callback) (child->widget, callback_data); + } + if(sheet->button) + (* callback) (sheet->button, callback_data); + if(sheet->sheet_entry) + (* callback) (sheet->sheet_entry, callback_data); +} + + +static void +gtk_sheet_position_children(GtkSheet *sheet) +{ + GList *children; + GtkSheetChild *child; + + children = sheet->children; + + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->col !=-1 && child->row != -1) + gtk_sheet_position_child(sheet, child); + + if(child->row == -1){ + if(child->col < MIN_VISIBLE_COLUMN(sheet) || + child->col > MAX_VISIBLE_COLUMN(sheet)) + gtk_sheet_child_hide(child); + else + gtk_sheet_child_show(child); + } + if(child->col == -1){ + if(child->row < MIN_VISIBLE_ROW(sheet) || + child->row > MAX_VISIBLE_ROW(sheet)) + gtk_sheet_child_hide(child); + else + gtk_sheet_child_show(child); + } + + children = children->next; + } + +} + +static void +gtk_sheet_remove (GtkContainer *container, GtkWidget *widget) +{ + GtkSheet *sheet; + GList *children; + GtkSheetChild *child = 0; + + g_return_if_fail(container != NULL); + g_return_if_fail(GTK_IS_SHEET(container)); + + sheet = GTK_SHEET(container); + + children = sheet->children; + + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->widget == widget) break; + + children = children->next; + } + + if (children) + { + if(child->row == -1) + sheet->row[child->col].button.child = NULL; + + if(child->col == -1) + sheet->column[child->row].button.child = NULL; + + gtk_widget_unparent (widget); + child->widget = NULL; + + sheet->children = g_list_remove_link (sheet->children, children); + g_list_free_1 (children); + g_free(child); + } + +} + +static void +gtk_sheet_realize_child(GtkSheet *sheet, GtkSheetChild *child) +{ + GtkWidget *widget; + + widget = GTK_WIDGET(sheet); + + if(GTK_WIDGET_REALIZED(widget)){ + if(child->row == -1) + gtk_widget_set_parent_window(child->widget, sheet->column_title_window); + else if(child->col == -1) + gtk_widget_set_parent_window(child->widget, sheet->row_title_window); + else + gtk_widget_set_parent_window(child->widget, sheet->sheet_window); + } + + gtk_widget_set_parent(child->widget, widget); +} + + + +GtkSheetChild * +gtk_sheet_get_child_at(GtkSheet *sheet, gint row, gint col) +{ + GList *children; + GtkSheetChild *child = 0; + + g_return_val_if_fail(sheet != NULL, NULL); + g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL); + + children = sheet->children; + + while(children) + { + child = (GtkSheetChild *)children->data; + + if(child->attached_to_cell) + if(child->row == row && child->col == col) break; + + children = children->next; + } + + if(children) return child; + + return NULL; +} + +static void +gtk_sheet_child_hide(GtkSheetChild *child) +{ + g_return_if_fail(child != NULL); + gtk_widget_hide(child->widget); +} + +static void +gtk_sheet_child_show(GtkSheetChild *child) +{ + g_return_if_fail(child != NULL); + + gtk_widget_show(child->widget); +} diff --git a/gattrib/src/i_basic.c b/gattrib/src/i_basic.c new file mode 100644 index 000000000..5282bc815 --- /dev/null +++ b/gattrib/src/i_basic.c @@ -0,0 +1,123 @@ +/* gEDA - GPL Electronic Design Automation + * gschem - gEDA Schematic Capture + * Copyright (C) 1998-2000 Ales V. Hvezda + * + * 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 USA + */ + + + +#include <config.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + + +/*------------------------------------------------------------------ + * Gattrib specific includes. Note that include order is important. + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + +/* --- This is necessary for i_basic.c --- */ +#include "../include/x_states.h" + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +static void i_update_status(TOPLEVEL * w_current, const char *string) +{ + if (!w_current->status_label) { + return; + } + + if (string) { + /* NOTE: consider optimizing this if same label */ + w_current->DONT_RESIZE = 1; + gtk_label_set(GTK_LABEL(w_current->status_label), (char *) string); + } +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void i_update_cursor(TOPLEVEL * w_current) +{ + + /* SDB asks: Do we need this?!?!?!?! */ + + GdkCursor *cursor; + cursor = gdk_cursor_new(GDK_ARROW); + gdk_window_set_cursor(w_current->window, cursor); + gdk_cursor_destroy(cursor); + +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void i_set_filename(TOPLEVEL * w_current, const char *string) +{ + char trunc_string[41]; + int len; + int i; + + if (!w_current->filename_label) { + return; + } + + if (string) { + len = strlen(string); + w_current->DONT_RESIZE = 1; + + if (w_current->filename_label) { + if (len > 40) { + + trunc_string[0] = '.'; + trunc_string[1] = '.'; + trunc_string[2] = '.'; + + trunc_string[40] = '\0'; + for (i = 39; i > 2; i--) { + if (len >= 0) { + trunc_string[i] = string[len]; + } else { + break; + } + len--; + } + + gtk_label_set(GTK_LABEL(w_current->filename_label), trunc_string); + + } else { + + gtk_label_set(GTK_LABEL(w_current-> + filename_label), (char *) string); + } + } + } +} + + + + + + diff --git a/gattrib/src/i_vars.c b/gattrib/src/i_vars.c new file mode 100644 index 000000000..3a7f7eba9 --- /dev/null +++ b/gattrib/src/i_vars.c @@ -0,0 +1,88 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +#include <config.h> + +#include <stdio.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + + + +/*------------------------------------------------------------------ + * Gattrib specific includes. Note that include order is important. + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + +#include "../include/i_vars.h" + + + +/*------------------------------------------------------------------ + * Define the vars we'll use later + *------------------------------------------------------------------*/ +int default_graphic_color = GRAPHIC_COLOR; +int default_text_color = TEXT_COLOR; +int default_text_size = 10; +int default_text_caps = LOWER; + +int default_attribute_color = ATTRIBUTE_COLOR; +int default_paper_width = 11000; /* letter size */ +int default_paper_height = 85000; +int default_init_right = WIDTH_C; +int default_init_bottom = HEIGHT_C; + + +/*------------------------------------------------------------------ + * This initializes the vars in pr_current. It is copied from + * gnetlist. It is called by the guile initializtion fcns. + * It competes with i_window_vars_set. This is badly architected!! + *------------------------------------------------------------------*/ +void i_vars_set(TOPLEVEL * pr_current) +{ + i_vars_libgeda_set(pr_current); +} + + +/*------------------------------------------------------------------ + * This initializes the vars in pr_current. It is copied from + * gschem. It is called from s_toplevel_init. + *------------------------------------------------------------------*/ +void i_window_vars_set(TOPLEVEL * pr_current) +{ + + /* i_vars_libgeda_set(pr_current); */ + + pr_current->graphic_color = default_graphic_color; + pr_current->text_color = default_text_color; + pr_current->text_size = default_text_size; + pr_current->text_caps = default_text_caps; + + pr_current->attribute_color = default_attribute_color; + pr_current->paper_width = default_paper_width; + pr_current->paper_height = default_paper_height; + pr_current->init_right = default_init_right; + pr_current->init_bottom = default_init_bottom; + +} diff --git a/gattrib/src/listsort.c b/gattrib/src/listsort.c new file mode 100644 index 000000000..a0be61039 --- /dev/null +++ b/gattrib/src/listsort.c @@ -0,0 +1,173 @@ +#include <stdio.h> + +/*----------------------------------------------------------------* + * Linked list sorting code taken from + * http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + * and hacked to serve in gattrib by SDB. + *----------------------------------------------------------------*/ + +/* + * Demonstration code for sorting a linked list. + * + * The algorithm used is Mergesort, because that works really well + * on linked lists, without requiring the O(N) extra space it needs + * when you do it on arrays. + * + * This code can handle singly and doubly linked lists, and + * circular and linear lists too. For any serious application, + * you'll probably want to remove the conditionals on `is_circular' + * and `is_double' to adapt the code to your own purpose. + * + */ + +/* + * This file is copyright 2001 Simon Tatham. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* --- We don't need these 'cause they are already defined elsewhere --- + * #define FALSE 0 + * #define TRUE 1 + */ + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + +/*----------------------------------------------------------------* + * Comparison function -- compare values of string data. + *----------------------------------------------------------------*/ +int cmp(STRING_LIST *a, STRING_LIST *b) { + return strcmp(a->data, b->data); +} + +/*----------------------------------------------------------------* + * This is the actual sort function. Notice that it returns the new + * head of the list. (It has to, because the head will not + * generally be the same element after the sort.) So unlike sorting + * an array, where you can do + * + * sort(myarray); + * + * you now have to do + * + * list = listsort(mylist); + *----------------------------------------------------------------*/ +STRING_LIST *listsort(STRING_LIST *list, int is_circular, int is_double) { + STRING_LIST *p, *q, *e, *tail, *oldhead; + int insize, nmerges, psize, qsize, i; + + /* + * Silly special case: if `list' was passed in as NULL, return + * NULL immediately. + */ + if (!list) + return NULL; + + insize = 1; + + while (1) { + p = list; + oldhead = list; /* only used for circular linkage */ + list = NULL; + tail = NULL; + + nmerges = 0; /* count number of merges we do in this pass */ + + while (p) { + nmerges++; /* there exists a merge to be done */ + /* step `insize' places along from p */ + q = p; + psize = 0; + for (i = 0; i < insize; i++) { + psize++; + if (is_circular) + q = (q->next == oldhead ? NULL : q->next); + else + q = q->next; + if (!q) break; + } + + /* if q hasn't fallen off end, we have two lists to merge */ + qsize = insize; + + /* now we have two lists; merge them */ + while (psize > 0 || (qsize > 0 && q)) { + + /* decide whether next element of merge comes from p or q */ + if (psize == 0) { + /* p is empty; e must come from q. */ + e = q; q = q->next; qsize--; + if (is_circular && q == oldhead) q = NULL; + } else if (qsize == 0 || !q) { + /* q is empty; e must come from p. */ + e = p; p = p->next; psize--; + if (is_circular && p == oldhead) p = NULL; + } else if (cmp(p,q) <= 0) { + /* First element of p is lower (or same); + * e must come from p. */ + e = p; p = p->next; psize--; + if (is_circular && p == oldhead) p = NULL; + } else { + /* First element of q is lower; e must come from q. */ + e = q; q = q->next; qsize--; + if (is_circular && q == oldhead) q = NULL; + } + + /* add the next element to the merged list */ + if (tail) { + tail->next = e; + } else { + list = e; + } + if (is_double) { + /* Maintain reverse pointers in a doubly linked list. */ + e->prev = tail; + } + tail = e; + } + + /* now p has stepped `insize' places along, and q has too */ + p = q; + } + if (is_circular) { + tail->next = list; + if (is_double) + list->prev = tail; + } else + tail->next = NULL; + + /* If we have done only one merge, we're finished. */ + if (nmerges <= 1) /* allow for nmerges==0, the empty list case */ + return list; + + /* Otherwise repeat, merging lists twice the size */ + insize *= 2; + } +} + diff --git a/gattrib/src/parsecmd.c b/gattrib/src/parsecmd.c new file mode 100644 index 000000000..c241edc44 --- /dev/null +++ b/gattrib/src/parsecmd.c @@ -0,0 +1,91 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +#include <config.h> + +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + +#define OPTIONS "qvh" + +#ifndef OPTARG_IN_UNISTD +extern char *optarg; +extern int optind; +#endif + + +void usage(char *cmd) +{ + printf("Usage: %s [OPTIONS] filename1 ... filenameN\n", cmd); + printf(" -q Quiet mode\n"); + printf(" -v Verbose mode on\n"); + printf("\n"); + exit(0); +} + + +int parse_commandline(int argc, char *argv[]) +{ + int ch; + + while ((ch = getopt(argc, argv, OPTIONS)) != -1) { + switch (ch) { + + case 'v': + verbose_mode = TRUE; + break; + + case 'q': + quiet_mode = TRUE; + break; + + case 'h': + usage(argv[0]); + break; + + case '?': + default: + usage(argv[0]); + break; + } + } + + if (quiet_mode) { + verbose_mode = FALSE; + } + + return (optind); +} + + + diff --git a/gattrib/src/s_misc.c b/gattrib/src/s_misc.c new file mode 100644 index 000000000..143073df0 --- /dev/null +++ b/gattrib/src/s_misc.c @@ -0,0 +1,71 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +#include <config.h> + +#include <stdio.h> +#include <math.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + +/*------------------------------------------------------------------ + * The below fcns identical to those defined in + * geda-gnetlist/src/s_misc.c + *------------------------------------------------------------------*/ +static int char_index = 0; + +void verbose_print(char *string) +{ + if (verbose_mode) { + printf("%s", string); + char_index++; + if ((char_index + 1) >= 78) { + printf("\n"); + char_index = 0; + } + } +} + +void verbose_done(void) +{ + if (verbose_mode) { + if (char_index >= 70) { + printf("\nDONE\n"); + } else { + printf(" DONE\n"); + } + + char_index = 0; + } +} + +void verbose_reset_index(void) +{ + char_index = 0; +} diff --git a/gattrib/src/s_object.c b/gattrib/src/s_object.c new file mode 100644 index 000000000..66a2d7cd6 --- /dev/null +++ b/gattrib/src/s_object.c @@ -0,0 +1,329 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns involved in manipulating the OBJECT data + * structure. OBJECT is defined in libgeda. An OBJECT is a graphical + * primitive normally used in gschem. Example OBJECTs: some text, + * a component (complex), a pin, a line, etc. + * + * The fcns herein are fcns which I wrote as wrappers to the + * fcns in libgeda. + *------------------------------------------------------------------ */ + +#include <config.h> + +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include <math.h> + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + + +/* =================== Public Functions ====================== */ + +/*------------------------------------------------------------------ + * This fcn adds a new attrib to o_current, when o_current is a + * component. It does it in the following + * way: + * 1. It creates an object -- "attrib_graphic" -- and fills it in. + * 2. It gets the position info from o_current's refdes attrib and + * calls o_text_add to add pos info and name=value string + * to attrib_graphic. + * 3. It calls o_attrib_add to wrap attrib_graphic with (ATTRIB ) + *------------------------------------------------------------------ */ +void s_object_add_comp_attrib_to_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value) +{ + char *name_value_pair; + OBJECT *attrib_graphic_object; + + /* One last sanity check */ + if (strlen(new_attrib_value) != 0) { + name_value_pair = u_basic_strdup_multiple(new_attrib_name, "=", new_attrib_value, NULL); + attrib_graphic_object = s_object_attrib_add_attrib_in_object(pr_current, name_value_pair, INVISIBLE, + SHOW_NAME_VALUE, o_current); + } + + return; + +} + + +/*------------------------------------------------------------------* + * This needs to be filled in. + *------------------------------------------------------------------*/ +void s_object_add_net_attrib_to_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value) +{ + /* TBD */ +} + + +/*------------------------------------------------------------------ + * This fcn adds a new attrib to o_current, when o_current is a + * pin. It does it in the following + * way: + * 1. It creates an object -- "attrib_graphic" -- and fills it in. + * 2. It gets the position info from o_current's refdes attrib and + * calls o_text_add to add pos info and name=value string + * to attrib_graphic. + * 3. It calls o_attrib_add to wrap attrib_graphic with (ATTRIB ) + * Question: Do I really need separate fcns for comps, nets, and + * pins??? + *------------------------------------------------------------------ */ +void s_object_add_pin_attrib_to_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value) +{ + char *name_value_pair; + OBJECT *attrib_graphic_object; + + /* One last sanity check */ + if (strlen(new_attrib_value) != 0) { + name_value_pair = u_basic_strdup_multiple(new_attrib_name, "=", new_attrib_value, NULL); + attrib_graphic_object = s_object_attrib_add_attrib_in_object(pr_current, name_value_pair, INVISIBLE, + SHOW_NAME_VALUE, o_current); + } + + return; + +} + + + + +/*------------------------------------------------------------------ + * This fcn finds the instance of attrib_name on o_current, and + * replaces it's value wiht new_attrib_value. + *------------------------------------------------------------------*/ +void s_object_replace_attrib_in_object(OBJECT *o_current, char *new_attrib_name, + char *new_attrib_value) +{ + ATTRIB *a_current; + char *old_attrib_text; + char *old_attrib_name; + char *new_attrib_text; + + a_current = o_current->attribs; + while (a_current != NULL) { + if (a_current->object->type == OBJ_TEXT + && a_current->object->text != NULL) { /* found an attribute */ + + /* may need to check more thoroughly here. . . . */ + old_attrib_text = u_basic_strdup(a_current->object->text->string); + old_attrib_name = u_basic_breakup_string(old_attrib_text, '=', 0); + + if (strcmp(old_attrib_name, new_attrib_name) == 0) { + /* create attrib=value text string & stuff it back into pr_current */ + new_attrib_text = u_basic_strdup_multiple(new_attrib_name, "=", new_attrib_value, NULL); + free(a_current->object->text->string); /* remove old attrib string */ + a_current->object->text->string = u_basic_strdup(new_attrib_text); /* insert new attrib string */ + free(new_attrib_text); + free(old_attrib_text); + free(old_attrib_name); + return; /* we are done -- leave. */ + } + free(old_attrib_text); + free(old_attrib_name); + } /* if (a_current . . . . */ + + a_current = a_current->next; + } + + /* if we get here, it's because we have failed to find the attrib on the component. + * This is an error condition. */ + fprintf(stderr, + "In s_object_replace_attrib_in_object, we have failed to find the attrib %s on the component. Exiting . . .\n", + new_attrib_name); + exit(-1); +} + + +/*------------------------------------------------------------------ + * This fcn removes attrib from o_current. + *------------------------------------------------------------------*/ +void s_object_remove_attrib_in_object(OBJECT *o_current, char *new_attrib_name) +{ + + ATTRIB *a_current; + OBJECT *attribute_object; + char *old_attrib_text; + char *old_attrib_name; + + a_current = o_current->attribs; + while (a_current != NULL) { + if (a_current->object->type == OBJ_TEXT + && a_current->object->text != NULL) { /* found an attribute */ + + /* may need to check more thoroughly here. . . . */ + old_attrib_text = u_basic_strdup(a_current->object->text->string); + old_attrib_name = u_basic_breakup_string(old_attrib_text, '=', 0); + + if (strcmp(old_attrib_name, new_attrib_name) == 0) { + /* We've found the attrib. Delete it and then return. */ + +#ifdef DEBUG + printf("In s_object_remove_attrib_in_object, removing attrib with name = %s\n", old_attrib_name); +#endif + + attribute_object = a_current->object; + s_object_delete_text_object_in_object(pr_current, attribute_object); + + free(old_attrib_text); + free(old_attrib_name); + return; /* we are done -- leave. */ + } + free(old_attrib_text); + free(old_attrib_name); + } + a_current = a_current->next; + } + + /* if we get here, it's because we have failed to find the attrib on the component. + * This is an error condition. */ + fprintf(stderr, + "In s_object_remove_attrib_in_object, we have failed to find the attrib %s on the component. Exiting . . .\n", + new_attrib_name); + exit(-1); +} + + + +/*------------------------------------------------------------------ + * This fcn attaches the name=value pair to the OBJECT "object" It + * was stolen from gschem/src/o_attrib.c:o_attrib_add_attrib and + * hacked for gattrib. Does it need to return OBJECT? + *------------------------------------------------------------------*/ +OBJECT *s_object_attrib_add_attrib_in_object(TOPLEVEL * pr_current, char *text_string, + int visibility, int show_name_value, + OBJECT * object) +{ + int world_x = -1, world_y = -1; + int color; + int left, right, top, bottom; + OBJECT *o_current; + + color = pr_current->detachedattr_color; + + o_current = object; + + /* creating a toplevel or unattached attribute */ + if (o_current) { + /* get coordinates of where to place the text object */ + switch (o_current->type) { + case (OBJ_COMPLEX): + world_x = o_current->complex->x; + world_y = o_current->complex->y; + color = pr_current->attribute_color; + break; + + case (OBJ_NET): + world_x = o_current->complex->x; + world_y = o_current->complex->y; + color = pr_current->attribute_color; + break; + + default: + fprintf(stderr, "In s_object_attrib_add_attrib_in_object, trying to add attrib to non-complex or non-net!\n"); + exit(-1); + } + } else { /* This must be a floating attrib, but what is that !?!?!?!?! */ + world_get_complex_bounds(pr_current, + pr_current->page_current->object_head, + &left, &top, &right, &bottom); + + /* this really is the lower left hand corner */ + world_x = left; + world_y = top; + + /* printf("%d %d\n", world_x, world_y); */ + color = pr_current->detachedattr_color; + } + + /* first create text item */ +#if DEBUG + printf("=== In s_object_attrib_add_attrib_in_object, about to attach new text attrib with properties:\n"); + printf(" color = %d\n", color); + printf(" text_string = %s \n", text_string); + printf(" text_size = %d \n", pr_current->text_size); + printf(" visibility = %d \n", visibility); + printf(" show_name_value = %d \n", show_name_value); +#endif + + pr_current->page_current->object_tail = o_text_add(pr_current, + pr_current->page_current->object_tail, + OBJ_TEXT, + color, + world_x, + world_y, + LOWER_LEFT, + 0, /* zero is angle */ + text_string, + pr_current->text_size, /* current text size */ + visibility, + show_name_value); + + /* now pr_current->page_current->object_tail contains new text item */ + + /* now attach the attribute to the object (if o_current is not NULL) */ + /* remember that o_current contains the object to get the attribute */ + if (o_current) { + o_attrib_attach(pr_current, pr_current->page_current->object_head, + pr_current->page_current->object_tail, o_current); + } + + o_selection_add(pr_current->page_current->selection2_head, + pr_current->page_current->object_tail); + + + pr_current->page_current->CHANGED = 1; + + return (pr_current->page_current->object_tail); +} + + + + +/*------------------------------------------------------------------ + * This fcn deletes the text object pointed to by text_object. It + * was shamelessly stolen from gschem/src/o_delete.c and hacked + * for gattrib by SDB. + *------------------------------------------------------------------*/ +void s_object_delete_text_object_in_object(TOPLEVEL * pr_current, OBJECT * text_object) +{ + s_delete(pr_current, text_object); + pr_current->page_current->CHANGED = 1; + +#if 0 + /* What does this do?!?!? Maybe I don't need it!!! */ + pr_current->page_current->object_tail = + (OBJECT *) return_tail(pr_current->page_current->object_head); +#endif + +} + diff --git a/gattrib/src/s_rename.c b/gattrib/src/s_rename.c new file mode 100644 index 000000000..e47778ad7 --- /dev/null +++ b/gattrib/src/s_rename.c @@ -0,0 +1,270 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +#include <config.h> + +#include <stdio.h> +#include <ctype.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ASSERT_H +#include <assert.h> +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + + +typedef struct { + char *src; + char *dest; +} RENAME; + +#define MAX_RENAME 64 +#define MAX_SETS 10 + +/* size is fixed... TODO: maybe make this dynamic */ +static RENAME rename_pairs[MAX_SETS][MAX_RENAME]; + +static int rename_counter = 0; +static int cur_set = 0; + +void s_rename_init(void) +{ + int i, j; + + for (i = 0; i < MAX_SETS; i++) { + for (j = 0; j < MAX_RENAME; j++) { + rename_pairs[i][j].src = NULL; + rename_pairs[i][j].dest = NULL; + } + } + rename_counter = 0; + cur_set = 0; +} + +void s_rename_destroy_all(void) +{ + int i, j; + + for (i = 0; i < MAX_SETS; i++) { + for (j = 0; j < MAX_RENAME; j++) { + + if (rename_pairs[i][j].src) { + free(rename_pairs[i][j].src); + rename_pairs[i][j].src = NULL; + } + + if (rename_pairs[i][j].dest) { + free(rename_pairs[i][j].dest); + rename_pairs[i][j].dest = NULL; + } + } + } + rename_counter = 0; + cur_set = 0; +} + +void s_rename_next_set(void) +{ + if (cur_set == MAX_SETS) { + fprintf(stderr, + "Increase number of rename_pair sets in s_net.c\n"); + exit(-1); + } + cur_set++; + rename_counter = 0; +} + +void s_rename_print(void) +{ + int i,j; + + for (i = 0; i < MAX_SETS; i++) { + for (j = 0; j < MAX_RENAME; j++) { + if (rename_pairs[i][j].src) { + printf("%d) Source: _%s_", i, rename_pairs[i][j].src); + } + + if (rename_pairs[i][j].dest) { + printf(" -> Dest: _%s_\n", rename_pairs[i][j].dest); + } + } + } +} + +/* if the src is found, return true */ +/* if the dest is found, also return true, but warn user */ +/* If quiet_flag is true than don't print anything */ +int s_rename_search(char *src, char *dest, int quiet_flag) +{ + int i; + for (i = 0; i < rename_counter; i++) { + + if (rename_pairs[cur_set][i].src && rename_pairs[cur_set][i].dest) { + + if (strcmp(src, rename_pairs[cur_set][i].src) == 0) { + return (TRUE); + } + + if (strcmp(dest, rename_pairs[cur_set][i].src) == 0) { + if (!quiet_flag) { + fprintf(stderr, + "WARNING: Trying to rename something twice:\n\t%s and %s\nare both a src and dest name\n", + dest, rename_pairs[cur_set][i].src); + fprintf(stderr, + "This warning is okay if you have multiple levels of hierarchy!\n"); + } + return (TRUE); + } + } + + } + + return (FALSE); +} + +void s_rename_add(char *src, char *dest) +{ + int flag; + int i; + + if (src == NULL || dest == NULL) { + return; + } + + flag = s_rename_search(src, dest, FALSE); + + if (flag) { + // Rename_counter may be incremented within this loop, so it cannot + // be used in the loop exit condition. Just iterate over the number + // of renames that were in the list at the start of the loop. + int orig_rename_counter = rename_counter; + for (i = 0; i < orig_rename_counter; i++) { + if (rename_pairs[cur_set][i].src + && rename_pairs[cur_set][i].dest) { + if (strcmp(dest, rename_pairs[cur_set][i].src) == 0) { +#if DEBUG + printf + ("Found dest [%s] in src [%s] and that had a dest as: [%s]\nSo you want rename [%s] to [%s]\n", + dest, rename_pairs[cur_set][i].src, + rename_pairs[cur_set][i].dest, + src, rename_pairs[cur_set][i].dest); +#endif + + rename_pairs[cur_set][rename_counter].src = + (char *) malloc(sizeof(char) * (strlen(src) + 1)); + strcpy(rename_pairs[cur_set][rename_counter].src, src); + rename_pairs[cur_set][rename_counter].dest = + (char *) malloc(sizeof(char) * + (strlen + (rename_pairs[cur_set][i].dest) + + 1)); + strcpy(rename_pairs[cur_set][rename_counter].dest, + rename_pairs[cur_set][i].dest); + rename_counter++; + } + } + } + } else { + + rename_pairs[cur_set][rename_counter].src = + (char *) malloc(sizeof(char) * (strlen(src) + 1)); + strcpy(rename_pairs[cur_set][rename_counter].src, src); + rename_pairs[cur_set][rename_counter].dest = + (char *) malloc(sizeof(char) * (strlen(dest) + 1)); + strcpy(rename_pairs[cur_set][rename_counter].dest, dest); + rename_counter++; + } + if (rename_counter == MAX_RENAME) { + fprintf(stderr, + "Increase number of rename_pairs (MAX_RENAME) in s_rename.c\n"); + exit(-1); + } + +} + +void s_rename_all_lowlevel(NETLIST * netlist_head, char *src, char *dest) +{ + NETLIST *nl_current = NULL; + CPINLIST *pl_current; + + nl_current = netlist_head; + + while (nl_current != NULL) { + if (nl_current->cpins) { + pl_current = nl_current->cpins; + while (pl_current != NULL) { + + if (pl_current->net_name != NULL) { + + if (strcmp(pl_current->net_name, src) == 0) { + + /* this is a bad idea */ + /* because inside nets-> */ + /* there is another pointer */ + /*free(pl_current->net_name); */ + + pl_current->net_name = + malloc(sizeof(char) * (strlen(dest) + 1)); + strcpy(pl_current->net_name, dest); + } + } + + pl_current = pl_current->next; + } + } + nl_current = nl_current->next; + } + +} + +void s_rename_all(TOPLEVEL * pr_current, NETLIST * netlist_head) +{ + int i; + +#if DEBUG + s_rename_print(); +#endif + + for (i = 0; i < rename_counter; i++) { + + verbose_print("R"); + +#if DEBUG + printf("%d Renaming: %s -> %s\n", i, rename_pairs[cur_set][i].src, + rename_pairs[cur_set][i].dest); +#endif + + s_rename_all_lowlevel(netlist_head, + rename_pairs[cur_set][i].src, + rename_pairs[cur_set][i].dest); + } +} + diff --git a/gattrib/src/s_sheet_data.c b/gattrib/src/s_sheet_data.c new file mode 100644 index 000000000..c8237c1e0 --- /dev/null +++ b/gattrib/src/s_sheet_data.c @@ -0,0 +1,456 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns involved in manipulating an entire SHEET_DATA + * structure. The SHEET_DATA structure is the intermediate structure + * between TOPLEVEL (gEDA's native format) and the graphical gtksheet + * widget (from gtkextra), which is the spreadsheet widget displaying + * the attribs. + *------------------------------------------------------------------*/ + +#include <config.h> + +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include <math.h> + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + +/*------------------------------------------------------------------ + * This fcn is the sheet_data creator. It returns a pointer to + * an initialized SHEET_DATA struct. + *------------------------------------------------------------------*/ +SHEET_DATA *s_sheet_data_new() +{ + SHEET_DATA *new_sheet; + + new_sheet = (SHEET_DATA *) malloc(sizeof(SHEET_DATA)); + + /* We will malloc and fill out the comp table later. */ + new_sheet->component_table = NULL; + + /* We will malloc and fill out the net table later. */ + new_sheet->net_table = NULL; + + /* We will malloc and fill out the pin table later. */ + new_sheet->pin_table = NULL; + + /* Now we create the first cell in each master list. */ + new_sheet->master_comp_list_head = (STRING_LIST *) s_string_list_new(); + new_sheet->master_comp_attrib_list_head = (STRING_LIST *) s_string_list_new(); + new_sheet->comp_count = 0; + new_sheet->comp_attrib_count = 0; + + new_sheet->master_net_list_head = (STRING_LIST *) s_string_list_new(); + new_sheet->master_net_attrib_list_head = (STRING_LIST *) s_string_list_new(); + new_sheet->net_count = 0; + new_sheet->net_attrib_count = 0; + + new_sheet->master_pin_list_head = (STRING_LIST *) s_string_list_new(); + new_sheet->master_pin_attrib_list_head = (STRING_LIST *) s_string_list_new(); + new_sheet->pin_count = 0; + new_sheet->pin_attrib_count = 0; + + return (new_sheet); + +} + + + +/*------------------------------------------------------------------ + * This fcn adds to the master list of components refdeses by running + * through the components and recording the comp refdeses + * it discovers. Then it sorts them into alphabetical order. + * Data struct being searched is: OBJECT->attribs(->next. . .)->object->text->string + *------------------------------------------------------------------*/ +void s_sheet_data_add_master_comp_list_items(OBJECT *start_obj) { + char *temp_uref; + OBJECT *o_current; + OBJECT *temp_object; + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("=========== Just entered s_sheet_data_add_master_comp_list_items! ==============\n"); +#endif + + if (verbose_mode) { + printf("- Starting master comp list creation.\n"); + } + + /* ----- Iterate through all objects found on page looking for components ----- */ + o_current = start_obj; + while (o_current != NULL) { + +#ifdef DEBUG + printf("In s_sheet_data_add_master_comp_list_items, examining o_current->name = %s\n", o_current->name); +#endif + + /*----- only process if this is a non-graphical comp with attributes ----*/ + if ( (o_current->type == OBJ_COMPLEX) && + o_current->attribs && + !o_attrib_search_component(o_current, "graphical") ) { + +#if DEBUG + fflush(stderr); + fflush(stdout); + printf(" In s_sheet_data_add_master_comp_list_items; found component on page\n"); + fprintf(stderr, ". . . . complex_basename = %s.\n", o_current->complex_basename); +#endif + verbose_print(" C"); + + /*------ Try to get the refdes -----*/ + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + if (!temp_uref) { + temp_uref = o_attrib_search_name_single(o_current, "uref", NULL); // deprecated + if (temp_uref) { + printf("WARNING: Found uref=%s, uref= is deprecated, please use refdes=\n", temp_uref); + } else { /* didn't find refdes. Report error to log. */ + fprintf(stderr, " In s_sheet_data_add_master_comp_list_items, found non-graphical component with no refdes.\n"); +#ifdef DEBUG + fprintf(stderr, ". . . . complex_basename = %s.\n", o_current->complex_basename); +#endif + } + } /*--- if(!temp_uref) ---*/ + + + /* Now that we have refdes, store refdes and attach attrib list to component */ + if (temp_uref) { +#if DEBUG + fflush(stderr); + fflush(stdout); + printf(" In s_sheet_add_master_comp_list, about to add to master list refdes = %s\n", temp_uref); +#endif + s_string_list_add_item(sheet_head->master_comp_list_head, &(sheet_head->comp_count), temp_uref); + free(temp_uref); + } + + } /* if (o_current->type == OBJ_COMPLEX . . . . .) */ + + o_current = o_current->next; /* iterate to next object on page */ + } /* while o_current != NULL */ + + return; +} + + +/*------------------------------------------------------------------ + * This fcn adds to the master list of comp attribs by running + * through each component on the page and recording all attribs + * it discovers. Then it sorts them into an order used for the + * horiz listing of the attribs on the spreadsheet. + * Data struct being searched is: sheet_head->component_list_head->attrib->name; + *------------------------------------------------------------------*/ +void s_sheet_data_add_master_comp_attrib_list_items(OBJECT *start_obj) { + char *attrib_text; + char *attrib_name; + char *attrib_value; + char *temp_uref; + OBJECT *o_current; + OBJECT *temp_object; + ATTRIB *a_current; + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("=========== Just entered s_sheet_data_add_master_comp_attrib_list_items! ==============\n"); +#endif + + if (verbose_mode) { + printf("- Starting master comp attrib list creation.\n"); + } + + /* ----- Iterate through all objects found on page looking for components ----- */ + o_current = start_obj; + while (o_current != NULL) { + +#ifdef DEBUG + printf("In s_sheet_data_add_master_comp_attrib_list_items, examining o_current->name = %s\n", o_current->name); +#endif + + /*----- only process if this is a non-graphical comp with attributes ----*/ + if ( (o_current->type == OBJ_COMPLEX) && + o_current->attribs && + !o_attrib_search_component(o_current, "graphical") ) { + + + verbose_print(" C"); + + /*------ Iterate through all attribs found on component -----*/ + a_current = o_current->attribs; /* This has a side effect. Why? */ + while (a_current != NULL) { + if (a_current->object->type == OBJ_TEXT + && a_current->object->text != NULL) { /* found an attribute */ + attrib_text = u_basic_strdup(a_current->object->text->string); + attrib_name = u_basic_breakup_string(attrib_text, '=', 0); + if (strcmp(attrib_name, "refdes") != 0) { + /* Don't include "refdes" because it is already in other master list */ +#if DEBUG + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + fflush(stderr); + fflush(stdout); + printf("In s_sheet_data_add_master_comp_attrib_list, examining component refdes = %s\n", temp_uref); + free(temp_uref); + printf(" . . . from this component, about to add to master comp attrib list attrib = %s\n", attrib_name); +#endif + s_string_list_add_item(sheet_head->master_comp_attrib_list_head, + &(sheet_head->comp_attrib_count), attrib_name); + } /* if (strcmp(attrib_name, "refdes") != 0) */ + free(attrib_name); + free(attrib_text); + } + a_current = a_current->next; + } /* while */ + + } /* if (o_current->type == OBJ_COMPLEX) */ + + o_current = o_current->next; + } /* while (o_current != NULL) */ + + /* ----- Now sort component list into alphabetical order ----- */ + + return; +} + + + +/*------------------------------------------------------------------ + * This fcn builds the master list of net names by running + * through the individual cells and recording the net refdeses + * it discovers. + *------------------------------------------------------------------*/ +void s_sheet_data_add_master_net_list_items(OBJECT *start_obj) { + return; +} + + +/*------------------------------------------------------------------ + * This fcn builds the master list of net attribs. + *------------------------------------------------------------------*/ +void s_sheet_data_add_master_net_attrib_list_items(OBJECT *start_obj) { + return; +} + + +/*------------------------------------------------------------------ + * This fcn builds the master list of pin names. It writes the + * label refdes:pinnumber into the global master pin list. + * Algorithm: + * 1. Loop on o_current looking for OBJ_COMPLEX + * 2. When we find a complex, save the refdes. + * 3. Dive down to o_lower_current = o_current->complex->prim_objs + * 4. Loop on o_lower_current looking for OBJ_PIN + * 5. When we find a pin, find the pinnumber by calling + * o_attrib_search_name_single(o_lower_current, "pinnumber", NULL) + * 6. Create the pin list label as "refdes=XXX", and stick it into + * the master pin list. + * Since this fcn operates on the global sheet_data->master_pin_list, + * it doesn't return a value. + *------------------------------------------------------------------*/ +void s_sheet_data_add_master_pin_list_items(OBJECT *start_obj) { + char *temp_uref; + char *temp_pinnumber; + char *row_label; + OBJECT *o_current; + OBJECT *o_lower_current; + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("=========== Just entered s_sheet_data_add_master_pin_list_items! ==============\n"); +#endif + + if (verbose_mode) { + printf("- Starting master pin list creation.\n"); + } + + /* ----- Iterate through all objects found on page looking for components ----- */ + o_current = start_obj; + while (o_current != NULL) { + +#ifdef DEBUG + printf("In s_sheet_data_add_master_pin_list_items, examining o_current->name = %s\n", o_current->name); +#endif + + if (o_current->type == OBJ_COMPLEX) { + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + if (temp_uref != NULL) { /* make sure object complex has a refdes */ + + /* ----- Now iterate through lower level objects looking for pins. ----- */ + o_lower_current = o_current->complex->prim_objs; + while (o_lower_current != NULL) { +#if DEBUG + printf("In s_sheet_data_add_master_pin_list_items, examining object name %s\n", o_lower_current->name); +#endif + if (o_lower_current->type == OBJ_PIN) { + temp_pinnumber = o_attrib_search_name_single(o_lower_current, "pinnumber", NULL); + + if( temp_pinnumber != NULL) { + row_label = u_basic_strdup_multiple(temp_uref, ":", temp_pinnumber, NULL); +#if DEBUG + printf("In s_sheet_data_add_master_pin_list_items, about to add to master pin list row_label = %s\n", row_label); +#endif + s_string_list_add_item(sheet_head->master_pin_list_head, &(sheet_head->pin_count), row_label); + + } else { /* didn't find pinnumber. Report error to log. */ + fprintf(stderr, "In s_sheet_data_add_master_pin_list_items, found component pin with no pinnumber.\n"); +#ifdef DEBUG + fprintf(stderr, ". . . . refdes = %s.\n", temp_uref); +#endif + } + free(temp_pinnumber); + + } + o_lower_current = o_lower_current->next; + } /* while (o_lower_current != NULL) */ + + } else { /* didn't find refdes. Report error to log. */ + fprintf(stderr, "In s_sheet_data_add_master_pin_list_items, found non-graphical component with no refdes.\n"); +#ifdef DEBUG + fprintf(stderr, ". . . . complex_basename = %s.\n", o_current->complex_basename); +#endif + } + free(temp_uref); + + } /* if (o_current->type == OBJ_COMPLEX) */ + o_current = o_current->next; + + } /* while o_current != NULL */ + + return; +} + + +/*------------------------------------------------------------------ + * This fcn builds the master list of pin attributes. It writes + * each attrib name into the master pin attrib list. + * Algorithm: + * 1. Loop on o_current looking for OBJ_COMPLEX + * 2. When we find a complex, save the refdes. + * 3. Dive down to o_lower_current = o_current->complex->prim_objs + * 4. Loop on o_lower_current looking for OBJ_PIN + * 5. When we find a pin, get pin_attribs = o_lower_current->attribs + * 6. Loop on attribs looking for non-NULL text. + * 7. When we find a non-NULL text attrib, extract the attrib name + * and stick it in the master pin attrib list. + *------------------------------------------------------------------*/ +void s_sheet_data_add_master_pin_attrib_list_items(OBJECT *start_obj) { + char *temp_uref; + char *attrib_text; + char *attrib_name; + OBJECT *o_current; + OBJECT *o_lower_current; + ATTRIB *pin_attrib; + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("=========== Just entered s_sheet_data_add_master_pin_attrib_list_items! ==============\n"); +#endif + + if (verbose_mode) { + printf("- Starting master pin attrib list creation.\n"); + } + + /* ----- Iterate through all objects found on page looking for components ----- */ + o_current = start_obj; + while (o_current != NULL) { + +#ifdef DEBUG + printf("In s_sheet_data_add_master_pin_attrib_list_items, examining o_current->name = %s\n", o_current->name); +#endif + + if (o_current->type == OBJ_COMPLEX) { + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + if (temp_uref != NULL) { /* make sure object complex has a refdes */ + + /* ----- Now iterate through lower level objects looking for pins. ----- */ + o_lower_current = o_current->complex->prim_objs; + while (o_lower_current != NULL) { +#if DEBUG + printf("In s_sheet_data_add_master_pin_attrib_list_items, examining component refdes = %s\n", temp_uref); +#endif + if (o_lower_current->type == OBJ_PIN) { + /* ----- Found a pin. Now get attrib head and loop on attribs. ----- */ + pin_attrib = o_lower_current->attribs; + while (pin_attrib != NULL) { + if (pin_attrib->object->type == OBJ_TEXT + && pin_attrib->object->text != NULL) { /* found an attribute */ + attrib_text = u_basic_strdup(pin_attrib->object->text->string); + attrib_name = u_basic_breakup_string(attrib_text, '=', 0); + if (strcmp(attrib_name, "pinnumber") != 0) { + /* Don't include "pinnumber" because it is already in other master list */ + +#if DEBUG + printf("In s_sheet_data_add_master_pin_attrib_list_items, found pin attrib = %s\n", attrib_name); + printf(". . . . . adding it to master_pin_attrib_list\n"); +#endif + + s_string_list_add_item(sheet_head->master_pin_attrib_list_head, + &(sheet_head->pin_attrib_count), attrib_name); + } /* if (strcmp(attrib_name, "pinnumber") != 0) */ + free(attrib_name); + free(attrib_text); + } + pin_attrib = pin_attrib->next; + } /* while (pin_attrib != NULL) */ + } + o_lower_current = o_lower_current->next; + } /* while (o_lower_current != NULL) */ + + free(temp_uref); + } /* if (temp_uref != NULL ) */ + + } /* if (o_current->type == OBJ_COMPLEX) */ + o_current = o_current->next; + + } /* while (o_current != NULL) */ + return; + +} + + + + +/*------------------------------------------------------------------ + * This fcn extracts the attribs from the gtksheet + * cells, and places them back into SHEET_DATA. This is the + * first step in saving out a project. Right now I just invoke + * s_table_gtksheet_to_table. Do I need to do anything else here? + *------------------------------------------------------------------*/ +void s_sheet_data_gtksheet_to_sheetdata() { + s_table_gtksheet_to_all_tables(); + /* do I need to do anything else here? */ + + return; +} + + + + diff --git a/gattrib/src/s_string_list.c b/gattrib/src/s_string_list.c new file mode 100644 index 000000000..76d685980 --- /dev/null +++ b/gattrib/src/s_string_list.c @@ -0,0 +1,336 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns involved in manipulating the STRING_LIST + * structure. STRING_LIST is basically a linked list of strings + * (text). + *------------------------------------------------------------------*/ + +#include <config.h> + +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include <math.h> + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + + +/*------------------------------------------------------------------ + * This returns a pointer to a new STRING_LIST object. + *------------------------------------------------------------------*/ +STRING_LIST *s_string_list_new() { + STRING_LIST *local_string_list; + + local_string_list = malloc(sizeof(STRING_LIST)); + local_string_list->data = NULL; + local_string_list->next = NULL; + local_string_list->prev = NULL; + local_string_list->pos = -1; /* can look for this later . . . */ + + return local_string_list; +} + + +/*------------------------------------------------------------------ + * This fcn inserts the item into a char* list. It first cycles through the + * list to make sure that there are no duplications. The list is assumed + * to be a STRING_LIST: + * struct STRING_LIST + * { + * char *data; + * int pos; + * STRING_LIST *next; + * STRING_LIST *prev; + * }; + *------------------------------------------------------------------*/ +void s_string_list_add_item(STRING_LIST *list, int *count, char *item) { + + gchar *trial_item = NULL; + STRING_LIST *prev; + STRING_LIST *local_list; + + /* First check to see if list is empty. Handle insertion of first item + into empty list separately. (Is this necessary?) */ + if (list->data == NULL) { +#ifdef DEBUG + printf("In s_string_list_add_item, about to place first item in list.\n"); +#endif + list->data = (gchar *) u_basic_strdup(item); + list->next = NULL; + list->prev = NULL; /* this may have already been initialized. . . . */ + list->pos = (int) count; /* This enumerates the pos on the list. Value is reset later by sorting. */ + (*count)++; /* increment count to 1 */ + return; + } + + /* Otherwise, loop through list looking for duplicates */ + prev = list; + while (list != NULL) { + trial_item = (gchar *) u_basic_strdup(list->data); + if (strcmp(trial_item, item) == 0) { + /* Found item already in list. Just return. */ + free(trial_item); + return; + } + free(trial_item); + prev = list; + list = list->next; + } + + /* If we are here, it's 'cause we didn't find the item pre-existing in the list. */ + /* In this case, we insert it. */ + + local_list = malloc(sizeof(STRING_LIST)); /* allocate space for this list entry */ + local_list->data = (gchar *) u_basic_strdup(item); /* copy data into list */ + local_list->next = NULL; + local_list->prev = prev; /* point this item to last entry in old list */ + prev->next = local_list; /* make last item in old list point to this one. */ + local_list->pos = (int) count; /* This enumerates the pos on the list. Value is reset later by sorting. */ + (*count)++; /* increment count */ + list = local_list; + return; + +} + +/*------------------------------------------------------------------ + * This fcn looks for item in the list. It returns 1 if item is + * present, 0 if absent. + *------------------------------------------------------------------*/ +int s_string_list_in_list(STRING_LIST *list, char *item) { + + gchar *trial_item = NULL; + + /* First check to see if list is empty. If empty, return + * 0 automatically. (I probably don't need to handle this + * separately.) */ + if (list->data == NULL) { + return 0; + } + + /* Otherwise, loop through list looking for duplicates */ + while (list != NULL) { + trial_item = (gchar *) u_basic_strdup(list->data); + if (strcmp(trial_item, item) == 0) { + /* Found item already in list. return 1. */ + free(trial_item); + return 1; + } + free(trial_item); + list = list->next; + } + + /* If we are here, it's 'cause we didn't find the item + * pre-existing in the list. In this case, return 0 */ + return 0; + +} + + + +/*------------------------------------------------------------------ + * This fcn takes the master comp list sheet_head->master_comp_list_head + * and sorts it in this order: + * <all refdeses in alphabetical order> + * Right now it does nothing other than fill in the "position" + * and "length" variables. + *------------------------------------------------------------------*/ +void s_string_list_sort_master_comp_list() { + int i = 0; + STRING_LIST *local_list; + + /* Here's where we do the sort. The sort is done using a fcn found on the web. */ + local_list = sheet_head->master_comp_list_head; + local_list = listsort(local_list, 0, 1); + + /* Do this after sorting is done. This resets the order of the individual items + * in the list. */ + while (local_list != NULL) { /* make sure item is not null */ + local_list->pos = i; + if (local_list->next != NULL) { + i++; + local_list = local_list->next; + } else { + break; /* leave loop *before* iterating to NULL EOL marker */ + } + } + + /* Now go to first item in local list and reassign list head to new first element */ + while (local_list->prev != NULL) { + local_list = local_list->prev; + } + + sheet_head->master_comp_list_head = local_list; + + return; +} + +/*------------------------------------------------------------------ + * This fcn takes the master comp attrib list sheet_head->master_comp_attrib_list_head + * and sorts it in this order: + * <all refdeses in alphabetical order> + * Right now it does nothing other than fill in the "position" + * and "length" variables. + *------------------------------------------------------------------*/ +void s_string_list_sort_master_comp_attrib_list() { + int i = 0; + STRING_LIST *local_list; + + /* Here's where we do the sort */ + + /* + * Note that this sort is TBD -- it is more than just an alphabetic sort 'cause we want + * certain attribs to go first. + */ + + + /* Do this after sorting is done. This resets the order of the individual items + * in the list. */ + local_list = sheet_head->master_comp_attrib_list_head; + while (local_list != NULL) { + local_list->pos = i; + i++; + local_list = local_list->next; + } + + return; +} + +/*------------------------------------------------------------------ + * This fcn takes the master net list sheet_head->master_net_list_head + * and sorts it in this order: + * <all nets in alphabetical order> + *------------------------------------------------------------------*/ +void s_string_list_sort_master_net_list() { + int i = 0; + STRING_LIST *local_list; + + + /* Do this after sorting is done. This resets the order of the individual items + * in the list. */ + local_list = sheet_head->master_net_list_head; + while (local_list != NULL) { + local_list->pos = i; + i++; + local_list = local_list->next; + } + + return; +} + +/*------------------------------------------------------------------ + * This fcn takes the master net attrib list sheet_head->master_net_attrib_list_head + * and sorts it in this order: + * value, footprint, model-name, file, <all other attribs in alphabetical order> + *------------------------------------------------------------------*/ +void s_string_list_sort_master_net_attrib_list() { + int i = 0; + STRING_LIST *local_list; + + + /* Do this after sorting is done. This resets the order of the individual items + * in the list. */ + local_list = sheet_head->master_net_attrib_list_head; + while (local_list != NULL) { + local_list->pos = i; + i++; + local_list = local_list->next; + } + + return; +} + + +/*------------------------------------------------------------------ + * This fcn takes the master pin list sheet_head->master_pin_list_head + * and sorts it in this order: + * <all refdeses in alphabetical order> + * Right now it does nothing other than fill in the "position" + * and "length" variables. + *------------------------------------------------------------------*/ +void s_string_list_sort_master_pin_list() { + int i = 0; + STRING_LIST *local_list; + + /* Here's where we do the sort. The sort is done using a fcn found on the web. */ + local_list = sheet_head->master_pin_list_head; + local_list = listsort(local_list, 0, 1); + + /* Do this after sorting is done. This resets the order of the individual items + * in the list. */ + while (local_list != NULL) { /* make sure item is not null */ + local_list->pos = i; + if (local_list->next != NULL) { + i++; + local_list = local_list->next; + } else { + break; /* leave loop *before* iterating to NULL EOL marker */ + } + } + + /* Now go to first item in local list and reassign list head to new first element */ + while (local_list->prev != NULL) { + local_list = local_list->prev; + } + + sheet_head->master_pin_list_head = local_list; + + return; +} + +/*------------------------------------------------------------------ + * This fcn takes the master pin attrib list sheet_head->master_pin_attrib_list_head + * and sorts it in this order: + * <all pin attribs in alphabetical order> + * Right now it does nothing other than fill in the "position" + * and "length" variables. + *------------------------------------------------------------------*/ +void s_string_list_sort_master_pin_attrib_list() { + int i = 0; + STRING_LIST *local_list; + + /* Here's where we do the sort */ + + /* + * Note that this sort is TBD -- it is more than just an alphabetic sort 'cause we want + * certain attribs to go first. + */ + + + /* Do this after sorting is done. This resets the order of the individual items + * in the list. */ + local_list = sheet_head->master_pin_attrib_list_head; + while (local_list != NULL) { + local_list->pos = i; + i++; + local_list = local_list->next; + } + + return; +} + diff --git a/gattrib/src/s_table.c b/gattrib/src/s_table.c new file mode 100644 index 000000000..9076ac401 --- /dev/null +++ b/gattrib/src/s_table.c @@ -0,0 +1,557 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 -- 2004 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns involved in manipulating the TABLE structure, + * which is subsidiary to SHEET_DATA. TABLE is a 2 dimensional array + * of structs; each struct corresponds to the data about an element + * in a single cell of the spreadsheet. + *------------------------------------------------------------------*/ + +#include <config.h> + +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include <math.h> + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + +/* =================== Public Functions ====================== */ + +/*------------------------------------------------------------------ + * This fcn is the table creator. It returns a pointer to + * an initialized TABLE struct. As calling args, it needs + * the number of rows and cols to allocate. The table is a + * dynamically allocated 2D array of structs. To access data in + * a cell in the table, you reference (for example): + * ((sheet_data->comp_table)[i][j]).attrib_value + * (Parens used only for clarity. It works without parens.) + *------------------------------------------------------------------*/ +TABLE **s_table_new(int rows, int cols) +{ + TABLE **new_table; + int i, j; + + /* Here I am trying to create a 2 dimensional array of structs */ + new_table = (TABLE **) malloc(rows*sizeof(TABLE *)); + for (i = 0; i < rows; i++) { + new_table[i] = (TABLE *) malloc(cols * sizeof(TABLE)); + /* Note that I should put some checks in here to verify that + * malloc worked correctly. */ + } + + /* Now pre-load the table with NULLs */ + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + (new_table[i][j]).attrib_value = NULL; + (new_table[i][j]).row_name = NULL; + (new_table[i][j]).col_name = NULL; + (new_table[i][j]).row = i; + (new_table[i][j]).col = j; + (new_table[i][j]).visibility = 3; /* both name & value visible */ + } + } + + return (new_table); + +} + + +/*------------------------------------------------------------------ + * This fcn destroys the old table. Use it after reading in a new + * page to get rid of the old table before building a new one. + *------------------------------------------------------------------*/ +void s_table_destroy(TABLE **table, int row_count, int col_count) +{ + int i, j; + for (i = 0; i < row_count; i++) { + for (j = 0; j < row_count; j++) { + free( (table[i][j]).attrib_value ); + } + + } + return; +} + + + +/*------------------------------------------------------------------ + * This fcn returns the index number when given a STRING_LIST and a + * string to match. It finds the index + * number by iterating through the master list. + *------------------------------------------------------------------*/ +int s_table_get_index(STRING_LIST *local_list, char *local_string) { + int count = 0; + STRING_LIST *list_element; + +#ifdef DEBUG + printf("In s_table_get_index, examining %s to see if it is in the list.\n", local_string); +#endif + + + list_element = local_list; + while (list_element != NULL) { + if (strcmp(list_element->data, local_string) == 0) { + return count; + } + count++; + list_element = list_element->next; + } + /* If we are here, it is by mistake */ + fprintf(stderr, "s_table_get_index passed a string not in master list!.\n"); + exit(-1); +} + + +/*------------------------------------------------------------------ + * This fcn iterates over adds all objects found on this page looking + * for components. When it finds a component, it finds all component + * attribs and sticks them in the TABLE. + *------------------------------------------------------------------*/ +void s_table_add_toplevel_comp_items_to_comp_table(OBJECT *start_obj) { + OBJECT *o_current; + gchar *temp; + gchar *temp_uref; + int row, col; + gchar *attrib_text; + gchar *attrib_name; + gchar *attrib_value; + ATTRIB *a_current; + + if (verbose_mode) { + printf("- Starting internal component TABLE creation\n"); + } + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("=========== Just entered s_table_add_toplevel_comp_items_to_comp_table! ==============\n"); +#endif + + /* ----- Iterate through all objects found on page ----- */ + o_current = start_obj; + while (o_current != NULL) { + +#ifdef DEBUG + printf(" ---> In s_table_add_toplevel_comp_items_to_comp_table, examining o_current->name = %s\n", o_current->name); +#endif + + /* ----- Now process objects found on page ----- */ + if ( (o_current->type == OBJ_COMPLEX) && + o_current->attribs && + !o_attrib_search_component(o_current, "graphical") ) { + + /* ---- Don't process part if it lacks a refdes ----- */ + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + if (temp_uref) { + +#if DEBUG + printf(" In s_table_add_toplevel_comp_items_to_comp_table, found component on page. Refdes = %s\n", temp_uref); +#endif + verbose_print(" C"); + + /* Having found a component, we loop over all attribs in this + * component, and stick them + * into cells in the table. */ + a_current = o_current->attribs; + while (a_current != NULL) { + if (a_current->object->type == OBJ_TEXT + && a_current->object->text != NULL) { /* found an attribute */ + /* may need to check more thoroughly here. . . . */ + attrib_text = u_basic_strdup(a_current->object->text->string); + attrib_name = u_basic_breakup_string(attrib_text, '=', 0); + attrib_value = u_basic_breakup_string(attrib_text, '=', 1); + if (strcmp(attrib_name, "refdes") != 0) { + /* Don't include "refdes" */ + + /* Get row and col where to put this attrib */ + row = s_table_get_index(sheet_head->master_comp_list_head, temp_uref); + col = s_table_get_index(sheet_head->master_comp_attrib_list_head, attrib_name); +#if DEBUG + printf(" In s_table_add_toplevel_comp_items_to_comp_table, about to add row %d, col %d, attrib_value = %s\n", + row, col, attrib_value); + printf(" . . . current address of attrib_value cell is [%p]\n", &((sheet_head->component_table)[row][col]).attrib_value); +#endif + /* Is there a compelling reason for me to put this into a separate fcn? */ + ((sheet_head->component_table)[row][col]).row = row; + ((sheet_head->component_table)[row][col]).col = col; + ((sheet_head->component_table)[row][col]).row_name = u_basic_strdup(temp_uref); + ((sheet_head->component_table)[row][col]).col_name = u_basic_strdup(attrib_name); + ((sheet_head->component_table)[row][col]).attrib_value = u_basic_strdup(attrib_value); + } + free(attrib_name); + free(attrib_text); + free(attrib_value); + } + a_current = a_current->next; + + } /* while (a_current != NULL) */ + free(temp_uref); + } /* if (temp_uref) */ + } /* if (o_current->type == OBJ_COMPLEX) */ + + o_current = o_current->next; /* iterate to next object on page */ + + } /* while o_current != NULL */ + + verbose_done(); + +} + + +/*------------------------------------------------------------------ + * This fcn iterates over adds all items found on this page looking + * for nets and adds them individually to the net table. Looping over + * objects occurs here. + *------------------------------------------------------------------*/ +void s_table_add_toplevel_net_items_to_net_table(OBJECT *start_obj) { + OBJECT *o_current; + char *temp_netname; + int row, col; + char *attrib_text; + char *attrib_name; + char *attrib_value; + ATTRIB *a_current; + + /* ----- Iterate through all objects found on page ----- */ + o_current = start_obj; + while (o_current != NULL) { + + /* ----- Now process objects found on page ----- */ + if (o_current->type == OBJ_NET) { +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In s_table_add_toplevel_net_items_to_net_table, Found net on page\n"); +#endif + verbose_print(" N"); + + /* Having found a net, we stick it into the table. */ + a_current = o_current->attribs; + while (a_current != NULL) { + if (a_current->object->type == OBJ_TEXT + && a_current->object->text != NULL) { /* found an attribute */ + /* may need to check more thoroughly here. . . . */ + attrib_text = u_basic_strdup(a_current->object->text->string); + attrib_name = u_basic_breakup_string(attrib_text, '=', 0); + attrib_value = u_basic_breakup_string(attrib_text, '=', 1); + if (strcmp(attrib_name, "netname") != 0) { + /* Don't include "netname" */ + + /* Get row and col where to put this attrib */ + row = s_table_get_index(sheet_head->master_net_list_head, temp_netname); + col = s_table_get_index(sheet_head->master_net_attrib_list_head, attrib_name); +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In s_table_add_toplevel_net_items_to_net_table, about to add row %d, col %d, attrib_value = %s\n", + row, col, attrib_value); + printf(" . . . current address of attrib_value cell is [%p]\n", &((sheet_head->net_table)[row][col]).attrib_value); +#endif + /* Is there a compelling reason for me to put this into a separate fcn? */ + ((sheet_head->net_table)[row][col]).row = row; + ((sheet_head->net_table)[row][col]).col = col; + ((sheet_head->net_table)[row][col]).row_name = u_basic_strdup(temp_netname); + ((sheet_head->net_table)[row][col]).col_name = u_basic_strdup(attrib_name); + ((sheet_head->net_table)[row][col]).attrib_value = u_basic_strdup(attrib_value); + } + free(attrib_name); + free(attrib_text); + free(attrib_value); + } + a_current = a_current->next; + + } /* while (a_current != NULL) */ + free(temp_netname); + + } /*--- if (o_current->type == OBJ_NET) ---*/ + + + o_current = o_current->next; /* iterate to next object on page */ + } /* while o_current != NULL */ + + verbose_done(); + +#if DEBUG + fflush(stderr); + fflush(stdout); + printf("In s_table_add_toplevel_net_items_to_net_table -- we are about to return\n"); +#endif + +} + + + +/*------------------------------------------------------------------ + * This fcn iterates over adds all items found on this page + * looking for pins. WHen it finds a pin, it gathers all + * pin attribs and sticks them into the pin table. + *------------------------------------------------------------------*/ +void s_table_add_toplevel_pin_items_to_pin_table(OBJECT *start_obj) { + OBJECT *o_current; + OBJECT *o_lower_current;; + gchar *temp; + gchar *temp_uref; + gchar *pinnumber; + gchar *row_label; + int row, col; + gchar *attrib_text; + gchar *attrib_name; + gchar *attrib_value; + ATTRIB *pin_attrib; + + if (verbose_mode) { + printf("- Starting internal pin TABLE creation\n"); + } + +#ifdef DEBUG + printf("=========== Just entered s_table_add_toplevel_pin_items_to_pin_table! ==============\n"); +#endif + + /* ----- Iterate through all objects found on page ----- */ + o_current = start_obj; + while (o_current != NULL) { + +#ifdef DEBUG + printf(" ---> In s_table_add_toplevel_pin_items_to_pin_table, examining o_current->name = %s\n", o_current->name); +#endif + + /* ----- Now process objects found on page ----- */ + if ( (o_current->type == OBJ_COMPLEX) && + o_current->attribs && + !o_attrib_search_component(o_current, "graphical") ) { + + /* ---- Don't process part if it lacks a refdes ----- */ + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + if (temp_uref) { + + /* ----- Now iterate through lower level objects looking for pins. ----- */ + o_lower_current = o_current->complex->prim_objs; + while (o_lower_current != NULL) { + + if (o_lower_current->type == OBJ_PIN) { + /* ----- Found a pin. First get its pinnumber. then get attrib head and loop on attribs. ----- */ + pinnumber = o_attrib_search_name_single(o_lower_current, "pinnumber", NULL); + row_label = u_basic_strdup_multiple(temp_uref, ":", pinnumber, NULL); + +#if DEBUG + printf(" In s_table_add_toplevel_pin_items_to_pin_table, examining pin %s\n", row_label); +#endif + + pin_attrib = o_lower_current->attribs; + while (pin_attrib != NULL) { + if (pin_attrib->object->type == OBJ_TEXT + && pin_attrib->object->text != NULL) { /* found an attribute */ + attrib_text = u_basic_strdup(pin_attrib->object->text->string); + attrib_name = u_basic_breakup_string(attrib_text, '=', 0); + attrib_value = u_basic_breakup_string(attrib_text, '=', 1); + + if (strcmp(attrib_name, "pinnumber") != 0) { + /* Don't include "pinnumber" because it is already in other master list */ + + /* Get row and col where to put this attrib */ + row = s_table_get_index(sheet_head->master_pin_list_head, row_label); + col = s_table_get_index(sheet_head->master_pin_attrib_list_head, attrib_name); +#if DEBUG + printf(" In s_table_add_toplevel_pin_items_to_pin_table, about to add row %d, col %d, attrib_value = %s\n", + row, col, attrib_value); + printf(" . . . current address of attrib_value cell is [%p]\n", &((sheet_head->component_table)[row][col]).attrib_value); +#endif + /* Is there a compelling reason for me to put this into a separate fcn? */ + ((sheet_head->pin_table)[row][col]).row = row; + ((sheet_head->pin_table)[row][col]).col = col; + ((sheet_head->pin_table)[row][col]).row_name = u_basic_strdup(row_label); + ((sheet_head->pin_table)[row][col]).col_name = u_basic_strdup(attrib_name); + ((sheet_head->pin_table)[row][col]).attrib_value = u_basic_strdup(attrib_value); + } + free(attrib_name); + free(attrib_text); + free(attrib_value); + } + pin_attrib = pin_attrib->next; + + } /* while (pin_attrib != NULL) */ + free(pinnumber); + free(row_label); + } + + o_lower_current = o_lower_current->next; + } /* while (o_lower_current != NULL) */ + } + + free(temp_uref); + } + + o_current = o_current->next; /* iterate to next object on page */ + + } /* while o_current != NULL */ + + verbose_done(); +} + + +/*------------------------------------------------------------------ + * This fcn through the spreadsheet, extracts the attribs from + * the cells, and places them back into TABLE. This is the + * first step in saving out a project. + *------------------------------------------------------------------*/ +int s_table_gtksheet_to_all_tables() { + + int num_rows; + int num_cols; + STRING_LIST *master_row_list; + STRING_LIST *master_col_list; + TABLE **local_table; + GtkSheet *local_gtk_sheet; + + /* First handle component sheet */ + num_rows = sheet_head->comp_count; + num_cols = sheet_head->comp_attrib_count; + local_gtk_sheet = sheets[0]; + master_row_list = sheet_head->master_comp_list_head; + master_col_list = sheet_head->master_comp_attrib_list_head; + local_table = sheet_head->component_table; + + s_table_gtksheet_to_table(local_gtk_sheet, master_row_list, + master_col_list, local_table, + num_rows, num_cols); + +#if 0 + /* Next handle net sheet */ + num_rows = sheet_head->net_count; + num_cols = sheet_head->net_attrib_count; + local_gtk_sheet = sheets[1]; + master_row_list = sheet_head->master_net_list_head; + master_col_list = sheet_head->master_net_attrib_list_head; + local_table = sheet_head->net_table; + + s_table_gtksheet_to_table(local_gtk_sheet, master_row_list, + master_col_list, local_table, + num_rows, num_cols); +#endif + + /* Finally, handle component pin sheet */ + num_rows = sheet_head->pin_count; + num_cols = sheet_head->pin_attrib_count; + local_gtk_sheet = sheets[2]; + master_row_list = sheet_head->master_pin_list_head; + master_col_list = sheet_head->master_pin_attrib_list_head; + local_table = sheet_head->pin_table; + + s_table_gtksheet_to_table(local_gtk_sheet, master_row_list, + master_col_list, local_table, + num_rows, num_cols); + + return; +} + + +/* =================== Private Functions ====================== */ +/*------------------------------------------------------------------ + * This fcn does the actual heaving lifting of looping + * through the spreadsheet, extracting the attribs from + * the cells, and placing them back into TABLE. This is the + * first step in saving out a project. + *------------------------------------------------------------------*/ +int s_table_gtksheet_to_table(GtkSheet *local_gtk_sheet, STRING_LIST *master_row_list, + STRING_LIST *master_col_list, TABLE **local_table, + int num_rows, int num_cols) +{ + int row, col; + + STRING_LIST *row_list_item; + gchar *row_title; + + STRING_LIST *col_list_item; + gchar *col_title; + + gchar *attrib_value; + + row_list_item = master_row_list; + for (row = 0; row < num_rows; row++) { + row_title = (gchar *) u_basic_strdup(row_list_item->data); + + col_list_item = master_col_list; + for (col = 0; col < num_cols; col++) { + col_title = (gchar *) u_basic_strdup(col_list_item->data); + + /* get value of attrib in cell */ + attrib_value = (gchar *) gtk_sheet_cell_get_text(GTK_SHEET(local_gtk_sheet), row, col); + +#if 0 + if (strlen(attrib_value) == 0) { + /* free(attrib_value); */ /* sometimes we have spurious, zero length strings creep */ + attrib_value = NULL; /* into the GtkSheet */ + } +#endif + + +#ifdef DEBUG + printf("In s_table_update_table, found attrib_value = %s in cell row=%d, col=%d\n", + attrib_value, row, col); +#endif + + /* first handle attrib value in cell */ + if ( local_table[row][col].attrib_value != NULL) { + free( local_table[row][col].attrib_value ); + } + if (attrib_value != NULL) { + local_table[row][col].attrib_value = (gchar *) u_basic_strdup(attrib_value); + } else { + local_table[row][col].attrib_value = NULL; + } + + /* next handle name of row (also held in TABLE cell) */ + if ( local_table[row][col].row_name != NULL) { + free( local_table[row][col].row_name ); + } + if (row_title != NULL) { + local_table[row][col].row_name = (gchar *) u_basic_strdup(row_title); + } else { + local_table[row][col].row_name = NULL; + } + + /* finally handle name of col */ + if ( local_table[row][col].col_name != NULL) { + free( local_table[row][col].col_name ); + } + if (col_title != NULL) { + local_table[row][col].col_name = (gchar *) u_basic_strdup(col_title); + } else { + local_table[row][col].col_name = NULL; + } + + /* get next col list item and then iterate. */ + col_list_item = col_list_item->next; + } + + row_list_item = row_list_item->next; + } + + return; +} + diff --git a/gattrib/src/s_toplevel.c b/gattrib/src/s_toplevel.c new file mode 100644 index 000000000..8cf4e338d --- /dev/null +++ b/gattrib/src/s_toplevel.c @@ -0,0 +1,717 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns involved in manipulating the TOPLEVEL data + * structure. TOPLEVEL is the data structure inherited from gEDA's + * other programs, and holds all info about a project in a form + * native to gEDA. + *------------------------------------------------------------------*/ + +#include <config.h> + +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include <math.h> + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" + + +/* =================== Public Functions ====================== */ + +/*------------------------------------------------------------------ + * This fcn inits the toplevel data struct pr_current. It basically + * calls a fcn to initialize the window variables. + *------------------------------------------------------------------*/ +void +s_toplevel_init(TOPLEVEL *pr_current) +{ + i_window_vars_set(pr_current); /* The window vars are used in gschem, + but we need to set them here because + graphical information is used + when introducing new attributes. */ + return; +} + +/*------------------------------------------------------------------ + * This fcn reads in a page & calls f_open, which fills out the + * pr_current structure. + *------------------------------------------------------------------*/ +int s_toplevel_read_page(char *filename) +{ + PAGE local_page; + int return_code; + + /* If this is not the first page, try to create a new page. */ + if (first_page != 1) { + if (s_page_new(pr_current, filename) ) { + printf("Schematic page [%s] already loaded!\n", filename); + return; + } else { + /* if we get here, it's because this is a new page */ + if (!quiet_mode) { + printf("Loading schematic [%s]\n", filename); + } + } + } else { + /* if we get here, it's because this is the first page */ + if (!quiet_mode) { + printf("Loading schematic [%s]\n", filename); + } + } + + /* Now that we have a new page, set the new filename and read in the page */ + pr_current->page_current->page_filename = u_basic_strdup(filename); + + /* read in and fill out pr_current using f_open and its callees */ + return_code = f_open(pr_current, filename); + if (!return_code) { /* 1 = success reading in page */ + /* 0 = failure reading in page */ + fprintf(stderr, "Couldn't load schematic [%s]\n", filename); + } + + return return_code; +} + +/*------------------------------------------------------------------ + * This fcn returns 1 if the project is empty (i.e. pr_current is + * not filled out yet), and 0 if the project is non-empty (i.e. there + * is some data in pr_current). + *------------------------------------------------------------------*/ +int s_toplevel_empty_project() +{ + /* Nothing here yet. Is this necessary in current program + * architecture? */ +} + + + +/*------------------------------------------------------------------ + * This fcn is called when the user invokes "save". It first + * places all data from gtksheet into SHEET_DATA. Then it + * loops through all pages & calls s_toplevel_sheetdata_to_toplevel to place all + * stuff in SHEET_DATA into the libgeda TOPLEVEL structure. + *------------------------------------------------------------------*/ +void +s_toplevel_gtksheet_to_toplevel() +{ + PAGE *p_current; + +#if DEBUG + printf("--------------------- Entering s_toplevel_gtksheet_to_toplevel -------------------\n"); +#endif + + + s_sheet_data_gtksheet_to_sheetdata(); /* read data from gtksheet into SHEET_DATA */ +#if DEBUG + printf("In s_toplevel_gtksheet_to_toplevel -- done writing stuff from gtksheet into SHEET_DATA.\n"); +#endif + + p_current = pr_current->page_head; /* must iterate over all pages in design */ + while (p_current != NULL) { + if (p_current->pid != -1) { /* only traverse pages which are toplevel */ + if (p_current->object_head && p_current->page_control == 0) { + s_toplevel_sheetdata_to_toplevel(p_current->object_head); /* adds all objects from page */ + } + } + p_current = p_current->next; /* iterate to next schematic page */ + } +#if DEBUG + printf("In s_toplevel_gtksheet_to_toplevel -- done writing SHEEET_DATA text back into pr_currnet.\n"); +#endif + + return; + +} + + + +/* ======================= Callbacks ====================== */ + +/*------------------------------------------------------------------ + * This fcn is the callback from the menu bar. It throws up the + * filedialog widget, and then accepts from it a list of files to + * open. Then it figures out if there is already an existing + * project, and call the appropriate version of s_toplevel_read + * depending upon that. + *------------------------------------------------------------------*/ +void s_toplevel_menubar_file_open(TOPLEVEL *pr_current) +{ + int filesel_type = OPEN; + +#ifdef DEBUG + printf("In s_toplevel_menubar_file_open, about to create fileselect box\n"); +#endif + + x_fileselect_setup(pr_current, filesel_type); + return; +} + + +/*------------------------------------------------------------------ + * This fcn is the file->save callback from the menu bar. It + * first updates the proect, and then saves the project without + * throwing up the filedialog widget. + *------------------------------------------------------------------*/ +void s_toplevel_menubar_file_save(TOPLEVEL *pr_current) +{ + +#ifdef DEBUG + printf("In s_toplevel_menubar_file_save, about to save out the project\n"); +#endif + + s_toplevel_gtksheet_to_toplevel(); + s_page_save_all(pr_current); /* saves all pages in design */ + + return; +} + + +/*------------------------------------------------------------------ + * This fcn is a hack. It gives a non-NULL value to the select_func + * defined in globals.c for libgeda. A non-NULL value for this fcn + * makes sure that object->sel_func = 1 when the project is saved out, + * which keeps the objects selectable in gschem. + * Perhaps I should just set the variable myself when saving the + * project out . . . . . + *------------------------------------------------------------------*/ +void s_toplevel_select_object() +{ + /* I don't know if I should do anything in here to prevent + * the function from being optimized away by gcc. */ +} + + +/* ======================= Private fcns ====================== */ + +/*------------------------------------------------------------------ + * This fcn takes the updated SHEET_DATA->TABLE object and then + * loops through all objects on (PAGE page)->(OBJECT *start_obj) + * & updates the attached + * attrib values using the updated values held in the SHEET_DATA->TABLE + * structure. It does so in three steps: + * 1. First find and update component attribs. + * 2. Then find and update net attribs. + * 3. Finally find and update pin attribs. + *------------------------------------------------------------------*/ +void +s_toplevel_sheetdata_to_toplevel(OBJECT *start_obj) +{ + OBJECT *o_current; + OBJECT *comp_prim_obj; + char *temp_uref; + char *temp_netname; + char *temp_pin; + STRING_LIST *new_comp_attrib_list; + STRING_LIST *new_net_attrib_list; + STRING_LIST *new_pin_attrib_list; + + /* ----- First deal with all components on the page. ----- */ +#ifdef DEBUG + printf("----- In s_toplevel_sheetdata_to_toplevel, handling components\n"); +#endif + o_current = start_obj; + while (o_current != NULL) { + + /* ------- Object is a component. Handle component attributes. ------- */ + if (o_current->type == OBJ_COMPLEX) { /* Note that OBJ_COMPLEX = component + attribs */ + +#if 0 + if ( o_attrib_search_component(o_current, "graphical") ) { + break; /* Ignore graphical components */ + } +#endif + + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + if (temp_uref != NULL) { + new_comp_attrib_list = s_toplevel_get_component_attribs_in_sheet(temp_uref); + s_toplevel_update_component_attribs_in_toplevel(o_current, new_comp_attrib_list); + free(temp_uref); + } else { +#ifdef DEBUG + printf("In s_toplevel_sheetdata_to_toplevel, found complex with no refdes. name = %s\n", o_current->name); +#endif + } + } /* if (o_current->type == OBJ_COMPLEX) */ + + + o_current = o_current->next; + } /* while (o_current != NULL) */ + + +#if 0 + /* ----- Next deal with all nets on the page. ----- */ +#ifdef DEBUG + printf("----- In s_toplevel_sheetdata_to_toplevel, handling nets\n"); +#endif + o_current = start_obj; + while (o_current != NULL) { + if (o_current->type == OBJ_NET) { + + temp_netname = o_attrib_search_name_single(o_current, "netname", NULL); + if (temp_netname != NULL) { + new_net_attrib_list = s_toplevel_get_net_attribs_in_sheet(temp_netname); + s_toplevel_update_net_attribs_in_toplevel(o_current, new_net_attrib_list); + free(temp_netname); + } + + } + o_current = o_current->next; + } /* while (o_current != NULL) */ + +#endif + + + /* ----- Finally deal with all pins on the page. ----- */ + /* ----- Next deal with all nets on the page. ----- */ +#ifdef DEBUG + printf("----- In s_toplevel_sheetdata_to_toplevel, handling pins\n"); +#endif + o_current = start_obj; + while (o_current != NULL) { + + /* ------- Object is a complex. Handle pins by looking ------ */ + /* ------- for all pins attached to a component. ------ */ + if (o_current->type == OBJ_COMPLEX) { + /* Upon finding a component, here's what to do: + * 0. Get refdes of component. + * 1. Loop over prim_objects, looking for pins. + * 2. When a pin is found, create refdes:pinnumber pair + * used in searching TABLE. + * 3. Search TABLE using refdes:pinnumber as key, and get list of + * attribs corresponding to this refdes:pinnumber + * 4. Stick the attribs into the TOPLEVEL data structure. + */ + temp_uref = o_attrib_search_name_single(o_current, "refdes", NULL); + if ( (temp_uref != NULL) && (o_current->complex->prim_objs) ) { /* make sure object complex has a refdes */ + + comp_prim_obj = o_current->complex->prim_objs; + while (comp_prim_obj != NULL) { + if (comp_prim_obj->type == OBJ_PIN) { + new_pin_attrib_list = s_toplevel_get_pin_attribs_in_sheet(temp_uref, comp_prim_obj); + s_toplevel_update_pin_attribs_in_toplevel(temp_uref, comp_prim_obj, new_pin_attrib_list); + } + comp_prim_obj = comp_prim_obj->next; + } + } /* if(temp_uref */ + + free(temp_uref); + } + + o_current = o_current->next; + } /* while (o_current != NULL) */ + + return; +} + + +/*------------------------------------------------------------------ + * This fcn returns a list of attributes attached to obj_name = comp + * refdes or netlist. The returned list is a STRING_LIST where the + * ->data holds a name=value string. + *------------------------------------------------------------------*/ +STRING_LIST *s_toplevel_get_component_attribs_in_sheet(char *refdes) +{ + STRING_LIST *new_attrib_list; + STRING_LIST *local_attrib_list; + STRING_LIST *row_list; + int i; + int row = -1; + int count = 0; + char *row_item; + char *name_value_pair; + char *new_attrib_value; + char *new_attrib_name; + char *temp; + +#if DEBUG + printf("----- Entering s_toplevel_get_component_attribs_in_sheet.\n"); +#endif + + + /* First find pos of this refdes in the master list */ + row = s_table_get_index(sheet_head->master_comp_list_head, refdes); + + /* Sanity check */ + if (row == -1) { + /* we didn't find the item in the list */ + fprintf(stderr, + "In s_toplevel_get_component_attribs_in_sheet, we didn't find the refdes in the master list!\n"); + return NULL; + } + + /* Now get all attribs associated with this refdes (in TABLE, indexed + * by position), and insert them into new_attrib_list. */ + new_attrib_list = s_string_list_new(); /* init new_attrib_list */ + + i = 0; + local_attrib_list = sheet_head->master_comp_attrib_list_head; + while (local_attrib_list != NULL) { /* iterate over all possible attribs */ + new_attrib_name = u_basic_strdup(local_attrib_list->data); /* take attrib name from column headings */ + + if ( ((sheet_head->component_table)[row][i]).attrib_value ) { + new_attrib_value = u_basic_strdup( ((sheet_head->component_table)[row][i]).attrib_value ); + name_value_pair = u_basic_strdup_multiple(new_attrib_name, "=", new_attrib_value, NULL); + free(new_attrib_value); + } else { + name_value_pair = u_basic_strdup_multiple(new_attrib_name, "=", NULL); /* empty attrib */ + } + s_string_list_add_item(new_attrib_list, &count, name_value_pair); /* add name=value to new list */ + free(new_attrib_name); + free(name_value_pair); + + /* Sanity check */ + if (count != i+1) { + /* for some reason, we have lost a name_value_pair somewhere . . . */ + fprintf(stderr, + "In s_toplevel_get_component_attribs_in_sheet, count != i! Exiting . . . .\n"); + exit(-1); + } + + /* iterate */ + i++; + local_attrib_list = local_attrib_list->next; + } /* while (local_attrib_list != NULL) */ + + return new_attrib_list; +} + + + +/*------------------------------------------------------------------ + * For each attrib string attached to the component, update it using the value + * held in new_comp_attrib_list. Algorithm: + * 1. Loop over name=value pairs held in new_comp_attrib_list. + * 2. For each name=value pair, look for corresponding attrib on o_current. + * 3. If the attrib exists on o_current and in name=value pair, write the + * new value in. + * 4. If the attrib exists on o_current, but is null in name=value pair, + * delete the attrib. + * 5. If the attribs doesn't exist on o_current, but is non-null in + * the name=value pair, create an attrib object and add it to the part. + * + * Calling args: OBJECT *o_current -- component (complex) to be updated. + * STRING_LIST *new_comp... -- list of name=value attribute pairs. + * Returns: Returns nothing because the changes are made in o_current, which + * is part of the global TOPLEVEL pr_current. + *------------------------------------------------------------------*/ +void s_toplevel_update_component_attribs_in_toplevel(OBJECT *o_current, + STRING_LIST *new_comp_attrib_list) +{ + STRING_LIST *local_list; + char *new_name_value_pair; + char *new_attrib_name; + char *new_attrib_value; + char *old_attrib_name; + char *old_attrib_value; + ATTRIB *a_current; + +#if DEBUG + printf("----- Entering s_toplevel_update_component_attribs_in_toplevel.\n"); +#endif + + /* loop on name=value pairs held in new_comp_attrib_list */ + local_list = new_comp_attrib_list; + while (local_list != NULL) { + new_name_value_pair = u_basic_strdup(local_list->data); +#if DEBUG + printf(" In s_toplevel_update_component_attribs_in_toplevel, handling entry in master list %s .\n", new_name_value_pair); +#endif + new_attrib_name = u_basic_breakup_string(new_name_value_pair, '=', 0); + new_attrib_value = u_basic_breakup_string(new_name_value_pair, '=', 1); + if (strlen(new_attrib_value) == 0) { + free(new_attrib_value); /* I wonder if I should check for non-NULL first? */ + new_attrib_value = NULL; /* u_basic_breakup_string doesn't return NULL for empty substring. */ + } + old_attrib_value = o_attrib_search_name_single_count(o_current, new_attrib_name, 0); + + /* ------- Four cases to consider: Case 1 ----- */ + if ( (old_attrib_value != NULL) && (new_attrib_value != NULL) && (strlen(new_attrib_value) != 0) ) { + /* simply write new attrib into place of old one. */ +#if DEBUG + printf("In s_toplevel_update_component_attribs_in_toplevel, about to replace old attrib with name= %s, value= %s\n", + new_attrib_name, new_attrib_value); +#endif + s_object_replace_attrib_in_object(o_current, new_attrib_name, new_attrib_value); + } + + /* ------- Four cases to consider: Case 2 ----- */ + else if ( (old_attrib_value != NULL) && (new_attrib_value == NULL) ) { + /* remove attrib from component*/ +#if DEBUG + printf("In s_toplevel_update_component_attribs_in_toplevel, about to remove old attrib with name= %s, value= %s\n", + new_attrib_name, old_attrib_value); +#endif + s_object_remove_attrib_in_object(o_current, new_attrib_name); + } + + /* ------- Four cases to consider: Case 3 ----- */ + else if ( (old_attrib_value == NULL) && (new_attrib_value != NULL) ) { + /* add new attrib to component. */ + +#if DEBUG + printf("In s_toplevel_update_component_attribs_in_toplevel, about to add new attrib with name= %s, value= %s\n", + new_attrib_name, new_attrib_value); +#endif + + s_object_add_comp_attrib_to_object(o_current, new_attrib_name, new_attrib_value); + + /* ------- Four cases to consider: Case 4 ----- */ + } else { + /* Do nothing. */ +#if DEBUG + printf("In s_toplevel_update_component_attribs_in_toplevel, nothing needs to be done.\n"); +#endif + } + + /* free everything and iterate */ + free(new_name_value_pair); + free(new_attrib_name); + if (new_attrib_value != NULL) { + free(new_attrib_value); /* Be careful, this can be NULL */ + } + if (old_attrib_value != NULL) { + free(old_attrib_value); /* Be careful, this can be NULL */ + } + local_list = local_list->next; + } /* while (local_list != NULL) */ + return; +} + + +/*------------------------------------------------------------------ + * + *------------------------------------------------------------------*/ +STRING_LIST *s_toplevel_get_net_attribs_in_sheet(char *netname) +{ + /* must be filled in */ + return; +} + + +/*------------------------------------------------------------------ + * + *------------------------------------------------------------------*/ +void s_toplevel_update_net_attribs_in_toplevel(OBJECT *o_current, + STRING_LIST *new_net_attrib_list) +{ + /* must be filled in */ + return; +} + + +/*------------------------------------------------------------------ + * This fcn takes a pointer to the OBJECT pin, and returns a list + * of attribs found attached to the pin. The returned list is a + * STRING_LIST where the ->data holds a name=value string. + * The algorithm is as follows: + * 1. Form refdes:pinnumber label for this pin. + * 2. Get row number of this refdes:pinnumber + * 3. Create a list of name=value pairs from entries in the pin_table + * on this row. + * 4. Return list of name=value pairs found. + *------------------------------------------------------------------*/ +STRING_LIST *s_toplevel_get_pin_attribs_in_sheet(char *refdes, OBJECT *pin) +{ + STRING_LIST *new_attrib_list; + STRING_LIST *local_attrib_list; + STRING_LIST *row_list; + int i; + int row = -1; + int count = 0; + char *pinnumber; + char *row_label; + char *name_value_pair; + char *new_attrib_value; + char *new_attrib_name; + char *temp; + +#if DEBUG + printf("----- Entering s_toplevel_get_pin_attribs_in_sheet.\n"); +#endif + + /* First find pos of this pin in the master pin list */ + /* first convert refdes, pin to refdes:pinno text string. Then call table_get_index. */ + + pinnumber = o_attrib_search_name_single(pin, "pinnumber", NULL); + + if ( (refdes != NULL) && (pinnumber != NULL) ) { + row_label = u_basic_strdup_multiple(refdes, ":", pinnumber, NULL); + } else { + fprintf(stderr, + "In s_toplevel_get_pin_attribs_in_sheet, either refdes or pinnumber of object missing!\n"); + return NULL; + } + row = s_table_get_index(sheet_head->master_pin_list_head, row_label); + + /* Sanity check */ + if (row == -1) { + /* we didn't find the item in the list */ + fprintf(stderr, + "In s_toplevel_get_pin_attribs_in_sheet, we didn't find the refdes:pin in the master list!\n"); + return NULL; + } + + /* Now get all attribs associated with this refdes (in TABLE, indexed + * by position), and insert them into new_attrib_list. */ + new_attrib_list = s_string_list_new(); /* init new_attrib_list */ + + i = 0; + local_attrib_list = sheet_head->master_pin_attrib_list_head; + while (local_attrib_list != NULL) { /* iterate over all possible attribs */ + new_attrib_name = u_basic_strdup(local_attrib_list->data); /* take attrib name from column headings */ + + if ( ((sheet_head->pin_table)[row][i]).attrib_value ) { + new_attrib_value = u_basic_strdup( ((sheet_head->pin_table)[row][i]).attrib_value ); + name_value_pair = u_basic_strdup_multiple(new_attrib_name, "=", new_attrib_value, NULL); + free(new_attrib_value); + } else { + name_value_pair = u_basic_strdup_multiple(new_attrib_name, "=", NULL); /* empty attrib */ + } + s_string_list_add_item(new_attrib_list, &count, name_value_pair); /* add name=value to new list */ + free(new_attrib_name); + free(name_value_pair); + + /* Sanity check */ + if (count != i+1) { + /* for some reason, we have lost a name_value_pair somewhere . . . */ + fprintf(stderr, + "In s_toplevel_get_pin_attribs_in_sheet, count != i! Exiting . . . .\n"); + exit(-1); + } + + /* iterate */ + i++; + local_attrib_list = local_attrib_list->next; + } /* while (local_attrib_list != NULL) */ + + return new_attrib_list; +} + + + +/*------------------------------------------------------------------ + * For each attrib string attached to the pin, update it using the value + * held in new_pin_attrib_list. Algorithm: + * 1. Loop over name=value pairs held in new_pin_attrib_list. + * 2. For each name=value pair, look for corresponding attrib on pin. + * 3. If the attrib exists on pin and in name=value pair, write the + * new value in. + * 4. If the attrib exists on pin, but is null in name=value pair, + * delete the attrib. + * 5. If the attribs doesn't exist on pin, but is non-null in + * the name=value pair, create an attrib object and add it to the pin. + * Returns: Returns nothing because the changes are made in o_pin, which + * is part of the global TOPLEVEL pr_current. + *------------------------------------------------------------------*/ +void s_toplevel_update_pin_attribs_in_toplevel(char *refdes, OBJECT *o_pin, + STRING_LIST *new_pin_attrib_list) +{ + STRING_LIST *local_list; + char *new_name_value_pair; + char *new_attrib_name; + char *new_attrib_value; + char *old_attrib_name; + char *old_attrib_value; + ATTRIB *a_current; + +#if DEBUG + printf("----- Entering s_toplevel_update_pin_attribs_in_toplevel.\n"); +#endif + + /* loop on name=value pairs held in new_pin_attrib_list */ + local_list = new_pin_attrib_list; + while (local_list != NULL) { + new_name_value_pair = u_basic_strdup(local_list->data); +#if DEBUG + printf(" In s_toplevel_update_pin_attribs_in_toplevel, handling entry in master list %s .\n", new_name_value_pair); +#endif + new_attrib_name = u_basic_breakup_string(new_name_value_pair, '=', 0); + new_attrib_value = u_basic_breakup_string(new_name_value_pair, '=', 1); + if (strlen(new_attrib_value) == 0) { + free(new_attrib_value); /* I wonder if I should check for non-NULL first? */ + new_attrib_value = NULL; /* u_basic_breakup_string doesn't return NULL for empty substring. */ + } + old_attrib_value = o_attrib_search_name_single_count(o_pin, new_attrib_name, 0); + + /* ------- Four cases to consider: Case 1 ----- */ + if ( (old_attrib_value != NULL) && (new_attrib_value != NULL) && (strlen(new_attrib_value) != 0) ) { + /* simply write new attrib into place of old one. */ +#if DEBUG + printf("In s_toplevel_update_pin_attribs_in_toplevel, about to replace old attrib with new one: name= %s, value= %s\n", + new_attrib_name, new_attrib_value); +#endif + s_object_replace_attrib_in_object(o_pin, new_attrib_name, new_attrib_value); + } + + /* ------- Four cases to consider: Case 2 ----- */ + else if ( (old_attrib_value != NULL) && (new_attrib_value == NULL) ) { + /* remove attrib from pin */ +#if DEBUG + printf("In s_toplevel_update_pin_attribs_in_toplevel, about to remove old attrib with name= %s, value= %s\n", + new_attrib_name, old_attrib_value); +#endif + s_object_remove_attrib_in_object(o_pin, new_attrib_name); + } + + /* ------- Four cases to consider: Case 3 ----- */ + else if ( (old_attrib_value == NULL) && (new_attrib_value != NULL) ) { + /* add new attrib to pin. */ + +#if DEBUG + printf("In s_toplevel_update_pin_attribs_in_toplevel, about to add new attrib with name= %s, value= %s\n", + new_attrib_name, new_attrib_value); +#endif + + s_object_add_pin_attrib_to_object(o_pin, new_attrib_name, new_attrib_value); + + /* ------- Four cases to consider: Case 4 ----- */ + } else { + /* Do nothing. */ +#if DEBUG + printf("In s_toplevel_update_pin_attribs_in_toplevel, nothing needs to be done.\n"); +#endif + } + + /* free everything and iterate */ + free(new_name_value_pair); + free(new_attrib_name); + if (new_attrib_value != NULL) { + free(new_attrib_value); /* Be careful, this can be NULL */ + } + if (old_attrib_value != NULL) { + free(old_attrib_value); /* Be careful, this can be NULL */ + } + local_list = local_list->next; + } /* while (local_list != NULL) */ + + return; +} + + + + diff --git a/gattrib/src/x_dialog.c b/gattrib/src/x_dialog.c new file mode 100644 index 000000000..7efdd9233 --- /dev/null +++ b/gattrib/src/x_dialog.c @@ -0,0 +1,217 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns used to display dialog boxes. + *------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------ + * Includes required to run graphical widgets. + *------------------------------------------------------------------*/ +#include <stdio.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include <glib.h> +#ifdef HAS_GTK22 +#include <glib-object.h> +#endif + + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + + +#ifdef HAS_GTK22 +#include "gtksheet_2_2.h" +#include "gtkitementry_2_2.h" +#else +#include "gtksheet_1_2.h" +#include "gtkitementry_1_2.h" +#endif + + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <config.h> +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" +#include "../include/x_menu.h" + +/* --------------------------------------------------------- * + * Help/about dialog box fcns + * --------------------------------------------------------- */ +int x_dialog_about_keypress_callback(GtkWidget * widget, GdkEventKey * event, + GtkWidget *window) +{ + if (strcmp(gdk_keyval_name(event->keyval), "Escape") == 0) { +#ifdef DEBUG + printf("In x_dialog_about_keypress, trying to close window.\n"); +#endif + x_dialog_close_window(window); + return TRUE; + } + + return FALSE; +} + +/* --------------------------------------------------------- */ +int x_dialog_about_close_callback(GtkWidget * widget, GtkWidget *window) +{ + x_dialog_close_window(window); +} + + +/* --------------------------------------------------------- */ +void x_dialog_about_dialog() +{ + GtkWidget *about_window; + GtkWidget *label = NULL; + GtkWidget *buttonclose = NULL; + GtkWidget *vbox, *action_area; + char *string; + + + about_window = x_dialog_create_dialog_box(&vbox, &action_area); + /* about_window = gtk_window_new(GTK_WINDOW_POPUP); */ + + gtk_window_position(GTK_WINDOW(about_window), + GTK_WIN_POS_MOUSE); + + gtk_window_set_title(GTK_WINDOW(about_window), "About..."); + gtk_container_border_width(GTK_CONTAINER(about_window), 5); + + gtk_signal_connect(GTK_OBJECT(about_window), + "destroy", GTK_SIGNAL_FUNC(x_dialog_about_close_callback), + GTK_WIDGET(about_window) ); + + gtk_signal_connect(GTK_OBJECT(about_window), + "key_press_event", GTK_SIGNAL_FUNC(x_dialog_about_keypress_callback), + GTK_WIDGET(about_window) ); + + /* Now create text string to place in vbox area */ + string = g_strdup_printf("gEDA : GPL Electronic Design Automation"); + label = gtk_label_new(string); + free(string); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5); + gtk_widget_show(label); + + string = g_strdup_printf("This is gattrib -- gEDA's attribute editor"); + label = gtk_label_new(string); + free(string); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5); + gtk_widget_show(label); + + string = g_strdup_printf("Gattrib version: %s", VERSION); + label = gtk_label_new(string); + free(string); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5); + gtk_widget_show(label); + + string = + g_strdup_printf("Gattrib is written by: Stuart Brorson (sdb@cloud9.net)\n"); + string = + g_strdup_printf("%swith generous helpings of code from gschem, gnetlist, \n", + string); + string = + g_strdup_printf("%sand gtkextra, as well as support from the gEDA community.", string); + label = gtk_label_new(string); + free(string); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5); + gtk_widget_show(label); + + /* Now create button to stick in action area */ + buttonclose = gtk_button_new_with_label("Close"); + GTK_WIDGET_SET_FLAGS(buttonclose, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(action_area), buttonclose, TRUE, TRUE, 0); + gtk_signal_connect(GTK_OBJECT(buttonclose), "clicked", + GTK_SIGNAL_FUNC(x_dialog_about_close_callback), + GTK_WIDGET(about_window) ); + gtk_widget_show(buttonclose); + + if (!GTK_WIDGET_VISIBLE(about_window)) { + gtk_widget_show(about_window); + } +} + + +/* --------------------------------------------------------- * + * Fcns common to all dialog boxes + * This code also stolen from gschem & adapted for gattrib. + * --------------------------------------------------------- */ + +/* ---------------------------------------------------- * + * This creates a dialog box. It has two areas: the vbox + * area, and the action area. The idea is that the vbox + * area holds text, and the action area holds buttons or + * other active widgets. There is a separating line between + * the two area. You load one or the other areas like this (for example): + * gtk_box_pack_start(GTK_BOX(action_area), buttonclose, TRUE, TRUE, 0); + * --------------------------------------------------------- */ +GtkWidget *x_dialog_create_dialog_box(GtkWidget ** out_vbox, + GtkWidget ** out_action_area) +{ + GtkWidget *separator; + GtkWidget *vbox; + GtkWidget *action_area; + GtkWidget *dialog; + + if (!out_vbox) + return (NULL); + + if (!out_action_area) + return (NULL); + + dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(dialog), vbox); + gtk_widget_show(vbox); + + action_area = gtk_hbox_new(TRUE, 5); + gtk_container_set_border_width(GTK_CONTAINER(action_area), 10); + gtk_box_pack_end(GTK_BOX(vbox), action_area, FALSE, TRUE, 0); + gtk_widget_show(action_area); + + separator = gtk_hseparator_new(); + gtk_box_pack_end(GTK_BOX(vbox), separator, FALSE, TRUE, 0); + gtk_widget_show(separator); + + *out_vbox = vbox; + *out_action_area = action_area; + + return (dialog); +} + +/* ---------------------------------------------------- */ +void x_dialog_close_window(GtkWidget * window) +{ + gtk_widget_destroy(window); +} + + + + diff --git a/gattrib/src/x_fileselect.c b/gattrib/src/x_fileselect.c new file mode 100644 index 000000000..3f0574929 --- /dev/null +++ b/gattrib/src/x_fileselect.c @@ -0,0 +1,1433 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ +/*------------------------------------------------------------------ + * This file holds fcns used to display the file open/save dialog box. + * It was cloned from x_fileselect.c in gschem/src, and then hacked + * by SDB for use in gattrib. + *------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*------------------------------------------------------------------ + * Includes required to run graphical widgets. + *------------------------------------------------------------------*/ +#include <stdio.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include <glib.h> +#ifdef HAS_GTK22 +#include <glib-object.h> +#endif + +#include <sys/types.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#include <sys/stat.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_DIRENT_H +#include <dirent.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + + + +#ifdef HAS_GTK22 +#include "gtksheet_2_2.h" +#include "gtkitementry_2_2.h" +#else +#include "gtksheet_1_2.h" +#include "gtkitementry_1_2.h" +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <config.h> +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" +#include "../include/x_menu.h" + + + +/* ----- x_fileselect stuff begins here ----- */ + +#define DIR_LIST_WIDTH 180 +#define DIR_LIST_HEIGHT 180 +#define FILE_LIST_WIDTH 180 +#define FILE_LIST_HEIGHT 180 + +/* ------------------------------------------------------------- * + * This destroys the entire FILEDIALOG structure & frees its memory. + * There is another fcn which just closes the window. + * ------------------------------------------------------------- */ +void +x_fileselect_destroy_window(GtkWidget * widget, FILEDIALOG * f_current) +{ + +#if DEBUG + printf("In x_fileselect_destroy_window, about to destroy window!\n"); +#endif + x_fileselect_free_list_buffers(f_current); + + if (f_current->directory) { + free(f_current->directory); + f_current->directory = NULL; + } + + if (f_current->filename) { + free(f_current->filename); + f_current->filename = NULL; + } + + gtk_grab_remove(f_current->xfwindow); + f_current->toplevel = NULL; + f_current->xfwindow = NULL; + free(f_current); + /* *window = NULL; */ + return; +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +int +x_fileselect_keypress(GtkWidget * widget, GdkEventKey * event, + FILEDIALOG * f_current) +{ + if (strcmp(gdk_keyval_name(event->keyval), "Escape") == 0) { + x_fileselect_close(NULL, f_current); + return TRUE; + } + + return FALSE; +} + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void x_fileselect_init_list_buffers(FILEDIALOG * f_current) +{ + int i; + + /* Shouldn't we malloc something in here?? */ + for (i = MAX_FILES; i >= 0; i--) { + f_current->file_entries[i] = NULL; + } + + for (i = MAX_DIRS; i >= 0; i--) { + f_current->directory_entries[i] = NULL; + } + + return; +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void x_fileselect_free_list_buffers(FILEDIALOG * f_current) +{ + int i; + + for (i = MAX_FILES; i >= 0; i--) { + if (f_current->file_entries[i]) + free(f_current->file_entries[i]); + + f_current->file_entries[i] = NULL; + } + + for (i = MAX_DIRS; i >= 0; i--) { + if (f_current->directory_entries[i]) + free(f_current->directory_entries[i]); + + f_current->directory_entries[i] = NULL; + } + return; +} + + + +/*********** File Open/Save As... specific code starts here ***********/ +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void x_fileselect_update_dirfile(FILEDIALOG * f_current, char *filename) +{ + char *temp = NULL; + + if (f_current->filename) { + free(f_current->filename); + f_current->filename = NULL; + } + + if (f_current->directory) { + free(f_current->directory); + f_current->directory = NULL; + } + + /* this may cause problems on non POSIX complient systems */ + temp = getcwd(NULL, 1024); + + if (filename) { + f_current->directory = u_basic_strdup(temp); + f_current->filename = u_basic_strdup(filename); + + free(temp); +#ifdef __MINGW32__ + if (u_basic_has_trailing(f_current->directory, PATH_SEPARATER_CHAR)) { + temp = u_basic_strdup_multiple(f_current->directory, + f_current->filename, NULL); + } else { +#endif + temp = u_basic_strdup_multiple(f_current->directory, + PATH_SEPARATER_STRING, + f_current->filename, NULL); +#ifdef __MINGW32__ + } +#endif + + gtk_entry_set_text(GTK_ENTRY(f_current->filename_entry), temp); + + } else { + f_current->directory = u_basic_strdup(temp); + + if (f_current->filename) { + free(f_current->filename); + f_current->filename = NULL; + } + + gtk_entry_set_text(GTK_ENTRY(f_current->filename_entry), + f_current->directory); + } + + free(temp); + +#if DEBUG + printf("In x_fileselect_update_dirfile, directory: %s\n", f_current->directory); +#endif + + return; +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void +x_fileselect_setup_list_buffers(FILEDIALOG * f_current, + int num_directories, int num_files) +{ + int i; + + for (i = num_files+1; i >= 0; i--) { + if (f_current->file_entries[i]) { + free(f_current->file_entries[i]); + } + f_current->file_entries[i] = NULL; + } + + for (i = num_directories+1; i >= 0; i--) { + if (f_current->directory_entries[i]) { + free(f_current->directory_entries[i]); + } + f_current->directory_entries[i] = NULL; + } + return; +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +/* returns TRUE if the file should be included (passes the filter) */ +/* else returns FALSE */ +int x_fileselect_include_file(char *filename, int filter_type) +{ + switch (filter_type) { + case (FILEDIALOG_SCH_ONLY): + if (strstr(filename, ".sch")) { + return (TRUE); + } + break; + + case (FILEDIALOG_SYM_ONLY): + if (strstr(filename, ".sym")) { + return (TRUE); + } + break; + + case (FILEDIALOG_SCH_SYM): + if (strstr(filename, ".sch") || strstr(filename, ".sym")) { + return (TRUE); + } + break; + + case (FILEDIALOG_ALL_FILES): + return (TRUE); + break; + } + + return (FALSE); +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void x_fileselect_fill_lists(FILEDIALOG * f_current) +{ + DIR *directory; + struct dirent *dirent_ptr; + int num_files = 0; + int num_directories = 0; + int file_count = 0; + int dir_count = 0; + struct stat stat_en; + char path_buf[MAXPATHLEN * 2]; + char *text[2]; + char *temp; + int i; + int max_width = 0; + int width; + int first, last, j, done = 0; +#ifdef __MINGW32__ + int has_trailing = FALSE; +#endif + + directory = opendir(f_current->directory); +#ifdef DEBUG + printf("In x_fileselect_fill_lists, directory = %s\n", directory); +#endif + + +#ifdef __MINGW32__ + has_trailing = u_basic_has_trailing(f_current->directory, + PATH_SEPARATER_CHAR); +#endif + + if (!directory) { + fprintf(stderr, "Agg, could not open directory: %s\n", + f_current->directory); + return; + } + + while ((dirent_ptr = readdir(directory)) != NULL) { +#ifdef __MINGW32__ + if (has_trailing) { + sprintf(path_buf, "%s%s", f_current->directory, dirent_ptr->d_name); + } else { +#endif + sprintf(path_buf, "%s%c%s", f_current->directory, + PATH_SEPARATER_CHAR, dirent_ptr->d_name); +#ifdef __MINGW32__ + } +#endif + + if (stat(path_buf, &stat_en) >= 0 && S_ISDIR(stat_en.st_mode)) { + /* printf("dir: %s\n", path_buf); */ + num_directories++; + } else { + /* printf("file: %s\n", path_buf); */ + num_files++; + } + } + + + if (num_directories > MAX_DIRS) { + fprintf(stderr, "Too many directories! Increase MAX_DIRS\n"); + exit(-1); + } + + if (num_files > MAX_FILES) { + fprintf(stderr, "Too many files! Increase MAX_FILES\n"); + exit(-1); + } + + x_fileselect_setup_list_buffers(f_current, num_directories, num_files); + + rewinddir(directory); + + while ((dirent_ptr = readdir(directory)) != NULL) { +#ifdef __MINGW32__ + if (has_trailing) { + sprintf(path_buf, "%s%s", f_current->directory, dirent_ptr->d_name); + } else { +#endif + sprintf(path_buf, "%s%c%s", f_current->directory, + PATH_SEPARATER_CHAR, dirent_ptr->d_name); +#ifdef __MINGW32__ + } +#endif + if (stat(path_buf, &stat_en) >= 0 && S_ISDIR(stat_en.st_mode) && + (strcmp(dirent_ptr->d_name, ".") != 0)) { + + f_current->directory_entries[dir_count] = (char *) + malloc(sizeof(char) * (strlen(dirent_ptr->d_name) + 2)); + + sprintf(f_current->directory_entries[dir_count], + "%s", dirent_ptr->d_name); + dir_count++; + + } else { + if (x_fileselect_include_file(dirent_ptr->d_name, + f_current->filter_type)) { + f_current->file_entries[file_count] = (char *) + malloc(sizeof(char) * (strlen(dirent_ptr->d_name) + 1)); + strcpy(f_current->file_entries[file_count], dirent_ptr->d_name); + file_count++; + } + } + } + +#if DEBUG + printf("In x_fileselect_fill_lists, FILE COUNT: %d\n", file_count); +#endif + + /* lame bubble sort */ + first = 0; + last = file_count; + while (!done) { + + done = 1; + for (j = first; j < last - 1; j++) { + if (strcmp(f_current->file_entries[j], + f_current->file_entries[j + 1]) > 0) { + temp = f_current->file_entries[j]; + f_current->file_entries[j] = f_current->file_entries[j + 1]; + f_current->file_entries[j + 1] = temp; + done = 0; + } + } + last = last - 1; + + } + + /* lame bubble sort */ + done = 0; + first = 0; + last = dir_count; + while (!done) { + done = 1; + for (j = first; j < last - 1; j++) { + if (strcmp(f_current->directory_entries[j], + f_current->directory_entries[j + 1]) > 0) { + temp = f_current->directory_entries[j]; + f_current->directory_entries[j] = + f_current->directory_entries[j + 1]; + f_current->directory_entries[j + 1] = temp; + done = 0; + } + } + last = last - 1; + + } + + gtk_clist_freeze(GTK_CLIST(f_current->dir_list)); + gtk_clist_clear(GTK_CLIST(f_current->dir_list)); + gtk_clist_freeze(GTK_CLIST(f_current->file_list)); + gtk_clist_clear(GTK_CLIST(f_current->file_list)); + + text[0] = NULL; + text[1] = NULL; + max_width = 0; + for (i = 0; i < dir_count; i++) { + temp = u_basic_strdup_multiple(f_current->directory_entries[i], + PATH_SEPARATER_STRING, NULL); + text[0] = temp; + gtk_clist_append(GTK_CLIST(f_current->dir_list), text); + +#ifdef HAS_GTK22 + width = + gdk_string_width(gtk_style_get_font(f_current->dir_list->style), + f_current->directory_entries[i]); +#else + width = gdk_string_width(f_current->dir_list->style->font, + f_current->directory_entries[i]); +#endif + + if (width > max_width) { + gtk_clist_set_column_width(GTK_CLIST(f_current->dir_list), 0, width); + max_width = width; + } + + free(temp); +#if DEBUG + printf("In x_fileselect_fill_lists, directory: %s\n", f_current->directory_entries[i]); +#endif + } + + max_width = 0; + for (i = 0; i < file_count; i++) { + text[0] = f_current->file_entries[i]; + gtk_clist_append(GTK_CLIST(f_current->file_list), text); + +#ifdef HAS_GTK22 + width = + gdk_string_width(gtk_style_get_font(f_current->dir_list->style), + f_current->file_entries[i]); +#else + width = gdk_string_width(f_current->dir_list->style->font, + f_current->file_entries[i]); +#endif + + if (width > max_width) { + gtk_clist_set_column_width(GTK_CLIST(f_current-> + file_list), 0, width); + max_width = width; + } +#if DEBUG + printf("In x_fileselect_fill_lists, file: %s\n", f_current->file_entries[i]); +#endif + } + + closedir(directory); + gtk_clist_thaw(GTK_CLIST(f_current->file_list)); + gtk_clist_thaw(GTK_CLIST(f_current->dir_list)); + f_current->last_search = -1; + return; +} + + +/* ------------------------------------------------------------- * + * This is a support fcn for the filter menu + * ------------------------------------------------------------- */ +gint x_fileselect_sch_files(GtkWidget * w, FILEDIALOG * f_current) +{ + f_current->filter_type = FILEDIALOG_SCH_ONLY; + x_fileselect_fill_lists(f_current); + return (0); +} + + + +/* ------------------------------------------------------------- * + * This is a support fcn for the filter menu + * ------------------------------------------------------------- */ +gint x_fileselect_all_files(GtkWidget * w, FILEDIALOG * f_current) +{ + f_current->filter_type = FILEDIALOG_ALL_FILES; + x_fileselect_fill_lists(f_current); + return (0); +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +/* this is from gtktest.c */ +static GtkWidget *x_fileselect_filter_menu(FILEDIALOG * f_current) +{ + GtkWidget *menu; + GtkWidget *menuitem; + GSList *group; + char *buf; + + menu = gtk_menu_new(); + group = NULL; + + buf = g_strdup_printf("sch - Schematics"); + menuitem = gtk_radio_menu_item_new_with_label(group, buf); + free(buf); + group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem)); + gtk_menu_append(GTK_MENU(menu), menuitem); + gtk_signal_connect(GTK_OBJECT(menuitem), "activate", + (GtkSignalFunc) x_fileselect_sch_files, f_current); + gtk_widget_show(menuitem); + + buf = g_strdup_printf("* - All Files"); + menuitem = gtk_radio_menu_item_new_with_label(group, buf); + free(buf); + group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem)); + gtk_menu_append(GTK_MENU(menu), menuitem); + gtk_signal_connect(GTK_OBJECT(menuitem), "activate", + (GtkSignalFunc) x_fileselect_all_files, f_current); + gtk_widget_show(menuitem); + + switch (f_current->filter_type) { + + case (FILEDIALOG_SCH_ONLY): + gtk_menu_set_active(GTK_MENU(menu), 0); + break; + + case (FILEDIALOG_SYM_ONLY): + gtk_menu_set_active(GTK_MENU(menu), 1); + break; + + case (FILEDIALOG_SCH_SYM): + gtk_menu_set_active(GTK_MENU(menu), 2); + break; + + case (FILEDIALOG_ALL_FILES): + gtk_menu_set_active(GTK_MENU(menu), 3); + break; + } + + return menu; +} + + + + +/* ------------------------------------------------------------- * + * This fcn just closes the window and does nothing else. + * It is invoked when you click "cancel" during a save + * operation. + * ------------------------------------------------------------- */ +void x_fileselect_saveas_close(GtkWidget * w, FILEDIALOG * f_current) +{ + gtk_widget_destroy(GTK_WIDGET(f_current->xfwindow)); + +#if 0 /* this isn't relavent anymore */ + w_current = f_current->toplevel; + + if (f_current->filesel_type == SAVEAS_QUIT) { + exit_dialog(w_current); + } + + if (f_current->filesel_type == SAVEAS_OPEN) { + x_fileselect_setup(w_current, FILESELECT, SAVEAS_OPEN); + } + + if (f_current->filesel_type == SAVEAS_NEW) { + w_current->page_current->CHANGED = 0; + i_callback_file_new(w_current, 0, NULL); + } +#endif + + /* do nothing if close is pressed for SAVEAS_CLOSE case */ + return; +} + + +/* ------------------------------------------------------------- * + * This fcn saves out the files and then closes the fileselect + * dialog box. It is invoked when you click "save" during a save + * operation. + * ------------------------------------------------------------- */ +void x_fileselect_saveas(GtkWidget * w, FILEDIALOG * f_current) +{ + + TOPLEVEL *w_current; + gchar *string; + int len; + + w_current = f_current->toplevel; + + string = (gchar *) gtk_entry_get_text(GTK_ENTRY(f_current->filename_entry)); + + if (!string) { + return; + } + + len = strlen(string); + + if (string[len - 1] != PATH_SEPARATER_CHAR) { + if (w_current->page_current->page_filename) { + free(w_current->page_current->page_filename); + } + + w_current->page_current->page_filename = u_basic_strdup(string); + + /* Try to do save by calling f_save . . . . */ + if (f_save(w_current, string)) { + s_log_message("Saved As [%s]\n", + w_current->page_current->page_filename); + + /* Update filename for "saveas" operation */ + x_fileselect_set_filename(w_current, string); + + w_current->page_current->CHANGED = 0; + } else { + s_log_message("Could NOT save [%s]\n", + w_current->page_current->page_filename); + } + + x_fileselect_close(NULL, f_current); + +#if 0 + /* What do these do? */ + if (f_current->filesel_type == SAVEAS_QUIT) { + x_window_close(w_current); + } else if (f_current->filesel_type == SAVEAS_OPEN) { + i_callback_file_open(w_current, 0, NULL); + } else if (f_current->filesel_type == SAVEAS_NEW) { + i_callback_file_new(w_current, 0, NULL); + } else if (f_current->filesel_type == SAVEAS_CLOSE) { + i_callback_page_close(w_current, 0, NULL); + } +#endif + + /* do nothing if SAVEAS_NONE */ + } else { + s_log_message("Specify a Filename!\n"); + } + return; +} + + +/* ------------------------------------------------------------- * + * I think this puts the new filename back into pr_current as part + * of a "save as" operation. This was originally i_set_filename + * in gschem/src/i_basic.c + * ------------------------------------------------------------- */ +void x_fileselect_set_filename(TOPLEVEL * w_current, const char *string) +{ + char trunc_string[41]; + int len; + int i; + + if (!w_current->filename_label) { + return; + } + + if (string) { + len = strlen(string); + w_current->DONT_RESIZE = 1; + + if (w_current->filename_label) { + if (len > 40) { + + trunc_string[0] = '.'; + trunc_string[1] = '.'; + trunc_string[2] = '.'; + + trunc_string[40] = '\0'; + for (i = 39; i > 2; i--) { + if (len >= 0) { + trunc_string[i] = string[len]; + } else { + break; + } + len--; + } + + gtk_label_set(GTK_LABEL(w_current->filename_label), trunc_string); + + } else { + + gtk_label_set(GTK_LABEL(w_current-> + filename_label), (char *) string); + } + } + } + return; +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void x_fileselect_change_dir(FILEDIALOG * f_current, char *new_directory) +{ + if (new_directory) { + chdir(new_directory); + x_fileselect_update_dirfile(f_current, NULL); + x_fileselect_fill_lists(f_current); + } +} + + +/* ------------------------------------------------------------- * + * This is the callback from the fileselect "open" button. It does + * the following: + * 1. It loops over all filenames returned in the CLIST + * 2. For each filenname, it calls s_toplevel_open_file, which + * reads in the file & fills out pr_current (which is a gloabal). + * If this is a new file, it also calls s_sheet_data_add_master_*_list + * to fill out the master lists. + * ------------------------------------------------------------- */ +/* don't use widget, since it can be NULL */ +void x_fileselect_open_file(GtkWidget *w, FILEDIALOG *f_current) +{ + PAGE *p_local; + char *string; + int len; + int return_code = 0; + int old_num_rows, old_num_cols; /* There is a better way . . . */ + + char *filename = NULL; + GList *files; + int row; + +#ifdef DEBUG + printf("We have just entered x_fileselect_open_file.\n"); +#endif + + /* get GList of selected files */ + files = (GTK_CLIST(f_current->file_list))->selection; + if (files) { + + old_num_rows = sheet_head->comp_count; + old_num_cols = sheet_head->comp_attrib_count; + + /* iterate over selected files */ + for (; files; files = files->next) { + row = (int) files->data; /* Why do we need to do cast here? */ + /* because files->data is a void * */ + gtk_clist_get_text(GTK_CLIST(f_current->file_list), row, 0, + &filename); + + /* allocate space, then stick full, absolute filename into string */ + string = malloc(strlen(f_current->directory) + strlen(filename) + 2); + if (!string) { /* sanity check . . . */ + /* free(string); freeing a null pointer is bad. */ + return; + } + /* string is full name of file. */ + sprintf(string, "%s/%s", f_current->directory, filename); + +#if DEBUG + printf("In x_fileselect_open_file, opening string = %s\n", string); +#endif + + if (first_page == 1) { + if (pr_current->page_current->page_filename) { + /* Page structure & first page has already been created in + * s_project_create_new. Therefore, just rename the first page + * and open the project. */ + free(pr_current->page_current->page_filename); + } + return_code |= s_toplevel_read_page(string); /* read in first page, or in return code */ + first_page = 0; + } else { + return_code |= s_toplevel_read_page(string); /* read in secondary page, or in return code */ + } + free(string); + + /* Now add all items found to the master lists */ + s_sheet_data_add_master_comp_list_items(pr_current->page_current->object_head); + s_sheet_data_add_master_comp_attrib_list_items(pr_current->page_current->object_head); +#if 0 + /* Note that this must be changed. We need to input the entire project + * before doing anything with the nets because we need to first + * determine where they are all connected! */ + s_sheet_data_add_master_net_list_items(pr_current->page_current->object_head); + s_sheet_data_add_master_net_attrib_list_items(pr_current->page_current->object_head); +#endif + + s_sheet_data_add_master_pin_list_items(pr_current->page_current->object_head); + s_sheet_data_add_master_pin_attrib_list_items(pr_current->page_current->object_head); + + + } /* end of loop over files */ + + /* Now update rest of project if we had at least one new file */ + if (return_code) { + /* ---------- Sort the master lists ---------- */ + s_string_list_sort_master_comp_list(); + s_string_list_sort_master_comp_attrib_list(); +#if 0 + /* Note that this must be changed. We need to input the entire project + * before doing anything with the nets because we need to first + * determine where they are all connected! */ + s_string_list_sort_master_net_list(); + s_string_list_sort_master_net_attrib_list(); +#endif + + /* ---------- Now create and load the tables ---------- */ + sheet_head->component_table = s_table_new(sheet_head->comp_count, sheet_head->comp_attrib_count); + sheet_head->net_table = s_table_new(sheet_head->net_count, sheet_head->net_attrib_count); + + p_local = pr_current->page_head; /* must iterate over all pages in design */ + while (p_local != NULL) { + if (p_local->pid != -1) { /* only traverse pages which are toplevel */ + if (p_local->object_head && p_local->page_control == 0) { + s_table_add_toplevel_comp_items_to_comp_table(p_local->object_head); /* adds all objects from page */ +#if 0 + /* Note that this must be changed. We need to input the entire project + * before doing anything with the nets because we need to first + * determine where they are all connected! */ + s_table_add_toplevel_net_items_to_net_table(p_local->object_head); /* adds all objects from page */ +#endif + + s_table_add_toplevel_pin_items_to_pin_table(p_local->object_head); /* adds all objects from page */ + + } + } + p_local = p_local->next; /* iterate to next schematic page */ + } /* while(p_local != NULL) */ + +#if DEBUG + printf("In x_fileselect_open_file -- we have just added more files to the project.\n"); +#endif + + /* -------------- update windows --------------- */ + x_window_add_items(); /* This updates the top level stuff, + * and then calls another fcn to update + * the GtkSheet itself. */ +#ifdef DEBUG + printf("In x_fileselect_open_file -- we have just returned from x_window_add_items.\n"); +#endif + } else { + fprintf(stderr, "Couldn't open any file!.\n"); + } + } /* if (files) */ + + /* Now close file dialog window . . . . */ + gtk_widget_destroy(GTK_WIDGET(f_current->xfwindow)); + + return; +} + + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void +x_fileselect_dir_button(GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, FILEDIALOG * f_current) +/* + * SDB notes: This fcn is called for almost every event which occurs + * to the "open file" widget, except when the actual file is to be + * opened. Stuff handled by this fcn are e.g. highlighting the file + * selected in the file list, updating the dir/file lists upon change + * of directory, etc. + */ +{ + char *temp = NULL; + + gtk_clist_get_text(GTK_CLIST(f_current->dir_list), row, 0, &temp); + + if (temp) { +#if DEBUG + printf("In x_fileselect_dir_button, selected: %d _%s_\n", row, temp); +#endif + if (bevent) { + switch (bevent->type) { + case (GDK_2BUTTON_PRESS): + x_fileselect_change_dir(f_current, temp); + break; + + default: + + break; + } + } + } +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void +x_fileselect_file_button(GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, FILEDIALOG * f_current) +/* + * SDB notes: This fcn is apparently called for each event occuring to + * the file clist. This fcn determines if the button event captured was to + * update the directory/file display or was a doubleclick on a file + * in the clist meant to open the file. It then + * calls the appropriate handler. Unfortunately, in the event of + * selecting multiple files (using ENHANCED mode), bevent is nonnull + * only on the first call. + */ +{ + char *temp = NULL; + + /* put name of desired file into temp */ + gtk_clist_get_text(GTK_CLIST(f_current->file_list), row, 0, &temp); + + if (temp) { + +#if DEBUG + printf("In x_fileselect_file_button, file selected: %d %s\n", row, + temp); + if (bevent) { + printf("in x_fileselect_file_button, bevent->type = %d\n", + bevent->type); + } else { + printf("In x_fileselect_file_button, bevent = NULL\n"); + } +#endif + + if (bevent) { + switch (bevent->type) { + case (GDK_2BUTTON_PRESS): + x_fileselect_open_file(NULL, f_current); + break; + + default: + x_fileselect_update_dirfile(f_current, temp); + break; + } + } + } +} + + +/* ------------------------------------------------------------- * + * + * ------------------------------------------------------------- */ +void +x_fileselect_update_dirfile_saveas(FILEDIALOG * f_current, + char *new_filename) +{ + char *temp = NULL; + char *ptr = NULL; + char *filename = NULL; + char *directory = NULL; + int i; + +#ifdef DEBUG + printf("In x_fileselect_update_dirfile_saveas, new_filename = [%s]\n", new_filename); +#endif + + if (new_filename == NULL) { + return; + } + + if (f_current->filename) { + free(f_current->filename); + f_current->filename = NULL; + } + + if (f_current->directory) { + free(f_current->directory); + f_current->directory = NULL; + } + +#if 0 + directory = (char *) malloc(sizeof(char) * (strlen(new_filename) + 1)); + filename = (char *) malloc(sizeof(char) * (strlen(new_filename) + 1)); + + ptr = new_filename; + temp = strrchr(new_filename, PATH_SEPARATER_CHAR); + if (temp) { + /* SDB asks: What is all this stuff for? */ + i = 0; + while (ptr != temp && ptr[0] != '\0') { + directory[i] = *ptr; + ptr++; + i++; + } + directory[i] = '\0'; + ptr++; /* skip over last '/' */ + i = 0; + while (ptr[0] != '\0') { + filename[i] = *ptr; + ptr++; + i++; + } + filename[i] = '\0'; + } else { +#endif + /* SDB says: This is what generally is run. What is the above stuff for? */ + directory = getcwd(NULL, 1024); + filename = u_basic_strdup(new_filename); +#if 0 + } +#endif + +#if DEBUG + printf("In x_fileselect_update_dirfile_saveas, directory = %s\n", directory); + printf("In x_fileselect_update_dirfile_saveas, filename = %s\n", filename); +#endif + + if (directory) { + f_current->directory = u_basic_strdup(directory); + free(directory); + } + + if (filename) { + f_current->filename = u_basic_strdup(filename); + free(filename); + } +#ifdef __MINGW32__ + if (u_basic_has_trailing(f_current->directory, PATH_SEPARATER_CHAR)) { + temp = u_basic_strdup_multiple(f_current->directory, + f_current->filename, NULL); + } else { +#endif + temp = u_basic_strdup_multiple(f_current->directory, + PATH_SEPARATER_STRING, + f_current->filename, NULL); +#ifdef __MINGW32__ + } +#endif + gtk_entry_set_text(GTK_ENTRY(f_current->filename_entry), temp); + + free(temp); + +#if DEBUG + printf("In x_fileselect_update_dirfile_saveas, f_current->directory = %s\n", f_current->directory); + printf("In x_fileselect_update_dirfile_saveas, f_current->filename = %s\n", f_current->filename); +#endif + + return; +} + + +/* ------------------------------------------------------------- * + * This fcn closes the filedialog window, but doesn't kill the + * filedialog object. There is another fcn which will kill + * the whole object. + * ------------------------------------------------------------- */ +void x_fileselect_close(GtkWidget * w, FILEDIALOG * f_current) +{ + if(f_current->filter) + gtk_widget_destroy(GTK_WIDGET(f_current->filter)); + + if(f_current->search_entry) + gtk_widget_destroy(GTK_WIDGET(f_current->search_entry)); + + if(f_current->search_label) + gtk_widget_destroy(GTK_WIDGET(f_current->search_label)); + + if(f_current->filename_entry) + gtk_widget_destroy(GTK_WIDGET(f_current->filename_entry)); + + if(f_current->dir_list) + gtk_widget_destroy(GTK_WIDGET(f_current->dir_list)); + + if(f_current->file_list) + gtk_widget_destroy(GTK_WIDGET(f_current->file_list)); + + if(f_current->preview_checkbox) + gtk_widget_destroy(GTK_WIDGET(f_current->preview_checkbox)); + + if(f_current->component_pulldown) + gtk_widget_destroy(GTK_WIDGET(f_current->component_pulldown)); + + /* Finally kill main widget */ + gtk_widget_destroy(GTK_WIDGET(f_current->xfwindow)); + return; +} + + + +/*********** File Open/Save As... specific code ends here ***********/ + + +/******************************************************************/ +/*------------------------------------------------------------------ + * This is the main fcn which creates the "open file" dialog box. + * filesel_type can be one of: + *------------------------------------------------------------------*/ +void x_fileselect_setup(TOPLEVEL *pr_current, int filesel_type) +{ + GtkWidget *buttonapply = NULL; + GtkWidget *buttonclose = NULL; + GtkWidget *scrolled_win; + GtkWidget *action_area; + GtkWidget *separator; + GtkWidget *optionmenu; + GtkWidget *drawbox; + GtkWidget *label; + GtkWidget *searchbox; + + int type = FILESELECT; /* This was a calling arg in gschem */ + /* here, I have just made it fixed. */ + + FILEDIALOG *f_current; + + GtkWidget *vbox; + GtkWidget *list_hbox; + char *dir_title[2]; + char *file_title[2]; + + /* Initialize filedialog object. This object holds pointers + * to all information in filedialog window, including pointers + * to several widgets in the window. */ + f_current = malloc(sizeof(FILEDIALOG)); + /* (pr_current->fileselect)[0] = f_current; */ /* Keep a pointer to the file dialog in TOPLEVEL */ + + f_current->xfwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + f_current->toplevel = pr_current; /* This points back to toplevel for reading in files. */ + f_current->type = 0; /* used to be type */ + f_current->filesel_type = filesel_type; + f_current->last_search = -1; + f_current->filename = NULL; + f_current->directory = NULL; + f_current->dir_list = NULL; + f_current->file_list = NULL; + x_fileselect_init_list_buffers(f_current); /* initializes the directory and file lists */ + + + /* These widgets are not used in gattrib. I need to make them + * null so that I don't get segfaults when manipulating windows */ + f_current->filter = NULL; + f_current->search_entry = NULL; + f_current->search_label = NULL; + f_current->filename_entry = NULL; + f_current->preview = NULL; + f_current->preview_checkbox = NULL; + f_current->component_pulldown = NULL; + +#ifdef DEBUG + printf("We have just entered x_fileselect_setup.\n"); +#endif + + /* ----- Create main top-level window ----- */ + gtk_window_position(GTK_WINDOW(f_current->xfwindow), + GTK_WIN_POS_MOUSE); + + if (filesel_type == OPEN) { + gtk_window_set_title(GTK_WINDOW(f_current->xfwindow), + "Open..."); + } else if (filesel_type == SAVEAS) { + gtk_window_set_title(GTK_WINDOW(f_current->xfwindow), + "Save As..."); + } else if (filesel_type == SAVEAS_CLOSE) { + gtk_window_set_title(GTK_WINDOW(f_current->xfwindow), + "Save As..."); + } + + gtk_signal_connect(GTK_OBJECT(f_current->xfwindow), + "destroy", + GTK_SIGNAL_FUNC(x_fileselect_destroy_window), + f_current); + + gtk_signal_connect(GTK_OBJECT(f_current->xfwindow), "key_press_event", + (GtkSignalFunc) x_fileselect_keypress, f_current); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(f_current->xfwindow), 10); + gtk_container_add(GTK_CONTAINER(f_current->xfwindow), vbox); + gtk_widget_show(vbox); + + action_area = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(action_area), + GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(action_area), 5); + gtk_box_pack_end(GTK_BOX(vbox), action_area, TRUE, FALSE, 10); + gtk_widget_show(action_area); + + + /* ----- Create the filter selection area ----- */ + f_current->filter_type = FILEDIALOG_SCH_ONLY; /* we only handle schematics in gattrib */ + + label = gtk_label_new("Filter"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + f_current->filter = gtk_option_menu_new(); + gtk_option_menu_set_menu(GTK_OPTION_MENU(f_current->filter), + x_fileselect_filter_menu(f_current)); + /* gtk_option_menu_set_history(GTK_OPTION_MENU(f_current->filter), + 4); */ + gtk_box_pack_start(GTK_BOX(vbox), f_current->filter, + FALSE, FALSE, 0); + gtk_widget_show(f_current->filter); + + + list_hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), list_hbox, TRUE, TRUE, 0); + gtk_widget_show(list_hbox); + + separator = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE, TRUE, 0); + gtk_widget_show(separator); + + drawbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), drawbox, TRUE, FALSE, 5); + gtk_widget_show(drawbox); + + searchbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(drawbox), searchbox, TRUE, TRUE, 10); + gtk_widget_show(searchbox); + + + /* ----- Create the "directories"/"libraries" clist widgets ----- */ + dir_title[0] = u_basic_strdup("Directories"); + dir_title[1] = NULL; + f_current->dir_list = gtk_clist_new_with_titles(1, + (char **) dir_title); + gtk_widget_set_usize(f_current->dir_list, + DIR_LIST_WIDTH, DIR_LIST_HEIGHT); + gtk_signal_connect(GTK_OBJECT(f_current->dir_list), + "select_row", (GtkSignalFunc) + x_fileselect_dir_button, f_current); + + gtk_clist_column_titles_passive(GTK_CLIST(f_current->dir_list)); + + scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(scrolled_win), f_current->dir_list); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_container_set_border_width(GTK_CONTAINER(scrolled_win), 5); + gtk_box_pack_start(GTK_BOX(list_hbox), scrolled_win, TRUE, TRUE, 0); + gtk_clist_set_auto_sort(GTK_CLIST(f_current->dir_list), TRUE); + + gtk_widget_show(f_current->dir_list); + gtk_widget_show(scrolled_win); + free(dir_title[0]); + + /* ----- Create the files clist ----- */ + file_title[0] = u_basic_strdup("Files"); + file_title[1] = NULL; + f_current->file_list = gtk_clist_new_with_titles(1, + (gchar **) + file_title); + gtk_widget_set_usize(f_current->file_list, FILE_LIST_WIDTH, + FILE_LIST_HEIGHT); + + /* Stuff added by SDB to enable opening multiple files at once */ + gtk_clist_set_selection_mode(GTK_CLIST(f_current->file_list), + GTK_SELECTION_EXTENDED); + + gtk_signal_connect(GTK_OBJECT(f_current->file_list), "select_row", + /* This is file opening callback */ + (GtkSignalFunc) x_fileselect_file_button, + f_current); + + gtk_clist_column_titles_passive(GTK_CLIST(f_current->file_list)); + + scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(scrolled_win), f_current->file_list); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_container_set_border_width(GTK_CONTAINER(scrolled_win), 5); + gtk_box_pack_start(GTK_BOX(list_hbox), scrolled_win, TRUE, TRUE, 0); + gtk_clist_set_auto_sort(GTK_CLIST(f_current->file_list), TRUE); + + gtk_widget_show(f_current->file_list); + gtk_widget_show(scrolled_win); + free(file_title[0]); + + /* ----- Create the "Filename" text entry area ----- */ + label = gtk_label_new("Filename"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5); + gtk_widget_show(label); + + f_current->filename_entry = gtk_entry_new_with_max_length(1024); + gtk_editable_select_region(GTK_EDITABLE(f_current->filename_entry), 0, + -1); + gtk_box_pack_start(GTK_BOX(vbox), f_current->filename_entry, FALSE, + FALSE, 0); + + if (filesel_type == OPEN) { + gtk_signal_connect(GTK_OBJECT(f_current->filename_entry), + /* Here we connect the callback to + fileselect_open_file to the filename in + the filename text entry field.. */ + "activate", + GTK_SIGNAL_FUNC(x_fileselect_open_file), + f_current); + } else if ((filesel_type == SAVEAS_NONE) || + (filesel_type == SAVEAS_QUIT) || + (filesel_type == SAVEAS_OPEN) || + (filesel_type == SAVEAS_CLOSE) || + (filesel_type == SAVEAS_NEW)) { + gtk_signal_connect(GTK_OBJECT(f_current->filename_entry), + "activate", + GTK_SIGNAL_FUNC(x_fileselect_saveas), + f_current); + } + gtk_editable_select_region(GTK_EDITABLE(f_current->filename_entry), + 0, -1); + + gtk_widget_show(f_current->filename_entry); + + /* ----- Here we create the "open"/"save as"/"apply" buttons ----- */ + if (filesel_type == OPEN) { + buttonapply = gtk_button_new_with_label("Open"); + gtk_signal_connect(GTK_OBJECT(buttonapply), + "clicked", + GTK_SIGNAL_FUNC(x_fileselect_open_file), + f_current); /* Note that f_current points to pr_current + which is important when reading in + files. */ + } else if ((filesel_type == SAVEAS_NONE) || + (filesel_type == SAVEAS_QUIT) || + (filesel_type == SAVEAS_OPEN) || + (filesel_type == SAVEAS_CLOSE) || + (filesel_type == SAVEAS_NEW)) { + buttonapply = gtk_button_new_with_label("SaveAs"); + gtk_signal_connect(GTK_OBJECT(buttonapply), + "clicked", + GTK_SIGNAL_FUNC(x_fileselect_saveas), f_current); + } + + GTK_WIDGET_SET_FLAGS(buttonapply, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(action_area), buttonapply, TRUE, TRUE, 0); + /* This makes the "open" button the default */ + gtk_widget_grab_default(buttonapply); + gtk_widget_show(buttonapply); + + /* ----- Here we create the "cancel"/"close" buttons ----- */ + if (filesel_type == OPEN) { + buttonclose = gtk_button_new_with_label("Cancel"); + GTK_WIDGET_SET_FLAGS(buttonclose, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(action_area), + buttonclose, TRUE, TRUE, 0); + gtk_signal_connect(GTK_OBJECT(buttonclose), + "clicked", + GTK_SIGNAL_FUNC(x_fileselect_close), f_current); + gtk_widget_show(buttonclose); + + x_fileselect_update_dirfile(f_current, NULL); + x_fileselect_fill_lists(f_current); + } else if ((filesel_type == SAVEAS_NONE) || + (filesel_type == SAVEAS_QUIT) || + (filesel_type == SAVEAS_OPEN) || + (filesel_type == SAVEAS_CLOSE) || + (filesel_type == SAVEAS_NEW)) { + buttonclose = gtk_button_new_with_label("Cancel"); + GTK_WIDGET_SET_FLAGS(buttonclose, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(action_area), + buttonclose, TRUE, TRUE, 0); + gtk_signal_connect(GTK_OBJECT(buttonclose), + "clicked", + GTK_SIGNAL_FUNC(x_fileselect_saveas_close), + f_current); + gtk_widget_show(buttonclose); + + x_fileselect_update_dirfile_saveas(f_current, + pr_current->page_current-> + page_filename); + + x_fileselect_fill_lists(f_current); + } + + if (!GTK_WIDGET_VISIBLE(f_current->xfwindow)) { + gtk_widget_show(f_current->xfwindow); + gdk_window_raise(f_current->xfwindow->window); + + gtk_grab_add(f_current->xfwindow); + + } else { + /* window should already be mapped, otherwise this + * will core */ + gdk_window_raise(f_current->xfwindow->window); + } +} diff --git a/gattrib/src/x_gtksheet.c b/gattrib/src/x_gtksheet.c new file mode 100644 index 000000000..6ca9e230b --- /dev/null +++ b/gattrib/src/x_gtksheet.c @@ -0,0 +1,854 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns used to handle the spreadsheet widget. + * Much of this was hacked from testgtksheet.c starting in Jan 2004 + * by SDB. + *------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*------------------------------------------------------------------ + * Includes required to run graphical widgets. + *------------------------------------------------------------------*/ +#include <stdio.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include <glib.h> +#ifdef HAS_GTK22 +#include <glib-object.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + + +#ifdef HAS_GTK22 +#include "gtksheet_2_2.h" +#include "gtkitementry_2_2.h" +#else +#include "gtksheet_1_2.h" +#include "gtkitementry_1_2.h" +#endif + + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" +#include "../include/x_menu.h" + + + +/* ==================== Public functions =================== */ + +/*------------------------------------------------------------------ + * x_gtksheet_init -- This creates and initializes the GtkSheet widget, + * which is the spreadsheet widget used for displaying the data. + *------------------------------------------------------------------*/ +void +x_gtksheet_init() +{ + gint i, j; + int num_rows, num_cols; + gchar *folder[]= {"Components", + "Nets", + "Pins"}; + gchar *title[]= {"Components", + "Nets", + "Pins"}; + STRING_LIST *string_list_item; + gchar *text; + +#ifndef HAS_GTK22 + GtkSheetRange *Range; + Range = malloc(sizeof(GtkSheetRange)); +#endif + + /* --- Create three new sheets. were malloc'ed in x_window_init --- */ + + /* ----- Components ----- */ + if ((sheet_head->comp_count > 0) && (sheet_head->comp_attrib_count >0)) { +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_gtksheet_init, creating comp sheet. . . .\n"); +#endif + sheets[0] = (GtkSheet *) gtk_sheet_new((guint) sheet_head->comp_count, (guint) sheet_head->comp_attrib_count, "Components"); + } else { +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_gtksheet_init, no components in comp sheet.\n"); + exit(-1); +#endif + } + + + /* ----- Nets ----- */ + if ((sheet_head->net_count > 0) && (sheet_head->net_attrib_count >0)) { +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_gtksheet_init, creating net sheet. . . .\n"); +#endif + sheets[1] = (GtkSheet *) gtk_sheet_new(sheet_head->net_count, sheet_head->net_attrib_count, "Nets"); +#ifdef HAS_GTK22 + gtk_sheet_set_locked(GTK_SHEET(sheets[1]), TRUE); /* disallow editing of attribs for now */ +#else + Range->row0 = 0; + Range->col0 = 0; + Range->rowi = sheet_head->net_count; + Range->coli = sheet_head->net_attrib_count; + gtk_sheet_range_set_editable(sheets[1], Range, FALSE); +#endif + } else { + sheets[1] = (GtkSheet *) gtk_sheet_new(1, 1, "Nets"); + gtk_sheet_row_button_add_label(sheets[1], 0, "TBD"); + gtk_sheet_row_button_justify(sheets[1], 0, GTK_JUSTIFY_LEFT); + gtk_sheet_column_button_add_label(sheets[1], 0, "TBD"); + gtk_sheet_column_button_justify(sheets[1], 0, GTK_JUSTIFY_LEFT); +#ifdef HAS_GTK22 + gtk_sheet_set_locked(GTK_SHEET(sheets[1]), TRUE); /* disallow editing of attribs for now */ +#else + Range->row0 = 0; + Range->col0 = 0; + Range->rowi = 0; + Range->coli = 0; + gtk_sheet_range_set_editable(sheets[1], Range, FALSE); +#endif +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_gtksheet_init, no entries in net sheet. . . .\n"); +#endif + } + + + /* ----- Pins ----- */ + if ((sheet_head->pin_count > 0) && (sheet_head->pin_attrib_count >0)) { +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_gtksheet_init, creating pin sheet. . . .\n"); +#endif + sheets[2] = (GtkSheet *) gtk_sheet_new(sheet_head->pin_count, sheet_head->pin_attrib_count, "Pins"); +#ifdef HAS_GTK22 + gtk_sheet_set_locked(GTK_SHEET(sheets[2]), TRUE); /* disallow editing of attribs for now */ +#else + Range->row0 = 0; + Range->col0 = 0; + Range->rowi = sheet_head->pin_count; + Range->coli = sheet_head->pin_attrib_count; + gtk_sheet_range_set_editable(sheets[2], Range, FALSE); +#endif + } else { + sheets[2] = (GtkSheet *) gtk_sheet_new(1, 1, "Pins"); +#ifdef HAS_GTK22 + gtk_sheet_set_locked(GTK_SHEET(sheets[2]), TRUE); /* disallow editing of attribs for now */ +#else + Range->row0 = 0; + Range->col0 = 0; + Range->rowi = sheet_head->pin_count; + Range->coli = sheet_head->pin_attrib_count; + gtk_sheet_range_set_editable(sheets[2], Range, FALSE); +#endif +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_gtksheet_init, no entries in pin sheet. . . .\n"); +#endif + } + + +#ifndef HAS_GTK22 + /* Don't need this anymore */ + free(Range); +#endif + + + /* --- Finally stick labels on the notebooks holding the two sheets. --- */ + for(i=0; i<NUM_SHEETS; i++){ + if (sheets[i] != NULL) { /* is this check needed? + * Yes, it prevents us from segfaulting on empty nets sheet. */ + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_gtksheet_init, placing labels on sheet no %d.\n", i); +#endif + + scrolled_windows=(GtkWidget **)realloc(scrolled_windows, (i+1)*sizeof(GtkWidget *)); + scrolled_windows[i]=gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show( GTK_WIDGET(scrolled_windows[i]) ); + +#ifdef DEBUG + printf("In x_gtksheet_init, working on sheet %d. About to call gtk_container_add.\n", i); +#endif + gtk_container_add( GTK_CONTAINER(scrolled_windows[i]), GTK_WIDGET(sheets[i]) ); + gtk_widget_show( GTK_WIDGET(sheets[i]) ); + + /* First remove old notebook page. I should probably do some checking here. */ + if (notebook != NULL) + gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), i); + +#ifdef DEBUG + printf("In x_gtksheet_init, working on sheet %d. About to get new label and call gtk_notebook_append_page.\n", i); +#endif + + /* Then add new, updated notebook page */ + label= gtk_label_new(folder[i]); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(scrolled_windows[i]), + GTK_WIDGET(label) ); + + /* "changed" signal raised when user changes anything in entry cell */ + gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(GTK_SHEET(sheets[i]))), + "changed", (GtkSignalFunc) show_entry, NULL); + +#if 0 + /* "activate" signal raised when user hits <enter> */ + gtk_signal_connect(GTK_OBJECT(sheets[i]), + "activate", (GtkSignalFunc) activate_sheet_cell, + NULL); +#endif + + gtk_widget_show( GTK_WIDGET(notebook) ); + + } + } + + + return; + +} + + + +/*------------------------------------------------------------------ + * x_gtksheet_add_row_labels + *------------------------------------------------------------------*/ +void +x_gtksheet_add_row_labels(GtkSheet *sheet, int count, STRING_LIST *list_head) +{ + STRING_LIST *string_list_item; + gchar *text; + int j; + int width = 0; + int new_width = 0; + int char_width; + + /* Get character width based upon "X", which is a large char. + * font_combo is a global. Where is it set? */ +#ifdef HAS_GTK22 + if ( GTK_WIDGET(sheet)->style->private_font ) + char_width = gdk_char_width (GTK_WIDGET(sheet)->style->private_font, (gchar)'X'); + else + char_width = 12; +#else + char_width = gdk_char_width(GTK_WIDGET(sheet)->style->font, (gchar)'X'); +#endif + + string_list_item = list_head; + for (j = 0; j < count; j++) { + text = (gchar *) u_basic_strdup(string_list_item->data); + new_width = char_width * strlen(text); + if (new_width > width) + width = new_width; + + gtk_sheet_row_button_add_label(sheet, j, text); + gtk_sheet_row_button_justify(sheet, j, GTK_JUSTIFY_LEFT); + free(text); + string_list_item = string_list_item->next; + } + + + gtk_sheet_set_row_titles_width(sheet, width+8); + + return; +} + + +/*------------------------------------------------------------------ + * x_gtksheet_add_col_labels + *------------------------------------------------------------------*/ +void +x_gtksheet_add_col_labels(GtkSheet *sheet, int count, STRING_LIST *list_head) +{ + STRING_LIST *string_list_item; + gchar *text; + guint widest; + int j; + + string_list_item = list_head; + for (j = 0; j < count; j++) { + text = (gchar *) u_basic_strdup(string_list_item->data); + gtk_sheet_column_button_add_label(sheet, j, text); + gtk_sheet_column_button_justify(sheet, j, GTK_JUSTIFY_LEFT); + /* need to resize the column width here . . . */ + free(text); + string_list_item = string_list_item->next; + } + return; +} + + +/*------------------------------------------------------------------ + * x_gtksheet_add_cell_item: First construct the rest of the spreadsheet + * widget, then insert data into sheet's cells, then finally display + * the sheet window. + *------------------------------------------------------------------*/ +void +x_gtksheet_add_cell_item(GtkSheet *sheet, int i, int j, gchar *text) +{ + + /* Should I do some sanity checking here? */ + + gtk_sheet_set_cell(sheet, i, j, GTK_JUSTIFY_LEFT, text); + + /* Need to find a way to ensure that the text in a cell is clipped. + * Otherwise, long attribs overwrite adjacent cells. */ + + /* gtk_sheet_set_cell_attributes(GtkSheet *sheet, gint row, gint col, GtkSheetCellAttr attributes) */ + + + return; +} + + +/* ==================== Private functions =================== */ + +/* The stuff below was copied over from testgtksheet.c */ + +/* ----------------------------------------------------------------- * + * Copied from testgtksheet.c + * ----------------------------------------------------------------- */ +void +format_text (GtkSheet *sheet, gchar *text, gint *justification, gchar *label) +{ + /* I probably don't need this */ +#if 0 + double auxval; + int digspace=0; + int cell_width, char_width; + double val = 0.0; + int format; + double space; + int intspace; + int nonzero=FALSE; + int ipos; + gchar nchar; + + /* SDB says: I had to modifiy these names to make them work. . . . */ + enum {EMPTY_LOCAL, TEXT_LOCAL, NUMERIC_LOCAL}; + + cell_width=sheet->column[sheet->active_cell.col].width; + +#ifdef HAS_GTK22 + char_width = gdk_char_width (GTK_WIDGET(sheet)->style->private_font,(gchar)'X'); +#else + char_width = gdk_char_width(GTK_WIDGET(sheet)->style->font, (gchar)'X'); +#endif + + space= (double)cell_width/(double)char_width; + + intspace=MIN(space, DEFAULT_SPACE); + + format=EMPTY_LOCAL; + if(strlen(text) != 0) + { + for(ipos=0; ipos<strlen(text); ipos++){ + + switch(nchar=text[ipos]){ + case '.': + case ' ': case ',': + case '-': case '+': + case 'd': case 'D': + case 'E': case 'e': + case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + nonzero=TRUE; + break; + case '0': + break; + default: + format=TEXT_LOCAL; + } + if(format != EMPTY_LOCAL) break; + } + val=atof(text); + if(format!=EMPTY_LOCAL || (val==0. && nonzero)) + format = TEXT_LOCAL; + else + format = NUMERIC_LOCAL; + } + + switch(format){ + case TEXT_LOCAL: + case EMPTY_LOCAL: + strcpy(label, text); + return; + case NUMERIC_LOCAL: + val=atof(text); + *justification = GTK_JUSTIFY_LEFT; + } + + auxval= val < 0 ? -val : val; + + while(auxval<1 && auxval != 0.){ + auxval=auxval*10.; + digspace+=1; + } + + if(digspace+DEFAULT_PRECISION+1>intspace || digspace > DEFAULT_PRECISION){ + sprintf (label, "%*.*E", intspace, DEFAULT_PRECISION, val); + } + else + { + intspace=MIN(intspace, strlen(text)-digspace-1); + sprintf (label, "%*.*f", intspace, DEFAULT_PRECISION, val); + if(strlen(label) > space) + sprintf (label, "%*.*E", intspace, DEFAULT_PRECISION, val); + } +#endif +} + + + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +gint change_entry(GtkWidget *widget, + gint row, gint col, gint *new_row, gint *new_col, + gpointer data) +{ + GtkSheet *sheet; + + sheet = GTK_SHEET(widget); + + if(*new_col == 0 && col != 0) + gtk_sheet_change_entry(sheet, gtk_combo_get_type()); + + if(*new_col == 1 && col != 1) + gtk_sheet_change_entry(sheet, GTK_TYPE_ENTRY); + + if(*new_col == 2 && col != 2) + gtk_sheet_change_entry(sheet, GTK_TYPE_SPIN_BUTTON); + + if(*new_col >= 3 && col < 3) + gtk_sheet_change_entry(sheet, GTK_TYPE_ITEM_ENTRY); + + return TRUE; +} + + +/* ----------------------------------------------------------------- * + * Do we need this? Yes -- this sets the value in the cell. + * ----------------------------------------------------------------- */ +void +set_cell(GtkWidget *widget, gchar *insert, gint text_legth, gint position, + gpointer data) +{ + gchar *text; + GtkEntry *sheet_entry; + +#ifdef DEBUG + /* Debug statement */ + printf("Entering set_cell\n"); +#endif + + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry(GTK_SHEET(widget))); + + if( (text = (gchar *) gtk_entry_get_text (GTK_ENTRY(sheet_entry)) ) ) { + gtk_entry_set_text(GTK_ENTRY(entry), text); + } + + GTK_WIDGET_UNSET_FLAGS(entry, GTK_HAS_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_SHEET(widget)->sheet_entry, GTK_HAS_FOCUS); + +#ifdef DEBUG + /* Debug statement */ + printf("Leaving set_cell\n"); +#endif + +} + + +/* ----------------------------------------------------------------- * + * This is invoked each time the sheet is displayed. + * ----------------------------------------------------------------- */ +void +show_sheet_entry(GtkWidget *widget, gpointer data) +{ + gchar *text; + GtkSheet *sheet; + GtkEntry *sheet_entry; + gint cur_page; + +#ifdef DEBUG + /* Debug statement */ + printf("Entering show_sheet_entry\n"); +#endif + + if(!GTK_WIDGET_HAS_FOCUS(widget)) return; + + cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + + sheet=GTK_SHEET(sheets[cur_page]); + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry( GTK_SHEET(sheet) )); + + if((text = (gchar *) gtk_entry_get_text (GTK_ENTRY(entry)))){ + gtk_entry_set_text( GTK_ENTRY(sheet_entry), text); + } + +#ifdef DEBUG + /* Debug statement */ + printf("Leaving show_sheet_entry\n"); +#endif + +} + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +activate_sheet_entry(GtkWidget *widget, gpointer data) +{ + GtkSheet *sheet; + GtkEntry *sheet_entry; + gint cur_page, row, col; + gint justification=GTK_JUSTIFY_LEFT; + +#ifdef DEBUG + /* Debug statement */ + printf("Entering activate_sheet_entry\n"); +#endif + + cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + sheet=GTK_SHEET(sheets[cur_page]); + row=sheet->active_cell.row; col=sheet->active_cell.col; + + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry( GTK_SHEET(sheet) )); + + if(GTK_IS_ITEM_ENTRY(sheet_entry)) + justification = GTK_ITEM_ENTRY(sheet_entry)->justification; + + gtk_sheet_set_cell(sheet, row, col, + justification, + gtk_entry_get_text (sheet_entry)); + + + +#ifdef DEBUG + /* Debug statement */ + printf("Leaving activate_sheet_entry\n"); +#endif + +} + +/* ----------------------------------------------------------------- * + * This fcn displays a text entry box. It is not needed now, but + * may come in handy later. + * ----------------------------------------------------------------- */ +void +show_entry(GtkWidget *widget, gpointer data) +{ + gchar *text; + GtkSheet *sheet; + GtkWidget *sheet_entry; + gint cur_page; + +#ifdef DEBUG + /* Debug statement */ + printf("Entering show_entry\n"); +#endif + + if(!GTK_WIDGET_HAS_FOCUS(widget)) { + +#ifdef DEBUG + /* Debug statement */ + printf("Leaving show_entry -- we don't have focus!!\n"); +#endif + + return; + } + + cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + +#ifdef DEBUG + /* Debug statement */ + printf("In show_entry, handling cur_page = %d!!\n", cur_page); +#endif + + sheet = GTK_SHEET(sheets[cur_page]); + if (sheet != NULL) { + sheet_entry = gtk_sheet_get_entry( GTK_SHEET(sheet) ); + } else { +#ifdef DEBUG + /* Debug statement */ + printf("In show_entry, sheet_entry == NULL!\n"); +#endif + } + + /* Here's another place where we mix entry and sheet_entry */ + if (entry != NULL) { + text = (gchar *) gtk_entry_get_text (GTK_ENTRY(sheet_entry)); + if( text != NULL ) { + gtk_entry_set_text(GTK_ENTRY(entry), text); + } + else { + gtk_entry_set_text(GTK_ENTRY(entry), (const gchar *) ""); + /* gtk_entry_set_text(GTK_ENTRY(entry), NULL); */ + } + } + +#ifdef DEBUG + /* Debug statement */ + printf("Leaving show_entry normally.\n"); +#endif + +} + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +justify_left(GtkWidget *widget) +{ + GtkSheet *current; + gint cur_page; + + cur_page=gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + current=GTK_SHEET(sheets[cur_page]); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(left_button), + GTK_STATE_ACTIVE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(right_button), + GTK_STATE_NORMAL); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(center_button), + GTK_STATE_NORMAL); + + gtk_sheet_range_set_justification(current, ¤t->range, + GTK_JUSTIFY_LEFT); +} + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +justify_center(GtkWidget *widget) +{ + GtkSheet *current; + gint cur_page; + + cur_page=gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + current=GTK_SHEET(sheets[cur_page]); + + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(center_button), + GTK_STATE_ACTIVE); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(right_button), + GTK_STATE_NORMAL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(left_button), + GTK_STATE_NORMAL); + + gtk_sheet_range_set_justification(current, ¤t->range, + GTK_JUSTIFY_CENTER); +} + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +justify_right(GtkWidget *widget) +{ + GtkSheet *current; + gint cur_page; + + cur_page=gtk_notebook_current_page(GTK_NOTEBOOK(notebook)); + current=GTK_SHEET(sheets[cur_page]); + + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(right_button), + GTK_STATE_ACTIVE); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(left_button), + GTK_STATE_NORMAL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(center_button), + GTK_STATE_NORMAL); + + gtk_sheet_range_set_justification(current, ¤t->range, + GTK_JUSTIFY_RIGHT); +} + +/* ----------------------------------------------------------------- * + * Do we need this? In principle, this is the callback for + * when the user hits <enter>. HOwever, I have commented out the + * callback connection statement, and the sheet continues to + * function normally. + * ----------------------------------------------------------------- */ +gint +activate_sheet_cell(GtkWidget *widget, gint row, gint column, gpointer data) +{ + + GtkSheet *sheet; + GtkEntry *sheet_entry; + gchar cell[100]; + gchar *text; + GtkSheetCellAttr attributes; + +#ifdef DEBUG + /* Debug statement */ + printf("Entering activate_sheet_cell\n"); +#endif + + sheet=GTK_SHEET(widget); + sheet_entry = GTK_ENTRY(gtk_sheet_get_entry( GTK_SHEET(sheet) )); + + if(GTK_SHEET(widget)->column[column].name) + sprintf(cell," %s:%d ",GTK_SHEET(widget)->column[column].name, row); + else + sprintf(cell, " ROW: %d COLUMN: %d ", row, column); + + if (location != NULL) { + gtk_label_set(GTK_LABEL(location), cell); + } + + + if (entry != NULL) { + gtk_entry_set_max_length(GTK_ENTRY(entry), + GTK_ENTRY(sheet_entry)->text_max_length); + } + + + if((text = (gchar *) gtk_entry_get_text(GTK_ENTRY(gtk_sheet_get_entry( GTK_SHEET(sheet) ))))) + gtk_entry_set_text(GTK_ENTRY(sheet_entry), text); + else + gtk_entry_set_text(GTK_ENTRY(sheet_entry), (const gchar *) ""); + /* gtk_entry_set_text(GTK_ENTRY(sheet_entry), NULL); */ + + + + gtk_sheet_get_attributes(sheet, sheet->active_cell.row, + sheet->active_cell.col, &attributes); + + + if (entry != NULL) { + gtk_entry_set_editable(GTK_ENTRY(entry), attributes.is_editable); + } + + + /* These probably also spew errors . . . + * switch (attributes.justification){ + * case GTK_JUSTIFY_LEFT: + * justify_left(NULL); + * break; + * case GTK_JUSTIFY_CENTER: + * justify_center(NULL); + * break; + * case GTK_JUSTIFY_RIGHT: + * justify_right(NULL); + * break; + * default: + * justify_left(NULL); + * break; + * + * } + */ + +#ifdef DEBUG + /* Debug statement */ + printf("Leaving activate_sheet_cell\n"); +#endif + + return TRUE; + +} + + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +do_hide_row_titles(GtkWidget *widget) +{ + GtkSheet *current; + gint cur_page; + + cur_page=gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + current=GTK_SHEET(sheets[cur_page]); + + gtk_sheet_hide_row_titles(current); +} + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +do_hide_column_titles(GtkWidget *widget) +{ + GtkSheet *current; + gint cur_page; + + cur_page=gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + current=GTK_SHEET(sheets[cur_page]); + + gtk_sheet_hide_column_titles(current); + +} + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +do_show_row_titles(GtkWidget *widget) +{ + GtkSheet *current; + gint cur_page; + + cur_page=gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + current=GTK_SHEET(sheets[cur_page]); + + gtk_sheet_show_row_titles(current); +} + +/* ----------------------------------------------------------------- * + * Do we need this? + * ----------------------------------------------------------------- */ +void +do_show_column_titles(GtkWidget *widget) +{ + GtkSheet *current; + gint cur_page; + + cur_page=gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + current=GTK_SHEET(sheets[cur_page]); + + gtk_sheet_show_column_titles(current); + +} + + diff --git a/gattrib/src/x_window.c b/gattrib/src/x_window.c new file mode 100644 index 000000000..c7264c9fc --- /dev/null +++ b/gattrib/src/x_window.c @@ -0,0 +1,355 @@ +/* gEDA - GPL Electronic Design Automation + * gattrib -- gEDA component and net attribute manipulation using spreadsheet. + * Copyright (C) 2003 Stuart D. Brorson. + * + * 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 USA + */ + +/*------------------------------------------------------------------ + * This file holds fcns used to handle the toplevel window and + * various widgets held by that window. Widges used to handle + * (GtkSheet *sheet) itself are held in a different file. + *------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +/*------------------------------------------------------------------ + * Includes required to run graphical widgets. + *------------------------------------------------------------------*/ +#include <stdio.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include <glib.h> +#ifdef HAS_GTK22 +#include <glib-object.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + + +#ifdef HAS_GTK22 +#include "gtksheet_2_2.h" +#include "gtkitementry_2_2.h" +#else +#include "gtksheet_1_2.h" +#include "gtkitementry_1_2.h" +#endif + +/*------------------------------------------------------------------ + * Gattrib specific includes + *------------------------------------------------------------------*/ +#include <libgeda/libgeda.h> /* geda library fcns */ +#include "../include/struct.h" /* typdef and struct declarations */ +#include "../include/prototype.h" /* function prototypes */ +#include "../include/globals.h" +#include "../include/x_menu.h" + + +/* ====================== Public functions ======================== */ + +/*------------------------------------------------------------------ + * x_window_init -- this fcn initialies the toplevel gtksheet stuff. It + * basically just initializes the following widgets: + * GTK_WINDOW *window + * GTK_CONTAINER *main_vbox + * GTK_MENU + * Note that it doesn't display the spreadsheet itself. This is done + * in x_sheet_build_sheet. + * I suppose I ctould postpone all initialization until x_sheet_build_sheet, but I + * figured that I could at least do some initialization here. + * In particular, the stuff to put up the menus is long & it is worthwhile + * to separate it from other code. Maybe I'll refactor this later. + *------------------------------------------------------------------*/ +void +x_window_init() +{ + /* note that the graphical widgets themselves (window, vbox, menu_bar) + * are globals defined in ../include/globals.h On pr_current I + * attach pointers to some of them here for future reference. */ + + /* GdkFont *font; */ + gint i; + + /* ----- First initialize stuff in the top-level window ----- */ + +#ifdef DEBUG + printf("In x_window_init, about to call gtk_window_new.\n"); +#endif + /* window is a global declared in globals.h. */ + window = (GtkWidget *) gtk_window_new(GTK_WINDOW_TOPLEVEL); + + /* I attach a pointer to window to the TOPLEVEL structure */ + pr_current->main_window = window; + +#ifdef DEBUG + printf("In x_window_init, about to call gtk_window_set_title.\n"); +#endif + gtk_window_set_title( GTK_WINDOW(window), "gattrib -- gEDA attribute editor"); + /* gtk_widget_set_usize(GTK_WIDGET(window), 900, 600); */ + gtk_window_set_default_size(GTK_WINDOW(window), 750, 600); + + +#ifdef DEBUG + printf("In x_window_init, about to connect delete and destroy signals to window.\n"); +#endif + gtk_signal_connect (GTK_OBJECT (window), "delete_event", + GTK_SIGNAL_FUNC (gattrib_quit), NULL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gattrib_quit), NULL); + + + /* ----- Now create vbox. This is a container which organizes child ----- */ + /* ----- widgets into a vertical column. ----- */ + /* main_vbox is a global defined in globals.h */ +#ifdef DEBUG + printf("In x_window_init, about to set up vobx.\n"); +#endif + main_vbox=gtk_vbox_new(FALSE,1); + gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 1); + gtk_container_add(GTK_CONTAINER(window), main_vbox); + gtk_widget_show(main_vbox); + + + /* ----- Now create menu bar ----- */ + /* menu_bar is a global defined in globals.h */ +#ifdef DEBUG + printf("In x_window_init, about to create menu bar.\n"); +#endif + x_window_create_menu(&menu_bar); + pr_current->menubar = menu_bar; /* attach pointer to menu_bar to (TOPLEVEL pr_current) */ + gtk_box_pack_start (GTK_BOX (main_vbox), menu_bar, FALSE, TRUE, 0); + gtk_widget_show( GTK_WIDGET(menu_bar) ); + + /* ----- Now init notebook widget ----- */ +#ifdef DEBUG + printf("In x_window_init, about to create notbook.\n"); +#endif + notebook=gtk_notebook_new(); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM); + gtk_box_pack_start(GTK_BOX(main_vbox), notebook, TRUE, TRUE, 0); + gtk_widget_show( notebook ); + + + /* ----- Now malloc -- but don't fill out -- space for sheets ----- */ + /* This basically sets up the overhead for the sheets, as I understand + * it. The memory for the actual sheet cells is allocated later, + * when gtk_sheet_new is invoked, I think. */ +#ifdef DEBUG + printf("In x_window_init, about to malloc space for sheets.\n"); +#endif + for(i=0; i<NUM_SHEETS; i++){ + sheets=(GtkSheet **)realloc(sheets, (i+1)*sizeof(GtkWidget *)); + } + + /* ----- Finally show top level window to make everything appear ----- */ +#ifdef DEBUG + /* printf("In x_window_init, about to show window widget.\n"); */ +#endif + + /* + * gtk_widget_show( GTK_WIDGET(window) ); + */ + +} + + +/*------------------------------------------------------------------ + * x_window_create_menu: This creates the menu widget. This fcn + * cloned from GTK+ tutorial. + *------------------------------------------------------------------*/ +void +x_window_create_menu(GtkWidget **menubar) +{ + GtkItemFactory *item_factory; + GtkAccelGroup *accel_group; + gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); + + accel_group = gtk_accel_group_new (); + + /* This function initializes the item factory. + Param 1: The type of menu - can be GTK_TYPE_MENU_BAR, GTK_TYPE_MENU, + or GTK_TYPE_OPTION_MENU. + Param 2: The path of the menu. + Param 3: A pointer to a gtk_accel_group. The item factory sets up + the accelerator table while generating menus. + */ + + item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", + accel_group); + + /* This function generates the menu items. Pass the item factory, + the number of items in the array, the array itself, and any + callback data for the the menu items. */ + /* SDB notes: callback data is pr_current, which should hopefully have pointers + * to any data required in the callback. */ + gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, pr_current); + + /* Attach the new accelerator group to the window. */ + /* SDB says: Here's where it comes in handy to have attached a pointer to + * the main window to the TOPLEVEL structure. */ + gtk_window_add_accel_group (GTK_WINDOW(pr_current->main_window), accel_group); + + if (menubar) + /* Finally, return the actual menu bar created by the item factory. */ + *menubar = gtk_item_factory_get_widget (item_factory, "<main>"); + + return; +} + + +/*------------------------------------------------------------------ + * x_window_add_items -- this fcn updates the top level window + * after a new page is read in. It does the following: + * + * 2. Create a new gtksheet having the current dimensions. + * 3. Call x_gktsheet_add_row_labels(comp_count, master_*_list_head) + * 4. Call x_gktsheet_add_col_labels(comp_attrib_count, master_*_attrib_list_head) + * 5. Call x_gktsheet_add_row_labels(net_count, master_*_list_head) + * 6. Call x_gktsheet_add_col_labels(net_attrib_count, master_*_attrib_list_head) + * 7. loop on i, j -- call x_gtksheet_add_entry(i, j, attrib_value) + * 8. Call gtk_widget_show(window) to show new window. + *------------------------------------------------------------------*/ +void +x_window_add_items() +{ + int i, j; + int num_rows, num_cols; + char *text; + +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("Entered x_window_add_items . . . . . ..\n"); +#endif + + + /* Do these sanity check to prevent later segfaults */ + if (sheet_head->comp_count == 0) { + fprintf(stderr, "\n\nNo components found in entire design! \nDo you have refdeses on your components? \nExiting. . . .\n"); + exit(0); + } + + if (sheet_head->comp_attrib_count == 0) { + fprintf(stderr, "\n\nNo configurable component attributes found in entire design! \nPlease attach at least some attributes before running gattrib. \nExiting. . . .\n"); + exit(0); + } + + /* Since we have passed the sanity checking, initialize the gtksheet. */ +#ifdef DEBUG + fflush(stderr); + fflush(stdout); + printf("In x_window_add_items, about to call x_gtksheet_init.\n"); +#endif + x_gtksheet_init(); /* this creates a new gtksheet having dimensions specified + * in sheet_head->comp_count, etc. . . */ + + +#ifdef DEBUG + printf("In x_window_add_items, now load up the row and column labels.\n"); +#endif + x_gtksheet_add_row_labels(GTK_SHEET(sheets[0]), sheet_head->comp_count, sheet_head->master_comp_list_head); + x_gtksheet_add_col_labels(GTK_SHEET(sheets[0]), sheet_head->comp_attrib_count, sheet_head->master_comp_attrib_list_head); + +#if 0 + if (sheet_head->pin_count > 0 ) { + x_gtksheet_add_row_labels(GTK_SHEET(sheets[1]), sheet_head->net_count, sheet_head->master_net_list_head); + x_gtksheet_add_col_labels(GTK_SHEET(sheets[1]), sheet_head->net_attrib_count, sheet_head->master_net_attrib_list_head); + } else { + x_gtksheet_add_row_labels(GTK_SHEET(sheets[1]), 1, 1); + x_gtksheet_add_col_labels(GTK_SHEET(sheets[1]), 1, 1); + } +#endif + + if (sheet_head->pin_count > 0 ) { + x_gtksheet_add_row_labels(GTK_SHEET(sheets[2]), sheet_head->pin_count, sheet_head->master_pin_list_head); + x_gtksheet_add_col_labels(GTK_SHEET(sheets[2]), sheet_head->pin_attrib_count, sheet_head->master_pin_attrib_list_head); + } + + +#ifdef DEBUG + printf("In x_window_add_items, now put comp attrib values in the comp sheet.\n"); +#endif + /* ------ Comp sheet: put values in the individual cells ------- */ + num_rows = sheet_head->comp_count; + num_cols = sheet_head->comp_attrib_count; + for (i = 0; i < num_rows; i++) { + for (j = 0; j < num_cols; j++) { + if ( (sheet_head->component_table)[i][j].attrib_value ) { /* NULL = no entry */ + text = (gchar *) u_basic_strdup( (sheet_head->component_table)[i][j].attrib_value ); + x_gtksheet_add_cell_item( GTK_SHEET(sheets[0]), i, j, (gchar *) text ); + free(text); + } + } + } + gtk_widget_show( GTK_WIDGET(sheets[0]) ); + + +#ifdef DEBUG + printf("In x_window_add_items, now put net attrib values in the net sheet.\n"); +#endif + /* ------ Net sheet: put values in the individual cells ------- */ + num_rows = sheet_head->net_count; + num_cols = sheet_head->net_attrib_count; + for (i = 0; i < num_rows; i++) { + for (j = 0; j < num_cols; j++) { + if ( (sheet_head->net_table)[i][j].attrib_value ) { /* NULL = no entry */ + text = (gchar *) u_basic_strdup( (sheet_head->net_table)[i][j].attrib_value ); + x_gtksheet_add_cell_item( GTK_SHEET(sheets[1]), i, j, (gchar *) text ); + free(text); + } + } + } + if (sheet_head->net_count > 0) { + gtk_widget_show( GTK_WIDGET(sheets[1]) ); + } + + + +#ifdef DEBUG + printf("In x_window_add_items, now put pin attrib values in the pin sheet.\n"); +#endif + /* ------ Pin sheet: put pin attribs in the individual cells ------- */ + num_rows = sheet_head->pin_count; + num_cols = sheet_head->pin_attrib_count; + for (i = 0; i < num_rows; i++) { + for (j = 0; j < num_cols; j++) { + if ( (sheet_head->pin_table)[i][j].attrib_value ) { /* NULL = no entry */ + text = (gchar *) u_basic_strdup( (sheet_head->pin_table)[i][j].attrib_value ); + x_gtksheet_add_cell_item( GTK_SHEET(sheets[2]), i, j, (gchar *) text ); + free(text); + } + } + } + if (sheet_head->pin_count > 0) { + gtk_widget_show( GTK_WIDGET(sheets[2]) ); + } + + gtk_widget_show( GTK_WIDGET(notebook) ); + gtk_widget_show( GTK_WIDGET(window) ); + + return; + +} +/* ====================== Private functions ======================== */ + + -- 2.11.4.GIT