From: John Darrington Date: Mon, 15 Dec 2008 06:06:07 +0000 (+0900) Subject: Rename lib/gtksheet to lib/gtk-contrib X-Git-Tag: v0.7.1~27 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp-builds.git;a=commitdiff_plain;h=c9d55afdda746d7e8b39e5a117c1644debbb0c75 Rename lib/gtksheet to lib/gtk-contrib --- diff --git a/lib/automake.mk b/lib/automake.mk index 9197da97..7cd9c9f4 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -4,7 +4,7 @@ include $(top_srcdir)/lib/linreg/automake.mk include $(top_srcdir)/lib/misc/automake.mk if WITHGUI -include $(top_srcdir)/lib/gtksheet/automake.mk +include $(top_srcdir)/lib/gtk-contrib/automake.mk endif EXTRA_DIST += lib/OChangeLog diff --git a/lib/gtk-contrib/COPYING.LESSER b/lib/gtk-contrib/COPYING.LESSER new file mode 100644 index 00000000..8add30ad --- /dev/null +++ b/lib/gtk-contrib/COPYING.LESSER @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/lib/gtk-contrib/OChangeLog b/lib/gtk-contrib/OChangeLog new file mode 100644 index 00000000..b5ba1c82 --- /dev/null +++ b/lib/gtk-contrib/OChangeLog @@ -0,0 +1,149 @@ +2008-05-08 Ben Pfaff + + Patch #6506. Reviewed by John Darrington. + + * gtksheet.c (gtk_sheet_unrealize): Don't call gtk_widget_unparent + on sheet->button if it's null. + +2008-05-06 Ben Pfaff + + * gtksheet.c (gtk_sheet_dispose): Set the sheet's entry_container + and button members to NULL after unref'ing them, so that a later + call to gtk_sheet_for_all will not try to dereference a dangling + pointer. + +2008-03-06 John Darrington + + * gsheet-row-iface.c gsheet-row-iface.h: Delete unused, unneccesary + gpointer variable from the interface. + + * gtksheet.c: Update to match new gsheet-row-iface + +2008-02-27 John Darrington + * gtksheet.c gtksheet.h: Corrected some leaks and other problems + related to de-allocating the sheet. + +2008-02-27 John Darrington + * gtksheet.c: (gtk_sheet_expose) Don't queue a redraw on the entry + widget. Fixes bug #21073 + +2008-02-20 John Darrington + + * gtksheet.c gtksheet.h: Removed some unused signals. + Made the models properties of the widget. + +2008-02-08 John Darrington + + * gtksheet.c: Removed the sheet_locked feature, which we never + used, and interfered with the editability of the entry widget. + + * gtksheet.c: Add one to the row to which we scroll. Seems like + the best way to cope with granularity problems. + +21 Septempber 2007 John Darrington + + * gtksheet.c (range_update_callback): Scroll to cell 0,0 if the + current position is outside the model's range. + +24 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed the `clip' feature, which IMO + is a croc, and we're unlikely to use. In its place, added a primary + selection which supports text and html targets. + +16 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed some legacy functions called from + gtk_sheet_finalize which caused unnecessary delays when shutting down. + +12 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed view member and replaced with + function call. Removed hadjustment_changed and vadjustment_changed + functions which did nothing. Added some whitespace arount != + operators. + +09 July 2007 John Darrington + + * gtksheet.c gtksheet.h (gtk_sheet_get_active_cell): Allowed row, + column to be NULL. + +07 July 2007 John Darrington + + * gsheet-column-iface.c gsheet-column-iface.h gsheet-row-iface.c + gsheet-row-iface.h gtksheet.c gtksheet.h: Added a "subtitle" + feature on row/column titles, which shows tooltip-like popups. + +03 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed the autoscroll-on-select feature + that was causing us grief. + +28 June 2007 John Darrington + + * gtksheet.c: Removed some features that we dont use, to get better + speed. + +Sat Feb 17 17:36:56 2007 Ben Pfaff + + * gsheet-column-iface.c gsheet-hetero-column.c gsheet-row-iface.c + gsheet-uniform-column.c gsheet-uniform-row.c gsheetmodel.c + gtkextra-marshal.c gtkextra.c gtkiconlist.c gtkitementry.c + gtksheet.c: Add "#include ". + +Mon Jun 19 18:03:21 WST 2006 John Darrington + + * gsheet-column-iface.c gsheet-column-iface.h + gsheet-hetero-column.c gsheet-row-iface.c gsheet-row-iface.h + gsheet-uniform-column.c gsheet-uniform-row.c gtksheet.c + gtksheet.h: Fixed some warnings. Corrected errors updating + row/column titles + +Di Mai 30 19:51:19 WST 2006 John Darrington + + * gtksheet.c gtksheet.h: constness. Removed dependence on glib2.10 + +Sat May 27 16:29:36 WST 2006 John Darrington + + * gtksheet.c: Removed call to gtk_entry_set_text, which caused warnings + and was unnecessary. + +Thu May 25 17:58:51 WST 2006 John Darrington + + * gsheet-column-iface.c gsheet-column-iface.h gsheet-hetero-column.c + gsheet-row-iface.c gsheet-row-iface.h gsheet-uniform-row.c + gtksheet-extra.h gtksheet.c: Plugged memory leaks. Rationalised the way + that GtkSheetButtons are created. + +Sat May 20 21:02:03 WST 2006 John Darrington + + * gsheetmodel.c gsheetmodel.h: Added columns-inserted and columns-deleted + signals. Added g_sheet_get_{row,column}_count functions. + + * gtksheet.c gtksheet.h: Allowed -1 to be passed to + gtk_sheet_set_active_cell to indicate no active cell. + +Mon May 15 16:10:49 WST 2006 John Darrington + + * gtksheet.c: Removed code which rendered the title buttons a second + time. Cut and Paste error ? + +Sat May 13 07:58:32 WST 2006 John Darrington + + * gsheetmodel.c gsheetmodel.h gtksheet.c gtksheeet.h: Added + free_strings flag to tell the sheet whether to free the string + data passed from the model. + +Thu May 11 22:20:04 WST 2006 John Darrington + + * gtksheet.c, gtksheet.h: Fixed broken deallocation of sheet->pixmap. + +Thu May 4 17:55:48 WST 2006 John Darrington + + * gtksheet.c: Added callback on inserted rows. + +Sat Jan 28 08:48:08 2006 UTC John Darrington + + * Separated the data out of the GtkSheet. The gtksheet should now be + regarded as a way of looking into the data. The data is represented by a + GSheetModel and the rows and columns by GSheetRow and GSheetColumn. diff --git a/lib/gtk-contrib/README b/lib/gtk-contrib/README new file mode 100644 index 00000000..e6e23d94 --- /dev/null +++ b/lib/gtk-contrib/README @@ -0,0 +1,10 @@ +This is not part of the GNU PSPP program, but is used with GNU PSPP. + +This directory contains a version of the GtkXPaned widget. It includes +minor modifications. GtkXPaned is licensed under the GNU Lesser +General Public License. See COPYING.LESSER. + + +This directory also contains the PsppireSheet widget which is a very +heavily modified version of GtkSheet widget. This modified version if +licensed under the GNU General Public License version 3 or later. diff --git a/lib/gtk-contrib/automake.mk b/lib/gtk-contrib/automake.mk new file mode 100644 index 00000000..c78e61f1 --- /dev/null +++ b/lib/gtk-contrib/automake.mk @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in -*- makefile -*- + +noinst_LIBRARIES += lib/gtk-contrib/libgtksheet.a + +lib_gtk_contrib_libgtksheet_a_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 + +lib_gtk_contrib_libgtksheet_a_SOURCES = \ + lib/gtk-contrib/gtkextra-sheet.h \ + lib/gtk-contrib/psppire-sheet.c \ + lib/gtk-contrib/psppire-sheet.h \ + lib/gtk-contrib/gtkxpaned.c \ + lib/gtk-contrib/gtkxpaned.h + +EXTRA_DIST += lib/gtk-contrib/OChangeLog \ + lib/gtk-contrib/README + diff --git a/lib/gtk-contrib/gtkextra-sheet.h b/lib/gtk-contrib/gtkextra-sheet.h new file mode 100644 index 00000000..16d6b5b4 --- /dev/null +++ b/lib/gtk-contrib/gtkextra-sheet.h @@ -0,0 +1,61 @@ +/* This version of GtkSheet has been heavily modified, for the specific + * requirements of PSPPIRE. + * + * 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 Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef PSPPIRE_EXTRA_SHEET_H__ +#define PSPPIRE_EXTRA_SHEET_H__ + + +struct _PsppireSheet ; + +typedef struct _PsppireSheet PsppireSheet; + + +struct _PsppireSheetButton +{ + GtkStateType state; + gchar *label; + + gboolean label_visible; + + GtkJustification justification; +}; + +struct _PsppireSheetCell +{ + gint row; + gint col; +}; + +typedef struct _PsppireSheetButton PsppireSheetButton; +typedef struct _PsppireSheetCell PsppireSheetCell; + +PsppireSheetButton * psppire_sheet_button_new (void); + +void psppire_sheet_button_free (PsppireSheetButton *button); + + +#endif /* PSPPIRE_EXTRA_SHEET_H__ */ + + diff --git a/lib/gtk-contrib/gtkxpaned.c b/lib/gtk-contrib/gtkxpaned.c new file mode 100644 index 00000000..5db3ccea --- /dev/null +++ b/lib/gtk-contrib/gtkxpaned.c @@ -0,0 +1,3260 @@ +/******************************************************************************* +**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +** 10 20 30 40 50 60 70 80 +** +** library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+ +** Copyright (C) 2005-2006 Mirco "MacSlow" Müller +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +** +** GtkXPaned is based on GtkPaned which was done by... +** +** "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald" +** +** and later modified by... +** +** "the GTK+ Team and others 1997-2000" +** +*******************************************************************************/ + +#include "gtkxpaned.h" +#include +#include +#include +#include +#include +#include + +enum WidgetProperties +{ + PROP_0, + PROP_X_POSITION, + PROP_Y_POSITION, + PROP_POSITION_SET, + PROP_MIN_X_POSITION, + PROP_MIN_Y_POSITION, + PROP_MAX_X_POSITION, + PROP_MAX_Y_POSITION +}; + +enum ChildProperties +{ + CHILD_PROP_0, + CHILD_PROP_RESIZE, + CHILD_PROP_SHRINK +}; + +enum WidgetSignals +{ + CYCLE_CHILD_FOCUS, + TOGGLE_HANDLE_FOCUS, + MOVE_HANDLE, + CYCLE_HANDLE_FOCUS, + ACCEPT_POSITION, + CANCEL_POSITION, + LAST_SIGNAL +}; + +static void gtk_xpaned_class_init (GtkXPanedClass* klass); + +static void gtk_xpaned_init (GtkXPaned* xpaned); + +static void gtk_xpaned_size_request (GtkWidget* widget, + GtkRequisition* requisition); + +static void gtk_xpaned_size_allocate (GtkWidget* widget, + GtkAllocation* allocation); + +static void gtk_xpaned_set_property (GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_get_property (GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_set_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + const GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_get_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_finalize (GObject* object); + +static void gtk_xpaned_realize (GtkWidget* widget); + +static void gtk_xpaned_unrealize (GtkWidget* widget); + +static void gtk_xpaned_map (GtkWidget* widget); + +static void gtk_xpaned_unmap (GtkWidget* widget); + +static gboolean gtk_xpaned_expose (GtkWidget* widget, GdkEventExpose* event); + +static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event); + +static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event); + +static gboolean gtk_xpaned_button_press (GtkWidget* widget, + GdkEventButton* event); + +static gboolean gtk_xpaned_button_release (GtkWidget* widget, + GdkEventButton* event); + +static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event); + +static gboolean gtk_xpaned_focus (GtkWidget* widget, + GtkDirectionType direction); + +static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget); + +static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget); + +static void gtk_xpaned_forall (GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +static void gtk_xpaned_set_focus_child (GtkContainer* container, + GtkWidget* child); + +static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget); + +static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned, + GtkXPaned* first_xpaned); + +static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned, + gboolean reverse); + +static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned, + gboolean reverse); + +static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned, + GtkScrollType scroll); + +static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned); + +static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned); + +static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned); + +static GType gtk_xpaned_child_type (GtkContainer* container); + +static GtkContainerClass* parent_class = NULL; + +struct _GtkXPanedPrivate +{ + GtkWidget *saved_focus; + GtkXPaned *first_xpaned; +}; + +GType gtk_xpaned_get_type (void) +{ + static GType xpaned_type = 0; + + if (!xpaned_type) + { + static const GTypeInfo xpaned_info = + { + sizeof (GtkXPanedClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_xpaned_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkXPaned), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_xpaned_init + }; + + xpaned_type = g_type_register_static (GTK_TYPE_CONTAINER, + "GtkXPaned", + &xpaned_info, + 0); + } + + return xpaned_type; +} + +GtkWidget* gtk_xpaned_new (void) +{ + GtkXPaned* xpaned; + + xpaned = g_object_new (GTK_TYPE_XPANED, NULL); + + return GTK_WIDGET (xpaned); +} + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void add_tab_bindings (GtkBindingSet* binding_set, + GdkModifierType modifiers) +{ + gtk_binding_entry_add_signal (binding_set, + GDK_Tab, + modifiers, + "toggle_handle_focus", + 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KP_Tab, + modifiers, + "toggle_handle_focus", + 0); +} + +static void add_move_binding (GtkBindingSet* binding_set, + guint keyval, + GdkModifierType mask, + GtkScrollType scroll) +{ + gtk_binding_entry_add_signal (binding_set, + keyval, + mask, + "move_handle", + 1, + GTK_TYPE_SCROLL_TYPE, + scroll); +} + +static void gtk_xpaned_class_init (GtkXPanedClass* class) +{ + GObjectClass* object_class; + GtkWidgetClass* widget_class; + GtkContainerClass* container_class; + GtkXPanedClass* xpaned_class; + GtkBindingSet* binding_set; + + object_class = (GObjectClass *) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; + xpaned_class = (GtkXPanedClass *) class; + + parent_class = g_type_class_peek_parent (class); + + object_class->set_property = gtk_xpaned_set_property; + object_class->get_property = gtk_xpaned_get_property; + object_class->finalize = gtk_xpaned_finalize; + + widget_class->realize = gtk_xpaned_realize; + widget_class->unrealize = gtk_xpaned_unrealize; + widget_class->map = gtk_xpaned_map; + widget_class->unmap = gtk_xpaned_unmap; + widget_class->expose_event = gtk_xpaned_expose; + widget_class->focus = gtk_xpaned_focus; + widget_class->enter_notify_event = gtk_xpaned_enter; + widget_class->leave_notify_event = gtk_xpaned_leave; + widget_class->button_press_event = gtk_xpaned_button_press; + widget_class->button_release_event = gtk_xpaned_button_release; + widget_class->motion_notify_event = gtk_xpaned_motion; + widget_class->size_request = gtk_xpaned_size_request; + widget_class->size_allocate = gtk_xpaned_size_allocate; + + container_class->add = gtk_xpaned_add; + container_class->remove = gtk_xpaned_remove; + container_class->forall = gtk_xpaned_forall; + container_class->child_type = gtk_xpaned_child_type; + container_class->set_focus_child = gtk_xpaned_set_focus_child; + container_class->set_child_property = gtk_xpaned_set_child_property; + container_class->get_child_property = gtk_xpaned_get_child_property; + + xpaned_class->cycle_child_focus = gtk_xpaned_cycle_child_focus; + xpaned_class->toggle_handle_focus = gtk_xpaned_toggle_handle_focus; + xpaned_class->move_handle = gtk_xpaned_move_handle; + xpaned_class->cycle_handle_focus = gtk_xpaned_cycle_handle_focus; + xpaned_class->accept_position = gtk_xpaned_accept_position; + xpaned_class->cancel_position = gtk_xpaned_cancel_position; + + g_object_class_install_property (object_class, + PROP_X_POSITION, + g_param_spec_int ("x-position", + ("x-Position"), + ("x-Position of paned separator in pixels (0 means all the way to the left)"), + 0, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_Y_POSITION, + g_param_spec_int ("y-position", + "y-Position", + "y-Position of paned separator in pixels (0 means all the way to the top)", + 0, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_POSITION_SET, + g_param_spec_boolean ("position-set", + "Position Set", + "TRUE if the Position property should be used", + FALSE, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("handle-size", + "Handle Size", + "Width of handle", + 0, + G_MAXINT, + 3, + G_PARAM_READABLE)); + /** + * GtkXPaned:min-x-position: + * + * The smallest possible value for the x-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MIN_X_POSITION, + g_param_spec_int ("min-x-position", + "Minimal x-Position", + "Smallest possible value for the \"x-position\" property", + 0, + G_MAXINT, + 0, + G_PARAM_READABLE)); + + /** + * GtkXPaned:min-y-position: + * + * The smallest possible value for the y-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MIN_Y_POSITION, + g_param_spec_int ("min-y-position", + "Minimal y-Position", + "Smallest possible value for the \"y-position\" property", + 0, + G_MAXINT, + 0, + G_PARAM_READABLE)); + + /** + * GtkPaned:max-x-position: + * + * The largest possible value for the x-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MAX_X_POSITION, + g_param_spec_int ("max-x-position", + "Maximal x-Position", + "Largest possible value for the \"x-position\" property", + 0, + G_MAXINT, + G_MAXINT, + G_PARAM_READABLE)); + + /** + * GtkPaned:max-y-position: + * + * The largest possible value for the y-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MAX_Y_POSITION, + g_param_spec_int ("max-y-position", + "Maximal y-Position", + "Largest possible value for the \"y-position\" property", + 0, + G_MAXINT, + G_MAXINT, + G_PARAM_READABLE)); + + /** + * GtkPaned:resize: + * + * The "resize" child property determines whether the child expands and + * shrinks along with the paned widget. + * + * Since: 2.4 + */ + gtk_container_class_install_child_property (container_class, + CHILD_PROP_RESIZE, + g_param_spec_boolean ("resize", + "Resize", + "If TRUE, the child expands and shrinks along with the paned widget", + TRUE, + G_PARAM_READWRITE)); + + /** + * GtkPaned:shrink: + * + * The "shrink" child property determines whether the child can be made + * smaller than its requisition. + * + * Since: 2.4 + */ + gtk_container_class_install_child_property (container_class, + CHILD_PROP_SHRINK, + g_param_spec_boolean ("shrink", + "Shrink", + "If TRUE, the child can be made smaller than its requisition", + TRUE, + G_PARAM_READWRITE)); + + signals [CYCLE_CHILD_FOCUS] = g_signal_new ("cycle-child-focus", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, cycle_child_focus), + NULL, NULL, + psppire_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, + G_TYPE_BOOLEAN); + + signals [TOGGLE_HANDLE_FOCUS] = g_signal_new ("toggle-handle-focus", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, toggle_handle_focus), + NULL, NULL, + psppire_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + signals[MOVE_HANDLE] = g_signal_new ("move-handle", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, move_handle), + NULL, NULL, + psppire_marshal_BOOLEAN__ENUM, + G_TYPE_BOOLEAN, 1, + GTK_TYPE_SCROLL_TYPE); + + signals [CYCLE_HANDLE_FOCUS] = g_signal_new ("cycle-handle-focus", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, cycle_handle_focus), + NULL, NULL, + psppire_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, + G_TYPE_BOOLEAN); + + signals [ACCEPT_POSITION] = g_signal_new ("accept-position", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, accept_position), + NULL, NULL, + psppire_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + signals [CANCEL_POSITION] = g_signal_new ("cancel-position", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, cancel_position), + NULL, NULL, + psppire_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + binding_set = gtk_binding_set_by_class (class); + + /* F6 and friends */ + gtk_binding_entry_add_signal (binding_set, + GDK_F6, 0, + "cycle-child-focus", 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, + GDK_F6, GDK_SHIFT_MASK, + "cycle-child-focus", 1, + G_TYPE_BOOLEAN, TRUE); + + /* F8 and friends */ + gtk_binding_entry_add_signal (binding_set, + GDK_F8, 0, + "cycle-handle-focus", 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, + GDK_F8, GDK_SHIFT_MASK, + "cycle-handle-focus", 1, + G_TYPE_BOOLEAN, TRUE); + + add_tab_bindings (binding_set, 0); + add_tab_bindings (binding_set, GDK_CONTROL_MASK); + add_tab_bindings (binding_set, GDK_SHIFT_MASK); + add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK); + + /* accept and cancel positions */ + gtk_binding_entry_add_signal (binding_set, + GDK_Escape, 0, + "cancel-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_Return, 0, + "accept-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KP_Enter, 0, + "accept-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_space, 0, + "accept-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KP_Space, 0, + "accept-position", 0); + + /* move handle */ + add_move_binding (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_LEFT); + add_move_binding (binding_set, GDK_KP_Left, 0, GTK_SCROLL_STEP_LEFT); + add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); + add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); + + add_move_binding (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_RIGHT); + add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); + add_move_binding (binding_set, GDK_KP_Right, 0, GTK_SCROLL_STEP_RIGHT); + add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); + + add_move_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP); + add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP); + add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP); + + add_move_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN); + add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); + add_move_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN); + add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); + add_move_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); + add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); + + add_move_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START); + add_move_binding (binding_set, GDK_KP_Home, 0, GTK_SCROLL_START); + add_move_binding (binding_set, GDK_End, 0, GTK_SCROLL_END); + add_move_binding (binding_set, GDK_KP_End, 0, GTK_SCROLL_END); +} + +static GType gtk_xpaned_child_type (GtkContainer* container) +{ + if (!GTK_XPANED (container)->top_left_child || + !GTK_XPANED (container)->top_right_child || + !GTK_XPANED (container)->bottom_left_child || + !GTK_XPANED (container)->bottom_right_child) + return GTK_TYPE_WIDGET; + else + return G_TYPE_NONE; +} + +static void gtk_xpaned_init (GtkXPaned* xpaned) +{ + GTK_WIDGET_SET_FLAGS (xpaned, GTK_NO_WINDOW | GTK_CAN_FOCUS); + + xpaned->top_left_child = NULL; + xpaned->top_right_child = NULL; + xpaned->bottom_left_child = NULL; + xpaned->bottom_right_child = NULL; + xpaned->handle_east = NULL; + xpaned->handle_west = NULL; + xpaned->handle_north = NULL; + xpaned->handle_south = NULL; + xpaned->handle_middle = NULL; + xpaned->xor_gc = NULL; + xpaned->cursor_type_east = GDK_SB_V_DOUBLE_ARROW; + xpaned->cursor_type_west = GDK_SB_V_DOUBLE_ARROW; + xpaned->cursor_type_north = GDK_SB_H_DOUBLE_ARROW; + xpaned->cursor_type_south = GDK_SB_H_DOUBLE_ARROW; + xpaned->cursor_type_middle = GDK_FLEUR; + + xpaned->handle_pos_east.width = 5; + xpaned->handle_pos_east.height = 5; + xpaned->handle_pos_west.width = 5; + xpaned->handle_pos_west.height = 5; + xpaned->handle_pos_north.width = 5; + xpaned->handle_pos_north.height = 5; + xpaned->handle_pos_south.width = 5; + xpaned->handle_pos_south.height = 5; + xpaned->handle_pos_middle.width = 5; + xpaned->handle_pos_middle.height = 5; + + xpaned->position_set = FALSE; + xpaned->last_allocation.width = -1; + xpaned->last_allocation.height = -1; + xpaned->in_drag_vert = FALSE; + xpaned->in_drag_horiz = FALSE; + xpaned->in_drag_vert_and_horiz = FALSE; + + xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE; + xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE; + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE; + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE; + + xpaned->priv = g_new0 (GtkXPanedPrivate, 1); + xpaned->last_top_left_child_focus = NULL; + xpaned->last_top_right_child_focus = NULL; + xpaned->last_bottom_left_child_focus = NULL; + xpaned->last_bottom_right_child_focus = NULL; + xpaned->in_recursion = FALSE; + xpaned->handle_prelit = FALSE; + xpaned->original_position.x = -1; + xpaned->original_position.y = -1; + xpaned->unmaximized_position.x = -1; + xpaned->unmaximized_position.y = -1; + + xpaned->handle_pos_east.x = -1; + xpaned->handle_pos_east.y = -1; + xpaned->handle_pos_west.x = -1; + xpaned->handle_pos_west.y = -1; + xpaned->handle_pos_north.x = -1; + xpaned->handle_pos_north.y = -1; + xpaned->handle_pos_south.x = -1; + xpaned->handle_pos_south.y = -1; + xpaned->handle_pos_middle.x = -1; + xpaned->handle_pos_middle.y = -1; + + xpaned->drag_pos.x = -1; + xpaned->drag_pos.y = -1; +} + +static void gtk_xpaned_size_request (GtkWidget* widget, + GtkRequisition* requisition) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + GtkRequisition child_requisition; + + requisition->width = 0; + requisition->height = 0; + + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child)) + { + gtk_widget_size_request (xpaned->top_left_child, &child_requisition); + + requisition->width = child_requisition.width; + requisition->height = child_requisition.height; + } + + if (xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child)) + { + gtk_widget_size_request (xpaned->top_right_child, &child_requisition); + + requisition->width += child_requisition.width; + requisition->height = MAX (requisition->height, child_requisition.height); + } + + if (xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child)) + { + gtk_widget_size_request (xpaned->bottom_left_child, &child_requisition); + + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height += child_requisition.height; + } + + if (xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + gtk_widget_size_request (xpaned->bottom_right_child, &child_requisition); + + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height = MAX (requisition->height, child_requisition.height); + } + + /* add 2 times the set border-width to the GtkXPaneds requisition */ + requisition->width += GTK_CONTAINER (xpaned)->border_width * 2; + requisition->height += GTK_CONTAINER (xpaned)->border_width * 2; + + /* also add the handle "thickness" to GtkXPaneds width- and height-requisitions */ + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + gint handle_size; + + gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); + requisition->width += handle_size; + requisition->height += handle_size; + } +} + +void +gtk_xpaned_compute_position (GtkXPaned* xpaned, + const GtkAllocation* allocation, + GtkRequisition* top_left_child_req, + GtkRequisition* top_right_child_req, + GtkRequisition* bottom_left_child_req, + GtkRequisition* bottom_right_child_req); + + +static void gtk_xpaned_size_allocate (GtkWidget* widget, + GtkAllocation* allocation) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + gint border_width = GTK_CONTAINER (xpaned)->border_width; + GtkAllocation top_left_child_allocation; + GtkAllocation top_right_child_allocation; + GtkAllocation bottom_left_child_allocation; + GtkAllocation bottom_right_child_allocation; + GtkRequisition top_left_child_requisition; + GtkRequisition top_right_child_requisition; + GtkRequisition bottom_left_child_requisition; + GtkRequisition bottom_right_child_requisition; + gint handle_size; + + /* determine size of handle(s) */ + gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); + + widget->allocation = *allocation; + + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + /* what sizes do the children want to be at least at */ + gtk_widget_get_child_requisition (xpaned->top_left_child, + &top_left_child_requisition); + gtk_widget_get_child_requisition (xpaned->top_right_child, + &top_right_child_requisition); + gtk_widget_get_child_requisition (xpaned->bottom_left_child, + &bottom_left_child_requisition); + gtk_widget_get_child_requisition (xpaned->bottom_right_child, + &bottom_right_child_requisition); + + /* determine the total requisition-sum of all requisitions of borders, + * handles, children etc. */ + gtk_xpaned_compute_position (xpaned, + allocation, + &top_left_child_requisition, + &top_right_child_requisition, + &bottom_left_child_requisition, + &bottom_right_child_requisition); + + /* calculate the current positions and sizes of the handles */ + xpaned->handle_pos_east.x = widget->allocation.x + border_width + xpaned->top_left_child_size.width + handle_size; + xpaned->handle_pos_east.y = widget->allocation.y + border_width + xpaned->top_left_child_size.height; + xpaned->handle_pos_east.width = widget->allocation.width - xpaned->top_left_child_size.width - 2 * border_width - handle_size; + xpaned->handle_pos_east.height = handle_size; + + xpaned->handle_pos_west.x = widget->allocation.x + border_width; + xpaned->handle_pos_west.y = xpaned->handle_pos_east.y; + xpaned->handle_pos_west.width = widget->allocation.width - xpaned->handle_pos_east.width - 2 * border_width - handle_size; + xpaned->handle_pos_west.height = handle_size; + + xpaned->handle_pos_north.x = xpaned->handle_pos_east.x - handle_size; + xpaned->handle_pos_north.y = widget->allocation.y + border_width; + xpaned->handle_pos_north.width = handle_size; + xpaned->handle_pos_north.height = xpaned->handle_pos_east.y - widget->allocation.y - border_width; + + xpaned->handle_pos_south.x = xpaned->handle_pos_north.x; + xpaned->handle_pos_south.y = xpaned->handle_pos_east.y + handle_size; + xpaned->handle_pos_south.width = handle_size; + xpaned->handle_pos_south.height = widget->allocation.height - xpaned->handle_pos_north.height - 2 * border_width - handle_size; + + +#define CENTRUM 20 + xpaned->handle_pos_middle.x = xpaned->handle_pos_north.x ; + xpaned->handle_pos_middle.y = xpaned->handle_pos_east.y ; + xpaned->handle_pos_middle.width = handle_size + CENTRUM ; + xpaned->handle_pos_middle.height = handle_size + CENTRUM; + + /* set allocation for top-left child */ + top_left_child_allocation.x = widget->allocation.x + border_width; + top_left_child_allocation.y = widget->allocation.y + border_width; + top_left_child_allocation.width = xpaned->handle_pos_west.width; + top_left_child_allocation.height = xpaned->handle_pos_north.height; + + /* set allocation for top-right child */ + top_right_child_allocation.x = widget->allocation.x + border_width + handle_size + top_left_child_allocation.width; + top_right_child_allocation.y = widget->allocation.y + border_width; + top_right_child_allocation.width = xpaned->handle_pos_east.width; + top_right_child_allocation.height = xpaned->handle_pos_north.height; + + /* set allocation for bottom-left child */ + bottom_left_child_allocation.x = xpaned->handle_pos_west.x; + bottom_left_child_allocation.y = xpaned->handle_pos_south.y; + bottom_left_child_allocation.width = xpaned->handle_pos_west.width; + bottom_left_child_allocation.height = xpaned->handle_pos_south.height; + + /* set allocation for bottom-right child */ + bottom_right_child_allocation.x = top_right_child_allocation.x; + bottom_right_child_allocation.y = bottom_left_child_allocation.y; + bottom_right_child_allocation.width = xpaned->handle_pos_east.width; + bottom_right_child_allocation.height = xpaned->handle_pos_south.height; + + if (GTK_WIDGET_REALIZED (widget)) + { + if (GTK_WIDGET_MAPPED (widget)) + { + gdk_window_show (xpaned->handle_east); + gdk_window_show (xpaned->handle_west); + gdk_window_show (xpaned->handle_north); + gdk_window_show (xpaned->handle_south); + gdk_window_show (xpaned->handle_middle); + } + + gdk_window_move_resize (xpaned->handle_east, + xpaned->handle_pos_east.x, + xpaned->handle_pos_east.y, + xpaned->handle_pos_east.width, + xpaned->handle_pos_east.height); + + gdk_window_move_resize (xpaned->handle_west, + xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y, + xpaned->handle_pos_west.width, + xpaned->handle_pos_west.height); + + gdk_window_move_resize (xpaned->handle_north, + xpaned->handle_pos_north.x, + xpaned->handle_pos_north.y, + xpaned->handle_pos_north.width, + xpaned->handle_pos_north.height); + + gdk_window_move_resize (xpaned->handle_south, + xpaned->handle_pos_south.x, + xpaned->handle_pos_south.y, + xpaned->handle_pos_south.width, + xpaned->handle_pos_south.height); + + gdk_window_move_resize (xpaned->handle_middle, + xpaned->handle_pos_middle.x, + xpaned->handle_pos_middle.y, + xpaned->handle_pos_middle.width, + xpaned->handle_pos_middle.height); + } + + /* Now allocate the childen, making sure, when resizing not to + * overlap the windows + */ + if (GTK_WIDGET_MAPPED (widget)) + { + gtk_widget_size_allocate (xpaned->top_right_child, &top_right_child_allocation); + gtk_widget_size_allocate (xpaned->top_left_child, &top_left_child_allocation); + gtk_widget_size_allocate (xpaned->bottom_left_child, &bottom_left_child_allocation); + gtk_widget_size_allocate (xpaned->bottom_right_child, &bottom_right_child_allocation); + } + } +} + +static void gtk_xpaned_set_property (GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (object); + + switch (prop_id) + { + case PROP_X_POSITION: + gtk_xpaned_set_position_x (xpaned, g_value_get_int (value)); + break; + + case PROP_Y_POSITION: + gtk_xpaned_set_position_y (xpaned, g_value_get_int (value)); + break; + + case PROP_POSITION_SET: + xpaned->position_set = g_value_get_boolean (value); + gtk_widget_queue_resize (GTK_WIDGET (xpaned)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gtk_xpaned_get_property (GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (object); + + switch (prop_id) + { + case PROP_X_POSITION: + g_value_set_int (value, xpaned->top_left_child_size.width); + break; + + case PROP_Y_POSITION: + g_value_set_int (value, xpaned->top_left_child_size.height); + break; + + case PROP_POSITION_SET: + g_value_set_boolean (value, xpaned->position_set); + break; + + case PROP_MIN_X_POSITION: + g_value_set_int (value, xpaned->min_position.x); + break; + + case PROP_MIN_Y_POSITION: + g_value_set_int (value, xpaned->min_position.y); + break; + + case PROP_MAX_X_POSITION: + g_value_set_int (value, xpaned->max_position.x); + break; + + case PROP_MAX_Y_POSITION: + g_value_set_int (value, xpaned->max_position.y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gtk_xpaned_set_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + const GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (container); + gboolean old_value = FALSE; + gboolean new_value = FALSE; + + g_assert (child == xpaned->top_left_child || + child == xpaned->top_right_child || + child == xpaned->bottom_left_child || + child == xpaned->bottom_right_child); + + new_value = g_value_get_boolean (value); + + switch (property_id) + { + case CHILD_PROP_RESIZE: + if (child == xpaned->top_left_child) + { + old_value = xpaned->top_left_child_resize; + xpaned->top_left_child_resize = new_value; + } + else if (child == xpaned->top_right_child) + { + old_value = xpaned->top_right_child_resize; + xpaned->top_right_child_resize = new_value; + } + else if (child == xpaned->bottom_left_child) + { + old_value = xpaned->bottom_left_child_resize; + xpaned->bottom_left_child_resize = new_value; + } + else if (child == xpaned->bottom_right_child) + { + old_value = xpaned->bottom_right_child_resize; + xpaned->bottom_right_child_resize = new_value; + } + break; + + case CHILD_PROP_SHRINK : + if (child == xpaned->top_left_child) + { + old_value = xpaned->top_left_child_shrink; + xpaned->top_left_child_shrink = new_value; + } + else if (child == xpaned->top_right_child) + { + old_value = xpaned->top_right_child_shrink; + xpaned->top_right_child_shrink = new_value; + } + else if (child == xpaned->bottom_left_child) + { + old_value = xpaned->bottom_left_child_shrink; + xpaned->bottom_left_child_shrink = new_value; + } + else if (child == xpaned->bottom_right_child) + { + old_value = xpaned->bottom_right_child_shrink; + xpaned->bottom_right_child_shrink = new_value; + } + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, + property_id, + pspec); + old_value = -1; /* quiet gcc */ + break; + } + + if (old_value != new_value) + gtk_widget_queue_resize (GTK_WIDGET (container)); +} + +static void gtk_xpaned_get_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (container); + + g_assert (child == xpaned->top_left_child || + child == xpaned->top_right_child || + child == xpaned->bottom_left_child || + child == xpaned->bottom_right_child); + + switch (property_id) + { + case CHILD_PROP_RESIZE : + if (child == xpaned->top_left_child) + g_value_set_boolean (value, xpaned->top_left_child_resize); + else if (child == xpaned->top_right_child) + g_value_set_boolean (value, xpaned->top_right_child_resize); + else if (child == xpaned->bottom_left_child) + g_value_set_boolean (value, xpaned->bottom_left_child_resize); + else if (child == xpaned->bottom_right_child) + g_value_set_boolean (value, xpaned->bottom_right_child_resize); + break; + + case CHILD_PROP_SHRINK : + if (child == xpaned->top_left_child) + g_value_set_boolean (value, xpaned->top_left_child_shrink); + else if (child == xpaned->top_right_child) + g_value_set_boolean (value, xpaned->top_right_child_shrink); + else if (child == xpaned->bottom_left_child) + g_value_set_boolean (value, xpaned->bottom_left_child_shrink); + else if (child == xpaned->bottom_right_child) + g_value_set_boolean (value, xpaned->bottom_right_child_shrink); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, + property_id, + pspec); + break; + } +} + +static void gtk_xpaned_finalize (GObject* object) +{ + GtkXPaned* xpaned = GTK_XPANED (object); + + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + + g_free (xpaned->priv); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void gtk_xpaned_realize (GtkWidget* widget) +{ + GtkXPaned* xpaned; + GdkWindowAttr attributes_east; + GdkWindowAttr attributes_west; + GdkWindowAttr attributes_north; + GdkWindowAttr attributes_south; + GdkWindowAttr attributes_middle; + gint attributes_mask_east; + gint attributes_mask_west; + gint attributes_mask_north; + gint attributes_mask_south; + gint attributes_mask_middle; + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + xpaned = GTK_XPANED (widget); + + widget->window = gtk_widget_get_parent_window (widget); + g_object_ref (widget->window); + + attributes_east.window_type = GDK_WINDOW_CHILD; + attributes_west.window_type = GDK_WINDOW_CHILD; + attributes_north.window_type = GDK_WINDOW_CHILD; + attributes_south.window_type = GDK_WINDOW_CHILD; + attributes_middle.window_type = GDK_WINDOW_CHILD; + + attributes_east.wclass = GDK_INPUT_ONLY; + attributes_west.wclass = GDK_INPUT_ONLY; + attributes_north.wclass = GDK_INPUT_ONLY; + attributes_south.wclass = GDK_INPUT_ONLY; + attributes_middle.wclass = GDK_INPUT_ONLY; + + attributes_east.x = xpaned->handle_pos_east.x; + attributes_east.y = xpaned->handle_pos_east.y; + attributes_east.width = xpaned->handle_pos_east.width; + attributes_east.height = xpaned->handle_pos_east.height; + + attributes_west.x = xpaned->handle_pos_west.x; + attributes_west.y = xpaned->handle_pos_west.y; + attributes_west.width = xpaned->handle_pos_west.width; + attributes_west.height = xpaned->handle_pos_west.height; + + attributes_north.x = xpaned->handle_pos_north.x; + attributes_north.y = xpaned->handle_pos_north.y; + attributes_north.width = xpaned->handle_pos_north.width; + attributes_north.height = xpaned->handle_pos_north.height; + + attributes_south.x = xpaned->handle_pos_south.x; + attributes_south.y = xpaned->handle_pos_south.y; + attributes_south.width = xpaned->handle_pos_south.width; + attributes_south.height = xpaned->handle_pos_south.height; + + attributes_middle.x = xpaned->handle_pos_middle.x; + attributes_middle.y = xpaned->handle_pos_middle.y; + attributes_middle.width = xpaned->handle_pos_middle.width; + attributes_middle.height = xpaned->handle_pos_middle.height; + + attributes_east.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_east); + attributes_west.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_west); + attributes_north.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_north); + attributes_south.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_south); + attributes_middle.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_middle); + + attributes_east.event_mask = gtk_widget_get_events (widget); + attributes_west.event_mask = gtk_widget_get_events (widget); + attributes_north.event_mask = gtk_widget_get_events (widget); + attributes_south.event_mask = gtk_widget_get_events (widget); + attributes_middle.event_mask = gtk_widget_get_events (widget); + + attributes_east.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_west.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_north.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_south.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_middle.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + + attributes_mask_east = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_west = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_north = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_south = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_middle = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + + xpaned->handle_east = gdk_window_new (widget->window, + &attributes_east, + attributes_mask_east); + xpaned->handle_west = gdk_window_new (widget->window, + &attributes_west, + attributes_mask_west); + xpaned->handle_north = gdk_window_new (widget->window, + &attributes_north, + attributes_mask_north); + xpaned->handle_south = gdk_window_new (widget->window, + &attributes_south, + attributes_mask_south); + xpaned->handle_middle = gdk_window_new (widget->window, + &attributes_middle, + attributes_mask_middle); + + gdk_window_set_user_data (xpaned->handle_east, xpaned); + gdk_window_set_user_data (xpaned->handle_west, xpaned); + gdk_window_set_user_data (xpaned->handle_north, xpaned); + gdk_window_set_user_data (xpaned->handle_south, xpaned); + gdk_window_set_user_data (xpaned->handle_middle, xpaned); + + gdk_cursor_unref (attributes_east.cursor); + gdk_cursor_unref (attributes_west.cursor); + gdk_cursor_unref (attributes_north.cursor); + gdk_cursor_unref (attributes_south.cursor); + gdk_cursor_unref (attributes_middle.cursor); + + widget->style = gtk_style_attach (widget->style, widget->window); + + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + gdk_window_show (xpaned->handle_east); + gdk_window_show (xpaned->handle_west); + gdk_window_show (xpaned->handle_north); + gdk_window_show (xpaned->handle_south); + gdk_window_show (xpaned->handle_middle); + } +} + +static void gtk_xpaned_unrealize (GtkWidget *widget) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->xor_gc) + { + g_object_unref (xpaned->xor_gc); + xpaned->xor_gc = NULL; + } + + if (xpaned->handle_east) + { + gdk_window_set_user_data (xpaned->handle_east, NULL); + gdk_window_destroy (xpaned->handle_east); + xpaned->handle_east = NULL; + } + + if (xpaned->handle_west) + { + gdk_window_set_user_data (xpaned->handle_west, NULL); + gdk_window_destroy (xpaned->handle_west); + xpaned->handle_west = NULL; + } + + if (xpaned->handle_north) + { + gdk_window_set_user_data (xpaned->handle_north, NULL); + gdk_window_destroy (xpaned->handle_north); + xpaned->handle_north = NULL; + } + + if (xpaned->handle_south) + { + gdk_window_set_user_data (xpaned->handle_south, NULL); + gdk_window_destroy (xpaned->handle_south); + xpaned->handle_south = NULL; + } + + if (xpaned->handle_middle) + { + gdk_window_set_user_data (xpaned->handle_middle, NULL); + gdk_window_destroy (xpaned->handle_middle); + xpaned->handle_middle = NULL; + } + + gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL); + gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL); + gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL); + gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL); + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void gtk_xpaned_map (GtkWidget* widget) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + gdk_window_show (xpaned->handle_east); + gdk_window_show (xpaned->handle_west); + gdk_window_show (xpaned->handle_north); + gdk_window_show (xpaned->handle_south); + gdk_window_show (xpaned->handle_middle); + + GTK_WIDGET_CLASS (parent_class)->map (widget); +} + +static void gtk_xpaned_unmap (GtkWidget* widget) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + gdk_window_hide (xpaned->handle_east); + gdk_window_hide (xpaned->handle_west); + gdk_window_hide (xpaned->handle_north); + gdk_window_hide (xpaned->handle_south); + gdk_window_hide (xpaned->handle_middle); + + GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +static gboolean gtk_xpaned_expose (GtkWidget* widget, + GdkEventExpose* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + gint handle_size; + GdkRectangle horizontalClipArea; + GdkRectangle verticalClipArea; + + /* determine size of handle(s) */ + gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); + + /* I want the handle-"thickness" to be at least 3 */ + g_assert (handle_size >= 3); + + if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) && + xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + GtkStateType state; + + if (gtk_widget_is_focus (widget)) + state = GTK_STATE_SELECTED; + else if (xpaned->handle_prelit) + state = GTK_STATE_PRELIGHT; + else + state = GTK_WIDGET_STATE (widget); + + horizontalClipArea.x = xpaned->handle_pos_west.x; + horizontalClipArea.y = xpaned->handle_pos_west.y; + horizontalClipArea.width = xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width; + horizontalClipArea.height = handle_size; + + verticalClipArea.x = xpaned->handle_pos_north.x; + verticalClipArea.y = xpaned->handle_pos_north.y; + verticalClipArea.width = handle_size; + verticalClipArea.height = xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height; + + gtk_paint_handle (widget->style, + widget->window, + state, + GTK_SHADOW_NONE, + &horizontalClipArea, + widget, + "paned", + xpaned->handle_pos_east.x - handle_size - 256 / 2, + xpaned->handle_pos_west.y + 1, + 256 + handle_size, + handle_size - 2, + /*xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y + 1, + xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width, + handle_size - 2,*/ + GTK_ORIENTATION_HORIZONTAL); + gtk_paint_handle (widget->style, + widget->window, + state, + GTK_SHADOW_NONE, + &verticalClipArea, + widget, + "paned", + xpaned->handle_pos_north.x + 1, + xpaned->handle_pos_south.y - handle_size - 256 / 2, + handle_size - 2, + 256 + handle_size, + /*xpaned->handle_pos_north.x + 1, + xpaned->handle_pos_north.y, + handle_size - 2, + xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height,*/ + GTK_ORIENTATION_VERTICAL); + } + + /* Chain up to draw children */ + GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + + return FALSE; +} + +static gboolean is_rtl (GtkXPaned* xpaned) +{ + if (gtk_widget_get_direction (GTK_WIDGET (xpaned)) == GTK_TEXT_DIR_RTL) + return TRUE; + + return FALSE; +} + +static void update_drag (GtkXPaned* xpaned) +{ + GdkPoint pos; + gint handle_size; + GtkRequisition size; + + gtk_widget_get_pointer (GTK_WIDGET (xpaned), &pos.x, &pos.y); + + if (xpaned->in_drag_vert) + { + pos.y -= xpaned->drag_pos.y; + + if (is_rtl (xpaned)) + { + gtk_widget_style_get (GTK_WIDGET (xpaned), + "handle-size", &handle_size, + NULL); + + size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size; + } + else + { + size.height = pos.y; + } + + size.height -= GTK_CONTAINER (xpaned)->border_width; + + size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y); + + if (size.height != xpaned->top_left_child_size.height) + gtk_xpaned_set_position_y (xpaned, size.height); + } + + if (xpaned->in_drag_horiz) + { + pos.x -= xpaned->drag_pos.x; + + if (is_rtl (xpaned)) + { + gtk_widget_style_get (GTK_WIDGET (xpaned), + "handle-size", &handle_size, + NULL); + + size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size; + } + else + { + size.width = pos.x; + } + + size.width -= GTK_CONTAINER (xpaned)->border_width; + + size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x); + + if (size.width != xpaned->top_left_child_size.width) + gtk_xpaned_set_position_x (xpaned, size.width); + } + + if (xpaned->in_drag_vert_and_horiz) + { + pos.x -= xpaned->drag_pos.x; + pos.y -= xpaned->drag_pos.y; + + if (is_rtl (xpaned)) + { + gtk_widget_style_get (GTK_WIDGET (xpaned), + "handle-size", &handle_size, + NULL); + + size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size; + size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size; + } + else + { + size.width = pos.x; + size.height = pos.y; + } + + size.width -= GTK_CONTAINER (xpaned)->border_width; + size.height -= GTK_CONTAINER (xpaned)->border_width; + + size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x); + size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y); + + if (size.width != xpaned->top_left_child_size.width) + gtk_xpaned_set_position_x (xpaned, size.width); + + if (size.height != xpaned->top_left_child_size.height) + gtk_xpaned_set_position_y (xpaned, size.height); + } +} + +static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert || + xpaned->in_drag_horiz || + xpaned->in_drag_vert_and_horiz) + update_drag (xpaned); + else + { + xpaned->handle_prelit = TRUE; + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_east.x, + xpaned->handle_pos_east.y, + xpaned->handle_pos_east.width, + xpaned->handle_pos_east.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y, + xpaned->handle_pos_west.width, + xpaned->handle_pos_west.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_north.x, + xpaned->handle_pos_north.y, + xpaned->handle_pos_north.width, + xpaned->handle_pos_north.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_south.x, + xpaned->handle_pos_south.y, + xpaned->handle_pos_south.width, + xpaned->handle_pos_south.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_middle.x, + xpaned->handle_pos_middle.y, + xpaned->handle_pos_middle.width, + xpaned->handle_pos_middle.height); + } + + return TRUE; +} + +static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert || + xpaned->in_drag_horiz || + xpaned->in_drag_vert_and_horiz) + update_drag (xpaned); + else + { + xpaned->handle_prelit = FALSE; + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_east.x, + xpaned->handle_pos_east.y, + xpaned->handle_pos_east.width, + xpaned->handle_pos_east.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y, + xpaned->handle_pos_west.width, + xpaned->handle_pos_west.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_north.x, + xpaned->handle_pos_north.y, + xpaned->handle_pos_north.width, + xpaned->handle_pos_north.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_south.x, + xpaned->handle_pos_south.y, + xpaned->handle_pos_south.width, + xpaned->handle_pos_south.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_middle.x, + xpaned->handle_pos_middle.y, + xpaned->handle_pos_middle.width, + xpaned->handle_pos_middle.height); + } + + return TRUE; +} + +static gboolean gtk_xpaned_focus (GtkWidget* widget, GtkDirectionType direction) +{ + gboolean retval; + + /* This is a hack, but how can this be done without + * excessive cut-and-paste from gtkcontainer.c? + */ + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); + retval = (* GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction); + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + + return retval; +} + +static gboolean gtk_xpaned_button_press (GtkWidget* widget, + GdkEventButton* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + /* if any child is currently maximized, jump right back */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || + xpaned->maximized[GTK_XPANED_TOP_RIGHT] || + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + return FALSE; + + /* if user is dragging the handles around */ + if (!xpaned->in_drag_vert_and_horiz && + event->window != xpaned->handle_east && + event->window != xpaned->handle_west && + event->window != xpaned->handle_north && + event->window != xpaned->handle_south && + event->window == xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_vert_and_horiz = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_middle, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.x = event->x; + xpaned->drag_pos.y = event->y; + + return TRUE; + } + else if (!xpaned->in_drag_vert && + event->window == xpaned->handle_east && + event->window != xpaned->handle_west && + event->window != xpaned->handle_north && + event->window != xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_vert = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_east, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.y = event->y; + + return TRUE; + } + else if (!xpaned->in_drag_vert && + event->window != xpaned->handle_east && + event->window == xpaned->handle_west && + event->window != xpaned->handle_north && + event->window != xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_vert = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_west, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.y = event->y; + + return TRUE; + } + else if (!xpaned->in_drag_horiz && + event->window != xpaned->handle_east && + event->window != xpaned->handle_west && + event->window == xpaned->handle_north && + event->window != xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_horiz = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_north, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.x = event->x; + + return TRUE; + } + else if (!xpaned->in_drag_horiz && + event->window != xpaned->handle_east && + event->window != xpaned->handle_west && + event->window != xpaned->handle_north && + event->window == xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_horiz = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_south, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.x = event->x; + + return TRUE; + } + return FALSE; +} + +static gboolean gtk_xpaned_button_release (GtkWidget* widget, + GdkEventButton* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert && (event->button == 1)) + { + xpaned->in_drag_vert = FALSE; + xpaned->drag_pos.y = -1; + xpaned->position_set = TRUE; + gdk_display_pointer_ungrab (gtk_widget_get_display (widget), + event->time); + return TRUE; + } + else if (xpaned->in_drag_horiz && (event->button == 1)) + { + xpaned->in_drag_horiz = FALSE; + xpaned->drag_pos.x = -1; + xpaned->position_set = TRUE; + gdk_display_pointer_ungrab (gtk_widget_get_display (widget), + event->time); + return TRUE; + } + else if (xpaned->in_drag_vert_and_horiz && (event->button == 1)) + { + xpaned->in_drag_vert_and_horiz = FALSE; + xpaned->drag_pos.x = -1; + xpaned->drag_pos.y = -1; + xpaned->position_set = TRUE; + gdk_display_pointer_ungrab (gtk_widget_get_display (widget), + event->time); + return TRUE; + } + + return FALSE; +} + +static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert || + xpaned->in_drag_horiz || + xpaned->in_drag_vert_and_horiz) + + { + update_drag (xpaned); + return TRUE; + } + + return FALSE; +} + +void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_top_left (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_top_right (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_bottom_left (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_bottom_right (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_pack_top_left (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->top_left_child) + { + xpaned->top_left_child = child; + xpaned->top_left_child_resize = resize; + xpaned->top_left_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +void gtk_xpaned_pack_top_right (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->top_right_child) + { + xpaned->top_right_child = child; + xpaned->top_right_child_resize = resize; + xpaned->top_right_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->bottom_left_child) + { + xpaned->bottom_left_child = child; + xpaned->bottom_left_child_resize = resize; + xpaned->bottom_left_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->bottom_right_child) + { + xpaned->bottom_right_child = child; + xpaned->bottom_right_child_resize = resize; + xpaned->bottom_right_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget) +{ + GtkXPaned* xpaned; + + g_return_if_fail (GTK_IS_XPANED (container)); + + xpaned = GTK_XPANED (container); + + if (!xpaned->top_left_child) + gtk_xpaned_add_top_left (xpaned, widget); + else if (!xpaned->top_right_child) + gtk_xpaned_add_top_right (xpaned, widget); + else if (!xpaned->bottom_left_child) + gtk_xpaned_add_bottom_left (xpaned, widget); + else if (!xpaned->bottom_right_child) + gtk_xpaned_add_bottom_right (xpaned, widget); + else + g_warning ("GtkXPaned cannot have more than 4 children\n"); +} + +static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget) +{ + GtkXPaned* xpaned; + gboolean was_visible; + + xpaned = GTK_XPANED (container); + was_visible = GTK_WIDGET_VISIBLE (widget); + + if (xpaned->top_left_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->top_left_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else if (xpaned->top_right_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->top_right_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else if (xpaned->bottom_left_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->bottom_left_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else if (xpaned->bottom_right_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->bottom_right_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else + g_warning ("GtkXPaned has no more children attached\n"); + +} + +static void gtk_xpaned_forall (GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkXPaned* xpaned; + + g_return_if_fail (callback != NULL); + + xpaned = GTK_XPANED (container); + + if (xpaned->top_left_child) + (*callback) (xpaned->top_left_child, callback_data); + if (xpaned->top_right_child) + (*callback) (xpaned->top_right_child, callback_data); + if (xpaned->bottom_left_child) + (*callback) (xpaned->bottom_left_child, callback_data); + if (xpaned->bottom_right_child) + (*callback) (xpaned->bottom_right_child, callback_data); +} + +/** + * gtk_xpaned_get_position_x: + * @paned: a #GtkXPaned widget + * + * Obtains the x-position of the divider. + * + * Return value: x-position of the divider + **/ +gint gtk_xpaned_get_position_x (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0); + + return xpaned->top_left_child_size.width; +} + +/** + * gtk_xpaned_get_position_y: + * @paned: a #GtkXPaned widget + * + * Obtains the y-position of the divider. + * + * Return value: y-position of the divider + **/ +gint gtk_xpaned_get_position_y (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0); + + return xpaned->top_left_child_size.height; +} + +/** + * gtk_xpaned_set_position_x: + * @paned: a #GtkXPaned widget + * @xposition: pixel x-position of divider, a negative values + * of a component mean that the position is unset. + * + * Sets the x-position of the divider between the four panes. + **/ +void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition) +{ + GObject* object; + + g_return_if_fail (GTK_IS_XPANED (xpaned)); + + /* if any child is currently maximized, jump right back */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || + xpaned->maximized[GTK_XPANED_TOP_RIGHT] || + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + return; + + object = G_OBJECT (xpaned); + + if (xposition >= 0) + { + /* We don't clamp here - the assumption is that + * if the total allocation changes at the same time + * as the position, the position set is with reference + * to the new total size. If only the position changes, + * then clamping will occur in gtk_paned_compute_position() + */ + + xpaned->top_left_child_size.width = xposition; + xpaned->position_set = TRUE; + } + else + { + xpaned->position_set = FALSE; + } + + g_object_freeze_notify (object); + g_object_notify (object, "x-position"); + g_object_notify (object, "position-set"); + g_object_thaw_notify (object); + + gtk_widget_queue_resize (GTK_WIDGET (xpaned)); +} + +/** + * gtk_xpaned_set_position_y: + * @paned: a #GtkXPaned widget + * @yposition: pixel y-position of divider, a negative values + * of a component mean that the position is unset. + * + * Sets the y-position of the divider between the four panes. + **/ +void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition) +{ + GObject* object; + + g_return_if_fail (GTK_IS_XPANED (xpaned)); + + /* if any child is currently maximized, jump right back */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || + xpaned->maximized[GTK_XPANED_TOP_RIGHT] || + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + return; + + object = G_OBJECT (xpaned); + + if (yposition >= 0) + { + /* We don't clamp here - the assumption is that + * if the total allocation changes at the same time + * as the position, the position set is with reference + * to the new total size. If only the position changes, + * then clamping will occur in gtk_paned_compute_position() + */ + + xpaned->top_left_child_size.height = yposition; + xpaned->position_set = TRUE; + } + else + { + xpaned->position_set = FALSE; + } + + g_object_freeze_notify (object); + g_object_notify (object, "y-position"); + g_object_notify (object, "position-set"); + g_object_thaw_notify (object); + + gtk_widget_queue_resize (GTK_WIDGET (xpaned)); +} + +/* this call is private and only intended for internal use! */ +void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned) +{ + xpaned->unmaximized_position.x = gtk_xpaned_get_position_x (xpaned); +} + +/* this call is private and only intended for internal use! */ +void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned) +{ + xpaned->unmaximized_position.y = gtk_xpaned_get_position_y (xpaned); +} + +/* this call is private and only intended for internal use! */ +gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned) +{ + return xpaned->unmaximized_position.x; +} + +/* this call is private and only intended for internal use! */ +gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned) +{ + return xpaned->unmaximized_position.y; +} + +/** + * gtk_xpaned_get_top_left_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the top-left child of the xpaned widget. + * + * Return value: top-left child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->top_left_child; +} + +/** + * gtk_xpaned_get_top_right_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the top-right child of the xpaned widget. + * + * Return value: top-right child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->top_right_child; +} + +/** + * gtk_xpaned_get_bottom_left_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the bottom-left child of the xpaned widget. + * + * Return value: bottom-left child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->bottom_left_child; +} + +/** + * gtk_xpaned_get_bottom_right_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the bottom-right child of the xpaned widget. + * + * Return value: bottom-right child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->bottom_right_child; +} + +gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y); + + /* mark maximized flag for top-left child */ + xpaned->maximized[GTK_XPANED_TOP_LEFT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that top-left child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT]) + { + /* clear maximized flat for top-left child */ + xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* top-left child is currently not maximized, report error */ + else + return FALSE; + } +} + +gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y); + + /* mark maximized flag for top-right child */ + xpaned->maximized[GTK_XPANED_TOP_RIGHT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that top-right child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_TOP_RIGHT]) + { + /* clear maximized flat for top-right child */ + xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* top-right child is currently not maximized, report error */ + else + return FALSE; + } +} + +gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y); + + /* mark maximized flag for bottom-left child */ + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that bottom-left child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_BOTTOM_LEFT]) + { + /* clear maximized flat for bottom-left child */ + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* bottom-left child is currently not maximized, report error */ + else + return FALSE; + } +} + +gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y); + + /* mark maximized flag for bottom-right child */ + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that bottom-right child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* clear maximized flat for bottom-right child */ + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* bottom-right child is currently not maximized, report error */ + else + return FALSE; + } +} + +void +gtk_xpaned_compute_position (GtkXPaned* xpaned, + const GtkAllocation* allocation, + GtkRequisition* top_left_child_req, + GtkRequisition* top_right_child_req, + GtkRequisition* bottom_left_child_req, + GtkRequisition* bottom_right_child_req) +{ + GdkPoint old_position; + GdkPoint old_min_position; + GdkPoint old_max_position; + gint handle_size; + gint border_width = GTK_CONTAINER (xpaned)->border_width; + float fX; + float fY; + + g_return_if_fail (GTK_IS_XPANED (xpaned)); + + old_position.x = xpaned->top_left_child_size.width; + old_position.y = xpaned->top_left_child_size.height; + old_min_position.x = xpaned->min_position.x; + old_min_position.y = xpaned->min_position.y; + old_max_position.x = xpaned->max_position.x; + old_max_position.y = xpaned->max_position.y; + + fX = 100.0f * (float) old_position.x / (float) allocation->width; + fY = 100.0f * (float) old_position.y / (float) allocation->height; + + xpaned->min_position.x = xpaned->top_left_child_shrink ? 0 : top_left_child_req->width; + xpaned->min_position.y = xpaned->top_left_child_shrink ? 0 : top_left_child_req->height; + + gtk_widget_style_get (GTK_WIDGET (xpaned), "handle-size", &handle_size, NULL); + + xpaned->max_position.x = allocation->width - 2 * border_width - handle_size; + xpaned->max_position.y = allocation->height - 2 * border_width - handle_size; + if (!xpaned->top_left_child_shrink) + xpaned->max_position.x = MAX (1, xpaned->max_position.x - top_left_child_req->width); + xpaned->max_position.x = MAX (xpaned->min_position.x, xpaned->max_position.x); + + if (!xpaned->position_set) + { + if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width = MAX (0, allocation->width - top_right_child_req->width); + xpaned->top_left_child_size.height = MAX (0, allocation->height - top_right_child_req->height); + } + else if (!xpaned->top_left_child_resize && xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width = top_left_child_req->width; + xpaned->top_left_child_size.height = top_left_child_req->height; + } + else if (top_left_child_req->width + top_right_child_req->width != 0) + { + xpaned->top_left_child_size.width = allocation->width * ((gdouble)top_left_child_req->width / (top_left_child_req->width + top_right_child_req->width)) + 0.5; + } + else if (top_left_child_req->height + top_right_child_req->height != 0) + { + xpaned->top_left_child_size.height = allocation->height * ((gdouble)top_left_child_req->height / (top_left_child_req->height + top_right_child_req->height)) + 0.5; + } + else + { + xpaned->top_left_child_size.width = allocation->width * 0.5 + 0.5; + xpaned->top_left_child_size.height = allocation->height * 0.5 + 0.5; + } + } + else + { + /* If the position was set before the initial allocation. + ** (paned->last_allocation <= 0) just clamp it and leave it. */ + if (xpaned->last_allocation.width > 0) + { + if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width += allocation->width + - xpaned->last_allocation.width; + + xpaned->top_left_child_size.height += allocation->height + - xpaned->last_allocation.height; + } + else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize)) + { + xpaned->top_left_child_size.width = allocation->width + * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width)) + + 0.5; + + xpaned->top_left_child_size.height = allocation->height + * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height)) + + 0.5; + } + } + if (xpaned->last_allocation.height > 0) + { + if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width += allocation->width - xpaned->last_allocation.width; + xpaned->top_left_child_size.height += allocation->height - xpaned->last_allocation.height; + } + else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize)) + { + xpaned->top_left_child_size.width = allocation->width * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width)) + 0.5; + xpaned->top_left_child_size.height = allocation->height * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height)) + 0.5; + } + } + + } + + xpaned->top_left_child_size.width = CLAMP (xpaned->top_left_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->top_left_child_size.height = CLAMP (xpaned->top_left_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + xpaned->top_right_child_size.width = CLAMP (xpaned->top_right_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->top_right_child_size.height = CLAMP (xpaned->top_right_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + xpaned->bottom_left_child_size.width = CLAMP (xpaned->bottom_left_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->bottom_left_child_size.height = CLAMP (xpaned->bottom_left_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + xpaned->bottom_right_child_size.width = CLAMP (xpaned->bottom_right_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->bottom_right_child_size.height = CLAMP (xpaned->bottom_right_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + gtk_widget_set_child_visible (xpaned->top_left_child, TRUE); + gtk_widget_set_child_visible (xpaned->top_right_child, TRUE); + gtk_widget_set_child_visible (xpaned->bottom_left_child, TRUE); + gtk_widget_set_child_visible (xpaned->bottom_right_child, TRUE); + + g_object_freeze_notify (G_OBJECT (xpaned)); + + if (xpaned->top_left_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->top_left_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->top_right_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->top_right_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->bottom_left_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->bottom_left_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->bottom_right_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->bottom_right_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->min_position.x != old_min_position.x) + g_object_notify (G_OBJECT (xpaned), "min-x-position"); + if (xpaned->min_position.y != old_min_position.y) + g_object_notify (G_OBJECT (xpaned), "min-y-position"); + + if (xpaned->max_position.x != old_max_position.x) + g_object_notify (G_OBJECT (xpaned), "max-y-position"); + if (xpaned->max_position.y != old_max_position.y) + g_object_notify (G_OBJECT (xpaned), "max-y-position"); + + g_object_thaw_notify (G_OBJECT (xpaned)); + + xpaned->last_allocation.width = allocation->width; + xpaned->last_allocation.height = allocation->height; + + fX = 100.0f * (float) old_position.x / (float) allocation->width; + fY = 100.0f * (float) old_position.y / (float) allocation->height; +} + +static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget) +{ + if (xpaned->priv->saved_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->saved_focus), + (gpointer *)&(xpaned->priv->saved_focus)); + + xpaned->priv->saved_focus = widget; + + if (xpaned->priv->saved_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->priv->saved_focus), + (gpointer *)&(xpaned->priv->saved_focus)); +} + +static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned, + GtkXPaned* first_xpaned) +{ + if (xpaned->priv->first_xpaned) + g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned), + (gpointer *)&(xpaned->priv->first_xpaned)); + + xpaned->priv->first_xpaned = first_xpaned; + + if (xpaned->priv->first_xpaned) + g_object_add_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned), + (gpointer *)&(xpaned->priv->first_xpaned)); +} + +static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned, + GtkWidget* widget) +{ + if (xpaned->last_top_left_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus), + (gpointer *)&(xpaned->last_top_left_child_focus)); + + xpaned->last_top_left_child_focus = widget; + + if (xpaned->last_top_left_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus), + (gpointer *)&(xpaned->last_top_left_child_focus)); +} + +static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned, + GtkWidget *widget) +{ + if (xpaned->last_top_right_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus), + (gpointer *)&(xpaned->last_top_right_child_focus)); + + xpaned->last_top_right_child_focus = widget; + + if (xpaned->last_top_right_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus), + (gpointer *)&(xpaned->last_top_right_child_focus)); +} + +static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned, + GtkWidget *widget) +{ + if (xpaned->last_bottom_left_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus), + (gpointer *)&(xpaned->last_bottom_left_child_focus)); + + xpaned->last_bottom_left_child_focus = widget; + + if (xpaned->last_bottom_left_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus), + (gpointer *)&(xpaned->last_bottom_left_child_focus)); +} + +static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned, + GtkWidget *widget) +{ + if (xpaned->last_bottom_right_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus), + (gpointer *)&(xpaned->last_bottom_right_child_focus)); + + xpaned->last_bottom_right_child_focus = widget; + + if (xpaned->last_bottom_right_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus), + (gpointer *)&(xpaned->last_bottom_right_child_focus)); +} + +static GtkWidget* xpaned_get_focus_widget (GtkXPaned* xpaned) +{ + GtkWidget* toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); + if (GTK_WIDGET_TOPLEVEL (toplevel)) + return GTK_WINDOW (toplevel)->focus_widget; + + return NULL; +} + +static void gtk_xpaned_set_focus_child (GtkContainer* container, + GtkWidget* focus_child) +{ + GtkXPaned* xpaned; + + g_return_if_fail (GTK_IS_XPANED (container)); + + xpaned = GTK_XPANED (container); + + if (focus_child == NULL) + { + GtkWidget* last_focus; + GtkWidget* w; + + last_focus = xpaned_get_focus_widget (xpaned); + + if (last_focus) + { + /* If there is one or more paned widgets between us and the + * focus widget, we want the topmost of those as last_focus + */ + for (w = last_focus; w != GTK_WIDGET (xpaned); w = w->parent) + if (GTK_IS_XPANED (w)) + last_focus = w; + + if (container->focus_child == xpaned->top_left_child) + gtk_xpaned_set_last_top_left_child_focus (xpaned, last_focus); + else if (container->focus_child == xpaned->top_right_child) + gtk_xpaned_set_last_top_right_child_focus (xpaned, last_focus); + else if (container->focus_child == xpaned->bottom_left_child) + gtk_xpaned_set_last_bottom_left_child_focus (xpaned, last_focus); + else if (container->focus_child == xpaned->bottom_right_child) + gtk_xpaned_set_last_bottom_right_child_focus (xpaned, last_focus); + } + } + + if (parent_class->set_focus_child) + (* parent_class->set_focus_child) (container, focus_child); +} + +static void gtk_xpaned_get_cycle_chain (GtkXPaned* xpaned, + GtkDirectionType direction, + GList** widgets) +{ + GtkContainer* container = GTK_CONTAINER (xpaned); + GtkWidget* ancestor = NULL; + GList* temp_list = NULL; + GList* list; + + if (xpaned->in_recursion) + return; + + g_assert (widgets != NULL); + + if (xpaned->last_top_left_child_focus && + !gtk_widget_is_ancestor (xpaned->last_top_left_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL); + } + + if (xpaned->last_top_right_child_focus && + !gtk_widget_is_ancestor (xpaned->last_top_right_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL); + } + + if (xpaned->last_bottom_left_child_focus && + !gtk_widget_is_ancestor (xpaned->last_bottom_left_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL); + } + + if (xpaned->last_bottom_right_child_focus && + !gtk_widget_is_ancestor (xpaned->last_bottom_right_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL); + } + + if (GTK_WIDGET (xpaned)->parent) + ancestor = gtk_widget_get_ancestor (GTK_WIDGET (xpaned)->parent, + GTK_TYPE_XPANED); + + /* The idea here is that temp_list is a list of widgets we want to cycle + * to. The list is prioritized so that the first element is our first + * choice, the next our second, and so on. + * + * We can't just use g_list_reverse(), because we want to try + * paned->last_child?_focus before paned->child?, both when we + * are going forward and backward. + */ + if (direction == GTK_DIR_TAB_FORWARD) + { + if (container->focus_child == xpaned->top_left_child) + { + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + temp_list = g_list_append (temp_list, ancestor); + } + else if (container->focus_child == xpaned->top_right_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + } + else if (container->focus_child == xpaned->bottom_left_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_right_child); + } + else if (container->focus_child == xpaned->bottom_right_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_left_child); + } + else + { + temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_left_child); + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_right_child); + temp_list = g_list_append (temp_list, ancestor); + } + } + else + { + if (container->focus_child == xpaned->top_left_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + } + else if (container->focus_child == xpaned->top_right_child) + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + else if (container->focus_child == xpaned->bottom_right_child) + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + else if (container->focus_child == xpaned->top_right_child) + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + else + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_right_child); + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + } + + /* Walk the list and expand all the paned widgets. */ + for (list = temp_list; list != NULL; list = list->next) + { + GtkWidget *widget = list->data; + + if (widget) + { + if (GTK_IS_XPANED (widget)) + { + xpaned->in_recursion = TRUE; + gtk_xpaned_get_cycle_chain (GTK_XPANED (widget), + direction, + widgets); + xpaned->in_recursion = FALSE; + } + else + { + *widgets = g_list_append (*widgets, widget); + } + } + } + + g_list_free (temp_list); +} + +static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned, + gboolean reversed) +{ + GList* cycle_chain = NULL; + GList* list; + + GtkDirectionType direction = reversed ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD; + + /* ignore f6 if the handle is focused */ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + return TRUE; + + /* we can't just let the event propagate up the hierarchy, + * because the paned will want to cycle focus _unless_ an + * ancestor paned handles the event + */ + gtk_xpaned_get_cycle_chain (xpaned, direction, &cycle_chain); + + for (list = cycle_chain; list != NULL; list = list->next) + if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction)) + break; + + g_list_free (cycle_chain); + + return TRUE; +} + +static void get_child_xpanes (GtkWidget* widget, GList** xpanes) +{ + if (GTK_IS_XPANED (widget)) + { + GtkXPaned* xpaned = GTK_XPANED (widget); + + get_child_xpanes (xpaned->top_left_child, xpanes); + *xpanes = g_list_prepend (*xpanes, widget); + get_child_xpanes (xpaned->top_right_child, xpanes); + *xpanes = g_list_prepend (*xpanes, widget); + get_child_xpanes (xpaned->bottom_left_child, xpanes); + *xpanes = g_list_prepend (*xpanes, widget); + get_child_xpanes (xpaned->bottom_right_child, xpanes); + } + else if (GTK_IS_CONTAINER (widget)) + { + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback)get_child_xpanes, + xpanes); + } +} + +static GList* get_all_xpanes (GtkXPaned* xpaned) +{ + GtkXPaned* topmost = NULL; + GList* result = NULL; + GtkWidget* w; + + for (w = GTK_WIDGET (xpaned); w != NULL; w = w->parent) + { + if (GTK_IS_XPANED (w)) + topmost = GTK_XPANED (w); + } + + g_assert (topmost); + + get_child_xpanes (GTK_WIDGET (topmost), &result); + + return g_list_reverse (result); +} + +static void gtk_xpaned_find_neighbours (GtkXPaned* xpaned, + GtkXPaned** next, + GtkXPaned** prev) +{ + GList* all_xpanes; + GList* this_link; + + all_xpanes = get_all_xpanes (xpaned); + g_assert (all_xpanes); + + this_link = g_list_find (all_xpanes, xpaned); + + g_assert (this_link); + + if (this_link->next) + *next = this_link->next->data; + else + *next = all_xpanes->data; + + if (this_link->prev) + *prev = this_link->prev->data; + else + *prev = g_list_last (all_xpanes)->data; + + g_list_free (all_xpanes); +} + +static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned, GtkScrollType scroll) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + GdkPoint old_position; + GdkPoint new_position; + gint increment; + + enum + { + SINGLE_STEP_SIZE = 1, + PAGE_STEP_SIZE = 75 + }; + + new_position.x = old_position.x = gtk_xpaned_get_position_x (xpaned); + new_position.y = old_position.y = gtk_xpaned_get_position_y (xpaned); + increment = 0; + + switch (scroll) + { + case GTK_SCROLL_STEP_LEFT: + case GTK_SCROLL_STEP_UP: + case GTK_SCROLL_STEP_BACKWARD: + increment = - SINGLE_STEP_SIZE; + break; + + case GTK_SCROLL_STEP_RIGHT: + case GTK_SCROLL_STEP_DOWN: + case GTK_SCROLL_STEP_FORWARD: + increment = SINGLE_STEP_SIZE; + break; + + case GTK_SCROLL_PAGE_LEFT: + case GTK_SCROLL_PAGE_UP: + case GTK_SCROLL_PAGE_BACKWARD: + increment = - PAGE_STEP_SIZE; + break; + + case GTK_SCROLL_PAGE_RIGHT: + case GTK_SCROLL_PAGE_DOWN: + case GTK_SCROLL_PAGE_FORWARD: + increment = PAGE_STEP_SIZE; + break; + + case GTK_SCROLL_START: + new_position.x = xpaned->min_position.x; + new_position.y = xpaned->min_position.y; + break; + + case GTK_SCROLL_END: + new_position.x = xpaned->max_position.x; + new_position.y = xpaned->max_position.y; + break; + + default: + break; + } + + if (increment) + { + if (is_rtl (xpaned)) + increment = -increment; + + new_position.x = old_position.x + increment; + new_position.y = old_position.y + increment; + } + + new_position.x = CLAMP (new_position.x, + xpaned->min_position.x, + xpaned->max_position.x); + + new_position.y = CLAMP (new_position.y, + xpaned->min_position.y, + xpaned->max_position.y); + + if (old_position.x != new_position.x) + gtk_xpaned_set_position_x (xpaned, new_position.x); + + if (old_position.y != new_position.y) + gtk_xpaned_set_position_y (xpaned, new_position.y); + + return TRUE; + } + + return FALSE; +} + +static void gtk_xpaned_restore_focus (GtkXPaned* xpaned) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + if (xpaned->priv->saved_focus && + GTK_WIDGET_SENSITIVE (xpaned->priv->saved_focus)) + { + gtk_widget_grab_focus (xpaned->priv->saved_focus); + } + else + { + /* the saved focus is somehow not available for focusing, + * try + * 1) tabbing into the paned widget + * if that didn't work, + * 2) unset focus for the window if there is one + */ + + if (!gtk_widget_child_focus (GTK_WIDGET (xpaned), GTK_DIR_TAB_FORWARD)) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); + + if (GTK_IS_WINDOW (toplevel)) + gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); + } + } + + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + } +} + +static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + xpaned->original_position.x = -1; + xpaned->original_position.y = -1; + gtk_xpaned_restore_focus (xpaned); + + return TRUE; + } + + return FALSE; +} + +static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + if (xpaned->original_position.x != -1) + { + gtk_xpaned_set_position_x (xpaned, xpaned->original_position.x); + xpaned->original_position.x = -1; + } + + if (xpaned->original_position.y != -1) + { + gtk_xpaned_set_position_y (xpaned, xpaned->original_position.y); + xpaned->original_position.y = -1; + } + + gtk_xpaned_restore_focus (xpaned); + return TRUE; + } + + return FALSE; +} + +static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned, + gboolean reversed) +{ + GtkXPaned* next; + GtkXPaned* prev; + + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + GtkXPaned* focus = NULL; + + if (!xpaned->priv->first_xpaned) + { + /* The first_pane has disappeared. As an ad-hoc solution, + * we make the currently focused paned the first_paned. To the + * user this will seem like the paned cycling has been reset. + */ + gtk_xpaned_set_first_xpaned (xpaned, xpaned); + } + + gtk_xpaned_find_neighbours (xpaned, &next, &prev); + + if (reversed && prev && + prev != xpaned && xpaned != xpaned->priv->first_xpaned) + { + focus = prev; + } + else if (!reversed && + next && + next != xpaned && + next != xpaned->priv->first_xpaned) + { + focus = next; + } + else + { + gtk_xpaned_accept_position (xpaned); + return TRUE; + } + + g_assert (focus); + + gtk_xpaned_set_saved_focus (focus, xpaned->priv->saved_focus); + gtk_xpaned_set_first_xpaned (focus, xpaned->priv->first_xpaned); + + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + + gtk_widget_grab_focus (GTK_WIDGET (focus)); + + if (!gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + xpaned->original_position.x = -1; + xpaned->original_position.y = -1; + focus->original_position.x = gtk_xpaned_get_position_x (focus); + focus->original_position.y = gtk_xpaned_get_position_y (focus); + } + } + else + { + GtkContainer* container = GTK_CONTAINER (xpaned); + GtkXPaned* focus; + GtkXPaned* first; + GtkXPaned* prev; + GtkXPaned* next; + GtkWidget* toplevel; + + gtk_xpaned_find_neighbours (xpaned, &next, &prev); + + if (container->focus_child == xpaned->top_left_child) + { + if (reversed) + { + focus = prev; + first = xpaned; + } + else + { + focus = xpaned; + first = xpaned; + } + } + else if (container->focus_child == xpaned->top_right_child) + { + if (reversed) + { + focus = xpaned; + first = next; + } + else + { + focus = next; + first = next; + } + } + else + { + /* Focus is not inside this xpaned, and we don't have focus. + * Presumably this happened because the application wants us + * to start keyboard navigating. + */ + focus = xpaned; + + if (reversed) + first = xpaned; + else + first = next; + } + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); + + if (GTK_IS_WINDOW (toplevel)) + gtk_xpaned_set_saved_focus (focus, GTK_WINDOW (toplevel)->focus_widget); + gtk_xpaned_set_first_xpaned (focus, first); + focus->original_position.x = gtk_xpaned_get_position_x (focus); + focus->original_position.y = gtk_xpaned_get_position_y (focus); + + gtk_widget_grab_focus (GTK_WIDGET (focus)); + } + + return TRUE; +} + +static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned) +{ + /* This function/signal has the wrong name. It is called when you + * press Tab or Shift-Tab and what we do is act as if + * the user pressed Return and then Tab or Shift-Tab + */ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + gtk_xpaned_accept_position (xpaned); + + return FALSE; +} + +/*#define __GTK_XPANED_C__*/ +/*#include "gtkaliasdef.c"*/ diff --git a/lib/gtk-contrib/gtkxpaned.h b/lib/gtk-contrib/gtkxpaned.h new file mode 100644 index 00000000..47e752ee --- /dev/null +++ b/lib/gtk-contrib/gtkxpaned.h @@ -0,0 +1,175 @@ +/******************************************************************************* +**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +** 10 20 30 40 50 60 70 80 +** +** library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+ +** Copyright (C) 2005-2006 Mirco "MacSlow" Müller +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +** +** GtkXPaned is based on GtkPaned which was done by... +** +** "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald" +** +** and later modified by... +** +** "the GTK+ Team and others 1997-2000" +** +*******************************************************************************/ + +#ifndef GTK_XPANED_H +#define GTK_XPANED_H + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_XPANED (gtk_xpaned_get_type ()) +#define GTK_XPANED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_XPANED, GtkXPaned)) +#define GTK_XPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_XPANED, GtkXPanedClass)) +#define GTK_IS_XPANED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_XPANED)) +#define GTK_IS_XPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XPANED)) +#define GTK_XPANED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XPANED, GtkXPanedClass)) + +typedef struct _GtkXPaned GtkXPaned; +typedef struct _GtkXPanedClass GtkXPanedClass; +typedef struct _GtkXPanedPrivate GtkXPanedPrivate; + +typedef enum _GtkXPanedChild +{ + GTK_XPANED_TOP_LEFT = 0, + GTK_XPANED_TOP_RIGHT, + GTK_XPANED_BOTTOM_LEFT, + GTK_XPANED_BOTTOM_RIGHT +} GtkXPanedChild; + +struct _GtkXPaned +{ + GtkContainer container; + + GtkWidget* top_left_child; + GtkWidget* top_right_child; + GtkWidget* bottom_left_child; + GtkWidget* bottom_right_child; + + GdkWindow* handle_east; + GdkWindow* handle_west; + GdkWindow* handle_north; + GdkWindow* handle_south; + GdkWindow* handle_middle; + GdkGC* xor_gc; + GdkCursorType cursor_type_east; + GdkCursorType cursor_type_west; + GdkCursorType cursor_type_north; + GdkCursorType cursor_type_south; + GdkCursorType cursor_type_middle; + + /*< private >*/ + GdkRectangle handle_pos_east; + GdkRectangle handle_pos_west; + GdkRectangle handle_pos_north; + GdkRectangle handle_pos_south; + GdkRectangle handle_pos_middle; + GtkRequisition top_left_child_size; + GtkRequisition top_right_child_size; + GtkRequisition bottom_left_child_size; + GtkRequisition bottom_right_child_size; + + GtkRequisition last_allocation; + GdkPoint min_position; + GdkPoint max_position; + gboolean maximized[4]; + + guint position_set : 1; + guint in_drag_vert : 1; + guint in_drag_horiz : 1; + guint in_drag_vert_and_horiz : 1; + guint top_left_child_shrink : 1; + guint top_left_child_resize : 1; + guint top_right_child_shrink : 1; + guint top_right_child_resize : 1; + guint bottom_left_child_shrink : 1; + guint bottom_left_child_resize : 1; + guint bottom_right_child_shrink : 1; + guint bottom_right_child_resize : 1; + guint in_recursion : 1; + guint handle_prelit : 1; + + GtkWidget* last_top_left_child_focus; + GtkWidget* last_top_right_child_focus; + GtkWidget* last_bottom_left_child_focus; + GtkWidget* last_bottom_right_child_focus; + GtkXPanedPrivate* priv; + + GdkPoint drag_pos; + GdkPoint original_position; + GdkPoint unmaximized_position; +}; + +struct _GtkXPanedClass +{ + GtkContainerClass parent_class; + gboolean (*cycle_child_focus) (GtkXPaned* xpaned, gboolean reverse); + gboolean (*toggle_handle_focus) (GtkXPaned* xpaned); + gboolean (*move_handle) (GtkXPaned* xpaned, GtkScrollType scroll); + gboolean (*cycle_handle_focus) (GtkXPaned* xpaned, gboolean reverse); + gboolean (*accept_position) (GtkXPaned* xpaned); + gboolean (*cancel_position) (GtkXPaned* xpaned); +}; + +GType gtk_xpaned_get_type (void) G_GNUC_CONST; +GtkWidget* gtk_xpaned_new (void); +void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_pack_top_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +void gtk_xpaned_pack_top_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +gint gtk_xpaned_get_position_x (GtkXPaned* xpaned); +gint gtk_xpaned_get_position_y (GtkXPaned* xpaned); +void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition); +void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition); +void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned); +void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned); +gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned); +gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned); +gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize); +gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize); +gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize); +gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize); + +/* Internal function */ +#if !defined (GTK_DISABLE_DEPRECATED) || defined (GTK_COMPILATION) +void gtk_xpaned_compute_position (GtkXPaned* xpaned, + const GtkAllocation* allocation, + GtkRequisition* top_left_child_req, + GtkRequisition* top_right_child_req, + GtkRequisition* bottom_left_child_req, + GtkRequisition* bottom_right_child_req); +#endif /* !GTK_DISABLE_DEPRECATED || GTK_COMPILATION */ +#ifndef GTK_DISABLE_DEPRECATED +#define gtk_xpaned_gutter_size(p,s) (void) 0 +#define gtk_xpaned_set_gutter_size(p,s) (void) 0 +#endif /* GTK_DISABLE_DEPRECATED */ + +G_END_DECLS + +#endif /* GTK_XPANED_H */ diff --git a/lib/gtk-contrib/psppire-sheet.c b/lib/gtk-contrib/psppire-sheet.c new file mode 100644 index 00000000..dac561a1 --- /dev/null +++ b/lib/gtk-contrib/psppire-sheet.c @@ -0,0 +1,5465 @@ +/* + Copyright (C) 2006, 2008 Free Software Foundation + + 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 3 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, see . + + + This file is derived from the gtksheet.c and extensively modified for the + requirements of PSPPIRE. The changes are copyright by the + Free Software Foundation. The copyright notice for the original work is + below. +*/ + +/* 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 Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * SECTION:psppiresheet + * @short_description: spreadsheet widget for gtk2 + * + * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of + * cells where you can allocate text. Cell contents can be edited interactively + * through a specially designed entry, GtkItemEntry. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "psppire-sheet.h" +#include +#include +#include +#include + +/* sheet flags */ +enum + { + PSPPIRE_SHEET_IN_XDRAG = 1 << 1, + PSPPIRE_SHEET_IN_YDRAG = 1 << 2, + PSPPIRE_SHEET_IN_DRAG = 1 << 3, + PSPPIRE_SHEET_IN_SELECTION = 1 << 4, + PSPPIRE_SHEET_IN_RESIZE = 1 << 5 + }; + +#define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags) +#define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag)) +#define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag)) + +#define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG) +#define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG) +#define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG) +#define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION) +#define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE) + +#define CELL_SPACING 1 + +#define TIMEOUT_HOVER 300 +#define COLUMN_MIN_WIDTH 10 +#define COLUMN_TITLES_HEIGHT 4 +#define DEFAULT_COLUMN_WIDTH 80 +#define DEFAULT_ROW_HEIGHT 25 + +static void set_entry_widget_font (PsppireSheet *sheet); + +static void psppire_sheet_update_primary_selection (PsppireSheet *sheet); +static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n); +static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n); +static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range); + + +static void set_row_height (PsppireSheet *sheet, + gint row, + gint height); + +static void destroy_hover_window (PsppireSheetHoverTitle *); +static PsppireSheetHoverTitle *create_hover_window (void); + +static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col); + + +static inline void +dispose_string (const PsppireSheet *sheet, gchar *text) +{ + PsppireSheetModel *model = psppire_sheet_get_model (sheet); + + if ( ! model ) + return; + + if (psppire_sheet_model_free_strings (model)) + g_free (text); +} + + +/* FIXME: Why bother with these two ? */ + +/* returns the column index from a pixel location */ +static inline gint +column_from_xpixel (const PsppireSheet *sheet, gint pixel) +{ + return psppire_axis_unit_at_pixel (sheet->haxis, pixel); +} + +static inline gint +row_from_ypixel (const PsppireSheet *sheet, gint pixel) +{ + return psppire_axis_unit_at_pixel (sheet->vaxis, pixel); +} + + +/* Return the lowest row number which is wholly or partially on + the visible range of the sheet */ +static inline glong +min_visible_row (const PsppireSheet *sheet) +{ + return row_from_ypixel (sheet, sheet->vadjustment->value); +} + +static inline glong +min_fully_visible_row (const PsppireSheet *sheet) +{ + glong row = min_visible_row (sheet); + + if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value) + row++; + + return row; +} + +static inline glong +max_visible_row (const PsppireSheet *sheet) +{ + return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size); +} + + +static inline glong +max_fully_visible_row (const PsppireSheet *sheet) +{ + glong row = max_visible_row (sheet); + + if ( psppire_axis_start_pixel (sheet->vaxis, row) + + + psppire_axis_unit_size (sheet->vaxis, row) + > sheet->vadjustment->value) + row--; + + return row; +} + + +/* Returns the lowest column number which is wholly or partially + on the sheet */ +static inline glong +min_visible_column (const PsppireSheet *sheet) +{ + return column_from_xpixel (sheet, sheet->hadjustment->value); +} + +static inline glong +min_fully_visible_column (const PsppireSheet *sheet) +{ + glong col = min_visible_column (sheet); + + if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value) + col++; + + return col; +} + + +/* Returns the highest column number which is wholly or partially + on the sheet */ +static inline glong +max_visible_column (const PsppireSheet *sheet) +{ + return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size); +} + +static inline glong +max_fully_visible_column (const PsppireSheet *sheet) +{ + glong col = max_visible_column (sheet); + + if ( psppire_axis_start_pixel (sheet->haxis, col) + + + psppire_axis_unit_size (sheet->haxis, col) + > sheet->hadjustment->value) + col--; + + return col; +} + + + +/* The size of the region (in pixels) around the row/column boundaries + where the height/width may be grabbed to change size */ +#define DRAG_WIDTH 6 + +static gboolean +on_column_boundary (const PsppireSheet *sheet, gint x, gint *column) +{ + gint col; + gint pixel; + + x += sheet->hadjustment->value; + + if ( x < 0) + return FALSE; + + col = column_from_xpixel (sheet, x); + + pixel = x - DRAG_WIDTH / 2; + if (pixel < 0) + pixel = 0; + + if ( column_from_xpixel (sheet, pixel) < col ) + { + *column = col - 1; + return TRUE; + } + + if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col ) + { + *column = col; + return TRUE; + } + + return FALSE; +} + +static gboolean +on_row_boundary (const PsppireSheet *sheet, gint y, gint *row) +{ + gint r; + gint pixel; + + y += sheet->vadjustment->value; + + if ( y < 0) + return FALSE; + + r = row_from_ypixel (sheet, y); + + pixel = y - DRAG_WIDTH / 2; + if (pixel < 0) + pixel = 0; + + if ( row_from_ypixel (sheet, pixel) < r ) + { + *row = r - 1; + return TRUE; + } + + if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r ) + { + *row = r; + return TRUE; + } + + return FALSE; +} + + +static inline gboolean +POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint ydrag, xdrag; + + /* Can't drag if nothing is selected */ + if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || + sheet->range.col0 < 0 || sheet->range.coli < 0 ) + return FALSE; + + *drag_column = column_from_xpixel (sheet, x); + *drag_row = row_from_ypixel (sheet, y); + + if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 && + x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) + + psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2) + { + ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0); + if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) + { + *drag_row = sheet->range.row0; + return TRUE; + } + ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + + psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi); + if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) + { + *drag_row = sheet->range.rowi; + return TRUE; + } + } + + if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 && + y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + + psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2) + { + xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0); + if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) + { + *drag_column = sheet->range.col0; + return TRUE; + } + xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) + + psppire_axis_unit_size (sheet->haxis, sheet->range.coli); + if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) + { + *drag_column = sheet->range.coli; + return TRUE; + } + } + + return FALSE; +} + +static inline gboolean +POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint xdrag, ydrag; + + /* Can't drag if nothing is selected */ + if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || + sheet->range.col0 < 0 || sheet->range.coli < 0 ) + return FALSE; + + xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+ + psppire_axis_unit_size (sheet->haxis, sheet->range.coli); + + ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + + psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi); + + if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) + ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet)); + + if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) + xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet)); + + *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 gboolean +rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range, + GdkRectangle *r) +{ + g_return_val_if_fail (range, FALSE); + + r->x = psppire_axis_start_pixel (sheet->haxis, range->col0); + r->x -= round (sheet->hadjustment->value); + + r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0); + r->y -= round (sheet->vadjustment->value); + + r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) - + psppire_axis_start_pixel (sheet->haxis, range->col0) + + psppire_axis_unit_size (sheet->haxis, range->coli); + + r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) - + psppire_axis_start_pixel (sheet->vaxis, range->row0) + + psppire_axis_unit_size (sheet->vaxis, range->rowi); + + if ( sheet->column_titles_visible) + { + r->y += sheet->column_title_area.height; + } + + if ( sheet->row_titles_visible) + { + r->x += sheet->row_title_area.width; + } + + return TRUE; +} + +static gboolean +rectangle_from_cell (PsppireSheet *sheet, gint row, gint col, + GdkRectangle *r) +{ + PsppireSheetRange range; + g_return_val_if_fail (row >= 0, FALSE); + g_return_val_if_fail (col >= 0, FALSE); + + range.row0 = range.rowi = row; + range.col0 = range.coli = col; + + return rectangle_from_range (sheet, &range, r); +} + + +static void psppire_sheet_class_init (PsppireSheetClass *klass); +static void psppire_sheet_init (PsppireSheet *sheet); +static void psppire_sheet_dispose (GObject *object); +static void psppire_sheet_finalize (GObject *object); +static void psppire_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void psppire_sheet_realize (GtkWidget *widget); +static void psppire_sheet_unrealize (GtkWidget *widget); +static void psppire_sheet_map (GtkWidget *widget); +static void psppire_sheet_unmap (GtkWidget *widget); +static gint psppire_sheet_expose (GtkWidget *widget, + GdkEventExpose *event); + +static void psppire_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + +static gint psppire_sheet_button_press (GtkWidget *widget, + GdkEventButton *event); +static gint psppire_sheet_button_release (GtkWidget *widget, + GdkEventButton *event); +static gint psppire_sheet_motion (GtkWidget *widget, + GdkEventMotion *event); +static gboolean psppire_sheet_crossing_notify (GtkWidget *widget, + GdkEventCrossing *event); +static gint psppire_sheet_entry_key_press (GtkWidget *widget, + GdkEventKey *key); +static gboolean psppire_sheet_key_press (GtkWidget *widget, + GdkEventKey *key); +static void psppire_sheet_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void psppire_sheet_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + +/* Sheet queries */ + +static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet, + const PsppireSheetRange *range); +static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet, + gint row, gint column); +/* Drawing Routines */ + +/* draw cell */ +static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column); + + +/* draw visible part of range. */ +static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region); + + +/* highlight the visible part of the selected range */ +static void psppire_sheet_range_draw_selection (PsppireSheet *sheet, + PsppireSheetRange range); + +/* Selection */ + +static void psppire_sheet_real_select_range (PsppireSheet *sheet, + const PsppireSheetRange *range); +static void psppire_sheet_real_unselect_range (PsppireSheet *sheet, + const PsppireSheetRange *range); +static void psppire_sheet_extend_selection (PsppireSheet *sheet, + gint row, gint column); +static void psppire_sheet_new_selection (PsppireSheet *sheet, + PsppireSheetRange *range); +static void psppire_sheet_draw_border (PsppireSheet *sheet, + PsppireSheetRange range); + +/* Active Cell handling */ + +static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet); +static void change_active_cell (PsppireSheet *sheet, gint row, gint col); +static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet); +static void psppire_sheet_show_entry_widget (PsppireSheet *sheet); +static gboolean psppire_sheet_click_cell (PsppireSheet *sheet, + gint row, + gint column); + + +/* Scrollbars */ + +static void adjust_scrollbars (PsppireSheet *sheet); +static void vadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data); +static void hadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data); + + +static void draw_xor_vline (PsppireSheet *sheet); +static void draw_xor_hline (PsppireSheet *sheet); +static void draw_xor_rectangle (PsppireSheet *sheet, + PsppireSheetRange range); + +/* Sheet Button */ + +static void create_global_button (PsppireSheet *sheet); +static void global_button_clicked (GtkWidget *widget, + gpointer data); +/* Sheet Entry */ + +static void create_sheet_entry (PsppireSheet *sheet); +static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet); + +/* Sheet button gadgets */ + +static void draw_column_title_buttons (PsppireSheet *sheet); +static void draw_row_title_buttons (PsppireSheet *sheet); + + +static void size_allocate_global_button (PsppireSheet *sheet); +static void psppire_sheet_button_size_request (PsppireSheet *sheet, + const PsppireSheetButton *button, + GtkRequisition *requisition); + +static void psppire_sheet_real_cell_clear (PsppireSheet *sheet, + gint row, + gint column); + + +/* Signals */ +enum + { + SELECT_ROW, + SELECT_COLUMN, + DOUBLE_CLICK_ROW, + DOUBLE_CLICK_COLUMN, + BUTTON_EVENT_ROW, + BUTTON_EVENT_COLUMN, + SELECT_RANGE, + RESIZE_RANGE, + MOVE_RANGE, + TRAVERSE, + ACTIVATE, + LAST_SIGNAL + }; + +static GtkContainerClass *parent_class = NULL; +static guint sheet_signals[LAST_SIGNAL] = { 0 }; + + +GType +psppire_sheet_get_type () +{ + static GType sheet_type = 0; + + if (!sheet_type) + { + static const GTypeInfo sheet_info = + { + sizeof (PsppireSheetClass), + NULL, + NULL, + (GClassInitFunc) psppire_sheet_class_init, + NULL, + NULL, + sizeof (PsppireSheet), + 0, + (GInstanceInitFunc) psppire_sheet_init, + NULL, + }; + + sheet_type = + g_type_register_static (GTK_TYPE_BIN, "PsppireSheet", + &sheet_info, 0); + } + return sheet_type; +} + + + +static PsppireSheetRange* +psppire_sheet_range_copy (const PsppireSheetRange *range) +{ + PsppireSheetRange *new_range; + + g_return_val_if_fail (range != NULL, NULL); + + new_range = g_new (PsppireSheetRange, 1); + + *new_range = *range; + + return new_range; +} + +static void +psppire_sheet_range_free (PsppireSheetRange *range) +{ + g_return_if_fail (range != NULL); + + g_free (range); +} + +GType +psppire_sheet_range_get_type (void) +{ + static GType sheet_range_type = 0; + + if (!sheet_range_type) + { + sheet_range_type = + g_boxed_type_register_static ("PsppireSheetRange", + (GBoxedCopyFunc) psppire_sheet_range_copy, + (GBoxedFreeFunc) psppire_sheet_range_free); + } + + return sheet_range_type; +} + +static PsppireSheetCell* +psppire_sheet_cell_copy (const PsppireSheetCell *cell) +{ + PsppireSheetCell *new_cell; + + g_return_val_if_fail (cell != NULL, NULL); + + new_cell = g_new (PsppireSheetCell, 1); + + *new_cell = *cell; + + return new_cell; +} + +static void +psppire_sheet_cell_free (PsppireSheetCell *cell) +{ + g_return_if_fail (cell != NULL); + + g_free (cell); +} + +GType +psppire_sheet_cell_get_type (void) +{ + static GType sheet_cell_type = 0; + + if (!sheet_cell_type) + { + sheet_cell_type = + g_boxed_type_register_static ("PsppireSheetCell", + (GBoxedCopyFunc) psppire_sheet_cell_copy, + (GBoxedFreeFunc) psppire_sheet_cell_free); + } + + return sheet_cell_type; +} + + +/* Properties */ +enum + { + PROP_0, + PROP_VAXIS, + PROP_HAXIS, + PROP_MODEL + }; + +static void +resize_column (PsppireSheet *sheet, gint unit, glong size) +{ + PsppireSheetRange range; + range.col0 = unit; + range.coli = max_visible_column (sheet); + range.row0 = min_visible_row (sheet); + range.rowi = max_visible_row (sheet); + + redraw_range (sheet, &range); + + draw_column_title_buttons_range (sheet, range.col0, range.coli); +} + + +static void +psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a) +{ + if ( sheet->haxis ) + g_object_unref (sheet->haxis); + + sheet->haxis = a; + g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet); + + if ( sheet->haxis ) + g_object_ref (sheet->haxis); +} + +static void +resize_row (PsppireSheet *sheet, gint unit, glong size) +{ + PsppireSheetRange range; + range.col0 = min_visible_column (sheet); + range.coli = max_visible_column (sheet); + range.row0 = unit; + range.rowi = max_visible_row (sheet); + + redraw_range (sheet, &range); + + draw_row_title_buttons_range (sheet, range.row0, range.rowi); +} + +static void +psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a) +{ + if ( sheet->vaxis ) + g_object_unref (sheet->vaxis); + + sheet->vaxis = a; + + g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet); + + if ( sheet->vaxis ) + g_object_ref (sheet->vaxis); +} + + +static void +psppire_sheet_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + +{ + PsppireSheet *sheet = PSPPIRE_SHEET (object); + + switch (prop_id) + { + case PROP_VAXIS: + psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value)); + break; + case PROP_HAXIS: + psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value)); + break; + case PROP_MODEL: + psppire_sheet_set_model (sheet, g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + +static void +psppire_sheet_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (object); + + switch (prop_id) + { + case PROP_VAXIS: + g_value_set_pointer (value, sheet->vaxis); + break; + case PROP_HAXIS: + g_value_set_pointer (value, sheet->haxis); + break; + case PROP_MODEL: + g_value_set_pointer (value, sheet->model); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + + +static void +psppire_sheet_class_init (PsppireSheetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + GParamSpec *haxis_spec ; + GParamSpec *vaxis_spec ; + GParamSpec *model_spec ; + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + /** + * PsppireSheet::select-row + * @sheet: the sheet widget that emitted the signal + * @row: the newly selected row index + * + * A row has been selected. + */ + sheet_signals[SELECT_ROW] = + g_signal_new ("select-row", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, select_row), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::select - column + * @sheet: the sheet widget that emitted the signal + * @column: the newly selected column index + * + * A column has been selected. + */ + sheet_signals[SELECT_COLUMN] = + g_signal_new ("select-column", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, select_column), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::double-click-row + * @sheet: the sheet widget that emitted the signal + * @row: the row that was double clicked. + * + * A row's title button has been double clicked + */ + sheet_signals[DOUBLE_CLICK_ROW] = + g_signal_new ("double-click-row", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::double-click-column + * @sheet: the sheet widget that emitted the signal + * @column: the column that was double clicked. + * + * A column's title button has been double clicked + */ + sheet_signals[DOUBLE_CLICK_COLUMN] = + g_signal_new ("double-click-column", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::button-event-column + * @sheet: the sheet widget that emitted the signal + * @column: the column on which the event occured. + * + * A button event occured on a column title button + */ + sheet_signals[BUTTON_EVENT_COLUMN] = + g_signal_new ("button-event-column", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + psppire_marshal_VOID__INT_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_POINTER + ); + + + /** + * PsppireSheet::button-event-row + * @sheet: the sheet widget that emitted the signal + * @column: the column on which the event occured. + * + * A button event occured on a row title button + */ + sheet_signals[BUTTON_EVENT_ROW] = + g_signal_new ("button-event-row", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + psppire_marshal_VOID__INT_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_POINTER + ); + + + sheet_signals[SELECT_RANGE] = + g_signal_new ("select-range", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, select_range), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, + PSPPIRE_TYPE_SHEET_RANGE); + + + sheet_signals[RESIZE_RANGE] = + g_signal_new ("resize-range", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, resize_range), + NULL, NULL, + psppire_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, + 2, + PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE + ); + + sheet_signals[MOVE_RANGE] = + g_signal_new ("move-range", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, move_range), + NULL, NULL, + psppire_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, + 2, + PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE + ); + + sheet_signals[TRAVERSE] = + g_signal_new ("traverse", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, traverse), + NULL, NULL, + psppire_marshal_BOOLEAN__BOXED_POINTER, + G_TYPE_BOOLEAN, 2, + PSPPIRE_TYPE_SHEET_CELL, + G_TYPE_POINTER); + + + sheet_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, activate), + NULL, NULL, + psppire_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, G_TYPE_INT, + G_TYPE_INT, G_TYPE_INT); + + widget_class->set_scroll_adjustments_signal = + g_signal_new ("set-scroll-adjustments", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, set_scroll_adjustments), + NULL, NULL, + psppire_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + + + container_class->add = NULL; + container_class->remove = NULL; + container_class->forall = psppire_sheet_forall; + + object_class->dispose = psppire_sheet_dispose; + object_class->finalize = psppire_sheet_finalize; + + + vaxis_spec = + g_param_spec_pointer ("vertical-axis", + "Vertical Axis", + "A pointer to the PsppireAxis object for the rows", + G_PARAM_READABLE | G_PARAM_WRITABLE ); + + haxis_spec = + g_param_spec_pointer ("horizontal-axis", + "Horizontal Axis", + "A pointer to the PsppireAxis object for the columns", + G_PARAM_READABLE | G_PARAM_WRITABLE ); + + model_spec = + g_param_spec_pointer ("model", + "Model", + "A pointer to the data model", + G_PARAM_READABLE | G_PARAM_WRITABLE ); + + + object_class->set_property = psppire_sheet_set_property; + object_class->get_property = psppire_sheet_get_property; + + g_object_class_install_property (object_class, + PROP_VAXIS, + vaxis_spec); + + g_object_class_install_property (object_class, + PROP_HAXIS, + haxis_spec); + + g_object_class_install_property (object_class, + PROP_MODEL, + model_spec); + + + widget_class->realize = psppire_sheet_realize; + widget_class->unrealize = psppire_sheet_unrealize; + widget_class->map = psppire_sheet_map; + widget_class->unmap = psppire_sheet_unmap; + widget_class->style_set = psppire_sheet_style_set; + widget_class->button_press_event = psppire_sheet_button_press; + widget_class->button_release_event = psppire_sheet_button_release; + widget_class->motion_notify_event = psppire_sheet_motion; + widget_class->enter_notify_event = psppire_sheet_crossing_notify; + widget_class->leave_notify_event = psppire_sheet_crossing_notify; + widget_class->key_press_event = psppire_sheet_key_press; + widget_class->expose_event = psppire_sheet_expose; + widget_class->size_request = psppire_sheet_size_request; + widget_class->size_allocate = psppire_sheet_size_allocate; + widget_class->focus_in_event = NULL; + widget_class->focus_out_event = NULL; + + klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments; + klass->select_row = NULL; + klass->select_column = NULL; + klass->select_range = NULL; + klass->resize_range = NULL; + klass->move_range = NULL; + klass->traverse = NULL; + klass->activate = NULL; + klass->changed = NULL; +} + +static void +psppire_sheet_init (PsppireSheet *sheet) +{ + sheet->model = NULL; + sheet->haxis = NULL; + sheet->vaxis = NULL; + + sheet->flags = 0; + sheet->selection_mode = GTK_SELECTION_NONE; + sheet->state = PSPPIRE_SHEET_NORMAL; + + GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW); + GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS); + + 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; + + 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->range.row0 = 0; + sheet->range.rowi = 0; + sheet->range.col0 = 0; + sheet->range.coli = 0; + + sheet->state = PSPPIRE_SHEET_NORMAL; + + sheet->sheet_window = NULL; + sheet->entry_widget = NULL; + sheet->button = NULL; + + sheet->hadjustment = NULL; + sheet->vadjustment = NULL; + + sheet->cursor_drag = NULL; + + sheet->xor_gc = NULL; + sheet->fg_gc = NULL; + sheet->bg_gc = NULL; + sheet->x_drag = 0; + sheet->y_drag = 0; + sheet->show_grid = TRUE; + + sheet->motion_timer = 0; + + sheet->row_titles_visible = TRUE; + sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH; + + sheet->column_titles_visible = TRUE; + + + /* create sheet entry */ + sheet->entry_type = GTK_TYPE_ENTRY; + create_sheet_entry (sheet); + + /* create global selection button */ + create_global_button (sheet); +} + + +/* Cause RANGE to be redrawn. If RANGE is null, then the + entire visible range will be redrawn. + */ +static void +redraw_range (PsppireSheet *sheet, PsppireSheetRange *range) +{ + GdkRectangle rect; + + if ( ! GTK_WIDGET_REALIZED (sheet)) + return; + + if ( NULL != range ) + rectangle_from_range (sheet, range, &rect); + else + { + GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window); + gdk_region_get_clipbox (r, &rect); + + if ( sheet->column_titles_visible) + { + rect.y += sheet->column_title_area.height; + rect.height -= sheet->column_title_area.height; + } + + if ( sheet->row_titles_visible) + { + rect.x += sheet->row_title_area.width; + rect.width -= sheet->row_title_area.width; + } + } + + gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE); +} + + +/* Callback which occurs whenever columns are inserted / deleted in the model */ +static void +columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column, + gint n_columns, + gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + PsppireSheetRange range; + gint model_columns = psppire_sheet_model_get_column_count (model); + + + /* Need to update all the columns starting from the first column and onwards. + * Previous column are unchanged, so don't need to be updated. + */ + range.col0 = first_column; + range.row0 = 0; + range.coli = psppire_axis_unit_count (sheet->haxis) - 1; + range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + + adjust_scrollbars (sheet); + + if (sheet->active_cell.col >= model_columns) + change_active_cell (sheet, sheet->active_cell.row, model_columns - 1); + + draw_column_title_buttons_range (sheet, + first_column, max_visible_column (sheet)); + + + redraw_range (sheet, &range); +} + + + + +/* Callback which occurs whenever rows are inserted / deleted in the model */ +static void +rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row, + gint n_rows, gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + PsppireSheetRange range; + + gint model_rows = psppire_sheet_model_get_row_count (model); + + /* Need to update all the rows starting from the first row and onwards. + * Previous rows are unchanged, so don't need to be updated. + */ + range.row0 = first_row; + range.col0 = 0; + range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + range.coli = psppire_axis_unit_count (sheet->haxis) - 1; + + adjust_scrollbars (sheet); + + if (sheet->active_cell.row >= model_rows) + change_active_cell (sheet, model_rows - 1, sheet->active_cell.col); + + draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet)); + + redraw_range (sheet, &range); +} + +/* + If row0 or rowi are negative, then all rows will be updated. + If col0 or coli are negative, then all columns will be updated. +*/ +static void +range_update_callback (PsppireSheetModel *m, gint row0, gint col0, + gint rowi, gint coli, gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + PsppireSheetRange range; + + range.row0 = row0; + range.col0 = col0; + range.rowi = rowi; + range.coli = coli; + + if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) ) + { + redraw_range (sheet, NULL); + adjust_scrollbars (sheet); + + draw_row_title_buttons_range (sheet, min_visible_row (sheet), + max_visible_row (sheet)); + + draw_column_title_buttons_range (sheet, min_visible_column (sheet), + max_visible_column (sheet)); + + return; + } + else if ( row0 < 0 || rowi < 0 ) + { + range.row0 = min_visible_row (sheet); + range.rowi = max_visible_row (sheet); + } + else if ( col0 < 0 || coli < 0 ) + { + range.col0 = min_visible_column (sheet); + range.coli = max_visible_column (sheet); + } + + redraw_range (sheet, &range); +} + + +/** + * psppire_sheet_new: + * @rows: initial number of rows + * @columns: initial number of columns + * @title: sheet title + * @model: the model to use for the sheet data + * + * Creates a new sheet widget with the given number of rows and columns. + * + * Returns: the new sheet widget + */ +GtkWidget * +psppire_sheet_new (PsppireSheetModel *model) +{ + GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET, + "model", model, + NULL); + return widget; +} + + +/** + * psppire_sheet_set_model + * @sheet: the sheet to set the model for + * @model: the model to use for the sheet data + * + * Sets the model for a PsppireSheet + * + */ +void +psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model) +{ + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (sheet->model ) g_object_unref (sheet->model); + + sheet->model = model; + + if ( model) + { + g_object_ref (model); + + sheet->update_handler_id = g_signal_connect (model, "range_changed", + G_CALLBACK (range_update_callback), + sheet); + + g_signal_connect (model, "rows_inserted", + G_CALLBACK (rows_inserted_deleted_callback), sheet); + + g_signal_connect (model, "rows_deleted", + G_CALLBACK (rows_inserted_deleted_callback), sheet); + + g_signal_connect (model, "columns_inserted", + G_CALLBACK (columns_inserted_deleted_callback), sheet); + + g_signal_connect (model, "columns_deleted", + G_CALLBACK (columns_inserted_deleted_callback), sheet); + } +} + + +void +psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type) +{ + gint state; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + state = sheet->state; + + if (sheet->state == PSPPIRE_SHEET_NORMAL) + psppire_sheet_hide_entry_widget (sheet); + + sheet->entry_type = entry_type; + + create_sheet_entry (sheet); + + if (state == PSPPIRE_SHEET_NORMAL) + { + psppire_sheet_show_entry_widget (sheet); + } + +} + +void +psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (show == sheet->show_grid) return; + + sheet->show_grid = show; + + redraw_range (sheet, NULL); +} + +gboolean +psppire_sheet_grid_visible (PsppireSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + return sheet->show_grid; +} + +guint +psppire_sheet_get_columns_count (PsppireSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + return psppire_axis_unit_count (sheet->haxis); +} + +static void set_column_width (PsppireSheet *sheet, + gint column, + gint width); + + +void +psppire_sheet_show_column_titles (PsppireSheet *sheet) +{ + if (sheet->column_titles_visible) return; + + sheet->column_titles_visible = TRUE; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + 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); + + adjust_scrollbars (sheet); + + if (sheet->vadjustment) + g_signal_emit_by_name (sheet->vadjustment, + "value_changed"); + + size_allocate_global_button (sheet); + + if ( sheet->row_titles_visible) + gtk_widget_show (sheet->button); +} + + +void +psppire_sheet_show_row_titles (PsppireSheet *sheet) +{ + if (sheet->row_titles_visible) return; + + sheet->row_titles_visible = TRUE; + + + 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); + + adjust_scrollbars (sheet); + } + + if (sheet->hadjustment) + g_signal_emit_by_name (sheet->hadjustment, + "value_changed"); + + size_allocate_global_button (sheet); + + if ( sheet->column_titles_visible) + gtk_widget_show (sheet->button); +} + +void +psppire_sheet_hide_column_titles (PsppireSheet *sheet) +{ + if (!sheet->column_titles_visible) return; + + sheet->column_titles_visible = FALSE; + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + { + if (sheet->column_title_window) + gdk_window_hide (sheet->column_title_window); + + gtk_widget_hide (sheet->button); + + adjust_scrollbars (sheet); + } + + if (sheet->vadjustment) + g_signal_emit_by_name (sheet->vadjustment, + "value_changed"); +} + +void +psppire_sheet_hide_row_titles (PsppireSheet *sheet) +{ + if (!sheet->row_titles_visible) return; + + sheet->row_titles_visible = FALSE; + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + { + if (sheet->row_title_window) + gdk_window_hide (sheet->row_title_window); + + gtk_widget_hide (sheet->button); + + adjust_scrollbars (sheet); + } + + if (sheet->hadjustment) + g_signal_emit_by_name (sheet->hadjustment, + "value_changed"); +} + + +/* Scroll the sheet so that the cell ROW, COLUMN is visible. + If {ROW,COL}_ALIGN is zero, then the cell will be placed + at the {top,left} of the sheet. If it's 1, then it'll + be placed at the {bottom,right}. + ROW or COL may be -1, in which case scrolling in that dimension + does not occur. + */ +void +psppire_sheet_moveto (PsppireSheet *sheet, + gint row, + gint col, + gfloat row_align, + gfloat col_align) +{ + gint width, height; + + g_return_if_fail (row_align >= 0); + g_return_if_fail (col_align >= 0); + + g_return_if_fail (row_align <= 1); + g_return_if_fail (col_align <= 1); + + g_return_if_fail (col < + psppire_axis_unit_count (sheet->haxis)); + g_return_if_fail (row < + psppire_axis_unit_count (sheet->vaxis)); + + gdk_drawable_get_size (sheet->sheet_window, &width, &height); + + + if (row >= 0) + { + gint y = psppire_axis_start_pixel (sheet->vaxis, row); + + gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align); + } + + + if (col >= 0) + { + gint x = psppire_axis_start_pixel (sheet->haxis, col); + + gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align); + } +} + + +void +psppire_sheet_select_row (PsppireSheet *sheet, gint row) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->state = PSPPIRE_SHEET_ROW_SELECTED; + sheet->range.row0 = row; + sheet->range.col0 = 0; + sheet->range.rowi = row; + sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1; + sheet->active_cell.row = row; + + g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row); + psppire_sheet_real_select_range (sheet, NULL); +} + + +void +psppire_sheet_select_column (PsppireSheet *sheet, gint column) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis)) + return; + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED; + sheet->range.row0 = 0; + sheet->range.col0 = column; + sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + sheet->range.coli = column; + sheet->active_cell.col = column; + + g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column); + psppire_sheet_real_select_range (sheet, NULL); +} + + + + +static gboolean +psppire_sheet_range_isvisible (const PsppireSheet *sheet, + const PsppireSheetRange *range) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + + if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis)) + return FALSE; + + if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis)) + return FALSE; + + if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis)) + return FALSE; + + if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis)) + 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 gboolean +psppire_sheet_cell_isvisible (PsppireSheet *sheet, + gint row, gint column) +{ + PsppireSheetRange range; + + range.row0 = row; + range.col0 = column; + range.rowi = row; + range.coli = column; + + return psppire_sheet_range_isvisible (sheet, &range); +} + +void +psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_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); +} + + +static gboolean +psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + if ( sheet->vadjustment != vadjustment ) + { + if (sheet->vadjustment) + g_object_unref (sheet->vadjustment); + sheet->vadjustment = vadjustment; + + if ( vadjustment) + { + g_object_ref (vadjustment); + + g_signal_connect (sheet->vadjustment, "value_changed", + G_CALLBACK (vadjustment_value_changed), + sheet); + } + } + + if ( sheet->hadjustment != hadjustment ) + { + if (sheet->hadjustment) + g_object_unref (sheet->hadjustment); + + sheet->hadjustment = hadjustment; + + if ( hadjustment) + { + g_object_ref (hadjustment); + + g_signal_connect (sheet->hadjustment, "value_changed", + G_CALLBACK (hadjustment_value_changed), + sheet); + } + } + return TRUE; +} + +static void +psppire_sheet_finalize (GObject *object) +{ + PsppireSheet *sheet; + + g_return_if_fail (object != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (object)); + + sheet = PSPPIRE_SHEET (object); + + if (G_OBJECT_CLASS (parent_class)->finalize) + (*G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +psppire_sheet_dispose (GObject *object) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (object); + + g_return_if_fail (object != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (object)); + + if ( sheet->dispose_has_run ) + return ; + + sheet->dispose_has_run = TRUE; + + if (sheet->model) g_object_unref (sheet->model); + if (sheet->vaxis) g_object_unref (sheet->vaxis); + if (sheet->haxis) g_object_unref (sheet->haxis); + + g_object_unref (sheet->button); + sheet->button = NULL; + + /* unref adjustments */ + if (sheet->hadjustment) + { + g_signal_handlers_disconnect_matched (sheet->hadjustment, + G_SIGNAL_MATCH_DATA, + 0, 0, 0, 0, + sheet); + + g_object_unref (sheet->hadjustment); + sheet->hadjustment = NULL; + } + + if (sheet->vadjustment) + { + g_signal_handlers_disconnect_matched (sheet->vadjustment, + G_SIGNAL_MATCH_DATA, + 0, 0, 0, 0, + sheet); + + g_object_unref (sheet->vadjustment); + + sheet->vadjustment = NULL; + } + + if (G_OBJECT_CLASS (parent_class)->dispose) + (*G_OBJECT_CLASS (parent_class)->dispose) (object); +} + +static void +psppire_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + PsppireSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + if (GTK_WIDGET_CLASS (parent_class)->style_set) + (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style); + + sheet = PSPPIRE_SHEET (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + gtk_style_set_background (widget->style, widget->window, widget->state); + } + + set_entry_widget_font (sheet); +} + +#define BORDER_WIDTH 3 + +static void +psppire_sheet_realize (GtkWidget *widget) +{ + PsppireSheet *sheet; + GdkWindowAttr attributes; + const gint attributes_mask = + GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR; + + GdkGCValues values; + GdkColormap *colormap; + GdkDisplay *display; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + sheet = PSPPIRE_SHEET (widget); + + colormap = gtk_widget_get_colormap (widget); + display = gtk_widget_get_display (widget); + + 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 = colormap; + + 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_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + + attributes.cursor = gdk_cursor_new_for_display (display, 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); + + gdk_color_parse ("white", &sheet->color[BG_COLOR]); + gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE, + TRUE); + gdk_color_parse ("gray", &sheet->color[GRID_COLOR]); + gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE, + TRUE); + + attributes.x = 0; + 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; + 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_for_display (display, GDK_PLUS); + + attributes.x = 0; + attributes.y = 0; + + sheet->sheet_window = gdk_window_new (widget->window, + &attributes, attributes_mask); + gdk_window_set_user_data (sheet->sheet_window, sheet); + + gdk_cursor_unref (attributes.cursor); + + gdk_window_set_background (sheet->sheet_window, &widget->style->white); + gdk_window_show (sheet->sheet_window); + + /* GCs */ + sheet->fg_gc = gdk_gc_new (widget->window); + sheet->bg_gc = gdk_gc_new (widget->window); + + values.foreground = widget->style->white; + values.function = GDK_INVERT; + values.subwindow_mode = GDK_INCLUDE_INFERIORS; + values.line_width = BORDER_WIDTH; + + sheet->xor_gc = gdk_gc_new_with_values (widget->window, + &values, + GDK_GC_FOREGROUND | + GDK_GC_FUNCTION | + GDK_GC_SUBWINDOW | + GDK_GC_LINE_WIDTH + ); + + + gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); + gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); + + gtk_widget_set_parent_window (sheet->button, sheet->sheet_window); + gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet)); + + + sheet->cursor_drag = gdk_cursor_new_for_display (display, 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); + + sheet->hover_window = create_hover_window (); + + draw_row_title_buttons (sheet); + draw_column_title_buttons (sheet); + + psppire_sheet_update_primary_selection (sheet); + + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); +} + +static void +create_global_button (PsppireSheet *sheet) +{ + sheet->button = gtk_button_new_with_label (" "); + + GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS); + + g_object_ref_sink (sheet->button); + + g_signal_connect (sheet->button, + "pressed", + G_CALLBACK (global_button_clicked), + sheet); +} + +static void +size_allocate_global_button (PsppireSheet *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); +} + +static void +global_button_clicked (GtkWidget *widget, gpointer data) +{ + psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1); +} + + +static void +psppire_sheet_unrealize (GtkWidget *widget) +{ + PsppireSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + sheet = PSPPIRE_SHEET (widget); + + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = NULL; + + gdk_colormap_free_colors (gtk_widget_get_colormap (widget), + sheet->color, n_COLORS); + + g_object_unref (sheet->xor_gc); + g_object_unref (sheet->fg_gc); + g_object_unref (sheet->bg_gc); + + destroy_hover_window (sheet->hover_window); + + gdk_window_destroy (sheet->sheet_window); + gdk_window_destroy (sheet->column_title_window); + gdk_window_destroy (sheet->row_title_window); + + gtk_widget_unparent (sheet->entry_widget); + if (sheet->button != NULL) + gtk_widget_unparent (sheet->button); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +psppire_sheet_map (GtkWidget *widget) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + if (!GTK_WIDGET_MAPPED (widget)) + { + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + + gdk_window_show (widget->window); + gdk_window_show (sheet->sheet_window); + + if (sheet->column_titles_visible) + { + draw_column_title_buttons (sheet); + gdk_window_show (sheet->column_title_window); + } + if (sheet->row_titles_visible) + { + draw_row_title_buttons (sheet); + gdk_window_show (sheet->row_title_window); + } + + if (!GTK_WIDGET_MAPPED (sheet->entry_widget) + && sheet->active_cell.row >= 0 + && sheet->active_cell.col >= 0 ) + { + gtk_widget_show (sheet->entry_widget); + gtk_widget_map (sheet->entry_widget); + } + + if (!GTK_WIDGET_MAPPED (sheet->button)) + { + gtk_widget_show (sheet->button); + gtk_widget_map (sheet->button); + } + + redraw_range (sheet, NULL); + change_active_cell (sheet, + sheet->active_cell.row, + sheet->active_cell.col); + } +} + +static void +psppire_sheet_unmap (GtkWidget *widget) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + if (!GTK_WIDGET_MAPPED (widget)) + return; + + 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->entry_widget)) + gtk_widget_unmap (sheet->entry_widget); + + if (GTK_WIDGET_MAPPED (sheet->button)) + gtk_widget_unmap (sheet->button); +} + + +static void +psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col) +{ + PangoLayout *layout; + PangoRectangle text; + PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc; + gint font_height; + + gchar *label; + + PsppireSheetCellAttr attributes; + GdkRectangle area; + + g_return_if_fail (sheet != NULL); + + /* bail now if we aren't yet drawable */ + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + if (row < 0 || + row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (col < 0 || + col >= psppire_axis_unit_count (sheet->haxis)) + return; + + psppire_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); + + rectangle_from_cell (sheet, row, col, &area); + + gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0); + + if (sheet->show_grid) + { + gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]); + + gdk_draw_rectangle (sheet->sheet_window, + sheet->bg_gc, + FALSE, + area.x, area.y, + area.width, area.height); + } + + + label = psppire_sheet_cell_get_text (sheet, row, col); + if (NULL == label) + return; + + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label); + dispose_string (sheet, label); + + + pango_layout_set_font_description (layout, font_desc); + + pango_layout_get_pixel_extents (layout, NULL, &text); + + gdk_gc_set_clip_rectangle (sheet->fg_gc, &area); + + font_height = pango_font_description_get_size (font_desc); + if ( !pango_font_description_get_size_is_absolute (font_desc)) + font_height /= PANGO_SCALE; + + /* Centre the text vertically */ + area.y += (area.height - font_height) / 2.0; + + switch (attributes.justification) + { + case GTK_JUSTIFY_RIGHT: + area.x += area.width - text.width; + break; + case GTK_JUSTIFY_CENTER: + area.x += (area.width - text.width) / 2.0; + break; + case GTK_JUSTIFY_LEFT: + /* Do nothing */ + break; + default: + g_critical ("Unhandled justification %d in column %d\n", + attributes.justification, col); + break; + } + + gdk_draw_layout (sheet->sheet_window, sheet->fg_gc, + area.x, + area.y, + layout); + + gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL); + g_object_unref (layout); +} + + +static void +draw_sheet_region (PsppireSheet *sheet, GdkRegion *region) +{ + PsppireSheetRange range; + GdkRectangle area; + gint y, x; + gint i, j; + + PsppireSheetRange drawing_range; + + gdk_region_get_clipbox (region, &area); + + y = area.y + sheet->vadjustment->value; + x = area.x + sheet->hadjustment->value; + + if ( sheet->column_titles_visible) + y -= sheet->column_title_area.height; + + if ( sheet->row_titles_visible) + x -= sheet->row_title_area.width; + + maximize_int (&x, 0); + maximize_int (&y, 0); + + range.row0 = row_from_ypixel (sheet, y); + range.rowi = row_from_ypixel (sheet, y + area.height); + + range.col0 = column_from_xpixel (sheet, x); + range.coli = column_from_xpixel (sheet, x + area.width); + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_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; + + + 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)); + + g_return_if_fail (drawing_range.rowi >= drawing_range.row0); + g_return_if_fail (drawing_range.coli >= drawing_range.col0); + + for (i = drawing_range.row0; i <= drawing_range.rowi; i++) + { + for (j = drawing_range.col0; j <= drawing_range.coli; j++) + psppire_sheet_cell_draw (sheet, i, j); + } + + if (sheet->state != PSPPIRE_SHEET_NORMAL && + psppire_sheet_range_isvisible (sheet, &sheet->range)) + psppire_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) + psppire_sheet_show_entry_widget (sheet); +} + + +static void +psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range) +{ + GdkRectangle area; + gint i, j; + PsppireSheetRange aux; + + if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 || + range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0) + return; + + if (!psppire_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 (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED) + { + rectangle_from_cell (sheet, i, j, &area); + + 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); + } + } + + } + } + + psppire_sheet_draw_border (sheet, sheet->range); +} + +static inline gint +safe_strcmp (const gchar *s1, const gchar *s2) +{ + if ( !s1 && !s2) return 0; + if ( !s1) return -1; + if ( !s2) return +1; + return strcmp (s1, s2); +} + +static void +psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col, + GtkJustification justification, + const gchar *text) +{ + PsppireSheetModel *model ; + gchar *old_text ; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (col >= psppire_axis_unit_count (sheet->haxis) + || row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (col < 0 || row < 0) return; + + model = psppire_sheet_get_model (sheet); + + old_text = psppire_sheet_model_get_string (model, row, col); + + if (0 != safe_strcmp (old_text, text)) + { + g_signal_handler_block (sheet->model, sheet->update_handler_id); + psppire_sheet_model_set_string (model, text, row, col); + g_signal_handler_unblock (sheet->model, sheet->update_handler_id); + } + + if ( psppire_sheet_model_free_strings (model)) + g_free (old_text); +} + + +void +psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + if (column >= psppire_axis_unit_count (sheet->haxis) || + row >= psppire_axis_unit_count (sheet->vaxis)) return; + + if (column < 0 || row < 0) return; + + range.row0 = row; + range.rowi = row; + range.col0 = min_visible_column (sheet); + range.coli = max_visible_column (sheet); + + psppire_sheet_real_cell_clear (sheet, row, column); + + redraw_range (sheet, &range); +} + +static void +psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetModel *model = psppire_sheet_get_model (sheet); + + gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column); + + if (old_text && strlen (old_text) > 0 ) + { + psppire_sheet_model_datum_clear (model, row, column); + } + + dispose_string (sheet, old_text); +} + +gchar * +psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col) +{ + PsppireSheetModel *model; + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); + + if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) + return NULL; + if (col < 0 || row < 0) return NULL; + + model = psppire_sheet_get_model (sheet); + + if ( !model ) + return NULL; + + return psppire_sheet_model_get_string (model, row, col); +} + + +static GtkStateType +psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col) +{ + gint state; + PsppireSheetRange *range; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0; + if (col < 0 || row < 0) return 0; + + state = sheet->state; + range = &sheet->range; + + switch (state) + { + case PSPPIRE_SHEET_NORMAL: + return GTK_STATE_NORMAL; + break; + case PSPPIRE_SHEET_ROW_SELECTED: + if (row >= range->row0 && row <= range->rowi) + return GTK_STATE_SELECTED; + break; + case PSPPIRE_SHEET_COLUMN_SELECTED: + if (col >= range->col0 && col <= range->coli) + return GTK_STATE_SELECTED; + break; + case PSPPIRE_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; +} + +/* Convert X, Y (in pixels) to *ROW, *COLUMN + If the function returns FALSE, then the results will be unreliable. +*/ +static gboolean +psppire_sheet_get_pixel_info (PsppireSheet *sheet, + gint x, + gint y, + gint *row, + gint *column) +{ + gint trow, tcol; + *row = -G_MAXINT; + *column = -G_MAXINT; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + /* bounds checking, return false if the user clicked + on a blank area */ + if (y < 0) + return FALSE; + + if (x < 0) + return FALSE; + + if ( sheet->column_titles_visible) + y -= sheet->column_title_area.height; + + y += sheet->vadjustment->value; + + if ( y < 0 && sheet->column_titles_visible) + { + trow = -1; + } + else + { + trow = row_from_ypixel (sheet, y); + if (trow > psppire_axis_unit_count (sheet->vaxis)) + return FALSE; + } + + *row = trow; + + if ( sheet->row_titles_visible) + x -= sheet->row_title_area.width; + + x += sheet->hadjustment->value; + + if ( x < 0 && sheet->row_titles_visible) + { + tcol = -1; + } + else + { + tcol = column_from_xpixel (sheet, x); + if (tcol > psppire_axis_unit_count (sheet->haxis)) + return FALSE; + } + + *column = tcol; + + return TRUE; +} + +gboolean +psppire_sheet_get_cell_area (PsppireSheet *sheet, + gint row, + gint column, + GdkRectangle *area) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis)) + return FALSE; + + area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column); + area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row); + + area->width= (column == -1) ? sheet->row_title_area.width + : psppire_axis_unit_size (sheet->haxis, column); + + area->height= (row == -1) ? sheet->column_title_area.height + : psppire_axis_unit_size (sheet->vaxis, row); + + return TRUE; +} + +void +psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < -1 || col < -1) + return; + + if (row >= psppire_axis_unit_count (sheet->vaxis) + || + col >= psppire_axis_unit_count (sheet->haxis)) + return; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + if ( row == -1 || col == -1) + { + psppire_sheet_hide_entry_widget (sheet); + return; + } + + change_active_cell (sheet, row, col); +} + +void +psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if ( row ) *row = sheet->active_cell.row; + if (column) *column = sheet->active_cell.col; +} + +static void +entry_load_text (PsppireSheet *sheet) +{ + gint row, col; + const char *text; + GtkJustification justification; + PsppireSheetCellAttr attributes; + + if (!GTK_WIDGET_VISIBLE (sheet->entry_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; + + text = gtk_entry_get_text (psppire_sheet_get_entry (sheet)); + + if (text && strlen (text) > 0) + { + psppire_sheet_get_attributes (sheet, row, col, &attributes); + justification = attributes.justification; + psppire_sheet_set_cell (sheet, row, col, justification, text); + } +} + + +static void +psppire_sheet_hide_entry_widget (PsppireSheet *sheet) +{ + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + if (sheet->active_cell.row < 0 || + sheet->active_cell.col < 0) return; + + gtk_widget_hide (sheet->entry_widget); + gtk_widget_unmap (sheet->entry_widget); + + GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); +} + +static void +change_active_cell (PsppireSheet *sheet, gint row, gint col) +{ + gint old_row, old_col; + + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < 0 || col < 0) + return; + + if ( row > psppire_axis_unit_count (sheet->vaxis) + || col > psppire_axis_unit_count (sheet->haxis)) + return; + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + { + sheet->state = PSPPIRE_SHEET_NORMAL; + psppire_sheet_real_unselect_range (sheet, NULL); + } + + old_row = sheet->active_cell.row; + old_col = sheet->active_cell.col; + + /* Erase the old cell */ + psppire_sheet_draw_active_cell (sheet); + + entry_load_text (sheet); + + 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; + + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS); + + psppire_sheet_draw_active_cell (sheet); + psppire_sheet_show_entry_widget (sheet); + + GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS); + + g_signal_emit (sheet, sheet_signals [ACTIVATE], 0, + row, col, old_row, old_col); + +} + +static void +psppire_sheet_show_entry_widget (PsppireSheet *sheet) +{ + GtkEntry *sheet_entry; + PsppireSheetCellAttr attributes; + + gint row, col; + + g_return_if_fail (PSPPIRE_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 != PSPPIRE_SHEET_NORMAL) return; + if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return; + + GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); + + sheet_entry = psppire_sheet_get_entry (sheet); + + psppire_sheet_get_attributes (sheet, row, col, &attributes); + + if (GTK_IS_ENTRY (sheet_entry)) + { + gchar *text = psppire_sheet_cell_get_text (sheet, row, col); + const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry)); + + if ( ! text ) + text = g_strdup (""); + + if (strcmp (old_text, text) != 0) + gtk_entry_set_text (sheet_entry, text); + + dispose_string (sheet, text); + + { + switch (attributes.justification) + { + case GTK_JUSTIFY_RIGHT: + gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0); + break; + case GTK_JUSTIFY_CENTER: + gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5); + break; + case GTK_JUSTIFY_LEFT: + default: + gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0); + break; + } + } + } + + psppire_sheet_size_allocate_entry (sheet); + + gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry), + psppire_sheet_model_is_editable (sheet->model, + row, col)); + gtk_widget_map (sheet->entry_widget); +} + +static gboolean +psppire_sheet_draw_active_cell (PsppireSheet *sheet) +{ + gint row, col; + PsppireSheetRange range; + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + if (row < 0 || col < 0) return FALSE; + + if (!psppire_sheet_cell_isvisible (sheet, row, col)) + return FALSE; + + range.col0 = range.coli = col; + range.row0 = range.rowi = row; + + psppire_sheet_draw_border (sheet, range); + + return FALSE; +} + + + +static void +psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range) +{ + gint i, j, mask1, mask2; + gint state, selected; + gint x, y, width, height; + PsppireSheetRange 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 = psppire_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 && + (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 = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j)- x+ + psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + 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; + + if (i != sheet->active_cell.row || j != sheet->active_cell.col) + { + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j)- x+ + psppire_axis_unit_size (sheet->haxis, j); + + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + 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 = psppire_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) + { + + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + 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; + + } + } + } + + for (i = range->row0; i <= range->rowi; i++) + { + for (j = range->col0; j <= range->coli; j++) + { + + state = psppire_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 && + (i != sheet->active_cell.row || j != sheet->active_cell.col)) + { + + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + 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++) + { + state = psppire_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 = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_unit_size (sheet->vaxis, i); + 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; +} + + + +static void +psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range) +{ + GdkRectangle area; + + rectangle_from_range (sheet, &new_range, &area); + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + FALSE, + area.x, area.y, + area.width, area.height); +} + + +static void +psppire_sheet_real_select_range (PsppireSheet *sheet, + const PsppireSheetRange *range) +{ + gint state; + + g_return_if_fail (sheet != NULL); + + if (range == NULL) range = &sheet->range; + + memcpy (&sheet->range, range, sizeof (*range)); + + if (range->row0 < 0 || range->rowi < 0) return; + if (range->col0 < 0 || range->coli < 0) return; + + state = sheet->state; + +#if 0 + if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 || + range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0) + { + psppire_sheet_new_selection (sheet, &sheet->range); + } + else + { + psppire_sheet_range_draw_selection (sheet, sheet->range); + } +#endif + + psppire_sheet_update_primary_selection (sheet); + + g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range); +} + + +void +psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + *range = sheet->range; +} + + +void +psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *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 != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + 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 = PSPPIRE_SHEET_RANGE_SELECTED; + psppire_sheet_real_select_range (sheet, NULL); +} + +void +psppire_sheet_unselect_range (PsppireSheet *sheet) +{ + if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + psppire_sheet_real_unselect_range (sheet, NULL); + sheet->state = GTK_STATE_NORMAL; + + change_active_cell (sheet, + sheet->active_cell.row, sheet->active_cell.col); +} + + +static void +psppire_sheet_real_unselect_range (PsppireSheet *sheet, + const PsppireSheetRange *range) +{ + 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; + + g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1); + g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1); + + sheet->range.row0 = -1; + sheet->range.rowi = -1; + sheet->range.col0 = -1; + sheet->range.coli = -1; +} + + +static gint +psppire_sheet_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + g_return_val_if_fail (event != NULL, FALSE); + + if (!GTK_WIDGET_DRAWABLE (widget)) + return FALSE; + + /* exposure events on the sheet */ + if (event->window == sheet->row_title_window && + sheet->row_titles_visible) + { + draw_row_title_buttons_range (sheet, + min_visible_row (sheet), + max_visible_row (sheet)); + } + + if (event->window == sheet->column_title_window && + sheet->column_titles_visible) + { + draw_column_title_buttons_range (sheet, + min_visible_column (sheet), + max_visible_column (sheet)); + } + + if (event->window == sheet->sheet_window) + { + draw_sheet_region (sheet, event->region); + +#if 0 + if (sheet->state != PSPPIRE_SHEET_NORMAL) + { + if (psppire_sheet_range_isvisible (sheet, &sheet->range)) + psppire_sheet_range_draw (sheet, &sheet->range); + + if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet)) + psppire_sheet_range_draw (sheet, &sheet->drag_range); + + if (psppire_sheet_range_isvisible (sheet, &sheet->range)) + psppire_sheet_range_draw_selection (sheet, sheet->range); + if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet)) + draw_xor_rectangle (sheet, sheet->drag_range); + } +#endif + + if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet))) + { + GdkRectangle rect; + PsppireSheetRange range; + range.row0 = range.rowi = sheet->active_cell.row; + range.col0 = range.coli = sheet->active_cell.col; + + rectangle_from_range (sheet, &range, &rect); + + if (GDK_OVERLAP_RECTANGLE_OUT != + gdk_region_rect_in (event->region, &rect)) + { + psppire_sheet_draw_active_cell (sheet); + } + } + + } + + (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); + + return FALSE; +} + + +static gboolean +psppire_sheet_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + PsppireSheet *sheet; + GdkModifierType mods; + gint x, y; + gint row, column; + gboolean veto; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + sheet = PSPPIRE_SHEET (widget); + + /* Cancel any pending tooltips */ + if (sheet->motion_timer) + { + g_source_remove (sheet->motion_timer); + sheet->motion_timer = 0; + } + + gtk_widget_get_pointer (widget, &x, &y); + psppire_sheet_get_pixel_info (sheet, x, y, &row, &column); + + + if (event->window == sheet->column_title_window) + { + sheet->x_drag = event->x; + g_signal_emit (sheet, + sheet_signals[BUTTON_EVENT_COLUMN], 0, + column, event); + + if (psppire_sheet_model_get_column_sensitivity (sheet->model, column)) + { + if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) + g_signal_emit (sheet, + sheet_signals[DOUBLE_CLICK_COLUMN], 0, column); + } + } + else if (event->window == sheet->row_title_window) + { + g_signal_emit (sheet, + sheet_signals[BUTTON_EVENT_ROW], 0, + row, event); + + if (psppire_sheet_model_get_row_sensitivity (sheet->model, row)) + { + if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) + g_signal_emit (sheet, + sheet_signals[DOUBLE_CLICK_ROW], 0, row); + } + } + + gdk_window_get_pointer (widget->window, NULL, NULL, &mods); + + if (! (mods & GDK_BUTTON1_MASK)) return TRUE; + + + /* press on resize windows */ + if (event->window == sheet->column_title_window) + { + sheet->x_drag = event->x; + + if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col)) + { + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_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) + { + sheet->y_drag = event->y; + + if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row)) + { + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_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; + } + } + + /* the sheet itself does not handle other than single click events */ + if (event->type != GDK_BUTTON_PRESS) return FALSE; + + /* selections on the sheet */ + if (event->window == sheet->sheet_window) + { + gtk_widget_get_pointer (widget, &x, &y); + psppire_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)); + + if (sheet->selection_mode != GTK_SELECTION_SINGLE && + sheet->selection_mode != GTK_SELECTION_NONE && + sheet->cursor_drag->type == GDK_SIZING && + !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet)) + { + if (sheet->state == GTK_STATE_NORMAL) + { + row = sheet->active_cell.row; + column = sheet->active_cell.col; + sheet->active_cell.row = row; + sheet->active_cell.col = column; + sheet->drag_range = sheet->range; + sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + psppire_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); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE); + } + else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW && + !PSPPIRE_SHEET_IN_SELECTION (sheet) + && ! PSPPIRE_SHEET_IN_DRAG (sheet) + && sheet->active_cell.row >= 0 + && sheet->active_cell.col >= 0 + ) + { + if (sheet->state == GTK_STATE_NORMAL) + { + row = sheet->active_cell.row; + column = sheet->active_cell.col; + sheet->active_cell.row = row; + sheet->active_cell.col = column; + sheet->drag_range = sheet->range; + sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + psppire_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); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG); + } + else + { + veto = psppire_sheet_click_cell (sheet, row, column); + if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + } + } + + if (event->window == sheet->column_title_window) + { + gtk_widget_get_pointer (widget, &x, &y); + if ( sheet->row_titles_visible) + x -= sheet->row_title_area.width; + + x += sheet->hadjustment->value; + + column = column_from_xpixel (sheet, x); + + if (psppire_sheet_model_get_column_sensitivity (sheet->model, column)) + { + veto = psppire_sheet_click_cell (sheet, -1, column); + gtk_grab_add (GTK_WIDGET (sheet)); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + } + } + + if (event->window == sheet->row_title_window) + { + gtk_widget_get_pointer (widget, &x, &y); + if ( sheet->column_titles_visible) + y -= sheet->column_title_area.height; + + y += sheet->vadjustment->value; + + row = row_from_ypixel (sheet, y); + if (psppire_sheet_model_get_row_sensitivity (sheet->model, row)) + { + veto = psppire_sheet_click_cell (sheet, row, -1); + gtk_grab_add (GTK_WIDGET (sheet)); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + } + } + + return TRUE; +} + +static gboolean +psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetCell cell; + gboolean forbid_move; + + cell.row = row; + cell.col = column; + + if (row >= psppire_axis_unit_count (sheet->vaxis) + || column >= psppire_axis_unit_count (sheet->haxis)) + { + return FALSE; + } + + g_signal_emit (sheet, sheet_signals[TRAVERSE], 0, + &sheet->active_cell, + &cell, + &forbid_move); + + if (forbid_move) + { + if (sheet->state == GTK_STATE_NORMAL) + return FALSE; + + row = sheet->active_cell.row; + column = sheet->active_cell.col; + + change_active_cell (sheet, row, column); + return FALSE; + } + + if (row == -1 && column >= 0) + { + psppire_sheet_select_column (sheet, column); + return TRUE; + } + + if (column == -1 && row >= 0) + { + psppire_sheet_select_row (sheet, row); + return TRUE; + } + + if (row == -1 && column == -1) + { + sheet->range.row0 = 0; + sheet->range.col0 = 0; + sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + sheet->range.coli = + psppire_axis_unit_count (sheet->haxis) - 1; + sheet->active_cell.row = 0; + sheet->active_cell.col = 0; + psppire_sheet_select_range (sheet, NULL); + return TRUE; + } + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + { + sheet->state = PSPPIRE_SHEET_NORMAL; + psppire_sheet_real_unselect_range (sheet, NULL); + } + else + { + change_active_cell (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 = PSPPIRE_SHEET_NORMAL; + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget)); + + return TRUE; +} + +static gint +psppire_sheet_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GdkDisplay *display = gtk_widget_get_display (widget); + + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + /* release on resize windows */ + if (PSPPIRE_SHEET_IN_XDRAG (sheet)) + { + gint width; + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + gdk_display_pointer_ungrab (display, event->time); + draw_xor_vline (sheet); + + width = event->x - + psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col) + + sheet->hadjustment->value; + + set_column_width (sheet, sheet->drag_cell.col, width); + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_YDRAG (sheet)) + { + gint height; + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + gdk_display_pointer_ungrab (display, event->time); + draw_xor_hline (sheet); + + height = event->y - + psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) + + sheet->vadjustment->value; + + set_row_height (sheet, sheet->drag_cell.row, height); + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_DRAG (sheet)) + { + PsppireSheetRange old_range; + draw_xor_rectangle (sheet, sheet->drag_range); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG); + gdk_display_pointer_ungrab (display, event->time); + + psppire_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; + g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0, + &sheet->drag_range, &sheet->range); + psppire_sheet_select_range (sheet, &sheet->range); + } + + if (PSPPIRE_SHEET_IN_RESIZE (sheet)) + { + PsppireSheetRange old_range; + draw_xor_rectangle (sheet, sheet->drag_range); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE); + gdk_display_pointer_ungrab (display, event->time); + + psppire_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 = PSPPIRE_SHEET_RANGE_SELECTED; + g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0, + &sheet->drag_range, &sheet->range); + psppire_sheet_select_range (sheet, &sheet->range); + } + + if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet)) + { + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + gdk_display_pointer_ungrab (display, event->time); + change_active_cell (sheet, sheet->active_cell.row, + sheet->active_cell.col); + } + + if (PSPPIRE_SHEET_IN_SELECTION) + gdk_display_pointer_ungrab (display, event->time); + gtk_grab_remove (GTK_WIDGET (sheet)); + + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + return TRUE; +} + + + + + +/* Shamelessly lifted from gtktooltips */ +static gboolean +psppire_sheet_subtitle_paint_window (GtkWidget *tip_window) +{ + GtkRequisition req; + + gtk_widget_size_request (tip_window, &req); + gtk_paint_flat_box (tip_window->style, tip_window->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, GTK_WIDGET(tip_window), "tooltip", + 0, 0, req.width, req.height); + + return FALSE; +} + +static void +destroy_hover_window (PsppireSheetHoverTitle *h) +{ + gtk_widget_destroy (h->window); + g_free (h); +} + +static PsppireSheetHoverTitle * +create_hover_window (void) +{ + PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw)); + + hw->window = gtk_window_new (GTK_WINDOW_POPUP); + +#if GTK_CHECK_VERSION (2, 9, 0) + gtk_window_set_type_hint (GTK_WINDOW (hw->window), + GDK_WINDOW_TYPE_HINT_TOOLTIP); +#endif + + gtk_widget_set_app_paintable (hw->window, TRUE); + gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE); + gtk_widget_set_name (hw->window, "gtk-tooltips"); + gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4); + + g_signal_connect (hw->window, + "expose_event", + G_CALLBACK (psppire_sheet_subtitle_paint_window), + NULL); + + hw->label = gtk_label_new (NULL); + + + gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE); + gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5); + + gtk_container_add (GTK_CONTAINER (hw->window), hw->label); + + gtk_widget_show (hw->label); + + g_signal_connect (hw->window, + "destroy", + G_CALLBACK (gtk_widget_destroyed), + &hw->window); + + return hw; +} + +#define HOVER_WINDOW_Y_OFFSET 2 + +static void +show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle) +{ + gint x, y; + gint px, py; + gint width; + + if ( ! subtitle ) + return; + + gtk_label_set_text (GTK_LABEL (sheet->hover_window->label), + subtitle); + + + sheet->hover_window->row = row; + sheet->hover_window->column = column; + + gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y); + + gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py); + + gtk_widget_show (sheet->hover_window->window); + + width = GTK_WIDGET (sheet->hover_window->label)->allocation.width; + + if (row == -1 ) + { + x += px; + x -= width / 2; + y += sheet->column_title_area.y; + y += sheet->column_title_area.height; + y += HOVER_WINDOW_Y_OFFSET; + } + + if ( column == -1 ) + { + y += py; + x += sheet->row_title_area.x; + x += sheet->row_title_area.width * 2 / 3.0; + } + + gtk_window_move (GTK_WINDOW (sheet->hover_window->window), + x, y); +} + +static gboolean +motion_timeout_callback (gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + gint x, y; + gint row, column; + gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y); + + if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) ) + { + if (sheet->row_title_under && row >= 0) + { + gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row); + + show_subtitle (sheet, row, -1, text); + g_free (text); + } + + if (sheet->column_title_under && column >= 0) + { + gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model, + column); + + show_subtitle (sheet, -1, column, text); + + g_free (text); + } + } + + return FALSE; +} + +static gboolean +psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + GdkModifierType mods; + GdkCursorType new_cursor; + gint x, y; + gint row, column; + GdkDisplay *display; + + g_return_val_if_fail (event != NULL, FALSE); + + display = gtk_widget_get_display (widget); + + /* selections on the sheet */ + x = event->x; + y = event->y; + + if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window)) + { + if ( sheet->motion_timer > 0 ) + g_source_remove (sheet->motion_timer); + sheet->motion_timer = + g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet); + } + else + { + gint row, column; + gint wx, wy; + gtk_widget_get_pointer (widget, &wx, &wy); + + if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) ) + { + if ( row != sheet->hover_window->row || + column != sheet->hover_window->column) + { + gtk_widget_hide (sheet->hover_window->window); + } + } + } + + if (event->window == sheet->column_title_window) + { + if (!PSPPIRE_SHEET_IN_SELECTION (sheet) && + on_column_boundary (sheet, x, &column)) + { + new_cursor = GDK_SB_H_DOUBLE_ARROW; + if (new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + + gdk_window_set_cursor (sheet->column_title_window, + sheet->cursor_drag); + } + } + else + { + new_cursor = GDK_TOP_LEFT_ARROW; + if (!PSPPIRE_SHEET_IN_XDRAG (sheet) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + gdk_window_set_cursor (sheet->column_title_window, + sheet->cursor_drag); + } + } + } + else if (event->window == sheet->row_title_window) + { + if (!PSPPIRE_SHEET_IN_SELECTION (sheet) && + on_row_boundary (sheet, y, &row)) + { + new_cursor = GDK_SB_V_DOUBLE_ARROW; + if (new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + gdk_window_set_cursor (sheet->row_title_window, + sheet->cursor_drag); + } + } + else + { + new_cursor = GDK_TOP_LEFT_ARROW; + if (!PSPPIRE_SHEET_IN_YDRAG (sheet) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + gdk_window_set_cursor (sheet->row_title_window, + sheet->cursor_drag); + } + } + } + + new_cursor = GDK_PLUS; + if ( event->window == sheet->sheet_window && + !POSSIBLE_DRAG (sheet, x, y, &row, &column) && + !PSPPIRE_SHEET_IN_DRAG (sheet) && + !POSSIBLE_RESIZE (sheet, x, y, &row, &column) && + !PSPPIRE_SHEET_IN_RESIZE (sheet) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS); + gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); + } + + new_cursor = GDK_TOP_LEFT_ARROW; + if ( event->window == sheet->sheet_window && + ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || + PSPPIRE_SHEET_IN_RESIZE (sheet)) && + (POSSIBLE_DRAG (sheet, x, y, &row, &column) || + PSPPIRE_SHEET_IN_DRAG (sheet)) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); + } + + new_cursor = GDK_SIZING; + if ( event->window == sheet->sheet_window && + sheet->selection_mode != GTK_SELECTION_NONE && + !PSPPIRE_SHEET_IN_DRAG (sheet) && + (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || + PSPPIRE_SHEET_IN_RESIZE (sheet)) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = gdk_cursor_new_for_display (display, 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 (PSPPIRE_SHEET_IN_XDRAG (sheet)) + { + if (event->x != sheet->x_drag) + { + draw_xor_vline (sheet); + sheet->x_drag = event->x; + draw_xor_vline (sheet); + } + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_YDRAG (sheet)) + { + if (event->y != sheet->y_drag) + { + draw_xor_hline (sheet); + sheet->y_drag = event->y; + draw_xor_hline (sheet); + } + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_DRAG (sheet)) + { + PsppireSheetRange aux; + column = column_from_xpixel (sheet, x)- sheet->drag_cell.col; + row = row_from_ypixel (sheet, y) - sheet->drag_cell.row; + if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; + if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0; + sheet->x_drag = x; + sheet->y_drag = y; + aux = sheet->range; + if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) && + aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis)) + { + 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 (PSPPIRE_SHEET_IN_RESIZE (sheet)) + { + PsppireSheetRange aux; + gint v_h, current_col, current_row, col_threshold, row_threshold; + v_h = 1; + if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) > + abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2; + + current_col = column_from_xpixel (sheet, x); + current_row = row_from_ypixel (sheet, y); + column = current_col - sheet->drag_cell.col; + row = current_row - sheet->drag_cell.row; + + /*use half of column width resp. row height as threshold to + expand selection*/ + col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) + + psppire_axis_unit_size (sheet->haxis, current_col) / 2; + if (column > 0) + { + if (x < col_threshold) + column -= 1; + } + else if (column < 0) + { + if (x > col_threshold) + column +=1; + } + row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) + + psppire_axis_unit_size (sheet->vaxis, current_row)/2; + if (row > 0) + { + if (y < row_threshold) + row -= 1; + } + else if (row < 0) + { + if (y > row_threshold) + row +=1; + } + + if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; + if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0; + sheet->x_drag = x; + sheet->y_drag = y; + aux = sheet->range; + + if (v_h == 1) + column = 0; + else + row = 0; + + if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) && + aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis)) + { + 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; + } + + psppire_sheet_get_pixel_info (sheet, x, y, &row, &column); + + if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row && + column == sheet->active_cell.col) return TRUE; + + if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK) + psppire_sheet_extend_selection (sheet, row, column); + + return TRUE; +} + +static gboolean +psppire_sheet_crossing_notify (GtkWidget *widget, + GdkEventCrossing *event) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + if (event->window == sheet->column_title_window) + sheet->column_title_under = event->type == GDK_ENTER_NOTIFY; + else if (event->window == sheet->row_title_window) + sheet->row_title_under = event->type == GDK_ENTER_NOTIFY; + + return TRUE; +} + +static void +psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetRange 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_widget_grab_focus (GTK_WIDGET (sheet)); + + if (PSPPIRE_SHEET_IN_DRAG (sheet)) return; + + state = sheet->state; + + switch (sheet->state) + { + case PSPPIRE_SHEET_ROW_SELECTED: + column = psppire_axis_unit_count (sheet->haxis) - 1; + break; + case PSPPIRE_SHEET_COLUMN_SELECTED: + row = psppire_axis_unit_count (sheet->vaxis) - 1; + break; + case PSPPIRE_SHEET_NORMAL: + sheet->state = PSPPIRE_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; + psppire_sheet_range_draw_selection (sheet, sheet->range); + case PSPPIRE_SHEET_RANGE_SELECTED: + sheet->state = PSPPIRE_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 == PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_select_range (sheet, &range); + +} + +static gint +psppire_sheet_entry_key_press (GtkWidget *widget, + GdkEventKey *key) +{ + gboolean focus; + g_signal_emit_by_name (widget, "key_press_event", key, &focus); + return focus; +} + + +/* Number of rows in a step-increment */ +#define ROWS_PER_STEP 1 + + +static void +page_vertical (PsppireSheet *sheet, GtkScrollType dir) +{ + gint old_row = sheet->active_cell.row ; + glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row); + + gint new_row; + + vpixel -= psppire_axis_start_pixel (sheet->vaxis, + min_visible_row (sheet)); + + switch ( dir) + { + case GTK_SCROLL_PAGE_DOWN: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->value + + sheet->vadjustment->page_increment); + break; + case GTK_SCROLL_PAGE_UP: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->value - + sheet->vadjustment->page_increment); + + break; + default: + g_assert_not_reached (); + break; + } + + + vpixel += psppire_axis_start_pixel (sheet->vaxis, + min_visible_row (sheet)); + + new_row = row_from_ypixel (sheet, vpixel); + + change_active_cell (sheet, new_row, + sheet->active_cell.col); +} + + +static void +step_sheet (PsppireSheet *sheet, GtkScrollType dir) +{ + gint current_row = sheet->active_cell.row; + gint current_col = sheet->active_cell.col; + PsppireSheetCell new_cell ; + gboolean forbidden = FALSE; + + new_cell.row = current_row; + new_cell.col = current_col; + + switch ( dir) + { + case GTK_SCROLL_STEP_DOWN: + new_cell.row++; + break; + case GTK_SCROLL_STEP_UP: + new_cell.row--; + break; + case GTK_SCROLL_STEP_RIGHT: + new_cell.col++; + break; + case GTK_SCROLL_STEP_LEFT: + new_cell.col--; + break; + default: + g_assert_not_reached (); + break; + } + + + g_signal_emit (sheet, sheet_signals[TRAVERSE], 0, + &sheet->active_cell, + &new_cell, + &forbidden); + + if (forbidden) + return; + + + maximize_int (&new_cell.row, 0); + maximize_int (&new_cell.col, 0); + + minimize_int (&new_cell.row, + psppire_axis_unit_count (sheet->vaxis) - 1); + + minimize_int (&new_cell.col, + psppire_axis_unit_count (sheet->haxis) - 1); + + change_active_cell (sheet, new_cell.row, new_cell.col); + + + if ( new_cell.col > max_fully_visible_column (sheet)) + { + glong hpos = + psppire_axis_start_pixel (sheet->haxis, + new_cell.col + 1); + hpos -= sheet->hadjustment->page_size; + + gtk_adjustment_set_value (sheet->hadjustment, + hpos); + } + else if ( new_cell.col < min_fully_visible_column (sheet)) + { + glong hpos = + psppire_axis_start_pixel (sheet->haxis, + new_cell.col); + + gtk_adjustment_set_value (sheet->hadjustment, + hpos); + } + + + if ( new_cell.row > max_fully_visible_row (sheet)) + { + glong vpos = + psppire_axis_start_pixel (sheet->vaxis, + new_cell.row + 1); + vpos -= sheet->vadjustment->page_size; + + gtk_adjustment_set_value (sheet->vadjustment, + vpos); + } + else if ( new_cell.row < min_fully_visible_row (sheet)) + { + glong vpos = + psppire_axis_start_pixel (sheet->vaxis, + new_cell.row); + + gtk_adjustment_set_value (sheet->vadjustment, + vpos); + } + + gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget)); +} + + +static gboolean +psppire_sheet_key_press (GtkWidget *widget, + GdkEventKey *key) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + switch (key->keyval) + { + case GDK_Tab: + case GDK_Right: + step_sheet (sheet, GTK_SCROLL_STEP_RIGHT); + break; + case GDK_ISO_Left_Tab: + case GDK_Left: + step_sheet (sheet, GTK_SCROLL_STEP_LEFT); + break; + case GDK_Return: + case GDK_Down: + step_sheet (sheet, GTK_SCROLL_STEP_DOWN); + break; + case GDK_Up: + step_sheet (sheet, GTK_SCROLL_STEP_UP); + break; + + case GDK_Page_Down: + page_vertical (sheet, GTK_SCROLL_PAGE_DOWN); + break; + case GDK_Page_Up: + page_vertical (sheet, GTK_SCROLL_PAGE_UP); + break; + + case GDK_Home: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->lower); + + change_active_cell (sheet, 0, + sheet->active_cell.col); + + break; + + case GDK_End: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->upper - + sheet->vadjustment->page_size - + sheet->vadjustment->page_increment); + + /* + change_active_cellx (sheet, + psppire_axis_unit_count (sheet->vaxis) - 1, + sheet->active_cell.col); + */ + break; + case GDK_Delete: + psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col); + break; + default: + return FALSE; + break; + } + + return TRUE; +} + +static void +psppire_sheet_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + PsppireSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + g_return_if_fail (requisition != NULL); + + sheet = PSPPIRE_SHEET (widget); + + requisition->width = 3 * DEFAULT_COLUMN_WIDTH; + requisition->height = 3 * DEFAULT_ROW_HEIGHT; + + /* 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; +} + + +static void +psppire_sheet_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + PsppireSheet *sheet; + GtkAllocation sheet_allocation; + gint border_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + g_return_if_fail (allocation != NULL); + + sheet = PSPPIRE_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); + + sheet_allocation.x = 0; + sheet_allocation.y = 0; + sheet_allocation.width = allocation->width - 2 * border_width; + sheet_allocation.height = allocation->height - 2 * border_width; + + 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; + sheet->column_title_area.width = sheet_allocation.width ; + + + /* position the window which holds the row title buttons */ + sheet->row_title_area.x = 0; + sheet->row_title_area.y = 0; + sheet->row_title_area.height = sheet_allocation.height; + + if (sheet->row_titles_visible) + sheet->column_title_area.x += sheet->row_title_area.width; + + if (sheet->column_titles_visible) + sheet->row_title_area.y += sheet->column_title_area.height; + + 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); + + + 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); + + size_allocate_global_button (sheet); + + if (sheet->haxis) + { + gint width = sheet->column_title_area.width; + + if ( sheet->row_titles_visible) + width -= sheet->row_title_area.width; + + g_object_set (sheet->haxis, + "minimum-extent", width, + NULL); + } + + + if (sheet->vaxis) + { + gint height = sheet->row_title_area.height; + + if ( sheet->column_titles_visible) + height -= sheet->column_title_area.height; + + g_object_set (sheet->vaxis, + "minimum-extent", height, + NULL); + } + + + /* set the scrollbars adjustments */ + adjust_scrollbars (sheet); +} + +static void +draw_column_title_buttons (PsppireSheet *sheet) +{ + gint x, width; + + if (!sheet->column_titles_visible) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + gdk_drawable_get_size (sheet->sheet_window, &width, NULL); + x = 0; + + if (sheet->row_titles_visible) + { + 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) == + psppire_axis_unit_count (sheet->haxis) - 1) + 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; + + draw_column_title_buttons_range (sheet, min_visible_column (sheet), + max_visible_column (sheet)); +} + +static void +draw_row_title_buttons (PsppireSheet *sheet) +{ + gint y = 0; + gint height; + + if (!sheet->row_titles_visible) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + gdk_drawable_get_size (sheet->sheet_window, NULL, &height); + + if (sheet->column_titles_visible) + { + 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) == psppire_axis_unit_count (sheet->vaxis) - 1) + 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; + + draw_row_title_buttons_range (sheet, min_visible_row (sheet), + max_visible_row (sheet)); +} + + +static void +psppire_sheet_size_allocate_entry (PsppireSheet *sheet) +{ + GtkAllocation entry_alloc; + PsppireSheetCellAttr attributes = { 0 }; + GtkEntry *sheet_entry; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return; + + sheet_entry = psppire_sheet_get_entry (sheet); + + if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row, + sheet->active_cell.col, + &attributes) ) + return ; + + if ( GTK_WIDGET_REALIZED (sheet->entry_widget) ) + { + GtkStyle *style = GTK_WIDGET (sheet_entry)->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; + } + + rectangle_from_cell (sheet, sheet->active_cell.row, + sheet->active_cell.col, &entry_alloc); + + entry_alloc.width -= BORDER_WIDTH ; + entry_alloc.height -= BORDER_WIDTH ; + entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2); + entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2); + + + gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width, + entry_alloc.height); + gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc); +} + + +/* Copy the sheet's font to the entry widget */ +static void +set_entry_widget_font (PsppireSheet *sheet) +{ + GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget); + + pango_font_description_free (style->font_desc); + style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc); + + gtk_widget_modify_style (sheet->entry_widget, style); +} + + + +static void +create_sheet_entry (PsppireSheet *sheet) +{ + if (sheet->entry_widget) + { + gtk_widget_unparent (sheet->entry_widget); + } + + sheet->entry_widget = g_object_new (sheet->entry_type, NULL); + g_object_ref_sink (sheet->entry_widget); + + gtk_widget_size_request (sheet->entry_widget, NULL); + + if ( GTK_IS_ENTRY (sheet->entry_widget)) + { + g_object_set (sheet->entry_widget, + "has-frame", FALSE, + NULL); + } + + if (GTK_WIDGET_REALIZED (sheet)) + { + gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); + gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); + gtk_widget_realize (sheet->entry_widget); + } + + g_signal_connect_swapped (sheet->entry_widget, "key_press_event", + G_CALLBACK (psppire_sheet_entry_key_press), + sheet); + + set_entry_widget_font (sheet); + + gtk_widget_show (sheet->entry_widget); +} + + +/* Finds the last child widget that happens to be of type GtkEntry */ +static void +find_entry (GtkWidget *w, gpointer user_data) +{ + GtkWidget **entry = user_data; + if ( GTK_IS_ENTRY (w)) + { + *entry = w; + } +} + + +GtkEntry * +psppire_sheet_get_entry (PsppireSheet *sheet) +{ + GtkWidget *w = sheet->entry_widget; + + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); + g_return_val_if_fail (sheet->entry_widget != NULL, NULL); + + while (! GTK_IS_ENTRY (w)) + { + GtkWidget *entry = NULL; + + if (GTK_IS_CONTAINER (w)) + { + gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry); + + if (NULL == entry) + break; + + w = entry; + } + } + + return GTK_ENTRY (w); +} + + +static void +draw_button (PsppireSheet *sheet, GdkWindow *window, + PsppireSheetButton *button, gboolean is_sensitive, + GdkRectangle allocation) +{ + GtkShadowType shadow_type; + gint text_width = 0, text_height = 0; + PangoAlignment align = PANGO_ALIGN_LEFT; + + gboolean rtl ; + + gint state = 0; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (button != NULL); + + + rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL; + + gdk_window_clear_area (window, + allocation.x, allocation.y, + allocation.width, allocation.height); + + gtk_widget_ensure_style (sheet->button); + + gtk_paint_box (sheet->button->style, window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + &allocation, GTK_WIDGET (sheet->button), + "buttondefault", + allocation.x, allocation.y, + allocation.width, allocation.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), + "button", + allocation.x, allocation.y, + allocation.width, allocation.height); + + if (button->label_visible) + { + text_height = DEFAULT_ROW_HEIGHT - + 2 * COLUMN_TITLES_HEIGHT; + + 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); + + allocation.y += 2 * sheet->button->style->ythickness; + + if (button->label && strlen (button->label) > 0) + { + PangoRectangle rect; + gchar *line = button->label; + + PangoLayout *layout = NULL; + gint real_x = allocation.x; + gint real_y = allocation.y; + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line); + pango_layout_get_extents (layout, NULL, &rect); + + text_width = PANGO_PIXELS (rect.width); + switch (button->justification) + { + case GTK_JUSTIFY_LEFT: + real_x = allocation.x + COLUMN_TITLES_HEIGHT; + align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; + break; + case GTK_JUSTIFY_RIGHT: + real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT; + align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT; + break; + case GTK_JUSTIFY_CENTER: + default: + real_x = allocation.x + (allocation.width - text_width)/2; + align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; + pango_layout_set_justify (layout, TRUE); + } + pango_layout_set_alignment (layout, align); + gtk_paint_layout (GTK_WIDGET (sheet)->style, + window, + state, + FALSE, + &allocation, + GTK_WIDGET (sheet), + "label", + real_x, real_y, + layout); + g_object_unref (layout); + } + + 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); + + } + + psppire_sheet_button_free (button); +} + + +/* Draw the column title buttons FIRST through to LAST */ +static void +draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last) +{ + GdkRectangle rect; + gint col; + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + + if (!sheet->column_titles_visible) return; + + g_return_if_fail (first >= min_visible_column (sheet)); + g_return_if_fail (last <= max_visible_column (sheet)); + + rect.y = 0; + rect.height = sheet->column_title_area.height; + rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING; + rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING + + psppire_axis_unit_size (sheet->haxis, last); + + rect.x -= sheet->hadjustment->value; + + minimize_int (&rect.width, sheet->column_title_area.width); + maximize_int (&rect.x, 0); + + gdk_window_begin_paint_rect (sheet->column_title_window, &rect); + + for (col = first ; col <= last ; ++col) + { + GdkRectangle allocation; + gboolean is_sensitive = FALSE; + + PsppireSheetButton * + button = psppire_sheet_model_get_column_button (sheet->model, col); + allocation.y = 0; + allocation.x = psppire_axis_start_pixel (sheet->haxis, col) + + CELL_SPACING; + allocation.x -= sheet->hadjustment->value; + + allocation.height = sheet->column_title_area.height; + allocation.width = psppire_axis_unit_size (sheet->haxis, col); + is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col); + + draw_button (sheet, sheet->column_title_window, + button, is_sensitive, allocation); + } + + gdk_window_end_paint (sheet->column_title_window); +} + + +static void +draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last) +{ + GdkRectangle rect; + gint row; + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + + if (!sheet->row_titles_visible) return; + + g_return_if_fail (first >= min_visible_row (sheet)); + g_return_if_fail (last <= max_visible_row (sheet)); + + rect.x = 0; + rect.width = sheet->row_title_area.width; + rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING; + rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING + + psppire_axis_unit_size (sheet->vaxis, last); + + rect.y -= sheet->vadjustment->value; + + minimize_int (&rect.height, sheet->row_title_area.height); + maximize_int (&rect.y, 0); + + gdk_window_begin_paint_rect (sheet->row_title_window, &rect); + for (row = first; row <= last; ++row) + { + GdkRectangle allocation; + + gboolean is_sensitive = FALSE; + + PsppireSheetButton *button = + psppire_sheet_model_get_row_button (sheet->model, row); + allocation.x = 0; + allocation.y = psppire_axis_start_pixel (sheet->vaxis, row) + + CELL_SPACING; + allocation.y -= sheet->vadjustment->value; + + allocation.width = sheet->row_title_area.width; + allocation.height = psppire_axis_unit_size (sheet->vaxis, row); + is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row); + + draw_button (sheet, sheet->row_title_window, + button, is_sensitive, allocation); + } + + gdk_window_end_paint (sheet->row_title_window); +} + +/* SCROLLBARS + * + * functions: + * adjust_scrollbars + * vadjustment_value_changed + * hadjustment_value_changed */ + + +static void +update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size) +{ + double position = + (adj->value + adj->page_size) + / + (adj->upper - adj->lower); + + const glong last_item = psppire_axis_unit_count (axis) - 1; + + if (isnan (position) || position < 0) + position = 0; + + adj->upper = + psppire_axis_start_pixel (axis, last_item) + + + psppire_axis_unit_size (axis, last_item) + ; + + adj->lower = 0; + adj->page_size = page_size; + +#if 0 + adj->value = position * (adj->upper - adj->lower) - adj->page_size; + + if ( adj->value < adj->lower) + adj->value = adj->lower; +#endif + + gtk_adjustment_changed (adj); +} + + +static void +adjust_scrollbars (PsppireSheet *sheet) +{ + gint width, height; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + gdk_drawable_get_size (sheet->sheet_window, &width, &height); + + if ( sheet->row_titles_visible) + width -= sheet->row_title_area.width; + + if (sheet->column_titles_visible) + height -= sheet->column_title_area.height; + + if (sheet->vadjustment) + { + glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1; + + sheet->vadjustment->step_increment = + ROWS_PER_STEP * + psppire_axis_unit_size (sheet->vaxis, last_row); + + sheet->vadjustment->page_increment = + height - + sheet->column_title_area.height - + psppire_axis_unit_size (sheet->vaxis, last_row); + + update_adjustment (sheet->vadjustment, sheet->vaxis, height); + } + + if (sheet->hadjustment) + { + gint last_col = psppire_axis_unit_count (sheet->haxis) - 1; + sheet->hadjustment->step_increment = 1; + + sheet->hadjustment->page_increment = width; + + sheet->hadjustment->upper = + psppire_axis_start_pixel (sheet->haxis, last_col) + + + psppire_axis_unit_size (sheet->haxis, last_col) + ; + + update_adjustment (sheet->hadjustment, sheet->haxis, width); + } +} + +/* Subtracts the region of WIDGET from REGION */ +static void +subtract_widget_region (GdkRegion *region, GtkWidget *widget) +{ + GdkRectangle rect; + GdkRectangle intersect; + GdkRegion *region2; + + gdk_region_get_clipbox (region, &rect); + gtk_widget_intersect (widget, + &rect, + &intersect); + + region2 = gdk_region_rectangle (&intersect); + gdk_region_subtract (region, region2); + gdk_region_destroy (region2); +} + +static void +vadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GdkRegion *region; + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + g_return_if_fail (adjustment != NULL); + + if ( ! GTK_WIDGET_REALIZED (sheet)) return; + + gtk_widget_hide (sheet->entry_widget); + + region = + gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window)); + + subtract_widget_region (region, sheet->button); + gdk_window_begin_paint_region (sheet->sheet_window, region); + + draw_sheet_region (sheet, region); + + draw_row_title_buttons (sheet); + psppire_sheet_draw_active_cell (sheet); + + gdk_window_end_paint (sheet->sheet_window); + gdk_region_destroy (region); +} + + +static void +hadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GdkRegion *region; + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + g_return_if_fail (adjustment != NULL); + + if ( ! GTK_WIDGET_REALIZED (sheet)) return; + + gtk_widget_hide (sheet->entry_widget); + + + region = + gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window)); + + subtract_widget_region (region, sheet->button); + gdk_window_begin_paint_region (sheet->sheet_window, region); + + draw_sheet_region (sheet, region); + + draw_column_title_buttons (sheet); + + psppire_sheet_draw_active_cell (sheet); + + gdk_window_end_paint (sheet->sheet_window); + + gdk_region_destroy (region); +} + + +/* COLUMN RESIZING */ +static void +draw_xor_vline (PsppireSheet *sheet) +{ + gint height; + gint xpos = sheet->x_drag; + gdk_drawable_get_size (sheet->sheet_window, + NULL, &height); + + if (sheet->row_titles_visible) + xpos += sheet->row_title_area.width; + + gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc, + xpos, + sheet->column_title_area.height, + xpos, + height + CELL_SPACING); +} + +/* ROW RESIZING */ +static void +draw_xor_hline (PsppireSheet *sheet) + +{ + gint width; + gint ypos = sheet->y_drag; + + gdk_drawable_get_size (sheet->sheet_window, + &width, NULL); + + + if (sheet->column_titles_visible) + ypos += sheet->column_title_area.height; + + gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc, + sheet->row_title_area.width, + ypos, + width + CELL_SPACING, + ypos); +} + +/* SELECTED RANGE */ +static void +draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range) +{ + gint i = 0; + GdkRectangle clip_area, area; + GdkGCValues values; + + area.x = psppire_axis_start_pixel (sheet->haxis, range.col0); + area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0); + area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+ + psppire_axis_unit_size (sheet->haxis, range.coli); + area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y + + psppire_axis_unit_size (sheet->vaxis, range.rowi); + + clip_area.x = sheet->row_title_area.width; + clip_area.y = sheet->column_title_area.height; + + gdk_drawable_get_size (sheet->sheet_window, + &clip_area.width, &clip_area.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); + + 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); +} + + +static void +set_column_width (PsppireSheet *sheet, + gint column, + gint width) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis)) + return; + + if ( width <= 0) + return; + + psppire_axis_resize (sheet->haxis, column, width); + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + { + draw_column_title_buttons (sheet); + adjust_scrollbars (sheet); + psppire_sheet_size_allocate_entry (sheet); + redraw_range (sheet, NULL); + } +} + +static void +set_row_height (PsppireSheet *sheet, + gint row, + gint height) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (height <= 0) + return; + + psppire_axis_resize (sheet->vaxis, row, height); + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) ) + { + draw_row_title_buttons (sheet); + adjust_scrollbars (sheet); + psppire_sheet_size_allocate_entry (sheet); + redraw_range (sheet, NULL); + } +} + +gboolean +psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col, + PsppireSheetCellAttr *attr) +{ + GdkColor *fg, *bg; + const GtkJustification *j ; + GdkColormap *colormap; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE); + + if (row < 0 || col < 0) return FALSE; + + attr->foreground = GTK_WIDGET (sheet)->style->black; + attr->background = sheet->color[BG_COLOR]; + + attr->border.width = 0; + attr->border.line_style = GDK_LINE_SOLID; + attr->border.cap_style = GDK_CAP_NOT_LAST; + attr->border.join_style = GDK_JOIN_MITER; + attr->border.mask = 0; + attr->border.color = GTK_WIDGET (sheet)->style->black; + + attr->is_editable = psppire_sheet_model_is_editable (sheet->model, row, col); + + colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet)); + fg = psppire_sheet_model_get_foreground (sheet->model, row, col); + if ( fg ) + { + gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE); + attr->foreground = *fg; + } + + bg = psppire_sheet_model_get_background (sheet->model, row, col); + if ( bg ) + { + gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE); + attr->background = *bg; + } + + attr->justification = + psppire_sheet_model_get_column_justification (sheet->model, col); + + j = psppire_sheet_model_get_justification (sheet->model, row, col); + if (j) + attr->justification = *j; + + return TRUE; +} + +static void +psppire_sheet_button_size_request (PsppireSheet *sheet, + const PsppireSheetButton *button, + GtkRequisition *button_requisition) +{ + GtkRequisition requisition; + GtkRequisition label_requisition; + + label_requisition.height = DEFAULT_ROW_HEIGHT; + label_requisition.width = COLUMN_MIN_WIDTH; + + requisition.height = DEFAULT_ROW_HEIGHT; + 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 +psppire_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (container); + + g_return_if_fail (callback != NULL); + + if (sheet->button && sheet->button->parent) + (* callback) (sheet->button, callback_data); + + if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget)) + (* callback) (sheet->entry_widget, callback_data); +} + + +PsppireSheetModel * +psppire_sheet_get_model (const PsppireSheet *sheet) +{ + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); + + return sheet->model; +} + + +PsppireSheetButton * +psppire_sheet_button_new (void) +{ + PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton)); + + button->state = GTK_STATE_NORMAL; + button->label = NULL; + button->label_visible = TRUE; + button->justification = GTK_JUSTIFY_FILL; + + return button; +} + + +void +psppire_sheet_button_free (PsppireSheetButton *button) +{ + if (!button) return ; + + g_free (button->label); + g_free (button); +} + +static void +append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c) +{ + gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c); + + if ( NULL == celltext) + return; + + g_string_append (string, celltext); + g_free (celltext); +} + + +static GString * +range_to_text (const PsppireSheet *sheet) +{ + gint r, c; + GString *string; + + if ( !psppire_sheet_range_isvisible (sheet, &sheet->range)) + return NULL; + + string = g_string_sized_new (80); + + for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) + { + for (c = sheet->range.col0; c < sheet->range.coli; ++c) + { + append_cell_text (string, sheet, r, c); + g_string_append (string, "\t"); + } + append_cell_text (string, sheet, r, c); + if ( r < sheet->range.rowi) + g_string_append (string, "\n"); + } + + return string; +} + +static GString * +range_to_html (const PsppireSheet *sheet) +{ + gint r, c; + GString *string; + + if ( !psppire_sheet_range_isvisible (sheet, &sheet->range)) + return NULL; + + string = g_string_sized_new (480); + + g_string_append (string, "\n"); + g_string_append (string, "\n"); + g_string_append (string, "\n"); + for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) + { + g_string_append (string, "\n"); + for (c = sheet->range.col0; c <= sheet->range.coli; ++c) + { + g_string_append (string, "\n"); + } + g_string_append (string, "\n"); + } + g_string_append (string, "
"); + append_cell_text (string, sheet, r, c); + g_string_append (string, "
\n"); + g_string_append (string, "\n"); + g_string_append (string, "\n"); + + return string; +} + +enum { + SELECT_FMT_NULL, + SELECT_FMT_TEXT, + SELECT_FMT_HTML +}; + +static void +primary_get_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + GString *string = NULL; + + switch (info) + { + case SELECT_FMT_TEXT: + string = range_to_text (sheet); + break; + case SELECT_FMT_HTML: + string = range_to_html (sheet); + break; + default: + g_assert_not_reached (); + } + + gtk_selection_data_set (selection_data, selection_data->target, + 8, + (const guchar *) string->str, string->len); + g_string_free (string, TRUE); +} + +static void +primary_clear_cb (GtkClipboard *clipboard, + gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + psppire_sheet_real_unselect_range (sheet, NULL); +} + +static void +psppire_sheet_update_primary_selection (PsppireSheet *sheet) +{ + static const GtkTargetEntry targets[] = { + { "UTF8_STRING", 0, SELECT_FMT_TEXT }, + { "STRING", 0, SELECT_FMT_TEXT }, + { "TEXT", 0, SELECT_FMT_TEXT }, + { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT }, + { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT }, + { "text/plain", 0, SELECT_FMT_TEXT }, + { "text/html", 0, SELECT_FMT_HTML } + }; + + GtkClipboard *clipboard; + + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet), + GDK_SELECTION_PRIMARY); + + if (psppire_sheet_range_isvisible (sheet, &sheet->range)) + { + if (!gtk_clipboard_set_with_owner (clipboard, targets, + G_N_ELEMENTS (targets), + primary_get_cb, primary_clear_cb, + G_OBJECT (sheet))) + primary_clear_cb (clipboard, sheet); + } + else + { + if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet)) + gtk_clipboard_clear (clipboard); + } +} diff --git a/lib/gtk-contrib/psppire-sheet.h b/lib/gtk-contrib/psppire-sheet.h new file mode 100644 index 00000000..82de87be --- /dev/null +++ b/lib/gtk-contrib/psppire-sheet.h @@ -0,0 +1,320 @@ +/* + Copyright (C) 2006, 2008 Free Software Foundation + + 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 3 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, see . + + + This file is derived from the gtksheet.c and extensively modified for the + requirements of PSPPIRE. The changes are copyright by the + Free Software Foundation. The copyright notice for the original work is + below. + + + 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 Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __PSPPIRE_SHEET_H__ +#define __PSPPIRE_SHEET_H__ + +#include + +#include "gtkextra-sheet.h" +#include +#include + +G_BEGIN_DECLS + +/* sheet->state */ +enum +{ + PSPPIRE_SHEET_NORMAL, + PSPPIRE_SHEET_ROW_SELECTED, + PSPPIRE_SHEET_COLUMN_SELECTED, + PSPPIRE_SHEET_RANGE_SELECTED +}; + + +#define PSPPIRE_TYPE_SHEET_RANGE (psppire_sheet_range_get_type ()) +#define PSPPIRE_TYPE_SHEET_CELL (psppire_sheet_cell_get_type ()) +#define PSPPIRE_TYPE_SHEET (psppire_sheet_get_type ()) + +#define PSPPIRE_SHEET(obj) GTK_CHECK_CAST (obj, psppire_sheet_get_type (), PsppireSheet) +#define PSPPIRE_SHEET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, psppire_sheet_get_type (), PsppireSheetClass) +#define PSPPIRE_IS_SHEET(obj) GTK_CHECK_TYPE (obj, psppire_sheet_get_type ()) + + +typedef struct _PsppireSheetClass PsppireSheetClass; +typedef struct _PsppireSheetCellAttr PsppireSheetCellAttr; + +typedef struct _PsppireSheetHoverTitle PsppireSheetHoverTitle; + + +struct _PsppireSheetCellAttr +{ + GtkJustification justification; + GdkColor foreground; + GdkColor background; + PsppireSheetCellBorder border; + gboolean is_editable; +}; + +struct _PsppireSheetHoverTitle +{ + GtkWidget *window; + GtkWidget *label; + gint row, column; +}; + +enum + { + BG_COLOR, + GRID_COLOR, + n_COLORS + }; + +struct _PsppireSheet +{ + GtkBin parent; + + gboolean dispose_has_run; + PsppireAxis *haxis; + PsppireAxis *vaxis; + + guint16 flags; + + PsppireSheetModel *model; + + GtkSelectionMode selection_mode; + + /* Component colors */ + GdkColor color[n_COLORS]; + gboolean show_grid; + + /* active cell */ + PsppireSheetCell active_cell; + + /* The GtkEntry used for editing the cells */ + GtkWidget *entry_widget; + + /* The type of entry_widget */ + GtkType entry_type; + + /* expanding selection */ + PsppireSheetCell selection_cell; + + /* global selection button */ + GtkWidget *button; + + /* sheet state */ + gint state; + + /* selected range */ + PsppireSheetRange range; + + /* the scrolling window and its height and width to + * make things a little speedier */ + GdkWindow *sheet_window; + + /* border shadow style */ + GtkShadowType shadow_type; + + /* Column Titles */ + GdkRectangle column_title_area; + GdkWindow *column_title_window; + gboolean column_titles_visible; + /* TRUE if the cursor is over the column title window */ + gboolean column_title_under; + + /* Row Titles */ + GdkRectangle row_title_area; + GdkWindow *row_title_window; + gboolean row_titles_visible; + /* TRUE if the cursor is over the row title window */ + gboolean row_title_under; + + /*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 */ + PsppireSheetCell drag_cell; + /* current range being dragged */ + PsppireSheetRange drag_range; + + /* Used for the subtitle (popups) */ + gint motion_timer; + PsppireSheetHoverTitle *hover_window; + + gulong update_handler_id; +}; + +struct _PsppireSheetClass +{ + GtkBinClass parent_class; + + gboolean (*set_scroll_adjustments) (PsppireSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + + void (*select_row) (PsppireSheet *sheet, gint row); + + void (*select_column) (PsppireSheet *sheet, gint column); + + void (*select_range) (PsppireSheet *sheet, PsppireSheetRange *range); + + void (*resize_range) (PsppireSheet *sheet, + PsppireSheetRange *old_range, + PsppireSheetRange *new_range); + + void (*move_range) (PsppireSheet *sheet, + PsppireSheetRange *old_range, + PsppireSheetRange *new_range); + + gboolean (*traverse) (PsppireSheet *sheet, + gint row, gint column, + gint *new_row, gint *new_column); + + gboolean (*activate) (PsppireSheet *sheet, + gint row, gint column); + + void (*changed) (PsppireSheet *sheet, + gint row, gint column); +}; + +GType psppire_sheet_get_type (void); +GtkType psppire_sheet_range_get_type (void); + + +/* create a new sheet */ +GtkWidget * psppire_sheet_new (PsppireSheetModel *model); + +/* create a new sheet with custom entry */ +GtkWidget * +psppire_sheet_new_with_custom_entry (GtkType entry_type); + +/* Change entry */ +void psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type); + +GtkEntry *psppire_sheet_get_entry (PsppireSheet *sheet); + + +void psppire_sheet_get_selected_range (PsppireSheet *sheet, + PsppireSheetRange *range); + +void psppire_sheet_show_grid (PsppireSheet *sheet, + gboolean show); + +gboolean psppire_sheet_grid_visible (PsppireSheet *sheet); + + +/* 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 screen, 0.0 being top or left, + * 1.0 being bottom or right; if row or column is negative then there + * is no change */ +void psppire_sheet_moveto (PsppireSheet *sheet, + gint row, + gint column, + gfloat row_align, + gfloat col_align); + + +void psppire_sheet_show_row_titles (PsppireSheet *sheet); +void psppire_sheet_hide_row_titles (PsppireSheet *sheet); +void psppire_sheet_show_column_titles (PsppireSheet *sheet); +void psppire_sheet_hide_column_titles (PsppireSheet *sheet); + +/* select the row. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void psppire_sheet_select_row (PsppireSheet * sheet, gint row); + +/* select the column. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void psppire_sheet_select_column (PsppireSheet * sheet, gint column); + +/* highlight the selected range and store bounds in sheet->range */ +void psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range); + +void psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range); + + +/* obvious */ +void psppire_sheet_unselect_range (PsppireSheet *sheet); + +/* set active cell where the entry will be displayed */ +void psppire_sheet_set_active_cell (PsppireSheet *sheet, + gint row, gint column); + +/* Sets *ROW and *COLUMN to be the coordinates of the active cell. + ROW and/or COLUMN may be null if the caller is not interested in their + values */ +void psppire_sheet_get_active_cell (PsppireSheet *sheet, + gint *row, gint *column); + +/* get cell contents */ +gchar *psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col); + + +/* get cell attributes of the given cell */ +/* TRUE means that the cell is currently allocated */ +gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet, + gint row, gint col, + PsppireSheetCellAttr *attributes); + +void psppire_sheet_set_model (PsppireSheet *sheet, + PsppireSheetModel *model); + +PsppireSheetModel * psppire_sheet_get_model (const PsppireSheet *sheet); + + +G_END_DECLS + + +#endif /* __PSPPIRE_SHEET_H__ */ + + diff --git a/lib/gtksheet/COPYING.LESSER b/lib/gtksheet/COPYING.LESSER deleted file mode 100644 index 8add30ad..00000000 --- a/lib/gtksheet/COPYING.LESSER +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, 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 library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete 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 distribute a copy of this License along with the -Library. - - 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 Library or any portion -of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -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 Library, 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 Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you 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. - - If distribution of 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 satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be 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. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library 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. - - 9. 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 Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -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 with -this License. - - 11. 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 Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library 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 Library. - -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. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library 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. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser 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 Library -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 Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -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 - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "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 -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. 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 LIBRARY 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 -LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/lib/gtksheet/OChangeLog b/lib/gtksheet/OChangeLog deleted file mode 100644 index b5ba1c82..00000000 --- a/lib/gtksheet/OChangeLog +++ /dev/null @@ -1,149 +0,0 @@ -2008-05-08 Ben Pfaff - - Patch #6506. Reviewed by John Darrington. - - * gtksheet.c (gtk_sheet_unrealize): Don't call gtk_widget_unparent - on sheet->button if it's null. - -2008-05-06 Ben Pfaff - - * gtksheet.c (gtk_sheet_dispose): Set the sheet's entry_container - and button members to NULL after unref'ing them, so that a later - call to gtk_sheet_for_all will not try to dereference a dangling - pointer. - -2008-03-06 John Darrington - - * gsheet-row-iface.c gsheet-row-iface.h: Delete unused, unneccesary - gpointer variable from the interface. - - * gtksheet.c: Update to match new gsheet-row-iface - -2008-02-27 John Darrington - * gtksheet.c gtksheet.h: Corrected some leaks and other problems - related to de-allocating the sheet. - -2008-02-27 John Darrington - * gtksheet.c: (gtk_sheet_expose) Don't queue a redraw on the entry - widget. Fixes bug #21073 - -2008-02-20 John Darrington - - * gtksheet.c gtksheet.h: Removed some unused signals. - Made the models properties of the widget. - -2008-02-08 John Darrington - - * gtksheet.c: Removed the sheet_locked feature, which we never - used, and interfered with the editability of the entry widget. - - * gtksheet.c: Add one to the row to which we scroll. Seems like - the best way to cope with granularity problems. - -21 Septempber 2007 John Darrington - - * gtksheet.c (range_update_callback): Scroll to cell 0,0 if the - current position is outside the model's range. - -24 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed the `clip' feature, which IMO - is a croc, and we're unlikely to use. In its place, added a primary - selection which supports text and html targets. - -16 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed some legacy functions called from - gtk_sheet_finalize which caused unnecessary delays when shutting down. - -12 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed view member and replaced with - function call. Removed hadjustment_changed and vadjustment_changed - functions which did nothing. Added some whitespace arount != - operators. - -09 July 2007 John Darrington - - * gtksheet.c gtksheet.h (gtk_sheet_get_active_cell): Allowed row, - column to be NULL. - -07 July 2007 John Darrington - - * gsheet-column-iface.c gsheet-column-iface.h gsheet-row-iface.c - gsheet-row-iface.h gtksheet.c gtksheet.h: Added a "subtitle" - feature on row/column titles, which shows tooltip-like popups. - -03 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed the autoscroll-on-select feature - that was causing us grief. - -28 June 2007 John Darrington - - * gtksheet.c: Removed some features that we dont use, to get better - speed. - -Sat Feb 17 17:36:56 2007 Ben Pfaff - - * gsheet-column-iface.c gsheet-hetero-column.c gsheet-row-iface.c - gsheet-uniform-column.c gsheet-uniform-row.c gsheetmodel.c - gtkextra-marshal.c gtkextra.c gtkiconlist.c gtkitementry.c - gtksheet.c: Add "#include ". - -Mon Jun 19 18:03:21 WST 2006 John Darrington - - * gsheet-column-iface.c gsheet-column-iface.h - gsheet-hetero-column.c gsheet-row-iface.c gsheet-row-iface.h - gsheet-uniform-column.c gsheet-uniform-row.c gtksheet.c - gtksheet.h: Fixed some warnings. Corrected errors updating - row/column titles - -Di Mai 30 19:51:19 WST 2006 John Darrington - - * gtksheet.c gtksheet.h: constness. Removed dependence on glib2.10 - -Sat May 27 16:29:36 WST 2006 John Darrington - - * gtksheet.c: Removed call to gtk_entry_set_text, which caused warnings - and was unnecessary. - -Thu May 25 17:58:51 WST 2006 John Darrington - - * gsheet-column-iface.c gsheet-column-iface.h gsheet-hetero-column.c - gsheet-row-iface.c gsheet-row-iface.h gsheet-uniform-row.c - gtksheet-extra.h gtksheet.c: Plugged memory leaks. Rationalised the way - that GtkSheetButtons are created. - -Sat May 20 21:02:03 WST 2006 John Darrington - - * gsheetmodel.c gsheetmodel.h: Added columns-inserted and columns-deleted - signals. Added g_sheet_get_{row,column}_count functions. - - * gtksheet.c gtksheet.h: Allowed -1 to be passed to - gtk_sheet_set_active_cell to indicate no active cell. - -Mon May 15 16:10:49 WST 2006 John Darrington - - * gtksheet.c: Removed code which rendered the title buttons a second - time. Cut and Paste error ? - -Sat May 13 07:58:32 WST 2006 John Darrington - - * gsheetmodel.c gsheetmodel.h gtksheet.c gtksheeet.h: Added - free_strings flag to tell the sheet whether to free the string - data passed from the model. - -Thu May 11 22:20:04 WST 2006 John Darrington - - * gtksheet.c, gtksheet.h: Fixed broken deallocation of sheet->pixmap. - -Thu May 4 17:55:48 WST 2006 John Darrington - - * gtksheet.c: Added callback on inserted rows. - -Sat Jan 28 08:48:08 2006 UTC John Darrington - - * Separated the data out of the GtkSheet. The gtksheet should now be - regarded as a way of looking into the data. The data is represented by a - GSheetModel and the rows and columns by GSheetRow and GSheetColumn. diff --git a/lib/gtksheet/README b/lib/gtksheet/README deleted file mode 100644 index e6e23d94..00000000 --- a/lib/gtksheet/README +++ /dev/null @@ -1,10 +0,0 @@ -This is not part of the GNU PSPP program, but is used with GNU PSPP. - -This directory contains a version of the GtkXPaned widget. It includes -minor modifications. GtkXPaned is licensed under the GNU Lesser -General Public License. See COPYING.LESSER. - - -This directory also contains the PsppireSheet widget which is a very -heavily modified version of GtkSheet widget. This modified version if -licensed under the GNU General Public License version 3 or later. diff --git a/lib/gtksheet/automake.mk b/lib/gtksheet/automake.mk deleted file mode 100644 index d91b6db3..00000000 --- a/lib/gtksheet/automake.mk +++ /dev/null @@ -1,16 +0,0 @@ -## Process this file with automake to produce Makefile.in -*- makefile -*- - -noinst_LIBRARIES += lib/gtksheet/libgtksheet.a - -lib_gtksheet_libgtksheet_a_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 - -lib_gtksheet_libgtksheet_a_SOURCES = \ - lib/gtksheet/gtkextra-sheet.h \ - lib/gtksheet/psppire-sheet.c \ - lib/gtksheet/psppire-sheet.h \ - lib/gtksheet/gtkxpaned.c \ - lib/gtksheet/gtkxpaned.h - -EXTRA_DIST += lib/gtksheet/OChangeLog \ - lib/gtksheet/README - diff --git a/lib/gtksheet/gtkextra-sheet.h b/lib/gtksheet/gtkextra-sheet.h deleted file mode 100644 index 16d6b5b4..00000000 --- a/lib/gtksheet/gtkextra-sheet.h +++ /dev/null @@ -1,61 +0,0 @@ -/* This version of GtkSheet has been heavily modified, for the specific - * requirements of PSPPIRE. - * - * 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 Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef PSPPIRE_EXTRA_SHEET_H__ -#define PSPPIRE_EXTRA_SHEET_H__ - - -struct _PsppireSheet ; - -typedef struct _PsppireSheet PsppireSheet; - - -struct _PsppireSheetButton -{ - GtkStateType state; - gchar *label; - - gboolean label_visible; - - GtkJustification justification; -}; - -struct _PsppireSheetCell -{ - gint row; - gint col; -}; - -typedef struct _PsppireSheetButton PsppireSheetButton; -typedef struct _PsppireSheetCell PsppireSheetCell; - -PsppireSheetButton * psppire_sheet_button_new (void); - -void psppire_sheet_button_free (PsppireSheetButton *button); - - -#endif /* PSPPIRE_EXTRA_SHEET_H__ */ - - diff --git a/lib/gtksheet/gtkxpaned.c b/lib/gtksheet/gtkxpaned.c deleted file mode 100644 index 5db3ccea..00000000 --- a/lib/gtksheet/gtkxpaned.c +++ /dev/null @@ -1,3260 +0,0 @@ -/******************************************************************************* -**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 -** 10 20 30 40 50 60 70 80 -** -** library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+ -** Copyright (C) 2005-2006 Mirco "MacSlow" Müller -** -** This library is free software; you can redistribute it and/or -** modify it under the terms of the GNU Lesser General Public -** License as published by the Free Software Foundation; either -** version 2.1 of the License, or (at your option) any later version. -** -** This library is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** Lesser General Public License for more details. -** -** You should have received a copy of the GNU Lesser General Public -** License along with this library; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -** -** GtkXPaned is based on GtkPaned which was done by... -** -** "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald" -** -** and later modified by... -** -** "the GTK+ Team and others 1997-2000" -** -*******************************************************************************/ - -#include "gtkxpaned.h" -#include -#include -#include -#include -#include -#include - -enum WidgetProperties -{ - PROP_0, - PROP_X_POSITION, - PROP_Y_POSITION, - PROP_POSITION_SET, - PROP_MIN_X_POSITION, - PROP_MIN_Y_POSITION, - PROP_MAX_X_POSITION, - PROP_MAX_Y_POSITION -}; - -enum ChildProperties -{ - CHILD_PROP_0, - CHILD_PROP_RESIZE, - CHILD_PROP_SHRINK -}; - -enum WidgetSignals -{ - CYCLE_CHILD_FOCUS, - TOGGLE_HANDLE_FOCUS, - MOVE_HANDLE, - CYCLE_HANDLE_FOCUS, - ACCEPT_POSITION, - CANCEL_POSITION, - LAST_SIGNAL -}; - -static void gtk_xpaned_class_init (GtkXPanedClass* klass); - -static void gtk_xpaned_init (GtkXPaned* xpaned); - -static void gtk_xpaned_size_request (GtkWidget* widget, - GtkRequisition* requisition); - -static void gtk_xpaned_size_allocate (GtkWidget* widget, - GtkAllocation* allocation); - -static void gtk_xpaned_set_property (GObject* object, - guint prop_id, - const GValue* value, - GParamSpec* pspec); - -static void gtk_xpaned_get_property (GObject* object, - guint prop_id, - GValue* value, - GParamSpec* pspec); - -static void gtk_xpaned_set_child_property (GtkContainer* container, - GtkWidget* child, - guint property_id, - const GValue* value, - GParamSpec* pspec); - -static void gtk_xpaned_get_child_property (GtkContainer* container, - GtkWidget* child, - guint property_id, - GValue* value, - GParamSpec* pspec); - -static void gtk_xpaned_finalize (GObject* object); - -static void gtk_xpaned_realize (GtkWidget* widget); - -static void gtk_xpaned_unrealize (GtkWidget* widget); - -static void gtk_xpaned_map (GtkWidget* widget); - -static void gtk_xpaned_unmap (GtkWidget* widget); - -static gboolean gtk_xpaned_expose (GtkWidget* widget, GdkEventExpose* event); - -static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event); - -static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event); - -static gboolean gtk_xpaned_button_press (GtkWidget* widget, - GdkEventButton* event); - -static gboolean gtk_xpaned_button_release (GtkWidget* widget, - GdkEventButton* event); - -static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event); - -static gboolean gtk_xpaned_focus (GtkWidget* widget, - GtkDirectionType direction); - -static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget); - -static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget); - -static void gtk_xpaned_forall (GtkContainer* container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data); - -static void gtk_xpaned_set_focus_child (GtkContainer* container, - GtkWidget* child); - -static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget); - -static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned, - GtkXPaned* first_xpaned); - -static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned, - GtkWidget* widget); - -static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned, - GtkWidget* widget); - -static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned, - GtkWidget* widget); - -static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned, - GtkWidget* widget); - -static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned, - gboolean reverse); - -static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned, - gboolean reverse); - -static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned, - GtkScrollType scroll); - -static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned); - -static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned); - -static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned); - -static GType gtk_xpaned_child_type (GtkContainer* container); - -static GtkContainerClass* parent_class = NULL; - -struct _GtkXPanedPrivate -{ - GtkWidget *saved_focus; - GtkXPaned *first_xpaned; -}; - -GType gtk_xpaned_get_type (void) -{ - static GType xpaned_type = 0; - - if (!xpaned_type) - { - static const GTypeInfo xpaned_info = - { - sizeof (GtkXPanedClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_xpaned_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkXPaned), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_xpaned_init - }; - - xpaned_type = g_type_register_static (GTK_TYPE_CONTAINER, - "GtkXPaned", - &xpaned_info, - 0); - } - - return xpaned_type; -} - -GtkWidget* gtk_xpaned_new (void) -{ - GtkXPaned* xpaned; - - xpaned = g_object_new (GTK_TYPE_XPANED, NULL); - - return GTK_WIDGET (xpaned); -} - -static guint signals[LAST_SIGNAL] = { 0 }; - -static void add_tab_bindings (GtkBindingSet* binding_set, - GdkModifierType modifiers) -{ - gtk_binding_entry_add_signal (binding_set, - GDK_Tab, - modifiers, - "toggle_handle_focus", - 0); - - gtk_binding_entry_add_signal (binding_set, - GDK_KP_Tab, - modifiers, - "toggle_handle_focus", - 0); -} - -static void add_move_binding (GtkBindingSet* binding_set, - guint keyval, - GdkModifierType mask, - GtkScrollType scroll) -{ - gtk_binding_entry_add_signal (binding_set, - keyval, - mask, - "move_handle", - 1, - GTK_TYPE_SCROLL_TYPE, - scroll); -} - -static void gtk_xpaned_class_init (GtkXPanedClass* class) -{ - GObjectClass* object_class; - GtkWidgetClass* widget_class; - GtkContainerClass* container_class; - GtkXPanedClass* xpaned_class; - GtkBindingSet* binding_set; - - object_class = (GObjectClass *) class; - widget_class = (GtkWidgetClass *) class; - container_class = (GtkContainerClass *) class; - xpaned_class = (GtkXPanedClass *) class; - - parent_class = g_type_class_peek_parent (class); - - object_class->set_property = gtk_xpaned_set_property; - object_class->get_property = gtk_xpaned_get_property; - object_class->finalize = gtk_xpaned_finalize; - - widget_class->realize = gtk_xpaned_realize; - widget_class->unrealize = gtk_xpaned_unrealize; - widget_class->map = gtk_xpaned_map; - widget_class->unmap = gtk_xpaned_unmap; - widget_class->expose_event = gtk_xpaned_expose; - widget_class->focus = gtk_xpaned_focus; - widget_class->enter_notify_event = gtk_xpaned_enter; - widget_class->leave_notify_event = gtk_xpaned_leave; - widget_class->button_press_event = gtk_xpaned_button_press; - widget_class->button_release_event = gtk_xpaned_button_release; - widget_class->motion_notify_event = gtk_xpaned_motion; - widget_class->size_request = gtk_xpaned_size_request; - widget_class->size_allocate = gtk_xpaned_size_allocate; - - container_class->add = gtk_xpaned_add; - container_class->remove = gtk_xpaned_remove; - container_class->forall = gtk_xpaned_forall; - container_class->child_type = gtk_xpaned_child_type; - container_class->set_focus_child = gtk_xpaned_set_focus_child; - container_class->set_child_property = gtk_xpaned_set_child_property; - container_class->get_child_property = gtk_xpaned_get_child_property; - - xpaned_class->cycle_child_focus = gtk_xpaned_cycle_child_focus; - xpaned_class->toggle_handle_focus = gtk_xpaned_toggle_handle_focus; - xpaned_class->move_handle = gtk_xpaned_move_handle; - xpaned_class->cycle_handle_focus = gtk_xpaned_cycle_handle_focus; - xpaned_class->accept_position = gtk_xpaned_accept_position; - xpaned_class->cancel_position = gtk_xpaned_cancel_position; - - g_object_class_install_property (object_class, - PROP_X_POSITION, - g_param_spec_int ("x-position", - ("x-Position"), - ("x-Position of paned separator in pixels (0 means all the way to the left)"), - 0, - G_MAXINT, - 0, - G_PARAM_READABLE | G_PARAM_WRITABLE)); - - g_object_class_install_property (object_class, - PROP_Y_POSITION, - g_param_spec_int ("y-position", - "y-Position", - "y-Position of paned separator in pixels (0 means all the way to the top)", - 0, - G_MAXINT, - 0, - G_PARAM_READABLE | G_PARAM_WRITABLE)); - - g_object_class_install_property (object_class, - PROP_POSITION_SET, - g_param_spec_boolean ("position-set", - "Position Set", - "TRUE if the Position property should be used", - FALSE, - G_PARAM_READABLE | G_PARAM_WRITABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("handle-size", - "Handle Size", - "Width of handle", - 0, - G_MAXINT, - 3, - G_PARAM_READABLE)); - /** - * GtkXPaned:min-x-position: - * - * The smallest possible value for the x-position property. This property is derived from the - * size and shrinkability of the widget's children. - * - * Since: 2.4 - */ - g_object_class_install_property (object_class, - PROP_MIN_X_POSITION, - g_param_spec_int ("min-x-position", - "Minimal x-Position", - "Smallest possible value for the \"x-position\" property", - 0, - G_MAXINT, - 0, - G_PARAM_READABLE)); - - /** - * GtkXPaned:min-y-position: - * - * The smallest possible value for the y-position property. This property is derived from the - * size and shrinkability of the widget's children. - * - * Since: 2.4 - */ - g_object_class_install_property (object_class, - PROP_MIN_Y_POSITION, - g_param_spec_int ("min-y-position", - "Minimal y-Position", - "Smallest possible value for the \"y-position\" property", - 0, - G_MAXINT, - 0, - G_PARAM_READABLE)); - - /** - * GtkPaned:max-x-position: - * - * The largest possible value for the x-position property. This property is derived from the - * size and shrinkability of the widget's children. - * - * Since: 2.4 - */ - g_object_class_install_property (object_class, - PROP_MAX_X_POSITION, - g_param_spec_int ("max-x-position", - "Maximal x-Position", - "Largest possible value for the \"x-position\" property", - 0, - G_MAXINT, - G_MAXINT, - G_PARAM_READABLE)); - - /** - * GtkPaned:max-y-position: - * - * The largest possible value for the y-position property. This property is derived from the - * size and shrinkability of the widget's children. - * - * Since: 2.4 - */ - g_object_class_install_property (object_class, - PROP_MAX_Y_POSITION, - g_param_spec_int ("max-y-position", - "Maximal y-Position", - "Largest possible value for the \"y-position\" property", - 0, - G_MAXINT, - G_MAXINT, - G_PARAM_READABLE)); - - /** - * GtkPaned:resize: - * - * The "resize" child property determines whether the child expands and - * shrinks along with the paned widget. - * - * Since: 2.4 - */ - gtk_container_class_install_child_property (container_class, - CHILD_PROP_RESIZE, - g_param_spec_boolean ("resize", - "Resize", - "If TRUE, the child expands and shrinks along with the paned widget", - TRUE, - G_PARAM_READWRITE)); - - /** - * GtkPaned:shrink: - * - * The "shrink" child property determines whether the child can be made - * smaller than its requisition. - * - * Since: 2.4 - */ - gtk_container_class_install_child_property (container_class, - CHILD_PROP_SHRINK, - g_param_spec_boolean ("shrink", - "Shrink", - "If TRUE, the child can be made smaller than its requisition", - TRUE, - G_PARAM_READWRITE)); - - signals [CYCLE_CHILD_FOCUS] = g_signal_new ("cycle-child-focus", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkXPanedClass, cycle_child_focus), - NULL, NULL, - psppire_marshal_BOOLEAN__BOOLEAN, - G_TYPE_BOOLEAN, 1, - G_TYPE_BOOLEAN); - - signals [TOGGLE_HANDLE_FOCUS] = g_signal_new ("toggle-handle-focus", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkXPanedClass, toggle_handle_focus), - NULL, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - signals[MOVE_HANDLE] = g_signal_new ("move-handle", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkXPanedClass, move_handle), - NULL, NULL, - psppire_marshal_BOOLEAN__ENUM, - G_TYPE_BOOLEAN, 1, - GTK_TYPE_SCROLL_TYPE); - - signals [CYCLE_HANDLE_FOCUS] = g_signal_new ("cycle-handle-focus", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkXPanedClass, cycle_handle_focus), - NULL, NULL, - psppire_marshal_BOOLEAN__BOOLEAN, - G_TYPE_BOOLEAN, 1, - G_TYPE_BOOLEAN); - - signals [ACCEPT_POSITION] = g_signal_new ("accept-position", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkXPanedClass, accept_position), - NULL, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - signals [CANCEL_POSITION] = g_signal_new ("cancel-position", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GtkXPanedClass, cancel_position), - NULL, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - binding_set = gtk_binding_set_by_class (class); - - /* F6 and friends */ - gtk_binding_entry_add_signal (binding_set, - GDK_F6, 0, - "cycle-child-focus", 1, - G_TYPE_BOOLEAN, FALSE); - - gtk_binding_entry_add_signal (binding_set, - GDK_F6, GDK_SHIFT_MASK, - "cycle-child-focus", 1, - G_TYPE_BOOLEAN, TRUE); - - /* F8 and friends */ - gtk_binding_entry_add_signal (binding_set, - GDK_F8, 0, - "cycle-handle-focus", 1, - G_TYPE_BOOLEAN, FALSE); - - gtk_binding_entry_add_signal (binding_set, - GDK_F8, GDK_SHIFT_MASK, - "cycle-handle-focus", 1, - G_TYPE_BOOLEAN, TRUE); - - add_tab_bindings (binding_set, 0); - add_tab_bindings (binding_set, GDK_CONTROL_MASK); - add_tab_bindings (binding_set, GDK_SHIFT_MASK); - add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK); - - /* accept and cancel positions */ - gtk_binding_entry_add_signal (binding_set, - GDK_Escape, 0, - "cancel-position", 0); - - gtk_binding_entry_add_signal (binding_set, - GDK_Return, 0, - "accept-position", 0); - - gtk_binding_entry_add_signal (binding_set, - GDK_KP_Enter, 0, - "accept-position", 0); - - gtk_binding_entry_add_signal (binding_set, - GDK_space, 0, - "accept-position", 0); - - gtk_binding_entry_add_signal (binding_set, - GDK_KP_Space, 0, - "accept-position", 0); - - /* move handle */ - add_move_binding (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_LEFT); - add_move_binding (binding_set, GDK_KP_Left, 0, GTK_SCROLL_STEP_LEFT); - add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); - add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); - - add_move_binding (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_RIGHT); - add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); - add_move_binding (binding_set, GDK_KP_Right, 0, GTK_SCROLL_STEP_RIGHT); - add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); - - add_move_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP); - add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); - add_move_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP); - add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); - add_move_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP); - add_move_binding (binding_set, GDK_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP); - - add_move_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN); - add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); - add_move_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN); - add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); - add_move_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); - add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); - - add_move_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START); - add_move_binding (binding_set, GDK_KP_Home, 0, GTK_SCROLL_START); - add_move_binding (binding_set, GDK_End, 0, GTK_SCROLL_END); - add_move_binding (binding_set, GDK_KP_End, 0, GTK_SCROLL_END); -} - -static GType gtk_xpaned_child_type (GtkContainer* container) -{ - if (!GTK_XPANED (container)->top_left_child || - !GTK_XPANED (container)->top_right_child || - !GTK_XPANED (container)->bottom_left_child || - !GTK_XPANED (container)->bottom_right_child) - return GTK_TYPE_WIDGET; - else - return G_TYPE_NONE; -} - -static void gtk_xpaned_init (GtkXPaned* xpaned) -{ - GTK_WIDGET_SET_FLAGS (xpaned, GTK_NO_WINDOW | GTK_CAN_FOCUS); - - xpaned->top_left_child = NULL; - xpaned->top_right_child = NULL; - xpaned->bottom_left_child = NULL; - xpaned->bottom_right_child = NULL; - xpaned->handle_east = NULL; - xpaned->handle_west = NULL; - xpaned->handle_north = NULL; - xpaned->handle_south = NULL; - xpaned->handle_middle = NULL; - xpaned->xor_gc = NULL; - xpaned->cursor_type_east = GDK_SB_V_DOUBLE_ARROW; - xpaned->cursor_type_west = GDK_SB_V_DOUBLE_ARROW; - xpaned->cursor_type_north = GDK_SB_H_DOUBLE_ARROW; - xpaned->cursor_type_south = GDK_SB_H_DOUBLE_ARROW; - xpaned->cursor_type_middle = GDK_FLEUR; - - xpaned->handle_pos_east.width = 5; - xpaned->handle_pos_east.height = 5; - xpaned->handle_pos_west.width = 5; - xpaned->handle_pos_west.height = 5; - xpaned->handle_pos_north.width = 5; - xpaned->handle_pos_north.height = 5; - xpaned->handle_pos_south.width = 5; - xpaned->handle_pos_south.height = 5; - xpaned->handle_pos_middle.width = 5; - xpaned->handle_pos_middle.height = 5; - - xpaned->position_set = FALSE; - xpaned->last_allocation.width = -1; - xpaned->last_allocation.height = -1; - xpaned->in_drag_vert = FALSE; - xpaned->in_drag_horiz = FALSE; - xpaned->in_drag_vert_and_horiz = FALSE; - - xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE; - xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE; - xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE; - xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE; - - xpaned->priv = g_new0 (GtkXPanedPrivate, 1); - xpaned->last_top_left_child_focus = NULL; - xpaned->last_top_right_child_focus = NULL; - xpaned->last_bottom_left_child_focus = NULL; - xpaned->last_bottom_right_child_focus = NULL; - xpaned->in_recursion = FALSE; - xpaned->handle_prelit = FALSE; - xpaned->original_position.x = -1; - xpaned->original_position.y = -1; - xpaned->unmaximized_position.x = -1; - xpaned->unmaximized_position.y = -1; - - xpaned->handle_pos_east.x = -1; - xpaned->handle_pos_east.y = -1; - xpaned->handle_pos_west.x = -1; - xpaned->handle_pos_west.y = -1; - xpaned->handle_pos_north.x = -1; - xpaned->handle_pos_north.y = -1; - xpaned->handle_pos_south.x = -1; - xpaned->handle_pos_south.y = -1; - xpaned->handle_pos_middle.x = -1; - xpaned->handle_pos_middle.y = -1; - - xpaned->drag_pos.x = -1; - xpaned->drag_pos.y = -1; -} - -static void gtk_xpaned_size_request (GtkWidget* widget, - GtkRequisition* requisition) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - GtkRequisition child_requisition; - - requisition->width = 0; - requisition->height = 0; - - if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child)) - { - gtk_widget_size_request (xpaned->top_left_child, &child_requisition); - - requisition->width = child_requisition.width; - requisition->height = child_requisition.height; - } - - if (xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child)) - { - gtk_widget_size_request (xpaned->top_right_child, &child_requisition); - - requisition->width += child_requisition.width; - requisition->height = MAX (requisition->height, child_requisition.height); - } - - if (xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child)) - { - gtk_widget_size_request (xpaned->bottom_left_child, &child_requisition); - - requisition->width = MAX (requisition->width, child_requisition.width); - requisition->height += child_requisition.height; - } - - if (xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) - { - gtk_widget_size_request (xpaned->bottom_right_child, &child_requisition); - - requisition->width = MAX (requisition->width, child_requisition.width); - requisition->height = MAX (requisition->height, child_requisition.height); - } - - /* add 2 times the set border-width to the GtkXPaneds requisition */ - requisition->width += GTK_CONTAINER (xpaned)->border_width * 2; - requisition->height += GTK_CONTAINER (xpaned)->border_width * 2; - - /* also add the handle "thickness" to GtkXPaneds width- and height-requisitions */ - if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && - xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && - xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && - xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) - { - gint handle_size; - - gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); - requisition->width += handle_size; - requisition->height += handle_size; - } -} - -void -gtk_xpaned_compute_position (GtkXPaned* xpaned, - const GtkAllocation* allocation, - GtkRequisition* top_left_child_req, - GtkRequisition* top_right_child_req, - GtkRequisition* bottom_left_child_req, - GtkRequisition* bottom_right_child_req); - - -static void gtk_xpaned_size_allocate (GtkWidget* widget, - GtkAllocation* allocation) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - gint border_width = GTK_CONTAINER (xpaned)->border_width; - GtkAllocation top_left_child_allocation; - GtkAllocation top_right_child_allocation; - GtkAllocation bottom_left_child_allocation; - GtkAllocation bottom_right_child_allocation; - GtkRequisition top_left_child_requisition; - GtkRequisition top_right_child_requisition; - GtkRequisition bottom_left_child_requisition; - GtkRequisition bottom_right_child_requisition; - gint handle_size; - - /* determine size of handle(s) */ - gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); - - widget->allocation = *allocation; - - if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && - xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && - xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && - xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) - { - /* what sizes do the children want to be at least at */ - gtk_widget_get_child_requisition (xpaned->top_left_child, - &top_left_child_requisition); - gtk_widget_get_child_requisition (xpaned->top_right_child, - &top_right_child_requisition); - gtk_widget_get_child_requisition (xpaned->bottom_left_child, - &bottom_left_child_requisition); - gtk_widget_get_child_requisition (xpaned->bottom_right_child, - &bottom_right_child_requisition); - - /* determine the total requisition-sum of all requisitions of borders, - * handles, children etc. */ - gtk_xpaned_compute_position (xpaned, - allocation, - &top_left_child_requisition, - &top_right_child_requisition, - &bottom_left_child_requisition, - &bottom_right_child_requisition); - - /* calculate the current positions and sizes of the handles */ - xpaned->handle_pos_east.x = widget->allocation.x + border_width + xpaned->top_left_child_size.width + handle_size; - xpaned->handle_pos_east.y = widget->allocation.y + border_width + xpaned->top_left_child_size.height; - xpaned->handle_pos_east.width = widget->allocation.width - xpaned->top_left_child_size.width - 2 * border_width - handle_size; - xpaned->handle_pos_east.height = handle_size; - - xpaned->handle_pos_west.x = widget->allocation.x + border_width; - xpaned->handle_pos_west.y = xpaned->handle_pos_east.y; - xpaned->handle_pos_west.width = widget->allocation.width - xpaned->handle_pos_east.width - 2 * border_width - handle_size; - xpaned->handle_pos_west.height = handle_size; - - xpaned->handle_pos_north.x = xpaned->handle_pos_east.x - handle_size; - xpaned->handle_pos_north.y = widget->allocation.y + border_width; - xpaned->handle_pos_north.width = handle_size; - xpaned->handle_pos_north.height = xpaned->handle_pos_east.y - widget->allocation.y - border_width; - - xpaned->handle_pos_south.x = xpaned->handle_pos_north.x; - xpaned->handle_pos_south.y = xpaned->handle_pos_east.y + handle_size; - xpaned->handle_pos_south.width = handle_size; - xpaned->handle_pos_south.height = widget->allocation.height - xpaned->handle_pos_north.height - 2 * border_width - handle_size; - - -#define CENTRUM 20 - xpaned->handle_pos_middle.x = xpaned->handle_pos_north.x ; - xpaned->handle_pos_middle.y = xpaned->handle_pos_east.y ; - xpaned->handle_pos_middle.width = handle_size + CENTRUM ; - xpaned->handle_pos_middle.height = handle_size + CENTRUM; - - /* set allocation for top-left child */ - top_left_child_allocation.x = widget->allocation.x + border_width; - top_left_child_allocation.y = widget->allocation.y + border_width; - top_left_child_allocation.width = xpaned->handle_pos_west.width; - top_left_child_allocation.height = xpaned->handle_pos_north.height; - - /* set allocation for top-right child */ - top_right_child_allocation.x = widget->allocation.x + border_width + handle_size + top_left_child_allocation.width; - top_right_child_allocation.y = widget->allocation.y + border_width; - top_right_child_allocation.width = xpaned->handle_pos_east.width; - top_right_child_allocation.height = xpaned->handle_pos_north.height; - - /* set allocation for bottom-left child */ - bottom_left_child_allocation.x = xpaned->handle_pos_west.x; - bottom_left_child_allocation.y = xpaned->handle_pos_south.y; - bottom_left_child_allocation.width = xpaned->handle_pos_west.width; - bottom_left_child_allocation.height = xpaned->handle_pos_south.height; - - /* set allocation for bottom-right child */ - bottom_right_child_allocation.x = top_right_child_allocation.x; - bottom_right_child_allocation.y = bottom_left_child_allocation.y; - bottom_right_child_allocation.width = xpaned->handle_pos_east.width; - bottom_right_child_allocation.height = xpaned->handle_pos_south.height; - - if (GTK_WIDGET_REALIZED (widget)) - { - if (GTK_WIDGET_MAPPED (widget)) - { - gdk_window_show (xpaned->handle_east); - gdk_window_show (xpaned->handle_west); - gdk_window_show (xpaned->handle_north); - gdk_window_show (xpaned->handle_south); - gdk_window_show (xpaned->handle_middle); - } - - gdk_window_move_resize (xpaned->handle_east, - xpaned->handle_pos_east.x, - xpaned->handle_pos_east.y, - xpaned->handle_pos_east.width, - xpaned->handle_pos_east.height); - - gdk_window_move_resize (xpaned->handle_west, - xpaned->handle_pos_west.x, - xpaned->handle_pos_west.y, - xpaned->handle_pos_west.width, - xpaned->handle_pos_west.height); - - gdk_window_move_resize (xpaned->handle_north, - xpaned->handle_pos_north.x, - xpaned->handle_pos_north.y, - xpaned->handle_pos_north.width, - xpaned->handle_pos_north.height); - - gdk_window_move_resize (xpaned->handle_south, - xpaned->handle_pos_south.x, - xpaned->handle_pos_south.y, - xpaned->handle_pos_south.width, - xpaned->handle_pos_south.height); - - gdk_window_move_resize (xpaned->handle_middle, - xpaned->handle_pos_middle.x, - xpaned->handle_pos_middle.y, - xpaned->handle_pos_middle.width, - xpaned->handle_pos_middle.height); - } - - /* Now allocate the childen, making sure, when resizing not to - * overlap the windows - */ - if (GTK_WIDGET_MAPPED (widget)) - { - gtk_widget_size_allocate (xpaned->top_right_child, &top_right_child_allocation); - gtk_widget_size_allocate (xpaned->top_left_child, &top_left_child_allocation); - gtk_widget_size_allocate (xpaned->bottom_left_child, &bottom_left_child_allocation); - gtk_widget_size_allocate (xpaned->bottom_right_child, &bottom_right_child_allocation); - } - } -} - -static void gtk_xpaned_set_property (GObject* object, - guint prop_id, - const GValue* value, - GParamSpec* pspec) -{ - GtkXPaned* xpaned = GTK_XPANED (object); - - switch (prop_id) - { - case PROP_X_POSITION: - gtk_xpaned_set_position_x (xpaned, g_value_get_int (value)); - break; - - case PROP_Y_POSITION: - gtk_xpaned_set_position_y (xpaned, g_value_get_int (value)); - break; - - case PROP_POSITION_SET: - xpaned->position_set = g_value_get_boolean (value); - gtk_widget_queue_resize (GTK_WIDGET (xpaned)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void gtk_xpaned_get_property (GObject* object, - guint prop_id, - GValue* value, - GParamSpec* pspec) -{ - GtkXPaned* xpaned = GTK_XPANED (object); - - switch (prop_id) - { - case PROP_X_POSITION: - g_value_set_int (value, xpaned->top_left_child_size.width); - break; - - case PROP_Y_POSITION: - g_value_set_int (value, xpaned->top_left_child_size.height); - break; - - case PROP_POSITION_SET: - g_value_set_boolean (value, xpaned->position_set); - break; - - case PROP_MIN_X_POSITION: - g_value_set_int (value, xpaned->min_position.x); - break; - - case PROP_MIN_Y_POSITION: - g_value_set_int (value, xpaned->min_position.y); - break; - - case PROP_MAX_X_POSITION: - g_value_set_int (value, xpaned->max_position.x); - break; - - case PROP_MAX_Y_POSITION: - g_value_set_int (value, xpaned->max_position.y); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void gtk_xpaned_set_child_property (GtkContainer* container, - GtkWidget* child, - guint property_id, - const GValue* value, - GParamSpec* pspec) -{ - GtkXPaned* xpaned = GTK_XPANED (container); - gboolean old_value = FALSE; - gboolean new_value = FALSE; - - g_assert (child == xpaned->top_left_child || - child == xpaned->top_right_child || - child == xpaned->bottom_left_child || - child == xpaned->bottom_right_child); - - new_value = g_value_get_boolean (value); - - switch (property_id) - { - case CHILD_PROP_RESIZE: - if (child == xpaned->top_left_child) - { - old_value = xpaned->top_left_child_resize; - xpaned->top_left_child_resize = new_value; - } - else if (child == xpaned->top_right_child) - { - old_value = xpaned->top_right_child_resize; - xpaned->top_right_child_resize = new_value; - } - else if (child == xpaned->bottom_left_child) - { - old_value = xpaned->bottom_left_child_resize; - xpaned->bottom_left_child_resize = new_value; - } - else if (child == xpaned->bottom_right_child) - { - old_value = xpaned->bottom_right_child_resize; - xpaned->bottom_right_child_resize = new_value; - } - break; - - case CHILD_PROP_SHRINK : - if (child == xpaned->top_left_child) - { - old_value = xpaned->top_left_child_shrink; - xpaned->top_left_child_shrink = new_value; - } - else if (child == xpaned->top_right_child) - { - old_value = xpaned->top_right_child_shrink; - xpaned->top_right_child_shrink = new_value; - } - else if (child == xpaned->bottom_left_child) - { - old_value = xpaned->bottom_left_child_shrink; - xpaned->bottom_left_child_shrink = new_value; - } - else if (child == xpaned->bottom_right_child) - { - old_value = xpaned->bottom_right_child_shrink; - xpaned->bottom_right_child_shrink = new_value; - } - break; - - default: - GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, - property_id, - pspec); - old_value = -1; /* quiet gcc */ - break; - } - - if (old_value != new_value) - gtk_widget_queue_resize (GTK_WIDGET (container)); -} - -static void gtk_xpaned_get_child_property (GtkContainer* container, - GtkWidget* child, - guint property_id, - GValue* value, - GParamSpec* pspec) -{ - GtkXPaned* xpaned = GTK_XPANED (container); - - g_assert (child == xpaned->top_left_child || - child == xpaned->top_right_child || - child == xpaned->bottom_left_child || - child == xpaned->bottom_right_child); - - switch (property_id) - { - case CHILD_PROP_RESIZE : - if (child == xpaned->top_left_child) - g_value_set_boolean (value, xpaned->top_left_child_resize); - else if (child == xpaned->top_right_child) - g_value_set_boolean (value, xpaned->top_right_child_resize); - else if (child == xpaned->bottom_left_child) - g_value_set_boolean (value, xpaned->bottom_left_child_resize); - else if (child == xpaned->bottom_right_child) - g_value_set_boolean (value, xpaned->bottom_right_child_resize); - break; - - case CHILD_PROP_SHRINK : - if (child == xpaned->top_left_child) - g_value_set_boolean (value, xpaned->top_left_child_shrink); - else if (child == xpaned->top_right_child) - g_value_set_boolean (value, xpaned->top_right_child_shrink); - else if (child == xpaned->bottom_left_child) - g_value_set_boolean (value, xpaned->bottom_left_child_shrink); - else if (child == xpaned->bottom_right_child) - g_value_set_boolean (value, xpaned->bottom_right_child_shrink); - break; - - default: - GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, - property_id, - pspec); - break; - } -} - -static void gtk_xpaned_finalize (GObject* object) -{ - GtkXPaned* xpaned = GTK_XPANED (object); - - gtk_xpaned_set_saved_focus (xpaned, NULL); - gtk_xpaned_set_first_xpaned (xpaned, NULL); - - g_free (xpaned->priv); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void gtk_xpaned_realize (GtkWidget* widget) -{ - GtkXPaned* xpaned; - GdkWindowAttr attributes_east; - GdkWindowAttr attributes_west; - GdkWindowAttr attributes_north; - GdkWindowAttr attributes_south; - GdkWindowAttr attributes_middle; - gint attributes_mask_east; - gint attributes_mask_west; - gint attributes_mask_north; - gint attributes_mask_south; - gint attributes_mask_middle; - - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); - xpaned = GTK_XPANED (widget); - - widget->window = gtk_widget_get_parent_window (widget); - g_object_ref (widget->window); - - attributes_east.window_type = GDK_WINDOW_CHILD; - attributes_west.window_type = GDK_WINDOW_CHILD; - attributes_north.window_type = GDK_WINDOW_CHILD; - attributes_south.window_type = GDK_WINDOW_CHILD; - attributes_middle.window_type = GDK_WINDOW_CHILD; - - attributes_east.wclass = GDK_INPUT_ONLY; - attributes_west.wclass = GDK_INPUT_ONLY; - attributes_north.wclass = GDK_INPUT_ONLY; - attributes_south.wclass = GDK_INPUT_ONLY; - attributes_middle.wclass = GDK_INPUT_ONLY; - - attributes_east.x = xpaned->handle_pos_east.x; - attributes_east.y = xpaned->handle_pos_east.y; - attributes_east.width = xpaned->handle_pos_east.width; - attributes_east.height = xpaned->handle_pos_east.height; - - attributes_west.x = xpaned->handle_pos_west.x; - attributes_west.y = xpaned->handle_pos_west.y; - attributes_west.width = xpaned->handle_pos_west.width; - attributes_west.height = xpaned->handle_pos_west.height; - - attributes_north.x = xpaned->handle_pos_north.x; - attributes_north.y = xpaned->handle_pos_north.y; - attributes_north.width = xpaned->handle_pos_north.width; - attributes_north.height = xpaned->handle_pos_north.height; - - attributes_south.x = xpaned->handle_pos_south.x; - attributes_south.y = xpaned->handle_pos_south.y; - attributes_south.width = xpaned->handle_pos_south.width; - attributes_south.height = xpaned->handle_pos_south.height; - - attributes_middle.x = xpaned->handle_pos_middle.x; - attributes_middle.y = xpaned->handle_pos_middle.y; - attributes_middle.width = xpaned->handle_pos_middle.width; - attributes_middle.height = xpaned->handle_pos_middle.height; - - attributes_east.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), - xpaned->cursor_type_east); - attributes_west.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), - xpaned->cursor_type_west); - attributes_north.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), - xpaned->cursor_type_north); - attributes_south.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), - xpaned->cursor_type_south); - attributes_middle.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), - xpaned->cursor_type_middle); - - attributes_east.event_mask = gtk_widget_get_events (widget); - attributes_west.event_mask = gtk_widget_get_events (widget); - attributes_north.event_mask = gtk_widget_get_events (widget); - attributes_south.event_mask = gtk_widget_get_events (widget); - attributes_middle.event_mask = gtk_widget_get_events (widget); - - attributes_east.event_mask |= (GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK); - attributes_west.event_mask |= (GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK); - attributes_north.event_mask |= (GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK); - attributes_south.event_mask |= (GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK); - attributes_middle.event_mask |= (GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK); - - attributes_mask_east = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; - attributes_mask_west = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; - attributes_mask_north = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; - attributes_mask_south = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; - attributes_mask_middle = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; - - xpaned->handle_east = gdk_window_new (widget->window, - &attributes_east, - attributes_mask_east); - xpaned->handle_west = gdk_window_new (widget->window, - &attributes_west, - attributes_mask_west); - xpaned->handle_north = gdk_window_new (widget->window, - &attributes_north, - attributes_mask_north); - xpaned->handle_south = gdk_window_new (widget->window, - &attributes_south, - attributes_mask_south); - xpaned->handle_middle = gdk_window_new (widget->window, - &attributes_middle, - attributes_mask_middle); - - gdk_window_set_user_data (xpaned->handle_east, xpaned); - gdk_window_set_user_data (xpaned->handle_west, xpaned); - gdk_window_set_user_data (xpaned->handle_north, xpaned); - gdk_window_set_user_data (xpaned->handle_south, xpaned); - gdk_window_set_user_data (xpaned->handle_middle, xpaned); - - gdk_cursor_unref (attributes_east.cursor); - gdk_cursor_unref (attributes_west.cursor); - gdk_cursor_unref (attributes_north.cursor); - gdk_cursor_unref (attributes_south.cursor); - gdk_cursor_unref (attributes_middle.cursor); - - widget->style = gtk_style_attach (widget->style, widget->window); - - if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && - xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && - xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && - xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) - { - gdk_window_show (xpaned->handle_east); - gdk_window_show (xpaned->handle_west); - gdk_window_show (xpaned->handle_north); - gdk_window_show (xpaned->handle_south); - gdk_window_show (xpaned->handle_middle); - } -} - -static void gtk_xpaned_unrealize (GtkWidget *widget) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - if (xpaned->xor_gc) - { - g_object_unref (xpaned->xor_gc); - xpaned->xor_gc = NULL; - } - - if (xpaned->handle_east) - { - gdk_window_set_user_data (xpaned->handle_east, NULL); - gdk_window_destroy (xpaned->handle_east); - xpaned->handle_east = NULL; - } - - if (xpaned->handle_west) - { - gdk_window_set_user_data (xpaned->handle_west, NULL); - gdk_window_destroy (xpaned->handle_west); - xpaned->handle_west = NULL; - } - - if (xpaned->handle_north) - { - gdk_window_set_user_data (xpaned->handle_north, NULL); - gdk_window_destroy (xpaned->handle_north); - xpaned->handle_north = NULL; - } - - if (xpaned->handle_south) - { - gdk_window_set_user_data (xpaned->handle_south, NULL); - gdk_window_destroy (xpaned->handle_south); - xpaned->handle_south = NULL; - } - - if (xpaned->handle_middle) - { - gdk_window_set_user_data (xpaned->handle_middle, NULL); - gdk_window_destroy (xpaned->handle_middle); - xpaned->handle_middle = NULL; - } - - gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL); - gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL); - gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL); - gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL); - gtk_xpaned_set_saved_focus (xpaned, NULL); - gtk_xpaned_set_first_xpaned (xpaned, NULL); - - if (GTK_WIDGET_CLASS (parent_class)->unrealize) - (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); -} - -static void gtk_xpaned_map (GtkWidget* widget) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - gdk_window_show (xpaned->handle_east); - gdk_window_show (xpaned->handle_west); - gdk_window_show (xpaned->handle_north); - gdk_window_show (xpaned->handle_south); - gdk_window_show (xpaned->handle_middle); - - GTK_WIDGET_CLASS (parent_class)->map (widget); -} - -static void gtk_xpaned_unmap (GtkWidget* widget) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - gdk_window_hide (xpaned->handle_east); - gdk_window_hide (xpaned->handle_west); - gdk_window_hide (xpaned->handle_north); - gdk_window_hide (xpaned->handle_south); - gdk_window_hide (xpaned->handle_middle); - - GTK_WIDGET_CLASS (parent_class)->unmap (widget); -} - -static gboolean gtk_xpaned_expose (GtkWidget* widget, - GdkEventExpose* event) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - gint handle_size; - GdkRectangle horizontalClipArea; - GdkRectangle verticalClipArea; - - /* determine size of handle(s) */ - gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); - - /* I want the handle-"thickness" to be at least 3 */ - g_assert (handle_size >= 3); - - if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) && - xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && - xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && - xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && - xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) - { - GtkStateType state; - - if (gtk_widget_is_focus (widget)) - state = GTK_STATE_SELECTED; - else if (xpaned->handle_prelit) - state = GTK_STATE_PRELIGHT; - else - state = GTK_WIDGET_STATE (widget); - - horizontalClipArea.x = xpaned->handle_pos_west.x; - horizontalClipArea.y = xpaned->handle_pos_west.y; - horizontalClipArea.width = xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width; - horizontalClipArea.height = handle_size; - - verticalClipArea.x = xpaned->handle_pos_north.x; - verticalClipArea.y = xpaned->handle_pos_north.y; - verticalClipArea.width = handle_size; - verticalClipArea.height = xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height; - - gtk_paint_handle (widget->style, - widget->window, - state, - GTK_SHADOW_NONE, - &horizontalClipArea, - widget, - "paned", - xpaned->handle_pos_east.x - handle_size - 256 / 2, - xpaned->handle_pos_west.y + 1, - 256 + handle_size, - handle_size - 2, - /*xpaned->handle_pos_west.x, - xpaned->handle_pos_west.y + 1, - xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width, - handle_size - 2,*/ - GTK_ORIENTATION_HORIZONTAL); - gtk_paint_handle (widget->style, - widget->window, - state, - GTK_SHADOW_NONE, - &verticalClipArea, - widget, - "paned", - xpaned->handle_pos_north.x + 1, - xpaned->handle_pos_south.y - handle_size - 256 / 2, - handle_size - 2, - 256 + handle_size, - /*xpaned->handle_pos_north.x + 1, - xpaned->handle_pos_north.y, - handle_size - 2, - xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height,*/ - GTK_ORIENTATION_VERTICAL); - } - - /* Chain up to draw children */ - GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); - - return FALSE; -} - -static gboolean is_rtl (GtkXPaned* xpaned) -{ - if (gtk_widget_get_direction (GTK_WIDGET (xpaned)) == GTK_TEXT_DIR_RTL) - return TRUE; - - return FALSE; -} - -static void update_drag (GtkXPaned* xpaned) -{ - GdkPoint pos; - gint handle_size; - GtkRequisition size; - - gtk_widget_get_pointer (GTK_WIDGET (xpaned), &pos.x, &pos.y); - - if (xpaned->in_drag_vert) - { - pos.y -= xpaned->drag_pos.y; - - if (is_rtl (xpaned)) - { - gtk_widget_style_get (GTK_WIDGET (xpaned), - "handle-size", &handle_size, - NULL); - - size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size; - } - else - { - size.height = pos.y; - } - - size.height -= GTK_CONTAINER (xpaned)->border_width; - - size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y); - - if (size.height != xpaned->top_left_child_size.height) - gtk_xpaned_set_position_y (xpaned, size.height); - } - - if (xpaned->in_drag_horiz) - { - pos.x -= xpaned->drag_pos.x; - - if (is_rtl (xpaned)) - { - gtk_widget_style_get (GTK_WIDGET (xpaned), - "handle-size", &handle_size, - NULL); - - size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size; - } - else - { - size.width = pos.x; - } - - size.width -= GTK_CONTAINER (xpaned)->border_width; - - size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x); - - if (size.width != xpaned->top_left_child_size.width) - gtk_xpaned_set_position_x (xpaned, size.width); - } - - if (xpaned->in_drag_vert_and_horiz) - { - pos.x -= xpaned->drag_pos.x; - pos.y -= xpaned->drag_pos.y; - - if (is_rtl (xpaned)) - { - gtk_widget_style_get (GTK_WIDGET (xpaned), - "handle-size", &handle_size, - NULL); - - size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size; - size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size; - } - else - { - size.width = pos.x; - size.height = pos.y; - } - - size.width -= GTK_CONTAINER (xpaned)->border_width; - size.height -= GTK_CONTAINER (xpaned)->border_width; - - size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x); - size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y); - - if (size.width != xpaned->top_left_child_size.width) - gtk_xpaned_set_position_x (xpaned, size.width); - - if (size.height != xpaned->top_left_child_size.height) - gtk_xpaned_set_position_y (xpaned, size.height); - } -} - -static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - if (xpaned->in_drag_vert || - xpaned->in_drag_horiz || - xpaned->in_drag_vert_and_horiz) - update_drag (xpaned); - else - { - xpaned->handle_prelit = TRUE; - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_east.x, - xpaned->handle_pos_east.y, - xpaned->handle_pos_east.width, - xpaned->handle_pos_east.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_west.x, - xpaned->handle_pos_west.y, - xpaned->handle_pos_west.width, - xpaned->handle_pos_west.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_north.x, - xpaned->handle_pos_north.y, - xpaned->handle_pos_north.width, - xpaned->handle_pos_north.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_south.x, - xpaned->handle_pos_south.y, - xpaned->handle_pos_south.width, - xpaned->handle_pos_south.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_middle.x, - xpaned->handle_pos_middle.y, - xpaned->handle_pos_middle.width, - xpaned->handle_pos_middle.height); - } - - return TRUE; -} - -static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - if (xpaned->in_drag_vert || - xpaned->in_drag_horiz || - xpaned->in_drag_vert_and_horiz) - update_drag (xpaned); - else - { - xpaned->handle_prelit = FALSE; - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_east.x, - xpaned->handle_pos_east.y, - xpaned->handle_pos_east.width, - xpaned->handle_pos_east.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_west.x, - xpaned->handle_pos_west.y, - xpaned->handle_pos_west.width, - xpaned->handle_pos_west.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_north.x, - xpaned->handle_pos_north.y, - xpaned->handle_pos_north.width, - xpaned->handle_pos_north.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_south.x, - xpaned->handle_pos_south.y, - xpaned->handle_pos_south.width, - xpaned->handle_pos_south.height); - - gtk_widget_queue_draw_area (widget, - xpaned->handle_pos_middle.x, - xpaned->handle_pos_middle.y, - xpaned->handle_pos_middle.width, - xpaned->handle_pos_middle.height); - } - - return TRUE; -} - -static gboolean gtk_xpaned_focus (GtkWidget* widget, GtkDirectionType direction) -{ - gboolean retval; - - /* This is a hack, but how can this be done without - * excessive cut-and-paste from gtkcontainer.c? - */ - - GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); - retval = (* GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction); - GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); - - return retval; -} - -static gboolean gtk_xpaned_button_press (GtkWidget* widget, - GdkEventButton* event) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - /* if any child is currently maximized, jump right back */ - if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || - xpaned->maximized[GTK_XPANED_TOP_RIGHT] || - xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || - xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - return FALSE; - - /* if user is dragging the handles around */ - if (!xpaned->in_drag_vert_and_horiz && - event->window != xpaned->handle_east && - event->window != xpaned->handle_west && - event->window != xpaned->handle_north && - event->window != xpaned->handle_south && - event->window == xpaned->handle_middle && - event->button == 1) - { - xpaned->in_drag_vert_and_horiz = TRUE; - - /* We need a server grab here, not gtk_grab_add(), since - * we don't want to pass events on to the widget's children */ - if (gdk_pointer_grab (xpaned->handle_middle, - FALSE, - GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON1_MOTION_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK, - NULL, - NULL, - event->time) == GDK_GRAB_SUCCESS) - { - } - - xpaned->drag_pos.x = event->x; - xpaned->drag_pos.y = event->y; - - return TRUE; - } - else if (!xpaned->in_drag_vert && - event->window == xpaned->handle_east && - event->window != xpaned->handle_west && - event->window != xpaned->handle_north && - event->window != xpaned->handle_south && - event->window != xpaned->handle_middle && - event->button == 1) - { - xpaned->in_drag_vert = TRUE; - - /* We need a server grab here, not gtk_grab_add(), since - * we don't want to pass events on to the widget's children */ - if (gdk_pointer_grab (xpaned->handle_east, - FALSE, - GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON1_MOTION_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK, - NULL, - NULL, - event->time) == GDK_GRAB_SUCCESS) - { - } - - xpaned->drag_pos.y = event->y; - - return TRUE; - } - else if (!xpaned->in_drag_vert && - event->window != xpaned->handle_east && - event->window == xpaned->handle_west && - event->window != xpaned->handle_north && - event->window != xpaned->handle_south && - event->window != xpaned->handle_middle && - event->button == 1) - { - xpaned->in_drag_vert = TRUE; - - /* We need a server grab here, not gtk_grab_add(), since - * we don't want to pass events on to the widget's children */ - if (gdk_pointer_grab (xpaned->handle_west, - FALSE, - GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON1_MOTION_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK, - NULL, - NULL, - event->time) == GDK_GRAB_SUCCESS) - { - } - - xpaned->drag_pos.y = event->y; - - return TRUE; - } - else if (!xpaned->in_drag_horiz && - event->window != xpaned->handle_east && - event->window != xpaned->handle_west && - event->window == xpaned->handle_north && - event->window != xpaned->handle_south && - event->window != xpaned->handle_middle && - event->button == 1) - { - xpaned->in_drag_horiz = TRUE; - - /* We need a server grab here, not gtk_grab_add(), since - * we don't want to pass events on to the widget's children */ - if (gdk_pointer_grab (xpaned->handle_north, - FALSE, - GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON1_MOTION_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK, - NULL, - NULL, - event->time) == GDK_GRAB_SUCCESS) - { - } - - xpaned->drag_pos.x = event->x; - - return TRUE; - } - else if (!xpaned->in_drag_horiz && - event->window != xpaned->handle_east && - event->window != xpaned->handle_west && - event->window != xpaned->handle_north && - event->window == xpaned->handle_south && - event->window != xpaned->handle_middle && - event->button == 1) - { - xpaned->in_drag_horiz = TRUE; - - /* We need a server grab here, not gtk_grab_add(), since - * we don't want to pass events on to the widget's children */ - if (gdk_pointer_grab (xpaned->handle_south, - FALSE, - GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON1_MOTION_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK, - NULL, - NULL, - event->time) == GDK_GRAB_SUCCESS) - { - } - - xpaned->drag_pos.x = event->x; - - return TRUE; - } - return FALSE; -} - -static gboolean gtk_xpaned_button_release (GtkWidget* widget, - GdkEventButton* event) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - if (xpaned->in_drag_vert && (event->button == 1)) - { - xpaned->in_drag_vert = FALSE; - xpaned->drag_pos.y = -1; - xpaned->position_set = TRUE; - gdk_display_pointer_ungrab (gtk_widget_get_display (widget), - event->time); - return TRUE; - } - else if (xpaned->in_drag_horiz && (event->button == 1)) - { - xpaned->in_drag_horiz = FALSE; - xpaned->drag_pos.x = -1; - xpaned->position_set = TRUE; - gdk_display_pointer_ungrab (gtk_widget_get_display (widget), - event->time); - return TRUE; - } - else if (xpaned->in_drag_vert_and_horiz && (event->button == 1)) - { - xpaned->in_drag_vert_and_horiz = FALSE; - xpaned->drag_pos.x = -1; - xpaned->drag_pos.y = -1; - xpaned->position_set = TRUE; - gdk_display_pointer_ungrab (gtk_widget_get_display (widget), - event->time); - return TRUE; - } - - return FALSE; -} - -static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event) -{ - GtkXPaned* xpaned = GTK_XPANED (widget); - - if (xpaned->in_drag_vert || - xpaned->in_drag_horiz || - xpaned->in_drag_vert_and_horiz) - - { - update_drag (xpaned); - return TRUE; - } - - return FALSE; -} - -void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget *widget) -{ - gtk_xpaned_pack_top_left (xpaned, widget, FALSE, TRUE); -} - -void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget *widget) -{ - gtk_xpaned_pack_top_right (xpaned, widget, FALSE, TRUE); -} - -void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget *widget) -{ - gtk_xpaned_pack_bottom_left (xpaned, widget, FALSE, TRUE); -} - -void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget *widget) -{ - gtk_xpaned_pack_bottom_right (xpaned, widget, FALSE, TRUE); -} - -void gtk_xpaned_pack_top_left (GtkXPaned* xpaned, - GtkWidget* child, - gboolean resize, - gboolean shrink) -{ - g_return_if_fail (GTK_IS_XPANED (xpaned)); - g_return_if_fail (GTK_IS_WIDGET (child)); - - if (!xpaned->top_left_child) - { - xpaned->top_left_child = child; - xpaned->top_left_child_resize = resize; - xpaned->top_left_child_shrink = shrink; - - gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); - } -} - -void gtk_xpaned_pack_top_right (GtkXPaned* xpaned, - GtkWidget* child, - gboolean resize, - gboolean shrink) -{ - g_return_if_fail (GTK_IS_XPANED (xpaned)); - g_return_if_fail (GTK_IS_WIDGET (child)); - - if (!xpaned->top_right_child) - { - xpaned->top_right_child = child; - xpaned->top_right_child_resize = resize; - xpaned->top_right_child_shrink = shrink; - - gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); - } -} - -void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned, - GtkWidget* child, - gboolean resize, - gboolean shrink) -{ - g_return_if_fail (GTK_IS_XPANED (xpaned)); - g_return_if_fail (GTK_IS_WIDGET (child)); - - if (!xpaned->bottom_left_child) - { - xpaned->bottom_left_child = child; - xpaned->bottom_left_child_resize = resize; - xpaned->bottom_left_child_shrink = shrink; - - gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); - } -} - -void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned, - GtkWidget* child, - gboolean resize, - gboolean shrink) -{ - g_return_if_fail (GTK_IS_XPANED (xpaned)); - g_return_if_fail (GTK_IS_WIDGET (child)); - - if (!xpaned->bottom_right_child) - { - xpaned->bottom_right_child = child; - xpaned->bottom_right_child_resize = resize; - xpaned->bottom_right_child_shrink = shrink; - - gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); - } -} - -static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget) -{ - GtkXPaned* xpaned; - - g_return_if_fail (GTK_IS_XPANED (container)); - - xpaned = GTK_XPANED (container); - - if (!xpaned->top_left_child) - gtk_xpaned_add_top_left (xpaned, widget); - else if (!xpaned->top_right_child) - gtk_xpaned_add_top_right (xpaned, widget); - else if (!xpaned->bottom_left_child) - gtk_xpaned_add_bottom_left (xpaned, widget); - else if (!xpaned->bottom_right_child) - gtk_xpaned_add_bottom_right (xpaned, widget); - else - g_warning ("GtkXPaned cannot have more than 4 children\n"); -} - -static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget) -{ - GtkXPaned* xpaned; - gboolean was_visible; - - xpaned = GTK_XPANED (container); - was_visible = GTK_WIDGET_VISIBLE (widget); - - if (xpaned->top_left_child == widget) - { - gtk_widget_unparent (widget); - - xpaned->top_left_child = NULL; - - if (was_visible && GTK_WIDGET_VISIBLE (container)) - gtk_widget_queue_resize (GTK_WIDGET (container)); - } - else if (xpaned->top_right_child == widget) - { - gtk_widget_unparent (widget); - - xpaned->top_right_child = NULL; - - if (was_visible && GTK_WIDGET_VISIBLE (container)) - gtk_widget_queue_resize (GTK_WIDGET (container)); - } - else if (xpaned->bottom_left_child == widget) - { - gtk_widget_unparent (widget); - - xpaned->bottom_left_child = NULL; - - if (was_visible && GTK_WIDGET_VISIBLE (container)) - gtk_widget_queue_resize (GTK_WIDGET (container)); - } - else if (xpaned->bottom_right_child == widget) - { - gtk_widget_unparent (widget); - - xpaned->bottom_right_child = NULL; - - if (was_visible && GTK_WIDGET_VISIBLE (container)) - gtk_widget_queue_resize (GTK_WIDGET (container)); - } - else - g_warning ("GtkXPaned has no more children attached\n"); - -} - -static void gtk_xpaned_forall (GtkContainer* container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data) -{ - GtkXPaned* xpaned; - - g_return_if_fail (callback != NULL); - - xpaned = GTK_XPANED (container); - - if (xpaned->top_left_child) - (*callback) (xpaned->top_left_child, callback_data); - if (xpaned->top_right_child) - (*callback) (xpaned->top_right_child, callback_data); - if (xpaned->bottom_left_child) - (*callback) (xpaned->bottom_left_child, callback_data); - if (xpaned->bottom_right_child) - (*callback) (xpaned->bottom_right_child, callback_data); -} - -/** - * gtk_xpaned_get_position_x: - * @paned: a #GtkXPaned widget - * - * Obtains the x-position of the divider. - * - * Return value: x-position of the divider - **/ -gint gtk_xpaned_get_position_x (GtkXPaned* xpaned) -{ - g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0); - - return xpaned->top_left_child_size.width; -} - -/** - * gtk_xpaned_get_position_y: - * @paned: a #GtkXPaned widget - * - * Obtains the y-position of the divider. - * - * Return value: y-position of the divider - **/ -gint gtk_xpaned_get_position_y (GtkXPaned* xpaned) -{ - g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0); - - return xpaned->top_left_child_size.height; -} - -/** - * gtk_xpaned_set_position_x: - * @paned: a #GtkXPaned widget - * @xposition: pixel x-position of divider, a negative values - * of a component mean that the position is unset. - * - * Sets the x-position of the divider between the four panes. - **/ -void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition) -{ - GObject* object; - - g_return_if_fail (GTK_IS_XPANED (xpaned)); - - /* if any child is currently maximized, jump right back */ - if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || - xpaned->maximized[GTK_XPANED_TOP_RIGHT] || - xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || - xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - return; - - object = G_OBJECT (xpaned); - - if (xposition >= 0) - { - /* We don't clamp here - the assumption is that - * if the total allocation changes at the same time - * as the position, the position set is with reference - * to the new total size. If only the position changes, - * then clamping will occur in gtk_paned_compute_position() - */ - - xpaned->top_left_child_size.width = xposition; - xpaned->position_set = TRUE; - } - else - { - xpaned->position_set = FALSE; - } - - g_object_freeze_notify (object); - g_object_notify (object, "x-position"); - g_object_notify (object, "position-set"); - g_object_thaw_notify (object); - - gtk_widget_queue_resize (GTK_WIDGET (xpaned)); -} - -/** - * gtk_xpaned_set_position_y: - * @paned: a #GtkXPaned widget - * @yposition: pixel y-position of divider, a negative values - * of a component mean that the position is unset. - * - * Sets the y-position of the divider between the four panes. - **/ -void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition) -{ - GObject* object; - - g_return_if_fail (GTK_IS_XPANED (xpaned)); - - /* if any child is currently maximized, jump right back */ - if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || - xpaned->maximized[GTK_XPANED_TOP_RIGHT] || - xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || - xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - return; - - object = G_OBJECT (xpaned); - - if (yposition >= 0) - { - /* We don't clamp here - the assumption is that - * if the total allocation changes at the same time - * as the position, the position set is with reference - * to the new total size. If only the position changes, - * then clamping will occur in gtk_paned_compute_position() - */ - - xpaned->top_left_child_size.height = yposition; - xpaned->position_set = TRUE; - } - else - { - xpaned->position_set = FALSE; - } - - g_object_freeze_notify (object); - g_object_notify (object, "y-position"); - g_object_notify (object, "position-set"); - g_object_thaw_notify (object); - - gtk_widget_queue_resize (GTK_WIDGET (xpaned)); -} - -/* this call is private and only intended for internal use! */ -void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned) -{ - xpaned->unmaximized_position.x = gtk_xpaned_get_position_x (xpaned); -} - -/* this call is private and only intended for internal use! */ -void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned) -{ - xpaned->unmaximized_position.y = gtk_xpaned_get_position_y (xpaned); -} - -/* this call is private and only intended for internal use! */ -gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned) -{ - return xpaned->unmaximized_position.x; -} - -/* this call is private and only intended for internal use! */ -gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned) -{ - return xpaned->unmaximized_position.y; -} - -/** - * gtk_xpaned_get_top_left_child: - * @xpaned: a #GtkXPaned widget - * - * Obtains the top-left child of the xpaned widget. - * - * Return value: top-left child, or %NULL if it is not set. - * - * Since: 2.4 - **/ -GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned) -{ - g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); - - return xpaned->top_left_child; -} - -/** - * gtk_xpaned_get_top_right_child: - * @xpaned: a #GtkXPaned widget - * - * Obtains the top-right child of the xpaned widget. - * - * Return value: top-right child, or %NULL if it is not set. - * - * Since: 2.4 - **/ -GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned) -{ - g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); - - return xpaned->top_right_child; -} - -/** - * gtk_xpaned_get_bottom_left_child: - * @xpaned: a #GtkXPaned widget - * - * Obtains the bottom-left child of the xpaned widget. - * - * Return value: bottom-left child, or %NULL if it is not set. - * - * Since: 2.4 - **/ -GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned) -{ - g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); - - return xpaned->bottom_left_child; -} - -/** - * gtk_xpaned_get_bottom_right_child: - * @xpaned: a #GtkXPaned widget - * - * Obtains the bottom-right child of the xpaned widget. - * - * Return value: bottom-right child, or %NULL if it is not set. - * - * Since: 2.4 - **/ -GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned) -{ - g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); - - return xpaned->bottom_right_child; -} - -gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize) -{ - if (maximize) - { - /* see if any child is already maximized */ - if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && - !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - { - /* save current position */ - gtk_xpaned_save_unmaximized_x (xpaned); - gtk_xpaned_save_unmaximized_y (xpaned); - - /* set new maximized position */ - gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x); - gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y); - - /* mark maximized flag for top-left child */ - xpaned->maximized[GTK_XPANED_TOP_LEFT] = TRUE; - - return TRUE; - } - /* already one child maximized, report error */ - else - return FALSE; - } - else - { - /* verify that top-left child is really currently maximized */ - if (xpaned->maximized[GTK_XPANED_TOP_LEFT]) - { - /* clear maximized flat for top-left child */ - xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE; - - /* restore unmaximized position */ - gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); - gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); - - return TRUE; - } - /* top-left child is currently not maximized, report error */ - else - return FALSE; - } -} - -gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize) -{ - if (maximize) - { - /* see if any child is already maximized */ - if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && - !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - { - /* save current position */ - gtk_xpaned_save_unmaximized_x (xpaned); - gtk_xpaned_save_unmaximized_y (xpaned); - - /* set new maximized position */ - gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x); - gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y); - - /* mark maximized flag for top-right child */ - xpaned->maximized[GTK_XPANED_TOP_RIGHT] = TRUE; - - return TRUE; - } - /* already one child maximized, report error */ - else - return FALSE; - } - else - { - /* verify that top-right child is really currently maximized */ - if (xpaned->maximized[GTK_XPANED_TOP_RIGHT]) - { - /* clear maximized flat for top-right child */ - xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE; - - /* restore unmaximized position */ - gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); - gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); - - return TRUE; - } - /* top-right child is currently not maximized, report error */ - else - return FALSE; - } -} - -gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize) -{ - if (maximize) - { - /* see if any child is already maximized */ - if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && - !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - { - /* save current position */ - gtk_xpaned_save_unmaximized_x (xpaned); - gtk_xpaned_save_unmaximized_y (xpaned); - - /* set new maximized position */ - gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x); - gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y); - - /* mark maximized flag for bottom-left child */ - xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = TRUE; - - return TRUE; - } - /* already one child maximized, report error */ - else - return FALSE; - } - else - { - /* verify that bottom-left child is really currently maximized */ - if (xpaned->maximized[GTK_XPANED_BOTTOM_LEFT]) - { - /* clear maximized flat for bottom-left child */ - xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE; - - /* restore unmaximized position */ - gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); - gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); - - return TRUE; - } - /* bottom-left child is currently not maximized, report error */ - else - return FALSE; - } -} - -gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize) -{ - if (maximize) - { - /* see if any child is already maximized */ - if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && - !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && - !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - { - /* save current position */ - gtk_xpaned_save_unmaximized_x (xpaned); - gtk_xpaned_save_unmaximized_y (xpaned); - - /* set new maximized position */ - gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x); - gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y); - - /* mark maximized flag for bottom-right child */ - xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = TRUE; - - return TRUE; - } - /* already one child maximized, report error */ - else - return FALSE; - } - else - { - /* verify that bottom-right child is really currently maximized */ - if (xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) - { - /* clear maximized flat for bottom-right child */ - xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE; - - /* restore unmaximized position */ - gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); - gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); - - return TRUE; - } - /* bottom-right child is currently not maximized, report error */ - else - return FALSE; - } -} - -void -gtk_xpaned_compute_position (GtkXPaned* xpaned, - const GtkAllocation* allocation, - GtkRequisition* top_left_child_req, - GtkRequisition* top_right_child_req, - GtkRequisition* bottom_left_child_req, - GtkRequisition* bottom_right_child_req) -{ - GdkPoint old_position; - GdkPoint old_min_position; - GdkPoint old_max_position; - gint handle_size; - gint border_width = GTK_CONTAINER (xpaned)->border_width; - float fX; - float fY; - - g_return_if_fail (GTK_IS_XPANED (xpaned)); - - old_position.x = xpaned->top_left_child_size.width; - old_position.y = xpaned->top_left_child_size.height; - old_min_position.x = xpaned->min_position.x; - old_min_position.y = xpaned->min_position.y; - old_max_position.x = xpaned->max_position.x; - old_max_position.y = xpaned->max_position.y; - - fX = 100.0f * (float) old_position.x / (float) allocation->width; - fY = 100.0f * (float) old_position.y / (float) allocation->height; - - xpaned->min_position.x = xpaned->top_left_child_shrink ? 0 : top_left_child_req->width; - xpaned->min_position.y = xpaned->top_left_child_shrink ? 0 : top_left_child_req->height; - - gtk_widget_style_get (GTK_WIDGET (xpaned), "handle-size", &handle_size, NULL); - - xpaned->max_position.x = allocation->width - 2 * border_width - handle_size; - xpaned->max_position.y = allocation->height - 2 * border_width - handle_size; - if (!xpaned->top_left_child_shrink) - xpaned->max_position.x = MAX (1, xpaned->max_position.x - top_left_child_req->width); - xpaned->max_position.x = MAX (xpaned->min_position.x, xpaned->max_position.x); - - if (!xpaned->position_set) - { - if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) - { - xpaned->top_left_child_size.width = MAX (0, allocation->width - top_right_child_req->width); - xpaned->top_left_child_size.height = MAX (0, allocation->height - top_right_child_req->height); - } - else if (!xpaned->top_left_child_resize && xpaned->top_right_child_resize) - { - xpaned->top_left_child_size.width = top_left_child_req->width; - xpaned->top_left_child_size.height = top_left_child_req->height; - } - else if (top_left_child_req->width + top_right_child_req->width != 0) - { - xpaned->top_left_child_size.width = allocation->width * ((gdouble)top_left_child_req->width / (top_left_child_req->width + top_right_child_req->width)) + 0.5; - } - else if (top_left_child_req->height + top_right_child_req->height != 0) - { - xpaned->top_left_child_size.height = allocation->height * ((gdouble)top_left_child_req->height / (top_left_child_req->height + top_right_child_req->height)) + 0.5; - } - else - { - xpaned->top_left_child_size.width = allocation->width * 0.5 + 0.5; - xpaned->top_left_child_size.height = allocation->height * 0.5 + 0.5; - } - } - else - { - /* If the position was set before the initial allocation. - ** (paned->last_allocation <= 0) just clamp it and leave it. */ - if (xpaned->last_allocation.width > 0) - { - if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) - { - xpaned->top_left_child_size.width += allocation->width - - xpaned->last_allocation.width; - - xpaned->top_left_child_size.height += allocation->height - - xpaned->last_allocation.height; - } - else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize)) - { - xpaned->top_left_child_size.width = allocation->width - * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width)) - + 0.5; - - xpaned->top_left_child_size.height = allocation->height - * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height)) - + 0.5; - } - } - if (xpaned->last_allocation.height > 0) - { - if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) - { - xpaned->top_left_child_size.width += allocation->width - xpaned->last_allocation.width; - xpaned->top_left_child_size.height += allocation->height - xpaned->last_allocation.height; - } - else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize)) - { - xpaned->top_left_child_size.width = allocation->width * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width)) + 0.5; - xpaned->top_left_child_size.height = allocation->height * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height)) + 0.5; - } - } - - } - - xpaned->top_left_child_size.width = CLAMP (xpaned->top_left_child_size.width, - xpaned->min_position.x, - xpaned->max_position.x); - xpaned->top_left_child_size.height = CLAMP (xpaned->top_left_child_size.height, - xpaned->min_position.y, - xpaned->max_position.y); - - xpaned->top_right_child_size.width = CLAMP (xpaned->top_right_child_size.width, - xpaned->min_position.x, - xpaned->max_position.x); - xpaned->top_right_child_size.height = CLAMP (xpaned->top_right_child_size.height, - xpaned->min_position.y, - xpaned->max_position.y); - - xpaned->bottom_left_child_size.width = CLAMP (xpaned->bottom_left_child_size.width, - xpaned->min_position.x, - xpaned->max_position.x); - xpaned->bottom_left_child_size.height = CLAMP (xpaned->bottom_left_child_size.height, - xpaned->min_position.y, - xpaned->max_position.y); - - xpaned->bottom_right_child_size.width = CLAMP (xpaned->bottom_right_child_size.width, - xpaned->min_position.x, - xpaned->max_position.x); - xpaned->bottom_right_child_size.height = CLAMP (xpaned->bottom_right_child_size.height, - xpaned->min_position.y, - xpaned->max_position.y); - - gtk_widget_set_child_visible (xpaned->top_left_child, TRUE); - gtk_widget_set_child_visible (xpaned->top_right_child, TRUE); - gtk_widget_set_child_visible (xpaned->bottom_left_child, TRUE); - gtk_widget_set_child_visible (xpaned->bottom_right_child, TRUE); - - g_object_freeze_notify (G_OBJECT (xpaned)); - - if (xpaned->top_left_child_size.width != old_position.x) - g_object_notify (G_OBJECT (xpaned), "x-position"); - if (xpaned->top_left_child_size.height != old_position.y) - g_object_notify (G_OBJECT (xpaned), "y-position"); - - if (xpaned->top_right_child_size.width != old_position.x) - g_object_notify (G_OBJECT (xpaned), "x-position"); - if (xpaned->top_right_child_size.height != old_position.y) - g_object_notify (G_OBJECT (xpaned), "y-position"); - - if (xpaned->bottom_left_child_size.width != old_position.x) - g_object_notify (G_OBJECT (xpaned), "x-position"); - if (xpaned->bottom_left_child_size.height != old_position.y) - g_object_notify (G_OBJECT (xpaned), "y-position"); - - if (xpaned->bottom_right_child_size.width != old_position.x) - g_object_notify (G_OBJECT (xpaned), "x-position"); - if (xpaned->bottom_right_child_size.height != old_position.y) - g_object_notify (G_OBJECT (xpaned), "y-position"); - - if (xpaned->min_position.x != old_min_position.x) - g_object_notify (G_OBJECT (xpaned), "min-x-position"); - if (xpaned->min_position.y != old_min_position.y) - g_object_notify (G_OBJECT (xpaned), "min-y-position"); - - if (xpaned->max_position.x != old_max_position.x) - g_object_notify (G_OBJECT (xpaned), "max-y-position"); - if (xpaned->max_position.y != old_max_position.y) - g_object_notify (G_OBJECT (xpaned), "max-y-position"); - - g_object_thaw_notify (G_OBJECT (xpaned)); - - xpaned->last_allocation.width = allocation->width; - xpaned->last_allocation.height = allocation->height; - - fX = 100.0f * (float) old_position.x / (float) allocation->width; - fY = 100.0f * (float) old_position.y / (float) allocation->height; -} - -static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget) -{ - if (xpaned->priv->saved_focus) - g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->saved_focus), - (gpointer *)&(xpaned->priv->saved_focus)); - - xpaned->priv->saved_focus = widget; - - if (xpaned->priv->saved_focus) - g_object_add_weak_pointer (G_OBJECT (xpaned->priv->saved_focus), - (gpointer *)&(xpaned->priv->saved_focus)); -} - -static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned, - GtkXPaned* first_xpaned) -{ - if (xpaned->priv->first_xpaned) - g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned), - (gpointer *)&(xpaned->priv->first_xpaned)); - - xpaned->priv->first_xpaned = first_xpaned; - - if (xpaned->priv->first_xpaned) - g_object_add_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned), - (gpointer *)&(xpaned->priv->first_xpaned)); -} - -static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned, - GtkWidget* widget) -{ - if (xpaned->last_top_left_child_focus) - g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus), - (gpointer *)&(xpaned->last_top_left_child_focus)); - - xpaned->last_top_left_child_focus = widget; - - if (xpaned->last_top_left_child_focus) - g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus), - (gpointer *)&(xpaned->last_top_left_child_focus)); -} - -static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned, - GtkWidget *widget) -{ - if (xpaned->last_top_right_child_focus) - g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus), - (gpointer *)&(xpaned->last_top_right_child_focus)); - - xpaned->last_top_right_child_focus = widget; - - if (xpaned->last_top_right_child_focus) - g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus), - (gpointer *)&(xpaned->last_top_right_child_focus)); -} - -static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned, - GtkWidget *widget) -{ - if (xpaned->last_bottom_left_child_focus) - g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus), - (gpointer *)&(xpaned->last_bottom_left_child_focus)); - - xpaned->last_bottom_left_child_focus = widget; - - if (xpaned->last_bottom_left_child_focus) - g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus), - (gpointer *)&(xpaned->last_bottom_left_child_focus)); -} - -static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned, - GtkWidget *widget) -{ - if (xpaned->last_bottom_right_child_focus) - g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus), - (gpointer *)&(xpaned->last_bottom_right_child_focus)); - - xpaned->last_bottom_right_child_focus = widget; - - if (xpaned->last_bottom_right_child_focus) - g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus), - (gpointer *)&(xpaned->last_bottom_right_child_focus)); -} - -static GtkWidget* xpaned_get_focus_widget (GtkXPaned* xpaned) -{ - GtkWidget* toplevel; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); - if (GTK_WIDGET_TOPLEVEL (toplevel)) - return GTK_WINDOW (toplevel)->focus_widget; - - return NULL; -} - -static void gtk_xpaned_set_focus_child (GtkContainer* container, - GtkWidget* focus_child) -{ - GtkXPaned* xpaned; - - g_return_if_fail (GTK_IS_XPANED (container)); - - xpaned = GTK_XPANED (container); - - if (focus_child == NULL) - { - GtkWidget* last_focus; - GtkWidget* w; - - last_focus = xpaned_get_focus_widget (xpaned); - - if (last_focus) - { - /* If there is one or more paned widgets between us and the - * focus widget, we want the topmost of those as last_focus - */ - for (w = last_focus; w != GTK_WIDGET (xpaned); w = w->parent) - if (GTK_IS_XPANED (w)) - last_focus = w; - - if (container->focus_child == xpaned->top_left_child) - gtk_xpaned_set_last_top_left_child_focus (xpaned, last_focus); - else if (container->focus_child == xpaned->top_right_child) - gtk_xpaned_set_last_top_right_child_focus (xpaned, last_focus); - else if (container->focus_child == xpaned->bottom_left_child) - gtk_xpaned_set_last_bottom_left_child_focus (xpaned, last_focus); - else if (container->focus_child == xpaned->bottom_right_child) - gtk_xpaned_set_last_bottom_right_child_focus (xpaned, last_focus); - } - } - - if (parent_class->set_focus_child) - (* parent_class->set_focus_child) (container, focus_child); -} - -static void gtk_xpaned_get_cycle_chain (GtkXPaned* xpaned, - GtkDirectionType direction, - GList** widgets) -{ - GtkContainer* container = GTK_CONTAINER (xpaned); - GtkWidget* ancestor = NULL; - GList* temp_list = NULL; - GList* list; - - if (xpaned->in_recursion) - return; - - g_assert (widgets != NULL); - - if (xpaned->last_top_left_child_focus && - !gtk_widget_is_ancestor (xpaned->last_top_left_child_focus, - GTK_WIDGET (xpaned))) - { - gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL); - } - - if (xpaned->last_top_right_child_focus && - !gtk_widget_is_ancestor (xpaned->last_top_right_child_focus, - GTK_WIDGET (xpaned))) - { - gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL); - } - - if (xpaned->last_bottom_left_child_focus && - !gtk_widget_is_ancestor (xpaned->last_bottom_left_child_focus, - GTK_WIDGET (xpaned))) - { - gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL); - } - - if (xpaned->last_bottom_right_child_focus && - !gtk_widget_is_ancestor (xpaned->last_bottom_right_child_focus, - GTK_WIDGET (xpaned))) - { - gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL); - } - - if (GTK_WIDGET (xpaned)->parent) - ancestor = gtk_widget_get_ancestor (GTK_WIDGET (xpaned)->parent, - GTK_TYPE_XPANED); - - /* The idea here is that temp_list is a list of widgets we want to cycle - * to. The list is prioritized so that the first element is our first - * choice, the next our second, and so on. - * - * We can't just use g_list_reverse(), because we want to try - * paned->last_child?_focus before paned->child?, both when we - * are going forward and backward. - */ - if (direction == GTK_DIR_TAB_FORWARD) - { - if (container->focus_child == xpaned->top_left_child) - { - temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); - temp_list = g_list_append (temp_list, xpaned->top_right_child); - temp_list = g_list_append (temp_list, ancestor); - } - else if (container->focus_child == xpaned->top_right_child) - { - temp_list = g_list_append (temp_list, ancestor); - temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_left_child); - } - else if (container->focus_child == xpaned->bottom_left_child) - { - temp_list = g_list_append (temp_list, ancestor); - temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_right_child); - } - else if (container->focus_child == xpaned->bottom_right_child) - { - temp_list = g_list_append (temp_list, ancestor); - temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->top_left_child); - } - else - { - temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->top_left_child); - temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); - temp_list = g_list_append (temp_list, xpaned->top_right_child); - temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_left_child); - temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_right_child); - temp_list = g_list_append (temp_list, ancestor); - } - } - else - { - if (container->focus_child == xpaned->top_left_child) - { - temp_list = g_list_append (temp_list, ancestor); - temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); - temp_list = g_list_append (temp_list, xpaned->top_right_child); - } - else if (container->focus_child == xpaned->top_right_child) - { - temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_left_child); - temp_list = g_list_append (temp_list, ancestor); - } - else if (container->focus_child == xpaned->bottom_right_child) - { - temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_left_child); - temp_list = g_list_append (temp_list, ancestor); - } - else if (container->focus_child == xpaned->top_right_child) - { - temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_left_child); - temp_list = g_list_append (temp_list, ancestor); - } - else - { - temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_right_child); - temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->bottom_left_child); - temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); - temp_list = g_list_append (temp_list, xpaned->top_right_child); - temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); - temp_list = g_list_append (temp_list, xpaned->top_left_child); - temp_list = g_list_append (temp_list, ancestor); - } - } - - /* Walk the list and expand all the paned widgets. */ - for (list = temp_list; list != NULL; list = list->next) - { - GtkWidget *widget = list->data; - - if (widget) - { - if (GTK_IS_XPANED (widget)) - { - xpaned->in_recursion = TRUE; - gtk_xpaned_get_cycle_chain (GTK_XPANED (widget), - direction, - widgets); - xpaned->in_recursion = FALSE; - } - else - { - *widgets = g_list_append (*widgets, widget); - } - } - } - - g_list_free (temp_list); -} - -static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned, - gboolean reversed) -{ - GList* cycle_chain = NULL; - GList* list; - - GtkDirectionType direction = reversed ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD; - - /* ignore f6 if the handle is focused */ - if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) - return TRUE; - - /* we can't just let the event propagate up the hierarchy, - * because the paned will want to cycle focus _unless_ an - * ancestor paned handles the event - */ - gtk_xpaned_get_cycle_chain (xpaned, direction, &cycle_chain); - - for (list = cycle_chain; list != NULL; list = list->next) - if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction)) - break; - - g_list_free (cycle_chain); - - return TRUE; -} - -static void get_child_xpanes (GtkWidget* widget, GList** xpanes) -{ - if (GTK_IS_XPANED (widget)) - { - GtkXPaned* xpaned = GTK_XPANED (widget); - - get_child_xpanes (xpaned->top_left_child, xpanes); - *xpanes = g_list_prepend (*xpanes, widget); - get_child_xpanes (xpaned->top_right_child, xpanes); - *xpanes = g_list_prepend (*xpanes, widget); - get_child_xpanes (xpaned->bottom_left_child, xpanes); - *xpanes = g_list_prepend (*xpanes, widget); - get_child_xpanes (xpaned->bottom_right_child, xpanes); - } - else if (GTK_IS_CONTAINER (widget)) - { - gtk_container_foreach (GTK_CONTAINER (widget), - (GtkCallback)get_child_xpanes, - xpanes); - } -} - -static GList* get_all_xpanes (GtkXPaned* xpaned) -{ - GtkXPaned* topmost = NULL; - GList* result = NULL; - GtkWidget* w; - - for (w = GTK_WIDGET (xpaned); w != NULL; w = w->parent) - { - if (GTK_IS_XPANED (w)) - topmost = GTK_XPANED (w); - } - - g_assert (topmost); - - get_child_xpanes (GTK_WIDGET (topmost), &result); - - return g_list_reverse (result); -} - -static void gtk_xpaned_find_neighbours (GtkXPaned* xpaned, - GtkXPaned** next, - GtkXPaned** prev) -{ - GList* all_xpanes; - GList* this_link; - - all_xpanes = get_all_xpanes (xpaned); - g_assert (all_xpanes); - - this_link = g_list_find (all_xpanes, xpaned); - - g_assert (this_link); - - if (this_link->next) - *next = this_link->next->data; - else - *next = all_xpanes->data; - - if (this_link->prev) - *prev = this_link->prev->data; - else - *prev = g_list_last (all_xpanes)->data; - - g_list_free (all_xpanes); -} - -static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned, GtkScrollType scroll) -{ - if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) - { - GdkPoint old_position; - GdkPoint new_position; - gint increment; - - enum - { - SINGLE_STEP_SIZE = 1, - PAGE_STEP_SIZE = 75 - }; - - new_position.x = old_position.x = gtk_xpaned_get_position_x (xpaned); - new_position.y = old_position.y = gtk_xpaned_get_position_y (xpaned); - increment = 0; - - switch (scroll) - { - case GTK_SCROLL_STEP_LEFT: - case GTK_SCROLL_STEP_UP: - case GTK_SCROLL_STEP_BACKWARD: - increment = - SINGLE_STEP_SIZE; - break; - - case GTK_SCROLL_STEP_RIGHT: - case GTK_SCROLL_STEP_DOWN: - case GTK_SCROLL_STEP_FORWARD: - increment = SINGLE_STEP_SIZE; - break; - - case GTK_SCROLL_PAGE_LEFT: - case GTK_SCROLL_PAGE_UP: - case GTK_SCROLL_PAGE_BACKWARD: - increment = - PAGE_STEP_SIZE; - break; - - case GTK_SCROLL_PAGE_RIGHT: - case GTK_SCROLL_PAGE_DOWN: - case GTK_SCROLL_PAGE_FORWARD: - increment = PAGE_STEP_SIZE; - break; - - case GTK_SCROLL_START: - new_position.x = xpaned->min_position.x; - new_position.y = xpaned->min_position.y; - break; - - case GTK_SCROLL_END: - new_position.x = xpaned->max_position.x; - new_position.y = xpaned->max_position.y; - break; - - default: - break; - } - - if (increment) - { - if (is_rtl (xpaned)) - increment = -increment; - - new_position.x = old_position.x + increment; - new_position.y = old_position.y + increment; - } - - new_position.x = CLAMP (new_position.x, - xpaned->min_position.x, - xpaned->max_position.x); - - new_position.y = CLAMP (new_position.y, - xpaned->min_position.y, - xpaned->max_position.y); - - if (old_position.x != new_position.x) - gtk_xpaned_set_position_x (xpaned, new_position.x); - - if (old_position.y != new_position.y) - gtk_xpaned_set_position_y (xpaned, new_position.y); - - return TRUE; - } - - return FALSE; -} - -static void gtk_xpaned_restore_focus (GtkXPaned* xpaned) -{ - if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) - { - if (xpaned->priv->saved_focus && - GTK_WIDGET_SENSITIVE (xpaned->priv->saved_focus)) - { - gtk_widget_grab_focus (xpaned->priv->saved_focus); - } - else - { - /* the saved focus is somehow not available for focusing, - * try - * 1) tabbing into the paned widget - * if that didn't work, - * 2) unset focus for the window if there is one - */ - - if (!gtk_widget_child_focus (GTK_WIDGET (xpaned), GTK_DIR_TAB_FORWARD)) - { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); - - if (GTK_IS_WINDOW (toplevel)) - gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); - } - } - - gtk_xpaned_set_saved_focus (xpaned, NULL); - gtk_xpaned_set_first_xpaned (xpaned, NULL); - } -} - -static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned) -{ - if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) - { - xpaned->original_position.x = -1; - xpaned->original_position.y = -1; - gtk_xpaned_restore_focus (xpaned); - - return TRUE; - } - - return FALSE; -} - -static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned) -{ - if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) - { - if (xpaned->original_position.x != -1) - { - gtk_xpaned_set_position_x (xpaned, xpaned->original_position.x); - xpaned->original_position.x = -1; - } - - if (xpaned->original_position.y != -1) - { - gtk_xpaned_set_position_y (xpaned, xpaned->original_position.y); - xpaned->original_position.y = -1; - } - - gtk_xpaned_restore_focus (xpaned); - return TRUE; - } - - return FALSE; -} - -static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned, - gboolean reversed) -{ - GtkXPaned* next; - GtkXPaned* prev; - - if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) - { - GtkXPaned* focus = NULL; - - if (!xpaned->priv->first_xpaned) - { - /* The first_pane has disappeared. As an ad-hoc solution, - * we make the currently focused paned the first_paned. To the - * user this will seem like the paned cycling has been reset. - */ - gtk_xpaned_set_first_xpaned (xpaned, xpaned); - } - - gtk_xpaned_find_neighbours (xpaned, &next, &prev); - - if (reversed && prev && - prev != xpaned && xpaned != xpaned->priv->first_xpaned) - { - focus = prev; - } - else if (!reversed && - next && - next != xpaned && - next != xpaned->priv->first_xpaned) - { - focus = next; - } - else - { - gtk_xpaned_accept_position (xpaned); - return TRUE; - } - - g_assert (focus); - - gtk_xpaned_set_saved_focus (focus, xpaned->priv->saved_focus); - gtk_xpaned_set_first_xpaned (focus, xpaned->priv->first_xpaned); - - gtk_xpaned_set_saved_focus (xpaned, NULL); - gtk_xpaned_set_first_xpaned (xpaned, NULL); - - gtk_widget_grab_focus (GTK_WIDGET (focus)); - - if (!gtk_widget_is_focus (GTK_WIDGET (xpaned))) - { - xpaned->original_position.x = -1; - xpaned->original_position.y = -1; - focus->original_position.x = gtk_xpaned_get_position_x (focus); - focus->original_position.y = gtk_xpaned_get_position_y (focus); - } - } - else - { - GtkContainer* container = GTK_CONTAINER (xpaned); - GtkXPaned* focus; - GtkXPaned* first; - GtkXPaned* prev; - GtkXPaned* next; - GtkWidget* toplevel; - - gtk_xpaned_find_neighbours (xpaned, &next, &prev); - - if (container->focus_child == xpaned->top_left_child) - { - if (reversed) - { - focus = prev; - first = xpaned; - } - else - { - focus = xpaned; - first = xpaned; - } - } - else if (container->focus_child == xpaned->top_right_child) - { - if (reversed) - { - focus = xpaned; - first = next; - } - else - { - focus = next; - first = next; - } - } - else - { - /* Focus is not inside this xpaned, and we don't have focus. - * Presumably this happened because the application wants us - * to start keyboard navigating. - */ - focus = xpaned; - - if (reversed) - first = xpaned; - else - first = next; - } - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); - - if (GTK_IS_WINDOW (toplevel)) - gtk_xpaned_set_saved_focus (focus, GTK_WINDOW (toplevel)->focus_widget); - gtk_xpaned_set_first_xpaned (focus, first); - focus->original_position.x = gtk_xpaned_get_position_x (focus); - focus->original_position.y = gtk_xpaned_get_position_y (focus); - - gtk_widget_grab_focus (GTK_WIDGET (focus)); - } - - return TRUE; -} - -static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned) -{ - /* This function/signal has the wrong name. It is called when you - * press Tab or Shift-Tab and what we do is act as if - * the user pressed Return and then Tab or Shift-Tab - */ - if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) - gtk_xpaned_accept_position (xpaned); - - return FALSE; -} - -/*#define __GTK_XPANED_C__*/ -/*#include "gtkaliasdef.c"*/ diff --git a/lib/gtksheet/gtkxpaned.h b/lib/gtksheet/gtkxpaned.h deleted file mode 100644 index 47e752ee..00000000 --- a/lib/gtksheet/gtkxpaned.h +++ /dev/null @@ -1,175 +0,0 @@ -/******************************************************************************* -**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 -** 10 20 30 40 50 60 70 80 -** -** library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+ -** Copyright (C) 2005-2006 Mirco "MacSlow" Müller -** -** This library is free software; you can redistribute it and/or -** modify it under the terms of the GNU Lesser General Public -** License as published by the Free Software Foundation; either -** version 2.1 of the License, or (at your option) any later version. -** -** This library is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** Lesser General Public License for more details. -** -** You should have received a copy of the GNU Lesser General Public -** License along with this library; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -** -** GtkXPaned is based on GtkPaned which was done by... -** -** "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald" -** -** and later modified by... -** -** "the GTK+ Team and others 1997-2000" -** -*******************************************************************************/ - -#ifndef GTK_XPANED_H -#define GTK_XPANED_H - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_XPANED (gtk_xpaned_get_type ()) -#define GTK_XPANED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_XPANED, GtkXPaned)) -#define GTK_XPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_XPANED, GtkXPanedClass)) -#define GTK_IS_XPANED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_XPANED)) -#define GTK_IS_XPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XPANED)) -#define GTK_XPANED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XPANED, GtkXPanedClass)) - -typedef struct _GtkXPaned GtkXPaned; -typedef struct _GtkXPanedClass GtkXPanedClass; -typedef struct _GtkXPanedPrivate GtkXPanedPrivate; - -typedef enum _GtkXPanedChild -{ - GTK_XPANED_TOP_LEFT = 0, - GTK_XPANED_TOP_RIGHT, - GTK_XPANED_BOTTOM_LEFT, - GTK_XPANED_BOTTOM_RIGHT -} GtkXPanedChild; - -struct _GtkXPaned -{ - GtkContainer container; - - GtkWidget* top_left_child; - GtkWidget* top_right_child; - GtkWidget* bottom_left_child; - GtkWidget* bottom_right_child; - - GdkWindow* handle_east; - GdkWindow* handle_west; - GdkWindow* handle_north; - GdkWindow* handle_south; - GdkWindow* handle_middle; - GdkGC* xor_gc; - GdkCursorType cursor_type_east; - GdkCursorType cursor_type_west; - GdkCursorType cursor_type_north; - GdkCursorType cursor_type_south; - GdkCursorType cursor_type_middle; - - /*< private >*/ - GdkRectangle handle_pos_east; - GdkRectangle handle_pos_west; - GdkRectangle handle_pos_north; - GdkRectangle handle_pos_south; - GdkRectangle handle_pos_middle; - GtkRequisition top_left_child_size; - GtkRequisition top_right_child_size; - GtkRequisition bottom_left_child_size; - GtkRequisition bottom_right_child_size; - - GtkRequisition last_allocation; - GdkPoint min_position; - GdkPoint max_position; - gboolean maximized[4]; - - guint position_set : 1; - guint in_drag_vert : 1; - guint in_drag_horiz : 1; - guint in_drag_vert_and_horiz : 1; - guint top_left_child_shrink : 1; - guint top_left_child_resize : 1; - guint top_right_child_shrink : 1; - guint top_right_child_resize : 1; - guint bottom_left_child_shrink : 1; - guint bottom_left_child_resize : 1; - guint bottom_right_child_shrink : 1; - guint bottom_right_child_resize : 1; - guint in_recursion : 1; - guint handle_prelit : 1; - - GtkWidget* last_top_left_child_focus; - GtkWidget* last_top_right_child_focus; - GtkWidget* last_bottom_left_child_focus; - GtkWidget* last_bottom_right_child_focus; - GtkXPanedPrivate* priv; - - GdkPoint drag_pos; - GdkPoint original_position; - GdkPoint unmaximized_position; -}; - -struct _GtkXPanedClass -{ - GtkContainerClass parent_class; - gboolean (*cycle_child_focus) (GtkXPaned* xpaned, gboolean reverse); - gboolean (*toggle_handle_focus) (GtkXPaned* xpaned); - gboolean (*move_handle) (GtkXPaned* xpaned, GtkScrollType scroll); - gboolean (*cycle_handle_focus) (GtkXPaned* xpaned, gboolean reverse); - gboolean (*accept_position) (GtkXPaned* xpaned); - gboolean (*cancel_position) (GtkXPaned* xpaned); -}; - -GType gtk_xpaned_get_type (void) G_GNUC_CONST; -GtkWidget* gtk_xpaned_new (void); -void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget* child); -void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget* child); -void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget* child); -void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget* child); -void gtk_xpaned_pack_top_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); -void gtk_xpaned_pack_top_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); -void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); -void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); -gint gtk_xpaned_get_position_x (GtkXPaned* xpaned); -gint gtk_xpaned_get_position_y (GtkXPaned* xpaned); -void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition); -void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition); -void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned); -void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned); -gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned); -gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned); -GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned); -GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned); -GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned); -GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned); -gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize); -gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize); -gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize); -gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize); - -/* Internal function */ -#if !defined (GTK_DISABLE_DEPRECATED) || defined (GTK_COMPILATION) -void gtk_xpaned_compute_position (GtkXPaned* xpaned, - const GtkAllocation* allocation, - GtkRequisition* top_left_child_req, - GtkRequisition* top_right_child_req, - GtkRequisition* bottom_left_child_req, - GtkRequisition* bottom_right_child_req); -#endif /* !GTK_DISABLE_DEPRECATED || GTK_COMPILATION */ -#ifndef GTK_DISABLE_DEPRECATED -#define gtk_xpaned_gutter_size(p,s) (void) 0 -#define gtk_xpaned_set_gutter_size(p,s) (void) 0 -#endif /* GTK_DISABLE_DEPRECATED */ - -G_END_DECLS - -#endif /* GTK_XPANED_H */ diff --git a/lib/gtksheet/psppire-sheet.c b/lib/gtksheet/psppire-sheet.c deleted file mode 100644 index dac561a1..00000000 --- a/lib/gtksheet/psppire-sheet.c +++ /dev/null @@ -1,5465 +0,0 @@ -/* - Copyright (C) 2006, 2008 Free Software Foundation - - 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 3 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, see . - - - This file is derived from the gtksheet.c and extensively modified for the - requirements of PSPPIRE. The changes are copyright by the - Free Software Foundation. The copyright notice for the original work is - below. -*/ - -/* 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 Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * SECTION:psppiresheet - * @short_description: spreadsheet widget for gtk2 - * - * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of - * cells where you can allocate text. Cell contents can be edited interactively - * through a specially designed entry, GtkItemEntry. - * - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "psppire-sheet.h" -#include -#include -#include -#include - -/* sheet flags */ -enum - { - PSPPIRE_SHEET_IN_XDRAG = 1 << 1, - PSPPIRE_SHEET_IN_YDRAG = 1 << 2, - PSPPIRE_SHEET_IN_DRAG = 1 << 3, - PSPPIRE_SHEET_IN_SELECTION = 1 << 4, - PSPPIRE_SHEET_IN_RESIZE = 1 << 5 - }; - -#define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags) -#define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag)) -#define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag)) - -#define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG) -#define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG) -#define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG) -#define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION) -#define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE) - -#define CELL_SPACING 1 - -#define TIMEOUT_HOVER 300 -#define COLUMN_MIN_WIDTH 10 -#define COLUMN_TITLES_HEIGHT 4 -#define DEFAULT_COLUMN_WIDTH 80 -#define DEFAULT_ROW_HEIGHT 25 - -static void set_entry_widget_font (PsppireSheet *sheet); - -static void psppire_sheet_update_primary_selection (PsppireSheet *sheet); -static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n); -static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n); -static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range); - - -static void set_row_height (PsppireSheet *sheet, - gint row, - gint height); - -static void destroy_hover_window (PsppireSheetHoverTitle *); -static PsppireSheetHoverTitle *create_hover_window (void); - -static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col); - - -static inline void -dispose_string (const PsppireSheet *sheet, gchar *text) -{ - PsppireSheetModel *model = psppire_sheet_get_model (sheet); - - if ( ! model ) - return; - - if (psppire_sheet_model_free_strings (model)) - g_free (text); -} - - -/* FIXME: Why bother with these two ? */ - -/* returns the column index from a pixel location */ -static inline gint -column_from_xpixel (const PsppireSheet *sheet, gint pixel) -{ - return psppire_axis_unit_at_pixel (sheet->haxis, pixel); -} - -static inline gint -row_from_ypixel (const PsppireSheet *sheet, gint pixel) -{ - return psppire_axis_unit_at_pixel (sheet->vaxis, pixel); -} - - -/* Return the lowest row number which is wholly or partially on - the visible range of the sheet */ -static inline glong -min_visible_row (const PsppireSheet *sheet) -{ - return row_from_ypixel (sheet, sheet->vadjustment->value); -} - -static inline glong -min_fully_visible_row (const PsppireSheet *sheet) -{ - glong row = min_visible_row (sheet); - - if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value) - row++; - - return row; -} - -static inline glong -max_visible_row (const PsppireSheet *sheet) -{ - return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size); -} - - -static inline glong -max_fully_visible_row (const PsppireSheet *sheet) -{ - glong row = max_visible_row (sheet); - - if ( psppire_axis_start_pixel (sheet->vaxis, row) - + - psppire_axis_unit_size (sheet->vaxis, row) - > sheet->vadjustment->value) - row--; - - return row; -} - - -/* Returns the lowest column number which is wholly or partially - on the sheet */ -static inline glong -min_visible_column (const PsppireSheet *sheet) -{ - return column_from_xpixel (sheet, sheet->hadjustment->value); -} - -static inline glong -min_fully_visible_column (const PsppireSheet *sheet) -{ - glong col = min_visible_column (sheet); - - if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value) - col++; - - return col; -} - - -/* Returns the highest column number which is wholly or partially - on the sheet */ -static inline glong -max_visible_column (const PsppireSheet *sheet) -{ - return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size); -} - -static inline glong -max_fully_visible_column (const PsppireSheet *sheet) -{ - glong col = max_visible_column (sheet); - - if ( psppire_axis_start_pixel (sheet->haxis, col) - + - psppire_axis_unit_size (sheet->haxis, col) - > sheet->hadjustment->value) - col--; - - return col; -} - - - -/* The size of the region (in pixels) around the row/column boundaries - where the height/width may be grabbed to change size */ -#define DRAG_WIDTH 6 - -static gboolean -on_column_boundary (const PsppireSheet *sheet, gint x, gint *column) -{ - gint col; - gint pixel; - - x += sheet->hadjustment->value; - - if ( x < 0) - return FALSE; - - col = column_from_xpixel (sheet, x); - - pixel = x - DRAG_WIDTH / 2; - if (pixel < 0) - pixel = 0; - - if ( column_from_xpixel (sheet, pixel) < col ) - { - *column = col - 1; - return TRUE; - } - - if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col ) - { - *column = col; - return TRUE; - } - - return FALSE; -} - -static gboolean -on_row_boundary (const PsppireSheet *sheet, gint y, gint *row) -{ - gint r; - gint pixel; - - y += sheet->vadjustment->value; - - if ( y < 0) - return FALSE; - - r = row_from_ypixel (sheet, y); - - pixel = y - DRAG_WIDTH / 2; - if (pixel < 0) - pixel = 0; - - if ( row_from_ypixel (sheet, pixel) < r ) - { - *row = r - 1; - return TRUE; - } - - if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r ) - { - *row = r; - return TRUE; - } - - return FALSE; -} - - -static inline gboolean -POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y, - gint *drag_row, gint *drag_column) -{ - gint ydrag, xdrag; - - /* Can't drag if nothing is selected */ - if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || - sheet->range.col0 < 0 || sheet->range.coli < 0 ) - return FALSE; - - *drag_column = column_from_xpixel (sheet, x); - *drag_row = row_from_ypixel (sheet, y); - - if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 && - x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) + - psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2) - { - ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0); - if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) - { - *drag_row = sheet->range.row0; - return TRUE; - } - ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + - psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi); - if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) - { - *drag_row = sheet->range.rowi; - return TRUE; - } - } - - if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 && - y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + - psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2) - { - xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0); - if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) - { - *drag_column = sheet->range.col0; - return TRUE; - } - xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) + - psppire_axis_unit_size (sheet->haxis, sheet->range.coli); - if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) - { - *drag_column = sheet->range.coli; - return TRUE; - } - } - - return FALSE; -} - -static inline gboolean -POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y, - gint *drag_row, gint *drag_column) -{ - gint xdrag, ydrag; - - /* Can't drag if nothing is selected */ - if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || - sheet->range.col0 < 0 || sheet->range.coli < 0 ) - return FALSE; - - xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+ - psppire_axis_unit_size (sheet->haxis, sheet->range.coli); - - ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + - psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi); - - if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) - ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet)); - - if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) - xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet)); - - *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 gboolean -rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range, - GdkRectangle *r) -{ - g_return_val_if_fail (range, FALSE); - - r->x = psppire_axis_start_pixel (sheet->haxis, range->col0); - r->x -= round (sheet->hadjustment->value); - - r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0); - r->y -= round (sheet->vadjustment->value); - - r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) - - psppire_axis_start_pixel (sheet->haxis, range->col0) + - psppire_axis_unit_size (sheet->haxis, range->coli); - - r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) - - psppire_axis_start_pixel (sheet->vaxis, range->row0) + - psppire_axis_unit_size (sheet->vaxis, range->rowi); - - if ( sheet->column_titles_visible) - { - r->y += sheet->column_title_area.height; - } - - if ( sheet->row_titles_visible) - { - r->x += sheet->row_title_area.width; - } - - return TRUE; -} - -static gboolean -rectangle_from_cell (PsppireSheet *sheet, gint row, gint col, - GdkRectangle *r) -{ - PsppireSheetRange range; - g_return_val_if_fail (row >= 0, FALSE); - g_return_val_if_fail (col >= 0, FALSE); - - range.row0 = range.rowi = row; - range.col0 = range.coli = col; - - return rectangle_from_range (sheet, &range, r); -} - - -static void psppire_sheet_class_init (PsppireSheetClass *klass); -static void psppire_sheet_init (PsppireSheet *sheet); -static void psppire_sheet_dispose (GObject *object); -static void psppire_sheet_finalize (GObject *object); -static void psppire_sheet_style_set (GtkWidget *widget, - GtkStyle *previous_style); -static void psppire_sheet_realize (GtkWidget *widget); -static void psppire_sheet_unrealize (GtkWidget *widget); -static void psppire_sheet_map (GtkWidget *widget); -static void psppire_sheet_unmap (GtkWidget *widget); -static gint psppire_sheet_expose (GtkWidget *widget, - GdkEventExpose *event); - -static void psppire_sheet_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data); - -static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); - -static gint psppire_sheet_button_press (GtkWidget *widget, - GdkEventButton *event); -static gint psppire_sheet_button_release (GtkWidget *widget, - GdkEventButton *event); -static gint psppire_sheet_motion (GtkWidget *widget, - GdkEventMotion *event); -static gboolean psppire_sheet_crossing_notify (GtkWidget *widget, - GdkEventCrossing *event); -static gint psppire_sheet_entry_key_press (GtkWidget *widget, - GdkEventKey *key); -static gboolean psppire_sheet_key_press (GtkWidget *widget, - GdkEventKey *key); -static void psppire_sheet_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void psppire_sheet_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); - -/* Sheet queries */ - -static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet, - const PsppireSheetRange *range); -static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet, - gint row, gint column); -/* Drawing Routines */ - -/* draw cell */ -static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column); - - -/* draw visible part of range. */ -static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region); - - -/* highlight the visible part of the selected range */ -static void psppire_sheet_range_draw_selection (PsppireSheet *sheet, - PsppireSheetRange range); - -/* Selection */ - -static void psppire_sheet_real_select_range (PsppireSheet *sheet, - const PsppireSheetRange *range); -static void psppire_sheet_real_unselect_range (PsppireSheet *sheet, - const PsppireSheetRange *range); -static void psppire_sheet_extend_selection (PsppireSheet *sheet, - gint row, gint column); -static void psppire_sheet_new_selection (PsppireSheet *sheet, - PsppireSheetRange *range); -static void psppire_sheet_draw_border (PsppireSheet *sheet, - PsppireSheetRange range); - -/* Active Cell handling */ - -static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet); -static void change_active_cell (PsppireSheet *sheet, gint row, gint col); -static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet); -static void psppire_sheet_show_entry_widget (PsppireSheet *sheet); -static gboolean psppire_sheet_click_cell (PsppireSheet *sheet, - gint row, - gint column); - - -/* Scrollbars */ - -static void adjust_scrollbars (PsppireSheet *sheet); -static void vadjustment_value_changed (GtkAdjustment *adjustment, - gpointer data); -static void hadjustment_value_changed (GtkAdjustment *adjustment, - gpointer data); - - -static void draw_xor_vline (PsppireSheet *sheet); -static void draw_xor_hline (PsppireSheet *sheet); -static void draw_xor_rectangle (PsppireSheet *sheet, - PsppireSheetRange range); - -/* Sheet Button */ - -static void create_global_button (PsppireSheet *sheet); -static void global_button_clicked (GtkWidget *widget, - gpointer data); -/* Sheet Entry */ - -static void create_sheet_entry (PsppireSheet *sheet); -static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet); - -/* Sheet button gadgets */ - -static void draw_column_title_buttons (PsppireSheet *sheet); -static void draw_row_title_buttons (PsppireSheet *sheet); - - -static void size_allocate_global_button (PsppireSheet *sheet); -static void psppire_sheet_button_size_request (PsppireSheet *sheet, - const PsppireSheetButton *button, - GtkRequisition *requisition); - -static void psppire_sheet_real_cell_clear (PsppireSheet *sheet, - gint row, - gint column); - - -/* Signals */ -enum - { - SELECT_ROW, - SELECT_COLUMN, - DOUBLE_CLICK_ROW, - DOUBLE_CLICK_COLUMN, - BUTTON_EVENT_ROW, - BUTTON_EVENT_COLUMN, - SELECT_RANGE, - RESIZE_RANGE, - MOVE_RANGE, - TRAVERSE, - ACTIVATE, - LAST_SIGNAL - }; - -static GtkContainerClass *parent_class = NULL; -static guint sheet_signals[LAST_SIGNAL] = { 0 }; - - -GType -psppire_sheet_get_type () -{ - static GType sheet_type = 0; - - if (!sheet_type) - { - static const GTypeInfo sheet_info = - { - sizeof (PsppireSheetClass), - NULL, - NULL, - (GClassInitFunc) psppire_sheet_class_init, - NULL, - NULL, - sizeof (PsppireSheet), - 0, - (GInstanceInitFunc) psppire_sheet_init, - NULL, - }; - - sheet_type = - g_type_register_static (GTK_TYPE_BIN, "PsppireSheet", - &sheet_info, 0); - } - return sheet_type; -} - - - -static PsppireSheetRange* -psppire_sheet_range_copy (const PsppireSheetRange *range) -{ - PsppireSheetRange *new_range; - - g_return_val_if_fail (range != NULL, NULL); - - new_range = g_new (PsppireSheetRange, 1); - - *new_range = *range; - - return new_range; -} - -static void -psppire_sheet_range_free (PsppireSheetRange *range) -{ - g_return_if_fail (range != NULL); - - g_free (range); -} - -GType -psppire_sheet_range_get_type (void) -{ - static GType sheet_range_type = 0; - - if (!sheet_range_type) - { - sheet_range_type = - g_boxed_type_register_static ("PsppireSheetRange", - (GBoxedCopyFunc) psppire_sheet_range_copy, - (GBoxedFreeFunc) psppire_sheet_range_free); - } - - return sheet_range_type; -} - -static PsppireSheetCell* -psppire_sheet_cell_copy (const PsppireSheetCell *cell) -{ - PsppireSheetCell *new_cell; - - g_return_val_if_fail (cell != NULL, NULL); - - new_cell = g_new (PsppireSheetCell, 1); - - *new_cell = *cell; - - return new_cell; -} - -static void -psppire_sheet_cell_free (PsppireSheetCell *cell) -{ - g_return_if_fail (cell != NULL); - - g_free (cell); -} - -GType -psppire_sheet_cell_get_type (void) -{ - static GType sheet_cell_type = 0; - - if (!sheet_cell_type) - { - sheet_cell_type = - g_boxed_type_register_static ("PsppireSheetCell", - (GBoxedCopyFunc) psppire_sheet_cell_copy, - (GBoxedFreeFunc) psppire_sheet_cell_free); - } - - return sheet_cell_type; -} - - -/* Properties */ -enum - { - PROP_0, - PROP_VAXIS, - PROP_HAXIS, - PROP_MODEL - }; - -static void -resize_column (PsppireSheet *sheet, gint unit, glong size) -{ - PsppireSheetRange range; - range.col0 = unit; - range.coli = max_visible_column (sheet); - range.row0 = min_visible_row (sheet); - range.rowi = max_visible_row (sheet); - - redraw_range (sheet, &range); - - draw_column_title_buttons_range (sheet, range.col0, range.coli); -} - - -static void -psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a) -{ - if ( sheet->haxis ) - g_object_unref (sheet->haxis); - - sheet->haxis = a; - g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet); - - if ( sheet->haxis ) - g_object_ref (sheet->haxis); -} - -static void -resize_row (PsppireSheet *sheet, gint unit, glong size) -{ - PsppireSheetRange range; - range.col0 = min_visible_column (sheet); - range.coli = max_visible_column (sheet); - range.row0 = unit; - range.rowi = max_visible_row (sheet); - - redraw_range (sheet, &range); - - draw_row_title_buttons_range (sheet, range.row0, range.rowi); -} - -static void -psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a) -{ - if ( sheet->vaxis ) - g_object_unref (sheet->vaxis); - - sheet->vaxis = a; - - g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet); - - if ( sheet->vaxis ) - g_object_ref (sheet->vaxis); -} - - -static void -psppire_sheet_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) - -{ - PsppireSheet *sheet = PSPPIRE_SHEET (object); - - switch (prop_id) - { - case PROP_VAXIS: - psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value)); - break; - case PROP_HAXIS: - psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value)); - break; - case PROP_MODEL: - psppire_sheet_set_model (sheet, g_value_get_pointer (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - }; -} - -static void -psppire_sheet_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (object); - - switch (prop_id) - { - case PROP_VAXIS: - g_value_set_pointer (value, sheet->vaxis); - break; - case PROP_HAXIS: - g_value_set_pointer (value, sheet->haxis); - break; - case PROP_MODEL: - g_value_set_pointer (value, sheet->model); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - }; -} - - -static void -psppire_sheet_class_init (PsppireSheetClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - GParamSpec *haxis_spec ; - GParamSpec *vaxis_spec ; - GParamSpec *model_spec ; - - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - /** - * PsppireSheet::select-row - * @sheet: the sheet widget that emitted the signal - * @row: the newly selected row index - * - * A row has been selected. - */ - sheet_signals[SELECT_ROW] = - g_signal_new ("select-row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, select_row), - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * PsppireSheet::select - column - * @sheet: the sheet widget that emitted the signal - * @column: the newly selected column index - * - * A column has been selected. - */ - sheet_signals[SELECT_COLUMN] = - g_signal_new ("select-column", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, select_column), - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * PsppireSheet::double-click-row - * @sheet: the sheet widget that emitted the signal - * @row: the row that was double clicked. - * - * A row's title button has been double clicked - */ - sheet_signals[DOUBLE_CLICK_ROW] = - g_signal_new ("double-click-row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * PsppireSheet::double-click-column - * @sheet: the sheet widget that emitted the signal - * @column: the column that was double clicked. - * - * A column's title button has been double clicked - */ - sheet_signals[DOUBLE_CLICK_COLUMN] = - g_signal_new ("double-click-column", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * PsppireSheet::button-event-column - * @sheet: the sheet widget that emitted the signal - * @column: the column on which the event occured. - * - * A button event occured on a column title button - */ - sheet_signals[BUTTON_EVENT_COLUMN] = - g_signal_new ("button-event-column", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - psppire_marshal_VOID__INT_POINTER, - G_TYPE_NONE, - 2, - G_TYPE_INT, - G_TYPE_POINTER - ); - - - /** - * PsppireSheet::button-event-row - * @sheet: the sheet widget that emitted the signal - * @column: the column on which the event occured. - * - * A button event occured on a row title button - */ - sheet_signals[BUTTON_EVENT_ROW] = - g_signal_new ("button-event-row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - psppire_marshal_VOID__INT_POINTER, - G_TYPE_NONE, - 2, - G_TYPE_INT, - G_TYPE_POINTER - ); - - - sheet_signals[SELECT_RANGE] = - g_signal_new ("select-range", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, select_range), - NULL, NULL, - g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, - 1, - PSPPIRE_TYPE_SHEET_RANGE); - - - sheet_signals[RESIZE_RANGE] = - g_signal_new ("resize-range", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, resize_range), - NULL, NULL, - psppire_marshal_VOID__BOXED_BOXED, - G_TYPE_NONE, - 2, - PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE - ); - - sheet_signals[MOVE_RANGE] = - g_signal_new ("move-range", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, move_range), - NULL, NULL, - psppire_marshal_VOID__BOXED_BOXED, - G_TYPE_NONE, - 2, - PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE - ); - - sheet_signals[TRAVERSE] = - g_signal_new ("traverse", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, traverse), - NULL, NULL, - psppire_marshal_BOOLEAN__BOXED_POINTER, - G_TYPE_BOOLEAN, 2, - PSPPIRE_TYPE_SHEET_CELL, - G_TYPE_POINTER); - - - sheet_signals[ACTIVATE] = - g_signal_new ("activate", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, activate), - NULL, NULL, - psppire_marshal_VOID__INT_INT_INT_INT, - G_TYPE_NONE, 4, - G_TYPE_INT, G_TYPE_INT, - G_TYPE_INT, G_TYPE_INT); - - widget_class->set_scroll_adjustments_signal = - g_signal_new ("set-scroll-adjustments", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (PsppireSheetClass, set_scroll_adjustments), - NULL, NULL, - psppire_marshal_VOID__OBJECT_OBJECT, - G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); - - - container_class->add = NULL; - container_class->remove = NULL; - container_class->forall = psppire_sheet_forall; - - object_class->dispose = psppire_sheet_dispose; - object_class->finalize = psppire_sheet_finalize; - - - vaxis_spec = - g_param_spec_pointer ("vertical-axis", - "Vertical Axis", - "A pointer to the PsppireAxis object for the rows", - G_PARAM_READABLE | G_PARAM_WRITABLE ); - - haxis_spec = - g_param_spec_pointer ("horizontal-axis", - "Horizontal Axis", - "A pointer to the PsppireAxis object for the columns", - G_PARAM_READABLE | G_PARAM_WRITABLE ); - - model_spec = - g_param_spec_pointer ("model", - "Model", - "A pointer to the data model", - G_PARAM_READABLE | G_PARAM_WRITABLE ); - - - object_class->set_property = psppire_sheet_set_property; - object_class->get_property = psppire_sheet_get_property; - - g_object_class_install_property (object_class, - PROP_VAXIS, - vaxis_spec); - - g_object_class_install_property (object_class, - PROP_HAXIS, - haxis_spec); - - g_object_class_install_property (object_class, - PROP_MODEL, - model_spec); - - - widget_class->realize = psppire_sheet_realize; - widget_class->unrealize = psppire_sheet_unrealize; - widget_class->map = psppire_sheet_map; - widget_class->unmap = psppire_sheet_unmap; - widget_class->style_set = psppire_sheet_style_set; - widget_class->button_press_event = psppire_sheet_button_press; - widget_class->button_release_event = psppire_sheet_button_release; - widget_class->motion_notify_event = psppire_sheet_motion; - widget_class->enter_notify_event = psppire_sheet_crossing_notify; - widget_class->leave_notify_event = psppire_sheet_crossing_notify; - widget_class->key_press_event = psppire_sheet_key_press; - widget_class->expose_event = psppire_sheet_expose; - widget_class->size_request = psppire_sheet_size_request; - widget_class->size_allocate = psppire_sheet_size_allocate; - widget_class->focus_in_event = NULL; - widget_class->focus_out_event = NULL; - - klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments; - klass->select_row = NULL; - klass->select_column = NULL; - klass->select_range = NULL; - klass->resize_range = NULL; - klass->move_range = NULL; - klass->traverse = NULL; - klass->activate = NULL; - klass->changed = NULL; -} - -static void -psppire_sheet_init (PsppireSheet *sheet) -{ - sheet->model = NULL; - sheet->haxis = NULL; - sheet->vaxis = NULL; - - sheet->flags = 0; - sheet->selection_mode = GTK_SELECTION_NONE; - sheet->state = PSPPIRE_SHEET_NORMAL; - - GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW); - GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS); - - 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; - - 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->range.row0 = 0; - sheet->range.rowi = 0; - sheet->range.col0 = 0; - sheet->range.coli = 0; - - sheet->state = PSPPIRE_SHEET_NORMAL; - - sheet->sheet_window = NULL; - sheet->entry_widget = NULL; - sheet->button = NULL; - - sheet->hadjustment = NULL; - sheet->vadjustment = NULL; - - sheet->cursor_drag = NULL; - - sheet->xor_gc = NULL; - sheet->fg_gc = NULL; - sheet->bg_gc = NULL; - sheet->x_drag = 0; - sheet->y_drag = 0; - sheet->show_grid = TRUE; - - sheet->motion_timer = 0; - - sheet->row_titles_visible = TRUE; - sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH; - - sheet->column_titles_visible = TRUE; - - - /* create sheet entry */ - sheet->entry_type = GTK_TYPE_ENTRY; - create_sheet_entry (sheet); - - /* create global selection button */ - create_global_button (sheet); -} - - -/* Cause RANGE to be redrawn. If RANGE is null, then the - entire visible range will be redrawn. - */ -static void -redraw_range (PsppireSheet *sheet, PsppireSheetRange *range) -{ - GdkRectangle rect; - - if ( ! GTK_WIDGET_REALIZED (sheet)) - return; - - if ( NULL != range ) - rectangle_from_range (sheet, range, &rect); - else - { - GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window); - gdk_region_get_clipbox (r, &rect); - - if ( sheet->column_titles_visible) - { - rect.y += sheet->column_title_area.height; - rect.height -= sheet->column_title_area.height; - } - - if ( sheet->row_titles_visible) - { - rect.x += sheet->row_title_area.width; - rect.width -= sheet->row_title_area.width; - } - } - - gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE); -} - - -/* Callback which occurs whenever columns are inserted / deleted in the model */ -static void -columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column, - gint n_columns, - gpointer data) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (data); - - PsppireSheetRange range; - gint model_columns = psppire_sheet_model_get_column_count (model); - - - /* Need to update all the columns starting from the first column and onwards. - * Previous column are unchanged, so don't need to be updated. - */ - range.col0 = first_column; - range.row0 = 0; - range.coli = psppire_axis_unit_count (sheet->haxis) - 1; - range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; - - adjust_scrollbars (sheet); - - if (sheet->active_cell.col >= model_columns) - change_active_cell (sheet, sheet->active_cell.row, model_columns - 1); - - draw_column_title_buttons_range (sheet, - first_column, max_visible_column (sheet)); - - - redraw_range (sheet, &range); -} - - - - -/* Callback which occurs whenever rows are inserted / deleted in the model */ -static void -rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row, - gint n_rows, gpointer data) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (data); - - PsppireSheetRange range; - - gint model_rows = psppire_sheet_model_get_row_count (model); - - /* Need to update all the rows starting from the first row and onwards. - * Previous rows are unchanged, so don't need to be updated. - */ - range.row0 = first_row; - range.col0 = 0; - range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; - range.coli = psppire_axis_unit_count (sheet->haxis) - 1; - - adjust_scrollbars (sheet); - - if (sheet->active_cell.row >= model_rows) - change_active_cell (sheet, model_rows - 1, sheet->active_cell.col); - - draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet)); - - redraw_range (sheet, &range); -} - -/* - If row0 or rowi are negative, then all rows will be updated. - If col0 or coli are negative, then all columns will be updated. -*/ -static void -range_update_callback (PsppireSheetModel *m, gint row0, gint col0, - gint rowi, gint coli, gpointer data) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (data); - - PsppireSheetRange range; - - range.row0 = row0; - range.col0 = col0; - range.rowi = rowi; - range.coli = coli; - - if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) ) - { - redraw_range (sheet, NULL); - adjust_scrollbars (sheet); - - draw_row_title_buttons_range (sheet, min_visible_row (sheet), - max_visible_row (sheet)); - - draw_column_title_buttons_range (sheet, min_visible_column (sheet), - max_visible_column (sheet)); - - return; - } - else if ( row0 < 0 || rowi < 0 ) - { - range.row0 = min_visible_row (sheet); - range.rowi = max_visible_row (sheet); - } - else if ( col0 < 0 || coli < 0 ) - { - range.col0 = min_visible_column (sheet); - range.coli = max_visible_column (sheet); - } - - redraw_range (sheet, &range); -} - - -/** - * psppire_sheet_new: - * @rows: initial number of rows - * @columns: initial number of columns - * @title: sheet title - * @model: the model to use for the sheet data - * - * Creates a new sheet widget with the given number of rows and columns. - * - * Returns: the new sheet widget - */ -GtkWidget * -psppire_sheet_new (PsppireSheetModel *model) -{ - GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET, - "model", model, - NULL); - return widget; -} - - -/** - * psppire_sheet_set_model - * @sheet: the sheet to set the model for - * @model: the model to use for the sheet data - * - * Sets the model for a PsppireSheet - * - */ -void -psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model) -{ - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (sheet->model ) g_object_unref (sheet->model); - - sheet->model = model; - - if ( model) - { - g_object_ref (model); - - sheet->update_handler_id = g_signal_connect (model, "range_changed", - G_CALLBACK (range_update_callback), - sheet); - - g_signal_connect (model, "rows_inserted", - G_CALLBACK (rows_inserted_deleted_callback), sheet); - - g_signal_connect (model, "rows_deleted", - G_CALLBACK (rows_inserted_deleted_callback), sheet); - - g_signal_connect (model, "columns_inserted", - G_CALLBACK (columns_inserted_deleted_callback), sheet); - - g_signal_connect (model, "columns_deleted", - G_CALLBACK (columns_inserted_deleted_callback), sheet); - } -} - - -void -psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type) -{ - gint state; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - state = sheet->state; - - if (sheet->state == PSPPIRE_SHEET_NORMAL) - psppire_sheet_hide_entry_widget (sheet); - - sheet->entry_type = entry_type; - - create_sheet_entry (sheet); - - if (state == PSPPIRE_SHEET_NORMAL) - { - psppire_sheet_show_entry_widget (sheet); - } - -} - -void -psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (show == sheet->show_grid) return; - - sheet->show_grid = show; - - redraw_range (sheet, NULL); -} - -gboolean -psppire_sheet_grid_visible (PsppireSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); - - return sheet->show_grid; -} - -guint -psppire_sheet_get_columns_count (PsppireSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); - - return psppire_axis_unit_count (sheet->haxis); -} - -static void set_column_width (PsppireSheet *sheet, - gint column, - gint width); - - -void -psppire_sheet_show_column_titles (PsppireSheet *sheet) -{ - if (sheet->column_titles_visible) return; - - sheet->column_titles_visible = TRUE; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - 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); - - adjust_scrollbars (sheet); - - if (sheet->vadjustment) - g_signal_emit_by_name (sheet->vadjustment, - "value_changed"); - - size_allocate_global_button (sheet); - - if ( sheet->row_titles_visible) - gtk_widget_show (sheet->button); -} - - -void -psppire_sheet_show_row_titles (PsppireSheet *sheet) -{ - if (sheet->row_titles_visible) return; - - sheet->row_titles_visible = TRUE; - - - 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); - - adjust_scrollbars (sheet); - } - - if (sheet->hadjustment) - g_signal_emit_by_name (sheet->hadjustment, - "value_changed"); - - size_allocate_global_button (sheet); - - if ( sheet->column_titles_visible) - gtk_widget_show (sheet->button); -} - -void -psppire_sheet_hide_column_titles (PsppireSheet *sheet) -{ - if (!sheet->column_titles_visible) return; - - sheet->column_titles_visible = FALSE; - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - if (sheet->column_title_window) - gdk_window_hide (sheet->column_title_window); - - gtk_widget_hide (sheet->button); - - adjust_scrollbars (sheet); - } - - if (sheet->vadjustment) - g_signal_emit_by_name (sheet->vadjustment, - "value_changed"); -} - -void -psppire_sheet_hide_row_titles (PsppireSheet *sheet) -{ - if (!sheet->row_titles_visible) return; - - sheet->row_titles_visible = FALSE; - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - if (sheet->row_title_window) - gdk_window_hide (sheet->row_title_window); - - gtk_widget_hide (sheet->button); - - adjust_scrollbars (sheet); - } - - if (sheet->hadjustment) - g_signal_emit_by_name (sheet->hadjustment, - "value_changed"); -} - - -/* Scroll the sheet so that the cell ROW, COLUMN is visible. - If {ROW,COL}_ALIGN is zero, then the cell will be placed - at the {top,left} of the sheet. If it's 1, then it'll - be placed at the {bottom,right}. - ROW or COL may be -1, in which case scrolling in that dimension - does not occur. - */ -void -psppire_sheet_moveto (PsppireSheet *sheet, - gint row, - gint col, - gfloat row_align, - gfloat col_align) -{ - gint width, height; - - g_return_if_fail (row_align >= 0); - g_return_if_fail (col_align >= 0); - - g_return_if_fail (row_align <= 1); - g_return_if_fail (col_align <= 1); - - g_return_if_fail (col < - psppire_axis_unit_count (sheet->haxis)); - g_return_if_fail (row < - psppire_axis_unit_count (sheet->vaxis)); - - gdk_drawable_get_size (sheet->sheet_window, &width, &height); - - - if (row >= 0) - { - gint y = psppire_axis_start_pixel (sheet->vaxis, row); - - gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align); - } - - - if (col >= 0) - { - gint x = psppire_axis_start_pixel (sheet->haxis, col); - - gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align); - } -} - - -void -psppire_sheet_select_row (PsppireSheet *sheet, gint row) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis)) - return; - - if (sheet->state != PSPPIRE_SHEET_NORMAL) - psppire_sheet_real_unselect_range (sheet, NULL); - - sheet->state = PSPPIRE_SHEET_ROW_SELECTED; - sheet->range.row0 = row; - sheet->range.col0 = 0; - sheet->range.rowi = row; - sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1; - sheet->active_cell.row = row; - - g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row); - psppire_sheet_real_select_range (sheet, NULL); -} - - -void -psppire_sheet_select_column (PsppireSheet *sheet, gint column) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis)) - return; - - if (sheet->state != PSPPIRE_SHEET_NORMAL) - psppire_sheet_real_unselect_range (sheet, NULL); - - sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED; - sheet->range.row0 = 0; - sheet->range.col0 = column; - sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; - sheet->range.coli = column; - sheet->active_cell.col = column; - - g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column); - psppire_sheet_real_select_range (sheet, NULL); -} - - - - -static gboolean -psppire_sheet_range_isvisible (const PsppireSheet *sheet, - const PsppireSheetRange *range) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - - if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis)) - return FALSE; - - if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis)) - return FALSE; - - if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis)) - return FALSE; - - if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis)) - 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 gboolean -psppire_sheet_cell_isvisible (PsppireSheet *sheet, - gint row, gint column) -{ - PsppireSheetRange range; - - range.row0 = row; - range.col0 = column; - range.rowi = row; - range.coli = column; - - return psppire_sheet_range_isvisible (sheet, &range); -} - -void -psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_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); -} - - -static gboolean -psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment) -{ - if ( sheet->vadjustment != vadjustment ) - { - if (sheet->vadjustment) - g_object_unref (sheet->vadjustment); - sheet->vadjustment = vadjustment; - - if ( vadjustment) - { - g_object_ref (vadjustment); - - g_signal_connect (sheet->vadjustment, "value_changed", - G_CALLBACK (vadjustment_value_changed), - sheet); - } - } - - if ( sheet->hadjustment != hadjustment ) - { - if (sheet->hadjustment) - g_object_unref (sheet->hadjustment); - - sheet->hadjustment = hadjustment; - - if ( hadjustment) - { - g_object_ref (hadjustment); - - g_signal_connect (sheet->hadjustment, "value_changed", - G_CALLBACK (hadjustment_value_changed), - sheet); - } - } - return TRUE; -} - -static void -psppire_sheet_finalize (GObject *object) -{ - PsppireSheet *sheet; - - g_return_if_fail (object != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (object)); - - sheet = PSPPIRE_SHEET (object); - - if (G_OBJECT_CLASS (parent_class)->finalize) - (*G_OBJECT_CLASS (parent_class)->finalize) (object); -} - -static void -psppire_sheet_dispose (GObject *object) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (object); - - g_return_if_fail (object != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (object)); - - if ( sheet->dispose_has_run ) - return ; - - sheet->dispose_has_run = TRUE; - - if (sheet->model) g_object_unref (sheet->model); - if (sheet->vaxis) g_object_unref (sheet->vaxis); - if (sheet->haxis) g_object_unref (sheet->haxis); - - g_object_unref (sheet->button); - sheet->button = NULL; - - /* unref adjustments */ - if (sheet->hadjustment) - { - g_signal_handlers_disconnect_matched (sheet->hadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, - sheet); - - g_object_unref (sheet->hadjustment); - sheet->hadjustment = NULL; - } - - if (sheet->vadjustment) - { - g_signal_handlers_disconnect_matched (sheet->vadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, - sheet); - - g_object_unref (sheet->vadjustment); - - sheet->vadjustment = NULL; - } - - if (G_OBJECT_CLASS (parent_class)->dispose) - (*G_OBJECT_CLASS (parent_class)->dispose) (object); -} - -static void -psppire_sheet_style_set (GtkWidget *widget, - GtkStyle *previous_style) -{ - PsppireSheet *sheet; - - g_return_if_fail (widget != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (widget)); - - if (GTK_WIDGET_CLASS (parent_class)->style_set) - (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style); - - sheet = PSPPIRE_SHEET (widget); - - if (GTK_WIDGET_REALIZED (widget)) - { - gtk_style_set_background (widget->style, widget->window, widget->state); - } - - set_entry_widget_font (sheet); -} - -#define BORDER_WIDTH 3 - -static void -psppire_sheet_realize (GtkWidget *widget) -{ - PsppireSheet *sheet; - GdkWindowAttr attributes; - const gint attributes_mask = - GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR; - - GdkGCValues values; - GdkColormap *colormap; - GdkDisplay *display; - - g_return_if_fail (widget != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (widget)); - - sheet = PSPPIRE_SHEET (widget); - - colormap = gtk_widget_get_colormap (widget); - display = gtk_widget_get_display (widget); - - 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 = colormap; - - 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_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK); - - attributes.cursor = gdk_cursor_new_for_display (display, 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); - - gdk_color_parse ("white", &sheet->color[BG_COLOR]); - gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE, - TRUE); - gdk_color_parse ("gray", &sheet->color[GRID_COLOR]); - gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE, - TRUE); - - attributes.x = 0; - 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; - 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_for_display (display, GDK_PLUS); - - attributes.x = 0; - attributes.y = 0; - - sheet->sheet_window = gdk_window_new (widget->window, - &attributes, attributes_mask); - gdk_window_set_user_data (sheet->sheet_window, sheet); - - gdk_cursor_unref (attributes.cursor); - - gdk_window_set_background (sheet->sheet_window, &widget->style->white); - gdk_window_show (sheet->sheet_window); - - /* GCs */ - sheet->fg_gc = gdk_gc_new (widget->window); - sheet->bg_gc = gdk_gc_new (widget->window); - - values.foreground = widget->style->white; - values.function = GDK_INVERT; - values.subwindow_mode = GDK_INCLUDE_INFERIORS; - values.line_width = BORDER_WIDTH; - - sheet->xor_gc = gdk_gc_new_with_values (widget->window, - &values, - GDK_GC_FOREGROUND | - GDK_GC_FUNCTION | - GDK_GC_SUBWINDOW | - GDK_GC_LINE_WIDTH - ); - - - gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); - gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); - - gtk_widget_set_parent_window (sheet->button, sheet->sheet_window); - gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet)); - - - sheet->cursor_drag = gdk_cursor_new_for_display (display, 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); - - sheet->hover_window = create_hover_window (); - - draw_row_title_buttons (sheet); - draw_column_title_buttons (sheet); - - psppire_sheet_update_primary_selection (sheet); - - - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); -} - -static void -create_global_button (PsppireSheet *sheet) -{ - sheet->button = gtk_button_new_with_label (" "); - - GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS); - - g_object_ref_sink (sheet->button); - - g_signal_connect (sheet->button, - "pressed", - G_CALLBACK (global_button_clicked), - sheet); -} - -static void -size_allocate_global_button (PsppireSheet *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); -} - -static void -global_button_clicked (GtkWidget *widget, gpointer data) -{ - psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1); -} - - -static void -psppire_sheet_unrealize (GtkWidget *widget) -{ - PsppireSheet *sheet; - - g_return_if_fail (widget != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (widget)); - - sheet = PSPPIRE_SHEET (widget); - - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = NULL; - - gdk_colormap_free_colors (gtk_widget_get_colormap (widget), - sheet->color, n_COLORS); - - g_object_unref (sheet->xor_gc); - g_object_unref (sheet->fg_gc); - g_object_unref (sheet->bg_gc); - - destroy_hover_window (sheet->hover_window); - - gdk_window_destroy (sheet->sheet_window); - gdk_window_destroy (sheet->column_title_window); - gdk_window_destroy (sheet->row_title_window); - - gtk_widget_unparent (sheet->entry_widget); - if (sheet->button != NULL) - gtk_widget_unparent (sheet->button); - - if (GTK_WIDGET_CLASS (parent_class)->unrealize) - (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); -} - -static void -psppire_sheet_map (GtkWidget *widget) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (widget); - - g_return_if_fail (widget != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (widget)); - - if (!GTK_WIDGET_MAPPED (widget)) - { - GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); - - gdk_window_show (widget->window); - gdk_window_show (sheet->sheet_window); - - if (sheet->column_titles_visible) - { - draw_column_title_buttons (sheet); - gdk_window_show (sheet->column_title_window); - } - if (sheet->row_titles_visible) - { - draw_row_title_buttons (sheet); - gdk_window_show (sheet->row_title_window); - } - - if (!GTK_WIDGET_MAPPED (sheet->entry_widget) - && sheet->active_cell.row >= 0 - && sheet->active_cell.col >= 0 ) - { - gtk_widget_show (sheet->entry_widget); - gtk_widget_map (sheet->entry_widget); - } - - if (!GTK_WIDGET_MAPPED (sheet->button)) - { - gtk_widget_show (sheet->button); - gtk_widget_map (sheet->button); - } - - redraw_range (sheet, NULL); - change_active_cell (sheet, - sheet->active_cell.row, - sheet->active_cell.col); - } -} - -static void -psppire_sheet_unmap (GtkWidget *widget) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (widget); - - if (!GTK_WIDGET_MAPPED (widget)) - return; - - 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->entry_widget)) - gtk_widget_unmap (sheet->entry_widget); - - if (GTK_WIDGET_MAPPED (sheet->button)) - gtk_widget_unmap (sheet->button); -} - - -static void -psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col) -{ - PangoLayout *layout; - PangoRectangle text; - PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc; - gint font_height; - - gchar *label; - - PsppireSheetCellAttr attributes; - GdkRectangle area; - - g_return_if_fail (sheet != NULL); - - /* bail now if we aren't yet drawable */ - if (!GTK_WIDGET_DRAWABLE (sheet)) return; - - if (row < 0 || - row >= psppire_axis_unit_count (sheet->vaxis)) - return; - - if (col < 0 || - col >= psppire_axis_unit_count (sheet->haxis)) - return; - - psppire_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); - - rectangle_from_cell (sheet, row, col, &area); - - gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0); - - if (sheet->show_grid) - { - gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]); - - gdk_draw_rectangle (sheet->sheet_window, - sheet->bg_gc, - FALSE, - area.x, area.y, - area.width, area.height); - } - - - label = psppire_sheet_cell_get_text (sheet, row, col); - if (NULL == label) - return; - - - layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label); - dispose_string (sheet, label); - - - pango_layout_set_font_description (layout, font_desc); - - pango_layout_get_pixel_extents (layout, NULL, &text); - - gdk_gc_set_clip_rectangle (sheet->fg_gc, &area); - - font_height = pango_font_description_get_size (font_desc); - if ( !pango_font_description_get_size_is_absolute (font_desc)) - font_height /= PANGO_SCALE; - - /* Centre the text vertically */ - area.y += (area.height - font_height) / 2.0; - - switch (attributes.justification) - { - case GTK_JUSTIFY_RIGHT: - area.x += area.width - text.width; - break; - case GTK_JUSTIFY_CENTER: - area.x += (area.width - text.width) / 2.0; - break; - case GTK_JUSTIFY_LEFT: - /* Do nothing */ - break; - default: - g_critical ("Unhandled justification %d in column %d\n", - attributes.justification, col); - break; - } - - gdk_draw_layout (sheet->sheet_window, sheet->fg_gc, - area.x, - area.y, - layout); - - gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL); - g_object_unref (layout); -} - - -static void -draw_sheet_region (PsppireSheet *sheet, GdkRegion *region) -{ - PsppireSheetRange range; - GdkRectangle area; - gint y, x; - gint i, j; - - PsppireSheetRange drawing_range; - - gdk_region_get_clipbox (region, &area); - - y = area.y + sheet->vadjustment->value; - x = area.x + sheet->hadjustment->value; - - if ( sheet->column_titles_visible) - y -= sheet->column_title_area.height; - - if ( sheet->row_titles_visible) - x -= sheet->row_title_area.width; - - maximize_int (&x, 0); - maximize_int (&y, 0); - - range.row0 = row_from_ypixel (sheet, y); - range.rowi = row_from_ypixel (sheet, y + area.height); - - range.col0 = column_from_xpixel (sheet, x); - range.coli = column_from_xpixel (sheet, x + area.width); - - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_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; - - - 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)); - - g_return_if_fail (drawing_range.rowi >= drawing_range.row0); - g_return_if_fail (drawing_range.coli >= drawing_range.col0); - - for (i = drawing_range.row0; i <= drawing_range.rowi; i++) - { - for (j = drawing_range.col0; j <= drawing_range.coli; j++) - psppire_sheet_cell_draw (sheet, i, j); - } - - if (sheet->state != PSPPIRE_SHEET_NORMAL && - psppire_sheet_range_isvisible (sheet, &sheet->range)) - psppire_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) - psppire_sheet_show_entry_widget (sheet); -} - - -static void -psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range) -{ - GdkRectangle area; - gint i, j; - PsppireSheetRange aux; - - if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 || - range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0) - return; - - if (!psppire_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 (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED) - { - rectangle_from_cell (sheet, i, j, &area); - - 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); - } - } - - } - } - - psppire_sheet_draw_border (sheet, sheet->range); -} - -static inline gint -safe_strcmp (const gchar *s1, const gchar *s2) -{ - if ( !s1 && !s2) return 0; - if ( !s1) return -1; - if ( !s2) return +1; - return strcmp (s1, s2); -} - -static void -psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col, - GtkJustification justification, - const gchar *text) -{ - PsppireSheetModel *model ; - gchar *old_text ; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (col >= psppire_axis_unit_count (sheet->haxis) - || row >= psppire_axis_unit_count (sheet->vaxis)) - return; - - if (col < 0 || row < 0) return; - - model = psppire_sheet_get_model (sheet); - - old_text = psppire_sheet_model_get_string (model, row, col); - - if (0 != safe_strcmp (old_text, text)) - { - g_signal_handler_block (sheet->model, sheet->update_handler_id); - psppire_sheet_model_set_string (model, text, row, col); - g_signal_handler_unblock (sheet->model, sheet->update_handler_id); - } - - if ( psppire_sheet_model_free_strings (model)) - g_free (old_text); -} - - -void -psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column) -{ - PsppireSheetRange range; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - if (column >= psppire_axis_unit_count (sheet->haxis) || - row >= psppire_axis_unit_count (sheet->vaxis)) return; - - if (column < 0 || row < 0) return; - - range.row0 = row; - range.rowi = row; - range.col0 = min_visible_column (sheet); - range.coli = max_visible_column (sheet); - - psppire_sheet_real_cell_clear (sheet, row, column); - - redraw_range (sheet, &range); -} - -static void -psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column) -{ - PsppireSheetModel *model = psppire_sheet_get_model (sheet); - - gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column); - - if (old_text && strlen (old_text) > 0 ) - { - psppire_sheet_model_datum_clear (model, row, column); - } - - dispose_string (sheet, old_text); -} - -gchar * -psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col) -{ - PsppireSheetModel *model; - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); - - if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) - return NULL; - if (col < 0 || row < 0) return NULL; - - model = psppire_sheet_get_model (sheet); - - if ( !model ) - return NULL; - - return psppire_sheet_model_get_string (model, row, col); -} - - -static GtkStateType -psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col) -{ - gint state; - PsppireSheetRange *range; - - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); - if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0; - if (col < 0 || row < 0) return 0; - - state = sheet->state; - range = &sheet->range; - - switch (state) - { - case PSPPIRE_SHEET_NORMAL: - return GTK_STATE_NORMAL; - break; - case PSPPIRE_SHEET_ROW_SELECTED: - if (row >= range->row0 && row <= range->rowi) - return GTK_STATE_SELECTED; - break; - case PSPPIRE_SHEET_COLUMN_SELECTED: - if (col >= range->col0 && col <= range->coli) - return GTK_STATE_SELECTED; - break; - case PSPPIRE_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; -} - -/* Convert X, Y (in pixels) to *ROW, *COLUMN - If the function returns FALSE, then the results will be unreliable. -*/ -static gboolean -psppire_sheet_get_pixel_info (PsppireSheet *sheet, - gint x, - gint y, - gint *row, - gint *column) -{ - gint trow, tcol; - *row = -G_MAXINT; - *column = -G_MAXINT; - - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); - - /* bounds checking, return false if the user clicked - on a blank area */ - if (y < 0) - return FALSE; - - if (x < 0) - return FALSE; - - if ( sheet->column_titles_visible) - y -= sheet->column_title_area.height; - - y += sheet->vadjustment->value; - - if ( y < 0 && sheet->column_titles_visible) - { - trow = -1; - } - else - { - trow = row_from_ypixel (sheet, y); - if (trow > psppire_axis_unit_count (sheet->vaxis)) - return FALSE; - } - - *row = trow; - - if ( sheet->row_titles_visible) - x -= sheet->row_title_area.width; - - x += sheet->hadjustment->value; - - if ( x < 0 && sheet->row_titles_visible) - { - tcol = -1; - } - else - { - tcol = column_from_xpixel (sheet, x); - if (tcol > psppire_axis_unit_count (sheet->haxis)) - return FALSE; - } - - *column = tcol; - - return TRUE; -} - -gboolean -psppire_sheet_get_cell_area (PsppireSheet *sheet, - gint row, - gint column, - GdkRectangle *area) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); - - if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis)) - return FALSE; - - area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column); - area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row); - - area->width= (column == -1) ? sheet->row_title_area.width - : psppire_axis_unit_size (sheet->haxis, column); - - area->height= (row == -1) ? sheet->column_title_area.height - : psppire_axis_unit_size (sheet->vaxis, row); - - return TRUE; -} - -void -psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (row < -1 || col < -1) - return; - - if (row >= psppire_axis_unit_count (sheet->vaxis) - || - col >= psppire_axis_unit_count (sheet->haxis)) - return; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - if ( row == -1 || col == -1) - { - psppire_sheet_hide_entry_widget (sheet); - return; - } - - change_active_cell (sheet, row, col); -} - -void -psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if ( row ) *row = sheet->active_cell.row; - if (column) *column = sheet->active_cell.col; -} - -static void -entry_load_text (PsppireSheet *sheet) -{ - gint row, col; - const char *text; - GtkJustification justification; - PsppireSheetCellAttr attributes; - - if (!GTK_WIDGET_VISIBLE (sheet->entry_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; - - text = gtk_entry_get_text (psppire_sheet_get_entry (sheet)); - - if (text && strlen (text) > 0) - { - psppire_sheet_get_attributes (sheet, row, col, &attributes); - justification = attributes.justification; - psppire_sheet_set_cell (sheet, row, col, justification, text); - } -} - - -static void -psppire_sheet_hide_entry_widget (PsppireSheet *sheet) -{ - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - if (sheet->active_cell.row < 0 || - sheet->active_cell.col < 0) return; - - gtk_widget_hide (sheet->entry_widget); - gtk_widget_unmap (sheet->entry_widget); - - GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); -} - -static void -change_active_cell (PsppireSheet *sheet, gint row, gint col) -{ - gint old_row, old_col; - - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (row < 0 || col < 0) - return; - - if ( row > psppire_axis_unit_count (sheet->vaxis) - || col > psppire_axis_unit_count (sheet->haxis)) - return; - - if (sheet->state != PSPPIRE_SHEET_NORMAL) - { - sheet->state = PSPPIRE_SHEET_NORMAL; - psppire_sheet_real_unselect_range (sheet, NULL); - } - - old_row = sheet->active_cell.row; - old_col = sheet->active_cell.col; - - /* Erase the old cell */ - psppire_sheet_draw_active_cell (sheet); - - entry_load_text (sheet); - - 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; - - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - - GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS); - - psppire_sheet_draw_active_cell (sheet); - psppire_sheet_show_entry_widget (sheet); - - GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS); - - g_signal_emit (sheet, sheet_signals [ACTIVATE], 0, - row, col, old_row, old_col); - -} - -static void -psppire_sheet_show_entry_widget (PsppireSheet *sheet) -{ - GtkEntry *sheet_entry; - PsppireSheetCellAttr attributes; - - gint row, col; - - g_return_if_fail (PSPPIRE_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 != PSPPIRE_SHEET_NORMAL) return; - if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return; - - GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); - - sheet_entry = psppire_sheet_get_entry (sheet); - - psppire_sheet_get_attributes (sheet, row, col, &attributes); - - if (GTK_IS_ENTRY (sheet_entry)) - { - gchar *text = psppire_sheet_cell_get_text (sheet, row, col); - const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry)); - - if ( ! text ) - text = g_strdup (""); - - if (strcmp (old_text, text) != 0) - gtk_entry_set_text (sheet_entry, text); - - dispose_string (sheet, text); - - { - switch (attributes.justification) - { - case GTK_JUSTIFY_RIGHT: - gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0); - break; - case GTK_JUSTIFY_CENTER: - gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5); - break; - case GTK_JUSTIFY_LEFT: - default: - gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0); - break; - } - } - } - - psppire_sheet_size_allocate_entry (sheet); - - gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry), - psppire_sheet_model_is_editable (sheet->model, - row, col)); - gtk_widget_map (sheet->entry_widget); -} - -static gboolean -psppire_sheet_draw_active_cell (PsppireSheet *sheet) -{ - gint row, col; - PsppireSheetRange range; - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - if (row < 0 || col < 0) return FALSE; - - if (!psppire_sheet_cell_isvisible (sheet, row, col)) - return FALSE; - - range.col0 = range.coli = col; - range.row0 = range.rowi = row; - - psppire_sheet_draw_border (sheet, range); - - return FALSE; -} - - - -static void -psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range) -{ - gint i, j, mask1, mask2; - gint state, selected; - gint x, y, width, height; - PsppireSheetRange 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 = psppire_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 && - (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 = psppire_axis_start_pixel (sheet->haxis, j); - y = psppire_axis_start_pixel (sheet->vaxis, i); - width = psppire_axis_start_pixel (sheet->haxis, j)- x+ - psppire_axis_unit_size (sheet->haxis, j); - height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); - - 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; - - if (i != sheet->active_cell.row || j != sheet->active_cell.col) - { - x = psppire_axis_start_pixel (sheet->haxis, j); - y = psppire_axis_start_pixel (sheet->vaxis, i); - width = psppire_axis_start_pixel (sheet->haxis, j)- x+ - psppire_axis_unit_size (sheet->haxis, j); - - height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); - - 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 = psppire_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) - { - - x = psppire_axis_start_pixel (sheet->haxis, j); - y = psppire_axis_start_pixel (sheet->vaxis, i); - width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j); - height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); - - 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; - - } - } - } - - for (i = range->row0; i <= range->rowi; i++) - { - for (j = range->col0; j <= range->coli; j++) - { - - state = psppire_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 && - (i != sheet->active_cell.row || j != sheet->active_cell.col)) - { - - x = psppire_axis_start_pixel (sheet->haxis, j); - y = psppire_axis_start_pixel (sheet->vaxis, i); - width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j); - height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); - - 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++) - { - state = psppire_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 = psppire_axis_start_pixel (sheet->haxis, j); - y = psppire_axis_start_pixel (sheet->vaxis, i); - width = psppire_axis_unit_size (sheet->haxis, j); - height = psppire_axis_unit_size (sheet->vaxis, i); - 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; -} - - - -static void -psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range) -{ - GdkRectangle area; - - rectangle_from_range (sheet, &new_range, &area); - - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - FALSE, - area.x, area.y, - area.width, area.height); -} - - -static void -psppire_sheet_real_select_range (PsppireSheet *sheet, - const PsppireSheetRange *range) -{ - gint state; - - g_return_if_fail (sheet != NULL); - - if (range == NULL) range = &sheet->range; - - memcpy (&sheet->range, range, sizeof (*range)); - - if (range->row0 < 0 || range->rowi < 0) return; - if (range->col0 < 0 || range->coli < 0) return; - - state = sheet->state; - -#if 0 - if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 || - range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0) - { - psppire_sheet_new_selection (sheet, &sheet->range); - } - else - { - psppire_sheet_range_draw_selection (sheet, sheet->range); - } -#endif - - psppire_sheet_update_primary_selection (sheet); - - g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range); -} - - -void -psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range) -{ - g_return_if_fail (sheet != NULL); - *range = sheet->range; -} - - -void -psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *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 != PSPPIRE_SHEET_NORMAL) - psppire_sheet_real_unselect_range (sheet, NULL); - - 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 = PSPPIRE_SHEET_RANGE_SELECTED; - psppire_sheet_real_select_range (sheet, NULL); -} - -void -psppire_sheet_unselect_range (PsppireSheet *sheet) -{ - if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - psppire_sheet_real_unselect_range (sheet, NULL); - sheet->state = GTK_STATE_NORMAL; - - change_active_cell (sheet, - sheet->active_cell.row, sheet->active_cell.col); -} - - -static void -psppire_sheet_real_unselect_range (PsppireSheet *sheet, - const PsppireSheetRange *range) -{ - 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; - - g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1); - g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1); - - sheet->range.row0 = -1; - sheet->range.rowi = -1; - sheet->range.col0 = -1; - sheet->range.coli = -1; -} - - -static gint -psppire_sheet_expose (GtkWidget *widget, - GdkEventExpose *event) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (widget); - - g_return_val_if_fail (event != NULL, FALSE); - - if (!GTK_WIDGET_DRAWABLE (widget)) - return FALSE; - - /* exposure events on the sheet */ - if (event->window == sheet->row_title_window && - sheet->row_titles_visible) - { - draw_row_title_buttons_range (sheet, - min_visible_row (sheet), - max_visible_row (sheet)); - } - - if (event->window == sheet->column_title_window && - sheet->column_titles_visible) - { - draw_column_title_buttons_range (sheet, - min_visible_column (sheet), - max_visible_column (sheet)); - } - - if (event->window == sheet->sheet_window) - { - draw_sheet_region (sheet, event->region); - -#if 0 - if (sheet->state != PSPPIRE_SHEET_NORMAL) - { - if (psppire_sheet_range_isvisible (sheet, &sheet->range)) - psppire_sheet_range_draw (sheet, &sheet->range); - - if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet)) - psppire_sheet_range_draw (sheet, &sheet->drag_range); - - if (psppire_sheet_range_isvisible (sheet, &sheet->range)) - psppire_sheet_range_draw_selection (sheet, sheet->range); - if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet)) - draw_xor_rectangle (sheet, sheet->drag_range); - } -#endif - - if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet))) - { - GdkRectangle rect; - PsppireSheetRange range; - range.row0 = range.rowi = sheet->active_cell.row; - range.col0 = range.coli = sheet->active_cell.col; - - rectangle_from_range (sheet, &range, &rect); - - if (GDK_OVERLAP_RECTANGLE_OUT != - gdk_region_rect_in (event->region, &rect)) - { - psppire_sheet_draw_active_cell (sheet); - } - } - - } - - (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); - - return FALSE; -} - - -static gboolean -psppire_sheet_button_press (GtkWidget *widget, - GdkEventButton *event) -{ - PsppireSheet *sheet; - GdkModifierType mods; - gint x, y; - gint row, column; - gboolean veto; - - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - - sheet = PSPPIRE_SHEET (widget); - - /* Cancel any pending tooltips */ - if (sheet->motion_timer) - { - g_source_remove (sheet->motion_timer); - sheet->motion_timer = 0; - } - - gtk_widget_get_pointer (widget, &x, &y); - psppire_sheet_get_pixel_info (sheet, x, y, &row, &column); - - - if (event->window == sheet->column_title_window) - { - sheet->x_drag = event->x; - g_signal_emit (sheet, - sheet_signals[BUTTON_EVENT_COLUMN], 0, - column, event); - - if (psppire_sheet_model_get_column_sensitivity (sheet->model, column)) - { - if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) - g_signal_emit (sheet, - sheet_signals[DOUBLE_CLICK_COLUMN], 0, column); - } - } - else if (event->window == sheet->row_title_window) - { - g_signal_emit (sheet, - sheet_signals[BUTTON_EVENT_ROW], 0, - row, event); - - if (psppire_sheet_model_get_row_sensitivity (sheet->model, row)) - { - if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) - g_signal_emit (sheet, - sheet_signals[DOUBLE_CLICK_ROW], 0, row); - } - } - - gdk_window_get_pointer (widget->window, NULL, NULL, &mods); - - if (! (mods & GDK_BUTTON1_MASK)) return TRUE; - - - /* press on resize windows */ - if (event->window == sheet->column_title_window) - { - sheet->x_drag = event->x; - - if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col)) - { - PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_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) - { - sheet->y_drag = event->y; - - if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row)) - { - PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_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; - } - } - - /* the sheet itself does not handle other than single click events */ - if (event->type != GDK_BUTTON_PRESS) return FALSE; - - /* selections on the sheet */ - if (event->window == sheet->sheet_window) - { - gtk_widget_get_pointer (widget, &x, &y); - psppire_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)); - - if (sheet->selection_mode != GTK_SELECTION_SINGLE && - sheet->selection_mode != GTK_SELECTION_NONE && - sheet->cursor_drag->type == GDK_SIZING && - !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet)) - { - if (sheet->state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - column = sheet->active_cell.col; - sheet->active_cell.row = row; - sheet->active_cell.col = column; - sheet->drag_range = sheet->range; - sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; - psppire_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); - PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE); - } - else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW && - !PSPPIRE_SHEET_IN_SELECTION (sheet) - && ! PSPPIRE_SHEET_IN_DRAG (sheet) - && sheet->active_cell.row >= 0 - && sheet->active_cell.col >= 0 - ) - { - if (sheet->state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - column = sheet->active_cell.col; - sheet->active_cell.row = row; - sheet->active_cell.col = column; - sheet->drag_range = sheet->range; - sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; - psppire_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); - PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG); - } - else - { - veto = psppire_sheet_click_cell (sheet, row, column); - if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - } - } - - if (event->window == sheet->column_title_window) - { - gtk_widget_get_pointer (widget, &x, &y); - if ( sheet->row_titles_visible) - x -= sheet->row_title_area.width; - - x += sheet->hadjustment->value; - - column = column_from_xpixel (sheet, x); - - if (psppire_sheet_model_get_column_sensitivity (sheet->model, column)) - { - veto = psppire_sheet_click_cell (sheet, -1, column); - gtk_grab_add (GTK_WIDGET (sheet)); - PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - } - } - - if (event->window == sheet->row_title_window) - { - gtk_widget_get_pointer (widget, &x, &y); - if ( sheet->column_titles_visible) - y -= sheet->column_title_area.height; - - y += sheet->vadjustment->value; - - row = row_from_ypixel (sheet, y); - if (psppire_sheet_model_get_row_sensitivity (sheet->model, row)) - { - veto = psppire_sheet_click_cell (sheet, row, -1); - gtk_grab_add (GTK_WIDGET (sheet)); - PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - } - } - - return TRUE; -} - -static gboolean -psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column) -{ - PsppireSheetCell cell; - gboolean forbid_move; - - cell.row = row; - cell.col = column; - - if (row >= psppire_axis_unit_count (sheet->vaxis) - || column >= psppire_axis_unit_count (sheet->haxis)) - { - return FALSE; - } - - g_signal_emit (sheet, sheet_signals[TRAVERSE], 0, - &sheet->active_cell, - &cell, - &forbid_move); - - if (forbid_move) - { - if (sheet->state == GTK_STATE_NORMAL) - return FALSE; - - row = sheet->active_cell.row; - column = sheet->active_cell.col; - - change_active_cell (sheet, row, column); - return FALSE; - } - - if (row == -1 && column >= 0) - { - psppire_sheet_select_column (sheet, column); - return TRUE; - } - - if (column == -1 && row >= 0) - { - psppire_sheet_select_row (sheet, row); - return TRUE; - } - - if (row == -1 && column == -1) - { - sheet->range.row0 = 0; - sheet->range.col0 = 0; - sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; - sheet->range.coli = - psppire_axis_unit_count (sheet->haxis) - 1; - sheet->active_cell.row = 0; - sheet->active_cell.col = 0; - psppire_sheet_select_range (sheet, NULL); - return TRUE; - } - - if (sheet->state != PSPPIRE_SHEET_NORMAL) - { - sheet->state = PSPPIRE_SHEET_NORMAL; - psppire_sheet_real_unselect_range (sheet, NULL); - } - else - { - change_active_cell (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 = PSPPIRE_SHEET_NORMAL; - PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - - gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget)); - - return TRUE; -} - -static gint -psppire_sheet_button_release (GtkWidget *widget, - GdkEventButton *event) -{ - GdkDisplay *display = gtk_widget_get_display (widget); - - PsppireSheet *sheet = PSPPIRE_SHEET (widget); - - /* release on resize windows */ - if (PSPPIRE_SHEET_IN_XDRAG (sheet)) - { - gint width; - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG); - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - - gdk_display_pointer_ungrab (display, event->time); - draw_xor_vline (sheet); - - width = event->x - - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col) - + sheet->hadjustment->value; - - set_column_width (sheet, sheet->drag_cell.col, width); - - return TRUE; - } - - if (PSPPIRE_SHEET_IN_YDRAG (sheet)) - { - gint height; - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG); - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - - gdk_display_pointer_ungrab (display, event->time); - draw_xor_hline (sheet); - - height = event->y - - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) + - sheet->vadjustment->value; - - set_row_height (sheet, sheet->drag_cell.row, height); - - return TRUE; - } - - if (PSPPIRE_SHEET_IN_DRAG (sheet)) - { - PsppireSheetRange old_range; - draw_xor_rectangle (sheet, sheet->drag_range); - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG); - gdk_display_pointer_ungrab (display, event->time); - - psppire_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; - g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0, - &sheet->drag_range, &sheet->range); - psppire_sheet_select_range (sheet, &sheet->range); - } - - if (PSPPIRE_SHEET_IN_RESIZE (sheet)) - { - PsppireSheetRange old_range; - draw_xor_rectangle (sheet, sheet->drag_range); - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE); - gdk_display_pointer_ungrab (display, event->time); - - psppire_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 = PSPPIRE_SHEET_RANGE_SELECTED; - g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0, - &sheet->drag_range, &sheet->range); - psppire_sheet_select_range (sheet, &sheet->range); - } - - if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet)) - { - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - gdk_display_pointer_ungrab (display, event->time); - change_active_cell (sheet, sheet->active_cell.row, - sheet->active_cell.col); - } - - if (PSPPIRE_SHEET_IN_SELECTION) - gdk_display_pointer_ungrab (display, event->time); - gtk_grab_remove (GTK_WIDGET (sheet)); - - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - - return TRUE; -} - - - - - -/* Shamelessly lifted from gtktooltips */ -static gboolean -psppire_sheet_subtitle_paint_window (GtkWidget *tip_window) -{ - GtkRequisition req; - - gtk_widget_size_request (tip_window, &req); - gtk_paint_flat_box (tip_window->style, tip_window->window, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, - NULL, GTK_WIDGET(tip_window), "tooltip", - 0, 0, req.width, req.height); - - return FALSE; -} - -static void -destroy_hover_window (PsppireSheetHoverTitle *h) -{ - gtk_widget_destroy (h->window); - g_free (h); -} - -static PsppireSheetHoverTitle * -create_hover_window (void) -{ - PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw)); - - hw->window = gtk_window_new (GTK_WINDOW_POPUP); - -#if GTK_CHECK_VERSION (2, 9, 0) - gtk_window_set_type_hint (GTK_WINDOW (hw->window), - GDK_WINDOW_TYPE_HINT_TOOLTIP); -#endif - - gtk_widget_set_app_paintable (hw->window, TRUE); - gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE); - gtk_widget_set_name (hw->window, "gtk-tooltips"); - gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4); - - g_signal_connect (hw->window, - "expose_event", - G_CALLBACK (psppire_sheet_subtitle_paint_window), - NULL); - - hw->label = gtk_label_new (NULL); - - - gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE); - gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5); - - gtk_container_add (GTK_CONTAINER (hw->window), hw->label); - - gtk_widget_show (hw->label); - - g_signal_connect (hw->window, - "destroy", - G_CALLBACK (gtk_widget_destroyed), - &hw->window); - - return hw; -} - -#define HOVER_WINDOW_Y_OFFSET 2 - -static void -show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle) -{ - gint x, y; - gint px, py; - gint width; - - if ( ! subtitle ) - return; - - gtk_label_set_text (GTK_LABEL (sheet->hover_window->label), - subtitle); - - - sheet->hover_window->row = row; - sheet->hover_window->column = column; - - gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y); - - gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py); - - gtk_widget_show (sheet->hover_window->window); - - width = GTK_WIDGET (sheet->hover_window->label)->allocation.width; - - if (row == -1 ) - { - x += px; - x -= width / 2; - y += sheet->column_title_area.y; - y += sheet->column_title_area.height; - y += HOVER_WINDOW_Y_OFFSET; - } - - if ( column == -1 ) - { - y += py; - x += sheet->row_title_area.x; - x += sheet->row_title_area.width * 2 / 3.0; - } - - gtk_window_move (GTK_WINDOW (sheet->hover_window->window), - x, y); -} - -static gboolean -motion_timeout_callback (gpointer data) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (data); - gint x, y; - gint row, column; - gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y); - - if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) ) - { - if (sheet->row_title_under && row >= 0) - { - gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row); - - show_subtitle (sheet, row, -1, text); - g_free (text); - } - - if (sheet->column_title_under && column >= 0) - { - gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model, - column); - - show_subtitle (sheet, -1, column, text); - - g_free (text); - } - } - - return FALSE; -} - -static gboolean -psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (widget); - GdkModifierType mods; - GdkCursorType new_cursor; - gint x, y; - gint row, column; - GdkDisplay *display; - - g_return_val_if_fail (event != NULL, FALSE); - - display = gtk_widget_get_display (widget); - - /* selections on the sheet */ - x = event->x; - y = event->y; - - if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window)) - { - if ( sheet->motion_timer > 0 ) - g_source_remove (sheet->motion_timer); - sheet->motion_timer = - g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet); - } - else - { - gint row, column; - gint wx, wy; - gtk_widget_get_pointer (widget, &wx, &wy); - - if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) ) - { - if ( row != sheet->hover_window->row || - column != sheet->hover_window->column) - { - gtk_widget_hide (sheet->hover_window->window); - } - } - } - - if (event->window == sheet->column_title_window) - { - if (!PSPPIRE_SHEET_IN_SELECTION (sheet) && - on_column_boundary (sheet, x, &column)) - { - new_cursor = GDK_SB_H_DOUBLE_ARROW; - if (new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = - gdk_cursor_new_for_display (display, new_cursor); - - gdk_window_set_cursor (sheet->column_title_window, - sheet->cursor_drag); - } - } - else - { - new_cursor = GDK_TOP_LEFT_ARROW; - if (!PSPPIRE_SHEET_IN_XDRAG (sheet) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = - gdk_cursor_new_for_display (display, new_cursor); - gdk_window_set_cursor (sheet->column_title_window, - sheet->cursor_drag); - } - } - } - else if (event->window == sheet->row_title_window) - { - if (!PSPPIRE_SHEET_IN_SELECTION (sheet) && - on_row_boundary (sheet, y, &row)) - { - new_cursor = GDK_SB_V_DOUBLE_ARROW; - if (new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = - gdk_cursor_new_for_display (display, new_cursor); - gdk_window_set_cursor (sheet->row_title_window, - sheet->cursor_drag); - } - } - else - { - new_cursor = GDK_TOP_LEFT_ARROW; - if (!PSPPIRE_SHEET_IN_YDRAG (sheet) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = - gdk_cursor_new_for_display (display, new_cursor); - gdk_window_set_cursor (sheet->row_title_window, - sheet->cursor_drag); - } - } - } - - new_cursor = GDK_PLUS; - if ( event->window == sheet->sheet_window && - !POSSIBLE_DRAG (sheet, x, y, &row, &column) && - !PSPPIRE_SHEET_IN_DRAG (sheet) && - !POSSIBLE_RESIZE (sheet, x, y, &row, &column) && - !PSPPIRE_SHEET_IN_RESIZE (sheet) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS); - gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); - } - - new_cursor = GDK_TOP_LEFT_ARROW; - if ( event->window == sheet->sheet_window && - ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || - PSPPIRE_SHEET_IN_RESIZE (sheet)) && - (POSSIBLE_DRAG (sheet, x, y, &row, &column) || - PSPPIRE_SHEET_IN_DRAG (sheet)) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW); - gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); - } - - new_cursor = GDK_SIZING; - if ( event->window == sheet->sheet_window && - sheet->selection_mode != GTK_SELECTION_NONE && - !PSPPIRE_SHEET_IN_DRAG (sheet) && - (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || - PSPPIRE_SHEET_IN_RESIZE (sheet)) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new_for_display (display, 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 (PSPPIRE_SHEET_IN_XDRAG (sheet)) - { - if (event->x != sheet->x_drag) - { - draw_xor_vline (sheet); - sheet->x_drag = event->x; - draw_xor_vline (sheet); - } - - return TRUE; - } - - if (PSPPIRE_SHEET_IN_YDRAG (sheet)) - { - if (event->y != sheet->y_drag) - { - draw_xor_hline (sheet); - sheet->y_drag = event->y; - draw_xor_hline (sheet); - } - - return TRUE; - } - - if (PSPPIRE_SHEET_IN_DRAG (sheet)) - { - PsppireSheetRange aux; - column = column_from_xpixel (sheet, x)- sheet->drag_cell.col; - row = row_from_ypixel (sheet, y) - sheet->drag_cell.row; - if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; - if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0; - sheet->x_drag = x; - sheet->y_drag = y; - aux = sheet->range; - if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) && - aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis)) - { - 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 (PSPPIRE_SHEET_IN_RESIZE (sheet)) - { - PsppireSheetRange aux; - gint v_h, current_col, current_row, col_threshold, row_threshold; - v_h = 1; - if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) > - abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2; - - current_col = column_from_xpixel (sheet, x); - current_row = row_from_ypixel (sheet, y); - column = current_col - sheet->drag_cell.col; - row = current_row - sheet->drag_cell.row; - - /*use half of column width resp. row height as threshold to - expand selection*/ - col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) + - psppire_axis_unit_size (sheet->haxis, current_col) / 2; - if (column > 0) - { - if (x < col_threshold) - column -= 1; - } - else if (column < 0) - { - if (x > col_threshold) - column +=1; - } - row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) + - psppire_axis_unit_size (sheet->vaxis, current_row)/2; - if (row > 0) - { - if (y < row_threshold) - row -= 1; - } - else if (row < 0) - { - if (y > row_threshold) - row +=1; - } - - if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; - if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0; - sheet->x_drag = x; - sheet->y_drag = y; - aux = sheet->range; - - if (v_h == 1) - column = 0; - else - row = 0; - - if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) && - aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis)) - { - 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; - } - - psppire_sheet_get_pixel_info (sheet, x, y, &row, &column); - - if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row && - column == sheet->active_cell.col) return TRUE; - - if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK) - psppire_sheet_extend_selection (sheet, row, column); - - return TRUE; -} - -static gboolean -psppire_sheet_crossing_notify (GtkWidget *widget, - GdkEventCrossing *event) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (widget); - - if (event->window == sheet->column_title_window) - sheet->column_title_under = event->type == GDK_ENTER_NOTIFY; - else if (event->window == sheet->row_title_window) - sheet->row_title_under = event->type == GDK_ENTER_NOTIFY; - - return TRUE; -} - -static void -psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column) -{ - PsppireSheetRange 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_widget_grab_focus (GTK_WIDGET (sheet)); - - if (PSPPIRE_SHEET_IN_DRAG (sheet)) return; - - state = sheet->state; - - switch (sheet->state) - { - case PSPPIRE_SHEET_ROW_SELECTED: - column = psppire_axis_unit_count (sheet->haxis) - 1; - break; - case PSPPIRE_SHEET_COLUMN_SELECTED: - row = psppire_axis_unit_count (sheet->vaxis) - 1; - break; - case PSPPIRE_SHEET_NORMAL: - sheet->state = PSPPIRE_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; - psppire_sheet_range_draw_selection (sheet, sheet->range); - case PSPPIRE_SHEET_RANGE_SELECTED: - sheet->state = PSPPIRE_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 == PSPPIRE_SHEET_NORMAL) - psppire_sheet_real_select_range (sheet, &range); - -} - -static gint -psppire_sheet_entry_key_press (GtkWidget *widget, - GdkEventKey *key) -{ - gboolean focus; - g_signal_emit_by_name (widget, "key_press_event", key, &focus); - return focus; -} - - -/* Number of rows in a step-increment */ -#define ROWS_PER_STEP 1 - - -static void -page_vertical (PsppireSheet *sheet, GtkScrollType dir) -{ - gint old_row = sheet->active_cell.row ; - glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row); - - gint new_row; - - vpixel -= psppire_axis_start_pixel (sheet->vaxis, - min_visible_row (sheet)); - - switch ( dir) - { - case GTK_SCROLL_PAGE_DOWN: - gtk_adjustment_set_value (sheet->vadjustment, - sheet->vadjustment->value + - sheet->vadjustment->page_increment); - break; - case GTK_SCROLL_PAGE_UP: - gtk_adjustment_set_value (sheet->vadjustment, - sheet->vadjustment->value - - sheet->vadjustment->page_increment); - - break; - default: - g_assert_not_reached (); - break; - } - - - vpixel += psppire_axis_start_pixel (sheet->vaxis, - min_visible_row (sheet)); - - new_row = row_from_ypixel (sheet, vpixel); - - change_active_cell (sheet, new_row, - sheet->active_cell.col); -} - - -static void -step_sheet (PsppireSheet *sheet, GtkScrollType dir) -{ - gint current_row = sheet->active_cell.row; - gint current_col = sheet->active_cell.col; - PsppireSheetCell new_cell ; - gboolean forbidden = FALSE; - - new_cell.row = current_row; - new_cell.col = current_col; - - switch ( dir) - { - case GTK_SCROLL_STEP_DOWN: - new_cell.row++; - break; - case GTK_SCROLL_STEP_UP: - new_cell.row--; - break; - case GTK_SCROLL_STEP_RIGHT: - new_cell.col++; - break; - case GTK_SCROLL_STEP_LEFT: - new_cell.col--; - break; - default: - g_assert_not_reached (); - break; - } - - - g_signal_emit (sheet, sheet_signals[TRAVERSE], 0, - &sheet->active_cell, - &new_cell, - &forbidden); - - if (forbidden) - return; - - - maximize_int (&new_cell.row, 0); - maximize_int (&new_cell.col, 0); - - minimize_int (&new_cell.row, - psppire_axis_unit_count (sheet->vaxis) - 1); - - minimize_int (&new_cell.col, - psppire_axis_unit_count (sheet->haxis) - 1); - - change_active_cell (sheet, new_cell.row, new_cell.col); - - - if ( new_cell.col > max_fully_visible_column (sheet)) - { - glong hpos = - psppire_axis_start_pixel (sheet->haxis, - new_cell.col + 1); - hpos -= sheet->hadjustment->page_size; - - gtk_adjustment_set_value (sheet->hadjustment, - hpos); - } - else if ( new_cell.col < min_fully_visible_column (sheet)) - { - glong hpos = - psppire_axis_start_pixel (sheet->haxis, - new_cell.col); - - gtk_adjustment_set_value (sheet->hadjustment, - hpos); - } - - - if ( new_cell.row > max_fully_visible_row (sheet)) - { - glong vpos = - psppire_axis_start_pixel (sheet->vaxis, - new_cell.row + 1); - vpos -= sheet->vadjustment->page_size; - - gtk_adjustment_set_value (sheet->vadjustment, - vpos); - } - else if ( new_cell.row < min_fully_visible_row (sheet)) - { - glong vpos = - psppire_axis_start_pixel (sheet->vaxis, - new_cell.row); - - gtk_adjustment_set_value (sheet->vadjustment, - vpos); - } - - gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget)); -} - - -static gboolean -psppire_sheet_key_press (GtkWidget *widget, - GdkEventKey *key) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (widget); - - PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); - - switch (key->keyval) - { - case GDK_Tab: - case GDK_Right: - step_sheet (sheet, GTK_SCROLL_STEP_RIGHT); - break; - case GDK_ISO_Left_Tab: - case GDK_Left: - step_sheet (sheet, GTK_SCROLL_STEP_LEFT); - break; - case GDK_Return: - case GDK_Down: - step_sheet (sheet, GTK_SCROLL_STEP_DOWN); - break; - case GDK_Up: - step_sheet (sheet, GTK_SCROLL_STEP_UP); - break; - - case GDK_Page_Down: - page_vertical (sheet, GTK_SCROLL_PAGE_DOWN); - break; - case GDK_Page_Up: - page_vertical (sheet, GTK_SCROLL_PAGE_UP); - break; - - case GDK_Home: - gtk_adjustment_set_value (sheet->vadjustment, - sheet->vadjustment->lower); - - change_active_cell (sheet, 0, - sheet->active_cell.col); - - break; - - case GDK_End: - gtk_adjustment_set_value (sheet->vadjustment, - sheet->vadjustment->upper - - sheet->vadjustment->page_size - - sheet->vadjustment->page_increment); - - /* - change_active_cellx (sheet, - psppire_axis_unit_count (sheet->vaxis) - 1, - sheet->active_cell.col); - */ - break; - case GDK_Delete: - psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col); - break; - default: - return FALSE; - break; - } - - return TRUE; -} - -static void -psppire_sheet_size_request (GtkWidget *widget, - GtkRequisition *requisition) -{ - PsppireSheet *sheet; - - g_return_if_fail (widget != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (widget)); - g_return_if_fail (requisition != NULL); - - sheet = PSPPIRE_SHEET (widget); - - requisition->width = 3 * DEFAULT_COLUMN_WIDTH; - requisition->height = 3 * DEFAULT_ROW_HEIGHT; - - /* 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; -} - - -static void -psppire_sheet_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - PsppireSheet *sheet; - GtkAllocation sheet_allocation; - gint border_width; - - g_return_if_fail (widget != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (widget)); - g_return_if_fail (allocation != NULL); - - sheet = PSPPIRE_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); - - sheet_allocation.x = 0; - sheet_allocation.y = 0; - sheet_allocation.width = allocation->width - 2 * border_width; - sheet_allocation.height = allocation->height - 2 * border_width; - - 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; - sheet->column_title_area.width = sheet_allocation.width ; - - - /* position the window which holds the row title buttons */ - sheet->row_title_area.x = 0; - sheet->row_title_area.y = 0; - sheet->row_title_area.height = sheet_allocation.height; - - if (sheet->row_titles_visible) - sheet->column_title_area.x += sheet->row_title_area.width; - - if (sheet->column_titles_visible) - sheet->row_title_area.y += sheet->column_title_area.height; - - 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); - - - 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); - - size_allocate_global_button (sheet); - - if (sheet->haxis) - { - gint width = sheet->column_title_area.width; - - if ( sheet->row_titles_visible) - width -= sheet->row_title_area.width; - - g_object_set (sheet->haxis, - "minimum-extent", width, - NULL); - } - - - if (sheet->vaxis) - { - gint height = sheet->row_title_area.height; - - if ( sheet->column_titles_visible) - height -= sheet->column_title_area.height; - - g_object_set (sheet->vaxis, - "minimum-extent", height, - NULL); - } - - - /* set the scrollbars adjustments */ - adjust_scrollbars (sheet); -} - -static void -draw_column_title_buttons (PsppireSheet *sheet) -{ - gint x, width; - - if (!sheet->column_titles_visible) return; - if (!GTK_WIDGET_REALIZED (sheet)) - return; - - gdk_drawable_get_size (sheet->sheet_window, &width, NULL); - x = 0; - - if (sheet->row_titles_visible) - { - 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) == - psppire_axis_unit_count (sheet->haxis) - 1) - 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; - - draw_column_title_buttons_range (sheet, min_visible_column (sheet), - max_visible_column (sheet)); -} - -static void -draw_row_title_buttons (PsppireSheet *sheet) -{ - gint y = 0; - gint height; - - if (!sheet->row_titles_visible) return; - if (!GTK_WIDGET_REALIZED (sheet)) - return; - - gdk_drawable_get_size (sheet->sheet_window, NULL, &height); - - if (sheet->column_titles_visible) - { - 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) == psppire_axis_unit_count (sheet->vaxis) - 1) - 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; - - draw_row_title_buttons_range (sheet, min_visible_row (sheet), - max_visible_row (sheet)); -} - - -static void -psppire_sheet_size_allocate_entry (PsppireSheet *sheet) -{ - GtkAllocation entry_alloc; - PsppireSheetCellAttr attributes = { 0 }; - GtkEntry *sheet_entry; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return; - - sheet_entry = psppire_sheet_get_entry (sheet); - - if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row, - sheet->active_cell.col, - &attributes) ) - return ; - - if ( GTK_WIDGET_REALIZED (sheet->entry_widget) ) - { - GtkStyle *style = GTK_WIDGET (sheet_entry)->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; - } - - rectangle_from_cell (sheet, sheet->active_cell.row, - sheet->active_cell.col, &entry_alloc); - - entry_alloc.width -= BORDER_WIDTH ; - entry_alloc.height -= BORDER_WIDTH ; - entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2); - entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2); - - - gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width, - entry_alloc.height); - gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc); -} - - -/* Copy the sheet's font to the entry widget */ -static void -set_entry_widget_font (PsppireSheet *sheet) -{ - GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget); - - pango_font_description_free (style->font_desc); - style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc); - - gtk_widget_modify_style (sheet->entry_widget, style); -} - - - -static void -create_sheet_entry (PsppireSheet *sheet) -{ - if (sheet->entry_widget) - { - gtk_widget_unparent (sheet->entry_widget); - } - - sheet->entry_widget = g_object_new (sheet->entry_type, NULL); - g_object_ref_sink (sheet->entry_widget); - - gtk_widget_size_request (sheet->entry_widget, NULL); - - if ( GTK_IS_ENTRY (sheet->entry_widget)) - { - g_object_set (sheet->entry_widget, - "has-frame", FALSE, - NULL); - } - - if (GTK_WIDGET_REALIZED (sheet)) - { - gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); - gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); - gtk_widget_realize (sheet->entry_widget); - } - - g_signal_connect_swapped (sheet->entry_widget, "key_press_event", - G_CALLBACK (psppire_sheet_entry_key_press), - sheet); - - set_entry_widget_font (sheet); - - gtk_widget_show (sheet->entry_widget); -} - - -/* Finds the last child widget that happens to be of type GtkEntry */ -static void -find_entry (GtkWidget *w, gpointer user_data) -{ - GtkWidget **entry = user_data; - if ( GTK_IS_ENTRY (w)) - { - *entry = w; - } -} - - -GtkEntry * -psppire_sheet_get_entry (PsppireSheet *sheet) -{ - GtkWidget *w = sheet->entry_widget; - - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); - g_return_val_if_fail (sheet->entry_widget != NULL, NULL); - - while (! GTK_IS_ENTRY (w)) - { - GtkWidget *entry = NULL; - - if (GTK_IS_CONTAINER (w)) - { - gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry); - - if (NULL == entry) - break; - - w = entry; - } - } - - return GTK_ENTRY (w); -} - - -static void -draw_button (PsppireSheet *sheet, GdkWindow *window, - PsppireSheetButton *button, gboolean is_sensitive, - GdkRectangle allocation) -{ - GtkShadowType shadow_type; - gint text_width = 0, text_height = 0; - PangoAlignment align = PANGO_ALIGN_LEFT; - - gboolean rtl ; - - gint state = 0; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (button != NULL); - - - rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL; - - gdk_window_clear_area (window, - allocation.x, allocation.y, - allocation.width, allocation.height); - - gtk_widget_ensure_style (sheet->button); - - gtk_paint_box (sheet->button->style, window, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, - &allocation, GTK_WIDGET (sheet->button), - "buttondefault", - allocation.x, allocation.y, - allocation.width, allocation.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), - "button", - allocation.x, allocation.y, - allocation.width, allocation.height); - - if (button->label_visible) - { - text_height = DEFAULT_ROW_HEIGHT - - 2 * COLUMN_TITLES_HEIGHT; - - 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); - - allocation.y += 2 * sheet->button->style->ythickness; - - if (button->label && strlen (button->label) > 0) - { - PangoRectangle rect; - gchar *line = button->label; - - PangoLayout *layout = NULL; - gint real_x = allocation.x; - gint real_y = allocation.y; - - layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line); - pango_layout_get_extents (layout, NULL, &rect); - - text_width = PANGO_PIXELS (rect.width); - switch (button->justification) - { - case GTK_JUSTIFY_LEFT: - real_x = allocation.x + COLUMN_TITLES_HEIGHT; - align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; - break; - case GTK_JUSTIFY_RIGHT: - real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT; - align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT; - break; - case GTK_JUSTIFY_CENTER: - default: - real_x = allocation.x + (allocation.width - text_width)/2; - align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; - pango_layout_set_justify (layout, TRUE); - } - pango_layout_set_alignment (layout, align); - gtk_paint_layout (GTK_WIDGET (sheet)->style, - window, - state, - FALSE, - &allocation, - GTK_WIDGET (sheet), - "label", - real_x, real_y, - layout); - g_object_unref (layout); - } - - 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); - - } - - psppire_sheet_button_free (button); -} - - -/* Draw the column title buttons FIRST through to LAST */ -static void -draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last) -{ - GdkRectangle rect; - gint col; - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - if (!sheet->column_titles_visible) return; - - g_return_if_fail (first >= min_visible_column (sheet)); - g_return_if_fail (last <= max_visible_column (sheet)); - - rect.y = 0; - rect.height = sheet->column_title_area.height; - rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING; - rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING - + psppire_axis_unit_size (sheet->haxis, last); - - rect.x -= sheet->hadjustment->value; - - minimize_int (&rect.width, sheet->column_title_area.width); - maximize_int (&rect.x, 0); - - gdk_window_begin_paint_rect (sheet->column_title_window, &rect); - - for (col = first ; col <= last ; ++col) - { - GdkRectangle allocation; - gboolean is_sensitive = FALSE; - - PsppireSheetButton * - button = psppire_sheet_model_get_column_button (sheet->model, col); - allocation.y = 0; - allocation.x = psppire_axis_start_pixel (sheet->haxis, col) - + CELL_SPACING; - allocation.x -= sheet->hadjustment->value; - - allocation.height = sheet->column_title_area.height; - allocation.width = psppire_axis_unit_size (sheet->haxis, col); - is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col); - - draw_button (sheet, sheet->column_title_window, - button, is_sensitive, allocation); - } - - gdk_window_end_paint (sheet->column_title_window); -} - - -static void -draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last) -{ - GdkRectangle rect; - gint row; - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - if (!sheet->row_titles_visible) return; - - g_return_if_fail (first >= min_visible_row (sheet)); - g_return_if_fail (last <= max_visible_row (sheet)); - - rect.x = 0; - rect.width = sheet->row_title_area.width; - rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING; - rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING - + psppire_axis_unit_size (sheet->vaxis, last); - - rect.y -= sheet->vadjustment->value; - - minimize_int (&rect.height, sheet->row_title_area.height); - maximize_int (&rect.y, 0); - - gdk_window_begin_paint_rect (sheet->row_title_window, &rect); - for (row = first; row <= last; ++row) - { - GdkRectangle allocation; - - gboolean is_sensitive = FALSE; - - PsppireSheetButton *button = - psppire_sheet_model_get_row_button (sheet->model, row); - allocation.x = 0; - allocation.y = psppire_axis_start_pixel (sheet->vaxis, row) - + CELL_SPACING; - allocation.y -= sheet->vadjustment->value; - - allocation.width = sheet->row_title_area.width; - allocation.height = psppire_axis_unit_size (sheet->vaxis, row); - is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row); - - draw_button (sheet, sheet->row_title_window, - button, is_sensitive, allocation); - } - - gdk_window_end_paint (sheet->row_title_window); -} - -/* SCROLLBARS - * - * functions: - * adjust_scrollbars - * vadjustment_value_changed - * hadjustment_value_changed */ - - -static void -update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size) -{ - double position = - (adj->value + adj->page_size) - / - (adj->upper - adj->lower); - - const glong last_item = psppire_axis_unit_count (axis) - 1; - - if (isnan (position) || position < 0) - position = 0; - - adj->upper = - psppire_axis_start_pixel (axis, last_item) - + - psppire_axis_unit_size (axis, last_item) - ; - - adj->lower = 0; - adj->page_size = page_size; - -#if 0 - adj->value = position * (adj->upper - adj->lower) - adj->page_size; - - if ( adj->value < adj->lower) - adj->value = adj->lower; -#endif - - gtk_adjustment_changed (adj); -} - - -static void -adjust_scrollbars (PsppireSheet *sheet) -{ - gint width, height; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - gdk_drawable_get_size (sheet->sheet_window, &width, &height); - - if ( sheet->row_titles_visible) - width -= sheet->row_title_area.width; - - if (sheet->column_titles_visible) - height -= sheet->column_title_area.height; - - if (sheet->vadjustment) - { - glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1; - - sheet->vadjustment->step_increment = - ROWS_PER_STEP * - psppire_axis_unit_size (sheet->vaxis, last_row); - - sheet->vadjustment->page_increment = - height - - sheet->column_title_area.height - - psppire_axis_unit_size (sheet->vaxis, last_row); - - update_adjustment (sheet->vadjustment, sheet->vaxis, height); - } - - if (sheet->hadjustment) - { - gint last_col = psppire_axis_unit_count (sheet->haxis) - 1; - sheet->hadjustment->step_increment = 1; - - sheet->hadjustment->page_increment = width; - - sheet->hadjustment->upper = - psppire_axis_start_pixel (sheet->haxis, last_col) - + - psppire_axis_unit_size (sheet->haxis, last_col) - ; - - update_adjustment (sheet->hadjustment, sheet->haxis, width); - } -} - -/* Subtracts the region of WIDGET from REGION */ -static void -subtract_widget_region (GdkRegion *region, GtkWidget *widget) -{ - GdkRectangle rect; - GdkRectangle intersect; - GdkRegion *region2; - - gdk_region_get_clipbox (region, &rect); - gtk_widget_intersect (widget, - &rect, - &intersect); - - region2 = gdk_region_rectangle (&intersect); - gdk_region_subtract (region, region2); - gdk_region_destroy (region2); -} - -static void -vadjustment_value_changed (GtkAdjustment *adjustment, - gpointer data) -{ - GdkRegion *region; - PsppireSheet *sheet = PSPPIRE_SHEET (data); - - g_return_if_fail (adjustment != NULL); - - if ( ! GTK_WIDGET_REALIZED (sheet)) return; - - gtk_widget_hide (sheet->entry_widget); - - region = - gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window)); - - subtract_widget_region (region, sheet->button); - gdk_window_begin_paint_region (sheet->sheet_window, region); - - draw_sheet_region (sheet, region); - - draw_row_title_buttons (sheet); - psppire_sheet_draw_active_cell (sheet); - - gdk_window_end_paint (sheet->sheet_window); - gdk_region_destroy (region); -} - - -static void -hadjustment_value_changed (GtkAdjustment *adjustment, - gpointer data) -{ - GdkRegion *region; - PsppireSheet *sheet = PSPPIRE_SHEET (data); - - g_return_if_fail (adjustment != NULL); - - if ( ! GTK_WIDGET_REALIZED (sheet)) return; - - gtk_widget_hide (sheet->entry_widget); - - - region = - gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window)); - - subtract_widget_region (region, sheet->button); - gdk_window_begin_paint_region (sheet->sheet_window, region); - - draw_sheet_region (sheet, region); - - draw_column_title_buttons (sheet); - - psppire_sheet_draw_active_cell (sheet); - - gdk_window_end_paint (sheet->sheet_window); - - gdk_region_destroy (region); -} - - -/* COLUMN RESIZING */ -static void -draw_xor_vline (PsppireSheet *sheet) -{ - gint height; - gint xpos = sheet->x_drag; - gdk_drawable_get_size (sheet->sheet_window, - NULL, &height); - - if (sheet->row_titles_visible) - xpos += sheet->row_title_area.width; - - gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc, - xpos, - sheet->column_title_area.height, - xpos, - height + CELL_SPACING); -} - -/* ROW RESIZING */ -static void -draw_xor_hline (PsppireSheet *sheet) - -{ - gint width; - gint ypos = sheet->y_drag; - - gdk_drawable_get_size (sheet->sheet_window, - &width, NULL); - - - if (sheet->column_titles_visible) - ypos += sheet->column_title_area.height; - - gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc, - sheet->row_title_area.width, - ypos, - width + CELL_SPACING, - ypos); -} - -/* SELECTED RANGE */ -static void -draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range) -{ - gint i = 0; - GdkRectangle clip_area, area; - GdkGCValues values; - - area.x = psppire_axis_start_pixel (sheet->haxis, range.col0); - area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0); - area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+ - psppire_axis_unit_size (sheet->haxis, range.coli); - area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y + - psppire_axis_unit_size (sheet->vaxis, range.rowi); - - clip_area.x = sheet->row_title_area.width; - clip_area.y = sheet->column_title_area.height; - - gdk_drawable_get_size (sheet->sheet_window, - &clip_area.width, &clip_area.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); - - 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); -} - - -static void -set_column_width (PsppireSheet *sheet, - gint column, - gint width) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis)) - return; - - if ( width <= 0) - return; - - psppire_axis_resize (sheet->haxis, column, width); - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - draw_column_title_buttons (sheet); - adjust_scrollbars (sheet); - psppire_sheet_size_allocate_entry (sheet); - redraw_range (sheet, NULL); - } -} - -static void -set_row_height (PsppireSheet *sheet, - gint row, - gint height) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); - - if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis)) - return; - - if (height <= 0) - return; - - psppire_axis_resize (sheet->vaxis, row, height); - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) ) - { - draw_row_title_buttons (sheet); - adjust_scrollbars (sheet); - psppire_sheet_size_allocate_entry (sheet); - redraw_range (sheet, NULL); - } -} - -gboolean -psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col, - PsppireSheetCellAttr *attr) -{ - GdkColor *fg, *bg; - const GtkJustification *j ; - GdkColormap *colormap; - - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE); - - if (row < 0 || col < 0) return FALSE; - - attr->foreground = GTK_WIDGET (sheet)->style->black; - attr->background = sheet->color[BG_COLOR]; - - attr->border.width = 0; - attr->border.line_style = GDK_LINE_SOLID; - attr->border.cap_style = GDK_CAP_NOT_LAST; - attr->border.join_style = GDK_JOIN_MITER; - attr->border.mask = 0; - attr->border.color = GTK_WIDGET (sheet)->style->black; - - attr->is_editable = psppire_sheet_model_is_editable (sheet->model, row, col); - - colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet)); - fg = psppire_sheet_model_get_foreground (sheet->model, row, col); - if ( fg ) - { - gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE); - attr->foreground = *fg; - } - - bg = psppire_sheet_model_get_background (sheet->model, row, col); - if ( bg ) - { - gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE); - attr->background = *bg; - } - - attr->justification = - psppire_sheet_model_get_column_justification (sheet->model, col); - - j = psppire_sheet_model_get_justification (sheet->model, row, col); - if (j) - attr->justification = *j; - - return TRUE; -} - -static void -psppire_sheet_button_size_request (PsppireSheet *sheet, - const PsppireSheetButton *button, - GtkRequisition *button_requisition) -{ - GtkRequisition requisition; - GtkRequisition label_requisition; - - label_requisition.height = DEFAULT_ROW_HEIGHT; - label_requisition.width = COLUMN_MIN_WIDTH; - - requisition.height = DEFAULT_ROW_HEIGHT; - 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 -psppire_sheet_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (container); - - g_return_if_fail (callback != NULL); - - if (sheet->button && sheet->button->parent) - (* callback) (sheet->button, callback_data); - - if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget)) - (* callback) (sheet->entry_widget, callback_data); -} - - -PsppireSheetModel * -psppire_sheet_get_model (const PsppireSheet *sheet) -{ - g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); - - return sheet->model; -} - - -PsppireSheetButton * -psppire_sheet_button_new (void) -{ - PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton)); - - button->state = GTK_STATE_NORMAL; - button->label = NULL; - button->label_visible = TRUE; - button->justification = GTK_JUSTIFY_FILL; - - return button; -} - - -void -psppire_sheet_button_free (PsppireSheetButton *button) -{ - if (!button) return ; - - g_free (button->label); - g_free (button); -} - -static void -append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c) -{ - gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c); - - if ( NULL == celltext) - return; - - g_string_append (string, celltext); - g_free (celltext); -} - - -static GString * -range_to_text (const PsppireSheet *sheet) -{ - gint r, c; - GString *string; - - if ( !psppire_sheet_range_isvisible (sheet, &sheet->range)) - return NULL; - - string = g_string_sized_new (80); - - for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) - { - for (c = sheet->range.col0; c < sheet->range.coli; ++c) - { - append_cell_text (string, sheet, r, c); - g_string_append (string, "\t"); - } - append_cell_text (string, sheet, r, c); - if ( r < sheet->range.rowi) - g_string_append (string, "\n"); - } - - return string; -} - -static GString * -range_to_html (const PsppireSheet *sheet) -{ - gint r, c; - GString *string; - - if ( !psppire_sheet_range_isvisible (sheet, &sheet->range)) - return NULL; - - string = g_string_sized_new (480); - - g_string_append (string, "\n"); - g_string_append (string, "\n"); - g_string_append (string, "\n"); - for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) - { - g_string_append (string, "\n"); - for (c = sheet->range.col0; c <= sheet->range.coli; ++c) - { - g_string_append (string, "\n"); - } - g_string_append (string, "\n"); - } - g_string_append (string, "
"); - append_cell_text (string, sheet, r, c); - g_string_append (string, "
\n"); - g_string_append (string, "\n"); - g_string_append (string, "\n"); - - return string; -} - -enum { - SELECT_FMT_NULL, - SELECT_FMT_TEXT, - SELECT_FMT_HTML -}; - -static void -primary_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (data); - GString *string = NULL; - - switch (info) - { - case SELECT_FMT_TEXT: - string = range_to_text (sheet); - break; - case SELECT_FMT_HTML: - string = range_to_html (sheet); - break; - default: - g_assert_not_reached (); - } - - gtk_selection_data_set (selection_data, selection_data->target, - 8, - (const guchar *) string->str, string->len); - g_string_free (string, TRUE); -} - -static void -primary_clear_cb (GtkClipboard *clipboard, - gpointer data) -{ - PsppireSheet *sheet = PSPPIRE_SHEET (data); - if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - psppire_sheet_real_unselect_range (sheet, NULL); -} - -static void -psppire_sheet_update_primary_selection (PsppireSheet *sheet) -{ - static const GtkTargetEntry targets[] = { - { "UTF8_STRING", 0, SELECT_FMT_TEXT }, - { "STRING", 0, SELECT_FMT_TEXT }, - { "TEXT", 0, SELECT_FMT_TEXT }, - { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT }, - { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT }, - { "text/plain", 0, SELECT_FMT_TEXT }, - { "text/html", 0, SELECT_FMT_HTML } - }; - - GtkClipboard *clipboard; - - if (!GTK_WIDGET_REALIZED (sheet)) - return; - - clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet), - GDK_SELECTION_PRIMARY); - - if (psppire_sheet_range_isvisible (sheet, &sheet->range)) - { - if (!gtk_clipboard_set_with_owner (clipboard, targets, - G_N_ELEMENTS (targets), - primary_get_cb, primary_clear_cb, - G_OBJECT (sheet))) - primary_clear_cb (clipboard, sheet); - } - else - { - if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet)) - gtk_clipboard_clear (clipboard); - } -} diff --git a/lib/gtksheet/psppire-sheet.h b/lib/gtksheet/psppire-sheet.h deleted file mode 100644 index 82de87be..00000000 --- a/lib/gtksheet/psppire-sheet.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - Copyright (C) 2006, 2008 Free Software Foundation - - 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 3 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, see . - - - This file is derived from the gtksheet.c and extensively modified for the - requirements of PSPPIRE. The changes are copyright by the - Free Software Foundation. The copyright notice for the original work is - below. - - - 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 Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __PSPPIRE_SHEET_H__ -#define __PSPPIRE_SHEET_H__ - -#include - -#include "gtkextra-sheet.h" -#include -#include - -G_BEGIN_DECLS - -/* sheet->state */ -enum -{ - PSPPIRE_SHEET_NORMAL, - PSPPIRE_SHEET_ROW_SELECTED, - PSPPIRE_SHEET_COLUMN_SELECTED, - PSPPIRE_SHEET_RANGE_SELECTED -}; - - -#define PSPPIRE_TYPE_SHEET_RANGE (psppire_sheet_range_get_type ()) -#define PSPPIRE_TYPE_SHEET_CELL (psppire_sheet_cell_get_type ()) -#define PSPPIRE_TYPE_SHEET (psppire_sheet_get_type ()) - -#define PSPPIRE_SHEET(obj) GTK_CHECK_CAST (obj, psppire_sheet_get_type (), PsppireSheet) -#define PSPPIRE_SHEET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, psppire_sheet_get_type (), PsppireSheetClass) -#define PSPPIRE_IS_SHEET(obj) GTK_CHECK_TYPE (obj, psppire_sheet_get_type ()) - - -typedef struct _PsppireSheetClass PsppireSheetClass; -typedef struct _PsppireSheetCellAttr PsppireSheetCellAttr; - -typedef struct _PsppireSheetHoverTitle PsppireSheetHoverTitle; - - -struct _PsppireSheetCellAttr -{ - GtkJustification justification; - GdkColor foreground; - GdkColor background; - PsppireSheetCellBorder border; - gboolean is_editable; -}; - -struct _PsppireSheetHoverTitle -{ - GtkWidget *window; - GtkWidget *label; - gint row, column; -}; - -enum - { - BG_COLOR, - GRID_COLOR, - n_COLORS - }; - -struct _PsppireSheet -{ - GtkBin parent; - - gboolean dispose_has_run; - PsppireAxis *haxis; - PsppireAxis *vaxis; - - guint16 flags; - - PsppireSheetModel *model; - - GtkSelectionMode selection_mode; - - /* Component colors */ - GdkColor color[n_COLORS]; - gboolean show_grid; - - /* active cell */ - PsppireSheetCell active_cell; - - /* The GtkEntry used for editing the cells */ - GtkWidget *entry_widget; - - /* The type of entry_widget */ - GtkType entry_type; - - /* expanding selection */ - PsppireSheetCell selection_cell; - - /* global selection button */ - GtkWidget *button; - - /* sheet state */ - gint state; - - /* selected range */ - PsppireSheetRange range; - - /* the scrolling window and its height and width to - * make things a little speedier */ - GdkWindow *sheet_window; - - /* border shadow style */ - GtkShadowType shadow_type; - - /* Column Titles */ - GdkRectangle column_title_area; - GdkWindow *column_title_window; - gboolean column_titles_visible; - /* TRUE if the cursor is over the column title window */ - gboolean column_title_under; - - /* Row Titles */ - GdkRectangle row_title_area; - GdkWindow *row_title_window; - gboolean row_titles_visible; - /* TRUE if the cursor is over the row title window */ - gboolean row_title_under; - - /*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 */ - PsppireSheetCell drag_cell; - /* current range being dragged */ - PsppireSheetRange drag_range; - - /* Used for the subtitle (popups) */ - gint motion_timer; - PsppireSheetHoverTitle *hover_window; - - gulong update_handler_id; -}; - -struct _PsppireSheetClass -{ - GtkBinClass parent_class; - - gboolean (*set_scroll_adjustments) (PsppireSheet *sheet, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); - - void (*select_row) (PsppireSheet *sheet, gint row); - - void (*select_column) (PsppireSheet *sheet, gint column); - - void (*select_range) (PsppireSheet *sheet, PsppireSheetRange *range); - - void (*resize_range) (PsppireSheet *sheet, - PsppireSheetRange *old_range, - PsppireSheetRange *new_range); - - void (*move_range) (PsppireSheet *sheet, - PsppireSheetRange *old_range, - PsppireSheetRange *new_range); - - gboolean (*traverse) (PsppireSheet *sheet, - gint row, gint column, - gint *new_row, gint *new_column); - - gboolean (*activate) (PsppireSheet *sheet, - gint row, gint column); - - void (*changed) (PsppireSheet *sheet, - gint row, gint column); -}; - -GType psppire_sheet_get_type (void); -GtkType psppire_sheet_range_get_type (void); - - -/* create a new sheet */ -GtkWidget * psppire_sheet_new (PsppireSheetModel *model); - -/* create a new sheet with custom entry */ -GtkWidget * -psppire_sheet_new_with_custom_entry (GtkType entry_type); - -/* Change entry */ -void psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type); - -GtkEntry *psppire_sheet_get_entry (PsppireSheet *sheet); - - -void psppire_sheet_get_selected_range (PsppireSheet *sheet, - PsppireSheetRange *range); - -void psppire_sheet_show_grid (PsppireSheet *sheet, - gboolean show); - -gboolean psppire_sheet_grid_visible (PsppireSheet *sheet); - - -/* 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 screen, 0.0 being top or left, - * 1.0 being bottom or right; if row or column is negative then there - * is no change */ -void psppire_sheet_moveto (PsppireSheet *sheet, - gint row, - gint column, - gfloat row_align, - gfloat col_align); - - -void psppire_sheet_show_row_titles (PsppireSheet *sheet); -void psppire_sheet_hide_row_titles (PsppireSheet *sheet); -void psppire_sheet_show_column_titles (PsppireSheet *sheet); -void psppire_sheet_hide_column_titles (PsppireSheet *sheet); - -/* select the row. The range is then highlighted, and the bounds are stored - * in sheet->range */ -void psppire_sheet_select_row (PsppireSheet * sheet, gint row); - -/* select the column. The range is then highlighted, and the bounds are stored - * in sheet->range */ -void psppire_sheet_select_column (PsppireSheet * sheet, gint column); - -/* highlight the selected range and store bounds in sheet->range */ -void psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range); - -void psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range); - - -/* obvious */ -void psppire_sheet_unselect_range (PsppireSheet *sheet); - -/* set active cell where the entry will be displayed */ -void psppire_sheet_set_active_cell (PsppireSheet *sheet, - gint row, gint column); - -/* Sets *ROW and *COLUMN to be the coordinates of the active cell. - ROW and/or COLUMN may be null if the caller is not interested in their - values */ -void psppire_sheet_get_active_cell (PsppireSheet *sheet, - gint *row, gint *column); - -/* get cell contents */ -gchar *psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col); - - -/* get cell attributes of the given cell */ -/* TRUE means that the cell is currently allocated */ -gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet, - gint row, gint col, - PsppireSheetCellAttr *attributes); - -void psppire_sheet_set_model (PsppireSheet *sheet, - PsppireSheetModel *model); - -PsppireSheetModel * psppire_sheet_get_model (const PsppireSheet *sheet); - - -G_END_DECLS - - -#endif /* __PSPPIRE_SHEET_H__ */ - - diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index e3480d7a..57d9163c 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -54,7 +54,7 @@ src_ui_gui_psppire_LDADD = \ -dlopen src/ui/gui/libpsppire.la \ src/ui/gui/libpsppwidgets.la \ src/ui/gui/sheet/libsheet.la \ - lib/gtksheet/libgtksheet.a \ + lib/gtk-contrib/libgtksheet.a \ src/ui/libuicommon.la \ src/libpspp.la \ src/libpspp-core.la \ diff --git a/src/ui/gui/psppire-data-editor.c b/src/ui/gui/psppire-data-editor.c index 6963a424..00e227ec 100644 --- a/src/ui/gui/psppire-data-editor.c +++ b/src/ui/gui/psppire-data-editor.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include "psppire-data-editor.h" #include "psppire-var-sheet.h" @@ -26,7 +26,7 @@ #include #include "helper.h" -#include +#include #include #define _(msgid) gettext (msgid) #define N_(msgid) msgid diff --git a/src/ui/gui/psppire-var-sheet.h b/src/ui/gui/psppire-var-sheet.h index 79f911c8..b996275c 100644 --- a/src/ui/gui/psppire-var-sheet.h +++ b/src/ui/gui/psppire-var-sheet.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include "val-labs-dialog.h" #include "missing-val-dialog.h" #include "var-type-dialog.h" diff --git a/src/ui/gui/sheet/psppire-sheetmodel.h b/src/ui/gui/sheet/psppire-sheetmodel.h index 11f37e99..7869791e 100644 --- a/src/ui/gui/sheet/psppire-sheetmodel.h +++ b/src/ui/gui/sheet/psppire-sheetmodel.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include G_BEGIN_DECLS diff --git a/src/ui/gui/text-data-import-dialog.c b/src/ui/gui/text-data-import-dialog.c index 92e3f893..a82a81a1 100644 --- a/src/ui/gui/text-data-import-dialog.c +++ b/src/ui/gui/text-data-import-dialog.c @@ -25,7 +25,7 @@ #include -#include +#include #include #include #include