From: John Darrington Date: Sun, 10 Sep 2017 18:11:13 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/sheet' X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=17ca35e4ca8abf4ba0c0ae82920b985fa17648cf;hp=d5a566a2900f69ab51c7dae78cd7810a4204589a;p=pspp Merge remote-tracking branch 'origin/sheet' --- diff --git a/INSTALL b/INSTALL index f2e381947f..835092ede1 100644 --- a/INSTALL +++ b/INSTALL @@ -92,6 +92,8 @@ use the GUI, you must run `configure' with --without-gui. * GtkSourceView (http://projects.gnome.org/gtksourceview/) version 3.4.0 or later. + * GNU Spread Sheet Widget (http://www.gnu.org/software/ssw) + The following packages are optional: Installing the following packages will allow your PSPP program to read diff --git a/NEWS b/NEWS index 1c24e0c359..a02b144f4d 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,11 @@ See the end for copying conditions. Please send PSPP bug reports to bug-gnu-pspp@gnu.org. +Changes from 1.0.1 to 1.1.0: + + ** The code implementing the sheet rendering has been removed. Instead we + use a third party library: spread-sheet-widget. + Changes from 1.0.0 to 1.0.1: * Bug fixes, including fixes for CVE-2017-12958, CVE-2017-12959, diff --git a/configure.ac b/configure.ac index f1d344ded2..f6293d1cb2 100644 --- a/configure.ac +++ b/configure.ac @@ -18,7 +18,7 @@ dnl Process this file with autoconf to produce a configure script. dnl Initialize. AC_PREREQ(2.63) -AC_INIT([GNU PSPP], [1.0.1], [bug-gnu-pspp@gnu.org], [pspp]) +AC_INIT([GNU PSPP], [1.1.0], [bug-gnu-pspp@gnu.org], [pspp]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_TESTDIR([tests]) @@ -112,8 +112,11 @@ if test "$with_cairo" != no && test "$with_gui" != "no"; then PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-3.0 >= 3.4.2], [], [PSPP_REQUIRED_PREREQ([gtksourceview 3.0 version 3.4.2 or later (or use --without-gui)])]) - PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.32], [], - [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.32 or later (or use --without-gui)])]) + PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.44], [], + [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.44 or later (or use --without-gui)])]) + + PKG_CHECK_MODULES([SPREAD_SHEET_WIDGET], [spread-sheet-widget >= 0.1], [], + [PSPP_REQUIRED_PREREQ([spread-sheet-widget 0.1 (or use --without-gui)])]) AC_ARG_VAR([GLIB_GENMARSHAL]) AC_CHECK_PROGS([GLIB_GENMARSHAL], [glib-genmarshal]) diff --git a/lib/automake.mk b/lib/automake.mk index e2c98cc42f..dbda6b48a8 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -18,7 +18,3 @@ include $(top_srcdir)/lib/linreg/automake.mk include $(top_srcdir)/lib/tukey/automake.mk - -if HAVE_GUI -include $(top_srcdir)/lib/gtk-contrib/automake.mk -endif diff --git a/lib/gtk-contrib/COPYING.LESSER b/lib/gtk-contrib/COPYING.LESSER deleted file mode 100644 index 8add30ad59..0000000000 --- a/lib/gtk-contrib/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/gtk-contrib/README b/lib/gtk-contrib/README deleted file mode 100644 index c3ffaf2c66..0000000000 --- a/lib/gtk-contrib/README +++ /dev/null @@ -1,5 +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. diff --git a/lib/gtk-contrib/automake.mk b/lib/gtk-contrib/automake.mk deleted file mode 100644 index ff1519d2a9..0000000000 --- a/lib/gtk-contrib/automake.mk +++ /dev/null @@ -1,30 +0,0 @@ -# PSPP - a program for statistical analysis. -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 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 . -# -## Process this file with automake to produce Makefile.in -*- makefile -*- - -noinst_LIBRARIES += lib/gtk-contrib/libxpaned.a - -lib_gtk_contrib_libxpaned_a_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 - -lib_gtk_contrib_libxpaned_a_SOURCES = \ - lib/gtk-contrib/gtkxpaned.c \ - lib/gtk-contrib/gtkxpaned.h - -EXTRA_DIST += \ - lib/gtk-contrib/README \ - lib/gtk-contrib/COPYING.LESSER - diff --git a/lib/gtk-contrib/gtkxpaned.c b/lib/gtk-contrib/gtkxpaned.c deleted file mode 100644 index d5b9c843e5..0000000000 --- a/lib/gtk-contrib/gtkxpaned.c +++ /dev/null @@ -1,3353 +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) 2012, 2013 Free Software Foundation, Inc. - ** 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 -#include "gtkxpaned.h" - -#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_get_preferred_width (GtkWidget *widget, - gint *minimal_width, - gint *natural_width) -{ - GtkXPaned *xpaned = GTK_XPANED (widget); - gint tl[2], tr[2], bl[2], br[2]; - gint overhead; - gint w[2]; - int i; - - if (xpaned->top_left_child - && gtk_widget_get_visible (xpaned->top_left_child)) - gtk_widget_get_preferred_width (xpaned->top_left_child, &tl[0], &tl[1]); - else - tl[0] = tl[1] = 0; - - if (xpaned->top_right_child - && gtk_widget_get_visible (xpaned->top_right_child)) - gtk_widget_get_preferred_width (xpaned->top_right_child, &tr[0], &tr[1]); - else - tr[0] = tr[1] = 0; - - if (xpaned->bottom_left_child - && gtk_widget_get_visible (xpaned->bottom_left_child)) - gtk_widget_get_preferred_width (xpaned->bottom_left_child, &bl[0], &bl[1]); - else - bl[0] = bl[1] = 0; - - if (xpaned->bottom_right_child - && gtk_widget_get_visible (xpaned->bottom_right_child)) - gtk_widget_get_preferred_width (xpaned->bottom_right_child, - &br[0], &br[1]); - else - br[0] = br[1] = 0; - - /* add 2 times the set border-width to the GtkXPaneds requisition */ - overhead = gtk_container_get_border_width (GTK_CONTAINER (xpaned)) * 2; - - /* also add the handle "thickness" to GtkXPaned's width requisition */ - if (xpaned->top_left_child - && gtk_widget_get_visible (xpaned->top_left_child) - && xpaned->top_right_child - && gtk_widget_get_visible (xpaned->top_right_child) - && xpaned->bottom_left_child - && gtk_widget_get_visible (xpaned->bottom_left_child) - && xpaned->bottom_right_child - && gtk_widget_get_visible (xpaned->bottom_right_child)) - { - gint handle_size; - - gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); - overhead += handle_size; - } - - for (i = 0; i < 2; i++) - w[i] = (br[i] ? br[i] : MAX (tl[i] + tr[i], bl[i])) + overhead; - - *minimal_width = w[0]; - *natural_width = w[1]; -} - -static void -gtk_xpaned_get_preferred_height (GtkWidget *widget, - gint *minimal_height, - gint *natural_height) -{ - GtkXPaned *xpaned = GTK_XPANED (widget); - gint tl[2], tr[2], bl[2], br[2]; - gint overhead; - gint h[2]; - int i; - - if (xpaned->top_left_child - && gtk_widget_get_visible (xpaned->top_left_child)) - gtk_widget_get_preferred_height (xpaned->top_left_child, &tl[0], &tl[1]); - else - tl[0] = tl[1] = 0; - - if (xpaned->top_right_child - && gtk_widget_get_visible (xpaned->top_right_child)) - gtk_widget_get_preferred_height (xpaned->top_right_child, &tr[0], &tr[1]); - else - tr[0] = tr[1] = 0; - - if (xpaned->bottom_left_child - && gtk_widget_get_visible (xpaned->bottom_left_child)) - gtk_widget_get_preferred_height (xpaned->bottom_left_child, - &bl[0], &bl[1]); - else - bl[0] = bl[1] = 0; - - if (xpaned->bottom_right_child - && gtk_widget_get_visible (xpaned->bottom_right_child)) - gtk_widget_get_preferred_height (xpaned->bottom_right_child, - &br[0], &br[1]); - else - br[0] = br[1] = 0; - - /* add 2 times the set border-width to the GtkXPaneds requisition */ - overhead = gtk_container_get_border_width (GTK_CONTAINER (xpaned)) * 2; - - /* also add the handle "thickness" to GtkXPaned's height-requisition */ - if (xpaned->top_left_child - && gtk_widget_get_visible (xpaned->top_left_child) - && xpaned->top_right_child - && gtk_widget_get_visible (xpaned->top_right_child) - && xpaned->bottom_left_child - && gtk_widget_get_visible (xpaned->bottom_left_child) - && xpaned->bottom_right_child - && gtk_widget_get_visible (xpaned->bottom_right_child)) - { - gint handle_size; - - gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); - overhead += handle_size; - } - - for (i = 0; i < 2; i++) - h[i] = (br[i] ? br[i] : bl[i] + MAX (tl[i], tr[i])) + overhead; - - *minimal_height = h[0]; - *natural_height = h[1]; -} - -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_draw (GtkWidget * widget, - cairo_t *ct); - -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->draw = gtk_xpaned_draw; - 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->get_preferred_width = gtk_xpaned_get_preferred_width; - widget_class->get_preferred_height = gtk_xpaned_get_preferred_height; - - 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_can_focus (GTK_WIDGET (xpaned), TRUE); - gtk_widget_set_has_window (GTK_WIDGET (xpaned), FALSE); - - 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->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; -} - -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_get_border_width (GTK_CONTAINER (xpaned)); - 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); - - gtk_widget_set_allocation (widget, allocation); - - if (xpaned->top_left_child - && gtk_widget_get_visible (xpaned->top_left_child) - && xpaned->top_right_child - && gtk_widget_get_visible (xpaned->top_right_child) - && xpaned->bottom_left_child - && gtk_widget_get_visible (xpaned->bottom_left_child) - && xpaned->bottom_right_child - && gtk_widget_get_visible (xpaned->bottom_right_child)) - { - /* what sizes do the children want to be at least at */ - gtk_widget_get_preferred_size (xpaned->top_left_child, - &top_left_child_requisition, NULL); - gtk_widget_get_preferred_size (xpaned->top_right_child, - &top_right_child_requisition, NULL); - gtk_widget_get_preferred_size (xpaned->bottom_left_child, - &bottom_left_child_requisition, NULL); - gtk_widget_get_preferred_size (xpaned->bottom_right_child, - &bottom_right_child_requisition, NULL); - - /* 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 = - allocation->x + border_width + - xpaned->top_left_child_size.width + handle_size; - xpaned->handle_pos_east.y = - allocation->y + border_width + - xpaned->top_left_child_size.height; - xpaned->handle_pos_east.width = - 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 = allocation->x + border_width; - xpaned->handle_pos_west.y = xpaned->handle_pos_east.y; - xpaned->handle_pos_west.width = - 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 = allocation->y + border_width; - xpaned->handle_pos_north.width = handle_size; - xpaned->handle_pos_north.height = - xpaned->handle_pos_east.y - 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 = - 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 = allocation->x + border_width; - top_left_child_allocation.y = 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 = - allocation->x + border_width + handle_size + - top_left_child_allocation.width; - top_right_child_allocation.y = 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_get_realized (widget)) - { - if (gtk_widget_get_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 children, making sure, when resizing not to - * overlap the windows - */ - if (gtk_widget_get_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_realized (widget, TRUE); - xpaned = GTK_XPANED (widget); - - gtk_widget_set_window (widget, 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 (gtk_widget_get_window (widget), - &attributes_east, - attributes_mask_east); - xpaned->handle_west = gdk_window_new (gtk_widget_get_window (widget), - &attributes_west, - attributes_mask_west); - xpaned->handle_north = gdk_window_new (gtk_widget_get_window (widget), - &attributes_north, - attributes_mask_north); - xpaned->handle_south = gdk_window_new (gtk_widget_get_window (widget), - &attributes_south, - attributes_mask_south); - xpaned->handle_middle = gdk_window_new (gtk_widget_get_window (widget), - &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); - - g_object_unref (attributes_east.cursor); - g_object_unref (attributes_west.cursor); - g_object_unref (attributes_north.cursor); - g_object_unref (attributes_south.cursor); - g_object_unref (attributes_middle.cursor); - - if (xpaned->top_left_child - && gtk_widget_get_visible (xpaned->top_left_child) - && xpaned->top_right_child - && gtk_widget_get_visible (xpaned->top_right_child) - && xpaned->bottom_left_child - && gtk_widget_get_visible (xpaned->bottom_left_child) - && xpaned->bottom_right_child - && gtk_widget_get_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->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_draw (GtkWidget * widget, cairo_t *cr) -{ - GtkXPaned *xpaned = GTK_XPANED (widget); - gint handle_size; - - /* 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_get_visible (widget) && gtk_widget_get_mapped (widget) && - xpaned->top_left_child - && gtk_widget_get_visible (xpaned->top_left_child) - && xpaned->top_right_child - && gtk_widget_get_visible (xpaned->top_right_child) - && xpaned->bottom_left_child - && gtk_widget_get_visible (xpaned->bottom_left_child) - && xpaned->bottom_right_child - && gtk_widget_get_visible (xpaned->bottom_right_child)) - { - GtkStyleContext *context; - - context = gtk_widget_get_style_context (widget); - gtk_render_handle (context, cr, - xpaned->handle_pos_east.x - handle_size - 256 / 2, - xpaned->handle_pos_west.y + 1, - 256 + handle_size, handle_size - 2); - - gtk_render_handle (context, cr, - xpaned->handle_pos_north.x + 1, - xpaned->handle_pos_south.y - handle_size - 256 / 2, - handle_size - 2, 256 + handle_size); - } - - /* Chain up to draw children */ - GTK_WIDGET_CLASS (parent_class)->draw (widget, cr); - - 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; - GtkWidget *widget = GTK_WIDGET (xpaned); - gint handle_size; - GtkRequisition size; - GtkAllocation allocation; - - gtk_widget_get_allocation (widget, &allocation); - - gdk_window_get_device_position (gtk_widget_get_window (widget), - gdk_device_manager_get_client_pointer ( - gdk_display_get_device_manager ( - gtk_widget_get_display (widget))), - &pos.x, &pos.y, NULL); - if (!gtk_widget_get_has_window (widget)) - { - pos.x -= allocation.x; - pos.y -= allocation.y; - } - - if (xpaned->in_drag_vert) - { - pos.y -= xpaned->drag_pos.y; - - if (is_rtl (xpaned)) - { - gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); - - size.height = allocation.height - pos.y - handle_size; - } - else - { - size.height = pos.y; - } - - size.height -= gtk_container_get_border_width (GTK_CONTAINER (xpaned)); - - 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 (widget, "handle-size", &handle_size, NULL); - - size.width = allocation.width - pos.x - handle_size; - } - else - { - size.width = pos.x; - } - - size.width -= gtk_container_get_border_width (GTK_CONTAINER (xpaned)); - - 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 (widget, "handle-size", &handle_size, NULL); - - size.width = allocation.width - pos.x - handle_size; - size.height = allocation.height - pos.y - handle_size; - } - else - { - size.width = pos.x; - size.height = pos.y; - } - - size.width -= gtk_container_get_border_width (GTK_CONTAINER (xpaned)); - size.height -= gtk_container_get_border_width (GTK_CONTAINER (xpaned)); - - 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_set_can_focus (GTK_WIDGET (widget), FALSE); - retval = (*GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction); - gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE); - - return retval; -} - -static void -gtk_xpaned_button_press_grab (GdkWindow *handle, GdkEventButton *event) -{ - /* We need a server grab here, not gtk_grab_add(), since - * we don't want to pass events on to the widget's children */ - gdk_device_grab (event->device, handle, - GDK_OWNERSHIP_NONE, - FALSE, - (GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON1_MOTION_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK), - NULL, event->time); -} - -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; - gtk_xpaned_button_press_grab (xpaned->handle_middle, event); - 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; - gtk_xpaned_button_press_grab (xpaned->handle_east, event); - 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; - gtk_xpaned_button_press_grab (xpaned->handle_west, event); - 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; - gtk_xpaned_button_press_grab (xpaned->handle_north, event); - 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; - gtk_xpaned_button_press_grab (xpaned->handle_south, event); - 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_device_ungrab (event->device, 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_device_ungrab (event->device, 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_device_ungrab (event->device, 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_get_visible (widget); - - if (xpaned->top_left_child == widget) - { - gtk_widget_unparent (widget); - - xpaned->top_left_child = NULL; - - if (was_visible && gtk_widget_get_visible (GTK_WIDGET (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_get_visible (GTK_WIDGET (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_get_visible (GTK_WIDGET (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_get_visible (GTK_WIDGET (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_get_border_width (GTK_CONTAINER (xpaned)); - - 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; - - 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 - { - 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; -} - -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_is_toplevel (toplevel)) - return gtk_window_get_focus (GTK_WINDOW (toplevel)); - - 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 = gtk_widget_get_parent (w)) - if (GTK_IS_XPANED (w)) - last_focus = w; - - if (gtk_container_get_focus_child (container) == xpaned->top_left_child) - gtk_xpaned_set_last_top_left_child_focus (xpaned, last_focus); - else if (gtk_container_get_focus_child (container) == xpaned->top_right_child) - gtk_xpaned_set_last_top_right_child_focus (xpaned, last_focus); - else if (gtk_container_get_focus_child (container) == xpaned->bottom_left_child) - gtk_xpaned_set_last_bottom_left_child_focus (xpaned, last_focus); - else if (gtk_container_get_focus_child (container) == 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_get_parent (GTK_WIDGET (xpaned))) - ancestor = gtk_widget_get_ancestor (gtk_widget_get_parent (GTK_WIDGET (xpaned)), - 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 (gtk_container_get_focus_child (container) == 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 (gtk_container_get_focus_child (container) == 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 (gtk_container_get_focus_child (container) == 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 (gtk_container_get_focus_child (container) == 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 (gtk_container_get_focus_child (container) == 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 (gtk_container_get_focus_child (container) == 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 (gtk_container_get_focus_child (container) == 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 (gtk_container_get_focus_child (container) == 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 = gtk_widget_get_parent (w)) - { - 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_get_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 (gtk_container_get_focus_child (container) == xpaned->top_left_child) - { - if (reversed) - { - focus = prev; - first = xpaned; - } - else - { - focus = xpaned; - first = xpaned; - } - } - else if (gtk_container_get_focus_child (container) == 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_get_focus (GTK_WINDOW (toplevel))); - 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 deleted file mode 100644 index bb6b196949..0000000000 --- a/lib/gtk-contrib/gtkxpaned.h +++ /dev/null @@ -1,183 +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; - - 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/src/data/case.c b/src/data/case.c index fe569de200..7676922398 100644 --- a/src/data/case.c +++ b/src/data/case.c @@ -39,7 +39,7 @@ #endif static size_t case_size (const struct caseproto *); -static bool variable_matches_case (const struct ccase *, +static void assert_variable_matches_case (const struct ccase *, const struct variable *); static void copy_forward (struct ccase *dst, size_t dst_idx, const struct ccase *src, size_t src_idx, @@ -264,7 +264,7 @@ case_copy_in (struct ccase *c, const union value * case_data (const struct ccase *c, const struct variable *v) { - assert (variable_matches_case (c, v)); + assert_variable_matches_case (c, v); return &c->values[var_get_case_index (v)]; } @@ -286,7 +286,7 @@ case_data_idx (const struct ccase *c, size_t idx) union value * case_data_rw (struct ccase *c, const struct variable *v) { - assert (variable_matches_case (c, v)); + assert_variable_matches_case (c, v); assert (!case_is_shared (c)); return &c->values[var_get_case_index (v)]; } @@ -310,7 +310,7 @@ case_data_rw_idx (struct ccase *c, size_t idx) double case_num (const struct ccase *c, const struct variable *v) { - assert (variable_matches_case (c, v)); + assert_variable_matches_case (c, v); return c->values[var_get_case_index (v)].f; } @@ -332,8 +332,8 @@ case_num_idx (const struct ccase *c, size_t idx) const uint8_t * case_str (const struct ccase *c, const struct variable *v) { + assert_variable_matches_case (c, v); size_t idx = var_get_case_index (v); - assert (variable_matches_case (c, v)); return value_str (&c->values[idx], caseproto_get_width (c->proto, idx)); } @@ -360,8 +360,8 @@ case_str_idx (const struct ccase *c, size_t idx) uint8_t * case_str_rw (struct ccase *c, const struct variable *v) { + assert_variable_matches_case (c, v); size_t idx = var_get_case_index (v); - assert (variable_matches_case (c, v)); assert (!case_is_shared (c)); return value_str_rw (&c->values[idx], caseproto_get_width (c->proto, idx)); } @@ -468,12 +468,12 @@ case_size (const struct caseproto *proto) or write data in C. Useful in assertions. */ -static bool UNUSED -variable_matches_case (const struct ccase *c, const struct variable *v) +static void +assert_variable_matches_case (const struct ccase *c, const struct variable *v) { size_t case_idx = var_get_case_index (v); - return (case_idx < caseproto_get_n_widths (c->proto) - && caseproto_get_width (c->proto, case_idx) == var_get_width (v)); + assert (case_idx < caseproto_get_n_widths (c->proto)); + assert (caseproto_get_width (c->proto, case_idx) == var_get_width (v)); } /* Internal helper function for case_copy(). */ diff --git a/src/data/variable.c b/src/data/variable.c index 3e3c72a6bb..656a06204f 100644 --- a/src/data/variable.c +++ b/src/data/variable.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,6 +42,42 @@ #include "gettext.h" #define _(msgid) gettext (msgid) +#define N_(msgid) (msgid) + +/* This should follow the definition in Gtk */ +typedef struct +{ + int value; + const char *name; + const char *label; +} GEnumValue; + +const GEnumValue align[] = + { + {ALIGN_LEFT, "left", N_("Left")}, + {ALIGN_RIGHT, "right", N_("Right")}, + {ALIGN_CENTRE, "center", N_("Center")}, + {0,0,0} + }; + +const GEnumValue measure[] = + { + {MEASURE_NOMINAL, "nominal", N_("Nominal")}, + {MEASURE_ORDINAL, "ordinal", N_("Ordinal")}, + {MEASURE_SCALE, "scale", N_("Scale")}, + {0,0,0} + }; + +const GEnumValue role[] = + { + {ROLE_INPUT, "input", N_("Input")}, + {ROLE_TARGET, "output", N_("Output")}, + {ROLE_BOTH, "both", N_("Both")}, + {ROLE_NONE, "none", N_("None")}, + {ROLE_PARTITION, "partition", N_("Partition")}, + {ROLE_SPLIT, "split", N_("Split")}, + {0,0,0} + }; /* A variable. */ struct variable @@ -771,20 +807,8 @@ measure_is_valid (enum measure m) const char * measure_to_string (enum measure m) { - switch (m) - { - case MEASURE_NOMINAL: - return _("Nominal"); - - case MEASURE_ORDINAL: - return _("Ordinal"); - - case MEASURE_SCALE: - return _("Scale"); - - default: - return "Invalid"; - } + assert (m == measure[m].value); + return gettext (measure[m].label); } /* Returns a string version of measurement level M, for use in PSPP command @@ -866,31 +890,10 @@ var_role_is_valid (enum var_role role) /* Returns a string version of ROLE, for display to a user. */ const char * -var_role_to_string (enum var_role role) +var_role_to_string (enum var_role r) { - switch (role) - { - case ROLE_INPUT: - return _("Input"); - - case ROLE_TARGET: - return _("Output"); - - case ROLE_BOTH: - return _("Both"); - - case ROLE_NONE: - return _("None"); - - case ROLE_PARTITION: - return _("Partition"); - - case ROLE_SPLIT: - return _("Split"); - - default: - return "Invalid"; - } + assert (r == role[r].value); + return gettext (role[r].label); } /* Returns a string version of ROLE, for use in PSPP comamnd syntax. */ @@ -996,20 +999,8 @@ alignment_is_valid (enum alignment a) const char * alignment_to_string (enum alignment a) { - switch (a) - { - case ALIGN_LEFT: - return _("Left"); - - case ALIGN_RIGHT: - return _("Right"); - - case ALIGN_CENTRE: - return _("Center"); - - default: - return "Invalid"; - } + assert (a == align[a].value); + return gettext (align[a].label); } /* Returns a string version of alignment A, for use in PSPP command syntax. */ diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 3d10ed0229..1877263b73 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -85,13 +85,14 @@ EXTRA_DIST += \ src/ui/gui/marshaller-list \ src/ui/gui/pspplogo.svg +src_ui_gui_psppire_CPPFLAGS= if HAVE_GUI -bin_PROGRAMS += src/ui/gui/psppire -noinst_PROGRAMS += src/ui/gui/spreadsheet-test +bin_PROGRAMS += src/ui/gui/psppire +noinst_PROGRAMS += src/ui/gui/spreadsheet-test -src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GTKSOURCEVIEW_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 -src_ui_gui_spreadsheet_test_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 +src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GTKSOURCEVIEW_CFLAGS) $(SPREAD_SHEET_WIDGET_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 +src_ui_gui_spreadsheet_test_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 src_ui_gui_psppire_LDFLAGS = \ @@ -107,13 +108,13 @@ endif src_ui_gui_psppire_LDADD = \ - lib/gtk-contrib/libxpaned.a \ src/ui/libuicommon.la \ src/libpspp.la \ src/libpspp-core.la \ $(GTK_LIBS) \ $(GTHREAD_LIBS) \ $(GTKSOURCEVIEW_LIBS) \ + $(SPREAD_SHEET_WIDGET_LIBS) \ $(CAIRO_LIBS) \ $(LIBINTL) \ $(GSL_LIBS) @@ -137,23 +138,9 @@ INSTALL_DATA_HOOKS += install-lang dist_src_ui_gui_psppire_DATA = \ $(UI_FILES) \ - $(top_srcdir)/src/ui/gui/pspp.lang \ - $(top_srcdir)/src/ui/gui/psppire.gtkrc + $(top_srcdir)/src/ui/gui/pspp.lang src_ui_gui_psppire_SOURCES = \ - src/ui/gui/pspp-sheet-private.h \ - src/ui/gui/pspp-sheet-selection.c \ - src/ui/gui/pspp-sheet-selection.h \ - src/ui/gui/pspp-sheet-view-column.c \ - src/ui/gui/pspp-sheet-view-column.h \ - src/ui/gui/pspp-sheet-view.c \ - src/ui/gui/pspp-sheet-view.h \ - src/ui/gui/pspp-widget-facade.c \ - src/ui/gui/pspp-widget-facade.h \ - src/ui/gui/psppire-button-editable.c \ - src/ui/gui/psppire-button-editable.h \ - src/ui/gui/psppire-cell-renderer-button.c \ - src/ui/gui/psppire-cell-renderer-button.h \ src/ui/gui/psppire-dialog.c \ src/ui/gui/psppire-keypad.c \ src/ui/gui/psppire-selector.c \ @@ -196,8 +183,6 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-conf.h \ src/ui/gui/psppire-data-editor.c \ src/ui/gui/psppire-data-editor.h \ - src/ui/gui/psppire-data-sheet.c \ - src/ui/gui/psppire-data-sheet.h \ src/ui/gui/psppire-data-store.c \ src/ui/gui/psppire-data-store.h \ src/ui/gui/psppire-data-window.c \ @@ -293,8 +278,6 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-dict.h \ src/ui/gui/psppire-dictview.c \ src/ui/gui/psppire-dictview.h \ - src/ui/gui/psppire-empty-list-store.c \ - src/ui/gui/psppire-empty-list-store.h \ src/ui/gui/psppire-encoding-selector.c \ src/ui/gui/psppire-encoding-selector.h \ src/ui/gui/psppire-format.c \ @@ -317,14 +300,22 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-select-dest.h \ src/ui/gui/psppire-syntax-window.c \ src/ui/gui/psppire-syntax-window.h \ + src/ui/gui/psppire-delimited-text.c \ + src/ui/gui/psppire-delimited-text.h \ + src/ui/gui/psppire-text-file.c \ + src/ui/gui/psppire-text-file.h \ src/ui/gui/psppire-val-chooser.c \ src/ui/gui/psppire-val-chooser.h \ src/ui/gui/psppire-value-entry.c \ src/ui/gui/psppire-value-entry.h \ src/ui/gui/psppire-var-ptr.c \ src/ui/gui/psppire-var-ptr.h \ - src/ui/gui/psppire-var-sheet.c \ - src/ui/gui/psppire-var-sheet.h \ + src/ui/gui/psppire-data-sheet.c \ + src/ui/gui/psppire-data-sheet.h \ + src/ui/gui/psppire-variable-sheet.c \ + src/ui/gui/psppire-variable-sheet.h \ + src/ui/gui/psppire-var-sheet-header.h \ + src/ui/gui/psppire-var-sheet-header.c \ src/ui/gui/psppire-window.c \ src/ui/gui/psppire-window.h \ src/ui/gui/psppire-window-base.c \ @@ -339,6 +330,8 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/var-display.h \ src/ui/gui/var-type-dialog.c \ src/ui/gui/var-type-dialog.h \ + src/ui/gui/value-variant.c \ + src/ui/gui/value-variant.h \ src/ui/gui/widget-io.c \ src/ui/gui/widget-io.h \ src/ui/gui/widgets.c \ @@ -412,6 +405,7 @@ BUILT_SOURCES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h src/u CLEANFILES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h \ src/ui/gui/resources.c $(nodist_src_ui_gui_psppire_DATA) + endif HAVE_GUI #ensure the installcheck passes even if there is no X server available @@ -419,7 +413,7 @@ installcheck-local: DISPLAY=/invalid/port $(MAKE) $(AM_MAKEFLAGS) installcheck-binPROGRAMS # wrapper -src_ui_gui_psppire_CPPFLAGS = $(AM_CPPFLAGS) -Isrc/ui/gui/include +src_ui_gui_psppire_CPPFLAGS += $(AM_CPPFLAGS) -Isrc/ui/gui/include BUILT_SOURCES += src/ui/gui/include/gtk/gtk.h src/ui/gui/include/gtk/gtk.h: src/ui/gui/include/gtk/gtk.in.h @$(MKDIR_P) src/ui/gui/include/gtk diff --git a/src/ui/gui/find-dialog.c b/src/ui/gui/find-dialog.c index fddbe8c49b..123f7cf69b 100644 --- a/src/ui/gui/find-dialog.c +++ b/src/ui/gui/find-dialog.c @@ -36,7 +36,6 @@ which match particular strings */ #include "ui/gui/dict-display.h" #include "ui/gui/find-dialog.h" #include "ui/gui/helper.h" -#include "ui/gui/psppire-data-sheet.h" #include "ui/gui/psppire-data-store.h" #include "ui/gui/psppire-data-window.h" #include "ui/gui/psppire-dialog.h" @@ -100,13 +99,12 @@ refresh (GObject *obj, const struct find_dialog *fd) static void do_find (GObject *obj, const struct find_dialog *fd) { - PsppireDataSheet *data_sheet; casenumber x = -1; gint column = -1; glong row; - data_sheet = psppire_data_editor_get_active_data_sheet (fd->de->data_editor); - row = psppire_data_sheet_get_selected_case (data_sheet); + + row = 10; find_value (fd, row, &x, &column); @@ -115,8 +113,6 @@ do_find (GObject *obj, const struct find_dialog *fd) gtk_notebook_set_current_page (GTK_NOTEBOOK (fd->de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW); - psppire_data_sheet_goto_case (data_sheet, x); - psppire_data_sheet_goto_variable (data_sheet, column); } } diff --git a/src/ui/gui/goto-case-dialog.c b/src/ui/gui/goto-case-dialog.c index 282f416368..1b5e39a45d 100644 --- a/src/ui/gui/goto-case-dialog.c +++ b/src/ui/gui/goto-case-dialog.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2007, 2011, 2012 Free Software Foundation + Copyright (C) 2007, 2011, 2012, 2016 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 @@ -21,17 +21,17 @@ #include "psppire-dialog.h" #include "psppire-data-window.h" #include "psppire-data-store.h" +#include "psppire-data-sheet.h" static void refresh (PsppireDataSheet *ds, GtkBuilder *xml) { - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (ds); - casenumber case_count ; + PsppireDataStore *store = NULL; + g_object_get (ds, "data-model", &store, NULL); GtkWidget *case_num_entry = get_widget_assert (xml, "goto-case-case-num-entry"); - - case_count = psppire_data_store_get_case_count (data_store); + casenumber case_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (case_num_entry), 1, case_count); } @@ -51,18 +51,23 @@ goto_case_dialog (PsppireDataSheet *ds) response = psppire_dialog_run (PSPPIRE_DIALOG (dialog)); - if ( response == PSPPIRE_RESPONSE_GOTO ) + if (response == PSPPIRE_RESPONSE_GOTO) { - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (ds); - glong case_num; - GtkWidget *case_num_entry = - get_widget_assert (xml, "goto-case-case-num-entry"); + PsppireDataStore *store = NULL; + g_object_get (ds, "data-model", &store, NULL); - case_num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (case_num_entry)) - - FIRST_CASE_NUMBER ; - - if (case_num >= 0 - && case_num < psppire_data_store_get_case_count (data_store)) - psppire_data_sheet_goto_case (ds, case_num); + GtkWidget *case_num_entry = + get_widget_assert (xml, "goto-case-case-num-entry"); + + glong case_num = + gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (case_num_entry)) + - FIRST_CASE_NUMBER ; + + if (case_num >= 0 && + case_num < gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ds), NULL)) + { + ssw_sheet_scroll_to (ds, -1, case_num); + ssw_sheet_set_active_cell (ds, -1, case_num, 0); + } } } diff --git a/src/ui/gui/goto-case-dialog.h b/src/ui/gui/goto-case-dialog.h index e7c70eee25..422cdff37a 100644 --- a/src/ui/gui/goto-case-dialog.h +++ b/src/ui/gui/goto-case-dialog.h @@ -19,6 +19,7 @@ #include "psppire-data-sheet.h" + void goto_case_dialog (PsppireDataSheet *ds); #endif diff --git a/src/ui/gui/marshaller-list b/src/ui/gui/marshaller-list index 0f38e6fc2e..5b6c0e5b58 100644 --- a/src/ui/gui/marshaller-list +++ b/src/ui/gui/marshaller-list @@ -12,3 +12,4 @@ VOID:INT,INT VOID:OBJECT,OBJECT VOID:POINTER,INT,INT VOID:INT,UINT,POINTER +VOID:UINT,UINT,UINT diff --git a/src/ui/gui/missing-val-dialog.c b/src/ui/gui/missing-val-dialog.c index 45916dd932..83ec504edd 100644 --- a/src/ui/gui/missing-val-dialog.c +++ b/src/ui/gui/missing-val-dialog.c @@ -151,7 +151,7 @@ psppire_missing_val_dialog_new (const struct variable *var) NULL)); } -void +gint psppire_missing_val_dialog_run (GtkWindow *parent_window, const struct variable *var, struct missing_values *mv) @@ -163,12 +163,14 @@ psppire_missing_val_dialog_run (GtkWindow *parent_window, gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_widget_show (GTK_WIDGET (dialog)); - if (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK) + gint result = psppire_dialog_run (PSPPIRE_DIALOG (dialog)); + if (result == GTK_RESPONSE_OK) mv_copy (mv, psppire_missing_val_dialog_get_missing_values (dialog)); else mv_copy (mv, var_get_missing_values (var)); gtk_widget_destroy (GTK_WIDGET (dialog)); + return result; } diff --git a/src/ui/gui/missing-val-dialog.h b/src/ui/gui/missing-val-dialog.h index 12bf110129..6d04d55bd5 100644 --- a/src/ui/gui/missing-val-dialog.h +++ b/src/ui/gui/missing-val-dialog.h @@ -71,7 +71,7 @@ void psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *, const struct missing_values *psppire_missing_val_dialog_get_missing_values ( const PsppireMissingValDialog *); -void psppire_missing_val_dialog_run (GtkWindow *parent_window, +gint psppire_missing_val_dialog_run (GtkWindow *parent_window, const struct variable *, struct missing_values *); diff --git a/src/ui/gui/pspp-sheet-private.h b/src/ui/gui/pspp-sheet-private.h deleted file mode 100644 index e01fd47d21..0000000000 --- a/src/ui/gui/pspp-sheet-private.h +++ /dev/null @@ -1,455 +0,0 @@ - /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -/* gtktreeprivate.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GTK_TREE_PRIVATE_H__ -#define __GTK_TREE_PRIVATE_H__ - - -#include -#include "libpspp/range-tower.h" -#include "ui/gui/pspp-sheet-view.h" -#include "ui/gui/pspp-sheet-view-column.h" - -#define TREE_VIEW_DRAG_WIDTH 6 - -typedef enum -{ - PSPP_SHEET_VIEW_IN_COLUMN_RESIZE = 1 << 2, - PSPP_SHEET_VIEW_HEADERS_VISIBLE = 1 << 4, - PSPP_SHEET_VIEW_DRAW_KEYFOCUS = 1 << 5, - PSPP_SHEET_VIEW_MODEL_SETUP = 1 << 6, - PSPP_SHEET_VIEW_IN_COLUMN_DRAG = 1 << 7 -} PsppSheetViewFlags; - -enum -{ - DRAG_COLUMN_WINDOW_STATE_UNSET = 0, - DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1, - DRAG_COLUMN_WINDOW_STATE_ARROW = 2, - DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3, - DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4 -}; - -enum -{ - RUBBER_BAND_OFF = 0, - RUBBER_BAND_MAYBE_START = 1, - RUBBER_BAND_ACTIVE = 2 -}; - -#define PSPP_SHEET_VIEW_SET_FLAG(tree_view, flag) G_STMT_START{ (tree_view->priv->flags|=flag); }G_STMT_END -#define PSPP_SHEET_VIEW_UNSET_FLAG(tree_view, flag) G_STMT_START{ (tree_view->priv->flags&=~(flag)); }G_STMT_END -#define PSPP_SHEET_VIEW_FLAG_SET(tree_view, flag) ((tree_view->priv->flags&flag)==flag) -#define TREE_VIEW_HEADER_HEIGHT(tree_view) (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE)?tree_view->priv->header_height:0) -#define TREE_VIEW_COLUMN_REQUESTED_WIDTH(column) (CLAMP (column->requested_width, (column->min_width!=-1)?column->min_width:column->requested_width, (column->max_width!=-1)?column->max_width:column->requested_width)) - - /* This lovely little value is used to determine how far away from the title bar - * you can move the mouse and still have a column drag work. - */ -#define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*TREE_VIEW_HEADER_HEIGHT(tree_view)) - -typedef struct _PsppSheetViewColumnReorder PsppSheetViewColumnReorder; -struct _PsppSheetViewColumnReorder -{ - gint left_align; - gint right_align; - PsppSheetViewColumn *left_column; - PsppSheetViewColumn *right_column; -}; - -struct _PsppSheetViewPrivate -{ - GtkTreeModel *model; - - guint flags; - /* tree information */ - gint row_count; - struct range_tower *selected; - - /* Container info */ - GList *children; - gint width; - gint height; - - /* Adjustments */ - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - - /* Sub windows */ - GdkWindow *bin_window; - GdkWindow *header_window; - - /* Scroll position state keeping */ - GtkTreeRowReference *top_row; - gint top_row_dy; - /* dy == y pos of top_row + top_row_dy */ - /* we cache it for simplicity of the code */ - gint dy; - - guint presize_handler_timer; - guint validate_rows_timer; - guint scroll_sync_timer; - - /* Indentation and expander layout */ - gint expander_size; - - /* Key navigation (focus), selection */ - gint cursor_offset; - - GtkTreeRowReference *anchor; - GtkTreeRowReference *cursor; - - PsppSheetViewColumn *focus_column; - - /* Current pressed node, previously pressed, prelight */ - gint pressed_button; - gint press_start_x; - gint press_start_y; - gint press_start_node; - - gint event_last_x; - gint event_last_y; - - guint last_button_time; - gint last_button_x; - gint last_button_y; - - int prelight_node; - - /* Cell Editing */ - PsppSheetViewColumn *edited_column; - gint edited_row; - - /* Selection information */ - PsppSheetSelection *selection; - - /* Header information */ - gint n_columns; - GList *columns; - gint header_height; - gint n_selected_columns; - - PsppSheetViewColumnDropFunc column_drop_func; - gpointer column_drop_func_data; - GDestroyNotify column_drop_func_data_destroy; - GList *column_drag_info; - PsppSheetViewColumnReorder *cur_reorder; - - /* Interactive Header reordering */ - GdkWindow *drag_window; - GdkWindow *drag_highlight_window; - PsppSheetViewColumn *drag_column; - gint drag_column_x; - - /* Interactive Header Resizing */ - gint drag_pos; - gint x_drag; - - /* Non-interactive Header Resizing, expand flag support */ - gint prev_width; - - /* ATK Hack */ - PsppSheetDestroyCountFunc destroy_count_func; - gpointer destroy_count_data; - GDestroyNotify destroy_count_destroy; - - /* Scroll timeout (e.g. during dnd, rubber banding) */ - guint scroll_timeout; - - /* Row drag-and-drop */ - GtkTreeRowReference *drag_dest_row; - PsppSheetViewDropPosition drag_dest_pos; - guint open_dest_timeout; - - /* Rubber banding */ - gint rubber_band_status; - gint rubber_band_x; - gint rubber_band_y; - gint rubber_band_shift; - gint rubber_band_ctrl; - - int rubber_band_start_node; - - int rubber_band_end_node; - - /* Rectangular selection. */ - PsppSheetViewColumn *anchor_column; /* XXX needs to be a weak pointer? */ - - /* fixed height */ - gint fixed_height; - gboolean fixed_height_set; - - /* Scroll-to functionality when unrealized */ - GtkTreeRowReference *scroll_to_path; - PsppSheetViewColumn *scroll_to_column; - gfloat scroll_to_row_align; - gfloat scroll_to_col_align; - - /* Interactive search */ - gint selected_iter; - gint search_column; - PsppSheetViewSearchPositionFunc search_position_func; - PsppSheetViewSearchEqualFunc search_equal_func; - gpointer search_user_data; - GDestroyNotify search_destroy; - gpointer search_position_user_data; - GDestroyNotify search_position_destroy; - GtkWidget *search_window; - GtkWidget *search_entry; - guint search_entry_changed_id; - guint typeselect_flush_timeout; - - /* Grid and tree lines */ - PsppSheetViewGridLines grid_lines; - - /* Special cells. */ - PsppSheetViewSpecialCells special_cells; - - /* Tooltip support */ - gint tooltip_column; - - /* Cached style for button facades in columns. */ - GtkStyle *button_style; - - /* Here comes the bitfield */ - guint scroll_to_use_align : 1; - - guint reorderable : 1; - guint header_has_focus : 1; - guint drag_column_window_state : 3; - /* hint to display rows in alternating colors */ - guint has_rules : 1; - - /* for DnD */ - guint empty_view_drop : 1; - - guint init_hadjust_value : 1; - - guint in_top_row_to_dy : 1; - - /* interactive search */ - guint enable_search : 1; - guint disable_popdown : 1; - guint search_custom_entry_set : 1; - - guint hover_selection : 1; - guint imcontext_changed : 1; - - guint rubber_banding_enable : 1; - - guint in_grab : 1; - - guint post_validation_flag : 1; - - /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */ - - guint search_entry_avoid_unhandled_binding : 1; - /* GtkScrollablePolicy needs to be checked when - * driving the scrollable adjustment values */ - guint hscroll_policy : 1; - guint vscroll_policy : 1; - - /* For optimisation of size allocate requests */ - guint resized : 1; - }; - -#ifdef __GNUC__ - -#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "%s (%s): assertion `%s' failed.\n" \ - "There is a disparity between the internal view of the PsppSheetView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - G_STRLOC, \ - G_STRFUNC, \ - #expr); \ - return ret; \ - }; }G_STMT_END - -#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "%s (%s): assertion `%s' failed.\n" \ - "There is a disparity between the internal view of the PsppSheetView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - G_STRLOC, \ - G_STRFUNC, \ - #expr); \ - return; \ - }; }G_STMT_END - -#else - -#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d: assertion `%s' failed.\n" \ - "There is a disparity between the internal view of the PsppSheetView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - __FILE__, \ - __LINE__, \ - #expr); \ - return ret; \ - }; }G_STMT_END - -#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \ - if (!(expr)) \ - { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d: assertion '%s' failed.\n" \ - "There is a disparity between the internal view of the PsppSheetView,\n" \ - "and the GtkTreeModel. This generally means that the model has changed\n"\ - "without letting the view know. Any display from now on is likely to\n" \ - "be incorrect.\n", \ - __FILE__, \ - __LINE__, \ - #expr); \ - return; \ - }; }G_STMT_END -#endif - - -/* functions that shouldn't be exported */ -void _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection, - int node, - GtkTreePath *path, - PsppSheetSelectMode mode, - gboolean override_browse_mode); -void _pspp_sheet_selection_emit_changed (PsppSheetSelection *selection); -void _pspp_sheet_view_find_node (PsppSheetView *tree_view, - GtkTreePath *path, - int *node); -GtkTreePath *_pspp_sheet_view_find_path (PsppSheetView *tree_view, - int node); -void _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view, - GtkWidget *widget, - gint x, - gint y, - gint width, - gint height); -void _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view, - int node, - const GdkRectangle *clip_rect); - -void _pspp_sheet_view_column_realize_button (PsppSheetViewColumn *column); -void _pspp_sheet_view_column_unrealize_button (PsppSheetViewColumn *column); -void _pspp_sheet_view_column_set_tree_view (PsppSheetViewColumn *column, - PsppSheetView *tree_view); -void _pspp_sheet_view_column_unset_model (PsppSheetViewColumn *column, - GtkTreeModel *old_model); -void _pspp_sheet_view_column_unset_tree_view (PsppSheetViewColumn *column); -void _pspp_sheet_view_column_set_width (PsppSheetViewColumn *column, - gint width); -void _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view, - PsppSheetViewColumn *column); -gboolean _pspp_sheet_view_column_cell_event (PsppSheetViewColumn *tree_column, - GtkCellEditable **editable_widget, - GdkEvent *event, - gchar *path_string, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags); -void _pspp_sheet_view_column_start_editing (PsppSheetViewColumn *tree_column, - GtkCellEditable *editable_widget); -void _pspp_sheet_view_column_stop_editing (PsppSheetViewColumn *tree_column); -void _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view); -void _pspp_sheet_view_column_autosize (PsppSheetView *tree_view, - PsppSheetViewColumn *column); - -gboolean _pspp_sheet_view_column_has_editable_cell (PsppSheetViewColumn *column); -GtkCellRenderer *_pspp_sheet_view_column_get_edited_cell (PsppSheetViewColumn *column); -gint _pspp_sheet_view_column_count_special_cells (PsppSheetViewColumn *column); -GtkCellRenderer *_pspp_sheet_view_column_get_cell_at_pos (PsppSheetViewColumn *column, - gint x); - -PsppSheetSelection* _pspp_sheet_selection_new (void); -PsppSheetSelection* _pspp_sheet_selection_new_with_tree_view (PsppSheetView *tree_view); -void _pspp_sheet_selection_set_tree_view (PsppSheetSelection *selection, - PsppSheetView *tree_view); - -void _pspp_sheet_view_column_cell_render (PsppSheetViewColumn *tree_column, - cairo_t *cr, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags); -void _pspp_sheet_view_column_get_focus_area (PsppSheetViewColumn *tree_column, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GdkRectangle *focus_area); -gboolean _pspp_sheet_view_column_cell_focus (PsppSheetViewColumn *tree_column, - gint direction, - gboolean left, - gboolean right); -void _pspp_sheet_view_column_cell_draw_focus (PsppSheetViewColumn *tree_column, - cairo_t *cr, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags); -void _pspp_sheet_view_column_cell_set_dirty (PsppSheetViewColumn *tree_column); -void _pspp_sheet_view_column_get_neighbor_sizes (PsppSheetViewColumn *column, - GtkCellRenderer *cell, - gint *left, - gint *right); - -gboolean pspp_sheet_view_node_is_selected (PsppSheetView *tree_view, - int node); -void pspp_sheet_view_node_select (PsppSheetView *tree_view, - int node); -void pspp_sheet_view_node_unselect (PsppSheetView *tree_view, - int node); - -gint -pspp_sheet_view_node_next (PsppSheetView *tree_view, - gint node); -gint -pspp_sheet_view_node_prev (PsppSheetView *tree_view, - gint node); - -#endif /* __GTK_TREE_PRIVATE_H__ */ - diff --git a/src/ui/gui/pspp-sheet-selection.c b/src/ui/gui/pspp-sheet-selection.c deleted file mode 100644 index 98089147f9..0000000000 --- a/src/ui/gui/pspp-sheet-selection.c +++ /dev/null @@ -1,1303 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -/* gtktreeselection.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -#include "ui/gui/pspp-sheet-private.h" - -#include -#include - -#include "ui/gui/pspp-sheet-selection.h" - -#include "libpspp/range-set.h" - -static void pspp_sheet_selection_finalize (GObject *object); -static gint pspp_sheet_selection_real_select_all (PsppSheetSelection *selection); -static gint pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection); -static gint pspp_sheet_selection_real_select_node (PsppSheetSelection *selection, - int node, - gboolean select); - -enum -{ - CHANGED, - LAST_SIGNAL -}; - -static guint tree_selection_signals [LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE (PsppSheetSelection, pspp_sheet_selection, G_TYPE_OBJECT) - -static void -pspp_sheet_selection_class_init (PsppSheetSelectionClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass*) class; - - object_class->finalize = pspp_sheet_selection_finalize; - class->changed = NULL; - - tree_selection_signals[CHANGED] = - g_signal_new ("changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (PsppSheetSelectionClass, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -pspp_sheet_selection_init (PsppSheetSelection *selection) -{ - selection->type = PSPP_SHEET_SELECTION_SINGLE; -} - -static void -pspp_sheet_selection_finalize (GObject *object) -{ - G_OBJECT_CLASS (pspp_sheet_selection_parent_class)->finalize (object); -} - -/** - * _pspp_sheet_selection_new: - * - * Creates a new #PsppSheetSelection object. This function should not be invoked, - * as each #PsppSheetView will create its own #PsppSheetSelection. - * - * Return value: A newly created #PsppSheetSelection object. - **/ -PsppSheetSelection* -_pspp_sheet_selection_new (void) -{ - PsppSheetSelection *selection; - - selection = g_object_new (PSPP_TYPE_SHEET_SELECTION, NULL); - - return selection; -} - -/** - * _pspp_sheet_selection_new_with_tree_view: - * @tree_view: The #PsppSheetView. - * - * Creates a new #PsppSheetSelection object. This function should not be invoked, - * as each #PsppSheetView will create its own #PsppSheetSelection. - * - * Return value: A newly created #PsppSheetSelection object. - **/ -PsppSheetSelection* -_pspp_sheet_selection_new_with_tree_view (PsppSheetView *tree_view) -{ - PsppSheetSelection *selection; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - selection = _pspp_sheet_selection_new (); - _pspp_sheet_selection_set_tree_view (selection, tree_view); - - return selection; -} - -/** - * _pspp_sheet_selection_set_tree_view: - * @selection: A #PsppSheetSelection. - * @tree_view: The #PsppSheetView. - * - * Sets the #PsppSheetView of @selection. This function should not be invoked, as - * it is used internally by #PsppSheetView. - **/ -void -_pspp_sheet_selection_set_tree_view (PsppSheetSelection *selection, - PsppSheetView *tree_view) -{ - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - if (tree_view != NULL) - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - selection->tree_view = tree_view; -} - -/** - * pspp_sheet_selection_set_mode: - * @selection: A #PsppSheetSelection. - * @type: The selection mode - * - * Sets the selection mode of the @selection. If the previous type was - * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, then the - * anchor is kept selected, if it was previously selected. - **/ -void -pspp_sheet_selection_set_mode (PsppSheetSelection *selection, - PsppSheetSelectionMode type) -{ - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - - if (selection->type == type) - return; - - if (type == PSPP_SHEET_SELECTION_NONE) - { - pspp_sheet_selection_unselect_all (selection); - - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - selection->tree_view->priv->anchor = NULL; - } - else if (type == PSPP_SHEET_SELECTION_SINGLE || - type == PSPP_SHEET_SELECTION_BROWSE) - { - int node = -1; - gint selected = FALSE; - GtkTreePath *anchor_path = NULL; - - if (selection->tree_view->priv->anchor) - { - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); - - if (anchor_path) - { - _pspp_sheet_view_find_node (selection->tree_view, - anchor_path, - &node); - - if (node >= 0 && pspp_sheet_view_node_is_selected (selection->tree_view, node)) - selected = TRUE; - } - } - - /* We do this so that we unconditionally unset all rows - */ - pspp_sheet_selection_unselect_all (selection); - - if (node >= 0 && selected) - _pspp_sheet_selection_internal_select_node (selection, - node, - anchor_path, - 0, - FALSE); - if (anchor_path) - gtk_tree_path_free (anchor_path); - } - - /* XXX unselect all columns when switching to/from rectangular selection? */ - - selection->type = type; -} - -/** - * pspp_sheet_selection_get_mode: - * @selection: a #PsppSheetSelection - * - * Gets the selection mode for @selection. See - * pspp_sheet_selection_set_mode(). - * - * Return value: the current selection mode - **/ -PsppSheetSelectionMode -pspp_sheet_selection_get_mode (PsppSheetSelection *selection) -{ - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), PSPP_SHEET_SELECTION_SINGLE); - - return selection->type; -} - -/** - * pspp_sheet_selection_get_tree_view: - * @selection: A #PsppSheetSelection - * - * Returns the tree view associated with @selection. - * - * Return value: A #PsppSheetView - **/ -PsppSheetView * -pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection) -{ - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL); - - return selection->tree_view; -} - -/** - * pspp_sheet_selection_get_selected: - * @selection: A #PsppSheetSelection. - * @model: (out) (allow-none): A pointer to set to the #GtkTreeModel, or NULL. - * @iter: (allow-none): The #GtkTreeIter, or NULL. - * - * Sets @iter to the currently selected node if @selection is set to - * #PSPP_SHEET_SELECTION_SINGLE or #PSPP_SHEET_SELECTION_BROWSE. @iter may be - * NULL if you just want to test if @selection has any selected nodes. @model - * is filled with the current model as a convenience. This function will not - * work if @selection's mode is #PSPP_SHEET_SELECTION_MULTIPLE or - * #PSPP_SHEET_SELECTION_RECTANGLE. - * - * Return value: TRUE, if there is a selected node. - **/ -gboolean -pspp_sheet_selection_get_selected (PsppSheetSelection *selection, - GtkTreeModel **model, - GtkTreeIter *iter) -{ - int node; - GtkTreePath *anchor_path; - gboolean retval; - - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE); - g_return_val_if_fail (selection->type != PSPP_SHEET_SELECTION_MULTIPLE && - selection->type != PSPP_SHEET_SELECTION_RECTANGLE, - FALSE); - g_return_val_if_fail (selection->tree_view != NULL, FALSE); - - /* Clear the iter */ - if (iter) - memset (iter, 0, sizeof (GtkTreeIter)); - - if (model) - *model = selection->tree_view->priv->model; - - if (selection->tree_view->priv->anchor == NULL) - return FALSE; - - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); - - if (anchor_path == NULL) - return FALSE; - - retval = FALSE; - - _pspp_sheet_view_find_node (selection->tree_view, - anchor_path, - &node); - - if (pspp_sheet_view_node_is_selected (selection->tree_view, node)) - { - /* we only want to return the anchor if it exists in the rbtree and - * is selected. - */ - if (iter == NULL) - retval = TRUE; - else - retval = gtk_tree_model_get_iter (selection->tree_view->priv->model, - iter, - anchor_path); - } - else - { - /* We don't want to return the anchor if it isn't actually selected. - */ - retval = FALSE; - } - - gtk_tree_path_free (anchor_path); - - return retval; -} - -/** - * pspp_sheet_selection_get_selected_rows: - * @selection: A #PsppSheetSelection. - * @model: (allow-none): A pointer to set to the #GtkTreeModel, or NULL. - * - * Creates a list of path of all selected rows. Additionally, if you are - * planning on modifying the model after calling this function, you may - * want to convert the returned list into a list of #GtkTreeRowReferences. - * To do this, you can use gtk_tree_row_reference_new(). - * - * To free the return value, use: - * |[ - * g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); - * g_list_free (list); - * ]| - * - * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row. - * - * Since: 2.2 - **/ -GList * -pspp_sheet_selection_get_selected_rows (PsppSheetSelection *selection, - GtkTreeModel **model) -{ - const struct range_tower_node *node; - unsigned long int start; - GList *list = NULL; - - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL); - g_return_val_if_fail (selection->tree_view != NULL, NULL); - - if (model) - *model = selection->tree_view->priv->model; - - if (selection->tree_view->priv->row_count == 0) - return NULL; - - if (selection->type == PSPP_SHEET_SELECTION_NONE) - return NULL; - else if (selection->type != PSPP_SHEET_SELECTION_MULTIPLE && - selection->type != PSPP_SHEET_SELECTION_RECTANGLE) - { - GtkTreeIter iter; - - if (pspp_sheet_selection_get_selected (selection, NULL, &iter)) - { - GtkTreePath *path; - - path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter); - list = g_list_append (list, path); - - return list; - } - - return NULL; - } - - RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected) - { - unsigned long int width = range_tower_node_get_width (node); - unsigned long int index; - - for (index = start; index < start + width; index++) - list = g_list_prepend (list, gtk_tree_path_new_from_indices (index, -1)); - } - - return g_list_reverse (list); -} - -/** - * pspp_sheet_selection_count_selected_rows: - * @selection: A #PsppSheetSelection. - * - * Returns the number of rows that have been selected in @tree. - * - * Return value: The number of rows selected. - * - * Since: 2.2 - **/ -gint -pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection) -{ - const struct range_tower_node *node; - unsigned long int start; - gint count = 0; - - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), 0); - g_return_val_if_fail (selection->tree_view != NULL, 0); - - if (selection->tree_view->priv->row_count == 0) - return 0; - - if (selection->type == PSPP_SHEET_SELECTION_SINGLE || - selection->type == PSPP_SHEET_SELECTION_BROWSE) - { - if (pspp_sheet_selection_get_selected (selection, NULL, NULL)) - return 1; - else - return 0; - } - - count = 0; - RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected) - count += range_tower_node_get_width (node); - - return count; -} - -/* pspp_sheet_selection_selected_foreach helper */ -static void -model_changed (gpointer data) -{ - gboolean *stop = (gboolean *)data; - - *stop = TRUE; -} - -/** - * pspp_sheet_selection_selected_foreach: - * @selection: A #PsppSheetSelection. - * @func: The function to call for each selected node. - * @data: user data to pass to the function. - * - * Calls a function for each selected node. Note that you cannot modify - * the tree or selection from within this function. As a result, - * pspp_sheet_selection_get_selected_rows() might be more useful. - **/ -void -pspp_sheet_selection_selected_foreach (PsppSheetSelection *selection, - PsppSheetSelectionForeachFunc func, - gpointer data) -{ - const struct range_tower_node *node; - unsigned long int start; - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeModel *model; - - gulong inserted_id, deleted_id, reordered_id, changed_id; - gboolean stop = FALSE; - - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - if (func == NULL || - selection->tree_view->priv->row_count == 0) - return; - - if (selection->type == PSPP_SHEET_SELECTION_SINGLE || - selection->type == PSPP_SHEET_SELECTION_BROWSE) - { - if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor)) - { - path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); - gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path); - (* func) (selection->tree_view->priv->model, path, &iter, data); - gtk_tree_path_free (path); - } - return; - } - - model = selection->tree_view->priv->model; - g_object_ref (model); - - /* connect to signals to monitor changes in treemodel */ - inserted_id = g_signal_connect_swapped (model, "row-inserted", - G_CALLBACK (model_changed), - &stop); - deleted_id = g_signal_connect_swapped (model, "row-deleted", - G_CALLBACK (model_changed), - &stop); - reordered_id = g_signal_connect_swapped (model, "rows-reordered", - G_CALLBACK (model_changed), - &stop); - changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model", - G_CALLBACK (model_changed), - &stop); - - RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected) - { - unsigned long int width = range_tower_node_get_width (node); - unsigned long int index; - - for (index = start; index < start + width; index++) - { - GtkTreePath *path; - GtkTreeIter iter; - - path = gtk_tree_path_new_from_indices (index, -1); - gtk_tree_model_get_iter (model, &iter, path); - (* func) (model, path, &iter, data); - gtk_tree_path_free (path); - } - } - - g_signal_handler_disconnect (model, inserted_id); - g_signal_handler_disconnect (model, deleted_id); - g_signal_handler_disconnect (model, reordered_id); - g_signal_handler_disconnect (selection->tree_view, changed_id); - g_object_unref (model); - - /* check if we have to spew a scary message */ - if (stop) - g_warning ("The model has been modified from within pspp_sheet_selection_selected_foreach.\n" - "This function is for observing the selections of the tree only. If\n" - "you are trying to get all selected items from the tree, try using\n" - "pspp_sheet_selection_get_selected_rows instead.\n"); -} - -/** - * pspp_sheet_selection_select_path: - * @selection: A #PsppSheetSelection. - * @path: The #GtkTreePath to be selected. - * - * Select the row at @path. - **/ -void -pspp_sheet_selection_select_path (PsppSheetSelection *selection, - GtkTreePath *path) -{ - int node; - PsppSheetSelectMode mode = 0; - - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (path != NULL); - - _pspp_sheet_view_find_node (selection->tree_view, - path, - &node); - - if (node < 0 || pspp_sheet_view_node_is_selected (selection->tree_view, node)) - return; - - if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE || - selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - mode = PSPP_SHEET_SELECT_MODE_TOGGLE; - - _pspp_sheet_selection_internal_select_node (selection, - node, - path, - mode, - FALSE); -} - -/** - * pspp_sheet_selection_unselect_path: - * @selection: A #PsppSheetSelection. - * @path: The #GtkTreePath to be unselected. - * - * Unselects the row at @path. - **/ -void -pspp_sheet_selection_unselect_path (PsppSheetSelection *selection, - GtkTreePath *path) -{ - int node; - - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (path != NULL); - - _pspp_sheet_view_find_node (selection->tree_view, - path, - &node); - - if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) - return; - - _pspp_sheet_selection_internal_select_node (selection, - node, - path, - PSPP_SHEET_SELECT_MODE_TOGGLE, - TRUE); -} - -/** - * pspp_sheet_selection_select_iter: - * @selection: A #PsppSheetSelection. - * @iter: The #GtkTreeIter to be selected. - * - * Selects the specified iterator. - **/ -void -pspp_sheet_selection_select_iter (PsppSheetSelection *selection, - GtkTreeIter *iter) -{ - GtkTreePath *path; - - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->tree_view->priv->model != NULL); - g_return_if_fail (iter != NULL); - - path = gtk_tree_model_get_path (selection->tree_view->priv->model, - iter); - - if (path == NULL) - return; - - pspp_sheet_selection_select_path (selection, path); - gtk_tree_path_free (path); -} - - -/** - * pspp_sheet_selection_unselect_iter: - * @selection: A #PsppSheetSelection. - * @iter: The #GtkTreeIter to be unselected. - * - * Unselects the specified iterator. - **/ -void -pspp_sheet_selection_unselect_iter (PsppSheetSelection *selection, - GtkTreeIter *iter) -{ - GtkTreePath *path; - - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->tree_view->priv->model != NULL); - g_return_if_fail (iter != NULL); - - path = gtk_tree_model_get_path (selection->tree_view->priv->model, - iter); - - if (path == NULL) - return; - - pspp_sheet_selection_unselect_path (selection, path); - gtk_tree_path_free (path); -} - -/** - * pspp_sheet_selection_path_is_selected: - * @selection: A #PsppSheetSelection. - * @path: A #GtkTreePath to check selection on. - * - * Returns %TRUE if the row pointed to by @path is currently selected. If @path - * does not point to a valid location, %FALSE is returned - * - * Return value: %TRUE if @path is selected. - **/ -gboolean -pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection, - GtkTreePath *path) -{ - int node; - - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (selection->tree_view != NULL, FALSE); - - if (selection->tree_view->priv->model == NULL) - return FALSE; - - _pspp_sheet_view_find_node (selection->tree_view, - path, - &node); - - if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) - return FALSE; - - return TRUE; -} - -/** - * pspp_sheet_selection_iter_is_selected: - * @selection: A #PsppSheetSelection - * @iter: A valid #GtkTreeIter - * - * Returns %TRUE if the row at @iter is currently selected. - * - * Return value: %TRUE, if @iter is selected - **/ -gboolean -pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection, - GtkTreeIter *iter) -{ - GtkTreePath *path; - gboolean retval; - - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (selection->tree_view != NULL, FALSE); - g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE); - - path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter); - if (path == NULL) - return FALSE; - - retval = pspp_sheet_selection_path_is_selected (selection, path); - gtk_tree_path_free (path); - - return retval; -} - - -/* We have a real_{un,}select_all function that doesn't emit the signal, so we - * can use it in other places without fear of the signal being emitted. - */ -static gint -pspp_sheet_selection_real_select_all (PsppSheetSelection *selection) -{ - const struct range_tower_node *node; - int row_count = selection->tree_view->priv->row_count; - - if (row_count == 0) - return FALSE; - - node = range_tower_first (selection->tree_view->priv->selected); - if (node - && range_tower_node_get_start (node) == 0 - && range_tower_node_get_width (node) >= row_count) - return FALSE; - - range_tower_set1 (selection->tree_view->priv->selected, 0, row_count); - pspp_sheet_selection_select_all_columns (selection); - - /* XXX we could invalidate individual visible rows instead */ - gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE); - - return TRUE; -} - -/** - * pspp_sheet_selection_select_all: - * @selection: A #PsppSheetSelection. - * - * Selects all the nodes and column. @selection must be set to - * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE mode. - **/ -void -pspp_sheet_selection_select_all (PsppSheetSelection *selection) -{ - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL) - return; - - g_return_if_fail (selection->type == PSPP_SHEET_SELECTION_MULTIPLE || - selection->type == PSPP_SHEET_SELECTION_RECTANGLE); - - if (pspp_sheet_selection_real_select_all (selection)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -static gint -pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection) -{ - if (selection->type == PSPP_SHEET_SELECTION_SINGLE || - selection->type == PSPP_SHEET_SELECTION_BROWSE) - { - int node = -1; - GtkTreePath *anchor_path; - - if (selection->tree_view->priv->anchor == NULL) - return FALSE; - - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); - - if (anchor_path == NULL) - return FALSE; - - _pspp_sheet_view_find_node (selection->tree_view, - anchor_path, - &node); - - gtk_tree_path_free (anchor_path); - - if (node < 0) - return FALSE; - - if (pspp_sheet_view_node_is_selected (selection->tree_view, node)) - { - if (pspp_sheet_selection_real_select_node (selection, node, FALSE)) - { - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - selection->tree_view->priv->anchor = NULL; - return TRUE; - } - } - return FALSE; - } - else if (range_tower_is_empty (selection->tree_view->priv->selected)) - return FALSE; - else - { - range_tower_set0 (selection->tree_view->priv->selected, 0, ULONG_MAX); - pspp_sheet_selection_unselect_all_columns (selection); - - /* XXX we could invalidate individual visible rows instead */ - gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE); - - return TRUE; - } -} - -/** - * pspp_sheet_selection_unselect_all: - * @selection: A #PsppSheetSelection. - * - * Unselects all the nodes and columns. - **/ -void -pspp_sheet_selection_unselect_all (PsppSheetSelection *selection) -{ - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - - if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL) - return; - - if (pspp_sheet_selection_real_unselect_all (selection)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -enum -{ - RANGE_SELECT, - RANGE_UNSELECT -}; - -static gint -pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection, - gint mode, - GtkTreePath *start_path, - GtkTreePath *end_path) -{ - int start_node, end_node; - GtkTreePath *anchor_path = NULL; - gboolean dirty = FALSE; - - switch (gtk_tree_path_compare (start_path, end_path)) - { - case 1: - _pspp_sheet_view_find_node (selection->tree_view, - end_path, - &start_node); - _pspp_sheet_view_find_node (selection->tree_view, - start_path, - &end_node); - anchor_path = start_path; - break; - case 0: - _pspp_sheet_view_find_node (selection->tree_view, - start_path, - &start_node); - end_node = start_node; - anchor_path = start_path; - break; - case -1: - _pspp_sheet_view_find_node (selection->tree_view, - start_path, - &start_node); - _pspp_sheet_view_find_node (selection->tree_view, - end_path, - &end_node); - anchor_path = start_path; - break; - } - - g_return_val_if_fail (start_node >= 0, FALSE); - g_return_val_if_fail (end_node >= 0, FALSE); - - if (anchor_path) - { - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), - selection->tree_view->priv->model, - anchor_path); - } - - do - { - dirty |= pspp_sheet_selection_real_select_node (selection, start_node, (mode == RANGE_SELECT)?TRUE:FALSE); - - if (start_node == end_node) - break; - - start_node = pspp_sheet_view_node_next (selection->tree_view, start_node); - if (start_node < 0) - { - /* we just ran out of tree. That means someone passed in bogus values. - */ - return dirty; - } - } - while (TRUE); - - return dirty; -} - -/** - * pspp_sheet_selection_select_range: - * @selection: A #PsppSheetSelection. - * @start_path: The initial node of the range. - * @end_path: The final node of the range. - * - * Selects a range of nodes, determined by @start_path and @end_path inclusive. - * @selection must be set to #PSPP_SHEET_SELECTION_MULTIPLE or - * #PSPP_SHEET_SELECTION_RECTANGLE mode. - **/ -void -pspp_sheet_selection_select_range (PsppSheetSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path) -{ - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->type == PSPP_SHEET_SELECTION_MULTIPLE || - selection->type == PSPP_SHEET_SELECTION_RECTANGLE); - g_return_if_fail (selection->tree_view->priv->model != NULL); - - if (pspp_sheet_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -/** - * pspp_sheet_selection_unselect_range: - * @selection: A #PsppSheetSelection. - * @start_path: The initial node of the range. - * @end_path: The initial node of the range. - * - * Unselects a range of nodes, determined by @start_path and @end_path - * inclusive. - * - * Since: 2.2 - **/ -void -pspp_sheet_selection_unselect_range (PsppSheetSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path) -{ - g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->tree_view->priv->model != NULL); - - if (pspp_sheet_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path)) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -struct range_set * -pspp_sheet_selection_get_range_set (PsppSheetSelection *selection) -{ - const struct range_tower_node *node; - unsigned long int start; - struct range_set *set; - - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), - range_set_create ()); - g_return_val_if_fail (selection->tree_view != NULL, range_set_create ()); - - set = range_set_create (); - RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected) - range_set_set1 (set, start, range_tower_node_get_width (node)); - return set; -} - -/* Called internally by gtktreeview.c It handles actually selecting the tree. - */ - -/* - * docs about the 'override_browse_mode', we set this flag when we want to - * unset select the node and override the select browse mode behaviour (that is - * 'one node should *always* be selected'). - */ -void -_pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection, - int node, - GtkTreePath *path, - PsppSheetSelectMode mode, - gboolean override_browse_mode) -{ - gint dirty = FALSE; - GtkTreePath *anchor_path = NULL; - - if (selection->type == PSPP_SHEET_SELECTION_NONE) - return; - - if (selection->tree_view->priv->anchor) - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); - - if (selection->type == PSPP_SHEET_SELECTION_SINGLE || - selection->type == PSPP_SHEET_SELECTION_BROWSE) - { - /* just unselect */ - if (selection->type == PSPP_SHEET_SELECTION_BROWSE && override_browse_mode) - { - dirty = pspp_sheet_selection_real_unselect_all (selection); - } - /* Did we try to select the same node again? */ - else if (selection->type == PSPP_SHEET_SELECTION_SINGLE && - anchor_path && gtk_tree_path_compare (path, anchor_path) == 0) - { - if ((mode & PSPP_SHEET_SELECT_MODE_TOGGLE) == PSPP_SHEET_SELECT_MODE_TOGGLE) - { - dirty = pspp_sheet_selection_real_unselect_all (selection); - } - } - else - { - if (anchor_path) - { - dirty = pspp_sheet_selection_real_unselect_all (selection); - - /* if dirty is TRUE at this point, we successfully unselected the - * old one, and can then select the new one */ - if (dirty) - { - if (selection->tree_view->priv->anchor) - { - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - selection->tree_view->priv->anchor = NULL; - } - - if (pspp_sheet_selection_real_select_node (selection, node, TRUE)) - { - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); - } - } - } - else - { - if (pspp_sheet_selection_real_select_node (selection, node, TRUE)) - { - dirty = TRUE; - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); - } - } - } - } - else if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE || - selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - { - if ((mode & PSPP_SHEET_SELECT_MODE_EXTEND) == PSPP_SHEET_SELECT_MODE_EXTEND - && (anchor_path == NULL)) - { - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); - dirty = pspp_sheet_selection_real_select_node (selection, node, TRUE); - } - else if ((mode & (PSPP_SHEET_SELECT_MODE_EXTEND | PSPP_SHEET_SELECT_MODE_TOGGLE)) == (PSPP_SHEET_SELECT_MODE_EXTEND | PSPP_SHEET_SELECT_MODE_TOGGLE)) - { - pspp_sheet_selection_select_range (selection, - anchor_path, - path); - } - else if ((mode & PSPP_SHEET_SELECT_MODE_TOGGLE) == PSPP_SHEET_SELECT_MODE_TOGGLE) - { - bool selected = pspp_sheet_view_node_is_selected (selection->tree_view, node); - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); - - if (selected) - dirty |= pspp_sheet_selection_real_select_node (selection, node, FALSE); - else - dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE); - } - else if ((mode & PSPP_SHEET_SELECT_MODE_EXTEND) == PSPP_SHEET_SELECT_MODE_EXTEND) - { - dirty = pspp_sheet_selection_real_unselect_all (selection); - dirty |= pspp_sheet_selection_real_modify_range (selection, - RANGE_SELECT, - anchor_path, - path); - } - else - { - dirty = pspp_sheet_selection_real_unselect_all (selection); - - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); - - dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE); - } - } - - if (anchor_path) - gtk_tree_path_free (anchor_path); - - if (dirty) - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - - -void -_pspp_sheet_selection_emit_changed (PsppSheetSelection *selection) -{ - g_signal_emit (selection, tree_selection_signals[CHANGED], 0); -} - -/* NOTE: Any {un,}selection ever done _MUST_ be done through this function! - */ - -static gint -pspp_sheet_selection_real_select_node (PsppSheetSelection *selection, - int node, - gboolean select) -{ - select = !! select; - if (pspp_sheet_view_node_is_selected (selection->tree_view, node) != select) - { - if (select) - pspp_sheet_view_node_select (selection->tree_view, node); - else - pspp_sheet_view_node_unselect (selection->tree_view, node); - - _pspp_sheet_view_queue_draw_node (selection->tree_view, node, NULL); - - return TRUE; - } - - return FALSE; -} - -void -pspp_sheet_selection_unselect_all_columns (PsppSheetSelection *selection) -{ - PsppSheetView *sheet_view = selection->tree_view; - gboolean changed; - GList *list; - - changed = FALSE; - for (list = sheet_view->priv->columns; list; list = list->next) - { - PsppSheetViewColumn *column = list->data; - if (column->selected) - { - column->selected = FALSE; - changed = TRUE; - } - } - if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - { - gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); - _pspp_sheet_selection_emit_changed (selection); - } -} - -GList * -pspp_sheet_selection_get_selected_columns (PsppSheetSelection *selection) -{ - PsppSheetView *sheet_view = selection->tree_view; - GList *selected_columns = NULL; - GList *iter; - - g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL); - g_return_val_if_fail (selection->tree_view != NULL, NULL); - - if (selection->type != PSPP_SHEET_SELECTION_RECTANGLE) - return NULL; - - for (iter = sheet_view->priv->columns; iter; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - if (column->selected) - selected_columns = g_list_prepend (selected_columns, column); - } - return g_list_reverse (selected_columns); -} - -gint -pspp_sheet_selection_count_selected_columns (PsppSheetSelection *selection) -{ - PsppSheetView *sheet_view = selection->tree_view; - GList *list; - gint n; - - n = 0; - for (list = sheet_view->priv->columns; list; list = list->next) - { - PsppSheetViewColumn *column = list->data; - if (column->selected) - n++; - } - return n; -} - -void -pspp_sheet_selection_select_all_columns (PsppSheetSelection *selection) -{ - PsppSheetView *sheet_view = selection->tree_view; - gboolean changed; - GList *list; - - changed = FALSE; - for (list = sheet_view->priv->columns; list; list = list->next) - { - PsppSheetViewColumn *column = list->data; - if (!column->selected && column->selectable) - { - /* XXX should use pspp_sheet_view_column_set_selected() here (and - elsewhere) but we want to call - _pspp_sheet_selection_emit_changed() only once for all the - columns. */ - column->selected = TRUE; - changed = TRUE; - } - } - if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - { - _pspp_sheet_selection_emit_changed (selection); - gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); - } -} - -void -pspp_sheet_selection_select_column (PsppSheetSelection *selection, - PsppSheetViewColumn *column) -{ - if (!column->selected && column->selectable) - { - column->selected = TRUE; - if (selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - { - _pspp_sheet_selection_emit_changed (selection); - gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); - } - } -} - -void -pspp_sheet_selection_select_column_range (PsppSheetSelection *selection, - PsppSheetViewColumn *first, - PsppSheetViewColumn *last) -{ - PsppSheetView *sheet_view = selection->tree_view; - gboolean in_range; - gboolean changed; - GList *list; - - in_range = FALSE; - changed = FALSE; - for (list = sheet_view->priv->columns; list; list = list->next) - { - PsppSheetViewColumn *column = list->data; - gboolean c0 = column == first; - gboolean c1 = column == last; - - if (in_range || c0 || c1) - { - if (!column->selected && column->selectable) - { - column->selected = TRUE; - changed = TRUE; - } - } - - in_range = in_range ^ c0 ^ c1; - } - if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - { - _pspp_sheet_selection_emit_changed (selection); - gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); - } -} diff --git a/src/ui/gui/pspp-sheet-selection.h b/src/ui/gui/pspp-sheet-selection.h deleted file mode 100644 index ec96dd117b..0000000000 --- a/src/ui/gui/pspp-sheet-selection.h +++ /dev/null @@ -1,159 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -/* gtktreeselection.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __PSPP_SHEET_SELECTION_H__ -#define __PSPP_SHEET_SELECTION_H__ - -#include - -G_BEGIN_DECLS - - -#define PSPP_TYPE_SHEET_SELECTION (pspp_sheet_selection_get_type ()) -#define PSPP_SHEET_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPP_TYPE_SHEET_SELECTION, PsppSheetSelection)) -#define PSPP_SHEET_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PSPP_TYPE_SHEET_SELECTION, PsppSheetSelectionClass)) -#define PSPP_IS_SHEET_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPP_TYPE_SHEET_SELECTION)) -#define PSPP_IS_SHEET_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPP_TYPE_SHEET_SELECTION)) -#define PSPP_SHEET_SELECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PSPP_TYPE_SHEET_SELECTION, PsppSheetSelectionClass)) - -typedef enum - { - /* Same as GtkSelectionMode. */ - PSPP_SHEET_SELECTION_NONE = GTK_SELECTION_NONE, - PSPP_SHEET_SELECTION_SINGLE = GTK_SELECTION_SINGLE, - PSPP_SHEET_SELECTION_BROWSE = GTK_SELECTION_BROWSE, - PSPP_SHEET_SELECTION_MULTIPLE = GTK_SELECTION_MULTIPLE, - - /* PsppSheetView extension. */ - PSPP_SHEET_SELECTION_RECTANGLE = 10 - } -PsppSheetSelectionMode; - -typedef gboolean (* PsppSheetSelectionFunc) (PsppSheetSelection *selection, - GtkTreeModel *model, - GtkTreePath *path, - gboolean path_currently_selected, - gpointer data); -typedef void (* PsppSheetSelectionForeachFunc) (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); - -struct _PsppSheetSelection -{ - GObject parent; - - /*< private >*/ - - PsppSheetView *tree_view; - PsppSheetSelectionMode type; -}; - -struct _PsppSheetSelectionClass -{ - GObjectClass parent_class; - - void (* changed) (PsppSheetSelection *selection); - - /* Padding for future expansion */ - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); -}; - - -GType pspp_sheet_selection_get_type (void) G_GNUC_CONST; - -void pspp_sheet_selection_set_mode (PsppSheetSelection *selection, - PsppSheetSelectionMode type); -PsppSheetSelectionMode pspp_sheet_selection_get_mode (PsppSheetSelection *selection); -void pspp_sheet_selection_set_select_function (PsppSheetSelection *selection, - PsppSheetSelectionFunc func, - gpointer data, - GDestroyNotify destroy); -gpointer pspp_sheet_selection_get_user_data (PsppSheetSelection *selection); -PsppSheetView* pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection); - -PsppSheetSelectionFunc pspp_sheet_selection_get_select_function (PsppSheetSelection *selection); - -/* Only meaningful if PSPP_SHEET_SELECTION_SINGLE or PSPP_SHEET_SELECTION_BROWSE is set */ -/* Use selected_foreach or get_selected_rows for - PSPP_SHEET_SELECTION_MULTIPLE */ -gboolean pspp_sheet_selection_get_selected (PsppSheetSelection *selection, - GtkTreeModel **model, - GtkTreeIter *iter); -GList * pspp_sheet_selection_get_selected_rows (PsppSheetSelection *selection, - GtkTreeModel **model); -gint pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection); -void pspp_sheet_selection_selected_foreach (PsppSheetSelection *selection, - PsppSheetSelectionForeachFunc func, - gpointer data); -void pspp_sheet_selection_select_path (PsppSheetSelection *selection, - GtkTreePath *path); -void pspp_sheet_selection_unselect_path (PsppSheetSelection *selection, - GtkTreePath *path); -void pspp_sheet_selection_select_iter (PsppSheetSelection *selection, - GtkTreeIter *iter); -void pspp_sheet_selection_unselect_iter (PsppSheetSelection *selection, - GtkTreeIter *iter); -gboolean pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection, - GtkTreePath *path); -gboolean pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection, - GtkTreeIter *iter); -void pspp_sheet_selection_select_all (PsppSheetSelection *selection); -void pspp_sheet_selection_unselect_all (PsppSheetSelection *selection); -void pspp_sheet_selection_select_range (PsppSheetSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path); -void pspp_sheet_selection_unselect_range (PsppSheetSelection *selection, - GtkTreePath *start_path, - GtkTreePath *end_path); -struct range_set *pspp_sheet_selection_get_range_set (PsppSheetSelection *selection); - - -GList * pspp_sheet_selection_get_selected_columns (PsppSheetSelection *selection); -gint pspp_sheet_selection_count_selected_columns (PsppSheetSelection *selection); -void pspp_sheet_selection_select_all_columns (PsppSheetSelection *selection); -void pspp_sheet_selection_unselect_all_columns (PsppSheetSelection *selection); -void pspp_sheet_selection_select_column (PsppSheetSelection *selection, - PsppSheetViewColumn *column); -void pspp_sheet_selection_select_column_range (PsppSheetSelection *selection, - PsppSheetViewColumn *first, - PsppSheetViewColumn *last); - -G_END_DECLS - -#endif /* __PSPP_SHEET_SELECTION_H__ */ diff --git a/src/ui/gui/pspp-sheet-view-column.c b/src/ui/gui/pspp-sheet-view-column.c deleted file mode 100644 index cd3fae3c6e..0000000000 --- a/src/ui/gui/pspp-sheet-view-column.c +++ /dev/null @@ -1,4345 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2015 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -/* gtktreeviewcolumn.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -#include "ui/gui/pspp-sheet-private.h" - -#include -#include -#include -#include - -#include "ui/gui/psppire-marshal.h" -#include "ui/gui/pspp-sheet-selection.h" -#include "ui/gui/pspp-widget-facade.h" - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -#define P_(STRING) STRING -#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB -#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB - -enum -{ - PROP_0, - PROP_VISIBLE, - PROP_RESIZABLE, - PROP_WIDTH, - PROP_SPACING, - PROP_FIXED_WIDTH, - PROP_MIN_WIDTH, - PROP_MAX_WIDTH, - PROP_TITLE, - PROP_EXPAND, - PROP_CLICKABLE, - PROP_WIDGET, - PROP_ALIGNMENT, - PROP_REORDERABLE, - PROP_SORT_INDICATOR, - PROP_SORT_ORDER, - PROP_SORT_COLUMN_ID, - PROP_QUICK_EDIT, - PROP_SELECTED, - PROP_SELECTABLE, - PROP_ROW_HEAD, - PROP_TABBABLE -}; - -enum -{ - CLICKED, - QUERY_TOOLTIP, - POPUP_MENU, - BUTTON_PRESS_EVENT, - LAST_SIGNAL -}; - -typedef struct _PsppSheetViewColumnCellInfo PsppSheetViewColumnCellInfo; -struct _PsppSheetViewColumnCellInfo -{ - GtkCellRenderer *cell; - GSList *attributes; - PsppSheetCellDataFunc func; - gpointer func_data; - GDestroyNotify destroy; - gint requested_width; - gint real_width; - guint expand : 1; - guint pack : 1; - guint has_focus : 1; - guint in_editing_mode : 1; -}; - -/* Type methods */ -static void pspp_sheet_view_column_cell_layout_init (GtkCellLayoutIface *iface); - -/* GObject methods */ -static void pspp_sheet_view_column_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void pspp_sheet_view_column_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void pspp_sheet_view_column_finalize (GObject *object); - -/* GtkCellLayout implementation */ -static void pspp_sheet_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -static void pspp_sheet_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -static void pspp_sheet_view_column_cell_layout_clear (GtkCellLayout *cell_layout); -static void pspp_sheet_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const gchar *attribute, - gint column); -static void pspp_sheet_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy); -static void pspp_sheet_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell); -static void pspp_sheet_view_column_cell_layout_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gint position); -static GList *pspp_sheet_view_column_cell_layout_get_cells (GtkCellLayout *cell_layout); - -/* Button handling code */ -static void pspp_sheet_view_column_create_button (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column); - -/* Button signal handlers */ -static gint pspp_sheet_view_column_button_event (GtkWidget *widget, - GdkEventButton *event, - gpointer data); -static void pspp_sheet_view_column_button_clicked (GtkWidget *widget, - gpointer data); -static void pspp_sheet_view_column_button_popup_menu (GtkWidget *widget, - gpointer data); -static gboolean pspp_sheet_view_column_mnemonic_activate (GtkWidget *widget, - gboolean group_cycling, - gpointer data); -static gboolean on_pspp_sheet_view_column_button_clicked (PsppSheetViewColumn *); -static gboolean on_pspp_sheet_view_column_button_press_event (PsppSheetViewColumn *, - GdkEventButton *); - -/* Property handlers */ -static void pspp_sheet_view_model_sort_column_changed (GtkTreeSortable *sortable, - PsppSheetViewColumn *tree_column); - -/* Internal functions */ -static void pspp_sheet_view_column_sort (PsppSheetViewColumn *tree_column, - gpointer data); -static void pspp_sheet_view_column_setup_sort_column_id_callback (PsppSheetViewColumn *tree_column); -static void pspp_sheet_view_column_set_attributesv (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - va_list args); -static PsppSheetViewColumnCellInfo *pspp_sheet_view_column_get_cell_info (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer); - -/* cell list manipulation */ -static GList *pspp_sheet_view_column_cell_first (PsppSheetViewColumn *tree_column); -static GList *pspp_sheet_view_column_cell_last (PsppSheetViewColumn *tree_column); -static GList *pspp_sheet_view_column_cell_next (PsppSheetViewColumn *tree_column, - GList *current); -static GList *pspp_sheet_view_column_cell_prev (PsppSheetViewColumn *tree_column, - GList *current); -static void pspp_sheet_view_column_clear_attributes_by_info (PsppSheetViewColumn *tree_column, - PsppSheetViewColumnCellInfo *info); -/* GtkBuildable implementation */ -static void pspp_sheet_view_column_buildable_init (GtkBuildableIface *iface); - -static guint tree_column_signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE_WITH_CODE (PsppSheetViewColumn, pspp_sheet_view_column, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - pspp_sheet_view_column_cell_layout_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - pspp_sheet_view_column_buildable_init)) - - -static void -pspp_sheet_view_column_class_init (PsppSheetViewColumnClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass*) class; - - class->clicked = on_pspp_sheet_view_column_button_clicked; - class->button_press_event = on_pspp_sheet_view_column_button_press_event; - - object_class->finalize = pspp_sheet_view_column_finalize; - object_class->set_property = pspp_sheet_view_column_set_property; - object_class->get_property = pspp_sheet_view_column_get_property; - - tree_column_signals[CLICKED] = - g_signal_new ("clicked", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PsppSheetViewColumnClass, clicked), - g_signal_accumulator_true_handled, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - tree_column_signals[POPUP_MENU] = - g_signal_new ("popup-menu", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - tree_column_signals[QUERY_TOOLTIP] = - g_signal_new ("query-tooltip", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - g_signal_accumulator_true_handled, NULL, - psppire_marshal_BOOLEAN__OBJECT, - G_TYPE_BOOLEAN, 1, - GTK_TYPE_TOOLTIP); - - tree_column_signals[BUTTON_PRESS_EVENT] = - g_signal_new ("button-press-event", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PsppSheetViewColumnClass, button_press_event), - g_signal_accumulator_true_handled, NULL, - psppire_marshal_BOOLEAN__BOXED, - G_TYPE_BOOLEAN, 1, - GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); - - g_object_class_install_property (object_class, - PROP_VISIBLE, - g_param_spec_boolean ("visible", - P_("Visible"), - P_("Whether to display the column"), - TRUE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_RESIZABLE, - g_param_spec_boolean ("resizable", - P_("Resizable"), - P_("Column is user-resizable"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_WIDTH, - g_param_spec_int ("width", - P_("Width"), - P_("Current width of the column"), - 0, - G_MAXINT, - 0, - GTK_PARAM_READABLE)); - g_object_class_install_property (object_class, - PROP_SPACING, - g_param_spec_int ("spacing", - P_("Spacing"), - P_("Space which is inserted between cells"), - 0, - G_MAXINT, - 0, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_FIXED_WIDTH, - g_param_spec_int ("fixed-width", - P_("Fixed Width"), - P_("Current fixed width of the column"), - 1, - G_MAXINT, - 100, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_MIN_WIDTH, - g_param_spec_int ("min-width", - P_("Minimum Width"), - P_("Minimum allowed width of the column"), - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_MAX_WIDTH, - g_param_spec_int ("max-width", - P_("Maximum Width"), - P_("Maximum allowed width of the column"), - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_TITLE, - g_param_spec_string ("title", - P_("Title"), - P_("Title to appear in column header"), - "", - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_EXPAND, - g_param_spec_boolean ("expand", - P_("Expand"), - P_("Column gets share of extra width allocated to the widget"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_CLICKABLE, - g_param_spec_boolean ("clickable", - P_("Clickable"), - P_("Whether the header can be clicked"), - FALSE, - GTK_PARAM_READWRITE)); - - - g_object_class_install_property (object_class, - PROP_WIDGET, - g_param_spec_object ("widget", - P_("Widget"), - P_("Widget to put in column header button instead of column title"), - GTK_TYPE_WIDGET, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_ALIGNMENT, - g_param_spec_float ("alignment", - P_("Alignment"), - P_("X Alignment of the column header text or widget"), - 0.0, - 1.0, - 0.0, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_REORDERABLE, - g_param_spec_boolean ("reorderable", - P_("Reorderable"), - P_("Whether the column can be reordered around the headers"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_SORT_INDICATOR, - g_param_spec_boolean ("sort-indicator", - P_("Sort indicator"), - P_("Whether to show a sort indicator"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_SORT_ORDER, - g_param_spec_enum ("sort-order", - P_("Sort order"), - P_("Sort direction the sort indicator should indicate"), - GTK_TYPE_SORT_TYPE, - GTK_SORT_ASCENDING, - GTK_PARAM_READWRITE)); - - /** - * PsppSheetViewColumn:sort-column-id: - * - * Logical sort column ID this column sorts on when selected for sorting. Setting the sort column ID makes the column header - * clickable. Set to %-1 to make the column unsortable. - * - * Since: 2.18 - **/ - g_object_class_install_property (object_class, - PROP_SORT_COLUMN_ID, - g_param_spec_int ("sort-column-id", - P_("Sort column ID"), - P_("Logical sort column ID this column sorts on when selected for sorting"), - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_QUICK_EDIT, - g_param_spec_boolean ("quick-edit", - P_("Quick edit"), - P_("If true, editing starts upon the first click in the column. If false, the first click selects the column and a second click is needed to begin editing. This has no effect on cells that are not editable."), - TRUE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_SELECTED, - g_param_spec_boolean ("selected", - P_("Selected"), - P_("If true, this column is selected as part of a rectangular selection."), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_SELECTABLE, - g_param_spec_boolean ("selectable", - P_("Selectable"), - P_("If true, this column may be selected as part of a rectangular selection."), - TRUE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_ROW_HEAD, - g_param_spec_boolean ("row-head", - P_("Row head"), - P_("If true, this column is a \"row head\", equivalent to a column head. If rectangular selection is enabled, then shift+click and control+click in the column select row ranges and toggle row selection, respectively. The column should ordinarily include a button cell; clicking on the button will select the row (and deselect all other rows)."), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (object_class, - PROP_TABBABLE, - g_param_spec_boolean ("tabbable", - P_("Tabbable"), - P_("If true, Tab and Shift+Tab visit this column. If false, Tab and Shift+Tab skip this column."), - TRUE, - GTK_PARAM_READWRITE)); -} - - -static void _cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - gpointer *data); - - -static void _cell_layout_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *type); - - -static gboolean _cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - GMarkupParser *parser, - gpointer *data); - - -static void -pspp_sheet_view_column_buildable_init (GtkBuildableIface *iface) -{ - iface->add_child = _cell_layout_buildable_add_child; - iface->custom_tag_start = _cell_layout_buildable_custom_tag_start; - iface->custom_tag_end = _cell_layout_buildable_custom_tag_end; -} - -static void -pspp_sheet_view_column_cell_layout_init (GtkCellLayoutIface *iface) -{ - iface->pack_start = pspp_sheet_view_column_cell_layout_pack_start; - iface->pack_end = pspp_sheet_view_column_cell_layout_pack_end; - iface->clear = pspp_sheet_view_column_cell_layout_clear; - iface->add_attribute = pspp_sheet_view_column_cell_layout_add_attribute; - iface->set_cell_data_func = pspp_sheet_view_column_cell_layout_set_cell_data_func; - iface->clear_attributes = pspp_sheet_view_column_cell_layout_clear_attributes; - iface->reorder = pspp_sheet_view_column_cell_layout_reorder; - iface->get_cells = pspp_sheet_view_column_cell_layout_get_cells; -} - -static void -pspp_sheet_view_column_init (PsppSheetViewColumn *tree_column) -{ - tree_column->button = NULL; - tree_column->halign = GTK_ALIGN_START; - tree_column->width = 0; - tree_column->spacing = 0; - tree_column->requested_width = -1; - tree_column->min_width = -1; - tree_column->max_width = -1; - tree_column->resized_width = 0; - tree_column->visible = TRUE; - tree_column->resizable = FALSE; - tree_column->expand = FALSE; - tree_column->clickable = FALSE; - tree_column->dirty = TRUE; - tree_column->selected = FALSE; - tree_column->selectable = TRUE; - tree_column->row_head = FALSE; - tree_column->tabbable = TRUE; - tree_column->sort_order = GTK_SORT_ASCENDING; - tree_column->show_sort_indicator = FALSE; - tree_column->property_changed_signal = 0; - tree_column->sort_clicked_signal = 0; - tree_column->sort_column_changed_signal = 0; - tree_column->sort_column_id = -1; - tree_column->reorderable = FALSE; - tree_column->maybe_reordered = FALSE; - tree_column->fixed_width = 1; - tree_column->use_resized_width = FALSE; - tree_column->title = g_strdup (""); - tree_column->quick_edit = TRUE; -} - -static void -pspp_sheet_view_column_finalize (GObject *object) -{ - PsppSheetViewColumn *tree_column = (PsppSheetViewColumn *) object; - GList *list; - - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data; - - if (info->destroy) - { - GDestroyNotify d = info->destroy; - - info->destroy = NULL; - d (info->func_data); - } - pspp_sheet_view_column_clear_attributes_by_info (tree_column, info); - g_object_unref (info->cell); - g_free (info); - } - - g_free (tree_column->title); - g_list_free (tree_column->cell_list); - - if (tree_column->child) - g_object_unref (tree_column->child); - - G_OBJECT_CLASS (pspp_sheet_view_column_parent_class)->finalize (object); -} - -static void -pspp_sheet_view_column_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppSheetViewColumn *tree_column; - - tree_column = PSPP_SHEET_VIEW_COLUMN (object); - - switch (prop_id) - { - case PROP_VISIBLE: - pspp_sheet_view_column_set_visible (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_RESIZABLE: - pspp_sheet_view_column_set_resizable (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_FIXED_WIDTH: - pspp_sheet_view_column_set_fixed_width (tree_column, - g_value_get_int (value)); - break; - - case PROP_MIN_WIDTH: - pspp_sheet_view_column_set_min_width (tree_column, - g_value_get_int (value)); - break; - - case PROP_MAX_WIDTH: - pspp_sheet_view_column_set_max_width (tree_column, - g_value_get_int (value)); - break; - - case PROP_SPACING: - pspp_sheet_view_column_set_spacing (tree_column, - g_value_get_int (value)); - break; - - case PROP_TITLE: - pspp_sheet_view_column_set_title (tree_column, - g_value_get_string (value)); - break; - - case PROP_EXPAND: - pspp_sheet_view_column_set_expand (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_CLICKABLE: - pspp_sheet_view_column_set_clickable (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_WIDGET: - pspp_sheet_view_column_set_widget (tree_column, - (GtkWidget*) g_value_get_object (value)); - break; - - case PROP_ALIGNMENT: - pspp_sheet_view_column_set_alignment (tree_column, - g_value_get_float (value)); - break; - - case PROP_REORDERABLE: - pspp_sheet_view_column_set_reorderable (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_SORT_INDICATOR: - pspp_sheet_view_column_set_sort_indicator (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_SORT_ORDER: - pspp_sheet_view_column_set_sort_order (tree_column, - g_value_get_enum (value)); - break; - - case PROP_SORT_COLUMN_ID: - pspp_sheet_view_column_set_sort_column_id (tree_column, - g_value_get_int (value)); - break; - - case PROP_QUICK_EDIT: - pspp_sheet_view_column_set_quick_edit (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_SELECTED: - pspp_sheet_view_column_set_selected (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_SELECTABLE: - pspp_sheet_view_column_set_selectable (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_ROW_HEAD: - pspp_sheet_view_column_set_row_head (tree_column, - g_value_get_boolean (value)); - break; - - case PROP_TABBABLE: - pspp_sheet_view_column_set_tabbable (tree_column, - g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -pspp_sheet_view_column_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppSheetViewColumn *tree_column; - - tree_column = PSPP_SHEET_VIEW_COLUMN (object); - - switch (prop_id) - { - case PROP_VISIBLE: - g_value_set_boolean (value, - pspp_sheet_view_column_get_visible (tree_column)); - break; - - case PROP_RESIZABLE: - g_value_set_boolean (value, - pspp_sheet_view_column_get_resizable (tree_column)); - break; - - case PROP_WIDTH: - g_value_set_int (value, - pspp_sheet_view_column_get_width (tree_column)); - break; - - case PROP_SPACING: - g_value_set_int (value, - pspp_sheet_view_column_get_spacing (tree_column)); - break; - - case PROP_FIXED_WIDTH: - g_value_set_int (value, - pspp_sheet_view_column_get_fixed_width (tree_column)); - break; - - case PROP_MIN_WIDTH: - g_value_set_int (value, - pspp_sheet_view_column_get_min_width (tree_column)); - break; - - case PROP_MAX_WIDTH: - g_value_set_int (value, - pspp_sheet_view_column_get_max_width (tree_column)); - break; - - case PROP_TITLE: - g_value_set_string (value, - pspp_sheet_view_column_get_title (tree_column)); - break; - - case PROP_EXPAND: - g_value_set_boolean (value, - pspp_sheet_view_column_get_expand (tree_column)); - break; - - case PROP_CLICKABLE: - g_value_set_boolean (value, - pspp_sheet_view_column_get_clickable (tree_column)); - break; - - case PROP_WIDGET: - g_value_set_object (value, - (GObject*) pspp_sheet_view_column_get_widget (tree_column)); - break; - - case PROP_ALIGNMENT: - g_value_set_float (value, - pspp_sheet_view_column_get_alignment (tree_column)); - break; - - case PROP_REORDERABLE: - g_value_set_boolean (value, - pspp_sheet_view_column_get_reorderable (tree_column)); - break; - - case PROP_SORT_INDICATOR: - g_value_set_boolean (value, - pspp_sheet_view_column_get_sort_indicator (tree_column)); - break; - - case PROP_SORT_ORDER: - g_value_set_enum (value, - pspp_sheet_view_column_get_sort_order (tree_column)); - break; - - case PROP_SORT_COLUMN_ID: - g_value_set_int (value, - pspp_sheet_view_column_get_sort_column_id (tree_column)); - break; - - case PROP_QUICK_EDIT: - g_value_set_boolean (value, - pspp_sheet_view_column_get_quick_edit (tree_column)); - break; - - case PROP_SELECTED: - g_value_set_boolean (value, - pspp_sheet_view_column_get_selected (tree_column)); - break; - - case PROP_SELECTABLE: - g_value_set_boolean (value, - pspp_sheet_view_column_get_selectable (tree_column)); - break; - - case PROP_ROW_HEAD: - g_value_set_boolean (value, - pspp_sheet_view_column_get_row_head (tree_column)); - break; - - case PROP_TABBABLE: - g_value_set_boolean (value, - pspp_sheet_view_column_get_tabbable (tree_column)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* Implementation of GtkCellLayout interface - */ - -static void -pspp_sheet_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand) -{ - PsppSheetViewColumn *column; - PsppSheetViewColumnCellInfo *cell_info; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout)); - column = PSPP_SHEET_VIEW_COLUMN (cell_layout); - g_return_if_fail (! pspp_sheet_view_column_get_cell_info (column, cell)); - - g_object_ref_sink (cell); - - cell_info = g_new0 (PsppSheetViewColumnCellInfo, 1); - cell_info->cell = cell; - cell_info->expand = expand ? TRUE : FALSE; - cell_info->pack = GTK_PACK_START; - cell_info->has_focus = 0; - cell_info->attributes = NULL; - - column->cell_list = g_list_append (column->cell_list, cell_info); -} - -static void -pspp_sheet_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand) -{ - PsppSheetViewColumn *column; - PsppSheetViewColumnCellInfo *cell_info; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout)); - column = PSPP_SHEET_VIEW_COLUMN (cell_layout); - g_return_if_fail (! pspp_sheet_view_column_get_cell_info (column, cell)); - - g_object_ref_sink (cell); - - cell_info = g_new0 (PsppSheetViewColumnCellInfo, 1); - cell_info->cell = cell; - cell_info->expand = expand ? TRUE : FALSE; - cell_info->pack = GTK_PACK_END; - cell_info->has_focus = 0; - cell_info->attributes = NULL; - - column->cell_list = g_list_append (column->cell_list, cell_info); -} - -static void -pspp_sheet_view_column_cell_layout_clear (GtkCellLayout *cell_layout) -{ - PsppSheetViewColumn *column; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout)); - column = PSPP_SHEET_VIEW_COLUMN (cell_layout); - - while (column->cell_list) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)column->cell_list->data; - - pspp_sheet_view_column_cell_layout_clear_attributes (cell_layout, info->cell); - g_object_unref (info->cell); - g_free (info); - column->cell_list = g_list_delete_link (column->cell_list, - column->cell_list); - } -} - -static void -pspp_sheet_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const gchar *attribute, - gint column) -{ - PsppSheetViewColumn *tree_column; - PsppSheetViewColumnCellInfo *info; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout)); - tree_column = PSPP_SHEET_VIEW_COLUMN (cell_layout); - - info = pspp_sheet_view_column_get_cell_info (tree_column, cell); - g_return_if_fail (info != NULL); - - info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column)); - info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute)); - - if (tree_column->tree_view) - _pspp_sheet_view_column_cell_set_dirty (tree_column); -} - -static void -pspp_sheet_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy) -{ - PsppSheetViewColumn *column; - PsppSheetViewColumnCellInfo *info; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout)); - column = PSPP_SHEET_VIEW_COLUMN (cell_layout); - - info = pspp_sheet_view_column_get_cell_info (column, cell); - g_return_if_fail (info != NULL); - - if (info->destroy) - { - GDestroyNotify d = info->destroy; - - info->destroy = NULL; - d (info->func_data); - } - - info->func = (PsppSheetCellDataFunc)func; - info->func_data = func_data; - info->destroy = destroy; - - if (column->tree_view) - _pspp_sheet_view_column_cell_set_dirty (column); -} - -static void -pspp_sheet_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell_renderer) -{ - PsppSheetViewColumn *column; - PsppSheetViewColumnCellInfo *info; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout)); - column = PSPP_SHEET_VIEW_COLUMN (cell_layout); - - info = pspp_sheet_view_column_get_cell_info (column, cell_renderer); - if (info) - pspp_sheet_view_column_clear_attributes_by_info (column, info); -} - -static void -pspp_sheet_view_column_cell_layout_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gint position) -{ - GList *link; - PsppSheetViewColumn *column; - PsppSheetViewColumnCellInfo *info; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout)); - column = PSPP_SHEET_VIEW_COLUMN (cell_layout); - - info = pspp_sheet_view_column_get_cell_info (column, cell); - - g_return_if_fail (info != NULL); - g_return_if_fail (position >= 0); - - link = g_list_find (column->cell_list, info); - - g_return_if_fail (link != NULL); - - column->cell_list = g_list_delete_link (column->cell_list, link); - column->cell_list = g_list_insert (column->cell_list, info, position); - - if (column->tree_view) - gtk_widget_queue_draw (column->tree_view); -} - -static void -pspp_sheet_view_column_clear_attributes_by_info (PsppSheetViewColumn *tree_column, - PsppSheetViewColumnCellInfo *info) -{ - GSList *list; - - list = info->attributes; - - while (list && list->next) - { - g_free (list->data); - list = list->next->next; - } - g_slist_free (info->attributes); - info->attributes = NULL; - - if (tree_column->tree_view) - _pspp_sheet_view_column_cell_set_dirty (tree_column); -} - -static gboolean -on_query_tooltip (GtkWidget *widget, - gint x, - gint y, - gboolean keyboard_mode, - GtkTooltip *tooltip, - gpointer user_data) -{ - PsppSheetViewColumn *tree_column = user_data; - gboolean handled; - - g_signal_emit (tree_column, tree_column_signals[QUERY_TOOLTIP], 0, - tooltip, &handled); - return handled; -} - -static gboolean -on_button_pressed (GtkWidget *widget, GdkEventButton *event, - gpointer user_data) -{ - PsppSheetViewColumn *tree_column = user_data; - gboolean handled; - - /* XXX See "Implement GtkWidget::popup_menu" in GTK+ reference manual. */ - g_signal_emit (tree_column, tree_column_signals[BUTTON_PRESS_EVENT], - 0, event, &handled); - return handled; -} - -/* Helper functions - */ - -/* Button handling code - */ -static void -pspp_sheet_view_column_create_button (PsppSheetViewColumn *tree_column) -{ - PsppSheetView *tree_view; - GtkWidget *child; - GtkWidget *hbox; - - tree_view = (PsppSheetView *) tree_column->tree_view; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (tree_column->button == NULL); - - tree_column->button = gtk_button_new (); - gtk_widget_add_events (tree_column->button, GDK_POINTER_MOTION_MASK); - - /* make sure we own a reference to it as well. */ - if (tree_view->priv->header_window) - gtk_widget_set_parent_window (tree_column->button, tree_view->priv->header_window); - gtk_widget_set_parent (tree_column->button, GTK_WIDGET (tree_view)); - - g_signal_connect (tree_column->button, "event", - G_CALLBACK (pspp_sheet_view_column_button_event), - tree_column); - g_signal_connect (tree_column->button, "clicked", - G_CALLBACK (pspp_sheet_view_column_button_clicked), - tree_column); - g_signal_connect (tree_column->button, "popup-menu", - G_CALLBACK (pspp_sheet_view_column_button_popup_menu), - tree_column); - g_signal_connect (tree_column->button, "button-press-event", - G_CALLBACK (on_button_pressed), tree_column); - - g_signal_connect (tree_column->button, "query-tooltip", - G_CALLBACK (on_query_tooltip), tree_column); - g_object_set (tree_column->button, "has-tooltip", TRUE, NULL); - - tree_column->bin = gtk_event_box_new (); - g_object_set (tree_column->bin, "halign", GTK_ALIGN_CENTER, NULL); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); - - if (tree_column->child) - child = tree_column->child; - else - { - child = gtk_label_new (tree_column->title); - gtk_widget_show (child); - } - - g_signal_connect (child, "mnemonic-activate", - G_CALLBACK (pspp_sheet_view_column_mnemonic_activate), - tree_column); - - - gtk_box_pack_start (GTK_BOX (hbox), tree_column->bin, TRUE, TRUE, 0); - - gtk_container_add (GTK_CONTAINER (tree_column->bin), child); - gtk_container_add (GTK_CONTAINER (tree_column->button), hbox); - - gtk_widget_show (hbox); - gtk_widget_show (tree_column->bin); - pspp_sheet_view_column_update_button (tree_column); -} - -void -pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column) -{ - gint sort_column_id = -1; - GtkWidget *alignment; - GtkWidget *current_child; - GtkTreeModel *model; - gboolean can_focus; - - if (tree_column->tree_view) - model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view)); - else - model = NULL; - - /* Create a button if necessary */ - if (tree_column->visible && - tree_column->button == NULL && - tree_column->tree_view && - gtk_widget_get_realized (tree_column->tree_view)) - pspp_sheet_view_column_create_button (tree_column); - - if (! tree_column->button) - return; - - alignment = tree_column->bin; - current_child = gtk_bin_get_child (GTK_BIN (alignment)); - - /* Set up the actual button */ - g_object_set (alignment, - "halign", tree_column->halign, - "valign", GTK_ALIGN_CENTER, NULL); - - if (tree_column->child) - { - if (current_child != tree_column->child) - { - gtk_container_remove (GTK_CONTAINER (alignment), - current_child); - gtk_container_add (GTK_CONTAINER (alignment), - tree_column->child); - } - } - else - { - if (current_child == NULL) - { - current_child = gtk_label_new (NULL); - gtk_widget_show (current_child); - gtk_container_add (GTK_CONTAINER (alignment), - current_child); - } - - g_return_if_fail (GTK_IS_LABEL (current_child)); - - if (tree_column->title) - gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child), - tree_column->title); - else - gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child), - ""); - } - - if (GTK_IS_TREE_SORTABLE (model)) - gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), - &sort_column_id, - NULL); - - - /* It's always safe to hide the button. It isn't always safe to show it, as - * if you show it before it's realized, it'll get the wrong window. */ - if (tree_column->button && - tree_column->tree_view != NULL && - gtk_widget_get_realized (tree_column->tree_view)) - { - if (tree_column->visible) - { - gtk_widget_show (tree_column->button); - if (tree_column->window) - { - if (tree_column->resizable) - { - gdk_window_show (tree_column->window); - gdk_window_raise (tree_column->window); - } - else - { - gdk_window_hide (tree_column->window); - } - } - } - else - { - gtk_widget_hide (tree_column->button); - if (tree_column->window) - gdk_window_hide (tree_column->window); - } - } - - can_focus = pspp_sheet_view_column_can_focus (tree_column); - gtk_widget_set_can_focus (tree_column->button, can_focus); - if (!can_focus && gtk_widget_has_focus (tree_column->button)) - { - GtkWidget *toplevel = gtk_widget_get_toplevel (tree_column->tree_view); - if (gtk_widget_is_toplevel (toplevel)) - { - gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); - } - } - - /* Queue a resize on the assumption that we always want to catch all changes - * and columns don't change all that often. - */ - if (gtk_widget_get_realized (tree_column->tree_view)) - gtk_widget_queue_resize (tree_column->tree_view); - -} - -/* Button signal handlers - */ - -static gint -pspp_sheet_view_column_button_event (GtkWidget *widget, - GdkEventButton *event, - gpointer data) -{ - PsppSheetViewColumn *column = (PsppSheetViewColumn *) data; - - g_return_val_if_fail (event != NULL, FALSE); - - if (event->type == GDK_BUTTON_PRESS && - column->reorderable && - ((GdkEventButton *)event)->button == 1) - { - column->maybe_reordered = TRUE; - gdk_window_get_device_position (gtk_button_get_event_window (GTK_BUTTON (widget)), - event->device, - &column->drag_x, - &column->drag_y, - NULL); - gtk_widget_grab_focus (widget); - } - - if (event->type == GDK_BUTTON_RELEASE || - event->type == GDK_LEAVE_NOTIFY) - column->maybe_reordered = FALSE; - - if (event->type == GDK_MOTION_NOTIFY && - column->maybe_reordered && - (gtk_drag_check_threshold (widget, - column->drag_x, - column->drag_y, - (gint) ((GdkEventMotion *)event)->x, - (gint) ((GdkEventMotion *)event)->y))) - { - column->maybe_reordered = FALSE; - _pspp_sheet_view_column_start_drag (PSPP_SHEET_VIEW (column->tree_view), column); - return TRUE; - } - if (column->clickable == FALSE) - { - switch (event->type) - { - case GDK_MOTION_NOTIFY: - case GDK_BUTTON_RELEASE: - case GDK_ENTER_NOTIFY: - case GDK_LEAVE_NOTIFY: - return TRUE; - default: - return FALSE; - } - } - return FALSE; -} - -static gboolean -all_rows_selected (PsppSheetView *sheet_view) -{ - PsppSheetSelection *selection = sheet_view->priv->selection; - gint n_rows, n_selected_rows; - - n_rows = sheet_view->priv->row_count; - n_selected_rows = pspp_sheet_selection_count_selected_rows (selection); - - return n_rows > 0 && n_selected_rows >= n_rows; -} - -static gboolean -on_pspp_sheet_view_column_button_press_event (PsppSheetViewColumn *column, - GdkEventButton *event) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (column->tree_view); - PsppSheetSelection *selection; - GSignalInvocationHint *hint; - guint modifiers; - - /* We only want to run first, not last, but combining that with return type - `gboolean' makes GObject warn, so just ignore the run_last call. */ - hint = g_signal_get_invocation_hint (column); - g_return_val_if_fail (hint != NULL, FALSE); - if (hint->run_type != G_SIGNAL_RUN_FIRST) - return FALSE; - - g_return_val_if_fail (sheet_view != NULL, FALSE); - - selection = sheet_view->priv->selection; - g_return_val_if_fail (selection != NULL, FALSE); - - if (pspp_sheet_selection_get_mode (selection) != PSPP_SHEET_SELECTION_RECTANGLE) - return FALSE; - - modifiers = event->state & gtk_accelerator_get_default_mod_mask (); - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - if (pspp_sheet_selection_count_selected_columns (selection) <= 1 - || !all_rows_selected (sheet_view)) - { - pspp_sheet_selection_select_all (selection); - pspp_sheet_selection_unselect_all_columns (selection); - pspp_sheet_selection_select_column (selection, column); - sheet_view->priv->anchor_column = column; - } - return FALSE; - } - else if (event->type == GDK_BUTTON_PRESS && event->button == 1 - && modifiers == GDK_CONTROL_MASK) - { - gboolean is_selected; - - if (!all_rows_selected (sheet_view)) - { - pspp_sheet_selection_select_all (selection); - pspp_sheet_selection_unselect_all_columns (selection); - } - sheet_view->priv->anchor_column = column; - - is_selected = pspp_sheet_view_column_get_selected (column); - pspp_sheet_view_column_set_selected (column, !is_selected); - - return TRUE; - } - else if (event->type == GDK_BUTTON_PRESS && event->button == 1 - && modifiers == GDK_SHIFT_MASK) - { - if (!all_rows_selected (sheet_view)) - { - pspp_sheet_selection_select_all (selection); - pspp_sheet_selection_unselect_all_columns (selection); - sheet_view->priv->anchor_column = column; - } - else if (sheet_view->priv->anchor_column == NULL) - sheet_view->priv->anchor_column = column; - - pspp_sheet_selection_unselect_all_columns (selection); - pspp_sheet_selection_select_column_range (selection, - sheet_view->priv->anchor_column, - column); - return TRUE; - } - - return FALSE; -} - -static gboolean -on_pspp_sheet_view_column_button_clicked (PsppSheetViewColumn *column) -{ - PsppSheetSelection *selection; - PsppSheetView *sheet_view; - - sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (column)); - selection = pspp_sheet_view_get_selection (sheet_view); - if (pspp_sheet_selection_get_mode (selection) == PSPP_SHEET_SELECTION_RECTANGLE) - { - pspp_sheet_selection_select_all (selection); - if (pspp_sheet_view_column_get_row_head (column)) - pspp_sheet_selection_select_all_columns (selection); - else - { - pspp_sheet_selection_unselect_all_columns (selection); - pspp_sheet_selection_select_column (selection, column); - } - sheet_view->priv->anchor_column = column; - return TRUE; - } - return FALSE; -} - -static void -pspp_sheet_view_column_button_clicked (GtkWidget *widget, gpointer data) -{ - PsppSheetViewColumn *column = data; - gboolean handled; - - g_signal_emit (column, tree_column_signals[CLICKED], 0, &handled); -} - -static void -pspp_sheet_view_column_button_popup_menu (GtkWidget *widget, gpointer data) -{ - g_signal_emit_by_name (data, "popup-menu"); -} - -static gboolean -pspp_sheet_view_column_mnemonic_activate (GtkWidget *widget, - gboolean group_cycling, - gpointer data) -{ - PsppSheetViewColumn *column = (PsppSheetViewColumn *)data; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), FALSE); - - PSPP_SHEET_VIEW (column->tree_view)->priv->focus_column = column; - if (column->clickable) - gtk_button_clicked (GTK_BUTTON (column->button)); - else if (gtk_widget_get_can_focus (column->button)) - gtk_widget_grab_focus (column->button); - else - gtk_widget_grab_focus (column->tree_view); - - return TRUE; -} - -static void -pspp_sheet_view_model_sort_column_changed (GtkTreeSortable *sortable, - PsppSheetViewColumn *column) -{ - gint sort_column_id; - GtkSortType order; - - if (gtk_tree_sortable_get_sort_column_id (sortable, - &sort_column_id, - &order)) - { - if (sort_column_id == column->sort_column_id) - { - pspp_sheet_view_column_set_sort_indicator (column, TRUE); - pspp_sheet_view_column_set_sort_order (column, order); - } - else - { - pspp_sheet_view_column_set_sort_indicator (column, FALSE); - } - } - else - { - pspp_sheet_view_column_set_sort_indicator (column, FALSE); - } -} - -static void -pspp_sheet_view_column_sort (PsppSheetViewColumn *tree_column, - gpointer data) -{ - gint sort_column_id; - GtkSortType order; - gboolean has_sort_column; - gboolean has_default_sort_func; - - g_return_if_fail (tree_column->tree_view != NULL); - - has_sort_column = - gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model), - &sort_column_id, - &order); - has_default_sort_func = - gtk_tree_sortable_has_default_sort_func (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model)); - - if (has_sort_column && - sort_column_id == tree_column->sort_column_id) - { - if (order == GTK_SORT_ASCENDING) - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model), - tree_column->sort_column_id, - GTK_SORT_DESCENDING); - else if (order == GTK_SORT_DESCENDING && has_default_sort_func) - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model), - GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, - GTK_SORT_ASCENDING); - else - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model), - tree_column->sort_column_id, - GTK_SORT_ASCENDING); - } - else - { - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model), - tree_column->sort_column_id, - GTK_SORT_ASCENDING); - } -} - - -static void -pspp_sheet_view_column_setup_sort_column_id_callback (PsppSheetViewColumn *tree_column) -{ - GtkTreeModel *model; - - if (tree_column->tree_view == NULL) - return; - - model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view)); - - if (model == NULL) - return; - - if (GTK_IS_TREE_SORTABLE (model) && - tree_column->sort_column_id != -1) - { - gint real_sort_column_id; - GtkSortType real_order; - - if (tree_column->sort_column_changed_signal == 0) - tree_column->sort_column_changed_signal = - g_signal_connect (model, "sort-column-changed", - G_CALLBACK (pspp_sheet_view_model_sort_column_changed), - tree_column); - - if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), - &real_sort_column_id, - &real_order) && - (real_sort_column_id == tree_column->sort_column_id)) - { - pspp_sheet_view_column_set_sort_indicator (tree_column, TRUE); - pspp_sheet_view_column_set_sort_order (tree_column, real_order); - } - else - { - pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE); - } - } -} - - -/* Exported Private Functions. - * These should only be called by gtktreeview.c or gtktreeviewcolumn.c - */ - -void -_pspp_sheet_view_column_realize_button (PsppSheetViewColumn *column) -{ - GtkAllocation allocation; - PsppSheetView *tree_view; - GdkWindowAttr attr; - guint attributes_mask; - gboolean rtl; - - tree_view = (PsppSheetView *)column->tree_view; - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); - g_return_if_fail (tree_view->priv->header_window != NULL); - if (!column->button) - return; - - g_return_if_fail (column->button != NULL); - - gtk_widget_set_parent_window (column->button, tree_view->priv->header_window); - - if (column->visible) - gtk_widget_show (column->button); - - attr.window_type = GDK_WINDOW_CHILD; - attr.wclass = GDK_INPUT_ONLY; - attr.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view)); - attr.event_mask = gtk_widget_get_events (GTK_WIDGET (tree_view)) | - (GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK | - GDK_KEY_PRESS_MASK); - attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y; - attr.cursor = gdk_cursor_new_for_display (gdk_window_get_display (tree_view->priv->header_window), - GDK_SB_H_DOUBLE_ARROW); - attr.y = 0; - attr.width = TREE_VIEW_DRAG_WIDTH; - attr.height = tree_view->priv->header_height; - gtk_widget_get_allocation (column->button, &allocation); - attr.x = (allocation.x + (rtl ? 0 : allocation.width)) - TREE_VIEW_DRAG_WIDTH / 2; - column->window = gdk_window_new (tree_view->priv->header_window, - &attr, attributes_mask); - gdk_window_set_user_data (column->window, tree_view); - - pspp_sheet_view_column_update_button (column); - - g_object_unref (attr.cursor); -} - -void -_pspp_sheet_view_column_unrealize_button (PsppSheetViewColumn *column) -{ - g_return_if_fail (column != NULL); - if (column->window != NULL) - { - gdk_window_set_user_data (column->window, NULL); - gdk_window_destroy (column->window); - column->window = NULL; - } -} - -void -_pspp_sheet_view_column_unset_model (PsppSheetViewColumn *column, - GtkTreeModel *old_model) -{ - if (column->sort_column_changed_signal) - { - g_signal_handler_disconnect (old_model, - column->sort_column_changed_signal); - column->sort_column_changed_signal = 0; - } - pspp_sheet_view_column_set_sort_indicator (column, FALSE); -} - -void -_pspp_sheet_view_column_set_tree_view (PsppSheetViewColumn *column, - PsppSheetView *tree_view) -{ - g_assert (column->tree_view == NULL); - - column->tree_view = GTK_WIDGET (tree_view); - pspp_sheet_view_column_create_button (column); - - column->property_changed_signal = - g_signal_connect_swapped (tree_view, - "notify::model", - G_CALLBACK (pspp_sheet_view_column_setup_sort_column_id_callback), - column); - - pspp_sheet_view_column_setup_sort_column_id_callback (column); -} - -void -_pspp_sheet_view_column_unset_tree_view (PsppSheetViewColumn *column) -{ - if (column->tree_view && column->button) - { - gtk_container_remove (GTK_CONTAINER (column->tree_view), column->button); - } - if (column->property_changed_signal) - { - g_signal_handler_disconnect (column->tree_view, column->property_changed_signal); - column->property_changed_signal = 0; - } - - if (column->sort_column_changed_signal) - { - g_signal_handler_disconnect (pspp_sheet_view_get_model (PSPP_SHEET_VIEW (column->tree_view)), - column->sort_column_changed_signal); - column->sort_column_changed_signal = 0; - } - - column->tree_view = NULL; - column->button = NULL; -} - -gboolean -_pspp_sheet_view_column_has_editable_cell (PsppSheetViewColumn *column) -{ - GList *list; - - for (list = column->cell_list; list; list = list->next) - { - GtkCellRendererMode mode; - g_object_get (((PsppSheetViewColumnCellInfo *)list->data)->cell, "mode", &mode, NULL); - if (mode == GTK_CELL_RENDERER_MODE_EDITABLE) - return TRUE; - } - - return FALSE; -} - -/* gets cell being edited */ -GtkCellRenderer * -_pspp_sheet_view_column_get_edited_cell (PsppSheetViewColumn *column) -{ - GList *list; - - for (list = column->cell_list; list; list = list->next) - if (((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode) - return ((PsppSheetViewColumnCellInfo *)list->data)->cell; - - return NULL; -} - -gint -_pspp_sheet_view_column_count_special_cells (PsppSheetViewColumn *column) -{ - gint i = 0; - GList *list; - - for (list = column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *cellinfo = list->data; - - GtkCellRendererMode mode; - g_object_get (cellinfo->cell, "mode", &mode, NULL); - - if ((mode == GTK_CELL_RENDERER_MODE_EDITABLE || - mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) && - gtk_cell_renderer_get_visible (cellinfo->cell)) - i++; - } - - return i; -} - -GtkCellRenderer * -_pspp_sheet_view_column_get_cell_at_pos (PsppSheetViewColumn *column, - gint x) -{ - GList *list; - gint current_x = 0; - - list = pspp_sheet_view_column_cell_first (column); - for (; list; list = pspp_sheet_view_column_cell_next (column, list)) - { - PsppSheetViewColumnCellInfo *cellinfo = list->data; - if (current_x <= x && x <= current_x + cellinfo->real_width) - return cellinfo->cell; - current_x += cellinfo->real_width; - } - - return NULL; -} - -/* Public Functions */ - - -/** - * pspp_sheet_view_column_new: - * - * Creates a new #PsppSheetViewColumn. - * - * Return value: A newly created #PsppSheetViewColumn. - **/ -PsppSheetViewColumn * -pspp_sheet_view_column_new (void) -{ - PsppSheetViewColumn *tree_column; - - tree_column = g_object_new (PSPP_TYPE_SHEET_VIEW_COLUMN, NULL); - - return tree_column; -} - -/** - * pspp_sheet_view_column_new_with_attributes: - * @title: The title to set the header to. - * @cell: The #GtkCellRenderer. - * @Varargs: A %NULL-terminated list of attributes. - * - * Creates a new #PsppSheetViewColumn with a number of default values. This is - * equivalent to calling pspp_sheet_view_column_set_title(), - * pspp_sheet_view_column_pack_start(), and - * pspp_sheet_view_column_set_attributes() on the newly created #PsppSheetViewColumn. - * - * Here's a simple example: - * |[ - * enum { TEXT_COLUMN, COLOR_COLUMN, N_COLUMNS }; - * ... - * { - * PsppSheetViewColumn *column; - * GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); - * - * column = pspp_sheet_view_column_new_with_attributes ("Title", - * renderer, - * "text", TEXT_COLUMN, - * "foreground", COLOR_COLUMN, - * NULL); - * } - * ]| - * - * Return value: A newly created #PsppSheetViewColumn. - **/ -PsppSheetViewColumn * -pspp_sheet_view_column_new_with_attributes (const gchar *title, - GtkCellRenderer *cell, - ...) -{ - PsppSheetViewColumn *retval; - va_list args; - - retval = pspp_sheet_view_column_new (); - - pspp_sheet_view_column_set_title (retval, title); - pspp_sheet_view_column_pack_start (retval, cell, TRUE); - - va_start (args, cell); - pspp_sheet_view_column_set_attributesv (retval, cell, args); - va_end (args); - - return retval; -} - -static PsppSheetViewColumnCellInfo * -pspp_sheet_view_column_get_cell_info (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer) -{ - GList *list; - for (list = tree_column->cell_list; list; list = list->next) - if (((PsppSheetViewColumnCellInfo *)list->data)->cell == cell_renderer) - return (PsppSheetViewColumnCellInfo *) list->data; - return NULL; -} - - -/** - * pspp_sheet_view_column_pack_start: - * @tree_column: A #PsppSheetViewColumn. - * @cell: The #GtkCellRenderer. - * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column. - * - * Packs the @cell into the beginning of the column. If @expand is %FALSE, then - * the @cell is allocated no more space than it needs. Any unused space is divided - * evenly between cells for which @expand is %TRUE. - **/ -void -pspp_sheet_view_column_pack_start (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand) -{ - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tree_column), cell, expand); -} - -/** - * pspp_sheet_view_column_pack_end: - * @tree_column: A #PsppSheetViewColumn. - * @cell: The #GtkCellRenderer. - * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column. - * - * Adds the @cell to end of the column. If @expand is %FALSE, then the @cell - * is allocated no more space than it needs. Any unused space is divided - * evenly between cells for which @expand is %TRUE. - **/ -void -pspp_sheet_view_column_pack_end (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand) -{ - gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (tree_column), cell, expand); -} - -/** - * pspp_sheet_view_column_clear: - * @tree_column: A #PsppSheetViewColumn - * - * Unsets all the mappings on all renderers on the @tree_column. - **/ -void -pspp_sheet_view_column_clear (PsppSheetViewColumn *tree_column) -{ - gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column)); -} - -static GList * -pspp_sheet_view_column_cell_layout_get_cells (GtkCellLayout *layout) -{ - PsppSheetViewColumn *tree_column = PSPP_SHEET_VIEW_COLUMN (layout); - GList *retval = NULL, *list; - - g_return_val_if_fail (tree_column != NULL, NULL); - - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data; - - retval = g_list_append (retval, info->cell); - } - - return retval; -} - -/** - * pspp_sheet_view_column_get_cell_renderers: - * @tree_column: A #PsppSheetViewColumn - * - * Returns a newly-allocated #GList of all the cell renderers in the column, - * in no particular order. The list must be freed with g_list_free(). - * - * Return value: A list of #GtkCellRenderers - * - * Deprecated: 2.18: use gtk_cell_layout_get_cells() instead. - **/ -GList * -pspp_sheet_view_column_get_cell_renderers (PsppSheetViewColumn *tree_column) -{ - return pspp_sheet_view_column_cell_layout_get_cells (GTK_CELL_LAYOUT (tree_column)); -} - -/** - * pspp_sheet_view_column_add_attribute: - * @tree_column: A #PsppSheetViewColumn. - * @cell_renderer: the #GtkCellRenderer to set attributes on - * @attribute: An attribute on the renderer - * @column: The column position on the model to get the attribute from. - * - * Adds an attribute mapping to the list in @tree_column. The @column is the - * column of the model to get a value from, and the @attribute is the - * parameter on @cell_renderer to be set from the value. So for example - * if column 2 of the model contains strings, you could have the - * "text" attribute of a #GtkCellRendererText get its values from - * column 2. - **/ -void -pspp_sheet_view_column_add_attribute (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - const gchar *attribute, - gint column) -{ - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (tree_column), - cell_renderer, attribute, column); -} - -static void -pspp_sheet_view_column_set_attributesv (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - va_list args) -{ - gchar *attribute; - gint column; - - attribute = va_arg (args, gchar *); - - pspp_sheet_view_column_clear_attributes (tree_column, cell_renderer); - - while (attribute != NULL) - { - column = va_arg (args, gint); - pspp_sheet_view_column_add_attribute (tree_column, cell_renderer, attribute, column); - attribute = va_arg (args, gchar *); - } -} - -/** - * pspp_sheet_view_column_set_attributes: - * @tree_column: A #PsppSheetViewColumn. - * @cell_renderer: the #GtkCellRenderer we're setting the attributes of - * @Varargs: A %NULL-terminated list of attributes. - * - * Sets the attributes in the list as the attributes of @tree_column. - * The attributes should be in attribute/column order, as in - * pspp_sheet_view_column_add_attribute(). All existing attributes - * are removed, and replaced with the new attributes. - **/ -void -pspp_sheet_view_column_set_attributes (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - ...) -{ - va_list args; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer)); - g_return_if_fail (pspp_sheet_view_column_get_cell_info (tree_column, cell_renderer)); - - va_start (args, cell_renderer); - pspp_sheet_view_column_set_attributesv (tree_column, cell_renderer, args); - va_end (args); -} - - -/** - * pspp_sheet_view_column_set_cell_data_func: - * @tree_column: A #PsppSheetViewColumn - * @cell_renderer: A #GtkCellRenderer - * @func: The #PsppSheetViewColumnFunc to use. - * @func_data: The user data for @func. - * @destroy: The destroy notification for @func_data - * - * Sets the #PsppSheetViewColumnFunc to use for the column. This - * function is used instead of the standard attributes mapping for - * setting the column value, and should set the value of @tree_column's - * cell renderer as appropriate. @func may be %NULL to remove an - * older one. - **/ -void -pspp_sheet_view_column_set_cell_data_func (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - PsppSheetCellDataFunc func, - gpointer func_data, - GDestroyNotify destroy) -{ - gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (tree_column), - cell_renderer, - (GtkCellLayoutDataFunc)func, - func_data, destroy); -} - - -/** - * pspp_sheet_view_column_clear_attributes: - * @tree_column: a #PsppSheetViewColumn - * @cell_renderer: a #GtkCellRenderer to clear the attribute mapping on. - * - * Clears all existing attributes previously set with - * pspp_sheet_view_column_set_attributes(). - **/ -void -pspp_sheet_view_column_clear_attributes (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer) -{ - gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (tree_column), - cell_renderer); -} - -/** - * pspp_sheet_view_column_set_spacing: - * @tree_column: A #PsppSheetViewColumn. - * @spacing: distance between cell renderers in pixels. - * - * Sets the spacing field of @tree_column, which is the number of pixels to - * place between cell renderers packed into it. - **/ -void -pspp_sheet_view_column_set_spacing (PsppSheetViewColumn *tree_column, - gint spacing) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (spacing >= 0); - - if (tree_column->spacing == spacing) - return; - - tree_column->spacing = spacing; - if (tree_column->tree_view) - _pspp_sheet_view_column_cell_set_dirty (tree_column); -} - -/** - * pspp_sheet_view_column_get_spacing: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns the spacing of @tree_column. - * - * Return value: the spacing of @tree_column. - **/ -gint -pspp_sheet_view_column_get_spacing (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0); - - return tree_column->spacing; -} - -/* Options for manipulating the columns */ - -/** - * pspp_sheet_view_column_set_visible: - * @tree_column: A #PsppSheetViewColumn. - * @visible: %TRUE if the @tree_column is visible. - * - * Sets the visibility of @tree_column. - **/ -void -pspp_sheet_view_column_set_visible (PsppSheetViewColumn *tree_column, - gboolean visible) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - visible = !! visible; - - if (tree_column->visible == visible) - return; - - tree_column->visible = visible; - - if (tree_column->visible) - _pspp_sheet_view_column_cell_set_dirty (tree_column); - - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "visible"); -} - -/** - * pspp_sheet_view_column_get_visible: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns %TRUE if @tree_column is visible. - * - * Return value: whether the column is visible or not. If it is visible, then - * the tree will show the column. - **/ -gboolean -pspp_sheet_view_column_get_visible (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->visible; -} - -/** - * pspp_sheet_view_column_set_resizable: - * @tree_column: A #PsppSheetViewColumn - * @resizable: %TRUE, if the column can be resized - * - * If @resizable is %TRUE, then the user can explicitly resize the column by - * grabbing the outer edge of the column button. - **/ -void -pspp_sheet_view_column_set_resizable (PsppSheetViewColumn *tree_column, - gboolean resizable) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - resizable = !! resizable; - - if (tree_column->resizable == resizable) - return; - - tree_column->resizable = resizable; - - pspp_sheet_view_column_update_button (tree_column); - - g_object_notify (G_OBJECT (tree_column), "resizable"); -} - -/** - * pspp_sheet_view_column_get_resizable: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if the @tree_column can be resized by the end user. - * - * Return value: %TRUE, if the @tree_column can be resized. - **/ -gboolean -pspp_sheet_view_column_get_resizable (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->resizable; -} - - -/** - * pspp_sheet_view_column_get_width: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns the current size of @tree_column in pixels. - * - * Return value: The current width of @tree_column. - **/ -gint -pspp_sheet_view_column_get_width (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0); - - return tree_column->width; -} - -/** - * pspp_sheet_view_column_set_fixed_width: - * @tree_column: A #PsppSheetViewColumn. - * @fixed_width: The size to set @tree_column to. Must be greater than 0. - * - * Sets the size of the column in pixels. The size of the column is clamped to - * the min/max width for the column. Please note that the min/max width of the - * column doesn't actually affect the "fixed_width" property of the widget, just - * the actual size when displayed. - **/ -void -pspp_sheet_view_column_set_fixed_width (PsppSheetViewColumn *tree_column, - gint fixed_width) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (fixed_width > 0); - - tree_column->fixed_width = fixed_width; - tree_column->use_resized_width = FALSE; - - if (tree_column->tree_view && - gtk_widget_get_realized (tree_column->tree_view)) - { - gtk_widget_queue_resize (tree_column->tree_view); - } - - g_object_notify (G_OBJECT (tree_column), "fixed-width"); -} - -/** - * pspp_sheet_view_column_get_fixed_width: - * @tree_column: a #PsppSheetViewColumn - * - * Gets the fixed width of the column. This value is only meaning may not be - * the actual width of the column on the screen, just what is requested. - * - * Return value: the fixed width of the column - **/ -gint -pspp_sheet_view_column_get_fixed_width (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0); - - return tree_column->fixed_width; -} - -/** - * pspp_sheet_view_column_set_min_width: - * @tree_column: A #PsppSheetViewColumn. - * @min_width: The minimum width of the column in pixels, or -1. - * - * Sets the minimum width of the @tree_column. If @min_width is -1, then the - * minimum width is unset. - **/ -void -pspp_sheet_view_column_set_min_width (PsppSheetViewColumn *tree_column, - gint min_width) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (min_width >= -1); - - if (min_width == tree_column->min_width) - return; - - if (tree_column->visible && - tree_column->tree_view != NULL && - gtk_widget_get_realized (tree_column->tree_view)) - { - if (min_width > tree_column->width) - gtk_widget_queue_resize (tree_column->tree_view); - } - - tree_column->min_width = min_width; - g_object_freeze_notify (G_OBJECT (tree_column)); - if (tree_column->max_width != -1 && tree_column->max_width < min_width) - { - tree_column->max_width = min_width; - g_object_notify (G_OBJECT (tree_column), "max-width"); - } - g_object_notify (G_OBJECT (tree_column), "min-width"); - g_object_thaw_notify (G_OBJECT (tree_column)); -} - -/** - * pspp_sheet_view_column_get_min_width: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns the minimum width in pixels of the @tree_column, or -1 if no minimum - * width is set. - * - * Return value: The minimum width of the @tree_column. - **/ -gint -pspp_sheet_view_column_get_min_width (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1); - - return tree_column->min_width; -} - -/** - * pspp_sheet_view_column_set_max_width: - * @tree_column: A #PsppSheetViewColumn. - * @max_width: The maximum width of the column in pixels, or -1. - * - * Sets the maximum width of the @tree_column. If @max_width is -1, then the - * maximum width is unset. Note, the column can actually be wider than max - * width if it's the last column in a view. In this case, the column expands to - * fill any extra space. - **/ -void -pspp_sheet_view_column_set_max_width (PsppSheetViewColumn *tree_column, - gint max_width) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (max_width >= -1); - - if (max_width == tree_column->max_width) - return; - - if (tree_column->visible && - tree_column->tree_view != NULL && - gtk_widget_get_realized (tree_column->tree_view)) - { - if (max_width != -1 && max_width < tree_column->width) - gtk_widget_queue_resize (tree_column->tree_view); - } - - tree_column->max_width = max_width; - g_object_freeze_notify (G_OBJECT (tree_column)); - if (max_width != -1 && max_width < tree_column->min_width) - { - tree_column->min_width = max_width; - g_object_notify (G_OBJECT (tree_column), "min-width"); - } - g_object_notify (G_OBJECT (tree_column), "max-width"); - g_object_thaw_notify (G_OBJECT (tree_column)); -} - -/** - * pspp_sheet_view_column_get_max_width: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns the maximum width in pixels of the @tree_column, or -1 if no maximum - * width is set. - * - * Return value: The maximum width of the @tree_column. - **/ -gint -pspp_sheet_view_column_get_max_width (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1); - - return tree_column->max_width; -} - -/** - * pspp_sheet_view_column_clicked: - * @tree_column: a #PsppSheetViewColumn - * - * Emits the "clicked" signal on the column. This function will only work if - * @tree_column is clickable. - **/ -void -pspp_sheet_view_column_clicked (PsppSheetViewColumn *tree_column) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - if (tree_column->visible && - tree_column->button && - tree_column->clickable) - gtk_button_clicked (GTK_BUTTON (tree_column->button)); -} - -/** - * pspp_sheet_view_column_set_title: - * @tree_column: A #PsppSheetViewColumn. - * @title: The title of the @tree_column. - * - * Sets the title of the @tree_column. If a custom widget has been set, then - * this value is ignored. - **/ -void -pspp_sheet_view_column_set_title (PsppSheetViewColumn *tree_column, - const gchar *title) -{ - gchar *new_title; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - new_title = g_strdup (title); - g_free (tree_column->title); - tree_column->title = new_title; - - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "title"); -} - -/** - * pspp_sheet_view_column_get_title: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns the title of the widget. - * - * Return value: the title of the column. This string should not be - * modified or freed. - **/ -const gchar * -pspp_sheet_view_column_get_title (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL); - - return tree_column->title; -} - -/** - * pspp_sheet_view_column_set_expand: - * @tree_column: A #PsppSheetViewColumn - * @expand: %TRUE if the column should take available extra space, %FALSE if not - * - * Sets the column to take available extra space. This space is shared equally - * amongst all columns that have the expand set to %TRUE. If no column has this - * option set, then the last column gets all extra space. By default, every - * column is created with this %FALSE. - * - * Since: 2.4 - **/ -void -pspp_sheet_view_column_set_expand (PsppSheetViewColumn *tree_column, - gboolean expand) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - expand = !!expand; - if (tree_column->expand == expand) - return; - tree_column->expand = expand; - - if (tree_column->visible && - tree_column->tree_view != NULL && - gtk_widget_get_realized (tree_column->tree_view)) - { - /* We want to continue using the original width of the - * column that includes additional space added by the user - * resizing the columns and possibly extra (expanded) space, which - * are not included in the resized width. - */ - tree_column->use_resized_width = FALSE; - - gtk_widget_queue_resize (tree_column->tree_view); - } - - g_object_notify (G_OBJECT (tree_column), "expand"); -} - -/** - * pspp_sheet_view_column_get_expand: - * @tree_column: a #PsppSheetViewColumn - * - * Return %TRUE if the column expands to take any available space. - * - * Return value: %TRUE, if the column expands - * - * Since: 2.4 - **/ -gboolean -pspp_sheet_view_column_get_expand (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->expand; -} - -/** - * pspp_sheet_view_column_set_clickable: - * @tree_column: A #PsppSheetViewColumn. - * @clickable: %TRUE if the header is active. - * - * Sets the header to be active if @active is %TRUE. When the header is active, - * then it can take keyboard focus, and can be clicked. - **/ -void -pspp_sheet_view_column_set_clickable (PsppSheetViewColumn *tree_column, - gboolean clickable) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - clickable = !! clickable; - if (tree_column->clickable == clickable) - return; - - tree_column->clickable = clickable; - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "clickable"); -} - -/** - * pspp_sheet_view_column_get_clickable: - * @tree_column: a #PsppSheetViewColumn - * - * Returns %TRUE if the user can click on the header for the column. - * - * Return value: %TRUE if user can click the column header. - **/ -gboolean -pspp_sheet_view_column_get_clickable (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->clickable; -} - -/** - * pspp_sheet_view_column_set_widget: - * @tree_column: A #PsppSheetViewColumn. - * @widget: (allow-none): A child #GtkWidget, or %NULL. - * - * Sets the widget in the header to be @widget. If widget is %NULL, then the - * header button is set with a #GtkLabel set to the title of @tree_column. - **/ -void -pspp_sheet_view_column_set_widget (PsppSheetViewColumn *tree_column, - GtkWidget *widget) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget)); - - if (widget) - g_object_ref_sink (widget); - - if (tree_column->child) - g_object_unref (tree_column->child); - - tree_column->child = widget; - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "widget"); -} - -/** - * pspp_sheet_view_column_get_widget: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns the #GtkWidget in the button on the column header. If a custom - * widget has not been set then %NULL is returned. - * - * Return value: The #GtkWidget in the column header, or %NULL - **/ -GtkWidget * -pspp_sheet_view_column_get_widget (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL); - - return tree_column->child; -} - -/** - * pspp_sheet_view_column_set_alignment: - * @tree_column: A #PsppSheetViewColumn. - * @xalign: The alignment, which is between [0.0 and 1.0] inclusive. - * - * Sets the alignment of the title or custom widget inside the column header. - * The alignment determines its location inside the button. - **/ -void -pspp_sheet_view_column_set_alignment (PsppSheetViewColumn *tree_column, - GtkAlign xalign) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - - if (tree_column->halign == xalign) - return; - - tree_column->halign = xalign; - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "alignment"); -} - -/** - * pspp_sheet_view_column_get_alignment: - * @tree_column: A #PsppSheetViewColumn. - * - * Returns the current x alignment of @tree_column. - * - * Return value: The current alignent of @tree_column. - **/ -GtkAlign -pspp_sheet_view_column_get_alignment (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), GTK_ALIGN_CENTER); - - return tree_column->halign; -} - - -/** - * pspp_sheet_view_column_set_reorderable: - * @tree_column: A #PsppSheetViewColumn - * @reorderable: %TRUE, if the column can be reordered. - * - * If @reorderable is %TRUE, then the column can be reordered by the end user - * dragging the header. - **/ -void -pspp_sheet_view_column_set_reorderable (PsppSheetViewColumn *tree_column, - gboolean reorderable) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - /* if (reorderable) - pspp_sheet_view_column_set_clickable (tree_column, TRUE);*/ - - reorderable = !!reorderable; - if (tree_column->reorderable == reorderable) - return; - - tree_column->reorderable = reorderable; - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "reorderable"); -} - -/** - * pspp_sheet_view_column_get_reorderable: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if the @tree_column can be reordered by the user. - * - * Return value: %TRUE if the @tree_column can be reordered by the user. - **/ -gboolean -pspp_sheet_view_column_get_reorderable (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->reorderable; -} - -/** - * pspp_sheet_view_column_set_quick_edit: - * @tree_column: A #PsppSheetViewColumn - * @quick_edit: If true, editing starts upon the first click in the column. If - * false, the first click selects the column and a second click is needed to - * begin editing. This has no effect on cells that are not editable. - **/ -void -pspp_sheet_view_column_set_quick_edit (PsppSheetViewColumn *tree_column, - gboolean quick_edit) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - quick_edit = !!quick_edit; - if (tree_column->quick_edit != quick_edit) - { - tree_column->quick_edit = quick_edit; - g_object_notify (G_OBJECT (tree_column), "quick-edit"); - } -} - -/** - * pspp_sheet_view_column_get_quick_edit: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if editing starts upon the first click in the column. Returns - * %FALSE, the first click selects the column and a second click is needed to - * begin editing. This is not meaningful for cells that are not editable. - * - * Return value: %TRUE if editing starts upon the first click. - **/ -gboolean -pspp_sheet_view_column_get_quick_edit (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->quick_edit; -} - - -/** - * pspp_sheet_view_column_set_selected: - * @tree_column: A #PsppSheetViewColumn - * @selected: If true, the column is selected as part of a rectangular - * selection. - **/ -void -pspp_sheet_view_column_set_selected (PsppSheetViewColumn *tree_column, - gboolean selected) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - selected = !!selected; - if (tree_column->selected != selected) - { - PsppSheetSelection *selection; - PsppSheetView *sheet_view; - - if (tree_column->tree_view != NULL) - gtk_widget_queue_draw (GTK_WIDGET (tree_column->tree_view)); - tree_column->selected = selected; - g_object_notify (G_OBJECT (tree_column), "selected"); - - sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view ( - tree_column)); - selection = pspp_sheet_view_get_selection (sheet_view); - _pspp_sheet_selection_emit_changed (selection); - } -} - -/** - * pspp_sheet_view_column_get_selected: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if the column is selected as part of a rectangular - * selection. - * - * Return value: %TRUE if the column is selected as part of a rectangular - * selection. - **/ -gboolean -pspp_sheet_view_column_get_selected (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->selected; -} - -/** - * pspp_sheet_view_column_set_selectable: - * @tree_column: A #PsppSheetViewColumn - * @selectable: If true, the column may be selected as part of a rectangular - * selection. - **/ -void -pspp_sheet_view_column_set_selectable (PsppSheetViewColumn *tree_column, - gboolean selectable) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - selectable = !!selectable; - if (tree_column->selectable != selectable) - { - if (tree_column->tree_view != NULL) - gtk_widget_queue_draw (GTK_WIDGET (tree_column->tree_view)); - tree_column->selectable = selectable; - g_object_notify (G_OBJECT (tree_column), "selectable"); - } -} - -/** - * pspp_sheet_view_column_get_selectable: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if the column may be selected as part of a rectangular - * selection. - * - * Return value: %TRUE if the column may be selected as part of a rectangular - * selection. - **/ -gboolean -pspp_sheet_view_column_get_selectable (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->selectable; -} - - -/** - * pspp_sheet_view_column_set_row_head: - * @tree_column: A #PsppSheetViewColumn - * @row_head: If true, the column is a "row head", analogous to a column head. - * See the description of the row-head property for more information. - **/ -void -pspp_sheet_view_column_set_row_head (PsppSheetViewColumn *tree_column, - gboolean row_head) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - row_head = !!row_head; - if (tree_column->row_head != row_head) - { - tree_column->row_head = row_head; - g_object_notify (G_OBJECT (tree_column), "row_head"); - } -} - -/** - * pspp_sheet_view_column_get_row_head: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if the column is a row head. - * - * Return value: %TRUE if the column is a row head. - **/ -gboolean -pspp_sheet_view_column_get_row_head (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->row_head; -} - - -/** - * pspp_sheet_view_column_set_tabbable: - * @tree_column: A #PsppSheetViewColumn - * @tabbable: If true, the column is "tabbable", meaning that Tab and Shift+Tab - * in the sheet visit this column. If false, Tab and Shift+Tab skip this - * column. - **/ -void -pspp_sheet_view_column_set_tabbable (PsppSheetViewColumn *tree_column, - gboolean tabbable) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - tabbable = !!tabbable; - if (tree_column->tabbable != tabbable) - { - tree_column->tabbable = tabbable; - g_object_notify (G_OBJECT (tree_column), "tabbable"); - } -} - -/** - * pspp_sheet_view_column_get_tabbable: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if the column is tabbable. - * - * Return value: %TRUE if the column is tabbable. - **/ -gboolean -pspp_sheet_view_column_get_tabbable (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->tabbable; -} - - -/** - * pspp_sheet_view_column_set_sort_column_id: - * @tree_column: a #PsppSheetViewColumn - * @sort_column_id: The @sort_column_id of the model to sort on. - * - * Sets the logical @sort_column_id that this column sorts on when this column - * is selected for sorting. Doing so makes the column header clickable. - **/ -void -pspp_sheet_view_column_set_sort_column_id (PsppSheetViewColumn *tree_column, - gint sort_column_id) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (sort_column_id >= -1); - - if (tree_column->sort_column_id == sort_column_id) - return; - - tree_column->sort_column_id = sort_column_id; - - /* Handle unsetting the id */ - if (sort_column_id == -1) - { - GtkTreeModel *model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view)); - - if (tree_column->sort_clicked_signal) - { - g_signal_handler_disconnect (tree_column, tree_column->sort_clicked_signal); - tree_column->sort_clicked_signal = 0; - } - - if (tree_column->sort_column_changed_signal) - { - g_signal_handler_disconnect (model, tree_column->sort_column_changed_signal); - tree_column->sort_column_changed_signal = 0; - } - - pspp_sheet_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING); - pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE); - pspp_sheet_view_column_set_clickable (tree_column, FALSE); - g_object_notify (G_OBJECT (tree_column), "sort-column-id"); - return; - } - - pspp_sheet_view_column_set_clickable (tree_column, TRUE); - - if (! tree_column->sort_clicked_signal) - tree_column->sort_clicked_signal = g_signal_connect (tree_column, - "clicked", - G_CALLBACK (pspp_sheet_view_column_sort), - NULL); - - pspp_sheet_view_column_setup_sort_column_id_callback (tree_column); - g_object_notify (G_OBJECT (tree_column), "sort-column-id"); -} - -/** - * pspp_sheet_view_column_get_sort_column_id: - * @tree_column: a #PsppSheetViewColumn - * - * Gets the logical @sort_column_id that the model sorts on when this - * column is selected for sorting. - * See pspp_sheet_view_column_set_sort_column_id(). - * - * Return value: the current @sort_column_id for this column, or -1 if - * this column can't be used for sorting. - **/ -gint -pspp_sheet_view_column_get_sort_column_id (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0); - - return tree_column->sort_column_id; -} - -/** - * pspp_sheet_view_column_set_sort_indicator: - * @tree_column: a #PsppSheetViewColumn - * @setting: %TRUE to display an indicator that the column is sorted - * - * Call this function with a @setting of %TRUE to display an arrow in - * the header button indicating the column is sorted. Call - * pspp_sheet_view_column_set_sort_order() to change the direction of - * the arrow. - * - **/ -void -pspp_sheet_view_column_set_sort_indicator (PsppSheetViewColumn *tree_column, - gboolean setting) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - setting = setting != FALSE; - - if (setting == tree_column->show_sort_indicator) - return; - - tree_column->show_sort_indicator = setting; - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "sort-indicator"); -} - -/** - * pspp_sheet_view_column_get_sort_indicator: - * @tree_column: a #PsppSheetViewColumn - * - * Gets the value set by pspp_sheet_view_column_set_sort_indicator(). - * - * Return value: whether the sort indicator arrow is displayed - **/ -gboolean -pspp_sheet_view_column_get_sort_indicator (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return tree_column->show_sort_indicator; -} - -/** - * pspp_sheet_view_column_set_sort_order: - * @tree_column: a #PsppSheetViewColumn - * @order: sort order that the sort indicator should indicate - * - * Changes the appearance of the sort indicator. - * - * This does not actually sort the model. Use - * pspp_sheet_view_column_set_sort_column_id() if you want automatic sorting - * support. This function is primarily for custom sorting behavior, and should - * be used in conjunction with gtk_tree_sortable_set_sort_column() to do - * that. For custom models, the mechanism will vary. - * - * The sort indicator changes direction to indicate normal sort or reverse sort. - * Note that you must have the sort indicator enabled to see anything when - * calling this function; see pspp_sheet_view_column_set_sort_indicator(). - **/ -void -pspp_sheet_view_column_set_sort_order (PsppSheetViewColumn *tree_column, - GtkSortType order) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - if (order == tree_column->sort_order) - return; - - tree_column->sort_order = order; - pspp_sheet_view_column_update_button (tree_column); - g_object_notify (G_OBJECT (tree_column), "sort-order"); -} - -/** - * pspp_sheet_view_column_get_sort_order: - * @tree_column: a #PsppSheetViewColumn - * - * Gets the value set by pspp_sheet_view_column_set_sort_order(). - * - * Return value: the sort order the sort indicator is indicating - **/ -GtkSortType -pspp_sheet_view_column_get_sort_order (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0); - - return tree_column->sort_order; -} - -/** - * pspp_sheet_view_column_cell_set_cell_data: - * @tree_column: A #PsppSheetViewColumn. - * @tree_model: The #GtkTreeModel to to get the cell renderers attributes from. - * @iter: The #GtkTreeIter to to get the cell renderer's attributes from. - * - * Sets the cell renderer based on the @tree_model and @iter. That is, for - * every attribute mapping in @tree_column, it will get a value from the set - * column on the @iter, and use that value to set the attribute on the cell - * renderer. This is used primarily by the #PsppSheetView. - **/ -void -pspp_sheet_view_column_cell_set_cell_data (PsppSheetViewColumn *tree_column, - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GSList *list; - GValue value = { 0, }; - GList *cell_list; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - if (tree_model == NULL) - return; - - for (cell_list = tree_column->cell_list; cell_list; cell_list = cell_list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) cell_list->data; - GObject *cell = (GObject *) info->cell; - - list = info->attributes; - - g_object_freeze_notify (cell); - - while (list && list->next) - { - gtk_tree_model_get_value (tree_model, iter, - GPOINTER_TO_INT (list->next->data), - &value); - g_object_set_property (cell, (gchar *) list->data, &value); - g_value_unset (&value); - list = list->next->next; - } - - if (info->func) - (* info->func) (tree_column, info->cell, tree_model, iter, info->func_data); - g_object_thaw_notify (G_OBJECT (info->cell)); - } - -} - -/** - * pspp_sheet_view_column_cell_get_size: - * @tree_column: A #PsppSheetViewColumn. - * @cell_area: (allow-none): The area a cell in the column will be allocated, or %NULL - * @x_offset: (allow-none): location to return x offset of a cell relative to @cell_area, or %NULL - * @y_offset: (allow-none): location to return y offset of a cell relative to @cell_area, or %NULL - * @width: (allow-none): location to return width needed to render a cell, or %NULL - * @height: (allow-none): location to return height needed to render a cell, or %NULL - * - * Obtains the width and height needed to render the column. This is used - * primarily by the #PsppSheetView. - **/ -void -pspp_sheet_view_column_cell_get_size (PsppSheetViewColumn *tree_column, - const GdkRectangle *cell_area, - gint *x_offset, - gint *y_offset, - gint *width, - gint *height) -{ - GList *list; - gboolean first_cell = TRUE; - gint focus_line_width; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - if (height) - * height = 0; - if (width) - * width = 0; - - gtk_widget_style_get (tree_column->tree_view, "focus-line-width", &focus_line_width, NULL); - - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data; - gboolean visible; - gint new_height = 0; - gint new_width = 0; - g_object_get (info->cell, "visible", &visible, NULL); - - if (visible == FALSE) - continue; - - if (first_cell == FALSE && width) - *width += tree_column->spacing; - - gtk_cell_renderer_get_padding (info->cell, x_offset, y_offset); - gtk_cell_renderer_get_preferred_width (info->cell, tree_column->tree_view, - NULL, &new_width); - - gtk_cell_renderer_get_preferred_height (info->cell, tree_column->tree_view, - NULL, &new_height); - - - if (height) - * height = MAX (*height, new_height + focus_line_width * 2); - info->requested_width = MAX (info->requested_width, new_width + focus_line_width * 2); - if (width) - * width += info->requested_width; - first_cell = FALSE; - } -} - -/* rendering, event handling and rendering focus are somewhat complicated, and - * quite a bit of code. Rather than duplicate them, we put them together to - * keep the code in one place. - * - * To better understand what's going on, check out - * docs/tree-column-sizing.png - */ -enum { - CELL_ACTION_RENDER, - CELL_ACTION_FOCUS, - CELL_ACTION_EVENT -}; - -static gboolean -pspp_sheet_view_column_cell_process_action (PsppSheetViewColumn *tree_column, - cairo_t *cr, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags, - gint action, - GdkRectangle *focus_rectangle, /* FOCUS */ - GtkCellEditable **editable_widget, /* EVENT */ - GdkEvent *event, /* EVENT */ - gchar *path_string) /* EVENT */ -{ - GList *list; - GdkRectangle real_cell_area; - GdkRectangle real_background_area; - gint depth = 0; - gint expand_cell_count = 0; - gint full_requested_width = 0; - gint extra_space; - gint min_x, min_y, max_x, max_y; - gint focus_line_width; - gint special_cells; - gint horizontal_separator; - gboolean cursor_row = FALSE; - gboolean first_cell = TRUE; - gboolean rtl; - /* If we have rtl text, we need to transform our areas */ - GdkRectangle rtl_cell_area; - GdkRectangle rtl_background_area; - - min_x = G_MAXINT; - min_y = G_MAXINT; - max_x = 0; - max_y = 0; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL); - special_cells = _pspp_sheet_view_column_count_special_cells (tree_column); - - if (special_cells > 1 && action == CELL_ACTION_FOCUS) - { - PsppSheetViewColumnCellInfo *info = NULL; - gboolean found_has_focus = FALSE; - - /* one should have focus */ - for (list = tree_column->cell_list; list; list = list->next) - { - info = list->data; - if (info && info->has_focus) - { - found_has_focus = TRUE; - break; - } - } - - if (!found_has_focus) - { - /* give the first one focus */ - info = pspp_sheet_view_column_cell_first (tree_column)->data; - info->has_focus = TRUE; - } - } - - cursor_row = flags & GTK_CELL_RENDERER_FOCUSED; - - gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view), - "focus-line-width", &focus_line_width, - "horizontal-separator", &horizontal_separator, - NULL); - - real_cell_area = *cell_area; - real_background_area = *background_area; - - - real_cell_area.x += focus_line_width; - real_cell_area.y += focus_line_width; - real_cell_area.height -= 2 * focus_line_width; - - if (rtl) - depth = real_background_area.width - real_cell_area.width; - else - depth = real_cell_area.x - real_background_area.x; - - /* Find out how much extra space we have to allocate */ - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data; - - if (! gtk_cell_renderer_get_visible (info->cell)) - continue; - - if (info->expand == TRUE) - expand_cell_count ++; - full_requested_width += info->requested_width; - - if (!first_cell) - full_requested_width += tree_column->spacing; - - first_cell = FALSE; - } - - extra_space = cell_area->width - full_requested_width; - if (extra_space < 0) - extra_space = 0; - else if (extra_space > 0 && expand_cell_count > 0) - extra_space /= expand_cell_count; - - /* iterate list for GTK_PACK_START cells */ - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data; - - if (info->pack == GTK_PACK_END) - continue; - - if (! gtk_cell_renderer_get_visible (info->cell)) - continue; - - if ((info->has_focus || special_cells == 1) && cursor_row) - flags |= GTK_CELL_RENDERER_FOCUSED; - else - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - info->real_width = info->requested_width + (info->expand?extra_space:0); - - /* We constrain ourselves to only the width available */ - if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width) - { - info->real_width = cell_area->x + cell_area->width - real_cell_area.x; - } - - if (real_cell_area.x > cell_area->x + cell_area->width) - break; - - real_cell_area.width = info->real_width; - real_cell_area.width -= 2 * focus_line_width; - - if (list->next) - { - real_background_area.width = info->real_width + depth; - } - else - { - /* fill the rest of background for the last cell */ - real_background_area.width = background_area->x + background_area->width - real_background_area.x; - } - - rtl_cell_area = real_cell_area; - rtl_background_area = real_background_area; - - if (rtl) - { - rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width; - rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width; - } - - /* RENDER */ - if (action == CELL_ACTION_RENDER) - { - gtk_cell_renderer_render (info->cell, - cr, - tree_column->tree_view, - &rtl_background_area, - &rtl_cell_area, - flags); - } - /* FOCUS */ - else if (action == CELL_ACTION_FOCUS) - { - gint x_offset, y_offset, width, height; - - gtk_cell_renderer_get_preferred_height (info->cell, tree_column->tree_view, - NULL, &height); - - gtk_cell_renderer_get_preferred_width (info->cell, tree_column->tree_view, - NULL, &width); - - - gtk_cell_renderer_get_padding (info->cell, - &x_offset, &y_offset); - - if (special_cells > 1) - { - if (info->has_focus) - { - min_x = rtl_cell_area.x + x_offset; - max_x = min_x + width; - min_y = rtl_cell_area.y + y_offset; - max_y = min_y + height; - } - } - else - { - if (min_x > (rtl_cell_area.x + x_offset)) - min_x = rtl_cell_area.x + x_offset; - if (max_x < rtl_cell_area.x + x_offset + width) - max_x = rtl_cell_area.x + x_offset + width; - if (min_y > (rtl_cell_area.y + y_offset)) - min_y = rtl_cell_area.y + y_offset; - if (max_y < rtl_cell_area.y + y_offset + height) - max_y = rtl_cell_area.y + y_offset + height; - } - } - /* EVENT */ - else if (action == CELL_ACTION_EVENT) - { - gboolean try_event = FALSE; - - if (event) - { - if (special_cells == 1) - { - /* only 1 activatable cell -> whole column can activate */ - if (cell_area->x <= ((GdkEventButton *)event)->x && - cell_area->x + cell_area->width > ((GdkEventButton *)event)->x) - try_event = TRUE; - } - else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x && - rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x) - /* only activate cell if the user clicked on an individual - * cell - */ - try_event = TRUE; - } - else if (special_cells > 1 && info->has_focus) - try_event = TRUE; - else if (special_cells == 1) - try_event = TRUE; - - if (try_event) - { - gboolean visible, mode; - - g_object_get (info->cell, - "visible", &visible, - "mode", &mode, - NULL); - if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) - { - if (gtk_cell_renderer_activate (info->cell, - event, - tree_column->tree_view, - path_string, - &rtl_background_area, - &rtl_cell_area, - flags)) - { - flags &= ~GTK_CELL_RENDERER_FOCUSED; - return TRUE; - } - } - else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE) - { - *editable_widget = - gtk_cell_renderer_start_editing (info->cell, - event, - tree_column->tree_view, - path_string, - &rtl_background_area, - &rtl_cell_area, - flags); - - if (*editable_widget != NULL) - { - g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE); - info->in_editing_mode = TRUE; - pspp_sheet_view_column_focus_cell (tree_column, info->cell); - - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - return TRUE; - } - } - } - } - - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing); - real_background_area.x += real_background_area.width + tree_column->spacing; - - /* Only needed for first cell */ - depth = 0; - } - - /* iterate list for PACK_END cells */ - for (list = g_list_last (tree_column->cell_list); list; list = list->prev) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data; - - if (info->pack == GTK_PACK_START) - continue; - - if (! gtk_cell_renderer_get_visible (info->cell)) - continue; - - if ((info->has_focus || special_cells == 1) && cursor_row) - flags |= GTK_CELL_RENDERER_FOCUSED; - else - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - info->real_width = info->requested_width + (info->expand?extra_space:0); - - /* We constrain ourselves to only the width available */ - if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width) - { - info->real_width = cell_area->x + cell_area->width - real_cell_area.x; - } - - if (real_cell_area.x > cell_area->x + cell_area->width) - break; - - real_cell_area.width = info->real_width; - real_cell_area.width -= 2 * focus_line_width; - real_background_area.width = info->real_width + depth; - - rtl_cell_area = real_cell_area; - rtl_background_area = real_background_area; - if (rtl) - { - rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width; - rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width; - } - - /* RENDER */ - if (action == CELL_ACTION_RENDER) - { - gtk_cell_renderer_render (info->cell, - cr, - tree_column->tree_view, - &rtl_background_area, - &rtl_cell_area, - flags); - } - /* FOCUS */ - else if (action == CELL_ACTION_FOCUS) - { - gint x_offset, y_offset, width, height; - - gtk_cell_renderer_get_preferred_height (info->cell, tree_column->tree_view, - NULL, &height); - - gtk_cell_renderer_get_preferred_width (info->cell, tree_column->tree_view, - NULL, &width); - - - gtk_cell_renderer_get_padding (info->cell, - &x_offset, &y_offset); - - if (special_cells > 1) - { - if (info->has_focus) - { - min_x = rtl_cell_area.x + x_offset; - max_x = min_x + width; - min_y = rtl_cell_area.y + y_offset; - max_y = min_y + height; - } - } - else - { - if (min_x > (rtl_cell_area.x + x_offset)) - min_x = rtl_cell_area.x + x_offset; - if (max_x < rtl_cell_area.x + x_offset + width) - max_x = rtl_cell_area.x + x_offset + width; - if (min_y > (rtl_cell_area.y + y_offset)) - min_y = rtl_cell_area.y + y_offset; - if (max_y < rtl_cell_area.y + y_offset + height) - max_y = rtl_cell_area.y + y_offset + height; - } - } - /* EVENT */ - else if (action == CELL_ACTION_EVENT) - { - gboolean try_event = FALSE; - - if (event) - { - if (special_cells == 1) - { - /* only 1 activatable cell -> whole column can activate */ - if (cell_area->x <= ((GdkEventButton *)event)->x && - cell_area->x + cell_area->width > ((GdkEventButton *)event)->x) - try_event = TRUE; - } - else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x && - rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x) - /* only activate cell if the user clicked on an individual - * cell - */ - try_event = TRUE; - } - else if (special_cells > 1 && info->has_focus) - try_event = TRUE; - else if (special_cells == 1) - try_event = TRUE; - - if (try_event) - { - gboolean visible, mode; - - g_object_get (info->cell, - "visible", &visible, - "mode", &mode, - NULL); - if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) - { - if (gtk_cell_renderer_activate (info->cell, - event, - tree_column->tree_view, - path_string, - &rtl_background_area, - &rtl_cell_area, - flags)) - { - flags &= ~GTK_CELL_RENDERER_FOCUSED; - return TRUE; - } - } - else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE) - { - *editable_widget = - gtk_cell_renderer_start_editing (info->cell, - event, - tree_column->tree_view, - path_string, - &rtl_background_area, - &rtl_cell_area, - flags); - - if (*editable_widget != NULL) - { - g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE); - info->in_editing_mode = TRUE; - pspp_sheet_view_column_focus_cell (tree_column, info->cell); - - flags &= ~GTK_CELL_RENDERER_FOCUSED; - return TRUE; - } - } - } - } - - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing); - real_background_area.x += (real_background_area.width + tree_column->spacing); - - /* Only needed for first cell */ - depth = 0; - } - - /* fill focus_rectangle when required */ - if (action == CELL_ACTION_FOCUS) - { - if (min_x >= max_x || min_y >= max_y) - { - *focus_rectangle = *cell_area; - /* don't change the focus_rectangle, just draw it nicely inside - * the cell area */ - } - else - { - focus_rectangle->x = min_x - focus_line_width; - focus_rectangle->y = min_y - focus_line_width; - focus_rectangle->width = (max_x - min_x) + 2 * focus_line_width; - focus_rectangle->height = (max_y - min_y) + 2 * focus_line_width; - } - } - - return FALSE; -} - -/** - * pspp_sheet_view_column_cell_render: - * @tree_column: A #PsppSheetViewColumn. - * @window: a #GdkDrawable to draw to - * @background_area: entire cell area (including tree expanders and maybe padding on the sides) - * @cell_area: area normally rendered by a cell renderer - * @flags: flags that affect rendering - * - * Renders the cell contained by #tree_column. This is used primarily by the - * #PsppSheetView. - **/ -void -_pspp_sheet_view_column_cell_render (PsppSheetViewColumn *tree_column, - cairo_t *cr, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (background_area != NULL); - g_return_if_fail (cell_area != NULL); - - pspp_sheet_view_column_cell_process_action (tree_column, - cr, - background_area, - cell_area, - flags, - CELL_ACTION_RENDER, - NULL, NULL, NULL, NULL); -} - -gboolean -_pspp_sheet_view_column_cell_event (PsppSheetViewColumn *tree_column, - GtkCellEditable **editable_widget, - GdkEvent *event, - gchar *path_string, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - return pspp_sheet_view_column_cell_process_action (tree_column, - NULL, - background_area, - cell_area, - flags, - CELL_ACTION_EVENT, - NULL, - editable_widget, - event, - path_string); -} - -void -_pspp_sheet_view_column_get_focus_area (PsppSheetViewColumn *tree_column, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GdkRectangle *focus_area) -{ - pspp_sheet_view_column_cell_process_action (tree_column, - NULL, - background_area, - cell_area, - 0, - CELL_ACTION_FOCUS, - focus_area, - NULL, NULL, NULL); -} - - -/* cell list manipulation */ -static GList * -pspp_sheet_view_column_cell_first (PsppSheetViewColumn *tree_column) -{ - GList *list = tree_column->cell_list; - - /* first GTK_PACK_START cell we find */ - for ( ; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = list->data; - if (info->pack == GTK_PACK_START) - return list; - } - - /* hmm, else the *last* GTK_PACK_END cell */ - list = g_list_last (tree_column->cell_list); - - for ( ; list; list = list->prev) - { - PsppSheetViewColumnCellInfo *info = list->data; - if (info->pack == GTK_PACK_END) - return list; - } - - return NULL; -} - -static GList * -pspp_sheet_view_column_cell_last (PsppSheetViewColumn *tree_column) -{ - GList *list = tree_column->cell_list; - - /* *first* GTK_PACK_END cell we find */ - for ( ; list ; list = list->next) - { - PsppSheetViewColumnCellInfo *info = list->data; - if (info->pack == GTK_PACK_END) - return list; - } - - /* hmm, else the last GTK_PACK_START cell */ - list = g_list_last (tree_column->cell_list); - - for ( ; list; list = list->prev) - { - PsppSheetViewColumnCellInfo *info = list->data; - if (info->pack == GTK_PACK_START) - return list; - } - - return NULL; -} - - -static GList * -pspp_sheet_view_column_cell_next (PsppSheetViewColumn *tree_column, - GList *current) -{ - GList *list; - PsppSheetViewColumnCellInfo *info = current->data; - - if (info->pack == GTK_PACK_START) - { - for (list = current->next; list; list = list->next) - { - PsppSheetViewColumnCellInfo *inf = list->data; - if (inf->pack == GTK_PACK_START) - return list; - } - - /* out of GTK_PACK_START cells, get *last* GTK_PACK_END one */ - list = g_list_last (tree_column->cell_list); - for (; list; list = list->prev) - { - PsppSheetViewColumnCellInfo *inf = list->data; - if (inf->pack == GTK_PACK_END) - return list; - } - } - - for (list = current->prev; list; list = list->prev) - { - PsppSheetViewColumnCellInfo *inf = list->data; - if (inf->pack == GTK_PACK_END) - return list; - } - - return NULL; -} - -static GList * -pspp_sheet_view_column_cell_prev (PsppSheetViewColumn *tree_column, - GList *current) -{ - GList *list; - PsppSheetViewColumnCellInfo *info = current->data; - - if (info->pack == GTK_PACK_END) - { - for (list = current->next; list; list = list->next) - { - PsppSheetViewColumnCellInfo *inf = list->data; - if (inf->pack == GTK_PACK_END) - return list; - } - - /* out of GTK_PACK_END, get last GTK_PACK_START one */ - list = g_list_last (tree_column->cell_list); - for ( ; list; list = list->prev) - { - PsppSheetViewColumnCellInfo *inf = list->data; - if (inf->pack == GTK_PACK_START) - return list; - } - } - - for (list = current->prev; list; list = list->prev) - { - PsppSheetViewColumnCellInfo *inf = list->data; - if (inf->pack == GTK_PACK_START) - return list; - } - - return NULL; -} - -gboolean -_pspp_sheet_view_column_cell_focus (PsppSheetViewColumn *tree_column, - gint direction, - gboolean left, - gboolean right) -{ - gint count; - gboolean rtl; - - count = _pspp_sheet_view_column_count_special_cells (tree_column); - rtl = gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL; - - /* if we are the current focus column and have multiple editable cells, - * try to select the next one, else move the focus to the next column - */ - if (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->focus_column == tree_column) - { - if (count > 1) - { - GList *next, *prev; - GList *list = tree_column->cell_list; - PsppSheetViewColumnCellInfo *info = NULL; - - /* find current focussed cell */ - for ( ; list; list = list->next) - { - info = list->data; - if (info->has_focus) - break; - } - - /* not a focussed cell in the focus column? */ - if (!list || !info || !info->has_focus) - return FALSE; - - if (rtl) - { - prev = pspp_sheet_view_column_cell_next (tree_column, list); - next = pspp_sheet_view_column_cell_prev (tree_column, list); - } - else - { - next = pspp_sheet_view_column_cell_next (tree_column, list); - prev = pspp_sheet_view_column_cell_prev (tree_column, list); - } - - info->has_focus = FALSE; - if (direction > 0 && next) - { - info = next->data; - info->has_focus = TRUE; - return TRUE; - } - else if (direction > 0 && !next && !right) - { - /* keep focus on last cell */ - if (rtl) - info = pspp_sheet_view_column_cell_first (tree_column)->data; - else - info = pspp_sheet_view_column_cell_last (tree_column)->data; - - info->has_focus = TRUE; - return TRUE; - } - else if (direction < 0 && prev) - { - info = prev->data; - info->has_focus = TRUE; - return TRUE; - } - else if (direction < 0 && !prev && !left) - { - /* keep focus on first cell */ - if (rtl) - info = pspp_sheet_view_column_cell_last (tree_column)->data; - else - info = pspp_sheet_view_column_cell_first (tree_column)->data; - - info->has_focus = TRUE; - return TRUE; - } - } - return FALSE; - } - - /* we get focus, if we have multiple editable cells, give the correct one - * focus - */ - if (count > 1) - { - GList *list = tree_column->cell_list; - - /* clear focus first */ - for ( ; list ; list = list->next) - { - PsppSheetViewColumnCellInfo *info = list->data; - if (info->has_focus) - info->has_focus = FALSE; - } - - list = NULL; - if (rtl) - { - if (direction > 0) - list = pspp_sheet_view_column_cell_last (tree_column); - else if (direction < 0) - list = pspp_sheet_view_column_cell_first (tree_column); - } - else - { - if (direction > 0) - list = pspp_sheet_view_column_cell_first (tree_column); - else if (direction < 0) - list = pspp_sheet_view_column_cell_last (tree_column); - } - - if (list) - ((PsppSheetViewColumnCellInfo *) list->data)->has_focus = TRUE; - } - - return TRUE; -} - -void -_pspp_sheet_view_column_cell_draw_focus (PsppSheetViewColumn *tree_column, - cairo_t *cr, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - guint flags) -{ - gint focus_line_width; - GtkStateType cell_state; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view), - "focus-line-width", &focus_line_width, NULL); - if (tree_column->editable_widget) - { - /* This function is only called on the editable row when editing. - */ -#if 0 - gtk_paint_focus (tree_column->tree_view->style, - window, - gtk_widget_get_state (tree_column->tree_view), - NULL, - tree_column->tree_view, - "treeview", - cell_area->x - focus_line_width, - cell_area->y - focus_line_width, - cell_area->width + 2 * focus_line_width, - cell_area->height + 2 * focus_line_width); -#endif - } - else - { - GdkRectangle focus_rectangle; - pspp_sheet_view_column_cell_process_action (tree_column, - cr, - background_area, - cell_area, - flags, - CELL_ACTION_FOCUS, - &focus_rectangle, - NULL, NULL, NULL); - - cell_state = flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED : - (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT : - (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL)); - - gtk_paint_focus (gtk_widget_get_style (GTK_WIDGET (tree_column->tree_view)), - cr, - cell_state, - tree_column->tree_view, - "treeview", - focus_rectangle.x, - focus_rectangle.y, - focus_rectangle.width, - focus_rectangle.height); - } -} - -/** - * pspp_sheet_view_column_cell_is_visible: - * @tree_column: A #PsppSheetViewColumn - * - * Returns %TRUE if any of the cells packed into the @tree_column are visible. - * For this to be meaningful, you must first initialize the cells with - * pspp_sheet_view_column_cell_set_cell_data() - * - * Return value: %TRUE, if any of the cells packed into the @tree_column are currently visible - **/ -gboolean -pspp_sheet_view_column_cell_is_visible (PsppSheetViewColumn *tree_column) -{ - GList *list; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE); - - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data; - - if (gtk_cell_renderer_get_visible (info->cell)) - return TRUE; - } - - return FALSE; -} - -/** - * pspp_sheet_view_column_focus_cell: - * @tree_column: A #PsppSheetViewColumn - * @cell: A #GtkCellRenderer - * - * Sets the current keyboard focus to be at @cell, if the column contains - * 2 or more editable and activatable cells. - * - * Since: 2.2 - **/ -void -pspp_sheet_view_column_focus_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell) -{ - GList *list; - gboolean found_cell = FALSE; - - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); - - if (_pspp_sheet_view_column_count_special_cells (tree_column) < 2) - return; - - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = list->data; - - if (info->cell == cell) - { - info->has_focus = TRUE; - found_cell = TRUE; - break; - } - } - - if (found_cell) - { - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = list->data; - - if (info->cell != cell) - info->has_focus = FALSE; - } - - /* FIXME: redraw? */ - } -} - -void -_pspp_sheet_view_column_cell_set_dirty (PsppSheetViewColumn *tree_column) -{ - GList *list; - - for (list = tree_column->cell_list; list; list = list->next) - { - PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data; - - info->requested_width = 0; - } - tree_column->dirty = TRUE; - tree_column->requested_width = -1; - tree_column->width = 0; - - if (tree_column->tree_view && - PSPP_SHEET_VIEW (tree_column->tree_view)->priv->resized && - gtk_widget_get_realized (tree_column->tree_view)) - { - PSPP_SHEET_VIEW (tree_column->tree_view)->priv->resized = FALSE; - _pspp_sheet_view_install_mark_rows_col_dirty (PSPP_SHEET_VIEW (tree_column->tree_view)); - gtk_widget_queue_resize (tree_column->tree_view); - } -} - -void -_pspp_sheet_view_column_start_editing (PsppSheetViewColumn *tree_column, - GtkCellEditable *cell_editable) -{ - g_return_if_fail (tree_column->editable_widget == NULL); - - tree_column->editable_widget = cell_editable; -} - -void -_pspp_sheet_view_column_stop_editing (PsppSheetViewColumn *tree_column) -{ - GList *list; - - g_return_if_fail (tree_column->editable_widget != NULL); - - tree_column->editable_widget = NULL; - for (list = tree_column->cell_list; list; list = list->next) - ((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode = FALSE; -} - -void -_pspp_sheet_view_column_get_neighbor_sizes (PsppSheetViewColumn *column, - GtkCellRenderer *cell, - gint *left, - gint *right) -{ - GList *list; - PsppSheetViewColumnCellInfo *info; - gint l, r; - gboolean rtl; - - l = r = 0; - - list = pspp_sheet_view_column_cell_first (column); - - while (list) - { - info = (PsppSheetViewColumnCellInfo *)list->data; - - list = pspp_sheet_view_column_cell_next (column, list); - - if (info->cell == cell) - break; - - if (gtk_cell_renderer_get_visible (info->cell)) - l += info->real_width + column->spacing; - } - - while (list) - { - info = (PsppSheetViewColumnCellInfo *)list->data; - - list = pspp_sheet_view_column_cell_next (column, list); - - if (gtk_cell_renderer_get_visible (info->cell)) - r += info->real_width + column->spacing; - } - - rtl = (gtk_widget_get_direction (GTK_WIDGET (column->tree_view)) == GTK_TEXT_DIR_RTL); - if (left) - *left = rtl ? r : l; - - if (right) - *right = rtl ? l : r; -} - -/** - * pspp_sheet_view_column_cell_get_position: - * @tree_column: a #PsppSheetViewColumn - * @cell_renderer: a #GtkCellRenderer - * @start_pos: return location for the horizontal position of @cell within - * @tree_column, may be %NULL - * @width: return location for the width of @cell, may be %NULL - * - * Obtains the horizontal position and size of a cell in a column. If the - * cell is not found in the column, @start_pos and @width are not changed and - * %FALSE is returned. - * - * Return value: %TRUE if @cell belongs to @tree_column. - */ -gboolean -pspp_sheet_view_column_cell_get_position (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - gint *start_pos, - gint *width) -{ - GList *list; - gint current_x = 0; - gboolean found_cell = FALSE; - PsppSheetViewColumnCellInfo *cellinfo = NULL; - - list = pspp_sheet_view_column_cell_first (tree_column); - for (; list; list = pspp_sheet_view_column_cell_next (tree_column, list)) - { - cellinfo = list->data; - if (cellinfo->cell == cell_renderer) - { - found_cell = TRUE; - break; - } - - if (gtk_cell_renderer_get_visible (cellinfo->cell)) - current_x += cellinfo->real_width; - } - - if (found_cell) - { - if (start_pos) - *start_pos = current_x; - if (width) - *width = cellinfo->real_width; - } - - return found_cell; -} - -/** - * pspp_sheet_view_column_queue_resize: - * @tree_column: A #PsppSheetViewColumn - * - * Flags the column, and the cell renderers added to this column, to have - * their sizes renegotiated. - * - * Since: 2.8 - **/ -void -pspp_sheet_view_column_queue_resize (PsppSheetViewColumn *tree_column) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column)); - - if (tree_column->tree_view) - _pspp_sheet_view_column_cell_set_dirty (tree_column); -} - -/** - * pspp_sheet_view_column_get_tree_view: - * @tree_column: A #PsppSheetViewColumn - * - * Returns the #PsppSheetView wherein @tree_column has been inserted. If - * @column is currently not inserted in any tree view, %NULL is - * returned. - * - * Return value: The tree view wherein @column has been inserted if any, - * %NULL otherwise. - * - * Since: 2.12 - */ -GtkWidget * -pspp_sheet_view_column_get_tree_view (PsppSheetViewColumn *tree_column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL); - - return tree_column->tree_view; -} - -typedef struct { - GtkCellLayout *cell_layout; - GtkCellRenderer *renderer; - gchar *attr_name; -} AttributesSubParserData; - -static void -attributes_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **names, - const gchar **values, - gpointer user_data, - GError **error) -{ - AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data; - guint i; - - if (strcmp (element_name, "attribute") == 0) - { - for (i = 0; names[i]; i++) - if (strcmp (names[i], "name") == 0) - parser_data->attr_name = g_strdup (values[i]); - } - else if (strcmp (element_name, "attributes") == 0) - return; - else - g_warning ("Unsupported tag for GtkCellLayout: %s\n", element_name); -} - -static void -attributes_text_element (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data; - glong l; - gchar *endptr; - gchar *string; - - if (!parser_data->attr_name) - return; - - errno = 0; - string = g_strndup (text, text_len); - l = strtol (string, &endptr, 0); - if (errno || endptr == string) - { - g_set_error (error, - GTK_BUILDER_ERROR, - GTK_BUILDER_ERROR_INVALID_VALUE, - "Could not parse integer `%s'", - string); - g_free (string); - return; - } - g_free (string); - - gtk_cell_layout_add_attribute (parser_data->cell_layout, - parser_data->renderer, - parser_data->attr_name, l); - g_free (parser_data->attr_name); - parser_data->attr_name = NULL; -} - -static const GMarkupParser attributes_parser = - { - attributes_start_element, - NULL, - attributes_text_element, - }; - -static gboolean -_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - GMarkupParser *parser, - gpointer *data) -{ - AttributesSubParserData *parser_data; - - if (!child) - return FALSE; - - if (strcmp (tagname, "attributes") == 0) - { - parser_data = g_slice_new0 (AttributesSubParserData); - parser_data->cell_layout = GTK_CELL_LAYOUT (buildable); - parser_data->renderer = GTK_CELL_RENDERER (child); - parser_data->attr_name = NULL; - - *parser = attributes_parser; - *data = parser_data; - return TRUE; - } - - return FALSE; -} - -static void -_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - gpointer *data) -{ - AttributesSubParserData *parser_data; - - parser_data = (AttributesSubParserData*)data; - g_assert (!parser_data->attr_name); - g_slice_free (AttributesSubParserData, parser_data); -} - -static void -_cell_layout_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *type) -{ - GtkCellLayoutIface *iface; - - g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable)); - g_return_if_fail (GTK_IS_CELL_RENDERER (child)); - - iface = GTK_CELL_LAYOUT_GET_IFACE (buildable); - g_return_if_fail (iface->pack_start != NULL); - iface->pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE); -} - -void -pspp_sheet_view_column_size_request (PsppSheetViewColumn *tree_column, - GtkRequisition *request) -{ - GtkWidget *base = GTK_WIDGET (tree_column->tree_view); - GtkRequisition label_req; - GtkRequisition align_req; - GtkRequisition arrow_req; - GtkRequisition hbox_req; - GtkStyle **button_style; - - if (tree_column->button) - { - gtk_widget_get_preferred_size (tree_column->button, NULL, request); - return; - } - - facade_label_get_size_request (0, 0, base, tree_column->title, &label_req); - facade_alignment_get_size_request (0, 0, 0, 0, 0, &label_req, &align_req); - facade_arrow_get_size_request (0, 0, &arrow_req); - - facade_hbox_get_base_size_request (0, 2, 2, &hbox_req); - facade_hbox_add_child_size_request (0, &arrow_req, 0, &hbox_req); - facade_hbox_add_child_size_request (0, &align_req, 0, &hbox_req); - - button_style = &PSPP_SHEET_VIEW (tree_column->tree_view)->priv->button_style; - if (*button_style == NULL) - { - *button_style = facade_get_style (base, GTK_TYPE_BUTTON, 0); - g_object_ref (*button_style); - } - facade_button_get_size_request (0, base, *button_style, &hbox_req, request); -} - -void -pspp_sheet_view_column_size_allocate (PsppSheetViewColumn *tree_column, - GtkAllocation *allocation) -{ - tree_column->allocation = *allocation; - if (tree_column->button) - gtk_widget_size_allocate (tree_column->button, allocation); -} - -gboolean -pspp_sheet_view_column_can_focus (PsppSheetViewColumn *tree_column) -{ - return tree_column->reorderable || tree_column->clickable; -} diff --git a/src/ui/gui/pspp-sheet-view-column.h b/src/ui/gui/pspp-sheet-view-column.h deleted file mode 100644 index 75482e9497..0000000000 --- a/src/ui/gui/pspp-sheet-view-column.h +++ /dev/null @@ -1,267 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -/* gtktreeviewcolumn.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __PSPP_SHEET_VIEW_COLUMN_H__ -#define __PSPP_SHEET_VIEW_COLUMN_H__ - -#include - -G_BEGIN_DECLS - -#define PSPP_TYPE_SHEET_VIEW_COLUMN (pspp_sheet_view_column_get_type ()) -#define PSPP_SHEET_VIEW_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPP_TYPE_SHEET_VIEW_COLUMN, PsppSheetViewColumn)) -#define PSPP_SHEET_VIEW_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PSPP_TYPE_SHEET_VIEW_COLUMN, PsppSheetViewColumnClass)) -#define PSPP_IS_SHEET_VIEW_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPP_TYPE_SHEET_VIEW_COLUMN)) -#define PSPP_IS_SHEET_VIEW_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPP_TYPE_SHEET_VIEW_COLUMN)) -#define PSPP_SHEET_VIEW_COLUMN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PSPP_TYPE_SHEET_VIEW_COLUMN, PsppSheetViewColumnClass)) - -typedef struct _PsppSheetViewColumn PsppSheetViewColumn; -typedef struct _PsppSheetViewColumnClass PsppSheetViewColumnClass; - -typedef void (* PsppSheetCellDataFunc) (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); - - -struct _PsppSheetViewColumn -{ - GObject parent; - - GtkWidget *tree_view; - GtkWidget *button; - GtkWidget *child; - GtkWidget *bin; - GdkWindow *window; - GtkCellEditable *editable_widget; - GtkAlign halign; - guint property_changed_signal; - gint spacing; - GtkAllocation allocation; - - /* Sizing fields */ - /* see gtk+/doc/tree-column-sizing.txt for more information on them */ - gint requested_width; - gint button_request; - gint resized_width; - gint width; - gint fixed_width; - gint min_width; - gint max_width; - - /* dragging columns */ - gint drag_x; - gint drag_y; - - gchar *title; - GList *cell_list; - - /* Sorting */ - guint sort_clicked_signal; - guint sort_column_changed_signal; - gint sort_column_id; - GtkSortType sort_order; - - /* Flags */ - guint visible : 1; - guint resizable : 1; - guint clickable : 1; - guint dirty : 1; - guint show_sort_indicator : 1; - guint maybe_reordered : 1; - guint reorderable : 1; - guint use_resized_width : 1; - guint expand : 1; - guint quick_edit : 1; - guint selected : 1; - guint selectable : 1; - guint row_head : 1; - guint tabbable : 1; -}; - -struct _PsppSheetViewColumnClass -{ - GObjectClass parent_class; - - gboolean (*clicked) (PsppSheetViewColumn *tree_column); - gboolean (*button_press_event) (PsppSheetViewColumn *, - GdkEventButton *); - - /* Padding for future expansion */ - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); -}; - -GType pspp_sheet_view_column_get_type (void) G_GNUC_CONST; -PsppSheetViewColumn *pspp_sheet_view_column_new (void); -PsppSheetViewColumn *pspp_sheet_view_column_new_with_attributes (const gchar *title, - GtkCellRenderer *cell, - ...) G_GNUC_NULL_TERMINATED; -void pspp_sheet_view_column_pack_start (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand); -void pspp_sheet_view_column_pack_end (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - gboolean expand); -void pspp_sheet_view_column_clear (PsppSheetViewColumn *tree_column); - -GList *pspp_sheet_view_column_get_cell_renderers (PsppSheetViewColumn *tree_column); - -void pspp_sheet_view_column_add_attribute (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - const gchar *attribute, - gint column); -void pspp_sheet_view_column_set_attributes (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - ...) G_GNUC_NULL_TERMINATED; -void pspp_sheet_view_column_set_cell_data_func (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - PsppSheetCellDataFunc func, - gpointer func_data, - GDestroyNotify destroy); -void pspp_sheet_view_column_clear_attributes (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer); -void pspp_sheet_view_column_set_spacing (PsppSheetViewColumn *tree_column, - gint spacing); -gint pspp_sheet_view_column_get_spacing (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_visible (PsppSheetViewColumn *tree_column, - gboolean visible); -gboolean pspp_sheet_view_column_get_visible (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_resizable (PsppSheetViewColumn *tree_column, - gboolean resizable); -gboolean pspp_sheet_view_column_get_resizable (PsppSheetViewColumn *tree_column); -gint pspp_sheet_view_column_get_width (PsppSheetViewColumn *tree_column); -gint pspp_sheet_view_column_get_fixed_width (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_fixed_width (PsppSheetViewColumn *tree_column, - gint fixed_width); -void pspp_sheet_view_column_set_min_width (PsppSheetViewColumn *tree_column, - gint min_width); -gint pspp_sheet_view_column_get_min_width (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_max_width (PsppSheetViewColumn *tree_column, - gint max_width); -gint pspp_sheet_view_column_get_max_width (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_clicked (PsppSheetViewColumn *tree_column); - - - -/* Options for manipulating the column headers - */ -void pspp_sheet_view_column_set_title (PsppSheetViewColumn *tree_column, - const gchar *title); -const gchar * pspp_sheet_view_column_get_title (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_expand (PsppSheetViewColumn *tree_column, - gboolean expand); -gboolean pspp_sheet_view_column_get_expand (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_clickable (PsppSheetViewColumn *tree_column, - gboolean clickable); -gboolean pspp_sheet_view_column_get_clickable (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_widget (PsppSheetViewColumn *tree_column, - GtkWidget *widget); -GtkWidget *pspp_sheet_view_column_get_widget (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_alignment (PsppSheetViewColumn *tree_columna, - GtkAlign xalign); -GtkAlign pspp_sheet_view_column_get_alignment (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_reorderable (PsppSheetViewColumn *tree_column, - gboolean reorderable); -gboolean pspp_sheet_view_column_get_reorderable (PsppSheetViewColumn *tree_column); - -void pspp_sheet_view_column_set_quick_edit (PsppSheetViewColumn *tree_column, - gboolean quick_edit); -gboolean pspp_sheet_view_column_get_quick_edit (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_selected (PsppSheetViewColumn *tree_column, - gboolean selected); -gboolean pspp_sheet_view_column_get_selected (PsppSheetViewColumn *tree_column); - -void pspp_sheet_view_column_set_selectable (PsppSheetViewColumn *tree_column, - gboolean selectable); -gboolean pspp_sheet_view_column_get_selectable (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_row_head (PsppSheetViewColumn *tree_column, - gboolean row_head); -gboolean pspp_sheet_view_column_get_row_head (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_tabbable (PsppSheetViewColumn *tree_column, - gboolean tabbable); -gboolean pspp_sheet_view_column_get_tabbable (PsppSheetViewColumn *tree_column); - - - -/* You probably only want to use pspp_sheet_view_column_set_sort_column_id. The - * other sorting functions exist primarily to let others do their own custom sorting. - */ -void pspp_sheet_view_column_set_sort_column_id (PsppSheetViewColumn *tree_column, - gint sort_column_id); -gint pspp_sheet_view_column_get_sort_column_id (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_sort_indicator (PsppSheetViewColumn *tree_column, - gboolean setting); -gboolean pspp_sheet_view_column_get_sort_indicator (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_set_sort_order (PsppSheetViewColumn *tree_column, - GtkSortType order); -GtkSortType pspp_sheet_view_column_get_sort_order (PsppSheetViewColumn *tree_column); - - -/* These functions are meant primarily for interaction between the PsppSheetView and the column. - */ -void pspp_sheet_view_column_cell_set_cell_data (PsppSheetViewColumn *tree_column, - GtkTreeModel *tree_model, - GtkTreeIter *iter); -void pspp_sheet_view_column_cell_get_size (PsppSheetViewColumn *tree_column, - const GdkRectangle *cell_area, - gint *x_offset, - gint *y_offset, - gint *width, - gint *height); -gboolean pspp_sheet_view_column_cell_is_visible (PsppSheetViewColumn *tree_column); -void pspp_sheet_view_column_focus_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell); -gboolean pspp_sheet_view_column_cell_get_position (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell_renderer, - gint *start_pos, - gint *width); -void pspp_sheet_view_column_queue_resize (PsppSheetViewColumn *tree_column); -GtkWidget *pspp_sheet_view_column_get_tree_view (PsppSheetViewColumn *tree_column); - -void pspp_sheet_view_column_size_request (PsppSheetViewColumn *tree_column, - GtkRequisition *requisition); - -void pspp_sheet_view_column_size_allocate (PsppSheetViewColumn *tree_column, - GtkAllocation *allocation); -gboolean pspp_sheet_view_column_can_focus (PsppSheetViewColumn *tree_column); - -G_END_DECLS - - -#endif /* __PSPP_SHEET_VIEW_COLUMN_H__ */ diff --git a/src/ui/gui/pspp-sheet-view.c b/src/ui/gui/pspp-sheet-view.c deleted file mode 100644 index e06ed49a0a..0000000000 --- a/src/ui/gui/pspp-sheet-view.c +++ /dev/null @@ -1,12532 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2013, 2015 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -/* gtktreeview.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -#include "ui/gui/pspp-sheet-private.h" - -#include -#include -#include -#include -#include - -#include "ui/gui/psppire-marshal.h" -#include "ui/gui/pspp-sheet-selection.h" - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -#define P_(STRING) STRING -#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB -#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB - -/* Many keyboard shortcuts for Mac are the same as for X - * except they use Command key instead of Control (e.g. Cut, - * Copy, Paste). This symbol is for those simple cases. */ -#ifndef GDK_WINDOWING_QUARTZ -#define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK -#else -#define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK -#endif - -#define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5) -#define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2) -#define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30 -#define SCROLL_EDGE_SIZE 15 -#define EXPANDER_EXTRA_PADDING 4 -#define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000 - -/* The "background" areas of all rows/cells add up to cover the entire tree. - * The background includes all inter-row and inter-cell spacing. - * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(), - * i.e. just the cells, no spacing. - */ - -#define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height) -#define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator)) - -/* Translate from bin_window coordinates to rbtree (tree coordinates) and - * vice versa. - */ -#define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy) -#define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy) - -/* This is in bin_window coordinates */ -#define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node)))) -#define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2) - -#define ROW_HEIGHT(tree_view) \ - ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size) - - -typedef struct _PsppSheetViewChild PsppSheetViewChild; -struct _PsppSheetViewChild -{ - GtkWidget *widget; - PsppSheetViewColumn *column; - int node; -}; - - -typedef struct _TreeViewDragInfo TreeViewDragInfo; -struct _TreeViewDragInfo -{ - GdkModifierType start_button_mask; - GtkTargetList *_unused_source_target_list; - GdkDragAction source_actions; - - GtkTargetList *_unused_dest_target_list; - - guint source_set : 1; - guint dest_set : 1; -}; - - -/* Signals */ -enum -{ - ROW_ACTIVATED, - COLUMNS_CHANGED, - CURSOR_CHANGED, - MOVE_CURSOR, - SELECT_ALL, - UNSELECT_ALL, - SELECT_CURSOR_ROW, - TOGGLE_CURSOR_ROW, - START_INTERACTIVE_SEARCH, - LAST_SIGNAL -}; - -/* Properties */ -enum { - PROP_0, - PROP_MODEL, - PROP_HADJUSTMENT, - PROP_VADJUSTMENT, - PROP_HSCROLL_POLICY, - PROP_VSCROLL_POLICY, - PROP_HEADERS_VISIBLE, - PROP_HEADERS_CLICKABLE, - PROP_REORDERABLE, - PROP_RULES_HINT, - PROP_ENABLE_SEARCH, - PROP_SEARCH_COLUMN, - PROP_HOVER_SELECTION, - PROP_RUBBER_BANDING, - PROP_ENABLE_GRID_LINES, - PROP_TOOLTIP_COLUMN, - PROP_SPECIAL_CELLS, - PROP_FIXED_HEIGHT, - PROP_FIXED_HEIGHT_SET -}; - -/* object signals */ -static void pspp_sheet_view_finalize (GObject *object); -static void pspp_sheet_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void pspp_sheet_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -static void pspp_sheet_view_dispose (GObject *object); - -/* gtkwidget signals */ -static void pspp_sheet_view_realize (GtkWidget *widget); -static void pspp_sheet_view_unrealize (GtkWidget *widget); -static void pspp_sheet_view_map (GtkWidget *widget); -static void pspp_sheet_view_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void pspp_sheet_view_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); -static gboolean pspp_sheet_view_draw (GtkWidget *widget, - cairo_t *cr); -static gboolean pspp_sheet_view_key_press (GtkWidget *widget, - GdkEventKey *event); -static gboolean pspp_sheet_view_key_release (GtkWidget *widget, - GdkEventKey *event); -static gboolean pspp_sheet_view_motion (GtkWidget *widget, - GdkEventMotion *event); -static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget, - GdkEventCrossing *event); -static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget, - GdkEventCrossing *event); -static gboolean pspp_sheet_view_button_press (GtkWidget *widget, - GdkEventButton *event); -static gboolean pspp_sheet_view_button_release (GtkWidget *widget, - GdkEventButton *event); -static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget, - GdkEventGrabBroken *event); - -static void pspp_sheet_view_set_focus_child (GtkContainer *container, - GtkWidget *child); -static gint pspp_sheet_view_focus_out (GtkWidget *widget, - GdkEventFocus *event); -static gint pspp_sheet_view_focus (GtkWidget *widget, - GtkDirectionType direction); -static void pspp_sheet_view_grab_focus (GtkWidget *widget); -static void pspp_sheet_view_style_updated (GtkWidget *widget); -static void pspp_sheet_view_grab_notify (GtkWidget *widget, - gboolean was_grabbed); -static void pspp_sheet_view_state_changed (GtkWidget *widget, - GtkStateType previous_state); - -/* container signals */ -static void pspp_sheet_view_remove (GtkContainer *container, - GtkWidget *widget); -static void pspp_sheet_view_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data); - -/* Source side drag signals */ -static void pspp_sheet_view_drag_begin (GtkWidget *widget, - GdkDragContext *context); -static void pspp_sheet_view_drag_end (GtkWidget *widget, - GdkDragContext *context); -static void pspp_sheet_view_drag_data_get (GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time); -static void pspp_sheet_view_drag_data_delete (GtkWidget *widget, - GdkDragContext *context); - -/* Target side drag signals */ -static void pspp_sheet_view_drag_leave (GtkWidget *widget, - GdkDragContext *context, - guint time); -static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time); -static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time); -static void pspp_sheet_view_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *selection_data, - guint info, - guint time); - -/* tree_model signals */ -static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view, - GtkAdjustment *hadj, - GtkAdjustment *vadj); -static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view, - GtkMovementStep step, - gint count); -static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view); -static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view); -static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view, - gboolean start_editing, - PsppSheetSelectMode mode); -static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view); -static void pspp_sheet_view_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -static void pspp_sheet_view_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -static void pspp_sheet_view_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data); -static void pspp_sheet_view_rows_reordered (GtkTreeModel *model, - GtkTreePath *parent, - GtkTreeIter *iter, - gint *new_order, - gpointer data); - -/* Incremental reflow */ -static gint validate_row (PsppSheetView *tree_view, - int node, - GtkTreeIter *iter, - GtkTreePath *path); -static void validate_visible_area (PsppSheetView *tree_view); -static gboolean validate_rows_handler (PsppSheetView *tree_view); -static gboolean presize_handler_callback (gpointer data); -static void install_presize_handler (PsppSheetView *tree_view); -static void install_scroll_sync_handler (PsppSheetView *tree_view); -static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view, - GtkTreePath *path, - gint offset); -static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view); -static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view); -static void invalidate_empty_focus (PsppSheetView *tree_view); - -/* Internal functions */ -static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *); -static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *); -static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment); -static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment); -static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set, - guint keyval, - guint modmask, - gboolean add_shifted_binding, - GtkMovementStep step, - gint count); -static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view, - GtkTreePath *path, - const GdkRectangle *clip_rect); -static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view, - gint i, - gint *x); -static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment, - PsppSheetView *tree_view); -static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view, - int node); -static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - gboolean focus_to_cell); -static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view, - GdkEventMotion *event); -static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view); -static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode); -static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode); -static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode); -static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode); -static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view, - gint count); -static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode); -static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view, - GtkTreePath *path, - gboolean clear_and_select, - gboolean clamp_node, - PsppSheetSelectMode mode); -static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view); -static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view); -static void update_prelight (PsppSheetView *tree_view, - int x, - int y); -static void initialize_fixed_height_mode (PsppSheetView *tree_view); - -/* interactive search */ -static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view); -static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog, - PsppSheetView *tree_view); -static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view, - GtkWidget *search_dialog, - gpointer user_data); -static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry, - GtkMenu *menu, - gpointer data); -static void pspp_sheet_view_search_activate (GtkEntry *entry, - PsppSheetView *tree_view); -static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data); -static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget, - gpointer data); -static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget, - GdkEventAny *event, - PsppSheetView *tree_view); -static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget, - GdkEventButton *event, - PsppSheetView *tree_view); -static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry, - GdkEventScroll *event, - PsppSheetView *tree_view); -static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry, - GdkEventKey *event, - PsppSheetView *tree_view); -static gboolean pspp_sheet_view_search_move (GtkWidget *window, - PsppSheetView *tree_view, - gboolean up); -static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model, - gint column, - const gchar *key, - GtkTreeIter *iter, - gpointer search_data); -static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model, - PsppSheetSelection *selection, - GtkTreeIter *iter, - const gchar *text, - gint *count, - gint n); -static void pspp_sheet_view_search_init (GtkWidget *entry, - PsppSheetView *tree_view); -static void pspp_sheet_view_put (PsppSheetView *tree_view, - GtkWidget *child_widget, - GtkTreePath *path, - PsppSheetViewColumn *column); -static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view, - GtkTreePath *cursor_path); -static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *, - GdkEventButton *, - PsppSheetView *); -static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *); -static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - GtkTreePath *path, - GtkCellEditable *cell_editable, - GdkRectangle *cell_area, - GdkEvent *event, - guint flags); -static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view, - gboolean keybinding); -static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view); -static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - gint drop_position); -static void -pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - const GdkRectangle *background_area, - gboolean subtract_focus_rect, - GdkRectangle *cell_area); -static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view, - gint height, - int *new_node); - -/* GtkBuildable */ -static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view, - GtkBuilder *builder, - GObject *child, - const gchar *type); -static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface); - - -static gboolean scroll_row_timeout (gpointer data); -static void add_scroll_timeout (PsppSheetView *tree_view); -static void remove_scroll_timeout (PsppSheetView *tree_view); - -static guint tree_view_signals [LAST_SIGNAL] = { 0 }; - -static GtkBindingSet *edit_bindings; - - - -/* GType Methods - */ - -G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER, - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - pspp_sheet_view_buildable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)) - -static void -pspp_sheet_view_get_preferred_width (GtkWidget *widget, - gint *minimal_width, - gint *natural_width) -{ - GtkRequisition requisition; - - pspp_sheet_view_size_request (widget, &requisition); - - *minimal_width = *natural_width = requisition.width; -} - -static void -pspp_sheet_view_get_preferred_height (GtkWidget *widget, - gint *minimal_height, - gint *natural_height) -{ - GtkRequisition requisition; - - pspp_sheet_view_size_request (widget, &requisition); - - *minimal_height = *natural_height = requisition.height; -} - -static void -pspp_sheet_view_class_init (PsppSheetViewClass *class) -{ - GObjectClass *o_class; - GtkWidgetClass *widget_class; - GtkContainerClass *container_class; - GtkBindingSet *binding_set[2]; - int i; - - binding_set[0] = gtk_binding_set_by_class (class); - - binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing"); - edit_bindings = binding_set[1]; - - o_class = (GObjectClass *) class; - widget_class = (GtkWidgetClass *) class; - container_class = (GtkContainerClass *) class; - - /* GObject signals */ - o_class->set_property = pspp_sheet_view_set_property; - o_class->get_property = pspp_sheet_view_get_property; - o_class->finalize = pspp_sheet_view_finalize; - o_class->dispose = pspp_sheet_view_dispose; - - /* GtkWidget signals */ - widget_class->map = pspp_sheet_view_map; - widget_class->realize = pspp_sheet_view_realize; - widget_class->unrealize = pspp_sheet_view_unrealize; - widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width; - widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height; - widget_class->size_allocate = pspp_sheet_view_size_allocate; - widget_class->button_press_event = pspp_sheet_view_button_press; - widget_class->button_release_event = pspp_sheet_view_button_release; - widget_class->grab_broken_event = pspp_sheet_view_grab_broken; - /*widget_class->configure_event = pspp_sheet_view_configure;*/ - widget_class->motion_notify_event = pspp_sheet_view_motion; - widget_class->draw = pspp_sheet_view_draw; - widget_class->key_press_event = pspp_sheet_view_key_press; - widget_class->key_release_event = pspp_sheet_view_key_release; - widget_class->enter_notify_event = pspp_sheet_view_enter_notify; - widget_class->leave_notify_event = pspp_sheet_view_leave_notify; - widget_class->focus_out_event = pspp_sheet_view_focus_out; - widget_class->drag_begin = pspp_sheet_view_drag_begin; - widget_class->drag_end = pspp_sheet_view_drag_end; - widget_class->drag_data_get = pspp_sheet_view_drag_data_get; - widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete; - widget_class->drag_leave = pspp_sheet_view_drag_leave; - widget_class->drag_motion = pspp_sheet_view_drag_motion; - widget_class->drag_drop = pspp_sheet_view_drag_drop; - widget_class->drag_data_received = pspp_sheet_view_drag_data_received; - widget_class->focus = pspp_sheet_view_focus; - widget_class->grab_focus = pspp_sheet_view_grab_focus; - widget_class->style_updated = pspp_sheet_view_style_updated; - widget_class->grab_notify = pspp_sheet_view_grab_notify; - widget_class->state_changed = pspp_sheet_view_state_changed; - - /* GtkContainer signals */ - container_class->remove = pspp_sheet_view_remove; - container_class->forall = pspp_sheet_view_forall; - container_class->set_focus_child = pspp_sheet_view_set_focus_child; - - class->set_scroll_adjustments = pspp_sheet_view_set_adjustments; - class->move_cursor = pspp_sheet_view_real_move_cursor; - class->select_all = pspp_sheet_view_real_select_all; - class->unselect_all = pspp_sheet_view_real_unselect_all; - class->select_cursor_row = pspp_sheet_view_real_select_cursor_row; - class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row; - class->start_interactive_search = pspp_sheet_view_start_interactive_search; - - /* Properties */ - - g_object_class_install_property (o_class, - PROP_MODEL, - g_param_spec_object ("model", - P_("TreeView Model"), - P_("The model for the tree view"), - GTK_TYPE_TREE_MODEL, - GTK_PARAM_READWRITE)); - - g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment"); - g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment"); - g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy"); - g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy"); - - g_object_class_install_property (o_class, - PROP_HEADERS_VISIBLE, - g_param_spec_boolean ("headers-visible", - P_("Headers Visible"), - P_("Show the column header buttons"), - TRUE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_HEADERS_CLICKABLE, - g_param_spec_boolean ("headers-clickable", - P_("Headers Clickable"), - P_("Column headers respond to click events"), - TRUE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_REORDERABLE, - g_param_spec_boolean ("reorderable", - P_("Reorderable"), - P_("View is reorderable"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_RULES_HINT, - g_param_spec_boolean ("rules-hint", - P_("Rules Hint"), - P_("Set a hint to the theme engine to draw rows in alternating colors"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_ENABLE_SEARCH, - g_param_spec_boolean ("enable-search", - P_("Enable Search"), - P_("View allows user to search through columns interactively"), - TRUE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_SEARCH_COLUMN, - g_param_spec_int ("search-column", - P_("Search Column"), - P_("Model column to search through during interactive search"), - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE)); - - /** - * PsppSheetView:hover-selection: - * - * Enables of disables the hover selection mode of @tree_view. - * Hover selection makes the selected row follow the pointer. - * Currently, this works only for the selection modes - * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE. - * - * This mode is primarily intended for treeviews in popups, e.g. - * in #GtkComboBox or #GtkEntryCompletion. - * - * Since: 2.6 - */ - g_object_class_install_property (o_class, - PROP_HOVER_SELECTION, - g_param_spec_boolean ("hover-selection", - P_("Hover Selection"), - P_("Whether the selection should follow the pointer"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_RUBBER_BANDING, - g_param_spec_boolean ("rubber-banding", - P_("Rubber Banding"), - P_("Whether to enable selection of multiple items by dragging the mouse pointer"), - FALSE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_ENABLE_GRID_LINES, - g_param_spec_enum ("enable-grid-lines", - P_("Enable Grid Lines"), - P_("Whether grid lines should be drawn in the tree view"), - PSPP_TYPE_SHEET_VIEW_GRID_LINES, - PSPP_SHEET_VIEW_GRID_LINES_NONE, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_TOOLTIP_COLUMN, - g_param_spec_int ("tooltip-column", - P_("Tooltip Column"), - P_("The column in the model containing the tooltip texts for the rows"), - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_SPECIAL_CELLS, - g_param_spec_enum ("special-cells", - P_("Special Cells"), - P_("Whether rows have special cells."), - PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS, - PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_FIXED_HEIGHT, - g_param_spec_int ("fixed-height", - P_("Fixed Height"), - P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."), - -1, - G_MAXINT, - -1, - GTK_PARAM_READWRITE)); - - g_object_class_install_property (o_class, - PROP_FIXED_HEIGHT_SET, - g_param_spec_boolean ("fixed-height-set", - P_("Fixed Height Set"), - P_("Whether fixed-height was set externally."), - FALSE, - GTK_PARAM_READWRITE)); - - /* Style properties */ -#define _TREE_VIEW_EXPANDER_SIZE 12 -#define _TREE_VIEW_VERTICAL_SEPARATOR 2 -#define _TREE_VIEW_HORIZONTAL_SEPARATOR 2 - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("expander-size", - P_("Expander Size"), - P_("Size of the expander arrow"), - 0, - G_MAXINT, - _TREE_VIEW_EXPANDER_SIZE, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("vertical-separator", - P_("Vertical Separator Width"), - P_("Vertical space between cells. Must be an even number"), - 0, - G_MAXINT, - _TREE_VIEW_VERTICAL_SEPARATOR, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("horizontal-separator", - P_("Horizontal Separator Width"), - P_("Horizontal space between cells. Must be an even number"), - 0, - G_MAXINT, - _TREE_VIEW_HORIZONTAL_SEPARATOR, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_boolean ("allow-rules", - P_("Allow Rules"), - P_("Allow drawing of alternating color rows"), - TRUE, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_boxed ("even-row-color", - P_("Even Row Color"), - P_("Color to use for even rows"), - GDK_TYPE_COLOR, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_boxed ("odd-row-color", - P_("Odd Row Color"), - P_("Color to use for odd rows"), - GDK_TYPE_COLOR, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_boolean ("row-ending-details", - P_("Row Ending details"), - P_("Enable extended row background theming"), - FALSE, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("grid-line-width", - P_("Grid line width"), - P_("Width, in pixels, of the tree view grid lines"), - 0, G_MAXINT, 1, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("tree-line-width", - P_("Tree line width"), - P_("Width, in pixels, of the tree view lines"), - 0, G_MAXINT, 1, - GTK_PARAM_READABLE)); - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_string ("tree-line-pattern", - P_("Tree line pattern"), - P_("Dash pattern used to draw the tree view lines"), - "\1\1", - GTK_PARAM_READABLE)); - - /* Signals */ - - /** - * PsppSheetView::row-activated: - * @tree_view: the object on which the signal is emitted - * @path: the #GtkTreePath for the activated row - * @column: the #PsppSheetViewColumn in which the activation occurred - * - * The "row-activated" signal is emitted when the method - * pspp_sheet_view_row_activated() is called or the user double clicks - * a treeview row. It is also emitted when a non-editable row is - * selected and one of the keys: Space, Shift+Space, Return or - * Enter is pressed. - * - * For selection handling refer to the tree - * widget conceptual overview as well as #PsppSheetSelection. - */ - tree_view_signals[ROW_ACTIVATED] = - g_signal_new ("row-activated", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (PsppSheetViewClass, row_activated), - NULL, NULL, - psppire_marshal_VOID__BOXED_OBJECT, - G_TYPE_NONE, 2, - GTK_TYPE_TREE_PATH, - PSPP_TYPE_SHEET_VIEW_COLUMN); - - /** - * PsppSheetView::columns-changed: - * @tree_view: the object on which the signal is emitted - * - * The number of columns of the treeview has changed. - */ - tree_view_signals[COLUMNS_CHANGED] = - g_signal_new ("columns-changed", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /** - * PsppSheetView::cursor-changed: - * @tree_view: the object on which the signal is emitted - * - * The position of the cursor (focused cell) has changed. - */ - tree_view_signals[CURSOR_CHANGED] = - g_signal_new ("cursor-changed", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - tree_view_signals[MOVE_CURSOR] = - g_signal_new ("move-cursor", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor), - NULL, NULL, - psppire_marshal_BOOLEAN__ENUM_INT, - G_TYPE_BOOLEAN, 2, - GTK_TYPE_MOVEMENT_STEP, - G_TYPE_INT); - - tree_view_signals[SELECT_ALL] = - g_signal_new ("select-all", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (PsppSheetViewClass, select_all), - NULL, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - tree_view_signals[UNSELECT_ALL] = - g_signal_new ("unselect-all", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all), - NULL, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - tree_view_signals[SELECT_CURSOR_ROW] = - g_signal_new ("select-cursor-row", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row), - NULL, NULL, - psppire_marshal_BOOLEAN__BOOLEAN, - G_TYPE_BOOLEAN, 2, - G_TYPE_BOOLEAN, G_TYPE_INT); - - tree_view_signals[TOGGLE_CURSOR_ROW] = - g_signal_new ("toggle-cursor-row", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row), - NULL, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - tree_view_signals[START_INTERACTIVE_SEARCH] = - g_signal_new ("start-interactive-search", - G_TYPE_FROM_CLASS (o_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search), - NULL, NULL, - psppire_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - /* Key bindings */ - for (i = 0; i < 2; i++) - { - pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, -1); - pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, -1); - - pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, 1); - pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, 1); - - pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE, - GTK_MOVEMENT_DISPLAY_LINES, -1); - - pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE, - GTK_MOVEMENT_DISPLAY_LINES, 1); - - pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - - pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - - pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE, - GTK_MOVEMENT_PAGES, -1); - pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE, - GTK_MOVEMENT_PAGES, -1); - - pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE, - GTK_MOVEMENT_PAGES, 1); - pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE, - GTK_MOVEMENT_PAGES, 1); - - - gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS, - G_TYPE_INT, -1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS, - G_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, -1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS, - G_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS, - G_TYPE_INT, -1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS, - G_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS, - G_TYPE_INT, -1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS, - G_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS, - G_TYPE_INT, -1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, -1); - - gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0); - - gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0); - } - - gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); - - gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0); - - gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0); - - gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1, - G_TYPE_BOOLEAN, TRUE, - G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND); - gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1, - G_TYPE_BOOLEAN, TRUE, - G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND); - - gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1, - G_TYPE_BOOLEAN, TRUE, - G_TYPE_INT, 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1, - G_TYPE_BOOLEAN, TRUE, - G_TYPE_INT, 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1, - G_TYPE_BOOLEAN, TRUE, - G_TYPE_INT, 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1, - G_TYPE_BOOLEAN, TRUE, - G_TYPE_INT, 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1, - G_TYPE_BOOLEAN, TRUE, - G_TYPE_INT, 0); - - gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0); - gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0); - - g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate)); -} - -static void -pspp_sheet_view_buildable_init (GtkBuildableIface *iface) -{ - iface->add_child = pspp_sheet_view_buildable_add_child; -} - -static void -pspp_sheet_view_init (PsppSheetView *tree_view) -{ - tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate); - - gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE); - gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE); - - tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS - | PSPP_SHEET_VIEW_HEADERS_VISIBLE; - - /* We need some padding */ - tree_view->priv->selected = range_tower_create (); - tree_view->priv->dy = 0; - tree_view->priv->cursor_offset = 0; - tree_view->priv->n_columns = 0; - tree_view->priv->header_height = 1; - tree_view->priv->x_drag = 0; - tree_view->priv->drag_pos = -1; - tree_view->priv->header_has_focus = FALSE; - tree_view->priv->pressed_button = -1; - tree_view->priv->press_start_x = -1; - tree_view->priv->press_start_y = -1; - tree_view->priv->reorderable = FALSE; - tree_view->priv->presize_handler_timer = 0; - tree_view->priv->scroll_sync_timer = 0; - tree_view->priv->fixed_height = -1; - tree_view->priv->fixed_height_set = FALSE; - pspp_sheet_view_set_adjustments (tree_view, NULL, NULL); - tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view); - tree_view->priv->enable_search = TRUE; - tree_view->priv->search_column = -1; - tree_view->priv->search_position_func = pspp_sheet_view_search_position_func; - tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func; - tree_view->priv->search_custom_entry_set = FALSE; - tree_view->priv->typeselect_flush_timeout = 0; - tree_view->priv->init_hadjust_value = TRUE; - tree_view->priv->width = 0; - - tree_view->priv->hover_selection = FALSE; - - tree_view->priv->rubber_banding_enable = FALSE; - - tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE; - - tree_view->priv->tooltip_column = -1; - - tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT; - - tree_view->priv->post_validation_flag = FALSE; - - tree_view->priv->last_button_x = -1; - tree_view->priv->last_button_y = -1; - - tree_view->priv->event_last_x = -10000; - tree_view->priv->event_last_y = -10000; - - tree_view->priv->prelight_node = -1; - tree_view->priv->rubber_band_start_node = -1; - tree_view->priv->rubber_band_end_node = -1; - - tree_view->priv->anchor_column = NULL; - - tree_view->priv->button_style = NULL; - - tree_view->dispose_has_run = FALSE; - - pspp_sheet_view_do_set_vadjustment (tree_view, NULL); - pspp_sheet_view_do_set_hadjustment (tree_view, NULL); - gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)), - GTK_STYLE_CLASS_VIEW); -} - - - -/* GObject Methods - */ - -static void -pspp_sheet_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppSheetView *tree_view; - - tree_view = PSPP_SHEET_VIEW (object); - - switch (prop_id) - { - case PROP_MODEL: - pspp_sheet_view_set_model (tree_view, g_value_get_object (value)); - break; - case PROP_HADJUSTMENT: - pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value)); - break; - case PROP_VADJUSTMENT: - pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value)); - break; - case PROP_HSCROLL_POLICY: - tree_view->priv->hscroll_policy = g_value_get_enum (value); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - break; - case PROP_VSCROLL_POLICY: - tree_view->priv->vscroll_policy = g_value_get_enum (value); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - break; - case PROP_HEADERS_VISIBLE: - pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value)); - break; - case PROP_HEADERS_CLICKABLE: - pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value)); - break; - case PROP_REORDERABLE: - pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value)); - break; - case PROP_RULES_HINT: - pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value)); - break; - case PROP_ENABLE_SEARCH: - pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value)); - break; - case PROP_SEARCH_COLUMN: - pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value)); - break; - case PROP_HOVER_SELECTION: - tree_view->priv->hover_selection = g_value_get_boolean (value); - break; - case PROP_RUBBER_BANDING: - tree_view->priv->rubber_banding_enable = g_value_get_boolean (value); - break; - case PROP_ENABLE_GRID_LINES: - pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value)); - break; - case PROP_TOOLTIP_COLUMN: - pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value)); - break; - case PROP_SPECIAL_CELLS: - pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value)); - break; - case PROP_FIXED_HEIGHT: - pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value)); - break; - case PROP_FIXED_HEIGHT_SET: - if (g_value_get_boolean (value)) - { - if (!tree_view->priv->fixed_height_set - && tree_view->priv->fixed_height >= 0) - { - tree_view->priv->fixed_height_set = true; - g_object_notify (G_OBJECT (tree_view), "fixed-height-set"); - } - } - else - { - if (tree_view->priv->fixed_height_set) - { - tree_view->priv->fixed_height_set = false; - g_object_notify (G_OBJECT (tree_view), "fixed-height-set"); - install_presize_handler (tree_view); - } - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -pspp_sheet_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppSheetView *tree_view; - - tree_view = PSPP_SHEET_VIEW (object); - - switch (prop_id) - { - case PROP_MODEL: - g_value_set_object (value, tree_view->priv->model); - break; - case PROP_HADJUSTMENT: - g_value_set_object (value, tree_view->priv->hadjustment); - break; - case PROP_VADJUSTMENT: - g_value_set_object (value, tree_view->priv->vadjustment); - break; - case PROP_HSCROLL_POLICY: - g_value_set_enum (value, tree_view->priv->hscroll_policy); - break; - case PROP_VSCROLL_POLICY: - g_value_set_enum (value, tree_view->priv->vscroll_policy); - break; - case PROP_HEADERS_VISIBLE: - g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view)); - break; - case PROP_HEADERS_CLICKABLE: - g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view)); - break; - case PROP_REORDERABLE: - g_value_set_boolean (value, tree_view->priv->reorderable); - break; - case PROP_RULES_HINT: - g_value_set_boolean (value, tree_view->priv->has_rules); - break; - case PROP_ENABLE_SEARCH: - g_value_set_boolean (value, tree_view->priv->enable_search); - break; - case PROP_SEARCH_COLUMN: - g_value_set_int (value, tree_view->priv->search_column); - break; - case PROP_HOVER_SELECTION: - g_value_set_boolean (value, tree_view->priv->hover_selection); - break; - case PROP_RUBBER_BANDING: - g_value_set_boolean (value, tree_view->priv->rubber_banding_enable); - break; - case PROP_ENABLE_GRID_LINES: - g_value_set_enum (value, tree_view->priv->grid_lines); - break; - case PROP_TOOLTIP_COLUMN: - g_value_set_int (value, tree_view->priv->tooltip_column); - break; - case PROP_SPECIAL_CELLS: - g_value_set_enum (value, tree_view->priv->special_cells); - break; - case PROP_FIXED_HEIGHT: - g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view)); - break; - case PROP_FIXED_HEIGHT_SET: - g_value_set_boolean (value, tree_view->priv->fixed_height_set); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -pspp_sheet_view_dispose (GObject *object) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (object); - - if (tree_view->dispose_has_run) - return; - - tree_view->dispose_has_run = TRUE; - - if (tree_view->priv->selection != NULL) - { - _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL); - g_object_unref (tree_view->priv->selection); - tree_view->priv->selection = NULL; - } - - if (tree_view->priv->hadjustment) - { - g_object_unref (tree_view->priv->hadjustment); - tree_view->priv->hadjustment = NULL; - } - if (tree_view->priv->vadjustment) - { - g_object_unref (tree_view->priv->vadjustment); - tree_view->priv->vadjustment = NULL; - } - - if (tree_view->priv->button_style) - { - g_object_unref (tree_view->priv->button_style); - tree_view->priv->button_style = NULL; - } - - - G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object); -} - - - -static void -pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view, - GtkBuilder *builder, - GObject *child, - const gchar *type) -{ - pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child)); -} - -static void -pspp_sheet_view_finalize (GObject *object) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (object); - - pspp_sheet_view_stop_editing (tree_view, TRUE); - - if (tree_view->priv->selected != NULL) - { - range_tower_destroy (tree_view->priv->selected); - tree_view->priv->selected = NULL; - } - - - tree_view->priv->prelight_node = -1; - - - if (tree_view->priv->scroll_to_path != NULL) - { - gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); - tree_view->priv->scroll_to_path = NULL; - } - - if (tree_view->priv->drag_dest_row != NULL) - { - gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); - tree_view->priv->drag_dest_row = NULL; - } - - if (tree_view->priv->top_row != NULL) - { - gtk_tree_row_reference_free (tree_view->priv->top_row); - tree_view->priv->top_row = NULL; - } - - if (tree_view->priv->column_drop_func_data && - tree_view->priv->column_drop_func_data_destroy) - { - tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data); - tree_view->priv->column_drop_func_data = NULL; - } - - if (tree_view->priv->destroy_count_destroy && - tree_view->priv->destroy_count_data) - { - tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data); - tree_view->priv->destroy_count_data = NULL; - } - - gtk_tree_row_reference_free (tree_view->priv->cursor); - tree_view->priv->cursor = NULL; - - gtk_tree_row_reference_free (tree_view->priv->anchor); - tree_view->priv->anchor = NULL; - - /* destroy interactive search dialog */ - if (tree_view->priv->search_window) - { - gtk_widget_destroy (tree_view->priv->search_window); - tree_view->priv->search_window = NULL; - tree_view->priv->search_entry = NULL; - if (tree_view->priv->typeselect_flush_timeout) - { - g_source_remove (tree_view->priv->typeselect_flush_timeout); - tree_view->priv->typeselect_flush_timeout = 0; - } - } - - if (tree_view->priv->search_destroy && tree_view->priv->search_user_data) - { - tree_view->priv->search_destroy (tree_view->priv->search_user_data); - tree_view->priv->search_user_data = NULL; - } - - if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data) - { - tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data); - tree_view->priv->search_position_user_data = NULL; - } - - pspp_sheet_view_set_model (tree_view, NULL); - - - G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object); -} - - - -/* GtkWidget Methods - */ - -/* GtkWidget::map helper */ -static void -pspp_sheet_view_map_buttons (PsppSheetView *tree_view) -{ - GList *list; - - g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view))); - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE)) - { - PsppSheetViewColumn *column; - - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (column->button != NULL && - gtk_widget_get_visible (column->button) && - !gtk_widget_get_mapped (column->button)) - gtk_widget_map (column->button); - } - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (column->visible == FALSE || column->window == NULL) - continue; - if (column->resizable) - { - gdk_window_raise (column->window); - gdk_window_show (column->window); - } - else - gdk_window_hide (column->window); - } - gdk_window_show (tree_view->priv->header_window); - } -} - -static void -pspp_sheet_view_map (GtkWidget *widget) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GList *tmp_list; - - gtk_widget_set_mapped (widget, TRUE); - - tmp_list = tree_view->priv->children; - while (tmp_list) - { - PsppSheetViewChild *child = tmp_list->data; - tmp_list = tmp_list->next; - - if (gtk_widget_get_visible (child->widget)) - { - if (!gtk_widget_get_mapped (child->widget)) - gtk_widget_map (child->widget); - } - } - gdk_window_show (tree_view->priv->bin_window); - - pspp_sheet_view_map_buttons (tree_view); - - gdk_window_show (gtk_widget_get_window (widget)); -} - -static void -pspp_sheet_view_realize (GtkWidget *widget) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GList *tmp_list; - GdkWindow *window; - GdkWindowAttr attributes; - gint attributes_mask; - GtkAllocation allocation; - - gtk_widget_set_realized (widget, TRUE); - - gtk_widget_get_allocation (widget, &allocation); - - /* Make the main, clipping window */ - attributes.window_type = GDK_WINDOW_CHILD; - attributes.x = allocation.x; - attributes.y = allocation.y; - attributes.width = allocation.width; - attributes.height = allocation.height; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual (widget); - attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK; - - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; - - window = gdk_window_new (gtk_widget_get_parent_window (widget), - &attributes, attributes_mask); - gtk_widget_set_window (widget, window); - - gtk_widget_register_window (widget, window); - gtk_widget_get_allocation (widget, &allocation); - - /* Make the window for the tree */ - attributes.x = 0; - attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view); - attributes.width = MAX (tree_view->priv->width, allocation.width); - attributes.height = allocation.height; - attributes.event_mask = (GDK_EXPOSURE_MASK | - GDK_SCROLL_MASK | - GDK_POINTER_MOTION_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - gtk_widget_get_events (widget)); - - tree_view->priv->bin_window = gdk_window_new (window, - &attributes, attributes_mask); - gtk_widget_register_window (widget, tree_view->priv->bin_window); - gtk_widget_get_allocation (widget, &allocation); - - /* Make the column header window */ - attributes.x = 0; - attributes.y = 0; - attributes.width = MAX (tree_view->priv->width, allocation.width); - attributes.height = tree_view->priv->header_height; - attributes.event_mask = (GDK_EXPOSURE_MASK | - GDK_SCROLL_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_KEY_PRESS_MASK | - GDK_KEY_RELEASE_MASK | - gtk_widget_get_events (widget)); - - tree_view->priv->header_window = gdk_window_new (window, - &attributes, attributes_mask); - gtk_widget_register_window (widget, tree_view->priv->header_window); - - { /* Ensure Background */ - GtkStyleContext *context; - - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - - gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view))); - gtk_style_context_set_background (context, tree_view->priv->header_window); - } - - tmp_list = tree_view->priv->children; - while (tmp_list) - { - PsppSheetViewChild *child = tmp_list->data; - tmp_list = tmp_list->next; - - gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window); - } - - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)); - - /* Need to call those here, since they create GCs */ - pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines); - - install_presize_handler (tree_view); -} - -static void -pspp_sheet_view_unrealize (GtkWidget *widget) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - PsppSheetViewPrivate *priv = tree_view->priv; - GList *list; - - GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget); - - if (priv->scroll_timeout != 0) - { - g_source_remove (priv->scroll_timeout); - priv->scroll_timeout = 0; - } - - if (priv->open_dest_timeout != 0) - { - g_source_remove (priv->open_dest_timeout); - priv->open_dest_timeout = 0; - } - - if (priv->presize_handler_timer != 0) - { - g_source_remove (priv->presize_handler_timer); - priv->presize_handler_timer = 0; - } - - if (priv->validate_rows_timer != 0) - { - g_source_remove (priv->validate_rows_timer); - priv->validate_rows_timer = 0; - } - - if (priv->scroll_sync_timer != 0) - { - g_source_remove (priv->scroll_sync_timer); - priv->scroll_sync_timer = 0; - } - - if (priv->typeselect_flush_timeout) - { - g_source_remove (priv->typeselect_flush_timeout); - priv->typeselect_flush_timeout = 0; - } - - for (list = priv->columns; list; list = list->next) - _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data)); - - gdk_window_set_user_data (priv->bin_window, NULL); - gdk_window_destroy (priv->bin_window); - priv->bin_window = NULL; - - gdk_window_set_user_data (priv->header_window, NULL); - gdk_window_destroy (priv->header_window); - priv->header_window = NULL; - - if (priv->drag_window) - { - gdk_window_set_user_data (priv->drag_window, NULL); - gdk_window_destroy (priv->drag_window); - priv->drag_window = NULL; - } - - if (priv->drag_highlight_window) - { - gdk_window_set_user_data (priv->drag_highlight_window, NULL); - gdk_window_destroy (priv->drag_highlight_window); - priv->drag_highlight_window = NULL; - } - - if (tree_view->priv->columns != NULL) - { - list = tree_view->priv->columns; - while (list) - { - PsppSheetViewColumn *column; - column = PSPP_SHEET_VIEW_COLUMN (list->data); - list = list->next; - pspp_sheet_view_remove_column (tree_view, column); - } - tree_view->priv->columns = NULL; - } -} - -/* GtkWidget::size_request helper */ -static void -pspp_sheet_view_size_request_columns (PsppSheetView *tree_view) -{ - GList *list; - - tree_view->priv->header_height = 0; - - if (tree_view->priv->model) - { - for (list = tree_view->priv->columns; list; list = list->next) - { - GtkRequisition requisition; - PsppSheetViewColumn *column = list->data; - - pspp_sheet_view_column_size_request (column, &requisition); - column->button_request = requisition.width; - tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height); - } - } -} - - -/* Called only by ::size_request */ -static void -pspp_sheet_view_update_size (PsppSheetView *tree_view) -{ - GList *list; - PsppSheetViewColumn *column; - gint i; - - if (tree_view->priv->model == NULL) - { - tree_view->priv->width = 0; - tree_view->priv->prev_width = 0; - tree_view->priv->height = 0; - return; - } - - tree_view->priv->prev_width = tree_view->priv->width; - tree_view->priv->width = 0; - - /* keep this in sync with size_allocate below */ - for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++) - { - gint real_requested_width = 0; - column = list->data; - if (!column->visible) - continue; - - if (column->use_resized_width) - { - real_requested_width = column->resized_width; - } - else - { - real_requested_width = column->fixed_width; - } - - if (column->min_width != -1) - real_requested_width = MAX (real_requested_width, column->min_width); - if (column->max_width != -1) - real_requested_width = MIN (real_requested_width, column->max_width); - - tree_view->priv->width += real_requested_width; - } - - tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count; -} - -static void -pspp_sheet_view_size_request (GtkWidget *widget, - GtkRequisition *requisition) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GList *tmp_list; - - /* we validate some rows initially just to make sure we have some size. - * In practice, with a lot of static lists, this should get a good width. - */ - initialize_fixed_height_mode (tree_view); - pspp_sheet_view_size_request_columns (tree_view); - pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget)); - - requisition->width = tree_view->priv->width; - requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view); - - tmp_list = tree_view->priv->children; - - while (tmp_list) - { - PsppSheetViewChild *child = tmp_list->data; - GtkRequisition child_requisition; - - tmp_list = tmp_list->next; - - if (gtk_widget_get_visible (child->widget)) - { - gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition); - } - } -} - -static void -invalidate_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column) -{ - gint column_offset = 0; - GList *list; - GtkWidget *widget = GTK_WIDGET (tree_view); - gboolean rtl; - - if (!gtk_widget_get_realized (widget)) - return; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - PsppSheetViewColumn *tmpcolumn = list->data; - if (tmpcolumn == column) - { - GdkRectangle invalid_rect; - GtkAllocation allocation; - - gtk_widget_get_allocation (widget, &allocation); - invalid_rect.x = column_offset; - invalid_rect.y = 0; - invalid_rect.width = column->width; - invalid_rect.height = allocation.height; - - gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE); - break; - } - - column_offset += tmpcolumn->width; - } -} - -static void -invalidate_last_column (PsppSheetView *tree_view) -{ - GList *last_column; - gboolean rtl; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns)); - last_column; - last_column = (rtl ? last_column->next : last_column->prev)) - { - if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible) - { - invalidate_column (tree_view, last_column->data); - return; - } - } -} - -static gint -pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column) -{ - gint real_requested_width; - - if (column->use_resized_width) - { - real_requested_width = column->resized_width; - } - else - { - real_requested_width = column->fixed_width; - } - - if (column->min_width != -1) - real_requested_width = MAX (real_requested_width, column->min_width); - if (column->max_width != -1) - real_requested_width = MIN (real_requested_width, column->max_width); - - return real_requested_width; -} - -static gboolean -span_intersects (int a0, int a_width, - int b0, int b_width) -{ - int a1 = a0 + a_width; - int b1 = b0 + b_width; - return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1); -} - -/* GtkWidget::size_allocate helper */ -static void -pspp_sheet_view_size_allocate_columns (GtkWidget *widget, - gboolean *width_changed) -{ - PsppSheetView *tree_view; - GList *list, *first_column, *last_column; - PsppSheetViewColumn *column; - GtkAllocation col_allocation; - GtkAllocation allocation; - gint width = 0; - gint extra, extra_per_column; - gint full_requested_width = 0; - gint number_of_expand_columns = 0; - gboolean column_changed = FALSE; - gboolean rtl; - - tree_view = PSPP_SHEET_VIEW (widget); - - for (last_column = g_list_last (tree_view->priv->columns); - last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible); - last_column = last_column->prev) - ; - - if (last_column == NULL) - return; - - for (first_column = g_list_first (tree_view->priv->columns); - first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible); - first_column = first_column->next) - ; - - col_allocation.y = 0; - col_allocation.height = tree_view->priv->header_height; - - rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - /* find out how many extra space and expandable columns we have */ - for (list = tree_view->priv->columns; list != last_column->next; list = list->next) - { - column = (PsppSheetViewColumn *)list->data; - - if (!column->visible) - continue; - - full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column); - - if (column->expand) - number_of_expand_columns++; - } - - gtk_widget_get_allocation (widget, &allocation); - extra = MAX (allocation.width - full_requested_width, 0); - if (number_of_expand_columns > 0) - extra_per_column = extra/number_of_expand_columns; - else - extra_per_column = 0; - - for (list = (rtl ? last_column : first_column); - list != (rtl ? first_column->prev : last_column->next); - list = (rtl ? list->prev : list->next)) - { - gint real_requested_width = 0; - gint old_width; - - column = list->data; - old_width = column->width; - - if (!column->visible) - continue; - - /* We need to handle the dragged button specially. - */ - if (column == tree_view->priv->drag_column) - { - GtkAllocation drag_allocation; - drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window); - drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window); - drag_allocation.x = 0; - drag_allocation.y = 0; - pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column, - &drag_allocation); - width += drag_allocation.width; - continue; - } - - real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column); - - col_allocation.x = width; - column->width = real_requested_width; - - if (column->expand) - { - if (number_of_expand_columns == 1) - { - /* We add the remander to the last column as - * */ - column->width += extra; - } - else - { - column->width += extra_per_column; - extra -= extra_per_column; - number_of_expand_columns --; - } - } - - if (column->width != old_width) - g_object_notify (G_OBJECT (column), "width"); - - col_allocation.width = column->width; - width += column->width; - - if (column->width > old_width) - column_changed = TRUE; - - pspp_sheet_view_column_size_allocate (column, &col_allocation); - - if (column->window) - gdk_window_move_resize (column->window, - col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2, - col_allocation.y, - TREE_VIEW_DRAG_WIDTH, col_allocation.height); - } - - /* We change the width here. The user might have been resizing columns, - * so the total width of the tree view changes. - */ - tree_view->priv->width = width; - if (width_changed) - *width_changed = TRUE; - - if (column_changed) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} - -static void -update_childrens_allocation (PsppSheetView *tree_view) -{ - GList *tmp_list; - for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next) - { - PsppSheetViewChild *child = tmp_list->data; - GtkAllocation allocation; - GtkTreePath *path; - - /* totally ignore our child's requisition */ - path = _pspp_sheet_view_find_path (tree_view, child->node); - pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation); - gtk_tree_path_free (path); - gtk_widget_size_allocate (child->widget, &allocation); - } -} - -static void -pspp_sheet_view_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GList *tmp_list; - gboolean width_changed = FALSE; - GtkAllocation old_allocation; - gtk_widget_get_allocation (widget, &old_allocation); - - if (allocation->width != old_allocation.width) - width_changed = TRUE; - - if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) - allocation->x += allocation->width - tree_view->priv->width ; - - gtk_widget_set_allocation (widget, allocation); - - /* We size-allocate the columns first because the width of the - * tree view (used in updating the adjustments below) might change. - */ - pspp_sheet_view_size_allocate_columns (widget, &width_changed); - - gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width); - gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9); - gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1); - gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0); - gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width)); - - if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) - { - if (allocation->width < tree_view->priv->width) - { - if (tree_view->priv->init_hadjust_value) - { - gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0)); - tree_view->priv->init_hadjust_value = FALSE; - } - else if (allocation->width != old_allocation.width) - { - gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width)); - } - else - gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width)); - } - else - { - gtk_adjustment_set_value (tree_view->priv->hadjustment, 0); - tree_view->priv->init_hadjust_value = TRUE; - } - } - else - if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width) - gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0)); - - gtk_adjustment_changed (tree_view->priv->hadjustment); - - gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view)); - gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1); - gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9); - gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0); - gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height)); - - gtk_adjustment_changed (tree_view->priv->vadjustment); - - /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */ - if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0); - else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height) - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), - tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment)); - else if (gtk_tree_row_reference_valid (tree_view->priv->top_row)) - pspp_sheet_view_top_row_to_dy (tree_view); - else - pspp_sheet_view_dy_to_top_row (tree_view); - - if (gtk_widget_get_realized (widget)) - { - gdk_window_move_resize (gtk_widget_get_window (widget), - allocation->x, allocation->y, - allocation->width, allocation->height); - gdk_window_move_resize (tree_view->priv->header_window, - - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment), - 0, - MAX (tree_view->priv->width, allocation->width), - tree_view->priv->header_height); - gdk_window_move_resize (tree_view->priv->bin_window, - - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment), - TREE_VIEW_HEADER_HEIGHT (tree_view), - MAX (tree_view->priv->width, allocation->width), - allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view)); - } - - if (tree_view->priv->row_count == 0) - invalidate_empty_focus (tree_view); - - if (gtk_widget_get_realized (widget)) - { - gboolean has_expand_column = FALSE; - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - { - if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data))) - { - has_expand_column = TRUE; - break; - } - } - - /* This little hack only works if we have an LTR locale, and no column has the */ - if (width_changed) - { - if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR && - ! has_expand_column) - invalidate_last_column (tree_view); - else - gtk_widget_queue_draw (widget); - } - update_childrens_allocation(tree_view); - } - - tree_view->priv->resized = TRUE; -} - -/* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */ -static void -grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view) -{ - GtkWidget *widget = GTK_WIDGET (tree_view); - - if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget)) - gtk_widget_grab_focus (widget); - PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS); -} - -gboolean -pspp_sheet_view_node_is_selected (PsppSheetView *tree_view, - int node) -{ - return node >= 0 && range_tower_contains (tree_view->priv->selected, node); -} - -void -pspp_sheet_view_node_select (PsppSheetView *tree_view, - int node) -{ - range_tower_set1 (tree_view->priv->selected, node, 1); -} - -void -pspp_sheet_view_node_unselect (PsppSheetView *tree_view, - int node) -{ - range_tower_set0 (tree_view->priv->selected, node, 1); -} - -gint -pspp_sheet_view_node_next (PsppSheetView *tree_view, - gint node) -{ - return node + 1 < tree_view->priv->row_count ? node + 1 : -1; -} - -gint -pspp_sheet_view_node_prev (PsppSheetView *tree_view, - gint node) -{ - return node > 0 ? node - 1 : -1; -} - -static gboolean -all_columns_selected (PsppSheetView *tree_view) -{ - GList *list; - - for (list = tree_view->priv->columns; list; list = list->next) - { - PsppSheetViewColumn *column = list->data; - if (column->selectable && !column->selected) - return FALSE; - } - - return TRUE; -} - -static gboolean -pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view, - gint node, - PsppSheetViewColumn *column, - GdkEventButton *event) -{ - PsppSheetSelection *selection; - PsppSheetSelectionMode mode; - GtkTreePath *path; - gboolean update_anchor; - gboolean handled; - guint modifiers; - - g_return_val_if_fail (tree_view != NULL, FALSE); - g_return_val_if_fail (column != NULL, FALSE); - - selection = tree_view->priv->selection; - mode = pspp_sheet_selection_get_mode (selection); - if (mode != PSPP_SHEET_SELECTION_RECTANGLE) - return FALSE; - - if (!column->row_head) - return FALSE; - - if (event) - { - modifiers = event->state & gtk_accelerator_get_default_mod_mask (); - if (event->type != GDK_BUTTON_PRESS - || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK)) - return FALSE; - } - else - modifiers = 0; - - path = gtk_tree_path_new_from_indices (node, -1); - if (event == NULL) - { - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - update_anchor = TRUE; - handled = TRUE; - } - else if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - if (pspp_sheet_selection_count_selected_rows (selection) <= 1 - || !all_columns_selected (tree_view)) - { - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - update_anchor = TRUE; - handled = FALSE; - } - else - update_anchor = handled = FALSE; - } - else if (event->type == GDK_BUTTON_PRESS && event->button == 1 - && modifiers == GDK_CONTROL_MASK) - { - if (!all_columns_selected (tree_view)) - { - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_all_columns (selection); - } - - if (pspp_sheet_selection_path_is_selected (selection, path)) - pspp_sheet_selection_unselect_path (selection, path); - else - pspp_sheet_selection_select_path (selection, path); - update_anchor = TRUE; - handled = TRUE; - } - else if (event->type == GDK_BUTTON_PRESS && event->button == 1 - && modifiers == GDK_SHIFT_MASK) - { - GtkTreeRowReference *anchor = tree_view->priv->anchor; - GtkTreePath *anchor_path; - - if (all_columns_selected (tree_view) - && gtk_tree_row_reference_valid (anchor)) - { - update_anchor = FALSE; - anchor_path = gtk_tree_row_reference_get_path (anchor); - } - else - { - update_anchor = TRUE; - anchor_path = gtk_tree_path_copy (path); - } - - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_range (selection, anchor_path, path); - pspp_sheet_selection_select_all_columns (selection); - - gtk_tree_path_free (anchor_path); - - handled = TRUE; - } - else - update_anchor = handled = FALSE; - - if (update_anchor) - { - if (tree_view->priv->anchor) - gtk_tree_row_reference_free (tree_view->priv->anchor); - tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), - tree_view->priv->model, - path); - } - - gtk_tree_path_free (path); - return handled; -} - -static gboolean -find_click (PsppSheetView *tree_view, - gint x, gint y, - gint *node, - PsppSheetViewColumn **column, - GdkRectangle *background_area, - GdkRectangle *cell_area) -{ - gint y_offset; - gboolean rtl; - GList *list; - gint new_y; - - /* find the node that was clicked */ - new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y); - if (new_y < 0) - new_y = 0; - y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node); - - if (*node < 0) - return FALSE; - - background_area->y = y_offset + y; - background_area->height = ROW_HEIGHT (tree_view); - background_area->x = 0; - - /* Let the column have a chance at selecting it. */ - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; list = (rtl ? list->prev : list->next)) - { - PsppSheetViewColumn *candidate = list->data; - - if (!candidate->visible) - continue; - - background_area->width = candidate->width; - if ((background_area->x > x) || - (background_area->x + background_area->width <= x)) - { - background_area->x += background_area->width; - continue; - } - - /* we found the focus column */ - - pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area, - TRUE, cell_area); - *column = candidate; - return TRUE; - } - - return FALSE; -} - -static gboolean -pspp_sheet_view_button_press (GtkWidget *widget, - GdkEventButton *event) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GList *list; - PsppSheetViewColumn *column = NULL; - gint i; - GdkRectangle background_area; - GdkRectangle cell_area; - gboolean rtl; - - rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - pspp_sheet_view_stop_editing (tree_view, FALSE); - - - /* Because grab_focus can cause reentrancy, we delay grab_focus until after - * we're done handling the button press. - */ - - if (event->window == tree_view->priv->bin_window) - { - int node; - GtkTreePath *path; - gint dval; - gint pre_val, aft_val; - PsppSheetViewColumn *column = NULL; - GtkCellRenderer *focus_cell = NULL; - gboolean row_double_click = FALSE; - - /* Empty tree? */ - if (tree_view->priv->row_count == 0) - { - grab_focus_and_unset_draw_keyfocus (tree_view); - return TRUE; - } - - if (!find_click (tree_view, event->x, event->y, &node, &column, - &background_area, &cell_area)) - { - grab_focus_and_unset_draw_keyfocus (tree_view); - return FALSE; - } - - tree_view->priv->focus_column = column; - - if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event)) - return TRUE; - - /* select */ - pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment); - - path = _pspp_sheet_view_find_path (tree_view, node); - - /* we only handle selection modifications on the first button press - */ - if (event->type == GDK_BUTTON_PRESS) - { - PsppSheetSelectionMode mode = 0; - - if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) - mode |= PSPP_SHEET_SELECT_MODE_TOGGLE; - if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) - mode |= PSPP_SHEET_SELECT_MODE_EXTEND; - - focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x); - if (focus_cell) - pspp_sheet_view_column_focus_cell (column, focus_cell); - - if (event->state & GDK_CONTROL_MASK) - { - pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode); - pspp_sheet_view_real_toggle_cursor_row (tree_view); - } - else if (event->state & GDK_SHIFT_MASK) - { - pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode); - pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode); - } - else - { - pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0); - } - - if (tree_view->priv->anchor_column == NULL || - !(event->state & GDK_SHIFT_MASK)) - tree_view->priv->anchor_column = column; - pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); - pspp_sheet_selection_select_column_range (tree_view->priv->selection, - tree_view->priv->anchor_column, - column); - } - - /* the treeview may have been scrolled because of _set_cursor, - * correct here - */ - - aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment); - dval = pre_val - aft_val; - - cell_area.y += dval; - background_area.y += dval; - - /* Save press to possibly begin a drag - */ - if (!tree_view->priv->in_grab && - tree_view->priv->pressed_button < 0) - { - tree_view->priv->pressed_button = event->button; - tree_view->priv->press_start_x = event->x; - tree_view->priv->press_start_y = event->y; - tree_view->priv->press_start_node = node; - - if (tree_view->priv->rubber_banding_enable - && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE || - tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)) - { - tree_view->priv->press_start_y += tree_view->priv->dy; - tree_view->priv->rubber_band_x = event->x; - tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy; - tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START; - - if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) - tree_view->priv->rubber_band_ctrl = TRUE; - if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) - tree_view->priv->rubber_band_shift = TRUE; - - } - } - - /* Test if a double click happened on the same row. */ - if (event->button == 1 && event->type == GDK_BUTTON_PRESS) - { - int double_click_time, double_click_distance; - - g_object_get (gtk_settings_get_for_screen ( - gtk_widget_get_screen (widget)), - "gtk-double-click-time", &double_click_time, - "gtk-double-click-distance", &double_click_distance, - NULL); - - /* Same conditions as _gdk_event_button_generate */ - if (tree_view->priv->last_button_x != -1 && - (event->time < tree_view->priv->last_button_time + double_click_time) && - (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) && - (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance)) - { - /* We do no longer compare paths of this row and the - * row clicked previously. We use the double click - * distance to decide whether this is a valid click, - * allowing the mouse to slightly move over another row. - */ - row_double_click = TRUE; - - tree_view->priv->last_button_time = 0; - tree_view->priv->last_button_x = -1; - tree_view->priv->last_button_y = -1; - } - else - { - tree_view->priv->last_button_time = event->time; - tree_view->priv->last_button_x = event->x; - tree_view->priv->last_button_y = event->y; - } - } - - if (row_double_click) - { - gtk_grab_remove (widget); - pspp_sheet_view_row_activated (tree_view, path, column); - - if (tree_view->priv->pressed_button == event->button) - tree_view->priv->pressed_button = -1; - } - - gtk_tree_path_free (path); - - /* If we activated the row through a double click we don't want to grab - * focus back, as moving focus to another widget is pretty common. - */ - if (!row_double_click) - grab_focus_and_unset_draw_keyfocus (tree_view); - - return TRUE; - } - - /* We didn't click in the window. Let's check to see if we clicked on a column resize window. - */ - for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++) - { - column = list->data; - if (event->window == column->window && - column->resizable && - column->window) - { - gpointer drag_data; - - if (GDK_GRAB_SUCCESS != gdk_device_grab (event->device, - column->window, - GDK_OWNERSHIP_NONE, - FALSE, - GDK_POINTER_MOTION_HINT_MASK | - GDK_BUTTON1_MOTION_MASK | - GDK_BUTTON_RELEASE_MASK, - NULL, event->time)) - return FALSE; - - gtk_grab_add (widget); - PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE); - column->resized_width = column->width; - - /* block attached dnd signal handler */ - drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); - if (drag_data) - g_signal_handlers_block_matched (widget, - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, - drag_data); - - tree_view->priv->drag_pos = i; - tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width); - - if (!gtk_widget_has_focus (widget)) - gtk_widget_grab_focus (widget); - - return TRUE; - } - } - return FALSE; -} - -/* GtkWidget::button_release_event helper */ -static gboolean -pspp_sheet_view_button_release_drag_column (GtkWidget *widget, - GdkEventButton *event) -{ - PsppSheetView *tree_view; - GList *l; - gboolean rtl; - - tree_view = PSPP_SHEET_VIEW (widget); - - rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME); - gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME); - - /* Move the button back */ - g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE); - - g_object_ref (tree_view->priv->drag_column->button); - gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button); - gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window); - gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view)); - g_object_unref (tree_view->priv->drag_column->button); - gtk_widget_queue_resize (widget); - if (tree_view->priv->drag_column->resizable) - { - gdk_window_raise (tree_view->priv->drag_column->window); - gdk_window_show (tree_view->priv->drag_column->window); - } - else - gdk_window_hide (tree_view->priv->drag_column->window); - - gtk_widget_grab_focus (tree_view->priv->drag_column->button); - - if (rtl) - { - if (tree_view->priv->cur_reorder && - tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column) - pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column, - tree_view->priv->cur_reorder->right_column); - } - else - { - if (tree_view->priv->cur_reorder && - tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column) - pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column, - tree_view->priv->cur_reorder->left_column); - } - tree_view->priv->drag_column = NULL; - gdk_window_hide (tree_view->priv->drag_window); - - for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next) - g_slice_free (PsppSheetViewColumnReorder, l->data); - g_list_free (tree_view->priv->column_drag_info); - tree_view->priv->column_drag_info = NULL; - tree_view->priv->cur_reorder = NULL; - - if (tree_view->priv->drag_highlight_window) - gdk_window_hide (tree_view->priv->drag_highlight_window); - - /* Reset our flags */ - tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET; - PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG); - - return TRUE; -} - -/* GtkWidget::button_release_event helper */ -static gboolean -pspp_sheet_view_button_release_column_resize (GtkWidget *widget, - GdkEventButton *event) -{ - PsppSheetView *tree_view; - gpointer drag_data; - - tree_view = PSPP_SHEET_VIEW (widget); - - tree_view->priv->drag_pos = -1; - - /* unblock attached dnd signal handler */ - drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); - if (drag_data) - g_signal_handlers_unblock_matched (widget, - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, - drag_data); - - PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE); - gtk_grab_remove (widget); - gdk_device_ungrab (event->device, event->time); - - return TRUE; -} - -static gboolean -pspp_sheet_view_button_release_edit (PsppSheetView *tree_view, - GdkEventButton *event) -{ - GtkCellEditable *cell_editable; - gchar *path_string; - GtkTreePath *path; - gint left, right; - GtkTreeIter iter; - PsppSheetViewColumn *column; - GdkRectangle background_area; - GdkRectangle cell_area; - GdkRectangle area; - guint modifiers; - guint flags; - int node; - - if (event->window != tree_view->priv->bin_window) - return FALSE; - - /* Ignore a released button, if that button wasn't depressed */ - if (tree_view->priv->pressed_button != event->button) - return FALSE; - - if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area, - &cell_area)) - return FALSE; - - /* decide if we edit */ - path = _pspp_sheet_view_find_path (tree_view, node); - modifiers = event->state & gtk_accelerator_get_default_mod_mask (); - if (event->button != 1 || modifiers) - return FALSE; - - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - pspp_sheet_view_column_cell_set_cell_data (column, - tree_view->priv->model, - &iter); - - if (!pspp_sheet_view_column_get_quick_edit (column) - && _pspp_sheet_view_column_has_editable_cell (column)) - return FALSE; - - flags = 0; /* FIXME: get the right flags */ - path_string = gtk_tree_path_to_string (path); - - if (!_pspp_sheet_view_column_cell_event (column, - &cell_editable, - (GdkEvent *)event, - path_string, - &background_area, - &cell_area, flags)) - return FALSE; - - if (cell_editable == NULL) - return FALSE; - - pspp_sheet_view_real_set_cursor (tree_view, path, - TRUE, TRUE, 0); /* XXX mode? */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - area = cell_area; - _pspp_sheet_view_column_get_neighbor_sizes ( - column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right); - - area.x += left; - area.width -= right + left; - - pspp_sheet_view_real_start_editing (tree_view, - column, - path, - cell_editable, - &area, - (GdkEvent *)event, - flags); - g_free (path_string); - gtk_tree_path_free (path); - return TRUE; -} - -static gboolean -pspp_sheet_view_button_release (GtkWidget *widget, - GdkEventButton *event) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - - pspp_sheet_view_stop_editing (tree_view, FALSE); - if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE - && pspp_sheet_view_button_release_edit (tree_view, event)) - { - if (tree_view->priv->pressed_button == event->button) - tree_view->priv->pressed_button = -1; - - tree_view->priv->rubber_band_status = RUBBER_BAND_OFF; - return TRUE; - } - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG)) - return pspp_sheet_view_button_release_drag_column (widget, event); - - if (tree_view->priv->rubber_band_status) - pspp_sheet_view_stop_rubber_band (tree_view); - - if (tree_view->priv->pressed_button == event->button) - tree_view->priv->pressed_button = -1; - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE)) - return pspp_sheet_view_button_release_column_resize (widget, event); - - return FALSE; -} - -static gboolean -pspp_sheet_view_grab_broken (GtkWidget *widget, - GdkEventGrabBroken *event) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG)) - pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event); - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE)) - pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event); - - return TRUE; -} - -/* GtkWidget::motion_event function set. - */ - -static void -do_prelight (PsppSheetView *tree_view, - int node, - /* these are in bin_window coords */ - gint x, - gint y) -{ - int prev_node = tree_view->priv->prelight_node; - - if (prev_node != node) - { - tree_view->priv->prelight_node = node; - - if (prev_node >= 0) - _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL); - - if (node >= 0) - _pspp_sheet_view_queue_draw_node (tree_view, node, NULL); - } -} - - -static void -prelight_or_select (PsppSheetView *tree_view, - int node, - /* these are in bin_window coords */ - gint x, - gint y) -{ - PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection); - - if (tree_view->priv->hover_selection && - (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) && - !(tree_view->priv->edited_column && - tree_view->priv->edited_column->editable_widget)) - { - if (node >= 0) - { - if (!pspp_sheet_view_node_is_selected (tree_view, node)) - { - GtkTreePath *path; - - path = _pspp_sheet_view_find_path (tree_view, node); - pspp_sheet_selection_select_path (tree_view->priv->selection, path); - if (pspp_sheet_view_node_is_selected (tree_view, node)) - { - PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS); - pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */ - } - gtk_tree_path_free (path); - } - } - - else if (mode == PSPP_SHEET_SELECTION_SINGLE) - pspp_sheet_selection_unselect_all (tree_view->priv->selection); - } - - do_prelight (tree_view, node, x, y); -} - -static void -ensure_unprelighted (PsppSheetView *tree_view) -{ - do_prelight (tree_view, - -1, - -1000, -1000); /* coords not possibly over an arrow */ - - g_assert (tree_view->priv->prelight_node < 0); -} - -static void -update_prelight (PsppSheetView *tree_view, - gint x, - gint y) -{ - int new_y; - int node; - - if (tree_view->priv->row_count == 0) - return; - - if (x == -10000) - { - ensure_unprelighted (tree_view); - return; - } - - new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y); - if (new_y < 0) - new_y = 0; - - pspp_sheet_view_find_offset (tree_view, new_y, &node); - - if (node >= 0) - prelight_or_select (tree_view, node, x, y); -} - - - - -/* Our motion arrow is either a box (in the case of the original spot) - * or an arrow. It is expander_size wide. - */ -/* - * 11111111111111 - * 01111111111110 - * 00111111111100 - * 00011111111000 - * 00001111110000 - * 00000111100000 - * 00000111100000 - * 00000111100000 - * ~ ~ ~ ~ ~ ~ ~ - * 00000111100000 - * 00000111100000 - * 00000111100000 - * 00001111110000 - * 00011111111000 - * 00111111111100 - * 01111111111110 - * 11111111111111 - */ - -static void -pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view) -{ -} - -static gboolean -pspp_sheet_view_motion_resize_column (GtkWidget *widget, - GdkEventMotion *event) -{ - gint x; - gint new_width; - PsppSheetViewColumn *column; - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - - column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos); - - if (event->is_hint || event->window != gtk_widget_get_window (widget)) - gtk_widget_get_pointer (widget, &x, NULL); - else - x = event->x; - - if (tree_view->priv->hadjustment) - x += gtk_adjustment_get_value (tree_view->priv->hadjustment); - - new_width = pspp_sheet_view_new_column_width (tree_view, - tree_view->priv->drag_pos, &x); - if (x != tree_view->priv->x_drag && - (new_width != column->fixed_width)) - { - column->use_resized_width = TRUE; - column->resized_width = new_width; -#if 0 - if (column->expand) - column->resized_width -= tree_view->priv->last_extra_space_per_column; -#endif - gtk_widget_queue_resize (widget); - } - - return FALSE; -} - - -static void -pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view) -{ - PsppSheetViewColumnReorder *reorder = NULL; - GList *list; - gint mouse_x; - - gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL); - for (list = tree_view->priv->column_drag_info; list; list = list->next) - { - reorder = (PsppSheetViewColumnReorder *) list->data; - if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align) - break; - reorder = NULL; - } - - /* if (reorder && reorder == tree_view->priv->cur_reorder) - return;*/ - - tree_view->priv->cur_reorder = reorder; - pspp_sheet_view_motion_draw_column_motion_arrow (tree_view); -} - -static void -pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view) -{ - GdkRectangle visible_rect; - gint y; - gint offset; - gfloat value; - - gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL); - y += tree_view->priv->dy; - - pspp_sheet_view_get_visible_rect (tree_view, &visible_rect); - - /* see if we are near the edge. */ - offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE); - if (offset > 0) - { - offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE); - if (offset < 0) - return; - } - - value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0, - gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment)); - gtk_adjustment_set_value (tree_view->priv->vadjustment, value); -} - -static gboolean -pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view) -{ - GdkRectangle visible_rect; - gint x; - gint offset; - gfloat value; - - gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL); - - pspp_sheet_view_get_visible_rect (tree_view, &visible_rect); - - /* See if we are near the edge. */ - offset = x - (visible_rect.x + SCROLL_EDGE_SIZE); - if (offset > 0) - { - offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE); - if (offset < 0) - return TRUE; - } - offset = offset/3; - - value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset, - 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)); - gtk_adjustment_set_value (tree_view->priv->hadjustment, value); - - return TRUE; - -} - -static gboolean -pspp_sheet_view_motion_drag_column (GtkWidget *widget, - GdkEventMotion *event) -{ - PsppSheetView *tree_view = (PsppSheetView *) widget; - PsppSheetViewColumn *column = tree_view->priv->drag_column; - gint x, y; - GtkAllocation allocation; - - /* Sanity Check */ - if ((column == NULL) || - (event->window != tree_view->priv->drag_window)) - return FALSE; - - /* Handle moving the header */ - gdk_window_get_position (tree_view->priv->drag_window, &x, &y); - gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); - x = CLAMP (x + (gint)event->x - column->drag_x, 0, - MAX (tree_view->priv->width, allocation.width) - column->allocation.width); - gdk_window_move (tree_view->priv->drag_window, x, y); - - /* autoscroll, if needed */ - pspp_sheet_view_horizontal_autoscroll (tree_view); - /* Update the current reorder position and arrow; */ - pspp_sheet_view_update_current_reorder (tree_view); - - return TRUE; -} - -static void -pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view) -{ - remove_scroll_timeout (tree_view); - gtk_grab_remove (GTK_WIDGET (tree_view)); - - if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE) - { - GtkTreePath *tmp_path; - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - /* The anchor path should be set to the start path */ - tmp_path = _pspp_sheet_view_find_path (tree_view, - tree_view->priv->rubber_band_start_node); - - if (tree_view->priv->anchor) - gtk_tree_row_reference_free (tree_view->priv->anchor); - - tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), - tree_view->priv->model, - tmp_path); - - gtk_tree_path_free (tmp_path); - - /* ... and the cursor to the end path */ - tmp_path = _pspp_sheet_view_find_path (tree_view, - tree_view->priv->rubber_band_end_node); - pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */ - gtk_tree_path_free (tmp_path); - - _pspp_sheet_selection_emit_changed (tree_view->priv->selection); - } - - /* Clear status variables */ - tree_view->priv->rubber_band_status = RUBBER_BAND_OFF; - tree_view->priv->rubber_band_shift = 0; - tree_view->priv->rubber_band_ctrl = 0; - - tree_view->priv->rubber_band_start_node = -1; - tree_view->priv->rubber_band_end_node = -1; -} - -static void -pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view, - int start_node, - int end_node, - gboolean select, - gboolean skip_start, - gboolean skip_end) -{ - if (start_node == end_node) - return; - - /* We skip the first node and jump inside the loop */ - if (skip_start) - goto skip_first; - - do - { - /* Small optimization by assuming insensitive nodes are never - * selected. - */ - if (select) - { - if (tree_view->priv->rubber_band_shift) - pspp_sheet_view_node_select (tree_view, start_node); - else if (tree_view->priv->rubber_band_ctrl) - { - /* Toggle the selection state */ - if (pspp_sheet_view_node_is_selected (tree_view, start_node)) - pspp_sheet_view_node_unselect (tree_view, start_node); - else - pspp_sheet_view_node_select (tree_view, start_node); - } - else - pspp_sheet_view_node_select (tree_view, start_node); - } - else - { - /* Mirror the above */ - if (tree_view->priv->rubber_band_shift) - pspp_sheet_view_node_unselect (tree_view, start_node); - else if (tree_view->priv->rubber_band_ctrl) - { - /* Toggle the selection state */ - if (pspp_sheet_view_node_is_selected (tree_view, start_node)) - pspp_sheet_view_node_unselect (tree_view, start_node); - else - pspp_sheet_view_node_select (tree_view, start_node); - } - else - pspp_sheet_view_node_unselect (tree_view, start_node); - } - - _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL); - - if (start_node == end_node) - break; - -skip_first: - - start_node = pspp_sheet_view_node_next (tree_view, start_node); - - if (start_node < 0) - /* Ran out of tree */ - break; - - if (skip_end && start_node == end_node) - break; - } - while (TRUE); -} - -static gint -pspp_sheet_view_node_find_offset (PsppSheetView *tree_view, - int node) -{ - return node * tree_view->priv->fixed_height; -} - -static gint -pspp_sheet_view_find_offset (PsppSheetView *tree_view, - gint height, - int *new_node) -{ - int fixed_height = tree_view->priv->fixed_height; - if (fixed_height <= 0 - || height < 0 - || height >= tree_view->priv->row_count * fixed_height) - { - *new_node = -1; - return 0; - } - else - { - *new_node = height / fixed_height; - return height % fixed_height; - } -} - -static void -pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view) -{ - int start_node; - int end_node; - - pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node); - pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node); - - /* Handle the start area first */ - if (tree_view->priv->rubber_band_start_node < 0) - { - pspp_sheet_view_update_rubber_band_selection_range (tree_view, - start_node, - end_node, - TRUE, - FALSE, - FALSE); - } - else if (start_node < tree_view->priv->rubber_band_start_node) - { - /* New node is above the old one; selection became bigger */ - pspp_sheet_view_update_rubber_band_selection_range (tree_view, - start_node, - tree_view->priv->rubber_band_start_node, - TRUE, - FALSE, - TRUE); - } - else if (start_node > tree_view->priv->rubber_band_start_node) - { - /* New node is below the old one; selection became smaller */ - pspp_sheet_view_update_rubber_band_selection_range (tree_view, - tree_view->priv->rubber_band_start_node, - start_node, - FALSE, - FALSE, - TRUE); - } - - tree_view->priv->rubber_band_start_node = start_node; - - /* Next, handle the end area */ - if (tree_view->priv->rubber_band_end_node < 0) - { - /* In the event this happens, start_node was also -1; this case is - * handled above. - */ - } - else if (end_node < 0) - { - /* Find the last node in the tree */ - pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1, - &end_node); - - /* Selection reached end of the tree */ - pspp_sheet_view_update_rubber_band_selection_range (tree_view, - tree_view->priv->rubber_band_end_node, - end_node, - TRUE, - TRUE, - FALSE); - } - else if (end_node > tree_view->priv->rubber_band_end_node) - { - /* New node is below the old one; selection became bigger */ - pspp_sheet_view_update_rubber_band_selection_range (tree_view, - tree_view->priv->rubber_band_end_node, - end_node, - TRUE, - TRUE, - FALSE); - } - else if (end_node < tree_view->priv->rubber_band_end_node) - { - /* New node is above the old one; selection became smaller */ - pspp_sheet_view_update_rubber_band_selection_range (tree_view, - end_node, - tree_view->priv->rubber_band_end_node, - FALSE, - TRUE, - FALSE); - } - - tree_view->priv->rubber_band_end_node = end_node; -} - -#define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X)) - -static void -pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view) -{ - gint x, y; - cairo_rectangle_int_t old_area; - cairo_rectangle_int_t new_area; - cairo_rectangle_int_t common; - cairo_region_t *invalid_region; - PsppSheetViewColumn *column; - - old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x); - old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy; - old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1; - old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1; - - gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL); - - x = MAX (x, 0); - y = MAX (y, 0) + tree_view->priv->dy; - - new_area.x = MIN (tree_view->priv->press_start_x, x); - new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy; - new_area.width = ABS (x - tree_view->priv->press_start_x) + 1; - new_area.height = ABS (y - tree_view->priv->press_start_y) + 1; - - invalid_region = cairo_region_create_rectangle (&old_area); - cairo_region_union_rectangle (invalid_region, &new_area); - - gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area), - GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common)); - if (common.width > 2 && common.height > 2) - { - cairo_region_t *common_region; - - /* make sure the border is invalidated */ - common.x += 1; - common.y += 1; - common.width -= 2; - common.height -= 2; - - common_region = cairo_region_create_rectangle (&common); - - cairo_region_subtract (invalid_region, common_region); - cairo_region_destroy (common_region); - } - -#if GTK_MAJOR_VERSION == 3 - gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE); -#else - { - cairo_rectangle_int_t extents; - GdkRegion *ereg; - cairo_region_get_extents (invalid_region, &extents); - ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents)); - gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE); - gdk_region_destroy (ereg); - } -#endif - - cairo_region_destroy (invalid_region); - - tree_view->priv->rubber_band_x = x; - tree_view->priv->rubber_band_y = y; - pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL); - - pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); - pspp_sheet_selection_select_column_range (tree_view->priv->selection, - tree_view->priv->anchor_column, - column); - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - pspp_sheet_view_update_rubber_band_selection (tree_view); -} - - - -static gboolean -pspp_sheet_view_motion_bin_window (GtkWidget *widget, - GdkEventMotion *event) -{ - PsppSheetView *tree_view; - int node; - gint new_y; - - tree_view = (PsppSheetView *) widget; - - if (tree_view->priv->row_count == 0) - return FALSE; - - if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START) - { - GdkRectangle background_area, cell_area; - PsppSheetViewColumn *column; - - if (find_click (tree_view, event->x, event->y, &node, &column, - &background_area, &cell_area) - && tree_view->priv->focus_column == column - && tree_view->priv->press_start_node == node) - return FALSE; - - gtk_grab_add (GTK_WIDGET (tree_view)); - pspp_sheet_view_update_rubber_band (tree_view); - - tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE; - } - else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE) - { - pspp_sheet_view_update_rubber_band (tree_view); - - add_scroll_timeout (tree_view); - } - - /* only check for an initiated drag when a button is pressed */ - if (tree_view->priv->pressed_button >= 0 - && !tree_view->priv->rubber_band_status) - pspp_sheet_view_maybe_begin_dragging_row (tree_view, event); - - new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y); - if (new_y < 0) - new_y = 0; - - pspp_sheet_view_find_offset (tree_view, new_y, &node); - - tree_view->priv->event_last_x = event->x; - tree_view->priv->event_last_y = event->y; - - prelight_or_select (tree_view, node, event->x, event->y); - - return TRUE; -} - -static gboolean -pspp_sheet_view_motion (GtkWidget *widget, - GdkEventMotion *event) -{ - PsppSheetView *tree_view; - - tree_view = (PsppSheetView *) widget; - - /* Resizing a column */ - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE)) - return pspp_sheet_view_motion_resize_column (widget, event); - - /* Drag column */ - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG)) - return pspp_sheet_view_motion_drag_column (widget, event); - - /* Sanity check it */ - if (event->window == tree_view->priv->bin_window) - return pspp_sheet_view_motion_bin_window (widget, event); - - return FALSE; -} - -/* Invalidate the focus rectangle near the edge of the bin_window; used when - * the tree is empty. - */ -static void -invalidate_empty_focus (PsppSheetView *tree_view) -{ - GdkRectangle area; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - area.x = 0; - area.y = 0; - area.width = gdk_window_get_width (tree_view->priv->bin_window); - area.height = gdk_window_get_height (tree_view->priv->bin_window); - gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE); -} - -/* Draws a focus rectangle near the edge of the bin_window; used when the tree - * is empty. - */ -static void -draw_empty_focus (PsppSheetView *tree_view) -{ - GtkWidget *widget = GTK_WIDGET (tree_view); - gint w, h; - cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window); - - if (!gtk_widget_has_focus (widget)) - return; - - w = gdk_window_get_width (tree_view->priv->bin_window); - h = gdk_window_get_height (tree_view->priv->bin_window); - - w -= 2; - h -= 2; - - if (w > 0 && h > 0) - gtk_paint_focus (gtk_widget_get_style (widget), - cr, - gtk_widget_get_state (widget), - widget, - NULL, - 1, 1, w, h); - cairo_destroy (cr); -} - -static void -pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view, - cairo_t *cr, - gint n_visible_columns, - gint min_y, - gint max_y) -{ - GList *list = tree_view->priv->columns; - gint x = 0; - - if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL - && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH) - return; - - /* Only draw the lines for visible rows and columns */ - gboolean rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - PsppSheetViewColumn *column = list->data; - - if (! column->visible) - continue; - - if (!rtl) - x += column->width; - - cairo_set_line_width (cr, 1.0); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); - cairo_move_to (cr, x + 0.5, min_y); - cairo_line_to (cr, x + 0.5, max_y - min_y - 0.5); - cairo_stroke (cr); - - if (rtl) - x += column->width; - } -} - -/* Warning: Very scary function. - * Modify at your own risk - * - * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()! - * FIXME: It's not... - */ -static void -pspp_sheet_view_draw_bin (GtkWidget *widget, - cairo_t *cr) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GtkTreePath *path; - GList *list; - int node; - int cursor = -1; - int drag_highlight = -1; - GtkTreeIter iter; - gint new_y; - gint y_offset, cell_offset; - gint max_height; - GdkRectangle background_area; - GdkRectangle cell_area; - guint flags; - gint bin_window_width; - gint bin_window_height; - GtkTreePath *cursor_path; - GtkTreePath *drag_dest_path; - GList *first_column, *last_column; - gint vertical_separator; - gint horizontal_separator; - gint focus_line_width; - gboolean allow_rules; - gboolean rtl; - gint n_visible_columns; - gint grid_line_width; - gboolean row_ending_details; - gboolean draw_vgrid_lines, draw_hgrid_lines; - gint min_y, max_y; - GtkStyleContext *context; - context = gtk_widget_get_style_context (widget); - - GdkRectangle Zarea; - GtkAllocation allocation; - gtk_widget_get_allocation (widget, &allocation); - - GdkRectangle exposed_rect; - gdk_cairo_get_clip_rectangle (cr, &exposed_rect); - - Zarea.x = 0; - Zarea.y = 0; - Zarea.height = allocation.height; - - rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - gtk_widget_style_get (widget, - "horizontal-separator", &horizontal_separator, - "vertical-separator", &vertical_separator, - "allow-rules", &allow_rules, - "focus-line-width", &focus_line_width, - "row-ending-details", &row_ending_details, - NULL); - - if (tree_view->priv->row_count == 0) - { - draw_empty_focus (tree_view); - return; - } - - validate_visible_area (tree_view); - - new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y); - - if (new_y < 0) - new_y = 0; - y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node); - bin_window_width = - gdk_window_get_width (tree_view->priv->bin_window); - - bin_window_height = - gdk_window_get_height (tree_view->priv->bin_window); - - - if (tree_view->priv->height < bin_window_height) - { - gtk_paint_flat_box (gtk_widget_get_style (widget), - cr, - gtk_widget_get_state (widget), - GTK_SHADOW_NONE, - widget, - "cell_even", - 0, tree_view->priv->height, - bin_window_width, - bin_window_height - tree_view->priv->height); - } - - if (node < 0) - return; - - /* find the path for the node */ - path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node); - gtk_tree_model_get_iter (tree_view->priv->model, - &iter, - path); - gtk_tree_path_free (path); - - cursor_path = NULL; - drag_dest_path = NULL; - - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - - if (cursor_path) - _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor); - - if (tree_view->priv->drag_dest_row) - drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); - - if (drag_dest_path) - _pspp_sheet_view_find_node (tree_view, drag_dest_path, - &drag_highlight); - - draw_vgrid_lines = - tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL - || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH; - draw_hgrid_lines = - tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL - || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH; - - if (draw_vgrid_lines || draw_hgrid_lines) - gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL); - - n_visible_columns = 0; - for (list = tree_view->priv->columns; list; list = list->next) - { - if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible) - continue; - n_visible_columns ++; - } - - /* Find the last column */ - for (last_column = g_list_last (tree_view->priv->columns); - last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible); - last_column = last_column->prev) - ; - - /* and the first */ - for (first_column = g_list_first (tree_view->priv->columns); - first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible); - first_column = first_column->next) - ; - - /* Actually process the expose event. To do this, we want to - * start at the first node of the event, and walk the tree in - * order, drawing each successive node. - */ - - min_y = y_offset; - do - { - gboolean parity; - gboolean is_first = FALSE; - gboolean is_last = FALSE; - gboolean done = FALSE; - gboolean selected; - - max_height = ROW_HEIGHT (tree_view); - - cell_offset = 0; - - background_area.y = y_offset + Zarea.y; - background_area.height = max_height; - max_y = background_area.y + max_height; - - flags = 0; - - if (node == tree_view->priv->prelight_node) - flags |= GTK_CELL_RENDERER_PRELIT; - - selected = pspp_sheet_view_node_is_selected (tree_view, node); - - parity = node % 2; - - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - PsppSheetViewColumn *column = list->data; - const gchar *detail = NULL; - gboolean selected_column; - GtkStateType state; - - if (!column->visible) - continue; - - if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - selected_column = column->selected && column->selectable; - else - selected_column = TRUE; - - if (selected && selected_column) - flags |= GTK_CELL_RENDERER_SELECTED; - else - flags &= ~GTK_CELL_RENDERER_SELECTED; - - if (column->show_sort_indicator) - flags |= GTK_CELL_RENDERER_SORTED; - else - flags &= ~GTK_CELL_RENDERER_SORTED; - - if (cursor == node) - flags |= GTK_CELL_RENDERER_FOCUSED; - else - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - background_area.x = cell_offset; - background_area.width = column->width; - - cell_area = background_area; - cell_area.y += vertical_separator / 2; - cell_area.x += horizontal_separator / 2; - cell_area.height -= vertical_separator; - cell_area.width -= horizontal_separator; - - if (draw_vgrid_lines) - { - if (list == first_column) - { - cell_area.width -= grid_line_width / 2; - } - else if (list == last_column) - { - cell_area.x += grid_line_width / 2; - cell_area.width -= grid_line_width / 2; - } - else - { - cell_area.x += grid_line_width / 2; - cell_area.width -= grid_line_width; - } - } - - if (draw_hgrid_lines) - { - cell_area.y += grid_line_width / 2; - cell_area.height -= grid_line_width; - } - - if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL)) - { - cell_offset += column->width; - continue; - } - - - pspp_sheet_view_column_cell_set_cell_data (column, - tree_view->priv->model, - &iter); - - /* Select the detail for drawing the cell. relevant - * factors are parity, sortedness, and whether to - * display rules. - */ - if (allow_rules && tree_view->priv->has_rules) - { - if ((flags & GTK_CELL_RENDERER_SORTED) && - n_visible_columns >= 3) - { - if (parity) - detail = "cell_odd_ruled_sorted"; - else - detail = "cell_even_ruled_sorted"; - } - else - { - if (parity) - detail = "cell_odd_ruled"; - else - detail = "cell_even_ruled"; - } - } - else - { - if ((flags & GTK_CELL_RENDERER_SORTED) && - n_visible_columns >= 3) - { - if (parity) - detail = "cell_odd_sorted"; - else - detail = "cell_even_sorted"; - } - else - { - if (parity) - detail = "cell_odd"; - else - detail = "cell_even"; - } - } - - g_assert (detail); - - gtk_style_context_save (context); - state = gtk_cell_renderer_get_state (NULL, widget, flags); - gtk_style_context_set_state (context, state); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL); - - /* Draw background */ - gtk_render_background (context, cr, - background_area.x, - background_area.y, - background_area.width, - background_area.height); - - /* Draw frame */ - gtk_render_frame (context, cr, - background_area.x, - background_area.y, - background_area.width, - background_area.height); - - if (draw_hgrid_lines) - { - cairo_set_line_width (cr, 1.0); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); - - if (background_area.y >= 0) - { - cairo_move_to (cr, background_area.x, background_area.y - 0.5); - cairo_line_to (cr, background_area.x + background_area.width, - background_area.y - 0.5); - } - - if (y_offset + max_height <= Zarea.height - 0.5) - { - cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5); - cairo_line_to (cr, background_area.x + background_area.width, - background_area.y + max_height - 0.5); - } - cairo_stroke (cr); - } - - _pspp_sheet_view_column_cell_render (column, - cr, - &background_area, - &cell_area, - flags); - - - cell_offset += column->width; - gtk_style_context_restore (context); - } - - if (node == drag_highlight) - { - /* Draw indicator for the drop - */ - gint highlight_y = -1; - int node = -1; - gint width; - - switch (tree_view->priv->drag_dest_pos) - { - case PSPP_SHEET_VIEW_DROP_BEFORE: - highlight_y = background_area.y - 1; - if (highlight_y < 0) - highlight_y = 0; - break; - - case PSPP_SHEET_VIEW_DROP_AFTER: - highlight_y = background_area.y + background_area.height - 1; - break; - - case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE: - case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER: - _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node); - - if (node < 0) - break; - width = gdk_window_get_width (tree_view->priv->bin_window); - - if (row_ending_details) - gtk_paint_focus (gtk_widget_get_style (widget), - cr, - gtk_widget_get_state (widget), - widget, - (is_first - ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" ) - : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )), - 0, BACKGROUND_FIRST_PIXEL (tree_view, node) - - focus_line_width / 2, - width, ROW_HEIGHT (tree_view) - - focus_line_width + 1); - else - gtk_paint_focus (gtk_widget_get_style (widget), - cr, - gtk_widget_get_state (widget), - widget, - "treeview-drop-indicator", - 0, BACKGROUND_FIRST_PIXEL (tree_view, node) - - focus_line_width / 2, - width, ROW_HEIGHT (tree_view) - - focus_line_width + 1); - break; - } - - } - - y_offset += max_height; - - do - { - node = pspp_sheet_view_node_next (tree_view, node); - if (node >= 0) - { - gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter); - done = TRUE; - - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_next); - } - else - goto done; - } - while (!done); - } - while (y_offset < Zarea.height); - -done: - pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns, - min_y, max_y); - - if (cursor_path) - gtk_tree_path_free (cursor_path); - - if (drag_dest_path) - gtk_tree_path_free (drag_dest_path); - - return; -} - - -static gboolean -pspp_sheet_view_draw (GtkWidget *widget, - cairo_t *cr) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GtkStyleContext *context; - - context = gtk_widget_get_style_context (widget); - - if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window)) - { - GList *tmp_list; - - cairo_save (cr); - gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window); - pspp_sheet_view_draw_bin (widget, cr); - cairo_restore (cr); - - /* We can't just chain up to Container::expose as it will try to send the - * event to the headers, so we handle propagating it to our children - * (eg. widgets being edited) ourselves. - */ - tmp_list = tree_view->priv->children; - while (tmp_list) - { - PsppSheetViewChild *child = tmp_list->data; - tmp_list = tmp_list->next; - - gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr); - } - } - else - { - gtk_render_background (context, cr, - 0, 0, - gtk_widget_get_allocated_width (widget), - gtk_widget_get_allocated_height (widget)); - } - - gtk_style_context_save (context); - gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW); - - if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window)) - { - gint n_visible_columns; - GList *list; - - for (list = tree_view->priv->columns; list != NULL; list = list->next) - { - PsppSheetViewColumn *column = list->data; - - if (column == tree_view->priv->drag_column || !column->visible) - continue; - - if (span_intersects (column->allocation.x, column->allocation.width, - (int) gtk_adjustment_get_value (tree_view->priv->hadjustment), - (int) gtk_widget_get_allocated_width (widget)) - && column->button != NULL) - gtk_container_propagate_draw (GTK_CONTAINER (tree_view), - column->button, cr); - } - - n_visible_columns = 0; - for (list = tree_view->priv->columns; list; list = list->next) - { - if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible) - continue; - n_visible_columns ++; - } - cairo_save (cr); - gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window); - pspp_sheet_view_draw_vertical_grid_lines (tree_view, - cr, - n_visible_columns, - 0, - TREE_VIEW_HEADER_HEIGHT (tree_view)); - cairo_restore (cr); - } - if (tree_view->priv->drag_window && - gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window)) - { - gtk_container_propagate_draw (GTK_CONTAINER (tree_view), - tree_view->priv->drag_column->button, - cr); - } - - gtk_style_context_restore (context); - return FALSE; -} - -enum -{ - DROP_HOME, - DROP_RIGHT, - DROP_LEFT, - DROP_END -}; - -/* returns 0x1 when no column has been found -- yes it's hackish */ -static PsppSheetViewColumn * -pspp_sheet_view_get_drop_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - gint drop_position) -{ - PsppSheetViewColumn *left_column = NULL; - PsppSheetViewColumn *cur_column = NULL; - GList *tmp_list; - - if (!column->reorderable) - return (PsppSheetViewColumn *)0x1; - - switch (drop_position) - { - case DROP_HOME: - /* find first column where we can drop */ - tmp_list = tree_view->priv->columns; - if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data)) - return (PsppSheetViewColumn *)0x1; - - while (tmp_list) - { - g_assert (tmp_list); - - cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; - - if (left_column && left_column->visible == FALSE) - continue; - - if (!tree_view->priv->column_drop_func) - return left_column; - - if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data)) - { - left_column = cur_column; - continue; - } - - return left_column; - } - - if (!tree_view->priv->column_drop_func) - return left_column; - - if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)) - return left_column; - else - return (PsppSheetViewColumn *)0x1; - break; - - case DROP_RIGHT: - /* find first column after column where we can drop */ - tmp_list = tree_view->priv->columns; - - for (; tmp_list; tmp_list = tmp_list->next) - if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column) - break; - - if (!tmp_list || !tmp_list->next) - return (PsppSheetViewColumn *)0x1; - - tmp_list = tmp_list->next; - left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; - - while (tmp_list) - { - g_assert (tmp_list); - - cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; - - if (left_column && left_column->visible == FALSE) - { - left_column = cur_column; - if (tmp_list) - tmp_list = tmp_list->next; - continue; - } - - if (!tree_view->priv->column_drop_func) - return left_column; - - if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data)) - { - left_column = cur_column; - continue; - } - - return left_column; - } - - if (!tree_view->priv->column_drop_func) - return left_column; - - if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)) - return left_column; - else - return (PsppSheetViewColumn *)0x1; - break; - - case DROP_LEFT: - /* find first column before column where we can drop */ - tmp_list = tree_view->priv->columns; - - for (; tmp_list; tmp_list = tmp_list->next) - if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column) - break; - - if (!tmp_list || !tmp_list->prev) - return (PsppSheetViewColumn *)0x1; - - tmp_list = tmp_list->prev; - cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->prev; - - while (tmp_list) - { - g_assert (tmp_list); - - left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - - if (left_column && !left_column->visible) - { - /*if (!tmp_list->prev) - return (PsppSheetViewColumn *)0x1; - */ -/* - cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data); - tmp_list = tmp_list->prev->prev; - continue;*/ - - cur_column = left_column; - if (tmp_list) - tmp_list = tmp_list->prev; - continue; - } - - if (!tree_view->priv->column_drop_func) - return left_column; - - if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data)) - return left_column; - - cur_column = left_column; - tmp_list = tmp_list->prev; - } - - if (!tree_view->priv->column_drop_func) - return NULL; - - if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data)) - return NULL; - else - return (PsppSheetViewColumn *)0x1; - break; - - case DROP_END: - /* same as DROP_HOME case, but doing it backwards */ - tmp_list = g_list_last (tree_view->priv->columns); - cur_column = NULL; - - if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data)) - return (PsppSheetViewColumn *)0x1; - - while (tmp_list) - { - g_assert (tmp_list); - - left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - - if (left_column && !left_column->visible) - { - cur_column = left_column; - tmp_list = tmp_list->prev; - } - - if (!tree_view->priv->column_drop_func) - return left_column; - - if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data)) - return left_column; - - cur_column = left_column; - tmp_list = tmp_list->prev; - } - - if (!tree_view->priv->column_drop_func) - return NULL; - - if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data)) - return NULL; - else - return (PsppSheetViewColumn *)0x1; - break; - } - - return (PsppSheetViewColumn *)0x1; -} - -static gboolean -pspp_sheet_view_key_press (GtkWidget *widget, - GdkEventKey *event) -{ - PsppSheetView *tree_view = (PsppSheetView *) widget; - - if (tree_view->priv->rubber_band_status) - { - if (event->keyval == GDK_Escape) - pspp_sheet_view_stop_rubber_band (tree_view); - - return TRUE; - } - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG)) - { - if (event->keyval == GDK_Escape) - { - tree_view->priv->cur_reorder = NULL; - pspp_sheet_view_button_release_drag_column (widget, NULL); - } - return TRUE; - } - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE)) - { - GList *focus_column; - gboolean rtl; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - for (focus_column = tree_view->priv->columns; - focus_column; - focus_column = focus_column->next) - { - PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data); - - if (column->button && gtk_widget_has_focus (column->button)) - break; - } - - if (focus_column && - (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) && - (event->keyval == GDK_Left || event->keyval == GDK_KP_Left - || event->keyval == GDK_Right || event->keyval == GDK_KP_Right)) - { - PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data); - - if (!column->resizable) - { - gtk_widget_error_bell (widget); - return TRUE; - } - - if (event->keyval == (rtl ? GDK_Right : GDK_Left) - || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left)) - { - gint old_width = column->resized_width; - - column->resized_width = MAX (column->resized_width, - column->width); - column->resized_width -= 2; - if (column->resized_width < 0) - column->resized_width = 0; - - if (column->min_width == -1) - column->resized_width = MAX (column->button_request, - column->resized_width); - else - column->resized_width = MAX (column->min_width, - column->resized_width); - - if (column->max_width != -1) - column->resized_width = MIN (column->resized_width, - column->max_width); - - column->use_resized_width = TRUE; - - if (column->resized_width != old_width) - gtk_widget_queue_resize (widget); - else - gtk_widget_error_bell (widget); - } - else if (event->keyval == (rtl ? GDK_Left : GDK_Right) - || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right)) - { - gint old_width = column->resized_width; - - column->resized_width = MAX (column->resized_width, - column->width); - column->resized_width += 2; - - if (column->max_width != -1) - column->resized_width = MIN (column->resized_width, - column->max_width); - - column->use_resized_width = TRUE; - - if (column->resized_width != old_width) - gtk_widget_queue_resize (widget); - else - gtk_widget_error_bell (widget); - } - - return TRUE; - } - - if (focus_column && - (event->state & GDK_MOD1_MASK) && - (event->keyval == GDK_Left || event->keyval == GDK_KP_Left - || event->keyval == GDK_Right || event->keyval == GDK_KP_Right - || event->keyval == GDK_Home || event->keyval == GDK_KP_Home - || event->keyval == GDK_End || event->keyval == GDK_KP_End)) - { - PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data); - - if (event->keyval == (rtl ? GDK_Right : GDK_Left) - || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left)) - { - PsppSheetViewColumn *col; - col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT); - if (col != (PsppSheetViewColumn *)0x1) - pspp_sheet_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - else if (event->keyval == (rtl ? GDK_Left : GDK_Right) - || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right)) - { - PsppSheetViewColumn *col; - col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT); - if (col != (PsppSheetViewColumn *)0x1) - pspp_sheet_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home) - { - PsppSheetViewColumn *col; - col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME); - if (col != (PsppSheetViewColumn *)0x1) - pspp_sheet_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - else if (event->keyval == GDK_End || event->keyval == GDK_KP_End) - { - PsppSheetViewColumn *col; - col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END); - if (col != (PsppSheetViewColumn *)0x1) - pspp_sheet_view_move_column_after (tree_view, column, col); - else - gtk_widget_error_bell (widget); - } - - return TRUE; - } - } - - /* Chain up to the parent class. It handles the keybindings. */ - if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event)) - return TRUE; - - if (tree_view->priv->search_entry_avoid_unhandled_binding) - { - tree_view->priv->search_entry_avoid_unhandled_binding = FALSE; - return FALSE; - } - - /* We pass the event to the search_entry. If its text changes, then we start - * the typeahead find capabilities. */ - if (gtk_widget_has_focus (GTK_WIDGET (tree_view)) - && tree_view->priv->enable_search - && !tree_view->priv->search_custom_entry_set) - { - GdkEvent *new_event; - char *old_text; - const char *new_text; - gboolean retval; - GdkScreen *screen; - gboolean text_modified; - gulong popup_menu_id; - - pspp_sheet_view_ensure_interactive_directory (tree_view); - - /* Make a copy of the current text */ - old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry))); - new_event = gdk_event_copy ((GdkEvent *) event); - g_object_unref (((GdkEventKey *) new_event)->window); - ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window)); - gtk_widget_realize (tree_view->priv->search_window); - - popup_menu_id = g_signal_connect (tree_view->priv->search_entry, - "popup-menu", G_CALLBACK (gtk_true), - NULL); - - /* Move the entry off screen */ - screen = gtk_widget_get_screen (GTK_WIDGET (tree_view)); - gtk_window_move (GTK_WINDOW (tree_view->priv->search_window), - gdk_screen_get_width (screen) + 1, - gdk_screen_get_height (screen) + 1); - gtk_widget_show (tree_view->priv->search_window); - - /* Send the event to the window. If the preedit_changed signal is emitted - * during this event, we will set priv->imcontext_changed */ - tree_view->priv->imcontext_changed = FALSE; - retval = gtk_widget_event (tree_view->priv->search_window, new_event); - gdk_event_free (new_event); - gtk_widget_hide (tree_view->priv->search_window); - - g_signal_handler_disconnect (tree_view->priv->search_entry, - popup_menu_id); - - /* We check to make sure that the entry tried to handle the text, and that - * the text has changed. - */ - new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)); - text_modified = strcmp (old_text, new_text) != 0; - g_free (old_text); - if (tree_view->priv->imcontext_changed || /* we're in a preedit */ - (retval && text_modified)) /* ...or the text was modified */ - { - if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE)) - { - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - return TRUE; - } - else - { - gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), ""); - return FALSE; - } - } - } - - return FALSE; -} - -static gboolean -pspp_sheet_view_key_release (GtkWidget *widget, - GdkEventKey *event) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - - if (tree_view->priv->rubber_band_status) - return TRUE; - - return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event); -} - -/* FIXME Is this function necessary? Can I get an enter_notify event - * w/o either an expose event or a mouse motion event? - */ -static gboolean -pspp_sheet_view_enter_notify (GtkWidget *widget, - GdkEventCrossing *event) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - int node; - gint new_y; - - /* Sanity check it */ - if (event->window != tree_view->priv->bin_window) - return FALSE; - - if (tree_view->priv->row_count == 0) - return FALSE; - - if (event->mode == GDK_CROSSING_GRAB || - event->mode == GDK_CROSSING_GTK_GRAB || - event->mode == GDK_CROSSING_GTK_UNGRAB || - event->mode == GDK_CROSSING_STATE_CHANGED) - return TRUE; - - /* find the node internally */ - new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y); - if (new_y < 0) - new_y = 0; - pspp_sheet_view_find_offset (tree_view, new_y, &node); - - tree_view->priv->event_last_x = event->x; - tree_view->priv->event_last_y = event->y; - - prelight_or_select (tree_view, node, event->x, event->y); - - return TRUE; -} - -static gboolean -pspp_sheet_view_leave_notify (GtkWidget *widget, - GdkEventCrossing *event) -{ - PsppSheetView *tree_view; - - if (event->mode == GDK_CROSSING_GRAB) - return TRUE; - - tree_view = PSPP_SHEET_VIEW (widget); - - if (tree_view->priv->prelight_node >= 0) - _pspp_sheet_view_queue_draw_node (tree_view, - tree_view->priv->prelight_node, - NULL); - - tree_view->priv->event_last_x = -10000; - tree_view->priv->event_last_y = -10000; - - prelight_or_select (tree_view, - -1, - -1000, -1000); /* coords not possibly over an arrow */ - - return TRUE; -} - - -static gint -pspp_sheet_view_focus_out (GtkWidget *widget, - GdkEventFocus *event) -{ - PsppSheetView *tree_view; - - tree_view = PSPP_SHEET_VIEW (widget); - - gtk_widget_queue_draw (widget); - - /* destroy interactive search dialog */ - if (tree_view->priv->search_window) - pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view); - - return FALSE; -} - - -/* Incremental Reflow - */ - -static void -pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view, - int node) -{ - GtkAllocation allocation; - gint y = pspp_sheet_view_node_find_offset (tree_view, node) - - gtk_adjustment_get_value (tree_view->priv->vadjustment) - + TREE_VIEW_HEADER_HEIGHT (tree_view); - - gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); - - gtk_widget_queue_draw_area (GTK_WIDGET (tree_view), - 0, y, - allocation.width, - tree_view->priv->fixed_height); -} - -static gboolean -node_is_visible (PsppSheetView *tree_view, - int node) -{ - int y; - int height; - - y = pspp_sheet_view_node_find_offset (tree_view, node); - height = ROW_HEIGHT (tree_view); - - if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) && - y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment) - + gtk_adjustment_get_page_size (tree_view->priv->vadjustment))) - return TRUE; - - return FALSE; -} - -/* Returns the row height. */ -static gint -validate_row (PsppSheetView *tree_view, - int node, - GtkTreeIter *iter, - GtkTreePath *path) -{ - PsppSheetViewColumn *column; - GList *list, *first_column, *last_column; - gint height = 0; - gint horizontal_separator; - gint vertical_separator; - gint focus_line_width; - gboolean draw_vgrid_lines, draw_hgrid_lines; - gint focus_pad; - gint grid_line_width; - gboolean wide_separators; - gint separator_height; - - gtk_widget_style_get (GTK_WIDGET (tree_view), - "focus-padding", &focus_pad, - "focus-line-width", &focus_line_width, - "horizontal-separator", &horizontal_separator, - "vertical-separator", &vertical_separator, - "grid-line-width", &grid_line_width, - "wide-separators", &wide_separators, - "separator-height", &separator_height, - NULL); - - draw_vgrid_lines = - tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL - || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH; - draw_hgrid_lines = - tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL - || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH; - - for (last_column = g_list_last (tree_view->priv->columns); - last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible); - last_column = last_column->prev) - ; - - for (first_column = g_list_first (tree_view->priv->columns); - first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible); - first_column = first_column->next) - ; - - for (list = tree_view->priv->columns; list; list = list->next) - { - gint tmp_width; - gint tmp_height; - - column = list->data; - - if (! column->visible) - continue; - - pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter); - pspp_sheet_view_column_cell_get_size (column, - NULL, NULL, NULL, - &tmp_width, &tmp_height); - - tmp_height += vertical_separator; - height = MAX (height, tmp_height); - - tmp_width = tmp_width + horizontal_separator; - - if (draw_vgrid_lines) - { - if (list->data == first_column || list->data == last_column) - tmp_width += grid_line_width / 2.0; - else - tmp_width += grid_line_width; - } - - if (tmp_width > column->requested_width) - column->requested_width = tmp_width; - } - - if (draw_hgrid_lines) - height += grid_line_width; - - tree_view->priv->post_validation_flag = TRUE; - return height; -} - - -static void -validate_visible_area (PsppSheetView *tree_view) -{ - GtkTreePath *path = NULL; - GtkTreePath *above_path = NULL; - GtkTreeIter iter; - int node = -1; - gint total_height; - gint area_above = 0; - gint area_below = 0; - GtkAllocation allocation; - - if (tree_view->priv->row_count == 0) - return; - - if (tree_view->priv->scroll_to_path == NULL) - return; - - gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); - - total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); - - if (total_height == 0) - return; - - path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path); - if (path) - { - /* we are going to scroll, and will update dy */ - _pspp_sheet_view_find_node (tree_view, path, &node); - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - - if (tree_view->priv->scroll_to_use_align) - { - gint height = ROW_HEIGHT (tree_view); - area_above = (total_height - height) * - tree_view->priv->scroll_to_row_align; - area_below = total_height - area_above - height; - area_above = MAX (area_above, 0); - area_below = MAX (area_below, 0); - } - else - { - /* two cases: - * 1) row not visible - * 2) row visible - */ - gint dy; - gint height = ROW_HEIGHT (tree_view); - - dy = pspp_sheet_view_node_find_offset (tree_view, node); - - if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) && - dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment) - + gtk_adjustment_get_page_size (tree_view->priv->vadjustment))) - { - /* row visible: keep the row at the same position */ - area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment); - area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) + - gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - - dy - height; - } - else - { - /* row not visible */ - if (dy >= 0 - && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - { - /* row at the beginning -- fixed */ - area_above = dy; - area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - - area_above - height; - } - else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) - - gtk_adjustment_get_page_size (tree_view->priv->vadjustment))) - { - /* row at the end -- fixed */ - area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) - - gtk_adjustment_get_page_size (tree_view->priv->vadjustment)); - area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - - area_above - height; - - if (area_below < 0) - { - area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height; - area_below = 0; - } - } - else - { - /* row somewhere in the middle, bring it to the top - * of the view - */ - area_above = 0; - area_below = total_height - height; - } - } - } - } - else - /* the scroll to isn't valid; ignore it. - */ - { - gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); - tree_view->priv->scroll_to_path = NULL; - return; - } - - above_path = gtk_tree_path_copy (path); - - /* Now, we walk forwards and backwards, measuring rows. Unfortunately, - * backwards is much slower then forward, as there is no iter_prev function. - * We go forwards first in case we run out of tree. Then we go backwards to - * fill out the top. - */ - while (node >= 0 && area_below > 0) - { - gboolean done = FALSE; - do - { - node = pspp_sheet_view_node_next (tree_view, node); - if (node >= 0) - { - gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter); - done = TRUE; - gtk_tree_path_next (path); - - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_next); - } - else - break; - } - while (!done); - - if (node < 0) - break; - - area_below -= ROW_HEIGHT (tree_view); - } - gtk_tree_path_free (path); - - /* If we ran out of tree, and have extra area_below left, we need to add it - * to area_above */ - if (area_below > 0) - area_above += area_below; - - _pspp_sheet_view_find_node (tree_view, above_path, &node); - - /* We walk backwards */ - while (area_above > 0) - { - node = pspp_sheet_view_node_prev (tree_view, node); - - /* Always find the new path in the tree. We cannot just assume - * a gtk_tree_path_prev() is enough here, as there might be children - * in between this node and the previous sibling node. If this - * appears to be a performance hotspot in profiles, we can look into - * intrigate logic for keeping path, node and iter in sync like - * we do for forward walks. (Which will be hard because of the lacking - * iter_prev). - */ - - if (node < 0) - break; - - gtk_tree_path_free (above_path); - above_path = _pspp_sheet_view_find_path (tree_view, node); - - gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path); - - area_above -= ROW_HEIGHT (tree_view); - } - - /* set the dy here to scroll to the path, - * and sync the top row accordingly - */ - pspp_sheet_view_set_top_row (tree_view, above_path, -area_above); - pspp_sheet_view_top_row_to_dy (tree_view); - - gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); - tree_view->priv->scroll_to_path = NULL; - - if (above_path) - gtk_tree_path_free (above_path); - - if (tree_view->priv->scroll_to_column) - { - tree_view->priv->scroll_to_column = NULL; - } - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} - -static void -initialize_fixed_height_mode (PsppSheetView *tree_view) -{ - if (!tree_view->priv->row_count) - return; - - if (tree_view->priv->fixed_height_set) - return; - - if (tree_view->priv->fixed_height < 0) - { - GtkTreeIter iter; - GtkTreePath *path; - - int node = 0; - - path = _pspp_sheet_view_find_path (tree_view, node); - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - - tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path); - - gtk_tree_path_free (path); - - g_object_notify (G_OBJECT (tree_view), "fixed-height"); - } -} - -/* Our strategy for finding nodes to validate is a little convoluted. We find - * the left-most uninvalidated node. We then try walking right, validating - * nodes. Once we find a valid node, we repeat the previous process of finding - * the first invalid node. - */ - -static gboolean -validate_rows_handler (PsppSheetView *tree_view) -{ - initialize_fixed_height_mode (tree_view); - if (tree_view->priv->validate_rows_timer) - { - g_source_remove (tree_view->priv->validate_rows_timer); - tree_view->priv->validate_rows_timer = 0; - } - - return FALSE; -} - -static gboolean -do_presize_handler (PsppSheetView *tree_view) -{ - GtkRequisition requisition; - - validate_visible_area (tree_view); - tree_view->priv->presize_handler_timer = 0; - - if (! gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return FALSE; - - gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), NULL, &requisition); - - gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width)); - gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height)); - gtk_adjustment_changed (tree_view->priv->hadjustment); - gtk_adjustment_changed (tree_view->priv->vadjustment); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - - return FALSE; -} - -static gboolean -presize_handler_callback (gpointer data) -{ - do_presize_handler (PSPP_SHEET_VIEW (data)); - - return FALSE; -} - -static void -install_presize_handler (PsppSheetView *tree_view) -{ - if (! gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return; - - if (! tree_view->priv->presize_handler_timer) - { - tree_view->priv->presize_handler_timer = - gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL); - } - if (! tree_view->priv->validate_rows_timer) - { - tree_view->priv->validate_rows_timer = - gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL); - } -} - -static gboolean -scroll_sync_handler (PsppSheetView *tree_view) -{ - if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0); - else if (gtk_tree_row_reference_valid (tree_view->priv->top_row)) - pspp_sheet_view_top_row_to_dy (tree_view); - else - pspp_sheet_view_dy_to_top_row (tree_view); - - tree_view->priv->scroll_sync_timer = 0; - - return FALSE; -} - -static void -install_scroll_sync_handler (PsppSheetView *tree_view) -{ - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return; - - if (!tree_view->priv->scroll_sync_timer) - { - tree_view->priv->scroll_sync_timer = - gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL); - } -} - -static void -pspp_sheet_view_set_top_row (PsppSheetView *tree_view, - GtkTreePath *path, - gint offset) -{ - gtk_tree_row_reference_free (tree_view->priv->top_row); - - if (!path) - { - tree_view->priv->top_row = NULL; - tree_view->priv->top_row_dy = 0; - } - else - { - tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path); - tree_view->priv->top_row_dy = offset; - } -} - -/* Always call this iff dy is in the visible range. If the tree is empty, then - * it's set to be NULL, and top_row_dy is 0; - */ -static void -pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view) -{ - gint offset; - GtkTreePath *path; - int node; - - if (tree_view->priv->row_count == 0) - { - pspp_sheet_view_set_top_row (tree_view, NULL, 0); - } - else - { - offset = pspp_sheet_view_find_offset (tree_view, - tree_view->priv->dy, - &node); - - if (node < 0) - { - pspp_sheet_view_set_top_row (tree_view, NULL, 0); - } - else - { - path = _pspp_sheet_view_find_path (tree_view, node); - pspp_sheet_view_set_top_row (tree_view, path, offset); - gtk_tree_path_free (path); - } - } -} - -static void -pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view) -{ - GtkTreePath *path; - int node; - int new_dy; - - /* Avoid recursive calls */ - if (tree_view->priv->in_top_row_to_dy) - return; - - if (tree_view->priv->top_row) - path = gtk_tree_row_reference_get_path (tree_view->priv->top_row); - else - path = NULL; - - if (!path) - node = -1; - else - _pspp_sheet_view_find_node (tree_view, path, &node); - - if (path) - gtk_tree_path_free (path); - - if (node < 0) - { - /* keep dy and set new toprow */ - gtk_tree_row_reference_free (tree_view->priv->top_row); - tree_view->priv->top_row = NULL; - tree_view->priv->top_row_dy = 0; - /* DO NOT install the idle handler */ - pspp_sheet_view_dy_to_top_row (tree_view); - return; - } - - if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy) - { - /* new top row -- do NOT install the idle handler */ - pspp_sheet_view_dy_to_top_row (tree_view); - return; - } - - new_dy = pspp_sheet_view_node_find_offset (tree_view, node); - new_dy += tree_view->priv->top_row_dy; - - if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height) - new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment); - - new_dy = MAX (0, new_dy); - - tree_view->priv->in_top_row_to_dy = TRUE; - gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy); - tree_view->priv->in_top_row_to_dy = FALSE; -} - - -void -_pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view) -{ - install_presize_handler (tree_view); -} - -/* Drag-and-drop */ - -static void -set_source_row (GdkDragContext *context, - GtkTreeModel *model, - GtkTreePath *source_row) -{ - g_object_set_data_full (G_OBJECT (context), - "gtk-tree-view-source-row", - source_row ? gtk_tree_row_reference_new (model, source_row) : NULL, - (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL)); -} - -static GtkTreePath* -get_source_row (GdkDragContext *context) -{ - GtkTreeRowReference *ref = - g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); - - if (ref) - return gtk_tree_row_reference_get_path (ref); - else - return NULL; -} - -typedef struct -{ - GtkTreeRowReference *dest_row; - guint path_down_mode : 1; - guint empty_view_drop : 1; - guint drop_append_mode : 1; -} -DestRow; - -static void -dest_row_free (gpointer data) -{ - DestRow *dr = (DestRow *)data; - - gtk_tree_row_reference_free (dr->dest_row); - g_slice_free (DestRow, dr); -} - -static void -set_dest_row (GdkDragContext *context, - GtkTreeModel *model, - GtkTreePath *dest_row, - gboolean path_down_mode, - gboolean empty_view_drop, - gboolean drop_append_mode) -{ - DestRow *dr; - - if (!dest_row) - { - g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row", - NULL, NULL); - return; - } - - dr = g_slice_new (DestRow); - - dr->dest_row = gtk_tree_row_reference_new (model, dest_row); - dr->path_down_mode = path_down_mode != FALSE; - dr->empty_view_drop = empty_view_drop != FALSE; - dr->drop_append_mode = drop_append_mode != FALSE; - - g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row", - dr, (GDestroyNotify) dest_row_free); -} - -static GtkTreePath* -get_dest_row (GdkDragContext *context, - gboolean *path_down_mode) -{ - DestRow *dr = - g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row"); - - if (dr) - { - GtkTreePath *path = NULL; - - if (path_down_mode) - *path_down_mode = dr->path_down_mode; - - if (dr->dest_row) - path = gtk_tree_row_reference_get_path (dr->dest_row); - else if (dr->empty_view_drop) - path = gtk_tree_path_new_from_indices (0, -1); - else - path = NULL; - - if (path && dr->drop_append_mode) - gtk_tree_path_next (path); - - return path; - } - else - return NULL; -} - -/* Get/set whether drag_motion requested the drag data and - * drag_data_received should thus not actually insert the data, - * since the data doesn't result from a drop. - */ -static void -set_status_pending (GdkDragContext *context, - GdkDragAction suggested_action) -{ - g_object_set_data (G_OBJECT (context), - "gtk-tree-view-status-pending", - GINT_TO_POINTER (suggested_action)); -} - -static GdkDragAction -get_status_pending (GdkDragContext *context) -{ - return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), - "gtk-tree-view-status-pending")); -} - -static TreeViewDragInfo* -get_info (PsppSheetView *tree_view) -{ - return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info"); -} - -static void -destroy_info (TreeViewDragInfo *di) -{ - g_slice_free (TreeViewDragInfo, di); -} - -static TreeViewDragInfo* -ensure_info (PsppSheetView *tree_view) -{ - TreeViewDragInfo *di; - - di = get_info (tree_view); - - if (di == NULL) - { - di = g_slice_new0 (TreeViewDragInfo); - - g_object_set_data_full (G_OBJECT (tree_view), - "gtk-tree-view-drag-info", - di, - (GDestroyNotify) destroy_info); - } - - return di; -} - -static void -remove_info (PsppSheetView *tree_view) -{ - g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL); -} - -#if 0 -static gint -drag_scan_timeout (gpointer data) -{ - PsppSheetView *tree_view; - gint x, y; - GdkModifierType state; - GtkTreePath *path = NULL; - PsppSheetViewColumn *column = NULL; - GdkRectangle visible_rect; - - GDK_THREADS_ENTER (); - - tree_view = PSPP_SHEET_VIEW (data); - - gdk_window_get_pointer (tree_view->priv->bin_window, - &x, &y, &state); - - pspp_sheet_view_get_visible_rect (tree_view, &visible_rect); - - /* See if we are near the edge. */ - if ((x - visible_rect.x) < SCROLL_EDGE_SIZE || - (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE || - (y - visible_rect.y) < SCROLL_EDGE_SIZE || - (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE) - { - pspp_sheet_view_get_path_at_pos (tree_view, - tree_view->priv->bin_window, - x, y, - &path, - &column, - NULL, - NULL); - - if (path != NULL) - { - pspp_sheet_view_scroll_to_cell (tree_view, - path, - column, - TRUE, - 0.5, 0.5); - - gtk_tree_path_free (path); - } - } - - GDK_THREADS_LEAVE (); - - return TRUE; -} -#endif /* 0 */ - -static void -add_scroll_timeout (PsppSheetView *tree_view) -{ - if (tree_view->priv->scroll_timeout == 0) - { - tree_view->priv->scroll_timeout = - gdk_threads_add_timeout (150, scroll_row_timeout, tree_view); - } -} - -static void -remove_scroll_timeout (PsppSheetView *tree_view) -{ - if (tree_view->priv->scroll_timeout != 0) - { - g_source_remove (tree_view->priv->scroll_timeout); - tree_view->priv->scroll_timeout = 0; - } -} - -static gboolean -check_model_dnd (GtkTreeModel *model, - GType required_iface, - const gchar *signal) -{ - if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface)) - { - g_warning ("You must override the default '%s' handler " - "on PsppSheetView when using models that don't support " - "the %s interface and enabling drag-and-drop. The simplest way to do this " - "is to connect to '%s' and call " - "g_signal_stop_emission_by_name() in your signal handler to prevent " - "the default handler from running. Look at the source code " - "for the default handler in gtktreeview.c to get an idea what " - "your handler should do. (gtktreeview.c is in the GTK source " - "code.) If you're using GTK from a language other than C, " - "there may be a more natural way to override default handlers, e.g. via derivation.", - signal, g_type_name (required_iface), signal); - return FALSE; - } - else - return TRUE; -} - -static gboolean -scroll_row_timeout (gpointer data) -{ - PsppSheetView *tree_view = data; - - pspp_sheet_view_horizontal_autoscroll (tree_view); - pspp_sheet_view_vertical_autoscroll (tree_view); - - if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE) - pspp_sheet_view_update_rubber_band (tree_view); - - return TRUE; -} - -/* Returns TRUE if event should not be propagated to parent widgets */ -static gboolean -set_destination_row (PsppSheetView *tree_view, - GdkDragContext *context, - /* coordinates relative to the widget */ - gint x, - gint y, - GdkDragAction *suggested_action, - GdkAtom *target) -{ - GtkTreePath *path = NULL; - PsppSheetViewDropPosition pos; - PsppSheetViewDropPosition old_pos; - TreeViewDragInfo *di; - GtkWidget *widget; - GtkTreePath *old_dest_path = NULL; - gboolean can_drop = FALSE; - - *suggested_action = 0; - *target = GDK_NONE; - - widget = GTK_WIDGET (tree_view); - - di = get_info (tree_view); - - if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0) - { - /* someone unset us as a drag dest, note that if - * we return FALSE drag_leave isn't called - */ - - pspp_sheet_view_set_drag_dest_row (tree_view, - NULL, - PSPP_SHEET_VIEW_DROP_BEFORE); - - remove_scroll_timeout (PSPP_SHEET_VIEW (widget)); - - return FALSE; /* no longer a drop site */ - } - - *target = gtk_drag_dest_find_target (widget, context, - gtk_drag_dest_get_target_list (widget)); - if (*target == GDK_NONE) - { - return FALSE; - } - - if (!pspp_sheet_view_get_dest_row_at_pos (tree_view, - x, y, - &path, - &pos)) - { - gint n_children; - GtkTreeModel *model; - - /* the row got dropped on empty space, let's setup a special case - */ - - if (path) - gtk_tree_path_free (path); - - model = pspp_sheet_view_get_model (tree_view); - - n_children = gtk_tree_model_iter_n_children (model, NULL); - if (n_children) - { - pos = PSPP_SHEET_VIEW_DROP_AFTER; - path = gtk_tree_path_new_from_indices (n_children - 1, -1); - } - else - { - pos = PSPP_SHEET_VIEW_DROP_BEFORE; - path = gtk_tree_path_new_from_indices (0, -1); - } - - can_drop = TRUE; - - goto out; - } - - g_assert (path); - - /* If we left the current row's "open" zone, unset the timeout for - * opening the row - */ - pspp_sheet_view_get_drag_dest_row (tree_view, - &old_dest_path, - &old_pos); - - if (old_dest_path) - gtk_tree_path_free (old_dest_path); - - if (TRUE /* FIXME if the location droppable predicate */) - { - can_drop = TRUE; - } - -out: - if (can_drop) - { - GtkWidget *source_widget; - - *suggested_action = gdk_drag_context_get_suggested_action (context); - source_widget = gtk_drag_get_source_widget (context); - - if (source_widget == widget) - { - /* Default to MOVE, unless the user has - * pressed ctrl or shift to affect available actions - */ - if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0) - *suggested_action = GDK_ACTION_MOVE; - } - - pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget), - path, pos); - } - else - { - /* can't drop here */ - pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget), - NULL, - PSPP_SHEET_VIEW_DROP_BEFORE); - } - - if (path) - gtk_tree_path_free (path); - - return TRUE; -} - -static GtkTreePath* -get_logical_dest_row (PsppSheetView *tree_view, - gboolean *path_down_mode, - gboolean *drop_append_mode) -{ - /* adjust path to point to the row the drop goes in front of */ - GtkTreePath *path = NULL; - PsppSheetViewDropPosition pos; - - g_return_val_if_fail (path_down_mode != NULL, NULL); - g_return_val_if_fail (drop_append_mode != NULL, NULL); - - *path_down_mode = FALSE; - *drop_append_mode = 0; - - pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos); - - if (path == NULL) - return NULL; - - if (pos == PSPP_SHEET_VIEW_DROP_BEFORE) - ; /* do nothing */ - else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE || - pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER) - *path_down_mode = TRUE; - else - { - GtkTreeIter iter; - GtkTreeModel *model = pspp_sheet_view_get_model (tree_view); - - g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER); - - if (!gtk_tree_model_get_iter (model, &iter, path) || - !gtk_tree_model_iter_next (model, &iter)) - *drop_append_mode = 1; - else - { - *drop_append_mode = 0; - gtk_tree_path_next (path); - } - } - - return path; -} - -static gboolean -pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view, - GdkEventMotion *event) -{ - GtkWidget *widget = GTK_WIDGET (tree_view); - GdkDragContext *context; - TreeViewDragInfo *di; - GtkTreePath *path = NULL; - gint button; - gint cell_x, cell_y; - GtkTreeModel *model; - gboolean retval = FALSE; - - di = get_info (tree_view); - - if (di == NULL || !di->source_set) - goto out; - - if (tree_view->priv->pressed_button < 0) - goto out; - - if (!gtk_drag_check_threshold (widget, - tree_view->priv->press_start_x, - tree_view->priv->press_start_y, - event->x, event->y)) - goto out; - - model = pspp_sheet_view_get_model (tree_view); - - if (model == NULL) - goto out; - - button = tree_view->priv->pressed_button; - tree_view->priv->pressed_button = -1; - - pspp_sheet_view_get_path_at_pos (tree_view, - tree_view->priv->press_start_x, - tree_view->priv->press_start_y, - &path, - NULL, - &cell_x, - &cell_y); - - if (path == NULL) - goto out; - - if (!GTK_IS_TREE_DRAG_SOURCE (model) || - !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model), - path)) - goto out; - - if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask)) - goto out; - - /* Now we can begin the drag */ - - retval = TRUE; - - context = gtk_drag_begin (widget, - gtk_drag_source_get_target_list (widget), - di->source_actions, - button, - (GdkEvent*)event); - - set_source_row (context, model, path); - - out: - if (path) - gtk_tree_path_free (path); - - return retval; -} - - - -static void -pspp_sheet_view_drag_begin (GtkWidget *widget, - GdkDragContext *context) -{ -} - - -static void -pspp_sheet_view_drag_end (GtkWidget *widget, - GdkDragContext *context) -{ - /* do nothing */ -} - -/* Default signal implementations for the drag signals */ -static void -pspp_sheet_view_drag_data_get (GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time) -{ - PsppSheetView *tree_view; - GtkTreeModel *model; - TreeViewDragInfo *di; - GtkTreePath *source_row; - - tree_view = PSPP_SHEET_VIEW (widget); - - model = pspp_sheet_view_get_model (tree_view); - - if (model == NULL) - return; - - di = get_info (PSPP_SHEET_VIEW (widget)); - - if (di == NULL) - return; - - source_row = get_source_row (context); - - if (source_row == NULL) - return; - - /* We can implement the GTK_TREE_MODEL_ROW target generically for - * any model; for DragSource models there are some other targets - * we also support. - */ - - if (GTK_IS_TREE_DRAG_SOURCE (model) && - gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), - source_row, - selection_data)) - goto done; - - /* If drag_data_get does nothing, try providing row data. */ - if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW")) - { - gtk_tree_set_row_drag_data (selection_data, - model, - source_row); - } - - done: - gtk_tree_path_free (source_row); -} - - -static void -pspp_sheet_view_drag_data_delete (GtkWidget *widget, - GdkDragContext *context) -{ - TreeViewDragInfo *di; - GtkTreeModel *model; - PsppSheetView *tree_view; - GtkTreePath *source_row; - - tree_view = PSPP_SHEET_VIEW (widget); - model = pspp_sheet_view_get_model (tree_view); - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete")) - return; - - di = get_info (tree_view); - - if (di == NULL) - return; - - source_row = get_source_row (context); - - if (source_row == NULL) - return; - - gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), - source_row); - - gtk_tree_path_free (source_row); - - set_source_row (context, NULL, NULL); -} - -static void -pspp_sheet_view_drag_leave (GtkWidget *widget, - GdkDragContext *context, - guint time) -{ - /* unset any highlight row */ - pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget), - NULL, - PSPP_SHEET_VIEW_DROP_BEFORE); - - remove_scroll_timeout (PSPP_SHEET_VIEW (widget)); -} - - -static gboolean -pspp_sheet_view_drag_motion (GtkWidget *widget, - GdkDragContext *context, - /* coordinates relative to the widget */ - gint x, - gint y, - guint time) -{ - gboolean empty; - GtkTreePath *path = NULL; - PsppSheetViewDropPosition pos; - PsppSheetView *tree_view; - GdkDragAction suggested_action = 0; - GdkAtom target; - - tree_view = PSPP_SHEET_VIEW (widget); - - if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) - return FALSE; - - pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos); - - /* we only know this *after* set_desination_row */ - empty = tree_view->priv->empty_view_drop; - - if (path == NULL && !empty) - { - /* Can't drop here. */ - gdk_drag_status (context, 0, time); - } - else - { - if (tree_view->priv->open_dest_timeout == 0 && - (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER || - pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE)) - { - /* Nothing. */ - } - else - { - add_scroll_timeout (tree_view); - } - - if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW")) - { - /* Request data so we can use the source row when - * determining whether to accept the drop - */ - set_status_pending (context, suggested_action); - gtk_drag_get_data (widget, context, target, time); - } - else - { - set_status_pending (context, 0); - gdk_drag_status (context, suggested_action, time); - } - } - - if (path) - gtk_tree_path_free (path); - - return TRUE; -} - - -static gboolean -pspp_sheet_view_drag_drop (GtkWidget *widget, - GdkDragContext *context, - /* coordinates relative to the widget */ - gint x, - gint y, - guint time) -{ - PsppSheetView *tree_view; - GtkTreePath *path; - GdkDragAction suggested_action = 0; - GdkAtom target = GDK_NONE; - TreeViewDragInfo *di; - GtkTreeModel *model; - gboolean path_down_mode; - gboolean drop_append_mode; - - tree_view = PSPP_SHEET_VIEW (widget); - - model = pspp_sheet_view_get_model (tree_view); - - remove_scroll_timeout (PSPP_SHEET_VIEW (widget)); - - di = get_info (tree_view); - - if (di == NULL) - return FALSE; - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop")) - return FALSE; - - if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) - return FALSE; - - path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode); - - if (target != GDK_NONE && path != NULL) - { - /* in case a motion had requested drag data, change things so we - * treat drag data receives as a drop. - */ - set_status_pending (context, 0); - set_dest_row (context, model, path, - path_down_mode, tree_view->priv->empty_view_drop, - drop_append_mode); - } - - if (path) - gtk_tree_path_free (path); - - /* Unset this thing */ - pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget), - NULL, - PSPP_SHEET_VIEW_DROP_BEFORE); - - if (target != GDK_NONE) - { - gtk_drag_get_data (widget, context, target, time); - return TRUE; - } - else - return FALSE; -} - -static void -pspp_sheet_view_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - /* coordinates relative to the widget */ - gint x, - gint y, - GtkSelectionData *selection_data, - guint info, - guint time) -{ - GtkTreePath *path; - TreeViewDragInfo *di; - gboolean accepted = FALSE; - GtkTreeModel *model; - PsppSheetView *tree_view; - GtkTreePath *dest_row; - GdkDragAction suggested_action; - gboolean path_down_mode; - gboolean drop_append_mode; - - tree_view = PSPP_SHEET_VIEW (widget); - - model = pspp_sheet_view_get_model (tree_view); - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received")) - return; - - di = get_info (tree_view); - - if (di == NULL) - return; - - suggested_action = get_status_pending (context); - - if (suggested_action) - { - /* We are getting this data due to a request in drag_motion, - * rather than due to a request in drag_drop, so we are just - * supposed to call drag_status, not actually paste in the - * data. - */ - path = get_logical_dest_row (tree_view, &path_down_mode, - &drop_append_mode); - - if (path == NULL) - suggested_action = 0; - else if (path_down_mode) - gtk_tree_path_down (path); - - if (suggested_action) - { - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - path, - selection_data)) - { - if (path_down_mode) - { - path_down_mode = FALSE; - gtk_tree_path_up (path); - - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - path, - selection_data)) - suggested_action = 0; - } - else - suggested_action = 0; - } - } - - gdk_drag_status (context, suggested_action, time); - - if (path) - gtk_tree_path_free (path); - - /* If you can't drop, remove user drop indicator until the next motion */ - if (suggested_action == 0) - pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget), - NULL, - PSPP_SHEET_VIEW_DROP_BEFORE); - - return; - } - - dest_row = get_dest_row (context, &path_down_mode); - - if (dest_row == NULL) - return; - - if (gtk_selection_data_get_length (selection_data) >= 0) - { - if (path_down_mode) - { - gtk_tree_path_down (dest_row); - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - dest_row, selection_data)) - gtk_tree_path_up (dest_row); - } - } - - if (gtk_selection_data_get_length (selection_data) >= 0) - { - if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), - dest_row, - selection_data)) - accepted = TRUE; - } - - gtk_drag_finish (context, - accepted, - (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE), - time); - - if (gtk_tree_path_get_depth (dest_row) == 1 - && gtk_tree_path_get_indices (dest_row)[0] == 0) - { - /* special special case drag to "0", scroll to first item */ - if (!tree_view->priv->scroll_to_path) - pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0); - } - - gtk_tree_path_free (dest_row); - - /* drop dest_row */ - set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE); -} - - - -/* GtkContainer Methods - */ - - -static void -pspp_sheet_view_remove (GtkContainer *container, - GtkWidget *widget) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (container); - PsppSheetViewChild *child = NULL; - GList *tmp_list; - - tmp_list = tree_view->priv->children; - while (tmp_list) - { - child = tmp_list->data; - if (child->widget == widget) - { - gtk_widget_unparent (widget); - - tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list); - g_list_free_1 (tmp_list); - g_slice_free (PsppSheetViewChild, child); - return; - } - - tmp_list = tmp_list->next; - } - - tmp_list = tree_view->priv->columns; - - while (tmp_list) - { - PsppSheetViewColumn *column; - column = tmp_list->data; - if (column->button == widget) - { - gtk_widget_unparent (widget); - return; - } - tmp_list = tmp_list->next; - } -} - -static void -pspp_sheet_view_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (container); - PsppSheetViewChild *child = NULL; - PsppSheetViewColumn *column; - GList *tmp_list; - - tmp_list = tree_view->priv->children; - while (tmp_list) - { - child = tmp_list->data; - tmp_list = tmp_list->next; - - (* callback) (child->widget, callback_data); - } - if (include_internals == FALSE) - return; - - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - { - column = tmp_list->data; - - if (column->button) - (* callback) (column->button, callback_data); - } -} - -/* Returns TRUE if the treeview contains no "special" (editable or activatable) - * cells. If so we draw one big row-spanning focus rectangle. - */ -static gboolean -pspp_sheet_view_has_special_cell (PsppSheetView *tree_view) -{ - GList *list; - - if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT) - return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES; - - for (list = tree_view->priv->columns; list; list = list->next) - { - if (!((PsppSheetViewColumn *)list->data)->visible) - continue; - if (_pspp_sheet_view_column_count_special_cells (list->data)) - return TRUE; - } - - return FALSE; -} - -static void -pspp_sheet_view_focus_column (PsppSheetView *tree_view, - PsppSheetViewColumn *focus_column, - gboolean clamp_column_visible) -{ - g_return_if_fail (focus_column != NULL); - - tree_view->priv->focus_column = focus_column; - - if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button) - gtk_widget_grab_focus (focus_column->button); - - if (clamp_column_visible) - pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE); -} - -/* Returns TRUE if the focus is within the headers, after the focus operation is - * done - */ -static gboolean -pspp_sheet_view_header_focus (PsppSheetView *tree_view, - GtkDirectionType dir, - gboolean clamp_column_visible) -{ - GtkWidget *focus_child; - PsppSheetViewColumn *focus_column; - GList *last_column, *first_column; - GList *tmp_list; - gboolean rtl; - - if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE)) - return FALSE; - - focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view)); - - first_column = tree_view->priv->columns; - while (first_column) - { - PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data); - - if (pspp_sheet_view_column_can_focus (c) && c->visible) - break; - first_column = first_column->next; - } - - /* No headers are visible, or are focusable. We can't focus in or out. - */ - if (first_column == NULL) - return FALSE; - - last_column = g_list_last (tree_view->priv->columns); - while (last_column) - { - PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data); - - if (pspp_sheet_view_column_can_focus (c) && c->visible) - break; - last_column = last_column->prev; - } - - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - switch (dir) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_UP: - case GTK_DIR_DOWN: - if (focus_child == NULL) - { - if (tree_view->priv->focus_column != NULL && - pspp_sheet_view_column_can_focus (tree_view->priv->focus_column)) - focus_column = tree_view->priv->focus_column; - else - focus_column = first_column->data; - pspp_sheet_view_focus_column (tree_view, focus_column, - clamp_column_visible); - return TRUE; - } - return FALSE; - - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - if (focus_child == NULL) - { - if (tree_view->priv->focus_column != NULL) - focus_column = tree_view->priv->focus_column; - else if (dir == GTK_DIR_LEFT) - focus_column = last_column->data; - else - focus_column = first_column->data; - pspp_sheet_view_focus_column (tree_view, focus_column, - clamp_column_visible); - return TRUE; - } - - if (gtk_widget_child_focus (focus_child, dir)) - { - /* The focus moves inside the button. */ - /* This is probably a great example of bad UI */ - if (clamp_column_visible) - pspp_sheet_view_clamp_column_visible (tree_view, - tree_view->priv->focus_column, - FALSE); - return TRUE; - } - - /* We need to move the focus among the row of buttons. */ - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child) - break; - - if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT)) - || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))) - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - return TRUE; - } - - while (tmp_list) - { - PsppSheetViewColumn *column; - - if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)) - tmp_list = tmp_list->next; - else - tmp_list = tmp_list->prev; - - if (tmp_list == NULL) - { - g_warning ("Internal button not found"); - break; - } - column = tmp_list->data; - if (column->visible && - pspp_sheet_view_column_can_focus (column)) - { - if (column->button) - { - pspp_sheet_view_focus_column (tree_view, column, - clamp_column_visible); - return TRUE; - } - } - } - return FALSE; - - default: - g_assert_not_reached (); - break; - } - - return FALSE; -} - -/* This function returns in 'path' the first focusable path, if the given path - * is already focusable, it's the returned one. - * - */ -static gboolean -search_first_focusable_path (PsppSheetView *tree_view, - GtkTreePath **path, - gboolean search_forward, - int *new_node) -{ - /* XXX this function is trivial given that the sheetview doesn't support - separator rows */ - int node = -1; - - if (!path || !*path) - return FALSE; - - _pspp_sheet_view_find_node (tree_view, *path, &node); - - if (node < 0) - return FALSE; - - if (new_node) - *new_node = node; - - return (*path != NULL); -} - -static gint -pspp_sheet_view_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GtkContainer *container = GTK_CONTAINER (widget); - GtkWidget *focus_child; - - if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget)) - return FALSE; - - focus_child = gtk_container_get_focus_child (container); - - pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE); - /* Case 1. Headers currently have focus. */ - if (focus_child) - { - switch (direction) - { - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - pspp_sheet_view_header_focus (tree_view, direction, TRUE); - return TRUE; - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - return FALSE; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - gtk_widget_grab_focus (widget); - return TRUE; - default: - g_assert_not_reached (); - return FALSE; - } - } - - /* Case 2. We don't have focus at all. */ - if (!gtk_widget_has_focus (widget)) - { - if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE)) - gtk_widget_grab_focus (widget); - return TRUE; - } - - /* Case 3. We have focus already. */ - if (direction == GTK_DIR_TAB_BACKWARD) - return (pspp_sheet_view_header_focus (tree_view, direction, FALSE)); - else if (direction == GTK_DIR_TAB_FORWARD) - return FALSE; - - /* Other directions caught by the keybindings */ - gtk_widget_grab_focus (widget); - return TRUE; -} - -static void -pspp_sheet_view_grab_focus (GtkWidget *widget) -{ - GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget); - - pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget)); -} - -static void -pspp_sheet_view_style_updated (GtkWidget *widget) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - GList *list; - PsppSheetViewColumn *column; - GtkStyleContext *context; - - GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget); - - if (gtk_widget_get_realized (widget)) - { - context = gtk_widget_get_style_context (GTK_WIDGET (tree_view)); - gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view))); - gtk_style_context_set_background (context, tree_view->priv->header_window); - pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines); - } - - gtk_widget_style_get (widget, - "expander-size", &tree_view->priv->expander_size, - NULL); - tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING; - - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - _pspp_sheet_view_column_cell_set_dirty (column); - } - - tree_view->priv->fixed_height = -1; - - /* Invalidate cached button style. */ - if (tree_view->priv->button_style) - { - g_object_unref (tree_view->priv->button_style); - tree_view->priv->button_style = NULL; - } - - gtk_widget_queue_resize (widget); -} - - -static void -pspp_sheet_view_set_focus_child (GtkContainer *container, - GtkWidget *child) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (container); - GList *list; - - for (list = tree_view->priv->columns; list; list = list->next) - { - if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child) - { - tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data); - break; - } - } - - GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child); -} - -static void -pspp_sheet_view_set_adjustments (PsppSheetView *tree_view, - GtkAdjustment *hadj, - GtkAdjustment *vadj) -{ - gboolean need_adjust = FALSE; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (hadj) - g_return_if_fail (GTK_IS_ADJUSTMENT (hadj)); - else - hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); - if (vadj) - g_return_if_fail (GTK_IS_ADJUSTMENT (vadj)); - else - vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); - - if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj)) - { - g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment, - pspp_sheet_view_adjustment_changed, - tree_view); - g_object_unref (tree_view->priv->hadjustment); - } - - if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj)) - { - g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment, - pspp_sheet_view_adjustment_changed, - tree_view); - g_object_unref (tree_view->priv->vadjustment); - } - - if (tree_view->priv->hadjustment != hadj) - { - tree_view->priv->hadjustment = hadj; - g_object_ref_sink (tree_view->priv->hadjustment); - - g_signal_connect (tree_view->priv->hadjustment, "value-changed", - G_CALLBACK (pspp_sheet_view_adjustment_changed), - tree_view); - need_adjust = TRUE; - } - - if (tree_view->priv->vadjustment != vadj) - { - tree_view->priv->vadjustment = vadj; - g_object_ref_sink (tree_view->priv->vadjustment); - - g_signal_connect (tree_view->priv->vadjustment, "value-changed", - G_CALLBACK (pspp_sheet_view_adjustment_changed), - tree_view); - need_adjust = TRUE; - } - - if (need_adjust) - pspp_sheet_view_adjustment_changed (NULL, tree_view); -} - - -static gboolean -pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view, - GtkMovementStep step, - gint count) -{ - PsppSheetSelectMode mode; - GdkModifierType state; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS || - step == GTK_MOVEMENT_VISUAL_POSITIONS || - step == GTK_MOVEMENT_DISPLAY_LINES || - step == GTK_MOVEMENT_PAGES || - step == GTK_MOVEMENT_BUFFER_ENDS || - step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE); - - if (tree_view->priv->row_count == 0) - return FALSE; - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - pspp_sheet_view_stop_editing (tree_view, FALSE); - PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - mode = 0; - if (gtk_get_current_event_state (&state)) - { - if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) - mode |= PSPP_SHEET_SELECT_MODE_TOGGLE; - if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) - mode |= PSPP_SHEET_SELECT_MODE_EXTEND; - } - /* else we assume not pressed */ - - switch (step) - { - case GTK_MOVEMENT_LOGICAL_POSITIONS: - pspp_sheet_view_move_cursor_tab (tree_view, count); - break; - case GTK_MOVEMENT_VISUAL_POSITIONS: - pspp_sheet_view_move_cursor_left_right (tree_view, count, mode); - break; - case GTK_MOVEMENT_DISPLAY_LINES: - pspp_sheet_view_move_cursor_up_down (tree_view, count, mode); - break; - case GTK_MOVEMENT_PAGES: - pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode); - break; - case GTK_MOVEMENT_BUFFER_ENDS: - pspp_sheet_view_move_cursor_start_end (tree_view, count, mode); - break; - case GTK_MOVEMENT_DISPLAY_LINE_ENDS: - pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode); - break; - default: - g_assert_not_reached (); - } - - return TRUE; -} - -static void -pspp_sheet_view_put (PsppSheetView *tree_view, - GtkWidget *child_widget, - GtkTreePath *path, - PsppSheetViewColumn *column) -{ - PsppSheetViewChild *child; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (GTK_IS_WIDGET (child_widget)); - - child = g_slice_new (PsppSheetViewChild); - - child->widget = child_widget; - _pspp_sheet_view_find_node (tree_view, path, &child->node); - if (child->node < 0) - { - g_assert_not_reached (); - } - child->column = column; - - tree_view->priv->children = g_list_append (tree_view->priv->children, child); - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window); - - gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view)); -} - -/* TreeModel Callbacks - */ - -static void -pspp_sheet_view_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - PsppSheetView *tree_view = (PsppSheetView *)data; - int node; - gboolean free_path = FALSE; - GtkTreePath *cursor_path; - - g_return_if_fail (path != NULL || iter != NULL); - - if (tree_view->priv->cursor != NULL) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - else - cursor_path = NULL; - - if (tree_view->priv->edited_column && - (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0)) - pspp_sheet_view_stop_editing (tree_view, TRUE); - - if (cursor_path != NULL) - gtk_tree_path_free (cursor_path); - - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, iter, path); - - _pspp_sheet_view_find_node (tree_view, - path, - &node); - - if (node >= 0) - { - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - pspp_sheet_view_node_queue_redraw (tree_view, node); - } - - if (free_path) - gtk_tree_path_free (path); -} - -static void -pspp_sheet_view_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - PsppSheetView *tree_view = (PsppSheetView *) data; - gint *indices; - int tmpnode = -1; - gint height = tree_view->priv->fixed_height; - gboolean free_path = FALSE; - gboolean node_visible = TRUE; - - g_return_if_fail (path != NULL || iter != NULL); - - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, iter, path); - - tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL); - - /* Update all row-references */ - gtk_tree_row_reference_inserted (G_OBJECT (data), path); - indices = gtk_tree_path_get_indices (path); - tmpnode = indices[0]; - - range_tower_insert0 (tree_view->priv->selected, tmpnode, 1); - - if (height > 0) - { - if (node_visible && node_is_visible (tree_view, tmpnode)) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - else - gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view)); - } - else - install_presize_handler (tree_view); - if (free_path) - gtk_tree_path_free (path); -} - -static void -pspp_sheet_view_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data) -{ - PsppSheetView *tree_view = (PsppSheetView *)data; - int node; - - g_return_if_fail (path != NULL); - - gtk_tree_row_reference_deleted (G_OBJECT (data), path); - - _pspp_sheet_view_find_node (tree_view, path, &node); - - if (node < 0) - return; - - range_tower_delete (tree_view->priv->selected, node, 1); - - /* Ensure we don't have a dangling pointer to a dead node */ - ensure_unprelighted (tree_view); - - /* Cancel editting if we've started */ - pspp_sheet_view_stop_editing (tree_view, TRUE); - - if (tree_view->priv->destroy_count_func) - { - gint child_count = 0; - tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data); - } - - tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL); - - if (! gtk_tree_row_reference_valid (tree_view->priv->top_row)) - { - gtk_tree_row_reference_free (tree_view->priv->top_row); - tree_view->priv->top_row = NULL; - } - - install_scroll_sync_handler (tree_view); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - -#if 0 - if (helper_data.changed) - g_signal_emit_by_name (tree_view->priv->selection, "changed"); -#endif -} - -static void -pspp_sheet_view_rows_reordered (GtkTreeModel *model, - GtkTreePath *parent, - GtkTreeIter *iter, - gint *new_order, - gpointer data) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (data); - gint len; - - /* XXX need to adjust selection */ - len = gtk_tree_model_iter_n_children (model, iter); - - if (len < 2) - return; - - gtk_tree_row_reference_reordered (G_OBJECT (data), - parent, - iter, - new_order); - - if (gtk_tree_path_get_depth (parent) != 0) - return; - - if (tree_view->priv->edited_column) - pspp_sheet_view_stop_editing (tree_view, TRUE); - - /* we need to be unprelighted */ - ensure_unprelighted (tree_view); - - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - pspp_sheet_view_dy_to_top_row (tree_view); -} - - -/* Internal tree functions - */ - - -static void -pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - gint *x1, - gint *x2) -{ - PsppSheetViewColumn *tmp_column = NULL; - gint total_width; - GList *list; - gboolean rtl; - - if (x1) - *x1 = 0; - - if (x2) - *x2 = 0; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - total_width = 0; - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - tmp_column = list->data; - - if (tmp_column == column) - break; - - if (tmp_column->visible) - total_width += tmp_column->width; - } - - if (tmp_column != column) - { - g_warning (G_STRLOC": passed-in column isn't in the tree"); - return; - } - - if (x1) - *x1 = total_width; - - if (x2) - { - if (column->visible) - *x2 = total_width + column->width; - else - *x2 = total_width; /* width of 0 */ - } -} - -/* Make sure the node is visible vertically */ -static void -pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view, - int node) -{ - gint node_dy, height; - GtkTreePath *path = NULL; - - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return; - - /* just return if the node is visible, avoiding a costly expose */ - node_dy = pspp_sheet_view_node_find_offset (tree_view, node); - height = ROW_HEIGHT (tree_view); - if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) - && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment) - + gtk_adjustment_get_page_size (tree_view->priv->vadjustment))) - return; - - path = _pspp_sheet_view_find_path (tree_view, node); - if (path) - { - /* We process updates because we want to clear old selected items when we scroll. - * if this is removed, we get a "selection streak" at the bottom. */ - gdk_window_process_updates (tree_view->priv->bin_window, TRUE); - pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0); - gtk_tree_path_free (path); - } -} - -static void -pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - gboolean focus_to_cell) -{ - gint x, width; - - if (column == NULL) - return; - - x = column->allocation.x; - width = column->allocation.width; - - if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) - { - /* The column is larger than the horizontal page size. If the - * column has cells which can be focussed individually, then we make - * sure the cell which gets focus is fully visible (if even the - * focus cell is bigger than the page size, we make sure the - * left-hand side of the cell is visible). - * - * If the column does not have those so-called special cells, we - * make sure the left-hand side of the column is visible. - */ - - if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view)) - { - GtkTreePath *cursor_path; - GdkRectangle background_area, cell_area, focus_area; - - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - - pspp_sheet_view_get_cell_area (tree_view, - cursor_path, column, &cell_area); - pspp_sheet_view_get_background_area (tree_view, - cursor_path, column, - &background_area); - - gtk_tree_path_free (cursor_path); - - _pspp_sheet_view_column_get_focus_area (column, - &background_area, - &cell_area, - &focus_area); - - x = focus_area.x; - width = focus_area.width; - - if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) - { - if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width)) - gtk_adjustment_set_value (tree_view->priv->hadjustment, - x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)); - else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x) - gtk_adjustment_set_value (tree_view->priv->hadjustment, x); - } - } - - gtk_adjustment_set_value (tree_view->priv->hadjustment, - CLAMP (x, - gtk_adjustment_get_lower (tree_view->priv->hadjustment), - gtk_adjustment_get_upper (tree_view->priv->hadjustment) - - gtk_adjustment_get_page_size (tree_view->priv->hadjustment))); - } - else - { - if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width)) - gtk_adjustment_set_value (tree_view->priv->hadjustment, - x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)); - else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x) - gtk_adjustment_set_value (tree_view->priv->hadjustment, x); - } -} - -GtkTreePath * -_pspp_sheet_view_find_path (PsppSheetView *tree_view, - int node) -{ - GtkTreePath *path; - - path = gtk_tree_path_new (); - if (node >= 0) - gtk_tree_path_append_index (path, node); - return path; -} - -void -_pspp_sheet_view_find_node (PsppSheetView *tree_view, - GtkTreePath *path, - int *node) -{ - gint *indices = gtk_tree_path_get_indices (path); - gint depth = gtk_tree_path_get_depth (path); - - *node = -1; - if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count) - return; - *node = indices[0]; -} - -static void -pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set, - guint keyval, - guint modmask, - gboolean add_shifted_binding, - GtkMovementStep step, - gint count) -{ - - gtk_binding_entry_add_signal (binding_set, keyval, modmask, - "move-cursor", 2, - G_TYPE_ENUM, step, - G_TYPE_INT, count); - - if (add_shifted_binding) - gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, - "move-cursor", 2, - G_TYPE_ENUM, step, - G_TYPE_INT, count); - - if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) - return; - - gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "move-cursor", 2, - G_TYPE_ENUM, step, - G_TYPE_INT, count); - - gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, step, - G_TYPE_INT, count); -} - -static void -pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view, - PsppSheetViewColumn *column) -{ - PsppSheetViewColumn *left_column; - PsppSheetViewColumn *cur_column = NULL; - PsppSheetViewColumnReorder *reorder; - gboolean rtl; - GList *tmp_list; - gint left; - - /* We want to precalculate the motion list such that we know what column slots - * are available. - */ - left_column = NULL; - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - /* First, identify all possible drop spots */ - if (rtl) - tmp_list = g_list_last (tree_view->priv->columns); - else - tmp_list = g_list_first (tree_view->priv->columns); - - while (tmp_list) - { - cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list); - - if (cur_column->visible == FALSE) - continue; - - /* If it's not the column moving and func tells us to skip over the column, we continue. */ - if (left_column != column && cur_column != column && - tree_view->priv->column_drop_func && - ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data)) - { - left_column = cur_column; - continue; - } - reorder = g_slice_new0 (PsppSheetViewColumnReorder); - reorder->left_column = left_column; - left_column = reorder->right_column = cur_column; - - tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder); - } - - /* Add the last one */ - if (tree_view->priv->column_drop_func == NULL || - ((left_column != column) && - tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))) - { - reorder = g_slice_new0 (PsppSheetViewColumnReorder); - reorder->left_column = left_column; - reorder->right_column = NULL; - tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder); - } - - /* We quickly check to see if it even makes sense to reorder columns. */ - /* If there is nothing that can be moved, then we return */ - - if (tree_view->priv->column_drag_info == NULL) - return; - - /* We know there are always 2 slots possbile, as you can always return column. */ - /* If that's all there is, return */ - if (tree_view->priv->column_drag_info->next == NULL || - (tree_view->priv->column_drag_info->next->next == NULL && - ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column && - ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column)) - { - for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) - g_slice_free (PsppSheetViewColumnReorder, tmp_list->data); - g_list_free (tree_view->priv->column_drag_info); - tree_view->priv->column_drag_info = NULL; - return; - } - /* We fill in the ranges for the columns, now that we've isolated them */ - left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); - - for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) - { - reorder = (PsppSheetViewColumnReorder *) tmp_list->data; - - reorder->left_align = left; - if (tmp_list->next != NULL) - { - g_assert (tmp_list->next->data); - left = reorder->right_align = (reorder->right_column->allocation.x + - reorder->right_column->allocation.width + - ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2; - } - else - { - gint width = gdk_window_get_width (tree_view->priv->header_window); - reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); - } - } -} - -void -_pspp_sheet_view_column_start_drag (PsppSheetView *tree_view, - PsppSheetViewColumn *column) -{ - GdkEvent *send_event; - GtkAllocation allocation; - gint x, y; - GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view)); - GdkDisplay *display = gdk_screen_get_display (screen); - - g_return_if_fail (tree_view->priv->column_drag_info == NULL); - g_return_if_fail (tree_view->priv->cur_reorder == NULL); - g_return_if_fail (column->button); - - pspp_sheet_view_set_column_drag_info (tree_view, column); - - if (tree_view->priv->column_drag_info == NULL) - return; - - if (tree_view->priv->drag_window == NULL) - { - GdkWindowAttr attributes; - guint attributes_mask; - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.x = column->allocation.x; - attributes.y = 0; - attributes.width = column->allocation.width; - attributes.height = column->allocation.height; - attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view)); - attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK; - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ; - - tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window, - &attributes, - attributes_mask); - gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view)); - } - - gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); - gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME); - - gtk_grab_remove (column->button); - - send_event = gdk_event_new (GDK_LEAVE_NOTIFY); - send_event->crossing.send_event = TRUE; - send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button))); - send_event->crossing.subwindow = NULL; - send_event->crossing.detail = GDK_NOTIFY_ANCESTOR; - send_event->crossing.time = GDK_CURRENT_TIME; - - gtk_propagate_event (column->button, send_event); - gdk_event_free (send_event); - - send_event = gdk_event_new (GDK_BUTTON_RELEASE); - send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen)); - send_event->button.send_event = TRUE; - send_event->button.time = GDK_CURRENT_TIME; - send_event->button.x = -1; - send_event->button.y = -1; - send_event->button.axes = NULL; - send_event->button.state = 0; - send_event->button.button = 1; - send_event->button.device = - gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display)); - - send_event->button.x_root = 0; - send_event->button.y_root = 0; - - gtk_propagate_event (column->button, send_event); - gdk_event_free (send_event); - - /* Kids, don't try this at home */ - g_object_ref (column->button); - gtk_container_remove (GTK_CONTAINER (tree_view), column->button); - gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window); - gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view)); - g_object_unref (column->button); - - tree_view->priv->drag_column_x = column->allocation.x; - allocation = column->allocation; - allocation.x = 0; - gtk_widget_size_allocate (column->button, &allocation); - gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window); - - tree_view->priv->drag_column = column; - gdk_window_show (tree_view->priv->drag_window); - - gdk_window_get_origin (tree_view->priv->header_window, &x, &y); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - while (gtk_events_pending ()) - gtk_main_iteration (); - - PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG); - gdk_pointer_grab (tree_view->priv->drag_window, - FALSE, - GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK, - NULL, NULL, GDK_CURRENT_TIME); - gdk_keyboard_grab (tree_view->priv->drag_window, - FALSE, - GDK_CURRENT_TIME); -} - -void -_pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view, - int node, - const GdkRectangle *clip_rect) -{ - GdkRectangle rect; - GtkAllocation allocation; - - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return; - - gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); - rect.x = 0; - rect.width = MAX (tree_view->priv->width, allocation.width); - - rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node); - rect.height = ROW_HEIGHT (tree_view); - - if (clip_rect) - { - GdkRectangle new_rect; - - gdk_rectangle_intersect (clip_rect, &rect, &new_rect); - - gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE); - } - else - { - gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE); - } -} - -static void -pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view, - GtkTreePath *path, - const GdkRectangle *clip_rect) -{ - int node = -1; - - _pspp_sheet_view_find_node (tree_view, path, &node); - - if (node) - _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect); -} - -static void -pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view) - -{ - GtkTreePath *cursor_path; - - if ((tree_view->priv->row_count == 0) || - (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))) - return; - - cursor_path = NULL; - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - - if (cursor_path == NULL) - { - /* There's no cursor. Move the cursor to the first selected row, if any - * are selected, otherwise to the first row in the sheetview. - */ - GList *selected_rows; - GtkTreeModel *model; - PsppSheetSelection *selection; - - selection = pspp_sheet_view_get_selection (tree_view); - selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model); - - if (selected_rows) - { - /* XXX we could avoid doing O(n) work to get this result */ - cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data)); - g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL); - g_list_free (selected_rows); - } - else - { - cursor_path = gtk_tree_path_new_first (); - search_first_focusable_path (tree_view, &cursor_path, - TRUE, NULL); - } - - gtk_tree_row_reference_free (tree_view->priv->cursor); - tree_view->priv->cursor = NULL; - - if (cursor_path) - { - if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE || - tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) - pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0); - else - pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0); - } - } - - if (cursor_path) - { - /* Now find a column for the cursor. */ - PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS); - - pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL); - gtk_tree_path_free (cursor_path); - - if (tree_view->priv->focus_column == NULL) - { - GList *list; - for (list = tree_view->priv->columns; list; list = list->next) - { - if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible) - { - tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data); - pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); - pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column); - break; - } - } - - } - } -} - -static gboolean -pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode) -{ - gint selection_count; - int cursor_node = -1; - int new_cursor_node = -1; - GtkTreePath *cursor_path = NULL; - gboolean grab_focus = TRUE; - - if (! gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - cursor_path = NULL; - if (!gtk_tree_row_reference_valid (tree_view->priv->cursor)) - /* FIXME: we lost the cursor; should we get the first? */ - return FALSE; - - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); - - if (cursor_node < 0) - /* FIXME: we lost the cursor; should we get the first? */ - return FALSE; - - selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection); - - if (selection_count == 0 - && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE - && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE)) - { - /* Don't move the cursor, but just select the current node */ - new_cursor_node = cursor_node; - } - else - { - if (count == -1) - new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node); - else - new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node); - } - - gtk_tree_path_free (cursor_path); - - if (new_cursor_node) - { - cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node); - - search_first_focusable_path (tree_view, &cursor_path, - (count != -1), - &new_cursor_node); - - if (cursor_path) - gtk_tree_path_free (cursor_path); - } - - /* - * If the list has only one item and multi-selection is set then select - * the row (if not yet selected). - */ - if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE || - tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) && - new_cursor_node < 0) - { - if (count == -1) - new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node); - else - new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node); - - if (new_cursor_node < 0 - && !pspp_sheet_view_node_is_selected (tree_view, cursor_node)) - { - new_cursor_node = cursor_node; - } - else - { - new_cursor_node = -1; - } - } - - if (new_cursor_node >= 0) - { - cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node); - pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode); - gtk_tree_path_free (cursor_path); - } - else - { - pspp_sheet_view_clamp_node_visible (tree_view, cursor_node); - - if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND)) - { - if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view), - count < 0 ? - GTK_DIR_UP : GTK_DIR_DOWN)) - { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view)); - - if (toplevel) - gtk_widget_child_focus (toplevel, - count < 0 ? - GTK_DIR_TAB_BACKWARD : - GTK_DIR_TAB_FORWARD); - - grab_focus = FALSE; - } - } - else - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - } - } - - if (grab_focus) - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - return new_cursor_node >= 0; -} - -static void -pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode) -{ - int cursor_node = -1; - GtkTreePath *old_cursor_path = NULL; - GtkTreePath *cursor_path = NULL; - int start_cursor_node = -1; - gint y; - gint window_y; - gint vertical_separator; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) - old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - else - /* This is sorta weird. Focus in should give us a cursor */ - return; - - gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL); - _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node); - - if (cursor_node < 0) - { - /* FIXME: we lost the cursor. Should we try to get one? */ - gtk_tree_path_free (old_cursor_path); - return; - } - - y = pspp_sheet_view_node_find_offset (tree_view, cursor_node); - window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y); - y += tree_view->priv->cursor_offset; - y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment); - y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator); - - if (y >= tree_view->priv->height) - y = tree_view->priv->height - 1; - - tree_view->priv->cursor_offset = - pspp_sheet_view_find_offset (tree_view, y, &cursor_node); - - if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view)) - { - cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node); - tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view); - } - - y -= tree_view->priv->cursor_offset; - cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node); - - start_cursor_node = cursor_node; - - if (! search_first_focusable_path (tree_view, &cursor_path, - (count != -1), - &cursor_node)) - { - /* It looks like we reached the end of the view without finding - * a focusable row. We will step backwards to find the last - * focusable row. - */ - cursor_node = start_cursor_node; - cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node); - - search_first_focusable_path (tree_view, &cursor_path, - (count == -1), - &cursor_node); - } - - if (!cursor_path) - goto cleanup; - - /* update y */ - y = pspp_sheet_view_node_find_offset (tree_view, cursor_node); - - pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode); - - y -= window_y; - pspp_sheet_view_scroll_to_point (tree_view, -1, y); - pspp_sheet_view_clamp_node_visible (tree_view, cursor_node); - _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL); - - if (!gtk_tree_path_compare (old_cursor_path, cursor_path)) - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - -cleanup: - gtk_tree_path_free (old_cursor_path); - gtk_tree_path_free (cursor_path); -} - -static void -pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode) -{ - int cursor_node = -1; - GtkTreePath *cursor_path = NULL; - PsppSheetViewColumn *column; - GtkTreeIter iter; - GList *list; - gboolean found_column = FALSE; - gboolean rtl; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - else - return; - - _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); - if (cursor_node < 0) - return; - if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE) - { - gtk_tree_path_free (cursor_path); - return; - } - gtk_tree_path_free (cursor_path); - - list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns); - if (tree_view->priv->focus_column) - { - for (; list; list = (rtl ? list->prev : list->next)) - { - if (list->data == tree_view->priv->focus_column) - break; - } - } - - while (list) - { - gboolean left, right; - - column = list->data; - if (column->visible == FALSE || column->row_head) - goto loop_end; - - pspp_sheet_view_column_cell_set_cell_data (column, - tree_view->priv->model, - &iter); - - if (rtl) - { - right = list->prev ? TRUE : FALSE; - left = list->next ? TRUE : FALSE; - } - else - { - left = list->prev ? TRUE : FALSE; - right = list->next ? TRUE : FALSE; - } - - if (_pspp_sheet_view_column_cell_focus (column, count, left, right)) - { - tree_view->priv->focus_column = column; - found_column = TRUE; - break; - } - loop_end: - if (count == 1) - list = rtl ? list->prev : list->next; - else - list = rtl ? list->next : list->prev; - } - - if (found_column) - { - _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL); - g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - } - else - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - } - - pspp_sheet_view_clamp_column_visible (tree_view, - tree_view->priv->focus_column, TRUE); -} - -static void -pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode) -{ - int cursor_node = -1; - GtkTreePath *cursor_path = NULL; - PsppSheetViewColumn *column; - PsppSheetViewColumn *found_column; - GtkTreeIter iter; - GList *list; - gboolean rtl; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - else - return; - - _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); - if (cursor_node < 0) - return; - if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE) - { - gtk_tree_path_free (cursor_path); - return; - } - gtk_tree_path_free (cursor_path); - - list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns); - if (tree_view->priv->focus_column) - { - for (; list; list = (rtl ? list->prev : list->next)) - { - if (list->data == tree_view->priv->focus_column) - break; - } - } - - found_column = NULL; - while (list) - { - gboolean left, right; - - column = list->data; - if (column->visible == FALSE || column->row_head) - goto loop_end; - - pspp_sheet_view_column_cell_set_cell_data (column, - tree_view->priv->model, - &iter); - - if (rtl) - { - right = list->prev ? TRUE : FALSE; - left = list->next ? TRUE : FALSE; - } - else - { - left = list->prev ? TRUE : FALSE; - right = list->next ? TRUE : FALSE; - } - - if (column->tabbable - && _pspp_sheet_view_column_cell_focus (column, count, left, right)) - found_column = column; - - loop_end: - if (count == 1) - list = rtl ? list->prev : list->next; - else - list = rtl ? list->next : list->prev; - } - - if (found_column) - { - tree_view->priv->focus_column = found_column; - _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL); - g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - } - - pspp_sheet_view_clamp_column_visible (tree_view, - tree_view->priv->focus_column, TRUE); -} - -static gboolean -try_move_cursor_tab (PsppSheetView *tree_view, - gboolean start_at_focus_column, - gint count) -{ - PsppSheetViewColumn *column; - GtkTreeIter iter; - int cursor_node = -1; - GtkTreePath *cursor_path = NULL; - gboolean rtl; - GList *list; - - if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - else - return TRUE; - - _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); - if (cursor_node < 0) - return TRUE; - if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE) - { - gtk_tree_path_free (cursor_path); - return TRUE; - } - gtk_tree_path_free (cursor_path); - - rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL; - if (start_at_focus_column) - { - list = (rtl - ? g_list_last (tree_view->priv->columns) - : g_list_first (tree_view->priv->columns)); - if (tree_view->priv->focus_column) - { - for (; list; list = (rtl ? list->prev : list->next)) - { - if (list->data == tree_view->priv->focus_column) - break; - } - } - } - else - { - list = (rtl ^ (count == 1) - ? g_list_first (tree_view->priv->columns) - : g_list_last (tree_view->priv->columns)); - } - - while (list) - { - gboolean left, right; - - column = list->data; - if (column->visible == FALSE || !column->tabbable) - goto loop_end; - - pspp_sheet_view_column_cell_set_cell_data (column, - tree_view->priv->model, - &iter); - - if (rtl) - { - right = list->prev ? TRUE : FALSE; - left = list->next ? TRUE : FALSE; - } - else - { - left = list->prev ? TRUE : FALSE; - right = list->next ? TRUE : FALSE; - } - - if (column->tabbable - && _pspp_sheet_view_column_cell_focus (column, count, left, right)) - { - tree_view->priv->focus_column = column; - _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL); - g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - return TRUE; - } - loop_end: - if (count == 1) - list = rtl ? list->prev : list->next; - else - list = rtl ? list->next : list->prev; - } - - return FALSE; -} - -static void -pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view, - gint count) -{ - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - if (!try_move_cursor_tab (tree_view, TRUE, count)) - { - if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0) - && !try_move_cursor_tab (tree_view, FALSE, count)) - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - } - - pspp_sheet_view_clamp_column_visible (tree_view, - tree_view->priv->focus_column, TRUE); -} - -static void -pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view, - gint count, - PsppSheetSelectMode mode) -{ - int cursor_node; - GtkTreePath *path; - GtkTreePath *old_path; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; - - g_return_if_fail (tree_view->priv->row_count > 0); - - pspp_sheet_view_get_cursor (tree_view, &old_path, NULL); - - if (count == -1) - { - /* Now go forward to find the first focusable row. */ - path = _pspp_sheet_view_find_path (tree_view, 0); - search_first_focusable_path (tree_view, &path, - TRUE, &cursor_node); - } - else - { - /* Now go backwards to find last focusable row. */ - path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1); - search_first_focusable_path (tree_view, &path, - FALSE, &cursor_node); - } - - if (!path) - goto cleanup; - - if (gtk_tree_path_compare (old_path, path)) - { - pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - } - else - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - } - -cleanup: - gtk_tree_path_free (old_path); - gtk_tree_path_free (path); -} - -static gboolean -pspp_sheet_view_real_select_all (PsppSheetView *tree_view) -{ - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE && - tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE) - return FALSE; - - pspp_sheet_selection_select_all (tree_view->priv->selection); - - return TRUE; -} - -static gboolean -pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view) -{ - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE && - tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE) - return FALSE; - - pspp_sheet_selection_unselect_all (tree_view->priv->selection); - - return TRUE; -} - -static gboolean -pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view, - gboolean start_editing, - PsppSheetSelectMode mode) -{ - int new_node = -1; - int cursor_node = -1; - GtkTreePath *cursor_path = NULL; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - - if (cursor_path == NULL) - return FALSE; - - _pspp_sheet_view_find_node (tree_view, cursor_path, - &cursor_node); - - if (cursor_node < 0) - { - gtk_tree_path_free (cursor_path); - return FALSE; - } - - if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing && - tree_view->priv->focus_column) - { - if (pspp_sheet_view_start_editing (tree_view, cursor_path)) - { - gtk_tree_path_free (cursor_path); - return TRUE; - } - } - - _pspp_sheet_selection_internal_select_node (tree_view->priv->selection, - cursor_node, - cursor_path, - mode, - FALSE); - - /* We bail out if the original (tree, node) don't exist anymore after - * handling the selection-changed callback. We do return TRUE because - * the key press has been handled at this point. - */ - _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node); - - if (cursor_node != new_node) - return FALSE; - - pspp_sheet_view_clamp_node_visible (tree_view, cursor_node); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL); - - if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND)) - pspp_sheet_view_row_activated (tree_view, cursor_path, - tree_view->priv->focus_column); - - gtk_tree_path_free (cursor_path); - - return TRUE; -} - -static gboolean -pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view) -{ - int new_node = -1; - int cursor_node = -1; - GtkTreePath *cursor_path = NULL; - - if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return FALSE; - - cursor_path = NULL; - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - - if (cursor_path == NULL) - return FALSE; - - _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); - if (cursor_node < 0) - { - gtk_tree_path_free (cursor_path); - return FALSE; - } - - _pspp_sheet_selection_internal_select_node (tree_view->priv->selection, - cursor_node, - cursor_path, - PSPP_SHEET_SELECT_MODE_TOGGLE, - FALSE); - - /* We bail out if the original (tree, node) don't exist anymore after - * handling the selection-changed callback. We do return TRUE because - * the key press has been handled at this point. - */ - _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node); - - if (cursor_node != new_node) - return FALSE; - - pspp_sheet_view_clamp_node_visible (tree_view, cursor_node); - - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL); - gtk_tree_path_free (cursor_path); - - return TRUE; -} - -static gboolean -pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view) -{ - pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view); - tree_view->priv->typeselect_flush_timeout = 0; - - return FALSE; -} - -/* Cut and paste from gtkwindow.c */ -static void -send_focus_change (GtkWidget *widget, - gboolean in) -{ - GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE); - - fevent->focus_change.type = GDK_FOCUS_CHANGE; - fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget)); - fevent->focus_change.in = in; - - gtk_widget_send_focus_change (widget, fevent); - gdk_event_free (fevent); -} - -static void -pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view) -{ - GtkWidget *frame, *vbox, *toplevel; - GdkScreen *screen; - - if (tree_view->priv->search_custom_entry_set) - return; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view)); - screen = gtk_widget_get_screen (GTK_WIDGET (tree_view)); - - if (tree_view->priv->search_window != NULL) - { - if (gtk_window_get_group (GTK_WINDOW (toplevel))) - gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), - GTK_WINDOW (tree_view->priv->search_window)); - else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window))) - gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)), - GTK_WINDOW (tree_view->priv->search_window)); - gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen); - return; - } - - tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP); - gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen); - - if (gtk_window_get_group (GTK_WINDOW (toplevel))) - gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), - GTK_WINDOW (tree_view->priv->search_window)); - - gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window), - GDK_WINDOW_TYPE_HINT_UTILITY); - gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE); - g_signal_connect (tree_view->priv->search_window, "delete-event", - G_CALLBACK (pspp_sheet_view_search_delete_event), - tree_view); - g_signal_connect (tree_view->priv->search_window, "key-press-event", - G_CALLBACK (pspp_sheet_view_search_key_press_event), - tree_view); - g_signal_connect (tree_view->priv->search_window, "button-press-event", - G_CALLBACK (pspp_sheet_view_search_button_press_event), - tree_view); - g_signal_connect (tree_view->priv->search_window, "scroll-event", - G_CALLBACK (pspp_sheet_view_search_scroll_event), - tree_view); - - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); - gtk_widget_show (frame); - gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame); - - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_widget_show (vbox); - gtk_container_add (GTK_CONTAINER (frame), vbox); - gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); - - /* add entry */ - tree_view->priv->search_entry = gtk_entry_new (); - gtk_widget_show (tree_view->priv->search_entry); - g_signal_connect (tree_view->priv->search_entry, "populate-popup", - G_CALLBACK (pspp_sheet_view_search_disable_popdown), - tree_view); - g_signal_connect (tree_view->priv->search_entry, - "activate", G_CALLBACK (pspp_sheet_view_search_activate), - tree_view); - - gtk_container_add (GTK_CONTAINER (vbox), - tree_view->priv->search_entry); - - gtk_widget_realize (tree_view->priv->search_entry); -} - -/* Pops up the interactive search entry. If keybinding is TRUE then the user - * started this by typing the start_interactive_search keybinding. Otherwise, it came from - */ -static gboolean -pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view, - gboolean keybinding) -{ - /* We only start interactive search if we have focus or the columns - * have focus. If one of our children have focus, we don't want to - * start the search. - */ - GList *list; - gboolean found_focus = FALSE; - GtkWidgetClass *entry_parent_class; - - if (!tree_view->priv->enable_search && !keybinding) - return FALSE; - - if (tree_view->priv->search_custom_entry_set) - return FALSE; - - if (tree_view->priv->search_window != NULL && - gtk_widget_get_visible (tree_view->priv->search_window)) - return TRUE; - - for (list = tree_view->priv->columns; list; list = list->next) - { - PsppSheetViewColumn *column; - - column = list->data; - if (! column->visible) - continue; - - if (column->button && gtk_widget_has_focus (column->button)) - { - found_focus = TRUE; - break; - } - } - - if (gtk_widget_has_focus (GTK_WIDGET (tree_view))) - found_focus = TRUE; - - if (!found_focus) - return FALSE; - - if (tree_view->priv->search_column < 0) - return FALSE; - - pspp_sheet_view_ensure_interactive_directory (tree_view); - - if (keybinding) - gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), ""); - - /* done, show it */ - tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data); - gtk_widget_show (tree_view->priv->search_window); - if (tree_view->priv->search_entry_changed_id == 0) - { - tree_view->priv->search_entry_changed_id = - g_signal_connect (tree_view->priv->search_entry, "changed", - G_CALLBACK (pspp_sheet_view_search_init), - tree_view); - } - - tree_view->priv->typeselect_flush_timeout = - gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout, - tree_view); - - /* Grab focus will select all the text. We don't want that to happen, so we - * call the parent instance and bypass the selection change. This is probably - * really non-kosher. */ - entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry)); - (entry_parent_class->grab_focus) (tree_view->priv->search_entry); - - /* send focus-in event */ - send_focus_change (tree_view->priv->search_entry, TRUE); - - /* search first matching iter */ - pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view); - - return TRUE; -} - -static gboolean -pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view) -{ - return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE); -} - -/* this function returns the new width of the column being resized given - * the column and x position of the cursor; the x cursor position is passed - * in as a pointer and automagicly corrected if it's beyond min/max limits - */ -static gint -pspp_sheet_view_new_column_width (PsppSheetView *tree_view, - gint i, - gint *x) -{ - PsppSheetViewColumn *column; - gint width; - gboolean rtl; - - /* first translate the x position from gtk_widget_get_window (widget) - * to clist->clist_window - */ - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - column = g_list_nth (tree_view->priv->columns, i)->data; - width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x); - - /* Clamp down the value */ - if (column->min_width == -1) - width = MAX (column->button_request, width); - else - width = MAX (column->min_width, width); - if (column->max_width != -1) - width = MIN (width, column->max_width); - - *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width); - - return width; -} - -void -pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column); - -/* Callbacks */ -static void -pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment, - PsppSheetView *tree_view) -{ - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - gint dy; - - gdk_window_move (tree_view->priv->bin_window, - - gtk_adjustment_get_value (tree_view->priv->hadjustment), - TREE_VIEW_HEADER_HEIGHT (tree_view)); - gdk_window_move (tree_view->priv->header_window, - - gtk_adjustment_get_value (tree_view->priv->hadjustment), - 0); - dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment); - - gdk_window_scroll (tree_view->priv->bin_window, 0, dy); - - if (dy != 0) - { - /* update our dy and top_row */ - tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment); - - update_prelight (tree_view, - tree_view->priv->event_last_x, - tree_view->priv->event_last_y); - - if (!tree_view->priv->in_top_row_to_dy) - pspp_sheet_view_dy_to_top_row (tree_view); - } - - update_childrens_allocation(tree_view); - } -} - -/* Public methods - */ - -/** - * pspp_sheet_view_new: - * - * Creates a new #PsppSheetView widget. - * - * Return value: A newly created #PsppSheetView widget. - **/ -GtkWidget * -pspp_sheet_view_new (void) -{ - return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL); -} - -/** - * pspp_sheet_view_new_with_model: - * @model: the model. - * - * Creates a new #PsppSheetView widget with the model initialized to @model. - * - * Return value: A newly created #PsppSheetView widget. - **/ -GtkWidget * -pspp_sheet_view_new_with_model (GtkTreeModel *model) -{ - return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL); -} - -/* Public Accessors - */ - -/** - * pspp_sheet_view_get_model: - * @tree_view: a #PsppSheetView - * - * Returns the model the #PsppSheetView is based on. Returns %NULL if the - * model is unset. - * - * Return value: A #GtkTreeModel, or %NULL if none is currently being used. - **/ -GtkTreeModel * -pspp_sheet_view_get_model (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return tree_view->priv->model; -} - -/** - * pspp_sheet_view_set_model: - * @tree_view: A #GtkTreeNode. - * @model: (allow-none): The model. - * - * Sets the model for a #PsppSheetView. If the @tree_view already has a model - * set, it will remove it before setting the new model. If @model is %NULL, - * then it will unset the old model. - **/ -void -pspp_sheet_view_set_model (PsppSheetView *tree_view, - GtkTreeModel *model) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); - - if (model == tree_view->priv->model) - return; - - if (tree_view->priv->scroll_to_path) - { - gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); - tree_view->priv->scroll_to_path = NULL; - } - - if (tree_view->priv->model) - { - GList *tmplist = tree_view->priv->columns; - - if (tree_view->priv->selected) - range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX); - pspp_sheet_view_stop_editing (tree_view, TRUE); - - g_signal_handlers_disconnect_by_func (tree_view->priv->model, - pspp_sheet_view_row_changed, - tree_view); - g_signal_handlers_disconnect_by_func (tree_view->priv->model, - pspp_sheet_view_row_inserted, - tree_view); - g_signal_handlers_disconnect_by_func (tree_view->priv->model, - pspp_sheet_view_row_deleted, - tree_view); - g_signal_handlers_disconnect_by_func (tree_view->priv->model, - pspp_sheet_view_rows_reordered, - tree_view); - - for (; tmplist; tmplist = tmplist->next) - _pspp_sheet_view_column_unset_model (tmplist->data, - tree_view->priv->model); - - tree_view->priv->prelight_node = -1; - - gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); - tree_view->priv->drag_dest_row = NULL; - gtk_tree_row_reference_free (tree_view->priv->cursor); - tree_view->priv->cursor = NULL; - gtk_tree_row_reference_free (tree_view->priv->anchor); - tree_view->priv->anchor = NULL; - gtk_tree_row_reference_free (tree_view->priv->top_row); - tree_view->priv->top_row = NULL; - gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); - tree_view->priv->scroll_to_path = NULL; - - tree_view->priv->scroll_to_column = NULL; - - g_object_unref (tree_view->priv->model); - - tree_view->priv->search_column = -1; - tree_view->priv->fixed_height = -1; - tree_view->priv->dy = tree_view->priv->top_row_dy = 0; - tree_view->priv->last_button_x = -1; - tree_view->priv->last_button_y = -1; - } - - tree_view->priv->model = model; - - if (tree_view->priv->model) - { - gint i; - - if (tree_view->priv->search_column == -1) - { - for (i = 0; i < gtk_tree_model_get_n_columns (model); i++) - { - GType type = gtk_tree_model_get_column_type (model, i); - - if (g_value_type_transformable (type, G_TYPE_STRING)) - { - tree_view->priv->search_column = i; - break; - } - } - } - - g_object_ref (tree_view->priv->model); - g_signal_connect (tree_view->priv->model, - "row-changed", - G_CALLBACK (pspp_sheet_view_row_changed), - tree_view); - g_signal_connect (tree_view->priv->model, - "row-inserted", - G_CALLBACK (pspp_sheet_view_row_inserted), - tree_view); - g_signal_connect (tree_view->priv->model, - "row-deleted", - G_CALLBACK (pspp_sheet_view_row_deleted), - tree_view); - g_signal_connect (tree_view->priv->model, - "rows-reordered", - G_CALLBACK (pspp_sheet_view_rows_reordered), - tree_view); - - tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL); - - /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */ - install_presize_handler (tree_view); - } - - g_object_notify (G_OBJECT (tree_view), "model"); - - if (tree_view->priv->selection) - _pspp_sheet_selection_emit_changed (tree_view->priv->selection); - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); -} - -/** - * pspp_sheet_view_get_selection: - * @tree_view: A #PsppSheetView. - * - * Gets the #PsppSheetSelection associated with @tree_view. - * - * Return value: A #PsppSheetSelection object. - **/ -PsppSheetSelection * -pspp_sheet_view_get_selection (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return tree_view->priv->selection; -} - -/** - * pspp_sheet_view_get_hadjustment: - * @tree_view: A #PsppSheetView - * - * Gets the #GtkAdjustment currently being used for the horizontal aspect. - * - * Return value: A #GtkAdjustment object, or %NULL if none is currently being - * used. - **/ -GtkAdjustment * -pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return pspp_sheet_view_do_get_hadjustment (tree_view); -} - -static GtkAdjustment * -pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view) -{ - return tree_view->priv->hadjustment; -} - -/** - * pspp_sheet_view_set_hadjustment: - * @tree_view: A #PsppSheetView - * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL - * - * Sets the #GtkAdjustment for the current horizontal aspect. - **/ -void -pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - pspp_sheet_view_set_adjustments (tree_view, - adjustment, - tree_view->priv->vadjustment); - - g_object_notify (G_OBJECT (tree_view), "hadjustment"); -} - -static void -pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment) -{ - PsppSheetViewPrivate *priv = tree_view->priv; - - if (adjustment && priv->hadjustment == adjustment) - return; - - if (priv->hadjustment != NULL) - { - g_signal_handlers_disconnect_by_func (priv->hadjustment, - pspp_sheet_view_adjustment_changed, - tree_view); - g_object_unref (priv->hadjustment); - } - - if (adjustment == NULL) - adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, - 0.0, 0.0, 0.0); - - g_signal_connect (adjustment, "value-changed", - G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view); - priv->hadjustment = g_object_ref_sink (adjustment); - /* FIXME: Adjustment should probably be populated here with fresh values, but - * internal details are too complicated for me to decipher right now. - */ - pspp_sheet_view_adjustment_changed (NULL, tree_view); - - g_object_notify (G_OBJECT (tree_view), "hadjustment"); -} - -/** - * pspp_sheet_view_get_vadjustment: - * @tree_view: A #PsppSheetView - * - * Gets the #GtkAdjustment currently being used for the vertical aspect. - * - * Return value: (transfer none): A #GtkAdjustment object, or %NULL - * if none is currently being used. - * - * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment() - **/ -GtkAdjustment * -pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return pspp_sheet_view_do_get_vadjustment (tree_view); -} - -static GtkAdjustment * -pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view) -{ - return tree_view->priv->vadjustment; -} - -/** - * pspp_sheet_view_set_vadjustment: - * @tree_view: A #PsppSheetView - * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL - * - * Sets the #GtkAdjustment for the current vertical aspect. - * - * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment() - **/ -void -pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment)); - - pspp_sheet_view_do_set_vadjustment (tree_view, adjustment); -} - -static void -pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment) -{ - PsppSheetViewPrivate *priv = tree_view->priv; - - if (adjustment && priv->vadjustment == adjustment) - return; - - if (priv->vadjustment != NULL) - { - g_signal_handlers_disconnect_by_func (priv->vadjustment, - pspp_sheet_view_adjustment_changed, - tree_view); - g_object_unref (priv->vadjustment); - } - - if (adjustment == NULL) - adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, - 0.0, 0.0, 0.0); - - g_signal_connect (adjustment, "value-changed", - G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view); - priv->vadjustment = g_object_ref_sink (adjustment); - /* FIXME: Adjustment should probably be populated here with fresh values, but - * internal details are too complicated for me to decipher right now. - */ - pspp_sheet_view_adjustment_changed (NULL, tree_view); - g_object_notify (G_OBJECT (tree_view), "vadjustment"); -} - -/* Column and header operations */ - -/** - * pspp_sheet_view_get_headers_visible: - * @tree_view: A #PsppSheetView. - * - * Returns %TRUE if the headers on the @tree_view are visible. - * - * Return value: Whether the headers are visible or not. - **/ -gboolean -pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE); -} - -/** - * pspp_sheet_view_set_headers_visible: - * @tree_view: A #PsppSheetView. - * @headers_visible: %TRUE if the headers are visible - * - * Sets the visibility state of the headers. - **/ -void -pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view, - gboolean headers_visible) -{ - gint x, y; - GList *list; - PsppSheetViewColumn *column; - GtkAllocation allocation; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); - - headers_visible = !! headers_visible; - - if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible) - return; - - if (headers_visible) - PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE); - else - PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE); - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - gdk_window_get_position (tree_view->priv->bin_window, &x, &y); - if (headers_visible) - { - gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view), - tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view)); - - if (gtk_widget_get_mapped (GTK_WIDGET (tree_view))) - pspp_sheet_view_map_buttons (tree_view); - } - else - { - gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height); - - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (column->button) - gtk_widget_unmap (column->button); - } - gdk_window_hide (tree_view->priv->header_window); - } - } - - gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)); - gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2); - gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0); - gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height); - gtk_adjustment_changed (tree_view->priv->vadjustment); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - - g_object_notify (G_OBJECT (tree_view), "headers-visible"); -} - -/** - * pspp_sheet_view_columns_autosize: - * @tree_view: A #PsppSheetView. - * - * Resizes all columns to their optimal width. Only works after the - * treeview has been realized. - **/ -void -pspp_sheet_view_columns_autosize (PsppSheetView *tree_view) -{ - gboolean dirty = FALSE; - GList *list; - PsppSheetViewColumn *column; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - _pspp_sheet_view_column_cell_set_dirty (column); - dirty = TRUE; - } - - if (dirty) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); -} - -/** - * pspp_sheet_view_set_headers_clickable: - * @tree_view: A #PsppSheetView. - * @setting: %TRUE if the columns are clickable. - * - * Allow the column title buttons to be clicked. - **/ -void -pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view, - gboolean setting) -{ - GList *list; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - for (list = tree_view->priv->columns; list; list = list->next) - pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting); - - g_object_notify (G_OBJECT (tree_view), "headers-clickable"); -} - - -/** - * pspp_sheet_view_get_headers_clickable: - * @tree_view: A #PsppSheetView. - * - * Returns whether all header columns are clickable. - * - * Return value: %TRUE if all header columns are clickable, otherwise %FALSE - * - * Since: 2.10 - **/ -gboolean -pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view) -{ - GList *list; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - for (list = tree_view->priv->columns; list; list = list->next) - if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable) - return FALSE; - - return TRUE; -} - -/** - * pspp_sheet_view_set_rules_hint - * @tree_view: a #PsppSheetView - * @setting: %TRUE if the tree requires reading across rows - * - * This function tells GTK+ that the user interface for your - * application requires users to read across tree rows and associate - * cells with one another. By default, GTK+ will then render the tree - * with alternating row colors. Do not use it - * just because you prefer the appearance of the ruled tree; that's a - * question for the theme. Some themes will draw tree rows in - * alternating colors even when rules are turned off, and users who - * prefer that appearance all the time can choose those themes. You - * should call this function only as a semantic - * hint to the theme engine that your tree makes alternating colors - * useful from a functional standpoint (since it has lots of columns, - * generally). - * - **/ -void -pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view, - gboolean setting) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - setting = setting != FALSE; - - if (tree_view->priv->has_rules != setting) - { - tree_view->priv->has_rules = setting; - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - - g_object_notify (G_OBJECT (tree_view), "rules-hint"); -} - -/** - * pspp_sheet_view_get_rules_hint - * @tree_view: a #PsppSheetView - * - * Gets the setting set by pspp_sheet_view_set_rules_hint(). - * - * Return value: %TRUE if rules are useful for the user of this tree - **/ -gboolean -pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - return tree_view->priv->has_rules; -} - -/* Public Column functions - */ - -/** - * pspp_sheet_view_append_column: - * @tree_view: A #PsppSheetView. - * @column: The #PsppSheetViewColumn to add. - * - * Appends @column to the list of columns. - * - * Return value: The number of columns in @tree_view after appending. - **/ -gint -pspp_sheet_view_append_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1); - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1); - g_return_val_if_fail (column->tree_view == NULL, -1); - - return pspp_sheet_view_insert_column (tree_view, column, -1); -} - - -/** - * pspp_sheet_view_remove_column: - * @tree_view: A #PsppSheetView. - * @column: The #PsppSheetViewColumn to remove. - * - * Removes @column from @tree_view. - * - * Return value: The number of columns in @tree_view after removing. - **/ -gint -pspp_sheet_view_remove_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1); - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1); - g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1); - - if (tree_view->priv->focus_column == column) - tree_view->priv->focus_column = NULL; - - if (tree_view->priv->edited_column == column) - { - pspp_sheet_view_stop_editing (tree_view, TRUE); - - /* no need to, but just to be sure ... */ - tree_view->priv->edited_column = NULL; - } - - _pspp_sheet_view_column_unset_tree_view (column); - - tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column); - tree_view->priv->n_columns--; - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - GList *list; - - _pspp_sheet_view_column_unrealize_button (column); - for (list = tree_view->priv->columns; list; list = list->next) - { - PsppSheetViewColumn *tmp_column; - - tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data); - if (tmp_column->visible) - _pspp_sheet_view_column_cell_set_dirty (tmp_column); - } - - if (tree_view->priv->n_columns == 0 && - pspp_sheet_view_get_headers_visible (tree_view) && - tree_view->priv->header_window) - gdk_window_hide (tree_view->priv->header_window); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - g_object_unref (column); - g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); - - return tree_view->priv->n_columns; -} - -/** - * pspp_sheet_view_insert_column: - * @tree_view: A #PsppSheetView. - * @column: The #PsppSheetViewColumn to be inserted. - * @position: The position to insert @column in. - * - * This inserts the @column into the @tree_view at @position. If @position is - * -1, then the column is inserted at the end. - * - * Return value: The number of columns in @tree_view after insertion. - **/ -gint -pspp_sheet_view_insert_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - gint position) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1); - g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1); - g_return_val_if_fail (column->tree_view == NULL, -1); - - g_object_ref_sink (column); - - if (tree_view->priv->n_columns == 0 && - gtk_widget_get_realized (GTK_WIDGET (tree_view)) && - pspp_sheet_view_get_headers_visible (tree_view)) - { - gdk_window_show (tree_view->priv->header_window); - } - - tree_view->priv->columns = g_list_insert (tree_view->priv->columns, - column, position); - tree_view->priv->n_columns++; - - _pspp_sheet_view_column_set_tree_view (column, tree_view); - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - GList *list; - - _pspp_sheet_view_column_realize_button (column); - - for (list = tree_view->priv->columns; list; list = list->next) - { - column = PSPP_SHEET_VIEW_COLUMN (list->data); - if (column->visible) - _pspp_sheet_view_column_cell_set_dirty (column); - } - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - - g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); - - return tree_view->priv->n_columns; -} - -/** - * pspp_sheet_view_insert_column_with_attributes: - * @tree_view: A #PsppSheetView - * @position: The position to insert the new column in. - * @title: The title to set the header to. - * @cell: The #GtkCellRenderer. - * @Varargs: A %NULL-terminated list of attributes. - * - * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at - * @position. If @position is -1, then the newly created column is inserted at - * the end. The column is initialized with the attributes given. - * - * Return value: The number of columns in @tree_view after insertion. - **/ -gint -pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view, - gint position, - const gchar *title, - GtkCellRenderer *cell, - ...) -{ - PsppSheetViewColumn *column; - gchar *attribute; - va_list args; - gint column_id; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1); - - column = pspp_sheet_view_column_new (); - pspp_sheet_view_column_set_title (column, title); - pspp_sheet_view_column_pack_start (column, cell, TRUE); - - va_start (args, cell); - - attribute = va_arg (args, gchar *); - - while (attribute != NULL) - { - column_id = va_arg (args, gint); - pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id); - attribute = va_arg (args, gchar *); - } - - va_end (args); - - pspp_sheet_view_insert_column (tree_view, column, position); - - return tree_view->priv->n_columns; -} - -/** - * pspp_sheet_view_insert_column_with_data_func: - * @tree_view: a #PsppSheetView - * @position: Position to insert, -1 for append - * @title: column title - * @cell: cell renderer for column - * @func: function to set attributes of cell renderer - * @data: data for @func - * @dnotify: destroy notifier for @data - * - * Convenience function that inserts a new column into the #PsppSheetView - * with the given cell renderer and a #GtkCellDataFunc to set cell renderer - * attributes (normally using data from the model). See also - * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start(). - * - * Return value: number of columns in the tree view post-insert - **/ -gint -pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view, - gint position, - const gchar *title, - GtkCellRenderer *cell, - PsppSheetCellDataFunc func, - gpointer data, - GDestroyNotify dnotify) -{ - PsppSheetViewColumn *column; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1); - - column = pspp_sheet_view_column_new (); - pspp_sheet_view_column_set_title (column, title); - pspp_sheet_view_column_pack_start (column, cell, TRUE); - pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify); - - pspp_sheet_view_insert_column (tree_view, column, position); - - return tree_view->priv->n_columns; -} - -/** - * pspp_sheet_view_get_column: - * @tree_view: A #PsppSheetView. - * @n: The position of the column, counting from 0. - * - * Gets the #PsppSheetViewColumn at the given position in the #tree_view. - * - * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the - * range of columns. - **/ -PsppSheetViewColumn * -pspp_sheet_view_get_column (PsppSheetView *tree_view, - gint n) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - if (n < 0 || n >= tree_view->priv->n_columns) - return NULL; - - if (tree_view->priv->columns == NULL) - return NULL; - - return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data); -} - -/** - * pspp_sheet_view_get_columns: - * @tree_view: A #PsppSheetView - * - * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view. - * The returned list must be freed with g_list_free (). - * - * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s - **/ -GList * -pspp_sheet_view_get_columns (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return g_list_copy (tree_view->priv->columns); -} - -/** - * pspp_sheet_view_move_column_after: - * @tree_view: A #PsppSheetView - * @column: The #PsppSheetViewColumn to be moved. - * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL. - * - * Moves @column to be after to @base_column. If @base_column is %NULL, then - * @column is placed in the first position. - **/ -void -pspp_sheet_view_move_column_after (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - PsppSheetViewColumn *base_column) -{ - GList *column_list_el, *base_el = NULL; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - column_list_el = g_list_find (tree_view->priv->columns, column); - g_return_if_fail (column_list_el != NULL); - - if (base_column) - { - base_el = g_list_find (tree_view->priv->columns, base_column); - g_return_if_fail (base_el != NULL); - } - - if (column_list_el->prev == base_el) - return; - - tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el); - if (base_el == NULL) - { - column_list_el->prev = NULL; - column_list_el->next = tree_view->priv->columns; - if (column_list_el->next) - column_list_el->next->prev = column_list_el; - tree_view->priv->columns = column_list_el; - } - else - { - column_list_el->prev = base_el; - column_list_el->next = base_el->next; - if (column_list_el->next) - column_list_el->next->prev = column_list_el; - base_el->next = column_list_el; - } - - if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) - { - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL); - } - - g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); -} - -/** - * pspp_sheet_view_set_column_drag_function: - * @tree_view: A #PsppSheetView. - * @func: (allow-none): A function to determine which columns are reorderable, or %NULL. - * @user_data: (allow-none): User data to be passed to @func, or %NULL - * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL - * - * Sets a user function for determining where a column may be dropped when - * dragged. This function is called on every column pair in turn at the - * beginning of a column drag to determine where a drop can take place. The - * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being - * dragged, the two #PsppSheetViewColumn s determining the drop spot, and - * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot - * are %NULL, then they indicate an edge. If @func is set to be %NULL, then - * @tree_view reverts to the default behavior of allowing all columns to be - * dropped everywhere. - **/ -void -pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view, - PsppSheetViewColumnDropFunc func, - gpointer user_data, - GDestroyNotify destroy) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (tree_view->priv->column_drop_func_data_destroy) - tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data); - - tree_view->priv->column_drop_func = func; - tree_view->priv->column_drop_func_data = user_data; - tree_view->priv->column_drop_func_data_destroy = destroy; -} - -/** - * pspp_sheet_view_scroll_to_point: - * @tree_view: a #PsppSheetView - * @tree_x: X coordinate of new top-left pixel of visible area, or -1 - * @tree_y: Y coordinate of new top-left pixel of visible area, or -1 - * - * Scrolls the tree view such that the top-left corner of the visible - * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified - * in tree coordinates. The @tree_view must be realized before - * this function is called. If it isn't, you probably want to be - * using pspp_sheet_view_scroll_to_cell(). - * - * If either @tree_x or @tree_y are -1, then that direction isn't scrolled. - **/ -void -pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view, - gint tree_x, - gint tree_y) -{ - GtkAdjustment *hadj; - GtkAdjustment *vadj; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); - - hadj = tree_view->priv->hadjustment; - vadj = tree_view->priv->vadjustment; - - if (tree_x != -1) - gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj))); - if (tree_y != -1) - gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj))); -} - -/** - * pspp_sheet_view_scroll_to_cell: - * @tree_view: A #PsppSheetView. - * @path: (allow-none): The path of the row to move to, or %NULL. - * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL. - * @use_align: whether to use alignment arguments, or %FALSE. - * @row_align: The vertical alignment of the row specified by @path. - * @col_align: The horizontal alignment of the column specified by @column. - * - * Moves the alignments of @tree_view to the position specified by @column and - * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise, - * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column - * or @path need to be non-%NULL. @row_align determines where the row is - * placed, and @col_align determines where @column is placed. Both are expected - * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means - * right/bottom alignment, 0.5 means center. - * - * If @use_align is %FALSE, then the alignment arguments are ignored, and the - * tree does the minimum amount of work to scroll the cell onto the screen. - * This means that the cell will be scrolled to the edge closest to its current - * position. If the cell is currently visible on the screen, nothing is done. - * - * This function only works if the model is set, and @path is a valid row on the - * model. If the model changes before the @tree_view is realized, the centered - * path will be modified to reflect this change. - **/ -void -pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column, - gboolean use_align, - gfloat row_align, - gfloat col_align) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (tree_view->priv->model != NULL); - g_return_if_fail (row_align >= 0.0 && row_align <= 1.0); - g_return_if_fail (col_align >= 0.0 && col_align <= 1.0); - g_return_if_fail (path != NULL || column != NULL); - -#if 0 - g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n", - gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align); -#endif - row_align = CLAMP (row_align, 0.0, 1.0); - col_align = CLAMP (col_align, 0.0, 1.0); - - - /* Note: Despite the benefits that come from having one code path for the - * scrolling code, we short-circuit validate_visible_area's immplementation as - * it is much slower than just going to the point. - */ - if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) || - !gtk_widget_get_realized (GTK_WIDGET (tree_view)) - /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */) - { - if (tree_view->priv->scroll_to_path) - gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); - - tree_view->priv->scroll_to_path = NULL; - tree_view->priv->scroll_to_column = NULL; - - if (path) - tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path); - if (column) - tree_view->priv->scroll_to_column = column; - tree_view->priv->scroll_to_use_align = use_align; - tree_view->priv->scroll_to_row_align = row_align; - tree_view->priv->scroll_to_col_align = col_align; - - install_presize_handler (tree_view); - } - else - { - GdkRectangle cell_rect; - GdkRectangle vis_rect; - gint dest_x, dest_y; - - pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect); - pspp_sheet_view_get_visible_rect (tree_view, &vis_rect); - - cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y); - - dest_x = vis_rect.x; - dest_y = vis_rect.y; - - if (column) - { - if (use_align) - { - dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align); - } - else - { - if (cell_rect.x < vis_rect.x) - dest_x = cell_rect.x; - if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width) - dest_x = cell_rect.x + cell_rect.width - vis_rect.width; - } - } - - if (path) - { - if (use_align) - { - dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align); - dest_y = MAX (dest_y, 0); - } - else - { - if (cell_rect.y < vis_rect.y) - dest_y = cell_rect.y; - if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height) - dest_y = cell_rect.y + cell_rect.height - vis_rect.height; - } - } - - pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y); - } -} - -/** - * pspp_sheet_view_row_activated: - * @tree_view: A #PsppSheetView - * @path: The #GtkTreePath to be activated. - * @column: The #PsppSheetViewColumn to be activated. - * - * Activates the cell determined by @path and @column. - **/ -void -pspp_sheet_view_row_activated (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column); -} - - -/** - * pspp_sheet_view_get_reorderable: - * @tree_view: a #PsppSheetView - * - * Retrieves whether the user can reorder the tree via drag-and-drop. See - * pspp_sheet_view_set_reorderable(). - * - * Return value: %TRUE if the tree can be reordered. - **/ -gboolean -pspp_sheet_view_get_reorderable (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - return tree_view->priv->reorderable; -} - -/** - * pspp_sheet_view_set_reorderable: - * @tree_view: A #PsppSheetView. - * @reorderable: %TRUE, if the tree can be reordered. - * - * This function is a convenience function to allow you to reorder - * models that support the #GtkDragSourceIface and the - * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support - * these. If @reorderable is %TRUE, then the user can reorder the - * model by dragging and dropping rows. The developer can listen to - * these changes by connecting to the model's row_inserted and - * row_deleted signals. The reordering is implemented by setting up - * the tree view as a drag source and destination. Therefore, drag and - * drop can not be used in a reorderable view for any other purpose. - * - * This function does not give you any degree of control over the order -- any - * reordering is allowed. If more control is needed, you should probably - * handle drag and drop manually. - **/ -void -pspp_sheet_view_set_reorderable (PsppSheetView *tree_view, - gboolean reorderable) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - reorderable = reorderable != FALSE; - - if (tree_view->priv->reorderable == reorderable) - return; - - if (reorderable) - { - const GtkTargetEntry row_targets[] = { - { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 } - }; - - pspp_sheet_view_enable_model_drag_source (tree_view, - GDK_BUTTON1_MASK, - row_targets, - G_N_ELEMENTS (row_targets), - GDK_ACTION_MOVE); - pspp_sheet_view_enable_model_drag_dest (tree_view, - row_targets, - G_N_ELEMENTS (row_targets), - GDK_ACTION_MOVE); - } - else - { - pspp_sheet_view_unset_rows_drag_source (tree_view); - pspp_sheet_view_unset_rows_drag_dest (tree_view); - } - - tree_view->priv->reorderable = reorderable; - - g_object_notify (G_OBJECT (tree_view), "reorderable"); -} - -/* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift - is pressed, other rows will be unselected. - - If CLAMP_NODE is true, then the sheetview will scroll to make the row - visible. */ -static void -pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view, - GtkTreePath *path, - gboolean clear_and_select, - gboolean clamp_node, - PsppSheetSelectMode mode) -{ - int node = -1; - - if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) - { - GtkTreePath *cursor_path; - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL); - gtk_tree_path_free (cursor_path); - } - - gtk_tree_row_reference_free (tree_view->priv->cursor); - tree_view->priv->cursor = NULL; - - _pspp_sheet_view_find_node (tree_view, path, &node); - tree_view->priv->cursor = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), - tree_view->priv->model, - path); - - if (tree_view->priv->row_count > 0) - { - int new_node = -1; - - if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE)) - _pspp_sheet_selection_internal_select_node (tree_view->priv->selection, - node, path, mode, - FALSE); - - /* We have to re-find tree and node here again, somebody might have - * cleared the node or the whole tree in the PsppSheetSelection::changed - * callback. If the nodes differ we bail out here. - */ - _pspp_sheet_view_find_node (tree_view, path, &new_node); - - if (node != new_node) - return; - - if (clamp_node) - { - pspp_sheet_view_clamp_node_visible (tree_view, node); - _pspp_sheet_view_queue_draw_node (tree_view, node, NULL); - } - } - - g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); -} - -/** - * pspp_sheet_view_get_cursor: - * @tree_view: A #PsppSheetView - * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL - * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL - * - * Fills in @path and @focus_column with the current path and focus column. If - * the cursor isn't currently set, then *@path will be %NULL. If no column - * currently has focus, then *@focus_column will be %NULL. - * - * The returned #GtkTreePath must be freed with gtk_tree_path_free() when - * you are done with it. - **/ -void -pspp_sheet_view_get_cursor (PsppSheetView *tree_view, - GtkTreePath **path, - PsppSheetViewColumn **focus_column) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (path) - { - if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) - *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - else - *path = NULL; - } - - if (focus_column) - { - *focus_column = tree_view->priv->focus_column; - } -} - -/** - * pspp_sheet_view_set_cursor: - * @tree_view: A #PsppSheetView - * @path: A #GtkTreePath - * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL - * @start_editing: %TRUE if the specified cell should start being edited. - * - * Sets the current keyboard focus to be at @path, and selects it. This is - * useful when you want to focus the user's attention on a particular row. If - * @focus_column is not %NULL, then focus is given to the column specified by - * it. Additionally, if @focus_column is specified, and @start_editing is - * %TRUE, then editing should be started in the specified cell. - * This function is often followed by @gtk_widget_grab_focus (@tree_view) - * in order to give keyboard focus to the widget. Please note that editing - * can only happen when the widget is realized. - * - * If @path is invalid for @model, the current cursor (if any) will be unset - * and the function will return without failing. - **/ -void -pspp_sheet_view_set_cursor (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *focus_column, - gboolean start_editing) -{ - pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column, - NULL, start_editing); -} - -/** - * pspp_sheet_view_set_cursor_on_cell: - * @tree_view: A #PsppSheetView - * @path: A #GtkTreePath - * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL - * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL - * @start_editing: %TRUE if the specified cell should start being edited. - * - * Sets the current keyboard focus to be at @path, and selects it. This is - * useful when you want to focus the user's attention on a particular row. If - * @focus_column is not %NULL, then focus is given to the column specified by - * it. If @focus_column and @focus_cell are not %NULL, and @focus_column - * contains 2 or more editable or activatable cells, then focus is given to - * the cell specified by @focus_cell. Additionally, if @focus_column is - * specified, and @start_editing is %TRUE, then editing should be started in - * the specified cell. This function is often followed by - * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the - * widget. Please note that editing can only happen when the widget is - * realized. - * - * If @path is invalid for @model, the current cursor (if any) will be unset - * and the function will return without failing. - * - * Since: 2.2 - **/ -void -pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *focus_column, - GtkCellRenderer *focus_cell, - gboolean start_editing) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (path != NULL); - g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column)); - - if (!tree_view->priv->model) - return; - - if (focus_cell) - { - g_return_if_fail (focus_column); - g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell)); - } - - /* cancel the current editing, if it exists */ - if (tree_view->priv->edited_column && - tree_view->priv->edited_column->editable_widget) - pspp_sheet_view_stop_editing (tree_view, TRUE); - - pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0); - - if (focus_column && focus_column->visible) - { - GList *list; - gboolean column_in_tree = FALSE; - - for (list = tree_view->priv->columns; list; list = list->next) - if (list->data == focus_column) - { - column_in_tree = TRUE; - break; - } - g_return_if_fail (column_in_tree); - tree_view->priv->focus_column = focus_column; - if (focus_cell) - pspp_sheet_view_column_focus_cell (focus_column, focus_cell); - if (start_editing) - pspp_sheet_view_start_editing (tree_view, path); - - pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); - pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column); - - } -} - -/** - * pspp_sheet_view_get_bin_window: - * @tree_view: A #PsppSheetView - * - * Returns the window that @tree_view renders to. This is used primarily to - * compare to event->window to confirm that the event on - * @tree_view is on the right window. - * - * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet - **/ -GdkWindow * -pspp_sheet_view_get_bin_window (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return tree_view->priv->bin_window; -} - -/** - * pspp_sheet_view_get_path_at_pos: - * @tree_view: A #PsppSheetView. - * @x: The x position to be identified (relative to bin_window). - * @y: The y position to be identified (relative to bin_window). - * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL - * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL - * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL - * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL - * - * Finds the path at the point (@x, @y), relative to bin_window coordinates - * (please see pspp_sheet_view_get_bin_window()). - * That is, @x and @y are relative to an events coordinates. @x and @y must - * come from an event on the @tree_view only where event->window == - * pspp_sheet_view_get_bin_window (). It is primarily for - * things like popup menus. If @path is non-%NULL, then it will be filled - * with the #GtkTreePath at that point. This path should be freed with - * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled - * with the column at that point. @cell_x and @cell_y return the coordinates - * relative to the cell background (i.e. the @background_area passed to - * gtk_cell_renderer_render()). This function is only meaningful if - * @tree_view is realized. Therefore this function will always return %FALSE - * if @tree_view is not realized or does not have a model. - * - * For converting widget coordinates (eg. the ones you get from - * GtkWidget::query-tooltip), please see - * pspp_sheet_view_convert_widget_to_bin_window_coords(). - * - * Return value: %TRUE if a row exists at that coordinate. - **/ -gboolean -pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view, - gint x, - gint y, - GtkTreePath **path, - PsppSheetViewColumn **column, - gint *cell_x, - gint *cell_y) -{ - int node; - gint y_offset; - - g_return_val_if_fail (tree_view != NULL, FALSE); - - if (path) - *path = NULL; - if (column) - *column = NULL; - - if (tree_view->priv->bin_window == NULL) - return FALSE; - - if (tree_view->priv->row_count == 0) - return FALSE; - - if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment)) - return FALSE; - - if (x < 0 || y < 0) - return FALSE; - - if (column || cell_x) - { - PsppSheetViewColumn *tmp_column; - PsppSheetViewColumn *last_column = NULL; - GList *list; - gint remaining_x = x; - gboolean found = FALSE; - gboolean rtl; - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; - list = (rtl ? list->prev : list->next)) - { - tmp_column = list->data; - - if (tmp_column->visible == FALSE) - continue; - - last_column = tmp_column; - if (remaining_x <= tmp_column->width) - { - found = TRUE; - - if (column) - *column = tmp_column; - - if (cell_x) - *cell_x = remaining_x; - - break; - } - remaining_x -= tmp_column->width; - } - - /* If found is FALSE and there is a last_column, then it the remainder - * space is in that area - */ - if (!found) - { - if (last_column) - { - if (column) - *column = last_column; - - if (cell_x) - *cell_x = last_column->width + remaining_x; - } - else - { - return FALSE; - } - } - } - - y_offset = pspp_sheet_view_find_offset (tree_view, - TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y), - &node); - - if (node < 0) - return FALSE; - - if (cell_y) - *cell_y = y_offset; - - if (path) - *path = _pspp_sheet_view_find_path (tree_view, node); - - return TRUE; -} - -/* Computes 'cell_area' from 'background_area', which must be the background - area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area - as passed to a GtkCellRenderer's "render" function, or to FALSE to compute - the cell area as passed to _pspp_sheet_view_column_cell_render(). - - 'column' is required to properly adjust 'cell_area->x' and - 'cell_area->width'. It may be set to NULL if these values are not of - interest. In this case 'cell_area->x' and 'cell_area->width' will be - returned as 0. */ -static void -pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - const GdkRectangle *background_area, - gboolean subtract_focus_rect, - GdkRectangle *cell_area) -{ - gint vertical_separator; - gint horizontal_separator; - - *cell_area = *background_area; - - gtk_widget_style_get (GTK_WIDGET (tree_view), - "vertical-separator", &vertical_separator, - "horizontal-separator", &horizontal_separator, - NULL); - cell_area->x += horizontal_separator / 2; - cell_area->y += vertical_separator / 2; - cell_area->width -= horizontal_separator; - cell_area->height -= vertical_separator; - - if (subtract_focus_rect) - { - int focus_line_width; - - gtk_widget_style_get (GTK_WIDGET (tree_view), - "focus-line-width", &focus_line_width, - NULL); - cell_area->x += focus_line_width; - cell_area->y += focus_line_width; - cell_area->width -= 2 * focus_line_width; - cell_area->height -= 2 * focus_line_width; - } - - if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE) - { - gint grid_line_width; - gtk_widget_style_get (GTK_WIDGET (tree_view), - "grid-line-width", &grid_line_width, - NULL); - - if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL - || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH) - && column != NULL) - { - PsppSheetViewColumn *first_column, *last_column; - GList *list; - - /* Find the last visible column. */ - last_column = NULL; - for (list = g_list_last (tree_view->priv->columns); - list; - list = list->prev) - { - PsppSheetViewColumn *c = list->data; - if (c->visible) - { - last_column = c; - break; - } - } - - /* Find the first visible column. */ - first_column = NULL; - for (list = g_list_first (tree_view->priv->columns); - list; - list = list->next) - { - PsppSheetViewColumn *c = list->data; - if (c->visible) - { - first_column = c; - break; - } - } - - if (column == first_column) - { - cell_area->width -= grid_line_width / 2; - } - else if (column == last_column) - { - cell_area->x += grid_line_width / 2; - cell_area->width -= grid_line_width / 2; - } - else - { - cell_area->x += grid_line_width / 2; - cell_area->width -= grid_line_width; - } - } - - if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL - || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH) - { - cell_area->y += grid_line_width / 2; - cell_area->height -= grid_line_width; - } - } - - if (column == NULL) - { - cell_area->x = 0; - cell_area->width = 0; - } -} - -/** - * pspp_sheet_view_get_cell_area: - * @tree_view: a #PsppSheetView - * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates - * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates - * @rect: rectangle to fill with cell rect - * - * Fills the bounding rectangle in bin_window coordinates for the cell at the - * row specified by @path and the column specified by @column. If @path is - * %NULL, or points to a path not currently displayed, the @y and @height fields - * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width - * fields will be filled with 0. The sum of all cell rects does not cover the - * entire tree; there are extra pixels in between rows, for example. The - * returned rectangle is equivalent to the @cell_area passed to - * gtk_cell_renderer_render(). This function is only valid if @tree_view is - * realized. - **/ -void -pspp_sheet_view_get_cell_area (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column, - GdkRectangle *rect) -{ - GdkRectangle background_area; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column)); - g_return_if_fail (rect != NULL); - g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view); - g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); - - pspp_sheet_view_get_background_area (tree_view, path, column, - &background_area); - pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area, - FALSE, rect); -} - -/** - * pspp_sheet_view_get_background_area: - * @tree_view: a #PsppSheetView - * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates - * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes - * @rect: rectangle to fill with cell background rect - * - * Fills the bounding rectangle in bin_window coordinates for the cell at the - * row specified by @path and the column specified by @column. If @path is - * %NULL, or points to a node not found in the tree, the @y and @height fields of - * the rectangle will be filled with 0. If @column is %NULL, the @x and @width - * fields will be filled with 0. The returned rectangle is equivalent to the - * @background_area passed to gtk_cell_renderer_render(). These background - * areas tile to cover the entire bin window. Contrast with the @cell_area, - * returned by pspp_sheet_view_get_cell_area(), which returns only the cell - * itself, excluding surrounding borders. - * - **/ -void -pspp_sheet_view_get_background_area (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column, - GdkRectangle *rect) -{ - int node = -1; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column)); - g_return_if_fail (rect != NULL); - - rect->x = 0; - rect->y = 0; - rect->width = 0; - rect->height = 0; - - if (path) - { - /* Get vertical coords */ - - _pspp_sheet_view_find_node (tree_view, path, &node); - if (node < 0) - return; - - rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node); - - rect->height = ROW_HEIGHT (tree_view); - } - - if (column) - { - gint x2 = 0; - - pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2); - rect->width = x2 - rect->x; - } -} - -/** - * pspp_sheet_view_get_visible_rect: - * @tree_view: a #PsppSheetView - * @visible_rect: rectangle to fill - * - * Fills @visible_rect with the currently-visible region of the - * buffer, in tree coordinates. Convert to bin_window coordinates with - * pspp_sheet_view_convert_tree_to_bin_window_coords(). - * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire - * scrollable area of the tree. - **/ -void -pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view, - GdkRectangle *visible_rect) -{ - GtkWidget *widget; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - widget = GTK_WIDGET (tree_view); - - if (visible_rect) - { - GtkAllocation allocation; - gtk_widget_get_allocation (widget, &allocation); - visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment); - visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment); - visible_rect->width = allocation.width; - visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); - } -} - -/** - * pspp_sheet_view_widget_to_tree_coords: - * @tree_view: a #PsppSheetView - * @wx: X coordinate relative to bin_window - * @wy: Y coordinate relative to bin_window - * @tx: return location for tree X coordinate - * @ty: return location for tree Y coordinate - * - * Converts bin_window coordinates to coordinates for the - * tree (the full scrollable area of the tree). - * - * Deprecated: 2.12: Due to historial reasons the name of this function is - * incorrect. For converting coordinates relative to the widget to - * bin_window coordinates, please see - * pspp_sheet_view_convert_widget_to_bin_window_coords(). - * - **/ -void -pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view, - gint wx, - gint wy, - gint *tx, - gint *ty) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (tx) - *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment); - if (ty) - *ty = wy + tree_view->priv->dy; -} - -/** - * pspp_sheet_view_tree_to_widget_coords: - * @tree_view: a #PsppSheetView - * @tx: tree X coordinate - * @ty: tree Y coordinate - * @wx: return location for X coordinate relative to bin_window - * @wy: return location for Y coordinate relative to bin_window - * - * Converts tree coordinates (coordinates in full scrollable area of the tree) - * to bin_window coordinates. - * - * Deprecated: 2.12: Due to historial reasons the name of this function is - * incorrect. For converting bin_window coordinates to coordinates relative - * to bin_window, please see - * pspp_sheet_view_convert_bin_window_to_widget_coords(). - * - **/ -void -pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view, - gint tx, - gint ty, - gint *wx, - gint *wy) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (wx) - *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment); - if (wy) - *wy = ty - tree_view->priv->dy; -} - - -/** - * pspp_sheet_view_convert_widget_to_tree_coords: - * @tree_view: a #PsppSheetView - * @wx: X coordinate relative to the widget - * @wy: Y coordinate relative to the widget - * @tx: return location for tree X coordinate - * @ty: return location for tree Y coordinate - * - * Converts widget coordinates to coordinates for the - * tree (the full scrollable area of the tree). - * - * Since: 2.12 - **/ -void -pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view, - gint wx, - gint wy, - gint *tx, - gint *ty) -{ - gint x, y; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, - wx, wy, - &x, &y); - pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view, - x, y, - tx, ty); -} - -/** - * pspp_sheet_view_convert_tree_to_widget_coords: - * @tree_view: a #PsppSheetView - * @tx: X coordinate relative to the tree - * @ty: Y coordinate relative to the tree - * @wx: return location for widget X coordinate - * @wy: return location for widget Y coordinate - * - * Converts tree coordinates (coordinates in full scrollable area of the tree) - * to widget coordinates. - * - * Since: 2.12 - **/ -void -pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view, - gint tx, - gint ty, - gint *wx, - gint *wy) -{ - gint x, y; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view, - tx, ty, - &x, &y); - pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view, - x, y, - wx, wy); -} - -/** - * pspp_sheet_view_convert_widget_to_bin_window_coords: - * @tree_view: a #PsppSheetView - * @wx: X coordinate relative to the widget - * @wy: Y coordinate relative to the widget - * @bx: return location for bin_window X coordinate - * @by: return location for bin_window Y coordinate - * - * Converts widget coordinates to coordinates for the bin_window - * (see pspp_sheet_view_get_bin_window()). - * - * Since: 2.12 - **/ -void -pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view, - gint wx, - gint wy, - gint *bx, - gint *by) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (bx) - *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment); - if (by) - *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view); -} - -/** - * pspp_sheet_view_convert_bin_window_to_widget_coords: - * @tree_view: a #PsppSheetView - * @bx: bin_window X coordinate - * @by: bin_window Y coordinate - * @wx: return location for widget X coordinate - * @wy: return location for widget Y coordinate - * - * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window()) - * to widget relative coordinates. - * - * Since: 2.12 - **/ -void -pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view, - gint bx, - gint by, - gint *wx, - gint *wy) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (wx) - *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment); - if (wy) - *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view); -} - -/** - * pspp_sheet_view_convert_tree_to_bin_window_coords: - * @tree_view: a #PsppSheetView - * @tx: tree X coordinate - * @ty: tree Y coordinate - * @bx: return location for X coordinate relative to bin_window - * @by: return location for Y coordinate relative to bin_window - * - * Converts tree coordinates (coordinates in full scrollable area of the tree) - * to bin_window coordinates. - * - * Since: 2.12 - **/ -void -pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view, - gint tx, - gint ty, - gint *bx, - gint *by) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (bx) - *bx = tx; - if (by) - *by = ty - tree_view->priv->dy; -} - -/** - * pspp_sheet_view_convert_bin_window_to_tree_coords: - * @tree_view: a #PsppSheetView - * @bx: X coordinate relative to bin_window - * @by: Y coordinate relative to bin_window - * @tx: return location for tree X coordinate - * @ty: return location for tree Y coordinate - * - * Converts bin_window coordinates to coordinates for the - * tree (the full scrollable area of the tree). - * - * Since: 2.12 - **/ -void -pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view, - gint bx, - gint by, - gint *tx, - gint *ty) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (tx) - *tx = bx; - if (ty) - *ty = by + tree_view->priv->dy; -} - - - -/** - * pspp_sheet_view_get_visible_range: - * @tree_view: A #PsppSheetView - * @start_path: (allow-none): Return location for start of region, or %NULL. - * @end_path: (allow-none): Return location for end of region, or %NULL. - * - * Sets @start_path and @end_path to be the first and last visible path. - * Note that there may be invisible paths in between. - * - * The paths should be freed with gtk_tree_path_free() after use. - * - * Returns: %TRUE, if valid paths were placed in @start_path and @end_path. - * - * Since: 2.8 - **/ -gboolean -pspp_sheet_view_get_visible_range (PsppSheetView *tree_view, - GtkTreePath **start_path, - GtkTreePath **end_path) -{ - int node; - gboolean retval; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - if (!tree_view->priv->row_count) - return FALSE; - - retval = TRUE; - - if (start_path) - { - pspp_sheet_view_find_offset (tree_view, - TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0), - &node); - if (node >= 0) - *start_path = _pspp_sheet_view_find_path (tree_view, node); - else - retval = FALSE; - } - - if (end_path) - { - gint y; - - if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - y = tree_view->priv->height - 1; - else - y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1; - - pspp_sheet_view_find_offset (tree_view, y, &node); - if (node >= 0) - *end_path = _pspp_sheet_view_find_path (tree_view, node); - else - retval = FALSE; - } - - return retval; -} - -static void -unset_reorderable (PsppSheetView *tree_view) -{ - if (tree_view->priv->reorderable) - { - tree_view->priv->reorderable = FALSE; - g_object_notify (G_OBJECT (tree_view), "reorderable"); - } -} - -/** - * pspp_sheet_view_enable_model_drag_source: - * @tree_view: a #PsppSheetView - * @start_button_mask: Mask of allowed buttons to start drag - * @targets: the table of targets that the drag will support - * @n_targets: the number of items in @targets - * @actions: the bitmask of possible actions for a drag from this - * widget - * - * Turns @tree_view into a drag source for automatic DND. Calling this - * method sets #PsppSheetView:reorderable to %FALSE. - **/ -void -pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view, - GdkModifierType start_button_mask, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions) -{ - TreeViewDragInfo *di; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - gtk_drag_source_set (GTK_WIDGET (tree_view), - 0, - targets, - n_targets, - actions); - - di = ensure_info (tree_view); - - di->start_button_mask = start_button_mask; - di->source_actions = actions; - di->source_set = TRUE; - - unset_reorderable (tree_view); -} - -/** - * pspp_sheet_view_enable_model_drag_dest: - * @tree_view: a #PsppSheetView - * @targets: the table of targets that the drag will support - * @n_targets: the number of items in @targets - * @actions: the bitmask of possible actions for a drag from this - * widget - * - * Turns @tree_view into a drop destination for automatic DND. Calling - * this method sets #PsppSheetView:reorderable to %FALSE. - **/ -void -pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions) -{ - TreeViewDragInfo *di; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - gtk_drag_dest_set (GTK_WIDGET (tree_view), - 0, - targets, - n_targets, - actions); - - di = ensure_info (tree_view); - di->dest_set = TRUE; - - unset_reorderable (tree_view); -} - -/** - * pspp_sheet_view_unset_rows_drag_source: - * @tree_view: a #PsppSheetView - * - * Undoes the effect of - * pspp_sheet_view_enable_model_drag_source(). Calling this method sets - * #PsppSheetView:reorderable to %FALSE. - **/ -void -pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view) -{ - TreeViewDragInfo *di; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - di = get_info (tree_view); - - if (di) - { - if (di->source_set) - { - gtk_drag_source_unset (GTK_WIDGET (tree_view)); - di->source_set = FALSE; - } - - if (!di->dest_set && !di->source_set) - remove_info (tree_view); - } - - unset_reorderable (tree_view); -} - -/** - * pspp_sheet_view_unset_rows_drag_dest: - * @tree_view: a #PsppSheetView - * - * Undoes the effect of - * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets - * #PsppSheetView:reorderable to %FALSE. - **/ -void -pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view) -{ - TreeViewDragInfo *di; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - di = get_info (tree_view); - - if (di) - { - if (di->dest_set) - { - gtk_drag_dest_unset (GTK_WIDGET (tree_view)); - di->dest_set = FALSE; - } - - if (!di->dest_set && !di->source_set) - remove_info (tree_view); - } - - unset_reorderable (tree_view); -} - -/** - * pspp_sheet_view_set_drag_dest_row: - * @tree_view: a #PsppSheetView - * @path: (allow-none): The path of the row to highlight, or %NULL. - * @pos: Specifies whether to drop before, after or into the row - * - * Sets the row that is highlighted for feedback. - **/ -void -pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewDropPosition pos) -{ - GtkTreePath *current_dest; - - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - current_dest = NULL; - - if (tree_view->priv->drag_dest_row) - { - current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); - gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); - } - - /* special case a drop on an empty model */ - tree_view->priv->empty_view_drop = 0; - - if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path - && gtk_tree_path_get_depth (path) == 1 - && gtk_tree_path_get_indices (path)[0] == 0) - { - gint n_children; - - n_children = gtk_tree_model_iter_n_children (tree_view->priv->model, - NULL); - - if (!n_children) - tree_view->priv->empty_view_drop = 1; - } - - tree_view->priv->drag_dest_pos = pos; - - if (path) - { - tree_view->priv->drag_dest_row = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path); - pspp_sheet_view_queue_draw_path (tree_view, path, NULL); - } - else - tree_view->priv->drag_dest_row = NULL; - - if (current_dest) - { - int node, new_node; - - _pspp_sheet_view_find_node (tree_view, current_dest, &node); - _pspp_sheet_view_queue_draw_node (tree_view, node, NULL); - - if (node >= 0) - { - new_node = pspp_sheet_view_node_next (tree_view, node); - if (new_node >= 0) - _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL); - - new_node = pspp_sheet_view_node_prev (tree_view, node); - if (new_node >= 0) - _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL); - } - gtk_tree_path_free (current_dest); - } -} - -/** - * pspp_sheet_view_get_drag_dest_row: - * @tree_view: a #PsppSheetView - * @path: (allow-none): Return location for the path of the highlighted row, or %NULL. - * @pos: (allow-none): Return location for the drop position, or %NULL - * - * Gets information about the row that is highlighted for feedback. - **/ -void -pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view, - GtkTreePath **path, - PsppSheetViewDropPosition *pos) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (path) - { - if (tree_view->priv->drag_dest_row) - *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); - else - { - if (tree_view->priv->empty_view_drop) - *path = gtk_tree_path_new_from_indices (0, -1); - else - *path = NULL; - } - } - - if (pos) - *pos = tree_view->priv->drag_dest_pos; -} - -/** - * pspp_sheet_view_get_dest_row_at_pos: - * @tree_view: a #PsppSheetView - * @drag_x: the position to determine the destination row for - * @drag_y: the position to determine the destination row for - * @path: (allow-none): Return location for the path of the highlighted row, or %NULL. - * @pos: (allow-none): Return location for the drop position, or %NULL - * - * Determines the destination row for a given position. @drag_x and - * @drag_y are expected to be in widget coordinates. This function is only - * meaningful if @tree_view is realized. Therefore this function will always - * return %FALSE if @tree_view is not realized or does not have a model. - * - * Return value: whether there is a row at the given position, %TRUE if this - * is indeed the case. - **/ -gboolean -pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view, - gint drag_x, - gint drag_y, - GtkTreePath **path, - PsppSheetViewDropPosition *pos) -{ - gint cell_y; - gint bin_x, bin_y; - gdouble offset_into_row; - gdouble third; - GdkRectangle cell; - PsppSheetViewColumn *column = NULL; - GtkTreePath *tmp_path = NULL; - - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ - - g_return_val_if_fail (tree_view != NULL, FALSE); - g_return_val_if_fail (drag_x >= 0, FALSE); - g_return_val_if_fail (drag_y >= 0, FALSE); - - if (path) - *path = NULL; - - if (tree_view->priv->bin_window == NULL) - return FALSE; - - if (tree_view->priv->row_count == 0) - return FALSE; - - /* If in the top third of a row, we drop before that row; if - * in the bottom third, drop after that row; if in the middle, - * and the row has children, drop into the row. - */ - pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y, - &bin_x, &bin_y); - - if (!pspp_sheet_view_get_path_at_pos (tree_view, - bin_x, - bin_y, - &tmp_path, - &column, - NULL, - &cell_y)) - return FALSE; - - pspp_sheet_view_get_background_area (tree_view, tmp_path, column, - &cell); - - offset_into_row = cell_y; - - if (path) - *path = tmp_path; - else - gtk_tree_path_free (tmp_path); - - tmp_path = NULL; - - third = cell.height / 3.0; - - if (pos) - { - if (offset_into_row < third) - { - *pos = PSPP_SHEET_VIEW_DROP_BEFORE; - } - else if (offset_into_row < (cell.height / 2.0)) - { - *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE; - } - else if (offset_into_row < third * 2.0) - { - *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER; - } - else - { - *pos = PSPP_SHEET_VIEW_DROP_AFTER; - } - } - - return TRUE; -} - - - -/** - * pspp_sheet_view_set_destroy_count_func: - * @tree_view: A #PsppSheetView - * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL - * @data: (allow-none): User data to be passed to @func, or %NULL - * @destroy: (allow-none): Destroy notifier for @data, or %NULL - * - * This function should almost never be used. It is meant for private use by - * ATK for determining the number of visible children that are removed when a row is deleted. - **/ -void -pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view, - PsppSheetDestroyCountFunc func, - gpointer data, - GDestroyNotify destroy) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (tree_view->priv->destroy_count_destroy) - tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data); - - tree_view->priv->destroy_count_func = func; - tree_view->priv->destroy_count_data = data; - tree_view->priv->destroy_count_destroy = destroy; -} - - -/* - * Interactive search - */ - -/** - * pspp_sheet_view_set_enable_search: - * @tree_view: A #PsppSheetView - * @enable_search: %TRUE, if the user can search interactively - * - * If @enable_search is set, then the user can type in text to search through - * the tree interactively (this is sometimes called "typeahead find"). - * - * Note that even if this is %FALSE, the user can still initiate a search - * using the "start-interactive-search" key binding. - */ -void -pspp_sheet_view_set_enable_search (PsppSheetView *tree_view, - gboolean enable_search) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - enable_search = !!enable_search; - - if (tree_view->priv->enable_search != enable_search) - { - tree_view->priv->enable_search = enable_search; - g_object_notify (G_OBJECT (tree_view), "enable-search"); - } -} - -/** - * pspp_sheet_view_get_enable_search: - * @tree_view: A #PsppSheetView - * - * Returns whether or not the tree allows to start interactive searching - * by typing in text. - * - * Return value: whether or not to let the user search interactively - */ -gboolean -pspp_sheet_view_get_enable_search (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - return tree_view->priv->enable_search; -} - - -/** - * pspp_sheet_view_get_search_column: - * @tree_view: A #PsppSheetView - * - * Gets the column searched on by the interactive search code. - * - * Return value: the column the interactive search code searches in. - */ -gint -pspp_sheet_view_get_search_column (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1); - - return (tree_view->priv->search_column); -} - -/** - * pspp_sheet_view_set_search_column: - * @tree_view: A #PsppSheetView - * @column: the column of the model to search in, or -1 to disable searching - * - * Sets @column as the column where the interactive search code should - * search in for the current model. - * - * If the search column is set, users can use the "start-interactive-search" - * key binding to bring up search popup. The enable-search property controls - * whether simply typing text will also start an interactive search. - * - * Note that @column refers to a column of the current model. The search - * column is reset to -1 when the model is changed. - */ -void -pspp_sheet_view_set_search_column (PsppSheetView *tree_view, - gint column) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (column >= -1); - - if (tree_view->priv->search_column == column) - return; - - tree_view->priv->search_column = column; - g_object_notify (G_OBJECT (tree_view), "search-column"); -} - -/** - * pspp_sheet_view_get_search_equal_func: - * @tree_view: A #PsppSheetView - * - * Returns the compare function currently in use. - * - * Return value: the currently used compare function for the search code. - */ - -PsppSheetViewSearchEqualFunc -pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return tree_view->priv->search_equal_func; -} - -/** - * pspp_sheet_view_set_search_equal_func: - * @tree_view: A #PsppSheetView - * @search_equal_func: the compare function to use during the search - * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL - * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL - * - * Sets the compare function for the interactive search capabilities; note - * that somewhat like strcmp() returning 0 for equality - * #PsppSheetViewSearchEqualFunc returns %FALSE on matches. - **/ -void -pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view, - PsppSheetViewSearchEqualFunc search_equal_func, - gpointer search_user_data, - GDestroyNotify search_destroy) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (search_equal_func != NULL); - - if (tree_view->priv->search_destroy) - tree_view->priv->search_destroy (tree_view->priv->search_user_data); - - tree_view->priv->search_equal_func = search_equal_func; - tree_view->priv->search_user_data = search_user_data; - tree_view->priv->search_destroy = search_destroy; - if (tree_view->priv->search_equal_func == NULL) - tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func; -} - -/** - * pspp_sheet_view_get_search_entry: - * @tree_view: A #PsppSheetView - * - * Returns the #GtkEntry which is currently in use as interactive search - * entry for @tree_view. In case the built-in entry is being used, %NULL - * will be returned. - * - * Return value: the entry currently in use as search entry. - * - * Since: 2.10 - */ -GtkEntry * -pspp_sheet_view_get_search_entry (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - if (tree_view->priv->search_custom_entry_set) - return GTK_ENTRY (tree_view->priv->search_entry); - - return NULL; -} - -/** - * pspp_sheet_view_set_search_entry: - * @tree_view: A #PsppSheetView - * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL - * - * Sets the entry which the interactive search code will use for this - * @tree_view. This is useful when you want to provide a search entry - * in our interface at all time at a fixed position. Passing %NULL for - * @entry will make the interactive search code use the built-in popup - * entry again. - * - * Since: 2.10 - */ -void -pspp_sheet_view_set_search_entry (PsppSheetView *tree_view, - GtkEntry *entry) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry)); - - if (tree_view->priv->search_custom_entry_set) - { - if (tree_view->priv->search_entry_changed_id) - { - g_signal_handler_disconnect (tree_view->priv->search_entry, - tree_view->priv->search_entry_changed_id); - tree_view->priv->search_entry_changed_id = 0; - } - g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry, - G_CALLBACK (pspp_sheet_view_search_key_press_event), - tree_view); - - g_object_unref (tree_view->priv->search_entry); - } - else if (tree_view->priv->search_window) - { - gtk_widget_destroy (tree_view->priv->search_window); - - tree_view->priv->search_window = NULL; - } - - if (entry) - { - tree_view->priv->search_entry = g_object_ref (entry); - tree_view->priv->search_custom_entry_set = TRUE; - - if (tree_view->priv->search_entry_changed_id == 0) - { - tree_view->priv->search_entry_changed_id = - g_signal_connect (tree_view->priv->search_entry, "changed", - G_CALLBACK (pspp_sheet_view_search_init), - tree_view); - } - - g_signal_connect (tree_view->priv->search_entry, "key-press-event", - G_CALLBACK (pspp_sheet_view_search_key_press_event), - tree_view); - - pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view); - } - else - { - tree_view->priv->search_entry = NULL; - tree_view->priv->search_custom_entry_set = FALSE; - } -} - -/** - * pspp_sheet_view_set_search_position_func: - * @tree_view: A #PsppSheetView - * @func: (allow-none): the function to use to position the search dialog, or %NULL - * to use the default search position function - * @data: (allow-none): user data to pass to @func, or %NULL - * @destroy: (allow-none): Destroy notifier for @data, or %NULL - * - * Sets the function to use when positioning the search dialog. - * - * Since: 2.10 - **/ -void -pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view, - PsppSheetViewSearchPositionFunc func, - gpointer user_data, - GDestroyNotify destroy) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (tree_view->priv->search_position_destroy) - tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data); - - tree_view->priv->search_position_func = func; - tree_view->priv->search_position_user_data = user_data; - tree_view->priv->search_position_destroy = destroy; - if (tree_view->priv->search_position_func == NULL) - tree_view->priv->search_position_func = pspp_sheet_view_search_position_func; -} - -/** - * pspp_sheet_view_get_search_position_func: - * @tree_view: A #PsppSheetView - * - * Returns the positioning function currently in use. - * - * Return value: the currently used function for positioning the search dialog. - * - * Since: 2.10 - */ -PsppSheetViewSearchPositionFunc -pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL); - - return tree_view->priv->search_position_func; -} - - -static void -pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog, - PsppSheetView *tree_view) -{ - if (tree_view->priv->disable_popdown) - return; - - if (tree_view->priv->search_entry_changed_id) - { - g_signal_handler_disconnect (tree_view->priv->search_entry, - tree_view->priv->search_entry_changed_id); - tree_view->priv->search_entry_changed_id = 0; - } - if (tree_view->priv->typeselect_flush_timeout) - { - g_source_remove (tree_view->priv->typeselect_flush_timeout); - tree_view->priv->typeselect_flush_timeout = 0; - } - - if (gtk_widget_get_visible (search_dialog)) - { - /* send focus-in event */ - send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE); - gtk_widget_hide (search_dialog); - gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), ""); - send_focus_change (GTK_WIDGET (tree_view), TRUE); - } -} - -static void -pspp_sheet_view_search_position_func (PsppSheetView *tree_view, - GtkWidget *search_dialog, - gpointer user_data) -{ - gint x, y; - gint tree_x, tree_y; - gint tree_width, tree_height; - GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view)); - GdkScreen *screen = gdk_window_get_screen (tree_window); - GtkRequisition requisition; - gint monitor_num; - GdkRectangle monitor; - - monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window); - gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); - - gtk_widget_realize (search_dialog); - - gdk_window_get_origin (tree_window, &tree_x, &tree_y); - tree_width = gdk_window_get_width (tree_window); - tree_height = gdk_window_get_height (tree_window); - - gtk_widget_size_request (search_dialog, &requisition); - - if (tree_x + tree_width > gdk_screen_get_width (screen)) - x = gdk_screen_get_width (screen) - requisition.width; - else if (tree_x + tree_width - requisition.width < 0) - x = 0; - else - x = tree_x + tree_width - requisition.width; - - if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen)) - y = gdk_screen_get_height (screen) - requisition.height; - else if (tree_y + tree_height < 0) /* isn't really possible ... */ - y = 0; - else - y = tree_y + tree_height; - - gtk_window_move (GTK_WINDOW (search_dialog), x, y); -} - -static void -pspp_sheet_view_search_disable_popdown (GtkEntry *entry, - GtkMenu *menu, - gpointer data) -{ - PsppSheetView *tree_view = (PsppSheetView *)data; - - tree_view->priv->disable_popdown = 1; - g_signal_connect (menu, "hide", - G_CALLBACK (pspp_sheet_view_search_enable_popdown), data); -} - - -static void -pspp_sheet_view_search_activate (GtkEntry *entry, - PsppSheetView *tree_view) -{ - GtkTreePath *path; - int node; - - pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, - tree_view); - - /* If we have a row selected and it's the cursor row, we activate - * the row XXX */ - if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) - { - path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - - _pspp_sheet_view_find_node (tree_view, path, &node); - - if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node)) - pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column); - - gtk_tree_path_free (path); - } -} - -static gboolean -pspp_sheet_view_real_search_enable_popdown (gpointer data) -{ - PsppSheetView *tree_view = (PsppSheetView *)data; - - tree_view->priv->disable_popdown = 0; - - return FALSE; -} - -static void -pspp_sheet_view_search_enable_popdown (GtkWidget *widget, - gpointer data) -{ - gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref); -} - -static gboolean -pspp_sheet_view_search_delete_event (GtkWidget *widget, - GdkEventAny *event, - PsppSheetView *tree_view) -{ - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - - pspp_sheet_view_search_dialog_hide (widget, tree_view); - - return TRUE; -} - -static gboolean -pspp_sheet_view_search_button_press_event (GtkWidget *widget, - GdkEventButton *event, - PsppSheetView *tree_view) -{ - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - - pspp_sheet_view_search_dialog_hide (widget, tree_view); - - if (event->window == tree_view->priv->bin_window) - pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event); - - return TRUE; -} - -static gboolean -pspp_sheet_view_search_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - PsppSheetView *tree_view) -{ - gboolean retval = FALSE; - - if (event->direction == GDK_SCROLL_UP) - { - pspp_sheet_view_search_move (widget, tree_view, TRUE); - retval = TRUE; - } - else if (event->direction == GDK_SCROLL_DOWN) - { - pspp_sheet_view_search_move (widget, tree_view, FALSE); - retval = TRUE; - } - - /* renew the flush timeout */ - if (retval && tree_view->priv->typeselect_flush_timeout - && !tree_view->priv->search_custom_entry_set) - { - g_source_remove (tree_view->priv->typeselect_flush_timeout); - tree_view->priv->typeselect_flush_timeout = - gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout, - tree_view); - } - - return retval; -} - -static gboolean -pspp_sheet_view_search_key_press_event (GtkWidget *widget, - GdkEventKey *event, - PsppSheetView *tree_view) -{ - gboolean retval = FALSE; - - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - /* close window and cancel the search */ - if (!tree_view->priv->search_custom_entry_set - && (event->keyval == GDK_Escape || - event->keyval == GDK_Tab || - event->keyval == GDK_KP_Tab || - event->keyval == GDK_ISO_Left_Tab)) - { - pspp_sheet_view_search_dialog_hide (widget, tree_view); - return TRUE; - } - - /* select previous matching iter */ - if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up) - { - if (!pspp_sheet_view_search_move (widget, tree_view, TRUE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) - && (event->keyval == GDK_g || event->keyval == GDK_G)) - { - if (!pspp_sheet_view_search_move (widget, tree_view, TRUE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - /* select next matching iter */ - if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down) - { - if (!pspp_sheet_view_search_move (widget, tree_view, FALSE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK) - && (event->keyval == GDK_g || event->keyval == GDK_G)) - { - if (!pspp_sheet_view_search_move (widget, tree_view, FALSE)) - gtk_widget_error_bell (widget); - - retval = TRUE; - } - - /* renew the flush timeout */ - if (retval && tree_view->priv->typeselect_flush_timeout - && !tree_view->priv->search_custom_entry_set) - { - g_source_remove (tree_view->priv->typeselect_flush_timeout); - tree_view->priv->typeselect_flush_timeout = - gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout, - tree_view); - } - - return retval; -} - -/* this function returns FALSE if there is a search string but - * nothing was found, and TRUE otherwise. - */ -static gboolean -pspp_sheet_view_search_move (GtkWidget *window, - PsppSheetView *tree_view, - gboolean up) -{ - gboolean ret; - gint len; - gint count = 0; - const gchar *text; - GtkTreeIter iter; - GtkTreeModel *model; - PsppSheetSelection *selection; - - text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)); - - g_return_val_if_fail (text != NULL, FALSE); - - len = strlen (text); - - if (up && tree_view->priv->selected_iter == 1) - return strlen (text) < 1; - - len = strlen (text); - - if (len < 1) - return TRUE; - - model = pspp_sheet_view_get_model (tree_view); - selection = pspp_sheet_view_get_selection (tree_view); - - /* search */ - pspp_sheet_selection_unselect_all (selection); - if (!gtk_tree_model_get_iter_first (model, &iter)) - return TRUE; - - ret = pspp_sheet_view_search_iter (model, selection, &iter, text, - &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1))); - - if (ret) - { - /* found */ - tree_view->priv->selected_iter += up?(-1):(1); - return TRUE; - } - else - { - /* return to old iter */ - count = 0; - gtk_tree_model_get_iter_first (model, &iter); - pspp_sheet_view_search_iter (model, selection, - &iter, text, - &count, tree_view->priv->selected_iter); - return FALSE; - } -} - -static gboolean -pspp_sheet_view_search_equal_func (GtkTreeModel *model, - gint column, - const gchar *key, - GtkTreeIter *iter, - gpointer search_data) -{ - gboolean retval = TRUE; - const gchar *str; - gchar *normalized_string; - gchar *normalized_key; - gchar *case_normalized_string = NULL; - gchar *case_normalized_key = NULL; - GValue value = {0,}; - GValue transformed = {0,}; - - gtk_tree_model_get_value (model, iter, column, &value); - - g_value_init (&transformed, G_TYPE_STRING); - - if (!g_value_transform (&value, &transformed)) - { - g_value_unset (&value); - return TRUE; - } - - g_value_unset (&value); - - str = g_value_get_string (&transformed); - if (!str) - { - g_value_unset (&transformed); - return TRUE; - } - - normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL); - normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); - - if (normalized_string && normalized_key) - { - case_normalized_string = g_utf8_casefold (normalized_string, -1); - case_normalized_key = g_utf8_casefold (normalized_key, -1); - - if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0) - retval = FALSE; - } - - g_value_unset (&transformed); - g_free (normalized_key); - g_free (normalized_string); - g_free (case_normalized_key); - g_free (case_normalized_string); - - return retval; -} - -static gboolean -pspp_sheet_view_search_iter (GtkTreeModel *model, - PsppSheetSelection *selection, - GtkTreeIter *iter, - const gchar *text, - gint *count, - gint n) -{ - int node = -1; - GtkTreePath *path; - - PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection); - - path = gtk_tree_model_get_path (model, iter); - _pspp_sheet_view_find_node (tree_view, path, &node); - - do - { - gboolean done = FALSE; - - if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data)) - { - (*count)++; - if (*count == n) - { - pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, - TRUE, 0.5, 0.0); - pspp_sheet_selection_select_iter (selection, iter); - pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0); - - if (path) - gtk_tree_path_free (path); - - return TRUE; - } - } - - - do - { - node = pspp_sheet_view_node_next (tree_view, node); - - if (node >= 0) - { - gboolean has_next; - - has_next = gtk_tree_model_iter_next (model, iter); - - done = TRUE; - gtk_tree_path_next (path); - - /* sanity check */ - TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE); - } - else - { - if (path) - gtk_tree_path_free (path); - - /* we've run out of tree, done with this func */ - return FALSE; - } - } - while (!done); - } - while (1); - - return FALSE; -} - -static void -pspp_sheet_view_search_init (GtkWidget *entry, - PsppSheetView *tree_view) -{ - gint ret; - gint count = 0; - const gchar *text; - GtkTreeIter iter; - GtkTreeModel *model; - PsppSheetSelection *selection; - - g_return_if_fail (GTK_IS_ENTRY (entry)); - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - text = gtk_entry_get_text (GTK_ENTRY (entry)); - - model = pspp_sheet_view_get_model (tree_view); - selection = pspp_sheet_view_get_selection (tree_view); - - /* search */ - pspp_sheet_selection_unselect_all (selection); - if (tree_view->priv->typeselect_flush_timeout - && !tree_view->priv->search_custom_entry_set) - { - g_source_remove (tree_view->priv->typeselect_flush_timeout); - tree_view->priv->typeselect_flush_timeout = - gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout, - tree_view); - } - - if (*text == '\0') - return; - - if (!gtk_tree_model_get_iter_first (model, &iter)) - return; - - ret = pspp_sheet_view_search_iter (model, selection, - &iter, text, - &count, 1); - - if (ret) - tree_view->priv->selected_iter = 1; -} - -static void -pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable, - PsppSheetView *tree_view) -{ - if (tree_view->priv->edited_column == NULL) - return; - - _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column); - tree_view->priv->edited_column = NULL; - - if (gtk_widget_has_focus (GTK_WIDGET (cell_editable))) - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - g_signal_handlers_disconnect_by_func (cell_editable, - pspp_sheet_view_remove_widget, - tree_view); - g_signal_handlers_disconnect_by_func (cell_editable, - pspp_sheet_view_editable_button_press_event, - tree_view); - g_signal_handlers_disconnect_by_func (cell_editable, - pspp_sheet_view_editable_clicked, - tree_view); - - gtk_container_remove (GTK_CONTAINER (tree_view), - GTK_WIDGET (cell_editable)); - - /* FIXME should only redraw a single node */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} - -static gboolean -pspp_sheet_view_start_editing (PsppSheetView *tree_view, - GtkTreePath *cursor_path) -{ - GtkTreeIter iter; - GdkRectangle background_area; - GdkRectangle cell_area; - GtkCellEditable *editable_widget = NULL; - gchar *path_string; - guint flags = 0; /* can be 0, as the flags are primarily for rendering */ - gint retval = FALSE; - int cursor_node; - - g_assert (tree_view->priv->focus_column); - - if (!gtk_widget_get_realized (GTK_WIDGET (tree_view))) - return FALSE; - - _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); - if (cursor_node < 0) - return FALSE; - - path_string = gtk_tree_path_to_string (cursor_path); - gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path); - - pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column, - tree_view->priv->model, - &iter); - pspp_sheet_view_get_background_area (tree_view, - cursor_path, - tree_view->priv->focus_column, - &background_area); - pspp_sheet_view_get_cell_area (tree_view, - cursor_path, - tree_view->priv->focus_column, - &cell_area); - - if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column, - &editable_widget, - NULL, - path_string, - &background_area, - &cell_area, - flags)) - { - retval = TRUE; - if (editable_widget != NULL) - { - gint left, right; - GdkRectangle area; - GtkCellRenderer *cell; - - area = cell_area; - cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column); - - _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right); - - area.x += left; - area.width -= right + left; - - pspp_sheet_view_real_start_editing (tree_view, - tree_view->priv->focus_column, - cursor_path, - editable_widget, - &area, - NULL, - flags); - } - - } - g_free (path_string); - return retval; -} - -static gboolean -pspp_sheet_view_editable_button_press_event (GtkWidget *widget, - GdkEventButton *event, - PsppSheetView *sheet_view) -{ - gint node; - - node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), - "pspp-sheet-view-node")); - return pspp_sheet_view_row_head_clicked (sheet_view, - node, - sheet_view->priv->edited_column, - event); -} - -static void -pspp_sheet_view_editable_clicked (GtkButton *button, - PsppSheetView *sheet_view) -{ - pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL, - sheet_view); -} - -static gboolean -is_all_selected (GtkWidget *widget) -{ - GtkEntryBuffer *buffer; - gint start_pos, end_pos; - - if (!GTK_IS_ENTRY (widget)) - return FALSE; - - buffer = gtk_entry_get_buffer (GTK_ENTRY (widget)); - return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), - &start_pos, &end_pos) - && start_pos == 0 - && end_pos == gtk_entry_buffer_get_length (buffer)); -} - -static gboolean -is_at_left (GtkWidget *widget) -{ - return (GTK_IS_ENTRY (widget) - && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0); -} - -static gboolean -is_at_right (GtkWidget *widget) -{ - GtkEntryBuffer *buffer; - gint length; - - if (!GTK_IS_ENTRY (widget)) - return FALSE; - - buffer = gtk_entry_get_buffer (GTK_ENTRY (widget)); - length = gtk_entry_buffer_get_length (buffer); - return gtk_editable_get_position (GTK_EDITABLE (widget)) == length; -} - -static gboolean -pspp_sheet_view_event (GtkWidget *widget, - GdkEventKey *event, - PsppSheetView *tree_view) -{ - PsppSheetViewColumn *column; - GtkTreePath *path; - gboolean handled; - gboolean cancel; - guint keyval; - gint row; - - /* Intercept only key press events. - It would make sense to use "key-press-event" instead of "event", but - GtkEntry attaches its own signal handler to "key-press-event" that runs - before ours and overrides our desired behavior for GDK_Up and GDK_Down. - */ - if (event->type != GDK_KEY_PRESS) - return FALSE; - - keyval = event->keyval; - cancel = FALSE; - switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)) - { - case 0: - switch (event->keyval) - { - case GDK_Left: case GDK_KP_Left: - case GDK_Home: case GDK_KP_Home: - if (!is_all_selected (widget) && !is_at_left (widget)) - return FALSE; - break; - - case GDK_Right: case GDK_KP_Right: - case GDK_End: case GDK_KP_End: - if (!is_all_selected (widget) && !is_at_right (widget)) - return FALSE; - break; - - case GDK_Up: case GDK_KP_Up: - case GDK_Down: case GDK_KP_Down: - break; - - case GDK_Page_Up: case GDK_KP_Page_Up: - case GDK_Page_Down: case GDK_KP_Page_Down: - break; - - case GDK_Escape: - cancel = TRUE; - break; - - case GDK_Return: - keyval = GDK_Down; - break; - - case GDK_Tab: case GDK_KP_Tab: - case GDK_ISO_Left_Tab: - keyval = GDK_Tab; - break; - - default: - return FALSE; - } - break; - - case GDK_SHIFT_MASK: - switch (event->keyval) - { - case GDK_Tab: - case GDK_ISO_Left_Tab: - keyval = GDK_Tab; - break; - - default: - return FALSE; - } - break; - - case GDK_CONTROL_MASK: - switch (event->keyval) - { - case GDK_Left: case GDK_KP_Left: - if (!is_all_selected (widget) && !is_at_left (widget)) - return FALSE; - break; - - case GDK_Right: case GDK_KP_Right: - if (!is_all_selected (widget) && !is_at_right (widget)) - return FALSE; - break; - - case GDK_Up: case GDK_KP_Up: - case GDK_Down: case GDK_KP_Down: - break; - - default: - return FALSE; - } - break; - - default: - return FALSE; - } - - row = tree_view->priv->edited_row; - column = tree_view->priv->edited_column; - path = gtk_tree_path_new_from_indices (row, -1); - - pspp_sheet_view_stop_editing (tree_view, cancel); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - pspp_sheet_view_set_cursor (tree_view, path, column, FALSE); - gtk_tree_path_free (path); - - handled = gtk_binding_set_activate (edit_bindings, keyval, event->state, - G_OBJECT (tree_view)); - if (handled) - g_signal_stop_emission_by_name (widget, "event"); - - pspp_sheet_view_get_cursor (tree_view, &path, NULL); - pspp_sheet_view_start_editing (tree_view, path); - gtk_tree_path_free (path); - - return handled; -} - -static void -pspp_sheet_view_override_cell_keypresses (GtkWidget *widget, - gpointer data) -{ - PsppSheetView *sheet_view = data; - - g_signal_connect (widget, "event", - G_CALLBACK (pspp_sheet_view_event), - sheet_view); - - if (GTK_IS_CONTAINER (widget)) - gtk_container_foreach (GTK_CONTAINER (widget), - pspp_sheet_view_override_cell_keypresses, - data); -} - -static void -pspp_sheet_view_real_start_editing (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - GtkTreePath *path, - GtkCellEditable *cell_editable, - GdkRectangle *cell_area, - GdkEvent *event, - guint flags) -{ - PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection); - gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment); - gint row; - - g_return_if_fail (gtk_tree_path_get_depth (path) == 1); - - tree_view->priv->edited_column = column; - _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable)); - - row = gtk_tree_path_get_indices (path)[0]; - tree_view->priv->edited_row = row; - pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0); - cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment); - - pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); - pspp_sheet_selection_select_column (tree_view->priv->selection, column); - tree_view->priv->anchor_column = column; - - PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS); - - pspp_sheet_view_put (tree_view, - GTK_WIDGET (cell_editable), - path, - column); - - gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable), - (GdkEvent *)event); - - gtk_widget_grab_focus (GTK_WIDGET (cell_editable)); - g_signal_connect (cell_editable, "remove-widget", - G_CALLBACK (pspp_sheet_view_remove_widget), tree_view); - if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head && - GTK_IS_BUTTON (cell_editable)) - { - g_signal_connect (cell_editable, "button-press-event", - G_CALLBACK (pspp_sheet_view_editable_button_press_event), - tree_view); - g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node", - GINT_TO_POINTER (row)); - g_signal_connect (cell_editable, "clicked", - G_CALLBACK (pspp_sheet_view_editable_clicked), - tree_view); - } - - pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable), - tree_view); -} - -void -pspp_sheet_view_stop_editing (PsppSheetView *tree_view, - gboolean cancel_editing) -{ - PsppSheetViewColumn *column; - GtkCellRenderer *cell; - - if (tree_view->priv->edited_column == NULL) - return; - - /* - * This is very evil. We need to do this, because - * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed - * later on. If pspp_sheet_view_row_changed notices - * tree_view->priv->edited_column != NULL, it'll call - * pspp_sheet_view_stop_editing again. Bad things will happen then. - * - * Please read that again if you intend to modify anything here. - */ - - column = tree_view->priv->edited_column; - tree_view->priv->edited_column = NULL; - - cell = _pspp_sheet_view_column_get_edited_cell (column); - gtk_cell_renderer_stop_editing (cell, cancel_editing); - - if (!cancel_editing) - gtk_cell_editable_editing_done (column->editable_widget); - - tree_view->priv->edited_column = column; - - gtk_cell_editable_remove_widget (column->editable_widget); -} - - -/** - * pspp_sheet_view_set_hover_selection: - * @tree_view: a #PsppSheetView - * @hover: %TRUE to enable hover selection mode - * - * Enables of disables the hover selection mode of @tree_view. - * Hover selection makes the selected row follow the pointer. - * Currently, this works only for the selection modes - * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE. - * - * Since: 2.6 - **/ -void -pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view, - gboolean hover) -{ - hover = hover != FALSE; - - if (hover != tree_view->priv->hover_selection) - { - tree_view->priv->hover_selection = hover; - - g_object_notify (G_OBJECT (tree_view), "hover-selection"); - } -} - -/** - * pspp_sheet_view_get_hover_selection: - * @tree_view: a #PsppSheetView - * - * Returns whether hover selection mode is turned on for @tree_view. - * - * Return value: %TRUE if @tree_view is in hover selection mode - * - * Since: 2.6 - **/ -gboolean -pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view) -{ - return tree_view->priv->hover_selection; -} - -/** - * pspp_sheet_view_set_rubber_banding: - * @tree_view: a #PsppSheetView - * @enable: %TRUE to enable rubber banding - * - * Enables or disables rubber banding in @tree_view. If the selection mode is - * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber - * banding will allow the user to select multiple rows by dragging the mouse. - * - * Since: 2.10 - **/ -void -pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view, - gboolean enable) -{ - enable = enable != FALSE; - - if (enable != tree_view->priv->rubber_banding_enable) - { - tree_view->priv->rubber_banding_enable = enable; - - g_object_notify (G_OBJECT (tree_view), "rubber-banding"); - } -} - -/** - * pspp_sheet_view_get_rubber_banding: - * @tree_view: a #PsppSheetView - * - * Returns whether rubber banding is turned on for @tree_view. If the - * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or - * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to - * select multiple rows by dragging the mouse. - * - * Return value: %TRUE if rubber banding in @tree_view is enabled. - * - * Since: 2.10 - **/ -gboolean -pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view) -{ - return tree_view->priv->rubber_banding_enable; -} - -/** - * pspp_sheet_view_is_rubber_banding_active: - * @tree_view: a #PsppSheetView - * - * Returns whether a rubber banding operation is currently being done - * in @tree_view. - * - * Return value: %TRUE if a rubber banding operation is currently being - * done in @tree_view. - * - * Since: 2.12 - **/ -gboolean -pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - - if (tree_view->priv->rubber_banding_enable - && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE) - return TRUE; - - return FALSE; -} - -static void -pspp_sheet_view_grab_notify (GtkWidget *widget, - gboolean was_grabbed) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - - tree_view->priv->in_grab = !was_grabbed; - - if (!was_grabbed) - { - tree_view->priv->pressed_button = -1; - - if (tree_view->priv->rubber_band_status) - pspp_sheet_view_stop_rubber_band (tree_view); - } -} - -static void -pspp_sheet_view_state_changed (GtkWidget *widget, - GtkStateType previous_state) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - - if (gtk_widget_get_realized (widget)) - { - GtkStyle *style = gtk_widget_get_style (widget); - gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]); - } - - gtk_widget_queue_draw (widget); -} - -/** - * pspp_sheet_view_get_grid_lines: - * @tree_view: a #PsppSheetView - * - * Returns which grid lines are enabled in @tree_view. - * - * Return value: a #PsppSheetViewGridLines value indicating which grid lines - * are enabled. - * - * Since: 2.10 - */ -PsppSheetViewGridLines -pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0); - - return tree_view->priv->grid_lines; -} - -/** - * pspp_sheet_view_set_grid_lines: - * @tree_view: a #PsppSheetView - * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to - * enable. - * - * Sets which grid lines to draw in @tree_view. - * - * Since: 2.10 - */ -void -pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view, - PsppSheetViewGridLines grid_lines) -{ - PsppSheetViewPrivate *priv; - PsppSheetViewGridLines old_grid_lines; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - priv = tree_view->priv; - - old_grid_lines = priv->grid_lines; - priv->grid_lines = grid_lines; - - if (old_grid_lines != grid_lines) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - g_object_notify (G_OBJECT (tree_view), "enable-grid-lines"); - } -} - -/** - * pspp_sheet_view_get_special_cells: - * @tree_view: a #PsppSheetView - * - * Returns which grid lines are enabled in @tree_view. - * - * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in - * the sheet view contain special cells. - */ -PsppSheetViewSpecialCells -pspp_sheet_view_get_special_cells (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0); - - return tree_view->priv->special_cells; -} - -/** - * pspp_sheet_view_set_special_cells: - * @tree_view: a #PsppSheetView - * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in - * the sheet view contain special cells. - * - * Sets whether rows in the sheet view contain special cells, controlling the - * rendering of row selections. - */ -void -pspp_sheet_view_set_special_cells (PsppSheetView *tree_view, - PsppSheetViewSpecialCells special_cells) -{ - PsppSheetViewPrivate *priv; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - priv = tree_view->priv; - - if (priv->special_cells != special_cells) - { - priv->special_cells = special_cells; - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - g_object_notify (G_OBJECT (tree_view), "special-cells"); - } -} - -int -pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view) -{ - /* XXX (re)calculate fixed_height if necessary */ - return tree_view->priv->fixed_height; -} - -void -pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view, - int fixed_height) -{ - g_return_if_fail (fixed_height > 0); - - if (tree_view->priv->fixed_height != fixed_height) - { - tree_view->priv->fixed_height = fixed_height; - g_object_notify (G_OBJECT (tree_view), "fixed-height"); - } - if (!tree_view->priv->fixed_height_set) - { - tree_view->priv->fixed_height_set = TRUE; - g_object_notify (G_OBJECT (tree_view), "fixed-height-set"); - } -} - -/** - * pspp_sheet_view_set_tooltip_row: - * @tree_view: a #PsppSheetView - * @tooltip: a #GtkTooltip - * @path: a #GtkTreePath - * - * Sets the tip area of @tooltip to be the area covered by the row at @path. - * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative. - * See also gtk_tooltip_set_tip_area(). - * - * Since: 2.12 - */ -void -pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); - - pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL); -} - -/** - * pspp_sheet_view_set_tooltip_cell: - * @tree_view: a #PsppSheetView - * @tooltip: a #GtkTooltip - * @path: (allow-none): a #GtkTreePath or %NULL - * @column: (allow-none): a #PsppSheetViewColumn or %NULL - * @cell: (allow-none): a #GtkCellRenderer or %NULL - * - * Sets the tip area of @tooltip to the area @path, @column and @cell have - * in common. For example if @path is %NULL and @column is set, the tip - * area will be set to the full area covered by @column. See also - * gtk_tooltip_set_tip_area(). - * - * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative. - * - * Since: 2.12 - */ -void -pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path, - PsppSheetViewColumn *column, - GtkCellRenderer *cell) -{ - GdkRectangle rect; - - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); - g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column)); - g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); - - /* Determine x values. */ - if (column && cell) - { - GdkRectangle tmp; - gint start, width; - - pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp); - pspp_sheet_view_column_cell_get_position (column, cell, &start, &width); - - pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view, - tmp.x + start, 0, - &rect.x, NULL); - rect.width = width; - } - else if (column) - { - GdkRectangle tmp; - - pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp); - pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view, - tmp.x, 0, - &rect.x, NULL); - rect.width = tmp.width; - } - else - { - GtkAllocation allocation; - gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation); - rect.x = 0; - rect.width = allocation.width; - } - - /* Determine y values. */ - if (path) - { - GdkRectangle tmp; - - pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp); - pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view, - 0, tmp.y, - NULL, &rect.y); - rect.height = tmp.height; - } - else - { - rect.y = 0; - rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment); - } - - gtk_tooltip_set_tip_area (tooltip, &rect); -} - -/** - * pspp_sheet_view_get_tooltip_context: - * @tree_view: a #PsppSheetView - * @x: the x coordinate (relative to widget coordinates) - * @y: the y coordinate (relative to widget coordinates) - * @keyboard_tip: whether this is a keyboard tooltip or not - * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL - * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL - * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL - * - * This function is supposed to be used in a #GtkWidget::query-tooltip - * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values - * which are received in the signal handler, should be passed to this - * function without modification. - * - * The return value indicates whether there is a tree view row at the given - * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard - * tooltips the row returned will be the cursor row. When %TRUE, then any of - * @model, @path and @iter which have been provided will be set to point to - * that row and the corresponding model. @x and @y will always be converted - * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE. - * - * Return value: whether or not the given tooltip context points to a row. - * - * Since: 2.12 - */ -gboolean -pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view, - gint *x, - gint *y, - gboolean keyboard_tip, - GtkTreeModel **model, - GtkTreePath **path, - GtkTreeIter *iter) -{ - GtkTreePath *tmppath = NULL; - - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE); - g_return_val_if_fail (x != NULL, FALSE); - g_return_val_if_fail (y != NULL, FALSE); - - if (keyboard_tip) - { - pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL); - - if (!tmppath) - return FALSE; - } - else - { - pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y, - x, y); - - if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y, - &tmppath, NULL, NULL, NULL)) - return FALSE; - } - - if (model) - *model = pspp_sheet_view_get_model (tree_view); - - if (iter) - gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view), - iter, tmppath); - - if (path) - *path = tmppath; - else - gtk_tree_path_free (tmppath); - - return TRUE; -} - -static gboolean -pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget, - gint x, - gint y, - gboolean keyboard_tip, - GtkTooltip *tooltip, - gpointer data) -{ - GValue value = { 0, }; - GValue transformed = { 0, }; - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeModel *model; - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - - if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget), - &x, &y, - keyboard_tip, - &model, &path, &iter)) - return FALSE; - - gtk_tree_model_get_value (model, &iter, - tree_view->priv->tooltip_column, &value); - - g_value_init (&transformed, G_TYPE_STRING); - - if (!g_value_transform (&value, &transformed)) - { - g_value_unset (&value); - gtk_tree_path_free (path); - - return FALSE; - } - - g_value_unset (&value); - - if (!g_value_get_string (&transformed)) - { - g_value_unset (&transformed); - gtk_tree_path_free (path); - - return FALSE; - } - - gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed)); - pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path); - - gtk_tree_path_free (path); - g_value_unset (&transformed); - - return TRUE; -} - -/** - * pspp_sheet_view_set_tooltip_column: - * @tree_view: a #PsppSheetView - * @column: an integer, which is a valid column number for @tree_view's model - * - * If you only plan to have simple (text-only) tooltips on full rows, you - * can use this function to have #PsppSheetView handle these automatically - * for you. @column should be set to the column in @tree_view's model - * containing the tooltip texts, or -1 to disable this feature. - * - * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and - * @tree_view will connect a #GtkWidget::query-tooltip signal handler. - * - * Note that the signal handler sets the text with gtk_tooltip_set_markup(), - * so &, <, etc have to be escaped in the text. - * - * Since: 2.12 - */ -void -pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view, - gint column) -{ - g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); - - if (column == tree_view->priv->tooltip_column) - return; - - if (column == -1) - { - g_signal_handlers_disconnect_by_func (tree_view, - pspp_sheet_view_set_tooltip_query_cb, - NULL); - gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE); - } - else - { - if (tree_view->priv->tooltip_column == -1) - { - g_signal_connect (tree_view, "query-tooltip", - G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL); - gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE); - } - } - - tree_view->priv->tooltip_column = column; - g_object_notify (G_OBJECT (tree_view), "tooltip-column"); -} - -/** - * pspp_sheet_view_get_tooltip_column: - * @tree_view: a #PsppSheetView - * - * Returns the column of @tree_view's model which is being used for - * displaying tooltips on @tree_view's rows. - * - * Return value: the index of the tooltip column that is currently being - * used, or -1 if this is disabled. - * - * Since: 2.12 - */ -gint -pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view) -{ - g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0); - - return tree_view->priv->tooltip_column; -} - -gboolean -_gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint, - GValue *return_accu, - const GValue *handler_return, - gpointer dummy) -{ - gboolean continue_emission; - gboolean signal_handled; - - signal_handled = g_value_get_boolean (handler_return); - g_value_set_boolean (return_accu, signal_handled); - continue_emission = !signal_handled; - - return continue_emission; -} - - -GType -pspp_sheet_view_grid_lines_get_type (void) -{ - static GType etype = 0; - if (G_UNLIKELY(etype == 0)) { - static const GEnumValue values[] = { - { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" }, - { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" }, - { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" }, - { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" }, - { 0, NULL, NULL } - }; - etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values); - } - return etype; -} - -GType -pspp_sheet_view_special_cells_get_type (void) -{ - static GType etype = 0; - if (G_UNLIKELY(etype == 0)) { - static const GEnumValue values[] = { - { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" }, - { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" }, - { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" }, - { 0, NULL, NULL } - }; - etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values); - } - return etype; -} diff --git a/src/ui/gui/pspp-sheet-view.h b/src/ui/gui/pspp-sheet-view.h deleted file mode 100644 index 92d7315b8b..0000000000 --- a/src/ui/gui/pspp-sheet-view.h +++ /dev/null @@ -1,432 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -/* gtktreeview.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __PSPP_SHEET_VIEW_H__ -#define __PSPP_SHEET_VIEW_H__ - -#include -#include "ui/gui/pspp-sheet-view-column.h" - -G_BEGIN_DECLS - - -typedef enum -{ - PSPP_SHEET_VIEW_GRID_LINES_NONE, - PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, - PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, - PSPP_SHEET_VIEW_GRID_LINES_BOTH -} PsppSheetViewGridLines; - -GType pspp_sheet_view_grid_lines_get_type (void) G_GNUC_CONST; -#define PSPP_TYPE_SHEET_VIEW_GRID_LINES (pspp_sheet_view_grid_lines_get_type ()) - -/* A "special cell" is a cell that is editable or activatable. When a row that - * contains a special cell is selected, the cursor is drawn around a single - * cell; when other rows are selected, the cursor is drawn around the entire - * row. - * - * With the default of "detect", whether a given row contains a special cell is - * detected automatically. This is the best choice most of the time. For - * sheet views that contain more than 100 columns, an explicit "yes" or "no" - * improves performance. */ -typedef enum -{ - PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, - PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, - PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, -} PsppSheetViewSpecialCells; - -GType pspp_sheet_view_special_cells_get_type (void) G_GNUC_CONST; -#define PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS (pspp_sheet_view_special_cells_get_type ()) - -typedef enum -{ - /* drop before/after this row */ - PSPP_SHEET_VIEW_DROP_BEFORE, - PSPP_SHEET_VIEW_DROP_AFTER, - /* drop as a child of this row (with fallback to before or after - * if into is not possible) - */ - PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE, - PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER -} PsppSheetViewDropPosition; - -typedef enum -{ - PSPP_SHEET_SELECT_MODE_TOGGLE = 1 << 0, - PSPP_SHEET_SELECT_MODE_EXTEND = 1 << 1 -} -PsppSheetSelectMode; - -#define PSPP_TYPE_SHEET_VIEW (pspp_sheet_view_get_type ()) -#define PSPP_SHEET_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPP_TYPE_SHEET_VIEW, PsppSheetView)) -#define PSPP_SHEET_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PSPP_TYPE_SHEET_VIEW, PsppSheetViewClass)) -#define PSPP_IS_SHEET_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPP_TYPE_SHEET_VIEW)) -#define PSPP_IS_SHEET_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPP_TYPE_SHEET_VIEW)) -#define PSPP_SHEET_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PSPP_TYPE_SHEET_VIEW, PsppSheetViewClass)) - -typedef struct _PsppSheetView PsppSheetView; -typedef struct _PsppSheetViewClass PsppSheetViewClass; -typedef struct _PsppSheetViewPrivate PsppSheetViewPrivate; -typedef struct _PsppSheetSelection PsppSheetSelection; -typedef struct _PsppSheetSelectionClass PsppSheetSelectionClass; - -struct _PsppSheetView -{ - GtkContainer parent; - - PsppSheetViewPrivate *priv; - - gboolean dispose_has_run ; -}; - -struct _PsppSheetViewClass -{ - GtkContainerClass parent_class; - - void (* set_scroll_adjustments) (PsppSheetView *tree_view, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); - void (* row_activated) (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column); - void (* columns_changed) (PsppSheetView *tree_view); - void (* cursor_changed) (PsppSheetView *tree_view); - - /* Key Binding signals */ - gboolean (* move_cursor) (PsppSheetView *tree_view, - GtkMovementStep step, - gint count); - gboolean (* select_all) (PsppSheetView *tree_view); - gboolean (* unselect_all) (PsppSheetView *tree_view); - gboolean (* select_cursor_row) (PsppSheetView *tree_view, - gboolean start_editing, - PsppSheetSelectMode mode); - gboolean (* toggle_cursor_row) (PsppSheetView *tree_view); - gboolean (* select_cursor_parent) (PsppSheetView *tree_view); - gboolean (* start_interactive_search) (PsppSheetView *tree_view); - - /* Padding for future expansion */ - void (*_gtk_reserved0) (void); - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); -}; - - -typedef gboolean (* PsppSheetViewColumnDropFunc) (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - PsppSheetViewColumn *prev_column, - PsppSheetViewColumn *next_column, - gpointer data); -typedef void (* PsppSheetViewMappingFunc) (PsppSheetView *tree_view, - GtkTreePath *path, - gpointer user_data); -typedef gboolean (*PsppSheetViewSearchEqualFunc) (GtkTreeModel *model, - gint column, - const gchar *key, - GtkTreeIter *iter, - gpointer search_data); -typedef void (*PsppSheetViewSearchPositionFunc) (PsppSheetView *tree_view, - GtkWidget *search_dialog, - gpointer user_data); - - -/* Creators */ -GType pspp_sheet_view_get_type (void) G_GNUC_CONST; -GtkWidget *pspp_sheet_view_new (void); -GtkWidget *pspp_sheet_view_new_with_model (GtkTreeModel *model); - -/* Accessors */ -GtkTreeModel *pspp_sheet_view_get_model (PsppSheetView *tree_view); -void pspp_sheet_view_set_model (PsppSheetView *tree_view, - GtkTreeModel *model); -PsppSheetSelection *pspp_sheet_view_get_selection (PsppSheetView *tree_view); -GtkAdjustment *pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view); -void pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment); -GtkAdjustment *pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view); -void pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view, - GtkAdjustment *adjustment); -gboolean pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view); -void pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view, - gboolean headers_visible); -void pspp_sheet_view_columns_autosize (PsppSheetView *tree_view); -gboolean pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view); -void pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view, - gboolean setting); -void pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view, - gboolean setting); -gboolean pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view); - -/* Column funtions */ -gint pspp_sheet_view_append_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column); -gint pspp_sheet_view_remove_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column); -gint pspp_sheet_view_insert_column (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - gint position); -gint pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view, - gint position, - const gchar *title, - GtkCellRenderer *cell, - ...) G_GNUC_NULL_TERMINATED; -gint pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view, - gint position, - const gchar *title, - GtkCellRenderer *cell, - PsppSheetCellDataFunc func, - gpointer data, - GDestroyNotify dnotify); -PsppSheetViewColumn *pspp_sheet_view_get_column (PsppSheetView *tree_view, - gint n); -GList *pspp_sheet_view_get_columns (PsppSheetView *tree_view); -void pspp_sheet_view_move_column_after (PsppSheetView *tree_view, - PsppSheetViewColumn *column, - PsppSheetViewColumn *base_column); -void pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view, - PsppSheetViewColumnDropFunc func, - gpointer user_data, - GDestroyNotify destroy); - -/* Actions */ -void pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view, - gint tree_x, - gint tree_y); -void pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column, - gboolean use_align, - gfloat row_align, - gfloat col_align); -void pspp_sheet_view_row_activated (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column); -void pspp_sheet_view_set_reorderable (PsppSheetView *tree_view, - gboolean reorderable); -gboolean pspp_sheet_view_get_reorderable (PsppSheetView *tree_view); -void pspp_sheet_view_set_cursor (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *focus_column, - gboolean start_editing); -void pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *focus_column, - GtkCellRenderer *focus_cell, - gboolean start_editing); -void pspp_sheet_view_get_cursor (PsppSheetView *tree_view, - GtkTreePath **path, - PsppSheetViewColumn **focus_column); - - -/* Layout information */ -GdkWindow *pspp_sheet_view_get_bin_window (PsppSheetView *tree_view); -gboolean pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view, - gint x, - gint y, - GtkTreePath **path, - PsppSheetViewColumn **column, - gint *cell_x, - gint *cell_y); -void pspp_sheet_view_get_cell_area (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column, - GdkRectangle *rect); -void pspp_sheet_view_get_background_area (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewColumn *column, - GdkRectangle *rect); -void pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view, - GdkRectangle *visible_rect); - -#ifndef GTK_DISABLE_DEPRECATED -void pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view, - gint wx, - gint wy, - gint *tx, - gint *ty); -void pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view, - gint tx, - gint ty, - gint *wx, - gint *wy); -#endif /* !GTK_DISABLE_DEPRECATED */ -gboolean pspp_sheet_view_get_visible_range (PsppSheetView *tree_view, - GtkTreePath **start_path, - GtkTreePath **end_path); - -/* Drag-and-Drop support */ -void pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view, - GdkModifierType start_button_mask, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions); -void pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions); -void pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view); -void pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view); - - -/* These are useful to implement your own custom stuff. */ -void pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view, - GtkTreePath *path, - PsppSheetViewDropPosition pos); -void pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view, - GtkTreePath **path, - PsppSheetViewDropPosition *pos); -gboolean pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view, - gint drag_x, - gint drag_y, - GtkTreePath **path, - PsppSheetViewDropPosition *pos); - -/* Interactive search */ -void pspp_sheet_view_set_enable_search (PsppSheetView *tree_view, - gboolean enable_search); -gboolean pspp_sheet_view_get_enable_search (PsppSheetView *tree_view); -gint pspp_sheet_view_get_search_column (PsppSheetView *tree_view); -void pspp_sheet_view_set_search_column (PsppSheetView *tree_view, - gint column); -PsppSheetViewSearchEqualFunc pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view); -void pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view, - PsppSheetViewSearchEqualFunc search_equal_func, - gpointer search_user_data, - GDestroyNotify search_destroy); - -GtkEntry *pspp_sheet_view_get_search_entry (PsppSheetView *tree_view); -void pspp_sheet_view_set_search_entry (PsppSheetView *tree_view, - GtkEntry *entry); -PsppSheetViewSearchPositionFunc pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view); -void pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view, - PsppSheetViewSearchPositionFunc func, - gpointer data, - GDestroyNotify destroy); - -/* Convert between the different coordinate systems */ -void pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view, - gint wx, - gint wy, - gint *tx, - gint *ty); -void pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view, - gint tx, - gint ty, - gint *wx, - gint *wy); -void pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view, - gint wx, - gint wy, - gint *bx, - gint *by); -void pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view, - gint bx, - gint by, - gint *wx, - gint *wy); -void pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view, - gint tx, - gint ty, - gint *bx, - gint *by); -void pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view, - gint bx, - gint by, - gint *tx, - gint *ty); - -/* This function should really never be used. It is just for use by ATK. - */ -typedef void (* PsppSheetDestroyCountFunc) (PsppSheetView *tree_view, - GtkTreePath *path, - gint children, - gpointer user_data); -void pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view, - PsppSheetDestroyCountFunc func, - gpointer data, - GDestroyNotify destroy); - -void pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view, - gboolean hover); -gboolean pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view); -void pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view, - gboolean enable); -gboolean pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view); - -gboolean pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view); - -PsppSheetViewGridLines pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view); -void pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view, - PsppSheetViewGridLines grid_lines); - -PsppSheetViewSpecialCells pspp_sheet_view_get_special_cells (PsppSheetView *tree_view); -void pspp_sheet_view_set_special_cells (PsppSheetView *tree_view, - PsppSheetViewSpecialCells); - -int pspp_sheet_view_get_fixed_height (const PsppSheetView *); -void pspp_sheet_view_set_fixed_height (PsppSheetView *, - int fixed_height); - -/* Convenience functions for setting tooltips */ -void pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path); -void pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view, - GtkTooltip *tooltip, - GtkTreePath *path, - PsppSheetViewColumn *column, - GtkCellRenderer *cell); -gboolean pspp_sheet_view_get_tooltip_context(PsppSheetView *tree_view, - gint *x, - gint *y, - gboolean keyboard_tip, - GtkTreeModel **model, - GtkTreePath **path, - GtkTreeIter *iter); -void pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view, - gint column); -gint pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view); - -void pspp_sheet_view_stop_editing (PsppSheetView *tree_view, - gboolean cancel_editing); - -G_END_DECLS - - -#endif /* __PSPP_SHEET_VIEW_H__ */ diff --git a/src/ui/gui/pspp-widget-facade.c b/src/ui/gui/pspp-widget-facade.c deleted file mode 100644 index a95040bdf7..0000000000 --- a/src/ui/gui/pspp-widget-facade.c +++ /dev/null @@ -1,357 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2014 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#include - -#include "pspp-widget-facade.h" - -#include -#include -#include -#include - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -static void -inset_rectangle (const GdkRectangle *src, - const GtkBorder *inset, - GdkRectangle *dst) -{ - dst->x = src->x + inset->left; - dst->y = src->y + inset->top; - dst->width = MAX (1, src->width - inset->left - inset->right); - dst->height = MAX (1, src->height - inset->top - inset->bottom); -} - -static void -thicken_border (gint x, gint y, GtkBorder *border) -{ - border->left += x; - border->right += x; - border->top += y; - border->bottom += y; -} - -GtkStyle * -facade_get_style (GtkWidget *base, - GType type1, - ...) -{ - GString path, class_path; - GType final_type; - GtkStyle *style; - va_list args; - GType type; - - gtk_widget_path (base, NULL, &path.str, NULL); - path.len = path.allocated_len = strlen (path.str); - - gtk_widget_class_path (base, NULL, &class_path.str, NULL); - class_path.len = class_path.allocated_len = strlen (class_path.str); - - va_start (args, type1); - for (type = final_type = type1; type != 0; type = va_arg (args, GType)) - { - const gchar *type_name = g_type_name (type); - g_string_append_printf (&path, ".%s", type_name); - g_string_append_printf (&class_path, ".%s", type_name); - final_type = type; - } - va_end (args); - - style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (base), - path.str, class_path.str, final_type); - - free (path.str); - free (class_path.str); - - return style; -} - -void -facade_hbox_get_base_size_request (gint border_width, - gint spacing, - gint n_children, - GtkRequisition *request) -{ - request->width = border_width * 2; - if (n_children > 1) - request->width += spacing * (n_children - 1); - - request->height = border_width * 2; -} - -void -facade_hbox_add_child_size_request (gint hbox_border_width, - const GtkRequisition *child_request, - gint child_padding, - GtkRequisition *request) -{ - request->width += child_request->width + child_padding * 2; - request->height = MAX (request->height, - hbox_border_width * 2 + child_request->height); -} - -void -facade_arrow_get_size_request (gint xpad, - gint ypad, - GtkRequisition *request) -{ -#define MIN_ARROW_SIZE 15 - request->width = MIN_ARROW_SIZE + xpad * 2; - request->height = MIN_ARROW_SIZE + ypad * 2; -} - -void -facade_alignment_get_size_request (gint border_width, - gint padding_left, - gint padding_right, - gint padding_top, - gint padding_bottom, - const GtkRequisition *child_request, - GtkRequisition *request) -{ - request->width = (border_width * 2 + padding_left + padding_right - + child_request->width); - request->height = (border_width * 2 + padding_top + padding_bottom - + child_request->height); -} - -void -facade_label_get_size_request (gint xpad, - gint ypad, - GtkWidget *base, - const char *text, - GtkRequisition *request) -{ - PangoLayout *layout; - - layout = facade_label_get_layout (base, text); - facade_label_get_size_request_from_layout (xpad, ypad, layout, request); - g_object_unref (layout); -} - -void -facade_label_get_size_request_from_layout (gint xpad, - gint ypad, - PangoLayout *layout, - GtkRequisition *request) -{ - PangoRectangle logical_rect; - - pango_layout_get_extents (layout, NULL, &logical_rect); - request->width = xpad * 2 + PANGO_PIXELS (logical_rect.width); - request->height = ypad * 2 + PANGO_PIXELS (logical_rect.height); -} - -PangoLayout * -facade_label_get_layout (GtkWidget *base, - const char *text) -{ - PangoAlignment alignment; - PangoLayout *layout; - gboolean rtl; - - rtl = gtk_widget_get_direction (base) == GTK_TEXT_DIR_RTL; - alignment = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; - - layout = gtk_widget_create_pango_layout (base, text); - pango_layout_set_alignment (layout, alignment); - - return layout; -} - -static void -facade_button_get_inner_border (GtkStyle *button_style, - GtkBorder *inner_border) -{ - GtkBorder *tmp_border; - - gtk_style_get (button_style, GTK_TYPE_BUTTON, - "inner-border", &tmp_border, - NULL); - - if (tmp_border) - { - *inner_border = *tmp_border; - gtk_border_free (tmp_border); - } - else - { - static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; - *inner_border = default_inner_border; - } -} - -void -facade_button_get_size_request (gint border_width, - GtkWidget *base, - GtkStyle *button_style, - const GtkRequisition *child_request, - GtkRequisition *request) -{ - GtkBorder inner_border; - gint common_width; - gint focus_width; - gint focus_pad; - - gtk_style_get (button_style, GTK_TYPE_BUTTON, - "focus-line-width", &focus_width, - "focus-padding", &focus_pad, - NULL); - facade_button_get_inner_border (button_style, &inner_border); - - common_width = 2 * (border_width + focus_width + focus_pad); - request->width = (common_width - + 2 * button_style->xthickness - + inner_border.left + inner_border.right - + child_request->width); - request->height = (common_width - + 2 * button_style->ythickness - + inner_border.top + inner_border.bottom - + child_request->height); -} - -void -facade_button_get_focus_inset (gint border_width, - GtkWidget *base, - GtkStyle *button_style, - GtkBorder *focus_inset) -{ - facade_button_get_inner_border (button_style, focus_inset); - thicken_border (border_width + button_style->xthickness, - border_width + button_style->ythickness, - focus_inset); -} - -static void -facade_button_get_label_inset (gint border_width, - GtkWidget *base, - GtkStyle *button_style, - GtkBorder *label_inset) -{ - gint focus_width; - gint focus_pad; - - facade_button_get_focus_inset (border_width, base, button_style, - label_inset); - - gtk_style_get (button_style, GTK_TYPE_BUTTON, - "focus-line-width", &focus_width, - "focus-padding", &focus_pad, - NULL); - thicken_border (focus_width + focus_pad, - focus_width + focus_pad, - label_inset); -} - -static void -get_layout_location (GtkWidget *base, - const GdkRectangle *label_area, - PangoLayout *layout, - gint xpad, - gint ypad, - gfloat xalign, - gfloat yalign, - gint *x, - gint *y) -{ - PangoRectangle logical; - GtkRequisition req; - - if (gtk_widget_get_direction (base) == GTK_TEXT_DIR_RTL) - xalign = 1.0 - xalign; - - pango_layout_get_pixel_extents (layout, NULL, &logical); - - facade_label_get_size_request_from_layout (xpad, ypad, layout, &req); - - *x = floor (label_area->x + xpad + xalign * (label_area->width - req.width)); - - if (gtk_widget_get_direction (base) == GTK_TEXT_DIR_LTR) - *x = MAX (*x, label_area->x + xpad); - else - *x = MIN (*x, label_area->x + label_area->width - xpad); - *x -= logical.x; - - /* bgo#315462 - For single-line labels, *do* align the requisition with - * respect to the allocation, even if we are under-allocated. For multi-line - * labels, always show the top of the text when they are under-allocated. - * The rationale is this: - * - * - Single-line labels appear in GtkButtons, and it is very easy to get them - * to be smaller than their requisition. The button may clip the label, - * but the label will still be able to show most of itself and the focus - * rectangle. Also, it is fairly easy to read a single line of clipped - * text. - * - * - Multi-line labels should not be clipped to showing "something in the - * middle". You want to read the first line, at least, to get some - * context. - */ - if (pango_layout_get_line_count (layout) == 1) - *y = floor (label_area->y + ypad - + (label_area->height - req.height) * yalign); - else - *y = floor (label_area->y + ypad - + MAX (((label_area->height - req.height) * yalign), - 0)); -} - -void -facade_button_render (GtkWidget *base, - cairo_t *cr, - const GdkRectangle *button_area, - gint border_width, - GtkStyle *button_style, - GtkStateType state_type, - - GtkStyle *label_style, - const gchar *label, - gint xpad, - gint ypad, - gfloat xalign, - gfloat yalign) -{ - GdkRectangle label_area; - PangoLayout *layout; - GtkBorder inset; - gint x, y; - - /* Paint the button. */ - gtk_paint_box (button_style, cr, - state_type, - GTK_SHADOW_OUT, base, "button", - button_area->x + border_width, - button_area->y + border_width, - button_area->width - border_width * 2, - button_area->height - border_width * 2); - - /* Figure out where the label should go. */ - facade_button_get_label_inset (border_width, base, button_style, &inset); - inset_rectangle (button_area, &inset, &label_area); - - /* Paint the label. */ - layout = facade_label_get_layout (base, label); - get_layout_location (base, &label_area, layout, xpad, ypad, xalign, yalign, - &x, &y); - - gtk_paint_layout (label_style, cr, state_type, FALSE, - base, "label", x, y, layout); - - g_object_unref (layout); -} - diff --git a/src/ui/gui/pspp-widget-facade.h b/src/ui/gui/pspp-widget-facade.h deleted file mode 100644 index 6705185f7f..0000000000 --- a/src/ui/gui/pspp-widget-facade.h +++ /dev/null @@ -1,85 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#ifndef FACADE_WIDGET_FACADE_H -#define FACADE_WIDGET_FACADE_H 1 - -#include - -G_BEGIN_DECLS - -GtkStyle *facade_get_style (GtkWidget *base, GType, ...); - -void facade_hbox_get_base_size_request (gint border_width, - gint spacing, - gint n_children, - GtkRequisition *); -void facade_hbox_add_child_size_request (gint hbox_border_width, - const GtkRequisition *child_request, - gint child_padding, - GtkRequisition *); - -void facade_arrow_get_size_request (gint xpad, - gint ypad, - GtkRequisition *); - - -void facade_alignment_get_size_request (gint border_width, - gint padding_left, - gint padding_right, - gint padding_top, - gint padding_bottom, - const GtkRequisition *child_request, - GtkRequisition *); - -void facade_label_get_size_request (gint xpad, - gint ypad, - GtkWidget *base, - const char *text, - GtkRequisition *); -void facade_label_get_size_request_from_layout (gint xpad, - gint ypad, - PangoLayout *, - GtkRequisition *); -PangoLayout *facade_label_get_layout (GtkWidget *base, - const char *text); - -void facade_button_get_size_request (gint border_width, - GtkWidget *base, - GtkStyle *button_style, - const GtkRequisition *child_request, - GtkRequisition *); -void facade_button_render (GtkWidget *base, - cairo_t *cr, - const GdkRectangle *button_area, - gint border_width, - GtkStyle *button_style, - GtkStateType state_type, - - GtkStyle *label_style, - const gchar *label, - gint xpad, - gint ypad, - gfloat xalign, - gfloat yalign); -void facade_button_get_focus_inset (gint border_width, - GtkWidget *base, - GtkStyle *button_style, - GtkBorder *focus_inset); - -G_END_DECLS - -#endif /* FACADE_WIDGET_FACADE_H */ diff --git a/src/ui/gui/psppire-button-editable.c b/src/ui/gui/psppire-button-editable.c deleted file mode 100644 index f77e3ce217..0000000000 --- a/src/ui/gui/psppire-button-editable.c +++ /dev/null @@ -1,185 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2016 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#include - -#include "ui/gui/psppire-button-editable.h" - -#include -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid -#define P_(STRING) STRING - -/* GtkCellEditable interface. */ -static void gtk_cell_editable_interface_init (GtkCellEditableIface *iface); -static void button_editable_editing_done (GtkCellEditable *cell_editable); -static void button_editable_remove_widget (GtkCellEditable *cell_editable); -static void button_editable_start_editing (GtkCellEditable *cell_editable, - GdkEvent *event); - -G_DEFINE_TYPE_EXTENDED (PsppireButtonEditable, - psppire_button_editable, - GTK_TYPE_BUTTON, - 0, - G_IMPLEMENT_INTERFACE ( - GTK_TYPE_CELL_EDITABLE, - gtk_cell_editable_interface_init)); - -enum - { - PROP_0, - PROP_PATH, - PROP_EDITING_CANCELED - }; - -static void -psppire_button_editable_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireButtonEditable *obj = PSPPIRE_BUTTON_EDITABLE (object); - - switch (prop_id) - { - case PROP_PATH: - g_free (obj->path); - obj->path = g_value_dup_string (value); - break; - - case PROP_EDITING_CANCELED: - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_button_editable_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireButtonEditable *obj = PSPPIRE_BUTTON_EDITABLE (object); - - switch (prop_id) - { - case PROP_PATH: - g_value_set_string (value, obj->path); - break; - - case PROP_EDITING_CANCELED: - g_value_set_boolean (value, FALSE); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_button_editable_finalize (GObject *gobject) -{ - PsppireButtonEditable *obj = PSPPIRE_BUTTON_EDITABLE (gobject); - - g_free (obj->path); - - G_OBJECT_CLASS (psppire_button_editable_parent_class)->finalize (gobject); -} - -static gboolean -psppire_button_editable_button_release (GtkWidget *widget, - GdkEventButton *event) -{ - if (event->button == 1) - { - g_signal_emit_by_name (widget, "released", event, NULL); - } - - return TRUE; -} - -static void -psppire_button_editable_class_init (PsppireButtonEditableClass *class) -{ - GObjectClass *gobject_class; - GtkWidgetClass *widget_class; - - gobject_class = G_OBJECT_CLASS (class); - widget_class = GTK_WIDGET_CLASS (class); - - gobject_class->set_property = psppire_button_editable_set_property; - gobject_class->get_property = psppire_button_editable_get_property; - gobject_class->finalize = psppire_button_editable_finalize; - - widget_class->button_release_event = psppire_button_editable_button_release; - - g_object_class_install_property (gobject_class, - PROP_PATH, - g_param_spec_string ("path", - P_("TreeView path"), - P_("The path to the row in the GtkTreeView, as a string"), - "", - G_PARAM_READWRITE)); - - g_object_class_override_property (gobject_class, - PROP_EDITING_CANCELED, - "editing-canceled"); -} - -static void -psppire_button_editable_init (PsppireButtonEditable *obj) -{ - obj->path = g_strdup (""); -} - -PsppireButtonEditable * -psppire_button_editable_new (void) -{ - return PSPPIRE_BUTTON_EDITABLE (g_object_new (PSPPIRE_TYPE_BUTTON_EDITABLE, NULL)); -} - - -/* GtkCellEditable interface. */ - -static void -gtk_cell_editable_interface_init (GtkCellEditableIface *iface) -{ - g_return_if_fail (iface != NULL); - - iface->editing_done = button_editable_editing_done; - iface->remove_widget = button_editable_remove_widget; - iface->start_editing = button_editable_start_editing; -} - -static void -button_editable_editing_done (GtkCellEditable *cell_editable) -{ -} - -static void -button_editable_remove_widget (GtkCellEditable *cell_editable) -{ -} - -static void -button_editable_start_editing (GtkCellEditable *cell_editable, - GdkEvent *event) -{ -} diff --git a/src/ui/gui/psppire-button-editable.h b/src/ui/gui/psppire-button-editable.h deleted file mode 100644 index 80b6180d79..0000000000 --- a/src/ui/gui/psppire-button-editable.h +++ /dev/null @@ -1,48 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#ifndef PSPPIRE_BUTTON_EDITABLE_H -#define PSPPIRE_BUTTON_EDITABLE_H 1 - -#include - -G_BEGIN_DECLS - -#define PSPPIRE_TYPE_BUTTON_EDITABLE (psppire_button_editable_get_type()) -#define PSPPIRE_BUTTON_EDITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),PSPPIRE_TYPE_BUTTON_EDITABLE,PsppireButtonEditable)) -#define PSPPIRE_BUTTON_EDITABLE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),PSPPIRE_TYPE_BUTTON_EDITABLE,PsppireButtonEditableClass)) -#define PSPPIRE_IS_BUTTON_EDITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),PSPPIRE_TYPE_BUTTON_EDITABLE)) -#define PSPPIRE_IS_BUTTON_EDITABLE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),PSPPIRE_TYPE_BUTTON_EDITABLE)) -#define PSPPIRE_BUTTON_EDITABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),PSPPIRE_TYPE_BUTTON_EDITABLE,PsppireButtonEditableClass)) - -typedef struct _PsppireButtonEditable PsppireButtonEditable; -typedef struct _PsppireButtonEditableClass PsppireButtonEditableClass; - -struct _PsppireButtonEditable { - GtkButton parent; - gchar *path; -}; - -struct _PsppireButtonEditableClass { - GtkButtonClass parent_class; -}; - -GType psppire_button_editable_get_type (void) G_GNUC_CONST; -PsppireButtonEditable* psppire_button_editable_new (void); - -G_END_DECLS - -#endif /* PSPPIRE_BUTTON_EDITABLE_H */ diff --git a/src/ui/gui/psppire-cell-renderer-button.c b/src/ui/gui/psppire-cell-renderer-button.c deleted file mode 100644 index e801cc6bf3..0000000000 --- a/src/ui/gui/psppire-cell-renderer-button.c +++ /dev/null @@ -1,580 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#include - -#include "ui/gui/psppire-cell-renderer-button.h" - -#include -#include - -#include "ui/gui/psppire-button-editable.h" -#include "ui/gui/pspp-widget-facade.h" - -#include "gl/configmake.h" -#include "gl/relocatable.h" - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -#define P_(msgid) (msgid) - -static void psppire_cell_renderer_button_dispose (GObject *); -static void psppire_cell_renderer_button_finalize (GObject *); - -static void update_style_cache (PsppireCellRendererButton *button, - GtkWidget *widget); - -static void psppire_cell_renderer_button_load_gtkrc (void); - - -G_DEFINE_TYPE_EXTENDED (PsppireCellRendererButton, - psppire_cell_renderer_button, - GTK_TYPE_CELL_RENDERER, - 0, - psppire_cell_renderer_button_load_gtkrc ()); - -static void -psppire_cell_renderer_button_load_gtkrc (void) -{ - const char *gtkrc_file; - - gtkrc_file = relocate (PKGDATADIR "/psppire.gtkrc"); - gtk_rc_add_default_file (gtkrc_file); - gtk_rc_parse (gtkrc_file); -} - -enum - { - PROP_0, - PROP_EDITABLE, - PROP_LABEL, - PROP_SLASH - }; - -static void -psppire_cell_renderer_button_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireCellRendererButton *obj = PSPPIRE_CELL_RENDERER_BUTTON (object); - switch (prop_id) - { - case PROP_EDITABLE: - obj->editable = g_value_get_boolean (value); - if (obj->editable) - g_object_set (obj, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); - else - g_object_set (obj, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); - break; - - case PROP_LABEL: - g_free (obj->label); - obj->label = g_value_dup_string (value); - break; - - case PROP_SLASH: - psppire_cell_renderer_button_set_slash (obj, - g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_cell_renderer_button_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireCellRendererButton *obj = PSPPIRE_CELL_RENDERER_BUTTON (object); - - switch (prop_id) - { - case PROP_EDITABLE: - g_value_set_boolean (value, obj->editable); - break; - - case PROP_LABEL: - g_value_set_string (value, obj->label); - break; - - case PROP_SLASH: - g_value_set_boolean (value, - psppire_cell_renderer_button_get_slash (obj)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -on_style_set (GtkWidget *base, - GtkStyle *previous_style, - PsppireCellRendererButton *button) -{ - update_style_cache (button, NULL); -} - -static void -update_style_cache (PsppireCellRendererButton *button, - GtkWidget *widget) -{ - if (button->base == widget) - return; - - /* Clear old cache. */ - if (button->button_style) - { - g_object_unref (button->button_style); - button->button_style = NULL; - } - if (button->label_style) - { - g_object_unref (button->label_style); - button->label_style = NULL; - } - if (button->base != NULL) - { - if (button->style_set_handler) - { - g_signal_handler_disconnect (button->base, - button->style_set_handler); - button->style_set_handler = 0; - } - g_object_unref (button->base); - button->base = NULL; - } - - /* Populate cache. */ - if (widget) - { - button->button_style = facade_get_style (widget, GTK_TYPE_BUTTON, 0); - button->label_style = facade_get_style (widget, GTK_TYPE_BUTTON, - GTK_TYPE_LABEL, 0); - button->base = widget; - button->style_set_handler = g_signal_connect (widget, "style-set", - G_CALLBACK (on_style_set), - button); - g_object_ref (widget); - g_object_ref (button->button_style); - g_object_ref (button->label_style); - } -} - -static void -psppire_cell_renderer_button_render (GtkCellRenderer *cell, - cairo_t *cr, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - GtkStateType state_type; - PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (cell); - gfloat xalign, yalign; - - if (!button->editable || ! gtk_cell_renderer_get_sensitive (cell)) - state_type = GTK_STATE_INSENSITIVE; - else if (flags & GTK_CELL_RENDERER_SELECTED) - { - if (gtk_widget_has_focus (widget)) - state_type = GTK_STATE_SELECTED; - else - state_type = GTK_STATE_ACTIVE; - } - else if (flags & GTK_CELL_RENDERER_PRELIT) - state_type = GTK_STATE_PRELIGHT; - else - { - if (gtk_widget_get_state_flags (widget) == GTK_STATE_FLAG_INSENSITIVE) - state_type = GTK_STATE_INSENSITIVE; - else - state_type = GTK_STATE_NORMAL; - } - - gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); - - - update_style_cache (button, widget); - - facade_button_render (widget, cr, - cell_area, button->border_width, button->button_style, - state_type, - button->label_style, button->label, button->xpad, - button->ypad, xalign, yalign); - - if (button->slash) - { - cairo_set_line_width (cr, 1.0); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); - cairo_move_to (cr, - cell_area->x, - cell_area->y + cell_area->height); - - cairo_line_to (cr, - cell_area->x + cell_area->width, - cell_area->y); - cairo_stroke (cr); - } -} - -static void -psppire_cell_renderer_button_get_size (GtkCellRenderer *cell, - GtkWidget *widget, - const GdkRectangle *cell_area, - gint *x_offset, - gint *y_offset, - gint *width, - gint *height) -{ - PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (cell); - - update_style_cache (button, widget); - if (cell_area != NULL) - { - /* The caller is really asking for the placement of the focus rectangle. - The focus rectangle should surround the whole label area, so calculate - that area. */ - GtkBorder inset; - - facade_button_get_focus_inset (button->border_width, widget, - button->button_style, &inset); - - if (x_offset) - *x_offset = inset.left; - if (y_offset) - *y_offset = inset.top; - if (width) - *width = MAX (1, cell_area->width - inset.left - inset.right); - if (height) - *height = MAX (1, cell_area->height - inset.top - inset.bottom); - } - else - { - /* The caller is asking for the preferred size of the cell. */ - GtkRequisition label_req; - GtkRequisition request; - - facade_label_get_size_request (button->xpad, button->ypad, - widget, button->label, &label_req); - facade_button_get_size_request (button->border_width, widget, - button->button_style, &label_req, - &request); - - if (x_offset) - *x_offset = 0; - if (y_offset) - *y_offset = 0; - if (width) - *width = request.width; - if (height) - *height = request.height; - } -} - -static void -psppire_cell_renderer_button_clicked (GtkButton *button, - gpointer data) -{ - PsppireCellRendererButton *cell_button = data; - gchar *path; - - g_object_get (button, "path", &path, NULL); - g_signal_emit_by_name (cell_button, "clicked", path); - g_free (path); -} - -#define IDLE_ID_STRING "psppire-cell-renderer-button-idle-id" - -static gboolean -psppire_cell_renderer_button_initial_click (gpointer data) -{ - GtkButton *button = data; - - g_object_steal_data (G_OBJECT (button), IDLE_ID_STRING); - gtk_button_clicked (button); - return FALSE; -} - -static void -psppire_cell_renderer_button_on_destroy (GObject *object, gpointer user_data) -{ - guint idle_id; - - idle_id = GPOINTER_TO_INT (g_object_steal_data (object, IDLE_ID_STRING)); - if (idle_id != 0) - g_source_remove (idle_id); -} - -static void -psppire_cell_renderer_button_double_click (GtkButton *button, - PsppireCellRendererButton *cell_button) -{ - gchar *path; - - if (g_object_get_data (G_OBJECT (button), IDLE_ID_STRING)) - psppire_cell_renderer_button_initial_click (button); - - g_object_get (button, "path", &path, NULL); - g_signal_emit_by_name (cell_button, "double-clicked", path); - g_free (path); -} - -static gboolean -psppire_cell_renderer_button_press_event (GtkButton *button, - GdkEventButton *event, - gpointer data) -{ - PsppireCellRendererButton *cell_button = data; - - if (event->button == 3) - { - /* Allow right-click events to propagate upward in the widget hierarchy. - Otherwise right-click menus, that trigger on a button-press-event on - the containing PsppSheetView, will pop up if the button is rendered as - a facade but not if the button widget exists. - - We have to translate the event's data by hand to be relative to the - parent window, because the normal GObject signal propagation mechanism - won't do it for us. (This might be a hint that we're doing this - wrong.) */ - gint x, y; - - gdk_window_get_position (event->window, &x, &y); - event->x += x; - event->y += y; - g_signal_stop_emission_by_name (button, "button-press-event"); - return FALSE; - } - - if (cell_button->click_time != 0) - { - GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button)); - GtkSettings *settings = gtk_settings_get_for_screen (screen); - gint double_click_distance; - gint double_click_time; - - g_object_get (settings, - "gtk-double-click-time", &double_click_time, - "gtk-double-click-distance", &double_click_distance, - NULL); - - if (event->type == GDK_BUTTON_PRESS - && event->button == 1 - && event->time <= cell_button->click_time + double_click_time - && ABS (event->x_root - cell_button->click_x) <= double_click_distance - && ABS (event->y_root - cell_button->click_y) <= double_click_distance) - { - psppire_cell_renderer_button_double_click (button, cell_button); - return TRUE; - } - - cell_button->click_time = 0; - } - - if (event->type == GDK_2BUTTON_PRESS) - { - psppire_cell_renderer_button_double_click (button, cell_button); - return TRUE; - } - - return FALSE; -} - -static GtkCellEditable * -psppire_cell_renderer_button_start_editing (GtkCellRenderer *cell, - GdkEvent *event, - GtkWidget *widget, - const gchar *path, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - PsppireCellRendererButton *cell_button = PSPPIRE_CELL_RENDERER_BUTTON (cell); - gfloat xalign, yalign; - - gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); - cell_button->button = g_object_new (PSPPIRE_TYPE_BUTTON_EDITABLE, - "label", cell_button->label, - "xalign", xalign, - "yalign", yalign, - "path", path, - NULL); - - g_signal_connect (G_OBJECT (cell_button->button), "clicked", - G_CALLBACK (psppire_cell_renderer_button_clicked), - cell); - g_signal_connect (G_OBJECT (cell_button->button), "button-press-event", - G_CALLBACK (psppire_cell_renderer_button_press_event), - cell); - - gtk_widget_show (cell_button->button); - - if (event != NULL && event->any.type == GDK_BUTTON_RELEASE) - { - guint idle_id; - - cell_button->click_time = event->button.time; - cell_button->click_x = event->button.x_root; - cell_button->click_y = event->button.y_root; - idle_id = g_idle_add (psppire_cell_renderer_button_initial_click, - cell_button->button); - g_object_set_data (G_OBJECT (cell_button->button), IDLE_ID_STRING, - GINT_TO_POINTER (idle_id)); - g_signal_connect (G_OBJECT (cell_button->button), "destroy", - G_CALLBACK (psppire_cell_renderer_button_on_destroy), - NULL); - } - else - { - cell_button->click_time = 0; - cell_button->click_x = 0; - cell_button->click_y = 0; - } - - return GTK_CELL_EDITABLE (cell_button->button); -} - -static void -psppire_cell_renderer_button_class_init (PsppireCellRendererButtonClass *class) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); - - gobject_class->set_property = psppire_cell_renderer_button_set_property; - gobject_class->get_property = psppire_cell_renderer_button_get_property; - gobject_class->finalize = psppire_cell_renderer_button_finalize; - gobject_class->dispose = psppire_cell_renderer_button_dispose; - - cell_class->get_size = psppire_cell_renderer_button_get_size; - cell_class->render = psppire_cell_renderer_button_render; - cell_class->start_editing = psppire_cell_renderer_button_start_editing; - - g_signal_new ("clicked", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, G_TYPE_STRING); - - g_signal_new ("double-clicked", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, G_TYPE_STRING); - - g_object_class_install_property (gobject_class, - PROP_EDITABLE, - g_param_spec_boolean ("editable", - P_("Editable"), - P_("Whether the button may be clicked."), - FALSE, - G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, - PROP_LABEL, - g_param_spec_string ("label", - P_("Label"), - P_("Text to appear in button."), - "", - G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, - PROP_SLASH, - g_param_spec_boolean ("slash", - P_("Diagonal slash"), - P_("Whether to draw a diagonal slash across the button."), - FALSE, - G_PARAM_READWRITE)); - -} - -static void -psppire_cell_renderer_button_init (PsppireCellRendererButton *obj) -{ - obj->editable = FALSE; - obj->label = g_strdup (""); - obj->border_width = 0; - obj->xpad = 0; - obj->ypad = 0; - - obj->slash = FALSE; - - obj->button = NULL; - - obj->button_style = NULL; - obj->label_style = NULL; - obj->base = NULL; - obj->style_set_handler = 0; - obj->dispose_has_run = FALSE; -} - -static void -psppire_cell_renderer_button_finalize (GObject *obj) -{ - PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (obj); - - g_free (button->label); -} - -static void -psppire_cell_renderer_button_dispose (GObject *obj) -{ - PsppireCellRendererButton *button = PSPPIRE_CELL_RENDERER_BUTTON (obj); - - if (button->dispose_has_run) - return; - - button->dispose_has_run = TRUE; - - /* When called with NULL, as we are doing here, update_style_cache - does nothing more than to drop references */ - update_style_cache (button, NULL); - - G_OBJECT_CLASS (psppire_cell_renderer_button_parent_class)->dispose (obj); -} - -GtkCellRenderer * -psppire_cell_renderer_button_new (void) -{ - return GTK_CELL_RENDERER (g_object_new (PSPPIRE_TYPE_CELL_RENDERER_BUTTON, NULL)); -} - -void -psppire_cell_renderer_button_set_slash (PsppireCellRendererButton *button, - gboolean slash) -{ - g_return_if_fail (button != NULL); - button->slash = slash; -} - -gboolean -psppire_cell_renderer_button_get_slash (const PsppireCellRendererButton *button) -{ - g_return_val_if_fail (button != NULL, FALSE); - return button->slash; -} diff --git a/src/ui/gui/psppire-cell-renderer-button.h b/src/ui/gui/psppire-cell-renderer-button.h deleted file mode 100644 index 28d5dd14e5..0000000000 --- a/src/ui/gui/psppire-cell-renderer-button.h +++ /dev/null @@ -1,72 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#ifndef PSPPIRE_CELL_RENDERER_BUTTON_H -#define PSPPIRE_CELL_RENDERER_BUTTON_H 1 - -#include - -G_BEGIN_DECLS - -#define PSPPIRE_TYPE_CELL_RENDERER_BUTTON (psppire_cell_renderer_button_get_type()) -#define PSPPIRE_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),PSPPIRE_TYPE_CELL_RENDERER_BUTTON,PsppireCellRendererButton)) -#define PSPPIRE_CELL_RENDERER_BUTTON_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),PSPPIRE_TYPE_CELL_RENDERER_BUTTON,PsppireCellRendererButtonClass)) -#define PSPPIRE_IS_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),PSPPIRE_TYPE_CELL_RENDERER_BUTTON)) -#define PSPPIRE_IS_CELL_RENDERER_BUTTON_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),PSPPIRE_TYPE_CELL_RENDERER_BUTTON)) -#define PSPPIRE_CELL_RENDERER_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),PSPPIRE_TYPE_CELL_RENDERER_BUTTON,PsppireCellRendererButtonClass)) - -typedef struct _PsppireCellRendererButton PsppireCellRendererButton; -typedef struct _PsppireCellRendererButtonClass PsppireCellRendererButtonClass; - -struct _PsppireCellRendererButton -{ - GtkCellRenderer parent; - - gboolean editable; - gchar *label; - gint border_width; - gint xpad; - gint ypad; - - gboolean slash; - - GtkWidget *button; - guint32 click_time; - gdouble click_x; - gdouble click_y; - - /* Style caching. */ - GtkStyle *button_style; - GtkStyle *label_style; - GtkWidget *base; - gulong style_set_handler; - gboolean dispose_has_run; -}; - -struct _PsppireCellRendererButtonClass { - GtkCellRendererClass parent_class; -}; - -GType psppire_cell_renderer_button_get_type (void) G_GNUC_CONST; -GtkCellRenderer* psppire_cell_renderer_button_new (void); - -void psppire_cell_renderer_button_set_slash (PsppireCellRendererButton *, - gboolean slash); -gboolean psppire_cell_renderer_button_get_slash (const PsppireCellRendererButton *); - -G_END_DECLS - -#endif /* PSPPIRE_CELL_RENDERER_BUTTON_H */ diff --git a/src/ui/gui/psppire-data-editor.c b/src/ui/gui/psppire-data-editor.c index ab3a5f0668..b9c6fb6214 100644 --- a/src/ui/gui/psppire-data-editor.c +++ b/src/ui/gui/psppire-data-editor.c @@ -1,5 +1,6 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. + Copyright (C) 2008, 2009, 2010, 2011, 2012, 2016, + 2017 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,34 +20,34 @@ #include "ui/gui/psppire-data-editor.h" #include -#include #include "data/datasheet.h" #include "data/value-labels.h" #include "libpspp/range-set.h" #include "libpspp/str.h" + #include "ui/gui/helper.h" -#include "ui/gui/pspp-sheet-selection.h" -#include "ui/gui/psppire-data-sheet.h" +#include "ui/gui/var-display.h" +#include "ui/gui/val-labs-dialog.h" +#include "ui/gui/missing-val-dialog.h" +#include "ui/gui/var-type-dialog.h" +#include "ui/gui/psppire-dict.h" #include "ui/gui/psppire-data-store.h" +#include "ui/gui/psppire-data-window.h" #include "ui/gui/psppire-value-entry.h" -#include "ui/gui/psppire-var-sheet.h" #include "ui/gui/psppire-conf.h" +#include "ui/gui/psppire-variable-sheet.h" +#include "ui/gui/psppire-data-sheet.h" + + +#include #include #define _(msgid) gettext (msgid) -#define FOR_EACH_DATA_SHEET(DATA_SHEET, IDX, DATA_EDITOR) \ - for ((IDX) = 0; \ - (IDX) < 4 \ - && ((DATA_SHEET) = PSPPIRE_DATA_SHEET ( \ - (DATA_EDITOR)->data_sheets[IDX])) != NULL; \ - (IDX)++) - static void psppire_data_editor_class_init (PsppireDataEditorClass *klass); static void psppire_data_editor_init (PsppireDataEditor *de); -static void disconnect_data_sheets (PsppireDataEditor *); static void refresh_entry (PsppireDataEditor *); GType @@ -83,8 +84,6 @@ psppire_data_editor_dispose (GObject *obj) { PsppireDataEditor *de = (PsppireDataEditor *) obj; - disconnect_data_sheets (de); - if (de->data_store) { g_object_unref (de->data_store); @@ -119,15 +118,9 @@ enum static void psppire_data_editor_refresh_model (PsppireDataEditor *de) { - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet); - PsppireDataSheet *data_sheet; - int i; - - FOR_EACH_DATA_SHEET (data_sheet, i, de) - psppire_data_sheet_set_data_store (data_sheet, de->data_store); - psppire_var_sheet_set_dictionary (var_sheet, de->dict); } + static void psppire_data_editor_set_property (GObject *object, guint prop_id, @@ -135,13 +128,13 @@ psppire_data_editor_set_property (GObject *object, GParamSpec *pspec) { PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object); - PsppireDataSheet *data_sheet; - int i; switch (prop_id) { case PROP_SPLIT_WINDOW: - psppire_data_editor_split_window (de, g_value_get_boolean (value)); + de->split = g_value_get_boolean (value); + g_object_set (de->data_sheet, "split", de->split, NULL); + g_object_set (de->var_sheet, "split", de->split, NULL); break; case PROP_DATA_STORE: if ( de->data_store) @@ -154,8 +147,14 @@ psppire_data_editor_set_property (GObject *object, de->data_store = g_value_get_pointer (value); g_object_ref (de->data_store); + + g_object_set (de->data_sheet, "data-model", de->data_store, NULL); psppire_data_editor_refresh_model (de); + g_signal_connect_swapped (de->data_sheet, "selection-changed", + G_CALLBACK (refresh_entry), + de); + g_signal_connect_swapped (de->data_store, "case-changed", G_CALLBACK (refresh_entry), de); @@ -166,13 +165,18 @@ psppire_data_editor_set_property (GObject *object, de->dict = g_value_get_pointer (value); g_object_ref (de->dict); - psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet), - de->dict); + g_object_set (de->var_sheet, "data-model", de->dict, NULL); break; + case PROP_VALUE_LABELS: - FOR_EACH_DATA_SHEET (data_sheet, i, de) - psppire_data_sheet_set_value_labels (data_sheet, - g_value_get_boolean (value)); + { + gboolean l = g_value_get_boolean (value); + g_object_set (de->data_sheet, "forward-conversion", + l ? + psppire_data_store_value_to_string_with_labels : + psppire_data_store_value_to_string, + NULL); + } break; default: @@ -201,9 +205,6 @@ psppire_data_editor_get_property (GObject *object, g_value_set_pointer (value, de->dict); break; case PROP_VALUE_LABELS: - g_value_set_boolean (value, - psppire_data_sheet_get_value_labels ( - PSPPIRE_DATA_SHEET (de->data_sheets[0]))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -217,7 +218,6 @@ psppire_data_editor_switch_page (GtkNotebook *notebook, guint page_num) { GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, w, page_num); - } static void @@ -225,7 +225,20 @@ psppire_data_editor_set_focus_child (GtkContainer *container, GtkWidget *widget) { GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget); +} + +static gboolean +on_key_press (GtkWidget *w, GdkEventKey *e) +{ + PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (w); + if (e->keyval == GDK_KEY_F2 && + PSPPIRE_DATA_EDITOR_DATA_VIEW == gtk_notebook_get_current_page (GTK_NOTEBOOK (de))) + { + gtk_widget_grab_focus (de->datum_entry); + } + + return FALSE; } static void @@ -239,6 +252,7 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); parent_class = g_type_class_peek_parent (klass); @@ -247,8 +261,8 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass) object_class->get_property = psppire_data_editor_get_property; container_class->set_focus_child = psppire_data_editor_set_focus_child; - notebook_class->switch_page = psppire_data_editor_switch_page; + widget_class->key_press_event = on_key_press; data_store_spec = g_param_spec_pointer ("data-store", @@ -292,393 +306,176 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass) g_object_class_install_property (object_class, PROP_SPLIT_WINDOW, split_window_spec); - } -static gboolean -on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet, - gint dict_index, - PsppireDataEditor *de) + +static void +on_var_sheet_var_double_clicked (void *var_sheet, gint dict_index, + PsppireDataEditor *de) { gtk_notebook_set_current_page (GTK_NOTEBOOK (de), - PSPPIRE_DATA_EDITOR_VARIABLE_VIEW); - - psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet), - dict_index); + PSPPIRE_DATA_EDITOR_DATA_VIEW); - return TRUE; + ssw_sheet_scroll_to (SSW_SHEET (de->data_sheet), dict_index, -1); } -static gboolean -on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index, + +static void +on_data_sheet_var_double_clicked (SswSheet *data_sheet, gint dict_index, PsppireDataEditor *de) { - PsppireDataSheet *data_sheet; gtk_notebook_set_current_page (GTK_NOTEBOOK (de), - PSPPIRE_DATA_EDITOR_DATA_VIEW); - - data_sheet = psppire_data_editor_get_active_data_sheet (de); - psppire_data_sheet_goto_variable (data_sheet, dict_index); + PSPPIRE_DATA_EDITOR_VARIABLE_VIEW); - return TRUE; + ssw_sheet_scroll_to (SSW_SHEET (de->var_sheet), -1, dict_index); } + + /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently active cell or cells. */ static void refresh_entry (PsppireDataEditor *de) { - PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de); - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - - gchar *ref_cell_text; - GList *selected_columns, *iter; - struct variable *var; - gint n_cases; - gint n_vars; - - selected_columns = pspp_sheet_selection_get_selected_columns (selection); - n_vars = 0; - var = NULL; - for (iter = selected_columns; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *v = g_object_get_data (G_OBJECT (column), "variable"); - if (v != NULL) - { - var = v; - n_vars++; - } - } - g_list_free (selected_columns); - - n_cases = pspp_sheet_selection_count_selected_rows (selection); - if (n_cases > 0) - { - /* The final row is selectable but it isn't a case (it's just used to add - more cases), so don't count it. */ - GtkTreePath *path; - gint case_count; - - case_count = psppire_data_store_get_case_count (de->data_store); - path = gtk_tree_path_new_from_indices (case_count, -1); - if (pspp_sheet_selection_path_is_selected (selection, path)) - n_cases--; - gtk_tree_path_free (path); - } - - ref_cell_text = NULL; - if (n_cases == 1 && n_vars == 1) + gint row, col; + if (ssw_sheet_get_active_cell (SSW_SHEET (de->data_sheet), &col, &row)) { - PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry); - struct range_set *selected_rows; - gboolean show_value_labels; - union value value; - int width; - gint row; - - selected_rows = pspp_sheet_selection_get_range_set (selection); - row = range_set_scan (selected_rows, 0); - range_set_destroy (selected_rows); - - ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var)); - - show_value_labels = psppire_data_sheet_get_value_labels (data_sheet); - - psppire_value_entry_set_variable (value_entry, var); - psppire_value_entry_set_show_value_label (value_entry, - show_value_labels); + union value val; + const struct variable *var = psppire_dict_get_variable (de->dict, col); + if (var == NULL) + return; - width = var_get_width (var); - value_init (&value, width); - datasheet_get_value (de->data_store->datasheet, - row, var_get_case_index (var), &value); - psppire_value_entry_set_value (value_entry, &value, width); - value_destroy (&value, width); + psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry), var); - gtk_widget_set_sensitive (de->datum_entry, TRUE); - } - else - { - if (n_cases == 0 || n_vars == 0) - { - ref_cell_text = NULL; - } - else - { - struct string s; - - /* The glib string library does not understand the ' printf modifier - on all platforms, but the "struct string" library does (because - Gnulib fixes that problem), so use the latter. */ - ds_init_empty (&s); - ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases), - n_cases); - ds_put_byte (&s, ' '); - ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */ - ds_put_byte (&s, ' '); - ds_put_format (&s, ngettext ("%'d variable", "%'d variables", - n_vars), - n_vars); - ref_cell_text = ds_steal_cstr (&s); - } + int width = var_get_width (var); + if (! psppire_data_store_get_value (PSPPIRE_DATA_STORE (de->data_store), + row, var, &val)) + return; - psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry), - NULL); - gtk_entry_set_text ( - GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), ""); - gtk_widget_set_sensitive (de->datum_entry, FALSE); + psppire_value_entry_set_value (PSPPIRE_VALUE_ENTRY (de->datum_entry), + &val, width); + value_destroy (&val, width); } - - gtk_label_set_label (GTK_LABEL (de->cell_ref_label), - ref_cell_text ? ref_cell_text : ""); - g_free (ref_cell_text); -} - -static void -on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de) -{ - PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de); - struct variable *var; - union value value; - int width; - gint row; - - row = psppire_data_sheet_get_current_case (data_sheet); - var = psppire_data_sheet_get_current_variable (data_sheet); - if (row < 0 || !var) - return; - - width = var_get_width (var); - value_init (&value, width); - if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry), - &value, width)) - psppire_data_store_set_value (de->data_store, row, var, &value); - value_destroy (&value, width); } static void -on_data_sheet_selection_changed (PsppSheetSelection *selection, - PsppireDataEditor *de) +on_datum_entry_activate (GtkEntry *entry, PsppireDataEditor *de) { - /* In a split view, ensure that only a single data sheet has a nonempty - selection. */ - if (de->split - && pspp_sheet_selection_count_selected_rows (selection) - && pspp_sheet_selection_count_selected_columns (selection)) + gint row, col; + if (ssw_sheet_get_active_cell (SSW_SHEET (de->data_sheet), &col, &row)) { - PsppireDataSheet *ds; - int i; - - FOR_EACH_DATA_SHEET (ds, i, de) - { - PsppSheetSelection *s; - - s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds)); - if (s != selection) - pspp_sheet_selection_unselect_all (s); - } + union value val; + const struct variable *var = psppire_dict_get_variable (de->dict, col); + if (var == NULL) + return; + + int width = var_get_width (var); + value_init (&val, width); + if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry), + &val, width)) + { + psppire_data_store_set_value (de->data_store, row, var, &val); + } + value_destroy (&val, width); + + gtk_widget_grab_focus (de->data_sheet); + ssw_sheet_set_active_cell (de->data_sheet, col, row, NULL); } - - refresh_entry (de); } -/* Ensures that rows in the right-hand panes in the split view have the same - row height as the left-hand panes. Otherwise, the rows in the right-hand - pane tend to be smaller, because the right-hand pane doesn't have buttons - for case numbers. */ + +/* Called when the active cell or the selection in the data sheet changes */ static void -on_data_sheet_fixed_height_notify (PsppireDataSheet *ds, - GParamSpec *pspec, - PsppireDataEditor *de) +on_data_selection_change (PsppireDataEditor *de, SswRange *sel) { - enum - { - TL = GTK_XPANED_TOP_LEFT, - TR = GTK_XPANED_TOP_RIGHT, - BL = GTK_XPANED_BOTTOM_LEFT, - BR = GTK_XPANED_BOTTOM_RIGHT - }; - - int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds)); + gchar *ref_cell_text = NULL; - pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]), - fixed_height); - pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]), - fixed_height); -} + gint n_cases = abs (sel->end_y - sel->start_y) + 1; + gint n_vars = abs (sel->end_x - sel->start_x) + 1; -static void -disconnect_data_sheets (PsppireDataEditor *de) -{ - PsppireDataSheet *ds; - int i; + if (n_cases == 1 && n_vars == 1) + { + /* A single cell is selected */ + const struct variable *var = psppire_dict_get_variable (de->dict, sel->start_x); - FOR_EACH_DATA_SHEET (ds, i, de) + if (var) + ref_cell_text = g_strdup_printf (_("%d : %s"), + sel->start_y + 1, var_get_name (var)); + } + else { - PsppSheetSelection *selection; + struct string s; + + /* The glib string library does not understand the ' printf modifier + on all platforms, but the "struct string" library does (because + Gnulib fixes that problem), so use the latter. */ + ds_init_empty (&s); + ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases), + n_cases); + ds_put_byte (&s, ' '); + ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */ + ds_put_byte (&s, ' '); + ds_put_format (&s, ngettext ("%'d variable", "%'d variables", + n_vars), + n_vars); + ref_cell_text = ds_steal_cstr (&s); + } - if (ds == NULL) - { - /* This can only happen if 'dispose' runs more than once. */ - continue; - } + gtk_label_set_label (GTK_LABEL (de->cell_ref_label), + ref_cell_text ? ref_cell_text : ""); - if (i == GTK_XPANED_TOP_LEFT) - g_signal_handlers_disconnect_by_func ( - ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de); + g_free (ref_cell_text); +} - g_signal_handlers_disconnect_by_func ( - ds, G_CALLBACK (refresh_entry), de); - g_signal_handlers_disconnect_by_func ( - ds, G_CALLBACK (on_data_sheet_var_double_clicked), de); - selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds)); - g_signal_handlers_disconnect_by_func ( - selection, G_CALLBACK (on_data_sheet_selection_changed), de); +static void set_font_recursively (GtkWidget *w, gpointer data); - de->data_sheets[i] = NULL; - } -} + -static GtkWidget * -make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines, - gboolean show_value_labels) +void +psppire_data_editor_data_delete_variables (PsppireDataEditor *de) { - PsppSheetSelection *selection; - GtkWidget *ds; - - ds = psppire_data_sheet_new (); - pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines); - psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds), - show_value_labels); - - g_signal_connect_swapped (ds, "notify::value-labels", - G_CALLBACK (refresh_entry), de); - g_signal_connect (ds, "var-double-clicked", - G_CALLBACK (on_data_sheet_var_double_clicked), de); + SswRange *range = SSW_SHEET(de->data_sheet)->selection; - selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds)); - g_signal_connect (selection, "changed", - G_CALLBACK (on_data_sheet_selection_changed), de); + psppire_dict_delete_variables (de->dict, range->start_x, + (range->end_x - range->start_x + 1)); - return ds; + gtk_widget_queue_draw (GTK_WIDGET (de->data_sheet)); } -static GtkWidget * -make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines, - gboolean show_value_labels) +void +psppire_data_editor_var_delete_variables (PsppireDataEditor *de) { - GtkWidget *data_sheet_scroller; - - de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels); - de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL; + SswRange *range = SSW_SHEET(de->var_sheet)->selection; - /* Put data sheet in scroller. */ - data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]); + psppire_dict_delete_variables (de->dict, range->start_y, + (range->end_y - range->start_y + 1)); - return data_sheet_scroller; + gtk_widget_queue_draw (GTK_WIDGET (de->var_sheet)); } -static GtkWidget * -make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines, - gboolean show_value_labels) +void +psppire_data_editor_insert_new_case_at_posn (PsppireDataEditor *de, gint posn) { - /* Panes, in the order in which we want to create them. */ - enum - { - TL, /* top left */ - TR, /* top right */ - BL, /* bottom left */ - BR /* bottom right */ - }; - - PsppSheetView *ds[4]; - GtkXPaned *xpaned; - int i; - - xpaned = GTK_XPANED (gtk_xpaned_new ()); - - for (i = 0; i < 4; i++) - { - GtkAdjustment *hadjust, *vadjust; - GtkPolicyType hpolicy, vpolicy; - GtkWidget *scroller; - - de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels); - ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]); - - if (i == BL) - hadjust = pspp_sheet_view_get_hadjustment (ds[TL]); - else if (i == BR) - hadjust = pspp_sheet_view_get_hadjustment (ds[TR]); - else - hadjust = NULL; - - if (i == TR) - vadjust = pspp_sheet_view_get_vadjustment (ds[TL]); - else if (i == BR) - vadjust = pspp_sheet_view_get_vadjustment (ds[BL]); - else - vadjust = NULL; - - scroller = gtk_scrolled_window_new (hadjust, vadjust); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller), - GTK_SHADOW_ETCHED_IN); - hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS; - vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS; - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller), - hpolicy, vpolicy); - gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i])); - - switch (i) - { - case TL: - gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE); - break; - - case TR: - gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE); - break; - - case BL: - gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE); - break; - - case BR: - gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE); - break; + psppire_data_store_insert_new_case (de->data_store, posn); - default: - g_warn_if_reached (); - } - } - - /* Bottom sheets don't display variable names. */ - pspp_sheet_view_set_headers_visible (ds[BL], FALSE); - pspp_sheet_view_set_headers_visible (ds[BR], FALSE); - - /* Right sheets don't display case numbers. */ - psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE); - psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE); + gtk_widget_queue_draw (GTK_WIDGET (de->data_sheet)); +} - g_signal_connect (ds[TL], "notify::fixed-height", - G_CALLBACK (on_data_sheet_fixed_height_notify), de); +void +psppire_data_editor_insert_new_variable_at_posn (PsppireDataEditor *de, gint posn) +{ + const struct variable *v = psppire_dict_insert_variable (de->dict, posn, NULL); + psppire_data_store_insert_value (de->data_store, var_get_width(v), + var_get_case_index (v)); - return GTK_WIDGET (xpaned); + gtk_widget_queue_draw (GTK_WIDGET (de)); } -static void set_font_recursively (GtkWidget *w, gpointer data); - static void psppire_data_editor_init (PsppireDataEditor *de) { - GtkWidget *var_sheet_scroller; GtkWidget *hbox; gchar *fontname = NULL; @@ -686,7 +483,6 @@ psppire_data_editor_init (PsppireDataEditor *de) gtk_style_context_add_class (context, "psppire-data-editor"); de->font = NULL; - de->old_vbox_widget = NULL; g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL); @@ -695,41 +491,47 @@ psppire_data_editor_init (PsppireDataEditor *de) gtk_widget_set_valign (de->cell_ref_label, GTK_ALIGN_CENTER); de->datum_entry = psppire_value_entry_new (); - g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), - "activate", G_CALLBACK (on_datum_entry_activate), de); + g_signal_connect (de->datum_entry, "edit-done", + G_CALLBACK (on_datum_entry_activate), de); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0); de->split = FALSE; - de->datasheet_vbox_widget - = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH, FALSE); + de->data_sheet = psppire_data_sheet_new (); + GtkWidget *data_button = ssw_sheet_get_button (SSW_SHEET (de->data_sheet)); + gtk_button_set_label (GTK_BUTTON (data_button), _("Case")); de->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget, - TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (de->vbox), de->data_sheet, TRUE, TRUE, 0); + + + g_signal_connect_swapped (de->data_sheet, "selection-changed", + G_CALLBACK (on_data_selection_change), de); gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox, gtk_label_new_with_mnemonic (_("Data View"))); gtk_widget_show_all (de->vbox); - de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ()); - pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), - GTK_TREE_VIEW_GRID_LINES_BOTH); - var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet); - gtk_widget_show_all (var_sheet_scroller); - gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller, + de->var_sheet = psppire_variable_sheet_new (); + + GtkWidget *var_button = ssw_sheet_get_button (SSW_SHEET (de->var_sheet)); + gtk_button_set_label (GTK_BUTTON (var_button), _("Variable")); + + gtk_notebook_append_page (GTK_NOTEBOOK (de), de->var_sheet, gtk_label_new_with_mnemonic (_("Variable View"))); - g_signal_connect (de->var_sheet, "var-double-clicked", + gtk_widget_show_all (de->var_sheet); + + g_signal_connect (de->var_sheet, "row-header-double-clicked", G_CALLBACK (on_var_sheet_var_double_clicked), de); + g_signal_connect (de->data_sheet, "column-header-double-clicked", + G_CALLBACK (on_data_sheet_var_double_clicked), de); + g_object_set (de, "can-focus", FALSE, NULL); if (psppire_conf_get_string (psppire_conf_new (), @@ -741,6 +543,7 @@ psppire_data_editor_init (PsppireDataEditor *de) set_font_recursively (GTK_WIDGET (de), de->font); } + gtk_widget_add_events (GTK_WIDGET (de), GDK_KEY_PRESS_MASK); } GtkWidget* @@ -758,17 +561,8 @@ psppire_data_editor_new (PsppireDict *dict, void psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible) { - GtkTreeViewGridLines grid; - PsppireDataSheet *data_sheet; - int i; - - grid = (grid_visible - ? GTK_TREE_VIEW_GRID_LINES_BOTH - : GTK_TREE_VIEW_GRID_LINES_NONE); - - FOR_EACH_DATA_SHEET (data_sheet, i, de) - pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid); - pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid); + g_object_set (SSW_SHEET (de->var_sheet), "gridlines", grid_visible, NULL); + g_object_set (SSW_SHEET (de->data_sheet), "gridlines", grid_visible, NULL); } @@ -827,44 +621,7 @@ psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_ void psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split) { - GtkTreeViewGridLines grid_lines; - gboolean labels; - - if (split == de->split) - return; - - - grid_lines = pspp_sheet_view_get_grid_lines ( - PSPP_SHEET_VIEW (de->data_sheets[0])); - labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET ( - de->data_sheets[0])); - - disconnect_data_sheets (de); - if (de->old_vbox_widget) - g_object_unref (de->old_vbox_widget); - de->old_vbox_widget = de->datasheet_vbox_widget; - g_object_ref (de->old_vbox_widget); - /* FIXME: old_vbox_widget needs to be unreffed in dispose. - (currently it seems to provoke an error if I do that. - I don't know why. */ - gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget); - - if (split) - de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels); - else - de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels); - - psppire_data_editor_refresh_model (de); - - gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget, - TRUE, TRUE, 0); - gtk_widget_show_all (de->vbox); - - if (de->font) - set_font_recursively (GTK_WIDGET (de), de->font); - - de->split = split; - g_object_notify (G_OBJECT (de), "split"); + g_object_set (de, "split", split, NULL); } /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary @@ -872,54 +629,43 @@ psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split) void psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index) { - PsppireDataSheet *data_sheet; + gint page = gtk_notebook_get_current_page (GTK_NOTEBOOK (de)); - switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de))) + switch (page) { - case PSPPIRE_DATA_EDITOR_DATA_VIEW: - data_sheet = psppire_data_editor_get_active_data_sheet (de); - psppire_data_sheet_goto_variable (data_sheet, dict_index); - break; - - case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW: - psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet), - dict_index); - break; + case PSPPIRE_DATA_EDITOR_DATA_VIEW: + ssw_sheet_scroll_to (SSW_SHEET (de->data_sheet), dict_index, -1); + ssw_sheet_set_active_cell (SSW_SHEET (de->data_sheet), dict_index, -1, NULL); + break; + case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW: + ssw_sheet_scroll_to (SSW_SHEET (de->var_sheet), -1, dict_index); + ssw_sheet_set_active_cell (SSW_SHEET (de->var_sheet), -1, dict_index, NULL); + break; } } -/* Returns the "active" data sheet in DE. If DE is in single-paned mode, this - is the only data sheet. If DE is in split mode (showing four data sheets), - this is the focused data sheet or, if none is focused, the data sheet with - selected cells or, if none has selected cells, the upper-left data sheet. */ -PsppireDataSheet * -psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de) +/* Set the datum at COL, ROW, to that contained in VALUE. + */ +static void +store_set_datum (GtkTreeModel *model, gint col, gint row, + const GValue *value) { - if (de->split) - { - PsppireDataSheet *data_sheet; - GtkWidget *scroller; - int i; - - /* If one of the datasheet's scrollers is focused, choose that one. */ - scroller = gtk_container_get_focus_child ( - GTK_CONTAINER (de->datasheet_vbox_widget)); - if (scroller != NULL) - return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller))); - - /* Otherwise if there's a nonempty selection in some data sheet, choose - that one. */ - FOR_EACH_DATA_SHEET (data_sheet, i, de) - { - PsppSheetSelection *selection; + PsppireDataStore *store = PSPPIRE_DATA_STORE (model); + GVariant *v = g_value_get_variant (value); + union value uv; + value_variant_get (&uv, v); + const struct variable *var = psppire_dict_get_variable (store->dict, col); + psppire_data_store_set_value (store, row, var, &uv); + value_destroy_from_variant (&uv, v); +} - selection = pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (data_sheet)); - if (pspp_sheet_selection_count_selected_rows (selection) - && pspp_sheet_selection_count_selected_columns (selection)) - return data_sheet; - } - } +void +psppire_data_editor_paste (PsppireDataEditor *de) +{ + SswSheet *sheet = SSW_SHEET (de->data_sheet); + GtkClipboard *clip = + gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (sheet)), + GDK_SELECTION_CLIPBOARD); - return PSPPIRE_DATA_SHEET (de->data_sheets[0]); + ssw_sheet_paste (sheet, clip, store_set_datum); } diff --git a/src/ui/gui/psppire-data-editor.h b/src/ui/gui/psppire-data-editor.h index 28ae214a68..941d7a4997 100644 --- a/src/ui/gui/psppire-data-editor.h +++ b/src/ui/gui/psppire-data-editor.h @@ -33,7 +33,6 @@ #include #include "psppire-data-store.h" -#include "ui/gui/pspp-sheet-view.h" G_BEGIN_DECLS @@ -61,17 +60,14 @@ struct _PsppireDataEditor /* Variable sheet tab. */ GtkWidget *var_sheet; + GtkWidget *data_sheet; /* Data sheet tab. */ GtkWidget *vbox; /* Top-level widget in tab. */ GtkWidget *cell_ref_label; /* GtkLabel that shows selected case and var. */ GtkWidget *datum_entry; /* PsppireValueEntry for editing current cell. */ - GtkWidget *datasheet_vbox_widget; /* ->vbox child that holds data sheets. */ - GtkWidget *data_sheets[4]; /* Normally one data sheet; four, if split. */ - gboolean split; /* True if data sheets are split. */ - /* UI manager for whichever var or data sheet is currently in use. */ - GtkWidget *old_vbox_widget; + gboolean split; /* True if the sheets are in split view. */ }; struct _PsppireDataEditorClass @@ -86,12 +82,18 @@ void psppire_data_editor_show_grid (PsppireDataEditor *, gboolea void psppire_data_editor_set_font (PsppireDataEditor *, PangoFontDescription *); void psppire_data_editor_split_window (PsppireDataEditor *, gboolean ); -void psppire_data_editor_goto_variable (PsppireDataEditor *, gint dict_index); +void psppire_data_editor_goto_variable (PsppireDataEditor *, gint dict_index); +void psppire_data_editor_data_delete_variables (PsppireDataEditor *de); +void psppire_data_editor_var_delete_variables (PsppireDataEditor *de); +void psppire_data_editor_insert_new_case_at_posn (PsppireDataEditor *de, gint posn); +void psppire_data_editor_insert_new_variable_at_posn (PsppireDataEditor *de, gint posn); struct _PsppireDataSheet *psppire_data_editor_get_active_data_sheet (PsppireDataEditor *); enum {PSPPIRE_DATA_EDITOR_DATA_VIEW = 0, PSPPIRE_DATA_EDITOR_VARIABLE_VIEW}; +void psppire_data_editor_paste (PsppireDataEditor *de); + G_END_DECLS #endif /* __PSPPIRE_DATA_EDITOR_H__ */ diff --git a/src/ui/gui/psppire-data-sheet.c b/src/ui/gui/psppire-data-sheet.c index 962fa1ef2b..089ab8f479 100644 --- a/src/ui/gui/psppire-data-sheet.c +++ b/src/ui/gui/psppire-data-sheet.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc. + Copyright (C) 2017 John Darrington 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 @@ -12,2460 +12,405 @@ 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 . */ + along with this program. If not, see . +*/ #include +#include "psppire-data-sheet.h" -#include "ui/gui/psppire-data-sheet.h" - -#include "data/case-map.h" -#include "data/casereader.h" -#include "data/casewriter.h" -#include "data/data-out.h" -#include "data/datasheet.h" -#include "data/format.h" -#include "data/value-labels.h" -#include "libpspp/intern.h" -#include "libpspp/range-set.h" -#include "ui/gui/executor.h" -#include "ui/gui/find-dialog.h" -#include "ui/gui/goto-case-dialog.h" -#include "ui/gui/builder-wrapper.h" -#include "ui/gui/helper.h" -#include "ui/gui/pspp-sheet-selection.h" -#include "ui/gui/psppire-cell-renderer-button.h" -#include "ui/gui/psppire-data-store.h" -#include "ui/gui/psppire-data-window.h" -#include "ui/gui/psppire-dialog-action-var-info.h" -#include "ui/gui/psppire-empty-list-store.h" -#include "ui/gui/psppire-marshal.h" - -#include "gl/intprops.h" -#include "gl/xalloc.h" - -#include -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid - -static void psppire_data_sheet_dispose (GObject *); -static void psppire_data_sheet_unset_data_store (PsppireDataSheet *); - -static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *); -static void psppire_data_sheet_update_primary_selection (PsppireDataSheet *, - gboolean should_own); -static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut); - -static void on_selection_changed (PsppSheetSelection *, gpointer); -static void on_owner_change (GtkClipboard *, GdkEventOwnerChange *, gpointer); -static void psppire_data_sheet_clip_received_cb (GtkClipboard *, - GtkSelectionData *, gpointer); - -G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW); - -static gboolean -get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip, - gint wx, gint wy, - size_t *row, PsppSheetViewColumn **columnp) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - gint bx, by; - GtkTreePath *path; - GtkTreeIter iter; - PsppSheetViewColumn *tree_column; - GtkTreeModel *tree_model; - bool ok; - - /* Check that WIDGET is really visible on the screen before we - do anything else. This is a bug fix for a sticky situation: - when text_data_import_assistant() returns, it frees the data - necessary to compose the tool tip message, but there may be - a tool tip under preparation at that point (even if there is - no visible tool tip) that will call back into us a little - bit later. Perhaps the correct solution to this problem is - to make the data related to the tool tips part of a GObject - that only gets destroyed when all references are released, - but this solution appears to be effective too. */ - if (!gtk_widget_get_mapped (widget)) - return FALSE; - - pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, - wx, wy, &bx, &by); - if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by, - &path, &tree_column, NULL, NULL)) - return FALSE; - - *columnp = tree_column; - - pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column, - NULL); - - tree_model = pspp_sheet_view_get_model (tree_view); - ok = gtk_tree_model_get_iter (tree_model, &iter, path); - gtk_tree_path_free (path); - if (!ok) - return FALSE; - - *row = GPOINTER_TO_INT (iter.user_data); - return TRUE; -} - -static gboolean -on_query_tooltip (GtkWidget *widget, gint wx, gint wy, - gboolean keyboard_mode UNUSED, - GtkTooltip *tooltip, gpointer data UNUSED) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column; - struct variable *var; - const char *label; - union value v; - size_t row; - int width; - - g_return_val_if_fail (data_store != NULL, FALSE); - g_return_val_if_fail (data_store->datasheet != NULL, FALSE); - - if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column)) - return FALSE; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var == NULL) - { - if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL) - return FALSE; - - gtk_tooltip_set_text (tooltip, - _("Enter a number to add a new variable.")); - return TRUE; - } - else if (row >= datasheet_get_n_rows (data_store->datasheet)) - { - gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case.")); - return TRUE; - } - - width = var_get_width (var); - - value_init (&v, width); - datasheet_get_value (data_store->datasheet, row, var_get_case_index (var), - &v); - - label = var_lookup_value_label (var, &v); - if (label != NULL) - { - if (data_sheet->show_value_labels) - { - char *s = value_to_text (v, var); - gtk_tooltip_set_text (tooltip, s); - free (s); - } - else - gtk_tooltip_set_text (tooltip, label); - } - value_destroy (&v, width); - - return label != NULL; -} - -static void -render_row_number_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer store_) -{ - PsppireDataStore *store = store_; - GValue gvalue = { 0, }; - gint row = GPOINTER_TO_INT (iter->user_data); - - g_return_if_fail (store->datasheet); - - g_value_init (&gvalue, G_TYPE_INT); - g_value_set_int (&gvalue, row + 1); - g_object_set_property (G_OBJECT (cell), "label", &gvalue); - g_value_unset (&gvalue); - - if (row < datasheet_get_n_rows (store->datasheet)) - g_object_set (cell, "editable", TRUE, NULL); - else - g_object_set (cell, "editable", FALSE, NULL); - - g_object_set (cell, - "slash", psppire_data_store_filtered (store, row), - NULL); -} - -static void -on_row_number_clicked (PsppireCellRendererButton *button, - gchar *path_string, - PsppSheetView *sheet_view) -{ - PsppSheetSelection *selection; - GtkTreePath *path; - - path = gtk_tree_path_new_from_string (path_string); - - selection = pspp_sheet_view_get_selection (sheet_view); - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - - gtk_tree_path_free (path); -} - -static void -make_row_number_column (PsppireDataSheet *data_sheet, - PsppireDataStore *ds) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetViewColumn *column; - GtkCellRenderer *renderer; - - renderer = psppire_cell_renderer_button_new (); - g_object_set (renderer, "xalign", 1.0, NULL); - g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked), - sheet_view); - - column = pspp_sheet_view_column_new_with_attributes (_("Case"), - renderer, NULL); - pspp_sheet_view_column_set_selectable (column, TRUE); - pspp_sheet_view_column_set_row_head (column, TRUE); - pspp_sheet_view_column_set_tabbable (column, FALSE); - pspp_sheet_view_column_set_clickable (column, TRUE); - pspp_sheet_view_column_set_cell_data_func ( - column, renderer, render_row_number_cell, ds, NULL); - pspp_sheet_view_column_set_fixed_width (column, 50); - pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers); - pspp_sheet_view_append_column (sheet_view, column); -} - -static void -render_data_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data_sheet_) -{ - PsppireDataSheet *data_sheet = data_sheet_; - PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet); - struct variable *var; - gchar *string; - gint row; - - double xalign; - - row = GPOINTER_TO_INT (iter->user_data); - var = g_object_get_data (G_OBJECT (tree_column), "variable"); - - string = psppire_data_store_get_string (store, row, var, - data_sheet->show_value_labels); - if (string != NULL) - { - GValue gvalue = { 0 }; - - g_value_init (&gvalue, G_TYPE_STRING); - g_value_take_string (&gvalue, string); - g_object_set_property (G_OBJECT (cell), "text", &gvalue); - g_value_unset (&gvalue); - } - else - g_object_set (G_OBJECT (cell), "text", "", NULL); - - switch (var_get_alignment (var)) - { - case ALIGN_LEFT: xalign = 0.0; break; - case ALIGN_RIGHT: xalign = 1.0; break; - case ALIGN_CENTRE: xalign = 0.5; break; - default: xalign = 0.0; break; - } - g_object_set (cell, - "xalign", xalign, - "editable", TRUE, - NULL); -} - -static gint -get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer, - const char *string) -{ - gint width; - g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL); - gtk_cell_renderer_get_preferred_width (renderer, GTK_WIDGET (treeview), - NULL, &width); - - return width; -} - -static gint -get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer, - size_t char_cnt) -{ - struct string s; - gint width; - - ds_init_empty (&s); - ds_put_byte_multiple (&s, '0', char_cnt); - ds_put_byte (&s, ' '); - width = get_string_width (treeview, renderer, ds_cstr (&s)); - ds_destroy (&s); - - return width; -} - -static void -on_data_column_editing_started (GtkCellRenderer *cell, - GtkCellEditable *editable, - const gchar *path, - gpointer user_data) -{ - PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column"); - PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet"); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - struct variable *var; - - g_return_if_fail (column); - g_return_if_fail (data_sheet); - g_return_if_fail (data_store); - - - g_object_ref (editable); - g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable", - editable, g_object_unref); - - var = g_object_get_data (G_OBJECT (column), "variable"); - g_return_if_fail (var); - - if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable)) - { - const struct val_labs *labels = var_get_value_labels (var); - const struct val_lab **vls = val_labs_sorted (labels); - size_t n_vls = val_labs_count (labels); - GtkListStore *list_store; - int i; - - list_store = gtk_list_store_new (1, G_TYPE_STRING); - for (i = 0; i < n_vls; ++i) - { - const struct val_lab *vl = vls[i]; - GtkTreeIter iter; - - gtk_list_store_append (list_store, &iter); - gtk_list_store_set (list_store, &iter, - 0, val_lab_get_label (vl), - -1); - } - free (vls); - - gtk_combo_box_set_model (GTK_COMBO_BOX (editable), - GTK_TREE_MODEL (list_store)); - g_object_unref (list_store); - } -} - -static void -scroll_to_bottom (GtkWidget *widget, - GtkRequisition *requisition, - gpointer unused UNUSED) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget); - GtkAdjustment *vadjust; - - vadjust = pspp_sheet_view_get_vadjustment (sheet_view); - gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust)); - - if (data_sheet->scroll_to_bottom_signal) - { - g_signal_handler_disconnect (data_sheet, - data_sheet->scroll_to_bottom_signal); - data_sheet->scroll_to_bottom_signal = 0; - } -} - -static void -on_data_column_edited (GtkCellRendererText *cell, - gchar *path_string, - gchar *new_text, - gpointer user_data) -{ - PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column"); - PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet"); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - GtkEditable *editable; - struct variable *var; - GtkTreePath *path; - gboolean is_val_lab; - gboolean new_row; - gint row; - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - var = g_object_get_data (G_OBJECT (column), "variable"); - - new_row = row == psppire_data_store_get_case_count (data_store); - if (new_row && new_text[0] == '\0') - return; - - editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable"); - g_return_if_fail (editable != NULL); - is_val_lab = (GTK_IS_COMBO_BOX (editable) - && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0); - g_object_unref (editable); - - psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab); - - if (new_row && !data_sheet->scroll_to_bottom_signal) - { - gtk_widget_queue_resize (GTK_WIDGET (data_sheet)); - data_sheet->scroll_to_bottom_signal = - g_signal_connect (data_sheet, "size-allocate", - G_CALLBACK (scroll_to_bottom), NULL); - } - else - { - /* We could be more specific about what to redraw, if it seems - important for performance. */ - gtk_widget_queue_draw (GTK_WIDGET (data_sheet)); - } -} - -static void -scroll_to_right (GtkWidget *widget, - PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetViewColumn *column, *prev; - GList *columns, *iter; - - column = NULL; - prev = NULL; - columns = pspp_sheet_view_get_columns (sheet_view); - for (iter = columns; iter; iter = iter->next) - { - PsppSheetViewColumn *c = iter->data; - if (g_object_get_data (G_OBJECT (c), "new-var-column")) - { - column = c; - break; - } - prev = c; - } - g_list_free (columns); - - if (column == NULL) - return; - - pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0); - - if (prev) - { - GtkTreePath *path; - - pspp_sheet_view_get_cursor (sheet_view, &path, NULL); - if (path) - { - pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE); - gtk_tree_path_free (path); - } - } - - if (data_sheet->scroll_to_right_signal) - { - g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal); - data_sheet->scroll_to_right_signal = 0; - } -} - -static void -on_new_variable_column_edited (GtkCellRendererText *cell, - gchar *path_string, - gchar *new_text, - gpointer user_data) -{ - PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet"); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppireDict *dict = data_store->dict; - struct variable *var; - GtkTreePath *path; - char name[64]; - gint row; - - if (new_text[0] == '\0') - { - /* User didn't enter anything so don't create a variable. */ - return; - } - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - if (!psppire_dict_generate_name (dict, name, sizeof name)) - return; - - var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict), - name); - g_return_if_fail (var != NULL); - - psppire_data_store_set_string (data_store, new_text, row, var, FALSE); - - if (!data_sheet->scroll_to_right_signal) - { - gtk_widget_queue_resize (GTK_WIDGET (data_sheet)); - data_sheet->scroll_to_right_signal = - g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize", - G_CALLBACK (scroll_to_right), data_sheet); - } - else - { - /* We could be more specific about what to redraw, if it seems - important for performance. */ - gtk_widget_queue_draw (GTK_WIDGET (data_sheet)); - } -} - -static void -calc_width_conversion (PsppireDataSheet *data_sheet, - gint *base_width, gint *incr_width) -{ - GtkCellRenderer *cell; - gint w1, w10; - - cell = gtk_cell_renderer_text_new (); - w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1); - w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10); - *incr_width = MAX (1, (w10 - w1) / 9); - *base_width = MAX (0, w10 - *incr_width * 10); - g_object_ref_sink (cell); - g_object_unref (cell); -} - -static gint -display_width_from_pixel_width (PsppireDataSheet *data_sheet, - gint pixel_width) -{ - gint base_width, incr_width; - - calc_width_conversion (data_sheet, &base_width, &incr_width); - return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1); -} - -static gint -display_width_to_pixel_width (PsppireDataSheet *data_sheet, - gint display_width, - gint base_width, - gint incr_width) -{ - return base_width + incr_width * display_width; -} - -static void -on_data_column_resized (GObject *gobject, - GParamSpec *pspec, - gpointer user_data) -{ - PsppireDataSheet *data_sheet = user_data; - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject); - struct variable *var; - gint pixel_width; - int display_width; - - if (data_store == NULL) - return; - - pixel_width = pspp_sheet_view_column_get_width (column); - if (pixel_width == pspp_sheet_view_column_get_fixed_width (column)) - { - /* Short-circuit the expensive display_width_from_pixel_width() - calculation, to make loading .sav files with 2000 columns visibly - faster. */ - return; - } - - var = g_object_get_data (G_OBJECT (column), "variable"); - display_width = display_width_from_pixel_width (data_sheet, pixel_width); - var_set_display_width (var, display_width); -} - -enum sort_order - { - SORT_ASCEND, - SORT_DESCEND - }; - -static void -do_sort (PsppireDataSheet *data_sheet, enum sort_order order) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDataWindow *pdw; - GList *list, *iter; - GString *syntax; - int n_vars; - - pdw = psppire_data_window_for_data_store (data_sheet->data_store); - g_return_if_fail (pdw != NULL); - - list = pspp_sheet_selection_get_selected_columns (selection); - - syntax = g_string_new ("SORT CASES BY"); - n_vars = 0; - for (iter = list; iter; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var != NULL) - { - g_string_append_printf (syntax, " %s", var_get_name (var)); - n_vars++; - } - } - if (n_vars > 0) - { - if (order == SORT_DESCEND) - g_string_append (syntax, " (DOWN)"); - g_string_append_c (syntax, '.'); - execute_const_syntax_string (pdw, syntax->str); - } - g_string_free (syntax, TRUE); -} - -static void -on_sort_up (PsppireDataSheet *data_sheet) -{ - do_sort (data_sheet, SORT_ASCEND); -} - -static void -on_sort_down (PsppireDataSheet *data_sheet) -{ - do_sort (data_sheet, SORT_DESCEND); -} - -static void -do_data_column_popup_menu (PsppSheetViewColumn *column, - guint button, guint32 time) -{ - GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column); - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); - - gtk_menu_popup (GTK_MENU (data_sheet->column_popup_menu), NULL, NULL, NULL, NULL, button, time); -} - -static void -on_data_column_popup_menu (PsppSheetViewColumn *column, - gpointer user_data UNUSED) -{ - do_data_column_popup_menu (column, 0, gtk_get_current_event_time ()); -} - -static gboolean -on_column_button_press_event (PsppSheetViewColumn *column, - GdkEventButton *event, - gpointer user_data UNUSED) -{ - PsppSheetSelection *selection; - PsppSheetView *sheet_view; - - sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view ( - column)); - g_return_val_if_fail (sheet_view != NULL, FALSE); - - selection = pspp_sheet_view_get_selection (sheet_view); - g_return_val_if_fail (selection != NULL, FALSE); - - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - do_data_column_popup_menu (column, event->button, event->time); - return TRUE; - } - else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) - { - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var != NULL) - { - gboolean handled; - - g_signal_emit_by_name (data_sheet, "var-double-clicked", - var_get_dict_index (var), &handled); - return handled; - } - } - - return FALSE; -} - -static gboolean -on_data_column_query_tooltip (PsppSheetViewColumn *column, - GtkTooltip *tooltip, - gpointer user_data UNUSED) -{ - struct variable *var; - const char *text; - - var = g_object_get_data (G_OBJECT (column), "variable"); - g_return_val_if_fail (var != NULL, FALSE); - - text = var_has_label (var) ? var_get_label (var) : var_get_name (var); - gtk_tooltip_set_text (tooltip, text); - - return TRUE; -} - -static void -add_data_column_cell_renderer (PsppireDataSheet *data_sheet, - PsppSheetViewColumn *column) -{ - GtkCellRenderer *cell; - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - g_return_if_fail (var != NULL); - - if (data_sheet->show_value_labels && var_has_value_labels (var)) - { - cell = gtk_cell_renderer_combo_new (); - g_object_set (G_OBJECT (cell), - "has-entry", TRUE, - "text-column", 0, - NULL); - } - else - cell = gtk_cell_renderer_text_new (); - - g_signal_connect (cell, "editing-started", - G_CALLBACK (on_data_column_editing_started), NULL); - g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL); - - g_object_set_data (G_OBJECT (cell), "column", column); - g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet); - - pspp_sheet_view_column_clear (column); - pspp_sheet_view_column_pack_start (column, cell, TRUE); - - pspp_sheet_view_column_set_cell_data_func ( - column, cell, render_data_cell, data_sheet, NULL); -} - -static PsppSheetViewColumn * -make_data_column (PsppireDataSheet *data_sheet, gint dict_idx, - gint base_width, gint incr_width) -{ - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - struct variable *var; - PsppSheetViewColumn *column; - char *name; - int width; - - var = psppire_dict_get_variable (data_store->dict, dict_idx); - - column = pspp_sheet_view_column_new (); - - name = escape_underscores (var_get_name (var)); - pspp_sheet_view_column_set_title (column, name); - free (name); - - g_object_set_data (G_OBJECT (column), "variable", var); - - width = display_width_to_pixel_width (data_sheet, - var_get_display_width (var), - base_width, incr_width); - pspp_sheet_view_column_set_min_width (column, 10); - pspp_sheet_view_column_set_fixed_width (column, width); - pspp_sheet_view_column_set_resizable (column, TRUE); - - pspp_sheet_view_column_set_clickable (column, TRUE); - g_signal_connect (column, "notify::width", - G_CALLBACK (on_data_column_resized), data_sheet); - - g_signal_connect (column, "button-press-event", - G_CALLBACK (on_column_button_press_event), - data_sheet); - g_signal_connect (column, "query-tooltip", - G_CALLBACK (on_data_column_query_tooltip), NULL); - g_signal_connect (column, "popup-menu", - G_CALLBACK (on_data_column_popup_menu), data_sheet); - - add_data_column_cell_renderer (data_sheet, column); - - return column; -} - -static void -make_new_variable_column (PsppireDataSheet *data_sheet, - gint base_width, gint incr_width) -{ - PsppSheetViewColumn *column; - GtkCellRenderer *cell; - int width; - - cell = gtk_cell_renderer_text_new (); - g_object_set (cell, "editable", TRUE, NULL); - - g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited), - NULL); - - column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL); - g_object_set_data (G_OBJECT (column), "new-var-column", column); - - width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width); - pspp_sheet_view_column_set_min_width (column, 10); - pspp_sheet_view_column_set_fixed_width (column, width); - pspp_sheet_view_column_set_tabbable (column, FALSE); - - g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet); - g_signal_connect (column, "button-press-event", - G_CALLBACK (on_column_button_press_event), - data_sheet); - g_signal_connect (column, "popup-menu", - G_CALLBACK (on_data_column_popup_menu), data_sheet); - - pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars); - - pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column); - data_sheet->new_variable_column = column; -} - -static void -psppire_data_sheet_model_changed (GObject *gobject, - GParamSpec *pspec, - gpointer user_data) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject); - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *data_store; - - /* Remove old columns. */ - for (;;) - { - PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0); - if (column == NULL) - break; - - pspp_sheet_view_remove_column (sheet_view, column); - } - data_sheet->new_variable_column = NULL; - - if (pspp_sheet_view_get_model (sheet_view) == NULL) - { - /* Don't create any columns at all if there's no model. Otherwise we'll - create some columns as part of the "dispose" callback for the sheet - view, which sets the model to NULL. That causes warnings to be - logged and is obviously undesirable in any case. */ - return; - } - - /* Add new columns. */ - data_store = psppire_data_sheet_get_data_store (data_sheet); - if (data_store != NULL) - { - gint base_width, incr_width; - int i; - - calc_width_conversion (data_sheet, &base_width, &incr_width); - - make_row_number_column (data_sheet, data_store); - for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++) - { - PsppSheetViewColumn *column; - - column = make_data_column (data_sheet, i, base_width, incr_width); - pspp_sheet_view_append_column (sheet_view, column); - } - make_new_variable_column (data_sheet, base_width, incr_width); - } -} - -enum - { - PROP_0, - PROP_DATA_STORE, - PROP_VALUE_LABELS, - PROP_CASE_NUMBERS, - PROP_CURRENT_CASE, - PROP_MAY_CREATE_VARS, - PROP_MAY_DELETE_VARS - }; - -static void -psppire_data_sheet_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object); - - switch (prop_id) - { - case PROP_DATA_STORE: - psppire_data_sheet_set_data_store ( - obj, PSPPIRE_DATA_STORE (g_value_get_object (value))); - break; - - case PROP_VALUE_LABELS: - psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value)); - break; - - case PROP_CASE_NUMBERS: - psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value)); - break; - - case PROP_CURRENT_CASE: - psppire_data_sheet_goto_case (obj, g_value_get_long (value)); - break; - - case PROP_MAY_CREATE_VARS: - psppire_data_sheet_set_may_create_vars (obj, - g_value_get_boolean (value)); - break; - - case PROP_MAY_DELETE_VARS: - psppire_data_sheet_set_may_delete_vars (obj, - g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_data_sheet_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object); - - switch (prop_id) - { - case PROP_DATA_STORE: - g_value_set_object (value, psppire_data_sheet_get_data_store (obj)); - break; - - case PROP_VALUE_LABELS: - g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj)); - break; - - case PROP_CASE_NUMBERS: - g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj)); - break; - - case PROP_CURRENT_CASE: - g_value_set_long (value, psppire_data_sheet_get_selected_case (obj)); - break; - - case PROP_MAY_CREATE_VARS: - g_value_set_boolean (value, obj->may_create_vars); - break; - - case PROP_MAY_DELETE_VARS: - g_value_set_boolean (value, obj->may_delete_vars); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -gboolean -psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds) -{ - return ds->show_value_labels; -} - -void -psppire_data_sheet_set_value_labels (PsppireDataSheet *ds, - gboolean show_value_labels) -{ - show_value_labels = !!show_value_labels; - if (show_value_labels != ds->show_value_labels) - { - ds->show_value_labels = show_value_labels; - g_object_notify (G_OBJECT (ds), "value-labels"); - - /* Pretend the model changed, to force the columns to be rebuilt. - Otherwise cell renderers won't get changed from combo boxes to text - entries or vice versa. */ - g_object_notify (G_OBJECT (ds), "model"); - } -} - -gboolean -psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds) -{ - return ds->show_case_numbers; -} - -void -psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds, - gboolean show_case_numbers) -{ - show_case_numbers = !!show_case_numbers; - if (show_case_numbers != ds->show_case_numbers) - { - PsppSheetViewColumn *column; - - ds->show_case_numbers = show_case_numbers; - column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0); - if (column) - pspp_sheet_view_column_set_visible (column, show_case_numbers); - - g_object_notify (G_OBJECT (ds), "case-numbers"); - gtk_widget_queue_draw (GTK_WIDGET (ds)); - } -} - -gboolean -psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet) -{ - return data_sheet->may_create_vars; -} - -void -psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet, - gboolean may_create_vars) -{ - if (data_sheet->may_create_vars != may_create_vars) - { - data_sheet->may_create_vars = may_create_vars; - if (data_sheet->new_variable_column) - pspp_sheet_view_column_set_visible (data_sheet->new_variable_column, - may_create_vars); - - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (data_sheet)), NULL); - } -} - -gboolean -psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet) -{ - return data_sheet->may_delete_vars; -} - -void -psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet, - gboolean may_delete_vars) -{ - if (data_sheet->may_delete_vars != may_delete_vars) - { - data_sheet->may_delete_vars = may_delete_vars; - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (data_sheet)), NULL); - } -} - -static PsppSheetViewColumn * -psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet, - gint dict_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *data_store; - PsppSheetViewColumn *column; - struct variable *var; - GList *list, *iter; - - data_store = psppire_data_sheet_get_data_store (data_sheet); - g_return_val_if_fail (data_store != NULL, NULL); - g_return_val_if_fail (data_store->dict != NULL, NULL); - - var = psppire_dict_get_variable (data_store->dict, dict_index); - g_return_val_if_fail (var != NULL, NULL); - - column = NULL; - list = pspp_sheet_view_get_columns (sheet_view); - for (iter = list; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *c = iter->data; - struct variable *v; - - v = g_object_get_data (G_OBJECT (c), "variable"); - if (v == var) - { - column = c; - break; - } - } - g_list_free (list); - - return column; -} - -void -psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet, - gint dict_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetViewColumn *column; - - column = psppire_data_sheet_find_column_for_variable (data_sheet, - dict_index); - if (column != NULL) - { - GtkTreePath *path; - - gint row = psppire_data_sheet_get_current_case (data_sheet); - path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1); - - pspp_sheet_view_scroll_to_cell (sheet_view, path, column, - FALSE, 0.0, 0.0); - pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE); - gtk_tree_path_free (path); - } -} - -struct variable * -psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet) -{ - PsppSheetSelection *selection; - struct variable *var; - GList *selected_columns; - GList *iter; - - selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet)); - selected_columns = pspp_sheet_selection_get_selected_columns (selection); - - var = NULL; - for (iter = selected_columns; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *v = g_object_get_data (G_OBJECT (column), "variable"); - if (v != NULL) - { - if (var) - { - var = NULL; - break; - } - else - var = v; - } - } - - g_list_free (selected_columns); - - return var; - -} -void -psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *store = data_sheet->data_store; - PsppSheetSelection *selection; - GtkTreePath *path; - - g_return_if_fail (case_index >= 0); - g_return_if_fail (case_index < psppire_data_store_get_case_count (store)); - - path = gtk_tree_path_new_from_indices (case_index, -1); - - /* Select the case. */ - selection = pspp_sheet_view_get_selection (sheet_view); - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - - /* Scroll so that the case is visible. */ - pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0); - - gtk_tree_path_free (path); -} - -/* Returns the 0-based index of a selected case, if there is at least one, and - -1 otherwise. - - If more than one case is selected, returns the one with the smallest index, - that is, the index of the case closest to the beginning of the file. The - row that can be used to insert a new case is not considered a case. */ -gint -psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *store = data_sheet->data_store; - const struct range_set_node *node; - PsppSheetSelection *selection; - struct range_set *rows; - gint row; - - selection = pspp_sheet_view_get_selection (sheet_view); - rows = pspp_sheet_selection_get_range_set (selection); - node = range_set_first (rows); - row = (node && node->start < psppire_data_store_get_case_count (store) - ? node->start - : -1); - range_set_destroy (rows); - - return row; -} - -/* Returns the 0-based index of a selected case, if exactly one case is - selected, and -1 otherwise. Returns -1 if the row that can be used to - insert a new case is selected. */ -gint -psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *store = data_sheet->data_store; - const struct range_set_node *node; - PsppSheetSelection *selection; - struct range_set *rows; - gint row; - - selection = pspp_sheet_view_get_selection (sheet_view); - if (pspp_sheet_selection_count_selected_rows (selection) != 1) - return -1; - - rows = pspp_sheet_selection_get_range_set (selection); - node = range_set_first (rows); - row = (node && node->start < psppire_data_store_get_case_count (store) - ? node->start - : -1); - range_set_destroy (rows); - - return row; -} - - -static void -psppire_data_sheet_dispose (GObject *object) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object); - - if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0) - { - g_signal_handler_disconnect (data_sheet->clip, - data_sheet->on_owner_change_signal); - data_sheet->on_owner_change_signal = 0; - } - - if (data_sheet->dispose_has_run) - return; - - data_sheet->dispose_has_run = TRUE; - - psppire_data_sheet_unset_data_store (data_sheet); - - G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object); -} - -static void -psppire_data_sheet_map (GtkWidget *widget) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - - GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget); - - data_sheet->clip = gtk_widget_get_clipboard (widget, - GDK_SELECTION_CLIPBOARD); - if (data_sheet->on_owner_change_signal) - g_signal_handler_disconnect (data_sheet->clip, - data_sheet->on_owner_change_signal); - data_sheet->on_owner_change_signal - = g_signal_connect (data_sheet->clip, "owner-change", - G_CALLBACK (on_owner_change), widget); - on_owner_change (data_sheet->clip, NULL, widget); -} - -static void -psppire_data_sheet_class_init (PsppireDataSheetClass *class) -{ - GObjectClass *gobject_class; - GtkWidgetClass *widget_class; - - gobject_class = G_OBJECT_CLASS (class); - gobject_class->set_property = psppire_data_sheet_set_property; - gobject_class->get_property = psppire_data_sheet_get_property; - gobject_class->dispose = psppire_data_sheet_dispose; - - widget_class = GTK_WIDGET_CLASS (class); - widget_class->map = psppire_data_sheet_map; - - g_signal_new ("var-double-clicked", - G_OBJECT_CLASS_TYPE (gobject_class), - G_SIGNAL_RUN_LAST, - 0, - g_signal_accumulator_true_handled, NULL, - psppire_marshal_BOOLEAN__INT, - G_TYPE_BOOLEAN, 1, G_TYPE_INT); - - g_object_class_install_property ( - gobject_class, PROP_DATA_STORE, - g_param_spec_object ("data-store", - "Data Store", - "The data store for the data sheet to display.", - PSPPIRE_TYPE_DATA_STORE, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, PROP_VALUE_LABELS, - g_param_spec_boolean ("value-labels", - "Value Labels", - "Whether or not the data sheet should display labels instead of values", - FALSE, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, PROP_CASE_NUMBERS, - g_param_spec_boolean ("case-numbers", - "Case Numbers", - "Whether or not the data sheet should display case numbers", - FALSE, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, - PROP_CURRENT_CASE, - g_param_spec_long ("current-case", - "Current Case", - "Zero based number of the selected case", - 0, CASENUMBER_MAX, - 0, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, - PROP_MAY_CREATE_VARS, - g_param_spec_boolean ("may-create-vars", - "May create variables", - "Whether the user may create more variables", - TRUE, - G_PARAM_READWRITE)); - - - g_object_class_install_property ( - gobject_class, - PROP_MAY_DELETE_VARS, - g_param_spec_boolean ("may-delete-vars", - "May delete variables", - "Whether the user may delete variables", - TRUE, - G_PARAM_READWRITE)); -} - -static void -do_row_popup_menu (GtkWidget *widget, guint button, guint32 time) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - - - gtk_menu_popup (GTK_MENU (data_sheet->row_popup_menu), NULL, NULL, NULL, NULL, button, time); -} - -static void -on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED) -{ - do_row_popup_menu (widget, 0, gtk_get_current_event_time ()); -} - -static gboolean -on_button_pressed (GtkWidget *widget, GdkEventButton *event, - gpointer user_data UNUSED) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget); - - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - PsppSheetSelection *selection; - - selection = pspp_sheet_view_get_selection (sheet_view); - if (pspp_sheet_selection_count_selected_rows (selection) <= 1) - { - GtkTreePath *path; - - if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y, - &path, NULL, NULL, NULL)) - { - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - gtk_tree_path_free (path); - } - } - - do_row_popup_menu (widget, event->button, event->time); - - return TRUE; - } - - return FALSE; -} +#include +#define _(msgid) gettext (msgid) +#define P_(X) (X) -void -psppire_data_sheet_edit_clear_cases (PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - const struct range_set_node *node; - struct range_set *selected; - - selected = pspp_sheet_selection_get_range_set (selection); - for (node = range_set_last (selected); node != NULL; - node = range_set_prev (selected, node)) - { - unsigned long int start = range_set_node_get_start (node); - unsigned long int count = range_set_node_get_width (node); +#include "value-variant.h" - psppire_data_store_delete_cases (data_sheet->data_store, start, count); - } - range_set_destroy (selected); -} +#include "ui/gui/executor.h" +#include "psppire-data-window.h" static void -on_selection_changed (PsppSheetSelection *selection, - gpointer user_data UNUSED) +do_sort (PsppireDataSheet *sheet, GtkSortType order) { - PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection); - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); - gboolean any_variables_selected; - gboolean may_delete_cases, may_delete_vars, may_insert_vars; - GList *list, *iter; - GtkTreePath *path; - - GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)); - if (! PSPPIRE_IS_DATA_WINDOW (top)) - return; - - PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top); - - gint n_selected_rows = pspp_sheet_selection_count_selected_rows (selection); + SswRange *range = SSW_SHEET(sheet)->selection; - gtk_widget_set_sensitive (dw->mi_insert_case, n_selected_rows > 0); + PsppireDataStore *data_store = NULL; + g_object_get (sheet, "data-model", &data_store, NULL); - switch (n_selected_rows) - { - case 0: - may_delete_cases = FALSE; - break; - - case 1: - /* The row used for inserting new cases cannot be deleted. */ - path = gtk_tree_path_new_from_indices ( - psppire_data_store_get_case_count (data_sheet->data_store), -1); - may_delete_cases = !pspp_sheet_selection_path_is_selected (selection, - path); - gtk_tree_path_free (path); - break; - - default: - may_delete_cases = TRUE; - break; - } + int n_vars = 0; + int i; - gtk_widget_set_sensitive (dw->mi_clear_cases, may_delete_cases); + PsppireDataWindow *pdw = + psppire_data_window_for_data_store (data_store); - any_variables_selected = FALSE; - may_delete_vars = may_insert_vars = FALSE; - list = pspp_sheet_selection_get_selected_columns (selection); - - for (iter = list; iter != NULL; iter = iter->next) + GString *syntax = g_string_new ("SORT CASES BY"); + for (i = range->start_x ; i <= range->end_x; ++i) { - PsppSheetViewColumn *column = iter->data; - struct variable *var = g_object_get_data (G_OBJECT (column), "variable"); - + const struct variable *var = psppire_dict_get_variable (data_store->dict, i); if (var != NULL) { - may_delete_vars = may_insert_vars = TRUE; - any_variables_selected = TRUE; - break; + g_string_append_printf (syntax, " %s", var_get_name (var)); + n_vars++; } - if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL) - may_insert_vars = TRUE; - } - g_list_free (list); - - may_insert_vars = may_insert_vars && data_sheet->may_create_vars; - may_delete_vars = may_delete_vars && data_sheet->may_delete_vars; - - gtk_widget_set_sensitive (dw->mi_insert_var, may_insert_vars); - gtk_widget_set_sensitive (dw->mi_clear_variables, may_delete_vars); - gtk_widget_set_sensitive (data_sheet->pu_sort_up, may_delete_vars); - gtk_widget_set_sensitive (data_sheet->pu_sort_down, may_delete_vars); - - psppire_data_sheet_update_clip_actions (data_sheet); - psppire_data_sheet_update_primary_selection (data_sheet, - (n_selected_rows > 0 - && any_variables_selected)); -} - -static gboolean -psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet, - struct range_set **rowsp, - struct range_set **colsp) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *data_store = data_sheet->data_store; - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - unsigned long n_cases; - struct range_set *rows, *cols; - GList *list, *iter; - - if (data_store == NULL) - return FALSE; - n_cases = psppire_data_store_get_case_count (data_store); - - rows = pspp_sheet_selection_get_range_set (selection); - range_set_set0 (rows, n_cases, ULONG_MAX - n_cases); - if (range_set_is_empty (rows)) - { - range_set_destroy (rows); - return FALSE; - } - - cols = range_set_create (); - list = pspp_sheet_selection_get_selected_columns (selection); - for (iter = list; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var = g_object_get_data (G_OBJECT (column), "variable"); - - if (var != NULL) - range_set_set1 (cols, var_get_dict_index (var), 1); } - g_list_free (list); - if (range_set_is_empty (cols)) + if (n_vars > 0) { - range_set_destroy (rows); - range_set_destroy (cols); - return FALSE; + if (order == GTK_SORT_DESCENDING) + g_string_append (syntax, " (DOWN)"); + g_string_append_c (syntax, '.'); + execute_const_syntax_string (pdw, syntax->str); } - - *rowsp = rows; - *colsp = cols; - return TRUE; -} - -/* Insert a case at the selected row */ -void -psppire_data_sheet_insert_case (PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDataStore *data_store = data_sheet->data_store; - struct range_set *selected = pspp_sheet_selection_get_range_set (selection); - unsigned long row = range_set_scan (selected, 0); - range_set_destroy (selected); - - if (row <= psppire_data_store_get_case_count (data_store)) - psppire_data_store_insert_new_case (data_store, row); + g_string_free (syntax, TRUE); } -void -psppire_data_sheet_insert_variable (PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = data_sheet->data_store->dict; - PsppSheetViewColumn *column; - struct variable *var; - gchar name[64]; - GList *list; - gint index; - - list = pspp_sheet_selection_get_selected_columns (selection); - if (list == NULL) - return; - column = list->data; - g_list_free (list); - - var = g_object_get_data (G_OBJECT (column), "variable"); - index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict); - if (psppire_dict_generate_name (dict, name, sizeof name)) - psppire_dict_insert_variable (dict, index, name); -} -void -psppire_data_sheet_edit_clear_variables (PsppireDataSheet *data_sheet) +static void +sort_ascending (PsppireDataSheet *sheet) { - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = data_sheet->data_store->dict; - GList *iter; - GList *list = pspp_sheet_selection_get_selected_columns (selection); + do_sort (sheet, GTK_SORT_ASCENDING); - if (list == NULL) - return; - list = g_list_reverse (list); - for (iter = list; iter; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var = g_object_get_data (G_OBJECT (column), "variable"); - if (var != NULL) - psppire_dict_delete_variables (dict, var_get_dict_index (var), 1); - } - g_list_free (list); + gtk_widget_queue_draw (GTK_WIDGET (sheet)); } -void -psppire_data_sheet_edit_copy (PsppireDataSheet *data_sheet) +static void +sort_descending (PsppireDataSheet *sheet) { - psppire_data_sheet_set_clip (data_sheet, FALSE); -} + do_sort (sheet, GTK_SORT_DESCENDING); -void -psppire_data_sheet_edit_cut (PsppireDataSheet *data_sheet) -{ - psppire_data_sheet_set_clip (data_sheet, TRUE); + gtk_widget_queue_draw (GTK_WIDGET (sheet)); } -void -psppire_data_sheet_edit_paste (PsppireDataSheet *data_sheet) -{ - GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet)); - GtkClipboard *clipboard = - gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD); - - gtk_clipboard_request_contents (clipboard, - gdk_atom_intern ("UTF8_STRING", TRUE), - psppire_data_sheet_clip_received_cb, - data_sheet); -} + static void -psppire_data_sheet_init (PsppireDataSheet *obj) +change_data_value (PsppireDataSheet *sheet, gint col, gint row, GValue *value) { - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj); - - obj->show_value_labels = FALSE; - obj->show_case_numbers = TRUE; - obj->may_create_vars = TRUE; - obj->may_delete_vars = TRUE; - - obj->owns_primary_selection = FALSE; - - obj->scroll_to_bottom_signal = 0; - obj->scroll_to_right_signal = 0; - obj->on_owner_change_signal = 0; - obj->new_variable_column = NULL; - obj->container = NULL; - - obj->dispose_has_run = FALSE; - - pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES); - - { - obj->row_popup_menu = gtk_menu_new (); - int i = 0; - - GtkWidget *insert_case = gtk_menu_item_new_with_mnemonic (_("_Insert Case")); - GtkWidget *clear_cases = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases")); - - gtk_menu_attach (GTK_MENU (obj->row_popup_menu), insert_case, 0, 1, i, i + 1); ++i; - gtk_menu_attach (GTK_MENU (obj->row_popup_menu), clear_cases, 0, 1, i, i + 1); ++i; - - g_signal_connect_swapped (clear_cases, "activate", G_CALLBACK (psppire_data_sheet_edit_clear_cases), obj); - g_signal_connect_swapped (insert_case, "activate", G_CALLBACK (psppire_data_sheet_insert_case), obj); - - gtk_widget_show_all (obj->row_popup_menu); - } + PsppireDataStore *store = NULL; + g_object_get (sheet, "data-model", &store, NULL); - { - obj->column_popup_menu = gtk_menu_new (); - int i = 0; - - GtkWidget *insert_variable = gtk_menu_item_new_with_mnemonic (_("_Insert Variable")); - GtkWidget *clear_variables = gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables")); - obj->pu_sort_up = gtk_menu_item_new_with_mnemonic (_("Sort _Ascending")); - obj->pu_sort_down = gtk_menu_item_new_with_mnemonic (_("Sort _Descending")); - - g_signal_connect_swapped (clear_variables, "activate", G_CALLBACK (psppire_data_sheet_edit_clear_variables), obj); - g_signal_connect_swapped (insert_variable, "activate", G_CALLBACK (psppire_data_sheet_insert_variable), obj); - - g_signal_connect_swapped (obj->pu_sort_up, "activate", G_CALLBACK (on_sort_up), obj); - g_signal_connect_swapped (obj->pu_sort_down, "activate", G_CALLBACK (on_sort_down), obj); - - gtk_menu_attach (GTK_MENU (obj->column_popup_menu), insert_variable, 0, 1, i, i + 1); ++i; - gtk_menu_attach (GTK_MENU (obj->column_popup_menu), clear_variables, 0, 1, i, i + 1); ++i; - - gtk_menu_attach (GTK_MENU (obj->column_popup_menu), gtk_separator_menu_item_new (), 0, 1, i, i + 1); ++i; - - gtk_menu_attach (GTK_MENU (obj->column_popup_menu), obj->pu_sort_up, 0, 1, i, i + 1); ++i; - gtk_menu_attach (GTK_MENU (obj->column_popup_menu), obj->pu_sort_down, 0, 1, i, i + 1); ++i; - - gtk_widget_show_all (obj->column_popup_menu); - } + const struct variable *var = psppire_dict_get_variable (store->dict, col); + if (NULL == var) + return; - g_signal_connect (obj, "notify::model", - G_CALLBACK (psppire_data_sheet_model_changed), NULL); + union value v; - pspp_sheet_view_set_rubber_banding (sheet_view, TRUE); - pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view), - PSPP_SHEET_SELECTION_RECTANGLE); + GVariant *vrnt = g_value_get_variant (value); - g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL); - g_signal_connect (obj, "query-tooltip", - G_CALLBACK (on_query_tooltip), NULL); - g_signal_connect (obj, "button-press-event", - G_CALLBACK (on_button_pressed), NULL); + value_variant_get (&v, vrnt); - g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL); + psppire_data_store_set_value (store, row, var, &v); - g_signal_connect (pspp_sheet_view_get_selection (sheet_view), - "changed", G_CALLBACK (on_selection_changed), NULL); + value_destroy_from_variant (&v, vrnt); } -GtkWidget * -psppire_data_sheet_new (void) -{ - return g_object_new (PSPP_TYPE_DATA_SHEET, NULL); -} +gboolean myreversefunc (GtkTreeModel *model, gint col, gint row, const gchar *in, + GValue *out); -PsppireDataStore * -psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet) -{ - return data_sheet->data_store; -} + static void -refresh_model (PsppireDataSheet *data_sheet) +show_cases_row_popup (PsppireDataSheet *sheet, int row, + uint button, uint state, gpointer p) { - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL); + GListModel *vmodel = NULL; + g_object_get (sheet, "vmodel", &vmodel, NULL); + if (vmodel == NULL) + return; - if (data_sheet->data_store != NULL) - { - int n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1; - PsppireEmptyListStore *model = psppire_empty_list_store_new (n_rows); - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), - GTK_TREE_MODEL (model)); - g_object_unref (model); - } -} + guint n_items = g_list_model_get_n_items (vmodel); -static void -on_case_inserted (PsppireDataStore *data_store, gint row, - PsppireDataSheet *data_sheet) -{ - PsppireEmptyListStore *empty_list_store; - GtkTreeModel *tree_model; - gint n_rows; + if (row >= n_items) + return; - g_return_if_fail (data_store == data_sheet->data_store); + if (button != 3) + return; - n_rows = psppire_data_store_get_case_count (data_store) + 1; - if (row == n_rows - 1) - row++; + g_object_set_data (G_OBJECT (sheet->data_sheet_cases_row_popup), "item", + GINT_TO_POINTER (row)); - tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet)); - empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model); - psppire_empty_list_store_set_n_rows (empty_list_store, n_rows); - psppire_empty_list_store_row_inserted (empty_list_store, row); + gtk_menu_popup_at_pointer (GTK_MENU (sheet->data_sheet_cases_row_popup), NULL); } -static void -on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases, - PsppireDataSheet *data_sheet) -{ - - g_return_if_fail (data_store == data_sheet->data_store); - - if (n_cases > 1) - { - /* This is a bit of a cop-out. We could do better, if it ever turns out - that this performs too poorly. */ - refresh_model (data_sheet); - } - else - { - PsppireEmptyListStore *empty_list_store; - GtkTreeModel *tree_model; - gint n_rows = psppire_data_store_get_case_count (data_store) + 1; - - tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet)); - empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model); - psppire_empty_list_store_set_n_rows (empty_list_store, n_rows); - psppire_empty_list_store_row_deleted (empty_list_store, first); - } -} static void -on_case_change (PsppireDataStore *data_store, gint row, - PsppireDataSheet *data_sheet) +insert_new_case (PsppireDataSheet *sheet) { - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); + PsppireDataStore *data_store = NULL; + g_object_get (sheet, "data-model", &data_store, NULL); - pspp_sheet_view_stop_editing (sheet_view, TRUE); - gtk_widget_queue_draw (GTK_WIDGET (data_sheet)); -} + gint posn = GPOINTER_TO_INT (g_object_get_data + (G_OBJECT (sheet->data_sheet_cases_row_popup), "item")); -static void -on_backend_changed (PsppireDataStore *data_store, - PsppireDataSheet *data_sheet) -{ - g_return_if_fail (data_store == data_sheet->data_store); - refresh_model (data_sheet); + psppire_data_store_insert_new_case (data_store, posn); + + gtk_widget_queue_draw (GTK_WIDGET (sheet)); } static void -on_variable_display_width_changed (PsppireDict *dict, int dict_index, - PsppireDataSheet *data_sheet) +delete_cases (PsppireDataSheet *sheet) { - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column; - struct variable *var; - int display_width; - gint pixel_width; - - g_return_if_fail (data_sheet->data_store != NULL); - g_return_if_fail (dict == data_sheet->data_store->dict); - - column = psppire_data_sheet_find_column_for_variable (data_sheet, - dict_index); - if (column == NULL) - return; + SswRange *range = SSW_SHEET(sheet)->selection; - var = psppire_dict_get_variable (data_store->dict, dict_index); - g_return_if_fail (var != NULL); + PsppireDataStore *data_store = NULL; + g_object_get (sheet, "data-model", &data_store, NULL); - pixel_width = pspp_sheet_view_column_get_fixed_width (column); - display_width = display_width_from_pixel_width (data_sheet, pixel_width); - if (display_width != var_get_display_width (var)) - { - gint base_width, incr_width; + psppire_data_store_delete_cases (data_store, range->start_y, + range->end_y - range->start_y + 1); - display_width = var_get_display_width (var); - calc_width_conversion (data_sheet, &base_width, &incr_width); - pixel_width = display_width_to_pixel_width (data_sheet, display_width, - base_width, incr_width); - pspp_sheet_view_column_set_fixed_width (column, pixel_width); - } + gtk_widget_queue_draw (GTK_WIDGET (sheet)); } -static void -on_variable_changed (PsppireDict *dict, int dict_index, - guint what, const struct variable *oldvar, - PsppireDataSheet *data_sheet) +static GtkWidget * +create_data_row_header_popup_menu (PsppireDataSheet *sheet) { - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column; - GtkCellRenderer *cell; - struct variable *var; - GList *cells; - char *name; + GtkWidget *menu = gtk_menu_new (); - g_return_if_fail (data_sheet->data_store != NULL); - g_return_if_fail (dict == data_sheet->data_store->dict); + GtkWidget *item = + gtk_menu_item_new_with_mnemonic (_("_Insert Case")); + g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_case), sheet); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - if (what & VAR_TRAIT_DISPLAY_WIDTH) - on_variable_display_width_changed (dict, dict_index, data_sheet); - - column = psppire_data_sheet_find_column_for_variable (data_sheet, - dict_index); - if (column == NULL) - return; - - - var = psppire_dict_get_variable (data_store->dict, dict_index); - g_return_if_fail (var != NULL); - - name = escape_underscores (var_get_name (var)); - if (strcmp (name, pspp_sheet_view_column_get_title (column))) - pspp_sheet_view_column_set_title (column, name); - free (name); - - cells = pspp_sheet_view_column_get_cell_renderers (column); - g_return_if_fail (cells); - cell = cells->data; - g_list_free (cells); - - if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell)) - { - /* Stop editing before we delete and replace the cell renderers. - Otherwise if this column is currently being edited, an eventual call - to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass - that to gtk_cell_renderer_stop_editing(), which causes a critical. + item = gtk_separator_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - It's possible that this is a bug in PsppSheetView, and it's possible - that PsppSheetView inherits that from GtkTreeView, but I haven't - investigated yet. */ - pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE); + sheet->data_clear_cases_menu_item = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases")); + gtk_widget_set_sensitive (sheet->data_clear_cases_menu_item, FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_clear_cases_menu_item); + g_signal_connect_swapped (sheet->data_clear_cases_menu_item, "activate", + G_CALLBACK (delete_cases), sheet); - add_data_column_cell_renderer (data_sheet, column); - } + gtk_widget_show_all (menu); + return menu; } -static void -on_variable_inserted (PsppireDict *dict, int var_index, - PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - gint base_width, incr_width; - PsppSheetViewColumn *column; - - calc_width_conversion (data_sheet, &base_width, &incr_width); - column = make_data_column (data_sheet, var_index, base_width, incr_width); - pspp_sheet_view_insert_column (sheet_view, column, var_index + 1); -} static void -on_variable_deleted (PsppireDict *dict, - const struct variable *var, int case_idx, int width, - PsppireDataSheet *data_sheet) +show_cases_column_popup (PsppireDataSheet *sheet, int column, uint button, uint state, + gpointer p) { - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - GList *columns, *iter; - - columns = pspp_sheet_view_get_columns (sheet_view); - for (iter = columns; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - const struct variable *column_var; + GListModel *hmodel = NULL; + g_object_get (sheet, "hmodel", &hmodel, NULL); + if (hmodel == NULL) + return; - column_var = g_object_get_data (G_OBJECT (column), "variable"); - if (column_var == var) - pspp_sheet_view_remove_column (sheet_view, column); - } - g_list_free (columns); -} + guint n_items = g_list_model_get_n_items (hmodel); -static void -psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet) -{ - PsppireDataStore *store = data_sheet->data_store; + if (column >= n_items) + return; - if (store == NULL) + if (button != 3) return; - data_sheet->data_store = NULL; - - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_backend_changed), data_sheet); - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_case_inserted), data_sheet); - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_cases_deleted), data_sheet); - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_case_change), data_sheet); - - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_changed), data_sheet); - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet); - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_inserted), data_sheet); - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_deleted), data_sheet); - - g_object_unref (store); + g_object_set_data (G_OBJECT (sheet->data_sheet_cases_column_popup), "item", + GINT_TO_POINTER (column)); + + gtk_menu_popup_at_pointer (GTK_MENU (sheet->data_sheet_cases_column_popup), NULL); } -void -psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet, - PsppireDataStore *data_store) +static void +insert_new_variable (PsppireDataSheet *sheet) { - psppire_data_sheet_unset_data_store (data_sheet); + PsppireDataStore *data_store = NULL; + g_object_get (sheet, "data-model", &data_store, NULL); - data_sheet->data_store = data_store; - if (data_store != NULL) - { - g_object_ref (data_store); - g_signal_connect (data_store, "backend-changed", - G_CALLBACK (on_backend_changed), data_sheet); - g_signal_connect (data_store, "case-inserted", - G_CALLBACK (on_case_inserted), data_sheet); - g_signal_connect (data_store, "cases-deleted", - G_CALLBACK (on_cases_deleted), data_sheet); - g_signal_connect (data_store, "case-changed", - G_CALLBACK (on_case_change), data_sheet); - - /* XXX it's unclean to hook into the dict this way--what if the dict - changes? As of this writing, though, nothing ever changes the - data_store's dict. */ - g_signal_connect (data_store->dict, "variable-changed", - G_CALLBACK (on_variable_changed), - data_sheet); - g_signal_connect (data_store->dict, "variable-inserted", - G_CALLBACK (on_variable_inserted), data_sheet); - g_signal_connect (data_store->dict, "variable-deleted", - G_CALLBACK (on_variable_deleted), data_sheet); - } - refresh_model (data_sheet); -} - -/* Clipboard stuff */ + gint posn = GPOINTER_TO_INT (g_object_get_data + (G_OBJECT (sheet->data_sheet_cases_column_popup), + "item")); -/* A casereader and dictionary holding the data currently in the clip */ -static struct casereader *clip_datasheet = NULL; -static struct dictionary *clip_dict = NULL; + const struct variable *v = psppire_dict_insert_variable (data_store->dict, + posn, NULL); + psppire_data_store_insert_value (data_store, var_get_width(v), + var_get_case_index (v)); -static void psppire_data_sheet_update_clipboard (PsppireDataSheet *); + gtk_widget_queue_draw (GTK_WIDGET (sheet)); +} -static gboolean -psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut, - struct casereader **readerp, - struct dictionary **dictp) +static void +set_menu_items_sensitivity (PsppireDataSheet *sheet, gpointer selection, gpointer p) { - struct casewriter *writer ; - PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet); - struct case_map *map = NULL; - struct range_set *rows, *cols; - const struct range_set_node *node; - struct dictionary *dict; - - if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols)) - { - *readerp = NULL; - *dictp = NULL; - return FALSE; - } - - /* Construct clip dictionary. */ - *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict)); - RANGE_SET_FOR_EACH (node, cols) - { - int dict_index; - - for (dict_index = node->start; dict_index < node->end; dict_index++) - { - struct variable *var = dict_get_var (ds->dict->dict, dict_index); - dict_clone_var_assert (dict, var); - } - } + SswRange *range = selection; - /* Construct clip data. */ - map = case_map_by_name (ds->dict->dict, dict); - writer = autopaging_writer_create (dict_get_proto (dict)); - RANGE_SET_FOR_EACH (node, rows) - { - unsigned long int row; + PsppireDataStore *data_store = NULL; + g_object_get (sheet, "data-model", &data_store, NULL); - for (row = node->start; row < node->end; row++) - { - struct ccase *old = psppire_data_store_get_case (ds, row); - if (old != NULL) - casewriter_write (writer, case_map_execute (map, old)); - else - casewriter_force_error (writer); - } - } - case_map_destroy (map); - /* Clear data that we copied out, if we're doing a "cut". */ - if (cut && !casewriter_error (writer)) - { - RANGE_SET_FOR_EACH (node, rows) - { - unsigned long int row; - - for (row = node->start; row < node->end; row++) - { - const struct range_set_node *node2; - - RANGE_SET_FOR_EACH (node2, cols) - { - int dict_index; - - for (dict_index = node2->start; dict_index < node2->end; - dict_index++) - { - struct variable *var; - - var = dict_get_var (ds->dict->dict, dict_index); - psppire_data_store_set_string (ds, "", row, - var, false); - } - } - } - } - } + gint width = gtk_tree_model_get_n_columns (GTK_TREE_MODEL (data_store)); + gint length = psppire_data_store_get_case_count (data_store); - range_set_destroy (rows); - range_set_destroy (cols); - *readerp = casewriter_make_reader (writer); + gboolean whole_row_selected = (range->start_x == 0 && range->end_x == width - 1); + gtk_widget_set_sensitive (sheet->data_clear_cases_menu_item, whole_row_selected); - return TRUE; + gboolean whole_column_selected = + (range->start_y == 0 && range->end_y == length - 1); + gtk_widget_set_sensitive (sheet->data_clear_variables_menu_item, + whole_column_selected); + gtk_widget_set_sensitive (sheet->data_sort_ascending_menu_item, + whole_column_selected); + gtk_widget_set_sensitive (sheet->data_sort_descending_menu_item, + whole_column_selected); } -/* Set the clip from the currently selected range in DATA_SHEET. If CUT is - true, clears the original data from DATA_SHEET, otherwise leaves the - original data in-place. */ static void -psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet, - gboolean cut) +delete_variables (PsppireDataSheet *sheet) { - struct casereader *reader; - struct dictionary *dict; + SswRange *range = SSW_SHEET(sheet)->selection; - if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict)) - { - casereader_destroy (clip_datasheet); - dict_destroy (clip_dict); + PsppireDataStore *data_store = NULL; + g_object_get (sheet, "data-model", &data_store, NULL); - clip_datasheet = reader; - clip_dict = dict; + psppire_dict_delete_variables (data_store->dict, range->start_x, + (range->end_x - range->start_x + 1)); - psppire_data_sheet_update_clipboard (data_sheet); - } + gtk_widget_queue_draw (GTK_WIDGET (sheet)); } -enum { - SELECT_FMT_NULL, - SELECT_FMT_TEXT, - SELECT_FMT_HTML -}; -/* Perform data_out for case CC, variable V, appending to STRING */ -static void -data_out_g_string (GString *string, const struct variable *v, - const struct ccase *cc) +static GtkWidget * +create_data_column_header_popup_menu (PsppireDataSheet *sheet) { - const struct fmt_spec *fs = var_get_print_format (v); - const union value *val = case_data (cc, v); + GtkWidget *menu = gtk_menu_new (); - char *s = data_out (val, var_get_encoding (v), fs); + GtkWidget *item = + gtk_menu_item_new_with_mnemonic (_("_Insert Variable")); + g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable), + sheet); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - g_string_append (string, s); + item = gtk_separator_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - g_free (s); -} + sheet->data_clear_variables_menu_item = + gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables")); + g_signal_connect_swapped (sheet->data_clear_variables_menu_item, "activate", + G_CALLBACK (delete_variables), + sheet); + gtk_widget_set_sensitive (sheet->data_clear_variables_menu_item, FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_clear_variables_menu_item); -static GString * -clip_to_text (struct casereader *datasheet, struct dictionary *dict) -{ - casenumber r; - GString *string; + item = gtk_separator_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet)); - const casenumber case_cnt = casereader_get_case_cnt (datasheet); - const size_t var_cnt = dict_get_var_cnt (dict); - string = g_string_sized_new (10 * val_cnt * case_cnt); + sheet->data_sort_ascending_menu_item = + gtk_menu_item_new_with_mnemonic (_("Sort _Ascending")); + g_signal_connect_swapped (sheet->data_sort_ascending_menu_item, "activate", + G_CALLBACK (sort_ascending), sheet); + gtk_widget_set_sensitive (sheet->data_sort_ascending_menu_item, FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_sort_ascending_menu_item); - for (r = 0 ; r < case_cnt ; ++r ) - { - int c; - struct ccase *cc; - - cc = casereader_peek (datasheet, r); - if (cc == NULL) - { - g_warning ("Clipboard seems to have inexplicably shrunk"); - break; - } - - for (c = 0 ; c < var_cnt ; ++c) - { - const struct variable *v = dict_get_var (dict, c); - data_out_g_string (string, v, cc); - if ( c < val_cnt - 1 ) - g_string_append (string, "\t"); - } - - if ( r < case_cnt) - g_string_append (string, "\n"); - - case_unref (cc); - } + sheet->data_sort_descending_menu_item = + gtk_menu_item_new_with_mnemonic (_("Sort _Descending")); + g_signal_connect_swapped (sheet->data_sort_descending_menu_item, "activate", + G_CALLBACK (sort_descending), sheet); + gtk_widget_set_sensitive (sheet->data_sort_descending_menu_item, FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_sort_descending_menu_item); - return string; + gtk_widget_show_all (menu); + return menu; } -static GString * -clip_to_html (struct casereader *datasheet, struct dictionary *dict) -{ - casenumber r; - GString *string; - - const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet)); - const casenumber case_cnt = casereader_get_case_cnt (datasheet); - const size_t var_cnt = dict_get_var_cnt (dict); - - /* Guestimate the size needed */ - string = g_string_sized_new (80 + 20 * val_cnt * case_cnt); - - g_string_append (string, - "\n"); - - g_string_append (string, "\n"); - for (r = 0 ; r < case_cnt ; ++r ) - { - int c; - struct ccase *cc = casereader_peek (datasheet, r); - if (cc == NULL) - { - g_warning ("Clipboard seems to have inexplicably shrunk"); - break; - } - g_string_append (string, "\n"); - - for (c = 0 ; c < var_cnt ; ++c) - { - const struct variable *v = dict_get_var (dict, c); - g_string_append (string, "\n"); - } - - g_string_append (string, "\n"); - - case_unref (cc); - } - g_string_append (string, "
"); - data_out_g_string (string, v, cc); - g_string_append (string, "
\n"); - - return string; -} + +G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, SSW_TYPE_SHEET) +static GObjectClass * parent_class = NULL; +static gboolean dispose_has_run = FALSE; static void -psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data, - guint info, - struct casereader *reader, - struct dictionary *dict) +psppire_data_sheet_dispose (GObject *obj) { - GString *string = NULL; - - switch (info) - { - case SELECT_FMT_TEXT: - string = clip_to_text (reader, dict); - break; - case SELECT_FMT_HTML: - string = clip_to_html (reader, dict); - break; - default: - g_assert_not_reached (); - } + // PsppireDataSheet *sheet = PSPPIRE_DATA_SHEET (obj); - gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), - 8, - (const guchar *) string->str, string->len); + if (dispose_has_run) + return; - g_string_free (string, TRUE); -} + dispose_has_run = TRUE; -static void -psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) -{ - psppire_data_sheet_clipboard_set (selection_data, info, - clip_datasheet, clip_dict); + /* Chain up to the parent class */ + G_OBJECT_CLASS (parent_class)->dispose (obj); } static void -psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard, - gpointer data) +psppire_data_sheet_class_init (PsppireDataSheetClass *class) { - dict_destroy (clip_dict); - clip_dict = NULL; - - casereader_destroy (clip_datasheet); - clip_datasheet = NULL; -} + GObjectClass *object_class = G_OBJECT_CLASS (class); + object_class->dispose = psppire_data_sheet_dispose; - -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 } -}; - - - -static void -psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet) -{ - GtkClipboard *clipboard = - gtk_widget_get_clipboard (GTK_WIDGET (sheet), - GDK_SELECTION_CLIPBOARD); - - if (!gtk_clipboard_set_with_owner (clipboard, targets, - G_N_ELEMENTS (targets), - psppire_data_sheet_clipboard_get_cb, - psppire_data_sheet_clipboard_clear_cb, - G_OBJECT (sheet))) - psppire_data_sheet_clipboard_clear_cb (clipboard, sheet); + parent_class = g_type_class_peek_parent (class); } -static void -psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet) +GtkWidget* +psppire_data_sheet_new (void) { - struct range_set *rows, *cols; - GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)); - if (! PSPPIRE_IS_DATA_WINDOW (top)) - return; - - PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top); - gboolean enable = - psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols); - - if (enable) - { - range_set_destroy (rows); - range_set_destroy (cols); - } + GObject *obj = + g_object_new (PSPPIRE_TYPE_DATA_SHEET, + "forward-conversion", psppire_data_store_value_to_string, + "reverse-conversion", myreversefunc, + NULL); - gtk_widget_set_sensitive (dw->mi_copy, enable); - gtk_widget_set_sensitive (dw->mi_cut, enable); + return GTK_WIDGET (obj); } static void -psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) +set_dictionary (PsppireDataSheet *sheet) { - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data); - struct casereader *reader; - struct dictionary *dict; + GtkTreeModel *data_model = NULL; + g_object_get (sheet, "data-model", &data_model, NULL); - if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict)) - { - psppire_data_sheet_clipboard_set (selection_data, info, - reader, dict); - casereader_destroy (reader); - dict_destroy (dict); - } + PsppireDataStore *store = PSPPIRE_DATA_STORE (data_model); + g_object_set (sheet, "hmodel", store->dict, NULL); } static void -psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet, - gboolean should_own) -{ - GtkClipboard *clipboard; - GdkDisplay *display; - - display = gtk_widget_get_display (GTK_WIDGET (data_sheet)); - clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY); - g_return_if_fail (clipboard != NULL); - - if (data_sheet->owns_primary_selection && !should_own) - { - data_sheet->owns_primary_selection = FALSE; - gtk_clipboard_clear (clipboard); - } - else if (should_own) - data_sheet->owns_primary_selection = - gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets), - psppire_data_sheet_primary_get_cb, - NULL, G_OBJECT (data_sheet)); -} - -/* A callback for when the clipboard contents have been received. */ -static void -psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard, - GtkSelectionData *sd, - gpointer data) +move_variable (PsppireDataSheet *sheet, gint from, gint to, gpointer ud) { - PsppireDataSheet *data_sheet = data; - PsppireDataStore *store = data_sheet->data_store; - struct range_set *rows, *cols; - gint count = 0; - gint next_row, next_column; - gint first_column; - char *c; - - if ( gtk_selection_data_get_length (sd) < 0 ) - return; + PsppireDataStore *data_store = NULL; + g_object_get (sheet, "data-model", &data_store, NULL); - if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE)) + if (data_store == NULL) return; - c = (char *) gtk_selection_data_get_data (sd); + PsppireDict *dict = data_store->dict; + struct variable *var = psppire_dict_get_variable (dict, from); - /* Get the starting selected position in the data sheet. (Possibly we should - only paste into the selected range if it's larger than one cell?) */ - if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols)) + if (var == NULL) return; - next_row = range_set_first (rows)->start; - first_column = next_column = range_set_first (cols)->start; - range_set_destroy (rows); - range_set_destroy (cols); - - g_return_if_fail (next_row >= 0); - g_return_if_fail (next_column >= 0); - - while (count < gtk_selection_data_get_length (sd)) - { - gint row = next_row; - gint column = next_column; - struct variable *var; - char *s = c; - - while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd)) - { - c++; - count++; - } - if ( *c == '\t') - { - next_row = row ; - next_column = column + 1; - } - else if ( *c == '\n') - { - next_row = row + 1; - next_column = first_column; - } - *c++ = '\0'; - count++; - - var = psppire_dict_get_variable (store->dict, column); - if (var != NULL) - psppire_data_store_set_string (store, s, row, var, FALSE); - } + gint new_pos = to; + /* The index refers to the final position, so if the source + is less than the destination, then we must subtract 1, to + account for the position vacated by the source */ + if (from < to) + new_pos--; + dict_reorder_var (dict->dict, var, new_pos); } static void -psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard, - GdkAtom *atoms, - gint n_atoms, - gpointer data) +psppire_data_sheet_init (PsppireDataSheet *sheet) { - GtkWidget *mi = GTK_WIDGET (data); - gboolean compatible_target = FALSE; - gint i; - for (i = 0; i < G_N_ELEMENTS (targets); i++) - { - GdkAtom target = gdk_atom_intern (targets[i].target, TRUE); - gint j; - - for (j = 0; j < n_atoms; j++) - if (target == atoms[j]) - { - compatible_target = TRUE; - break; - } - } + sheet->data_sheet_cases_column_popup = + create_data_column_header_popup_menu (sheet); - gtk_widget_set_sensitive (mi, compatible_target); -} + sheet->data_sheet_cases_row_popup = + create_data_row_header_popup_menu (sheet); -static void -on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data); + g_signal_connect (sheet, "selection-changed", + G_CALLBACK (set_menu_items_sensitivity), sheet); - GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)); - if (! PSPPIRE_IS_DATA_WINDOW (top)) - return; + g_signal_connect (sheet, "column-header-pressed", + G_CALLBACK (show_cases_column_popup), sheet); + + g_signal_connect (sheet, "row-header-pressed", + G_CALLBACK (show_cases_row_popup), sheet); + + g_signal_connect (sheet, "value-changed", + G_CALLBACK (change_data_value), NULL); - PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top); + g_signal_connect (sheet, "notify::data-model", + G_CALLBACK (set_dictionary), NULL); - gtk_clipboard_request_targets (clip, - psppire_data_sheet_targets_received_cb, - dw->mi_paste); + g_signal_connect (sheet, "column-moved", G_CALLBACK (move_variable), NULL); } diff --git a/src/ui/gui/psppire-data-sheet.h b/src/ui/gui/psppire-data-sheet.h index 3870a92099..49e7a1751c 100644 --- a/src/ui/gui/psppire-data-sheet.h +++ b/src/ui/gui/psppire-data-sheet.h @@ -1,116 +1,50 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc. + Copyright (C) 2017 John Darrington - 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 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. + 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 . */ + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ -#ifndef PSPPIRE_DATA_SHEET_H -#define PSPPIRE_DATA_SHEET_H 1 - -/* PsppireDataSheet is a PsppSheetView that displays the data in a dataset, - with one column per variable and one row per case. - - PsppireDataSheet is usually a child of PsppireDataEditor in the widget - hierarchy. Other widgets can also use it. */ +#ifndef _PSPPIRE_DATA_SHEET_H +#define _PSPPIRE_DATA_SHEET_H #include -#include "ui/gui/pspp-sheet-view.h" - -G_BEGIN_DECLS - -#define PSPP_TYPE_DATA_SHEET (psppire_data_sheet_get_type()) -#define PSPPIRE_DATA_SHEET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),PSPP_TYPE_DATA_SHEET,PsppireDataSheet)) -#define PSPPIRE_DATA_SHEET_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),PSPP_TYPE_DATA_SHEET,PsppireDataSheetClass)) -#define PSPP_IS_DATA_SHEET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),PSPP_TYPE_DATA_SHEET)) -#define PSPP_IS_DATA_SHEET_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),PSPP_TYPE_DATA_SHEET)) -#define PSPPIRE_DATA_SHEET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),PSPP_TYPE_DATA_SHEET,PsppireDataSheetClass)) - -typedef struct _PsppireDataSheet PsppireDataSheet; -typedef struct _PsppireDataSheetClass PsppireDataSheetClass; +#include struct _PsppireDataSheet { - PsppSheetView parent; - - struct _PsppireDataStore *data_store; - gboolean show_value_labels; - gboolean show_case_numbers; - gboolean may_create_vars; - gboolean may_delete_vars; + SswSheet parent_instance; - gboolean owns_primary_selection; + GtkWidget *data_sheet_cases_column_popup; - guint scroll_to_bottom_signal; - guint scroll_to_right_signal; + GtkWidget *data_clear_variables_menu_item; + GtkWidget *data_clear_cases_menu_item; + GtkWidget *data_sheet_cases_row_popup; - GtkClipboard *clip; - guint on_owner_change_signal; - - PsppSheetViewColumn *new_variable_column; - - GtkWidget *container; - gboolean dispose_has_run; - - GtkWidget *column_popup_menu; - GtkWidget *row_popup_menu; - - GtkWidget *pu_sort_up; - GtkWidget *pu_sort_down; + /* Data sheet popup menu */ + GtkWidget *data_sort_ascending_menu_item; + GtkWidget *data_sort_descending_menu_item; }; struct _PsppireDataSheetClass { - PsppSheetViewClass parent_class; + SswSheetClass parent_class; }; -GType psppire_data_sheet_get_type (void) G_GNUC_CONST; -GtkWidget *psppire_data_sheet_new (void); +#define PSPPIRE_TYPE_DATA_SHEET psppire_data_sheet_get_type () -struct _PsppireDataStore *psppire_data_sheet_get_data_store (PsppireDataSheet *); -void psppire_data_sheet_set_data_store (PsppireDataSheet *, - struct _PsppireDataStore *); +G_DECLARE_FINAL_TYPE (PsppireDataSheet, psppire_data_sheet, PSPPIRE, DATA_SHEET, SswSheet) -gboolean psppire_data_sheet_get_value_labels (const PsppireDataSheet *); -void psppire_data_sheet_set_value_labels (PsppireDataSheet *, - gboolean show_value_labels); - -gboolean psppire_data_sheet_get_case_numbers (const PsppireDataSheet *); -void psppire_data_sheet_set_case_numbers (PsppireDataSheet *, - gboolean show_case_numbers); - -gboolean psppire_data_sheet_get_may_create_vars (PsppireDataSheet *); -void psppire_data_sheet_set_may_create_vars (PsppireDataSheet *, gboolean); - -gboolean psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *); -void psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *, gboolean); - -void psppire_data_sheet_goto_variable (PsppireDataSheet *, gint dict_index); -struct variable *psppire_data_sheet_get_current_variable (const PsppireDataSheet *); - -void psppire_data_sheet_goto_case (PsppireDataSheet *, gint case_index); -gint psppire_data_sheet_get_selected_case (const PsppireDataSheet *); -gint psppire_data_sheet_get_current_case (const PsppireDataSheet *); - -void psppire_data_sheet_insert_case (PsppireDataSheet *data_sheet); -void psppire_data_sheet_insert_variable (PsppireDataSheet *data_sheet); - -void psppire_data_sheet_edit_cut (PsppireDataSheet *data_sheet); -void psppire_data_sheet_edit_copy (PsppireDataSheet *data_sheet); -void psppire_data_sheet_edit_paste (PsppireDataSheet *data_sheet); - -void psppire_data_sheet_edit_clear_cases (PsppireDataSheet *data_sheet); -void psppire_data_sheet_edit_clear_variables (PsppireDataSheet *data_sheet); - -G_END_DECLS +GtkWidget *psppire_data_sheet_new (void); -#endif /* PSPPIRE_DATA_SHEET_H */ +#endif diff --git a/src/ui/gui/psppire-data-store.c b/src/ui/gui/psppire-data-store.c index 59099a153e..d0322204e4 100644 --- a/src/ui/gui/psppire-data-store.c +++ b/src/ui/gui/psppire-data-store.c @@ -1,5 +1,6 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2006, 2008, 2009, 2010, 2011, 2012, 2013, 2016 Free Software Foundation + Copyright (C) 2006, 2008, 2009, 2010, 2011, 2012, + 2013, 2016, 2017 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 @@ -19,7 +20,7 @@ #include #include #define _(msgid) gettext (msgid) -#define N_(msgid) msgid +#define P_(msgid) msgid #include #include @@ -44,7 +45,7 @@ #include "xalloc.h" #include "xmalloca.h" - +#include "value-variant.h" static void psppire_data_store_init (PsppireDataStore *data_store); static void psppire_data_store_class_init (PsppireDataStoreClass *class); @@ -67,15 +68,192 @@ static GObjectClass *parent_class = NULL; enum { - BACKEND_CHANGED, - CASES_DELETED, - CASE_INSERTED, + ITEMS_CHANGED, CASE_CHANGED, n_SIGNALS }; static guint signals [n_SIGNALS]; +static gint +__tree_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + if (store->datasheet == NULL) + return 0; + + gint n = datasheet_get_n_rows (store->datasheet); + + return n; +} + +static GtkTreeModelFlags +__tree_model_get_flags (GtkTreeModel *model) +{ + g_return_val_if_fail (PSPPIRE_IS_DATA_STORE (model), (GtkTreeModelFlags) 0); + + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint +__tree_model_get_n_columns (GtkTreeModel *tree_model) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + return psppire_dict_get_var_cnt (store->dict); +} + + +static gboolean +__iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + g_assert (parent == NULL); + g_return_val_if_fail (store, FALSE); + + if (!store->datasheet || n >= datasheet_get_n_rows (store->datasheet)) + { + iter->stamp = -1; + iter->user_data = NULL; + return FALSE; + } + + iter->user_data = GINT_TO_POINTER (n); + iter->stamp = store->stamp; + + return TRUE; +} + +gboolean +myreversefunc (GtkTreeModel *model, gint col, gint row, + const gchar *in, GValue *out) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (model); + + const struct variable *variable = psppire_dict_get_variable (store->dict, col); + g_return_val_if_fail (variable, FALSE); + + const struct fmt_spec *fmt = var_get_print_format (variable); + + int width = var_get_width (variable); + + union value val; + value_init (&val, width); + char *xx = + data_in (ss_cstr (in), psppire_dict_encoding (store->dict), + fmt->type, &val, width, "UTF-8"); + + GVariant *vrnt = value_variant_new (&val, width); + value_destroy (&val, width); + + g_value_init (out, G_TYPE_VARIANT); + g_value_set_variant (out, vrnt); + free (xx); + return TRUE; +} + +static char * +unlabeled_value (PsppireDataStore *store, const struct variable *variable, const union value *val) +{ + const struct fmt_spec *fmt = var_get_print_format (variable); + return data_out (val, psppire_dict_encoding (store->dict), fmt); +} + +gchar * +psppire_data_store_value_to_string (gpointer unused, PsppireDataStore *store, gint col, gint row, const GValue *v) +{ + const struct variable *variable = psppire_dict_get_variable (store->dict, col); + g_return_val_if_fail (variable, g_strdup ("???")); + + GVariant *vrnt = g_value_get_variant (v); + union value val; + value_variant_get (&val, vrnt); + + char *out = unlabeled_value (store, variable, &val); + + value_destroy_from_variant (&val, vrnt); + + return out; +} + +gchar * +psppire_data_store_value_to_string_with_labels (gpointer unused, PsppireDataStore *store, gint col, gint row, const GValue *v) +{ + const struct variable *variable = psppire_dict_get_variable (store->dict, col); + g_return_val_if_fail (variable, g_strdup ("???")); + + GVariant *vrnt = g_value_get_variant (v); + union value val; + value_variant_get (&val, vrnt); + + char *out = NULL; + + const struct val_labs *vls = var_get_value_labels (variable); + struct val_lab *vl = val_labs_lookup (vls, &val); + if (vl != NULL) + out = strdup (val_lab_get_label (vl)); + else + out = unlabeled_value (store, variable, &val); + + value_destroy_from_variant (&val, vrnt); + + return out; +} + +static void +__get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + g_return_if_fail (iter->stamp == store->stamp); + + const struct variable *variable = psppire_dict_get_variable (store->dict, column); + if (NULL == variable) + return; + + g_value_init (value, G_TYPE_VARIANT); + + gint row = GPOINTER_TO_INT (iter->user_data); + + struct ccase *cc = datasheet_get_row (store->datasheet, row); + + const union value *val = case_data_idx (cc, var_get_case_index (variable)); + + GVariant *vv = value_variant_new (val, var_get_width (variable)); + + g_value_set_variant (value, vv); + + case_unref (cc); +} + + +static void +__tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = __tree_model_get_flags; + iface->get_n_columns = __tree_model_get_n_columns ; + iface->get_column_type = NULL; + iface->get_iter = NULL; + iface->iter_next = NULL; + iface->get_path = NULL; + iface->get_value = __get_value; + + iface->iter_children = NULL; + iface->iter_has_child = NULL; + iface->iter_n_children = __tree_model_iter_n_children; + iface->iter_nth_child = __iter_nth_child; + iface->iter_parent = NULL; +} + GType psppire_data_store_get_type (void) @@ -97,9 +275,18 @@ psppire_data_store_get_type (void) (GInstanceInitFunc) psppire_data_store_init, }; + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) __tree_model_init, + NULL, + NULL + }; + data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore", &data_store_info, 0); + + g_type_add_interface_static (data_store_type, GTK_TYPE_TREE_MODEL, + &tree_model_info); } return data_store_type; @@ -117,27 +304,18 @@ psppire_data_store_class_init (PsppireDataStoreClass *class) object_class->finalize = psppire_data_store_finalize; object_class->dispose = psppire_data_store_dispose; - signals [BACKEND_CHANGED] = - g_signal_new ("backend-changed", + signals [ITEMS_CHANGED] = + g_signal_new ("items-changed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, + psppire_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, - 0); - - signals [CASE_INSERTED] = - g_signal_new ("case-inserted", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - + 3, + G_TYPE_UINT, /* Index of the start of the change */ + G_TYPE_UINT, /* The number of items deleted */ + G_TYPE_UINT); /* The number of items inserted */ signals [CASE_CHANGED] = g_signal_new ("case-changed", @@ -149,26 +327,10 @@ psppire_data_store_class_init (PsppireDataStoreClass *class) G_TYPE_NONE, 1, G_TYPE_INT); - - signals [CASES_DELETED] = - g_signal_new ("cases-deleted", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - psppire_marshal_VOID__INT_INT, - G_TYPE_NONE, - 2, - G_TYPE_INT, - G_TYPE_INT); } -static gboolean -psppire_data_store_insert_value (PsppireDataStore *ds, - gint width, gint where); - casenumber psppire_data_store_get_case_count (const PsppireDataStore *store) { @@ -193,8 +355,19 @@ psppire_data_store_init (PsppireDataStore *data_store) data_store->dict = NULL; data_store->datasheet = NULL; data_store->dispose_has_run = FALSE; + data_store->stamp = g_random_int (); } + +static void +psppire_data_store_delete_value (PsppireDataStore *store, gint case_index) +{ + g_return_if_fail (store->datasheet); + datasheet_delete_columns (store->datasheet, case_index, 1); + datasheet_insert_column (store->datasheet, NULL, -1, case_index); +} + + /* A callback which occurs after a variable has been deleted. */ @@ -205,10 +378,7 @@ delete_variable_callback (GObject *obj, const struct variable *var UNUSED, { PsppireDataStore *store = PSPPIRE_DATA_STORE (data); - g_return_if_fail (store->datasheet); - - datasheet_delete_columns (store->datasheet, case_index, 1); - datasheet_insert_column (store->datasheet, NULL, -1, case_index); + psppire_data_store_delete_value (store, case_index); } struct resize_datum_aux @@ -292,12 +462,17 @@ psppire_data_store_set_reader (PsppireDataStore *ds, struct casereader *reader) { gint i; - + gint old_n = 0; if ( ds->datasheet) - datasheet_destroy (ds->datasheet); + { + old_n = datasheet_get_n_rows (ds->datasheet); + datasheet_destroy (ds->datasheet); + } ds->datasheet = datasheet_create (reader); + gint new_n = datasheet_get_n_rows (ds->datasheet); + if ( ds->dict ) for (i = 0 ; i < n_dict_signals; ++i ) { @@ -308,7 +483,7 @@ psppire_data_store_set_reader (PsppireDataStore *ds, } } - g_signal_emit (ds, signals[BACKEND_CHANGED], 0); + g_signal_emit (ds, signals[ITEMS_CHANGED], 0, 0, old_n, new_n); } @@ -426,6 +601,27 @@ psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn) return result; } +gboolean +psppire_data_store_get_value (PsppireDataStore *store, + glong row, const struct variable *var, + union value *val) +{ + g_return_val_if_fail (store != NULL, FALSE); + g_return_val_if_fail (store->datasheet != NULL, FALSE); + g_return_val_if_fail (var != NULL, FALSE); + + if (row < 0 || row >= datasheet_get_n_rows (store->datasheet)) + return FALSE; + + int width = var_get_width (var); + value_init (val, width); + datasheet_get_value (store->datasheet, row, var_get_case_index (var), val); + + return TRUE; +} + + + gchar * psppire_data_store_get_string (PsppireDataStore *store, glong row, const struct variable *var, @@ -433,19 +629,10 @@ psppire_data_store_get_string (PsppireDataStore *store, { gchar *string; union value v; - int width; - - g_return_val_if_fail (store != NULL, NULL); - g_return_val_if_fail (store->datasheet != NULL, NULL); - g_return_val_if_fail (var != NULL, NULL); - - if (row < 0 || row >= datasheet_get_n_rows (store->datasheet)) + int width = var_get_width (var); + if (! psppire_data_store_get_value (store, row, var, &v)) return NULL; - width = var_get_width (var); - value_init (&v, width); - datasheet_get_value (store->datasheet, row, var_get_case_index (var), &v); - string = NULL; if (use_value_label) { @@ -515,7 +702,7 @@ psppire_data_store_clear (PsppireDataStore *ds) psppire_dict_clear (ds->dict); - g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1); + g_signal_emit (ds, signals [ITEMS_CHANGED], 0, 0, -1, 0); } @@ -568,7 +755,7 @@ psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first, datasheet_delete_rows (ds->datasheet, first, n_cases); - g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases); + g_signal_emit (ds, signals[ITEMS_CHANGED], 0, first, n_cases, 0); return TRUE; } @@ -590,7 +777,9 @@ psppire_data_store_insert_case (PsppireDataStore *ds, result = datasheet_insert_rows (ds->datasheet, posn, &cc, 1); if ( result ) - g_signal_emit (ds, signals [CASE_INSERTED], 0, posn); + { + g_signal_emit (ds, signals[ITEMS_CHANGED], 0, posn, 0, 1); + } else g_warning ("Cannot insert case at position %ld\n", posn); @@ -621,7 +810,10 @@ psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum, ok = datasheet_put_value (ds->datasheet, casenum, var_get_case_index (var), v); if (ok) - g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum); + { + g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum); + g_signal_emit (ds, signals [ITEMS_CHANGED], 0, casenum, 1, 1); + } return ok; } @@ -665,7 +857,7 @@ psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx, given WIDTH into every one of them at the position immediately preceding WHERE. */ -static gboolean +gboolean psppire_data_store_insert_value (PsppireDataStore *ds, gint width, gint where) { diff --git a/src/ui/gui/psppire-data-store.h b/src/ui/gui/psppire-data-store.h index 47ce5e5e72..65e3d24ad3 100644 --- a/src/ui/gui/psppire-data-store.h +++ b/src/ui/gui/psppire-data-store.h @@ -72,6 +72,7 @@ struct _PsppireDataStore gboolean dispose_has_run ; PsppireDict *dict; struct datasheet *datasheet; + gint stamp; gint dict_handler_id [n_dict_signals]; }; @@ -96,6 +97,8 @@ void psppire_data_store_clear (PsppireDataStore *data_store); gboolean psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn); +gboolean psppire_data_store_insert_value (PsppireDataStore *ds, + gint width, gint where); gboolean psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first, casenumber count); @@ -105,10 +108,26 @@ struct casereader * psppire_data_store_get_reader (PsppireDataStore *ds); gchar *psppire_data_store_get_string (PsppireDataStore *, glong row, const struct variable *, bool use_value_label); + +gchar * psppire_data_store_value_to_string (gpointer unused, PsppireDataStore *store, + gint col, gint row, + const GValue *v); + +gchar * psppire_data_store_value_to_string_with_labels (gpointer unused, PsppireDataStore *store, + gint col, gint row, + const GValue *v); + + +gboolean psppire_data_store_get_value (PsppireDataStore *store, + glong row, const struct variable *var, + union value *val); + gboolean psppire_data_store_set_value (PsppireDataStore *, casenumber casenum, const struct variable *, const union value *); + + gboolean psppire_data_store_set_string (PsppireDataStore *ds, const gchar *text, glong row, const struct variable *, diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c index 15fe2e9fe9..9b90930edd 100644 --- a/src/ui/gui/psppire-data-window.c +++ b/src/ui/gui/psppire-data-window.c @@ -1,5 +1,6 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Free Software Foundation + Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, + 2016, 2017 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 @@ -32,12 +33,11 @@ #include "ui/gui/helper.h" #include "ui/gui/psppire-import-assistant.h" #include "ui/gui/psppire-data-window.h" +#include "ui/gui/psppire-data-editor.h" #include "ui/gui/psppire-dialog-action.h" #include "ui/gui/psppire-encoding-selector.h" #include "ui/gui/psppire-syntax-window.h" #include "ui/gui/psppire-window.h" -#include "ui/gui/psppire-data-sheet.h" -#include "ui/gui/psppire-var-sheet.h" #include "ui/gui/windows-menu.h" #include "ui/gui/goto-case-dialog.h" #include "ui/gui/psppire.h" @@ -47,6 +47,8 @@ #include "gl/c-strcasestr.h" #include "gl/xvasprintf.h" +#include + #include "find-dialog.h" #include "options-dialog.h" #include "psppire-dialog-action-1sks.h" @@ -969,14 +971,9 @@ file_import (PsppireDataWindow *dw) PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w); gtk_widget_show_all (w); - asst->main_loop = g_main_loop_new (NULL, TRUE); - g_main_loop_run (asst->main_loop); - g_main_loop_unref (asst->main_loop); - - if (!asst->file_name) - goto end; + int response = psppire_import_assistant_run (asst); - switch (asst->response) + switch (response) { case GTK_RESPONSE_APPLY: { @@ -992,7 +989,6 @@ file_import (PsppireDataWindow *dw) break; } - end: gtk_widget_destroy (GTK_WIDGET (asst)); } @@ -1057,10 +1053,9 @@ connect_action_to_menuitem (GActionMap *map, const gchar *action_name, GtkWidget static void -set_data_page (PsppireDataWindow *dw) +on_realize (PsppireDataWindow *dw) { gtk_notebook_set_current_page (GTK_NOTEBOOK (dw->data_editor), 1); - gtk_notebook_set_current_page (GTK_NOTEBOOK (dw->data_editor), 0); } @@ -1068,10 +1063,52 @@ static void on_cut (PsppireDataWindow *dw) { int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor)); - if (p == 0) - { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - psppire_data_sheet_edit_cut (ds); + if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW) + { + PsppireDict *dict = NULL; + g_object_get (dw->data_editor, "dictionary", &dict, NULL); + + gint x, y; + SswSheet *sheet = SSW_SHEET (dw->data_editor->data_sheet); + SswRange sel = *sheet->selection; + + GtkClipboard *clip = + gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)), + GDK_SELECTION_CLIPBOARD); + + /* Save the selected area to a string */ + GString *str = g_string_new (""); + for (y = sel.start_y ; y <= sel.end_y; ++y) + { + for (x = sel.start_x ; x <= sel.end_x; ++x) + { + const struct variable * var = psppire_dict_get_variable (dict, x); + gchar *s = psppire_data_store_get_string (dw->data_editor->data_store, + y, var, FALSE); + g_string_append (str, s); + g_string_append (str, "\t"); + g_free (s); + } + g_string_append (str, "\n"); + } + + gtk_clipboard_set_text (clip, str->str, str->len); + g_string_free (str, TRUE); + + /* Now fill the selected area with SYSMIS */ + union value sm ; + sm.f = SYSMIS; + for (x = sel.start_x ; x <= sel.end_x; ++x) + { + const struct variable * var = psppire_dict_get_variable (dict, x); + for (y = sel.start_y ; y <= sel.end_y; ++y) + { + psppire_data_store_set_value (dw->data_editor->data_store, + y, + var, &sm); + } + } + } } @@ -1079,10 +1116,13 @@ static void on_copy (PsppireDataWindow *dw) { int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor)); - if (p == 0) + if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW) { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - psppire_data_sheet_edit_copy (ds); + GtkClipboard *clip = + gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)), + GDK_SELECTION_CLIPBOARD); + + ssw_sheet_set_clip (SSW_SHEET (dw->data_editor->data_sheet), clip); } } @@ -1090,75 +1130,78 @@ static void on_paste (PsppireDataWindow *dw) { int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor)); - if (p == 0) + if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW) { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - psppire_data_sheet_edit_paste (ds); + psppire_data_editor_paste (dw->data_editor); } } - static void on_clear_cases (PsppireDataWindow *dw) { - int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor)); - if (p == 0) + PsppireDataEditor *de = dw->data_editor; + int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de)); + if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW) { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - psppire_data_sheet_edit_clear_cases (ds); + SswRange *range = SSW_SHEET(de->data_sheet)->selection; + psppire_data_store_delete_cases (de->data_store, range->start_y, + range->end_y - range->start_y + 1); + gtk_widget_queue_draw (GTK_WIDGET (de->data_sheet)); } } static void on_clear_variables (PsppireDataWindow *dw) { - int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor)); - if (p == 0) + PsppireDataEditor *de = dw->data_editor; + int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de)); + if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW) { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - psppire_data_sheet_edit_clear_variables (ds); + psppire_data_editor_data_delete_variables (de); } else { - psppire_var_sheet_clear_variables (PSPPIRE_VAR_SHEET (dw->data_editor->var_sheet)); + psppire_data_editor_var_delete_variables (de); } } - static void insert_variable (PsppireDataWindow *dw) { - int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor)); - if (p == 0) + PsppireDataEditor *de = dw->data_editor; + int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de)); + + if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW) { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - psppire_data_sheet_insert_variable (ds); + SswRange *range = SSW_SHEET(de->data_sheet)->selection; + psppire_data_editor_insert_new_variable_at_posn (de, range->start_x); } else { - psppire_var_sheet_insert_variable (PSPPIRE_VAR_SHEET (dw->data_editor->var_sheet)); + SswRange *range = SSW_SHEET(de->var_sheet)->selection; + psppire_data_editor_insert_new_variable_at_posn (de, range->start_y); } } - static void insert_case_at_row (PsppireDataWindow *dw) { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - - psppire_data_sheet_insert_case (ds); + PsppireDataEditor *de = dw->data_editor; + SswRange *range = SSW_SHEET(de->data_sheet)->selection; + psppire_data_editor_insert_new_case_at_posn (de, range->start_y); } static void goto_case (PsppireDataWindow *dw) { - PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor); - - goto_case_dialog (ds); + PsppireDataEditor *de = dw->data_editor; + int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de)); + if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW) + { + goto_case_dialog (PSPPIRE_DATA_SHEET (de->data_sheet)); + } } - - static GtkWidget * create_file_menu (PsppireDataWindow *dw) { @@ -1291,6 +1334,7 @@ create_file_menu (PsppireDataWindow *dw) return menuitem; } + static GtkWidget * create_edit_menu (PsppireDataWindow *dw) { @@ -1376,7 +1420,6 @@ create_edit_menu (PsppireDataWindow *dw) return menuitem; } - static void psppire_data_window_finish_init (PsppireDataWindow *de, struct dataset *ds) @@ -1409,17 +1452,11 @@ psppire_data_window_finish_init (PsppireDataWindow *de, PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store)); g_signal_connect (de, "realize", - G_CALLBACK (set_data_page), de); + G_CALLBACK (on_realize), de); g_signal_connect_swapped (de->data_store, "case-changed", G_CALLBACK (set_unsaved), de); - g_signal_connect_swapped (de->data_store, "case-inserted", - G_CALLBACK (set_unsaved), de); - - g_signal_connect_swapped (de->data_store, "cases-deleted", - G_CALLBACK (set_unsaved), de); - dataset_set_callbacks (de->dataset, &cbs, de); connect_help (de->builder); @@ -1443,7 +1480,7 @@ psppire_data_window_finish_init (PsppireDataWindow *de, G_CALLBACK (on_split_change), de); - g_signal_connect_swapped (de->dict, "backend-changed", + g_signal_connect_swapped (de->dict, "items-changed", G_CALLBACK (enable_save), de); g_signal_connect_swapped (de->dict, "variable-inserted", G_CALLBACK (enable_save), de); @@ -1751,6 +1788,7 @@ psppire_data_window_finish_init (PsppireDataWindow *de, ll_push_head (&all_data_windows, &de->ll); } + static void psppire_data_window_dispose (GObject *object) { @@ -1853,6 +1891,7 @@ psppire_data_window_get_property (GObject *object, } + GtkWidget* psppire_data_window_new (struct dataset *ds) { @@ -1886,12 +1925,15 @@ psppire_data_window_new (struct dataset *ds) return dw; } + + bool psppire_data_window_is_empty (PsppireDataWindow *dw) { return psppire_dict_get_var_cnt (dw->dict) == 0; } + static void psppire_data_window_iface_init (PsppireWindowIface *iface) { @@ -1902,6 +1944,7 @@ psppire_data_window_iface_init (PsppireWindowIface *iface) + PsppireDataWindow * psppire_default_data_window (void) { @@ -1910,6 +1953,8 @@ psppire_default_data_window (void) return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll); } + + void psppire_data_window_set_default (PsppireDataWindow *pdw) { @@ -1924,6 +1969,8 @@ psppire_data_window_undefault (PsppireDataWindow *pdw) ll_push_tail (&all_data_windows, &pdw->ll); } + + PsppireDataWindow * psppire_data_window_for_dataset (struct dataset *ds) { diff --git a/src/ui/gui/psppire-delimited-text.c b/src/ui/gui/psppire-delimited-text.c new file mode 100644 index 0000000000..dd13f1c384 --- /dev/null +++ b/src/ui/gui/psppire-delimited-text.c @@ -0,0 +1,630 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2017 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 . */ + +#include +#include +#define _(msgid) gettext (msgid) +#define P_(msgid) msgid + +#include "psppire-delimited-text.h" +#include "psppire-text-file.h" +#include "libpspp/str.h" +#include "libpspp/i18n.h" + +#include + +/* Properties */ +enum + { + PROP_0, + PROP_CHILD, + PROP_DELIMITERS, + PROP_FIRST_LINE + }; + +struct enclosure +{ + gunichar opening; + gunichar closing; +}; + +static const struct enclosure enclosures[3] = + { + {'(', ')'}, + {'"', '"'}, + {'\'', '\''} + }; + +static void +count_delims (PsppireDelimitedText *tf) +{ + if (tf->child == NULL) + return; + + tf->max_delimiters = 0; + GtkTreeIter iter; + gboolean valid; + for (valid = gtk_tree_model_get_iter_first (tf->child, &iter); + valid; + valid = gtk_tree_model_iter_next (tf->child, &iter)) + { + gint enc = -1; + // FIXME: Box these lines to avoid constant allocation/deallocation + gchar *line = NULL; + gtk_tree_model_get (tf->child, &iter, 1, &line, -1); + { + char *p; + gint count = 0; + for (p = line; ; p = g_utf8_find_next_char (p, NULL)) + { + const gunichar c = g_utf8_get_char (p); + if (c == 0) + break; + if (enc == -1) + { + gint i; + for (i = 0; i < 3; ++i) + { + if (c == enclosures[i].opening) + { + enc = i; + break; + } + } + } + else if (c == enclosures[enc].closing) + { + enc = -1; + } + if (enc == -1) + { + GSList *del; + for (del = tf->delimiters; del; del = g_slist_next (del)) + { + if (c == GPOINTER_TO_INT (del->data)) + count++; + } + } + } + tf->max_delimiters = MAX (tf->max_delimiters, count); + } + g_free (line); + } +} + +static void +cache_invalidate (PsppireDelimitedText *tf) +{ + memset (tf->cache_starts, 0, 512); + if (tf->const_cache.string) + { + ss_dealloc (&tf->const_cache); + tf->const_cache.string = NULL; + tf->cache_row = -1; + } +} + +static void +psppire_delimited_text_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object); + + switch (prop_id) + { + case PROP_FIRST_LINE: + tf->first_line = g_value_get_int (value); + break; + case PROP_CHILD: + tf->child = g_value_get_object (value); + g_return_if_fail (PSPPIRE_IS_TEXT_FILE (tf->child)); + break; + case PROP_DELIMITERS: + g_slist_free (tf->delimiters); + tf->delimiters = g_slist_copy (g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; + + cache_invalidate (tf); + count_delims (tf); +} + +static void +psppire_delimited_text_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PsppireDelimitedText *text_file = PSPPIRE_DELIMITED_TEXT (object); + + switch (prop_id) + { + case PROP_FIRST_LINE: + g_value_set_int (value, text_file->first_line); + break; + case PROP_DELIMITERS: + g_value_set_pointer (value, text_file->delimiters); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + + +static void psppire_delimited_text_init (PsppireDelimitedText *text_file); +static void psppire_delimited_text_class_init (PsppireDelimitedTextClass *class); + +static void psppire_delimited_text_finalize (GObject *object); +static void psppire_delimited_text_dispose (GObject *object); + +static GObjectClass *parent_class = NULL; + +static gint +n_lines (PsppireDelimitedText *file) +{ + PsppireTextFile *child = PSPPIRE_TEXT_FILE (file->child); + + return child->maximum_lines; +} + +static gboolean +__tree_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); + if (path == NULL) + return FALSE; + + + gint *indices = gtk_tree_path_get_indices (path); + + if (!indices) + return FALSE; + + gint n = *indices; + + gint children = n_lines (file); + + if (n >= children - file->first_line) + return FALSE; + + + iter->user_data = GINT_TO_POINTER (n); + iter->stamp = file->stamp; + + return TRUE; +} + + +static gboolean +__tree_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); + g_return_val_if_fail (file->stamp == iter->stamp, FALSE); + + gint n = GPOINTER_TO_INT (iter->user_data); + + + gint children = n_lines (file); + + if (n + 1 >= children - file->first_line) + return FALSE; + + iter->user_data = GINT_TO_POINTER (n + 1); + + return TRUE; +} + + +static GType +__tree_get_column_type (GtkTreeModel *tree_model, + gint index) +{ + if (index == 0) + return G_TYPE_INT; + + return G_TYPE_STRING; +} + +static gboolean +__iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return 0; +} + + +static gboolean +__iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return 0; +} + +static GtkTreePath * +__tree_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); + g_return_val_if_fail (file->stamp == iter->stamp, FALSE); + + gint n = GPOINTER_TO_INT (iter->user_data); + + gint children = n_lines (file); + + if (n >= children - file->first_line) + return NULL; + + return gtk_tree_path_new_from_indices (n, -1); +} + + +static gboolean +__iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + return 0; +} + + +static gint +__tree_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); + g_assert (iter == NULL); + + gint children = n_lines (file); + + return children - file->first_line; +} + +static GtkTreeModelFlags +__tree_model_get_flags (GtkTreeModel *model) +{ + g_return_val_if_fail (PSPPIRE_IS_DELIMITED_TEXT (model), (GtkTreeModelFlags) 0); + + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint +__tree_model_get_n_columns (GtkTreeModel *tree_model) +{ + PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (tree_model); + + /* + 1 for the trailing field and +1 for the leading line number column */ + return tf->max_delimiters + 1 + 1; +} + + +static gboolean +__iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); + + g_assert (parent == NULL); + + g_return_val_if_fail (file, FALSE); + + gint children = gtk_tree_model_iter_n_children (file->child, NULL); + + if (n >= children - file->first_line) + { + iter->stamp = -1; + iter->user_data = NULL; + return FALSE; + } + + iter->user_data = GINT_TO_POINTER (n); + iter->stamp = file->stamp; + + return TRUE; +} + + +static void +nullify_char (struct substring cs) +{ + int char_len = ss_first_mblen (cs); + while (char_len > 0) + { + cs.string[char_len - 1] = '\0'; + char_len--; + } +} + + +/* Split row N into it's delimited fields (if it is not already cached) + and set this row as the current cache. */ +static void +split_row_into_fields (PsppireDelimitedText *file, gint n) +{ + if (n == file->cache_row) /* Cache hit */ + { + return; + } + + memset (file->cache_starts, 0, 512); + /* Cache miss */ + if (file->const_cache.string) + { + ss_dealloc (&file->const_cache); + } + ss_alloc_substring_pool (&file->const_cache, + PSPPIRE_TEXT_FILE (file->child)->lines[n], NULL); + struct substring cs = file->const_cache; + int field = 0; + file->cache_starts[0] = cs.string; + gint enc = -1; + for (; + UINT32_MAX != ss_first_mb (cs); + ss_get_mb (&cs)) + { + ucs4_t character = ss_first_mb (cs); + gboolean char_is_quote = FALSE; + if (enc == -1) + { + gint i; + for (i = 0; i < 3; ++i) + { + if (character == enclosures[i].opening) + { + enc = i; + char_is_quote = TRUE; + file->cache_starts[field] += ss_first_mblen (cs); + break; + } + } + } + else if (character == enclosures[enc].closing) + { + char_is_quote = TRUE; + nullify_char (cs); + enc = -1; + } + + if (enc == -1 && char_is_quote == FALSE) + { + GSList *del; + for (del = file->delimiters; del; del = g_slist_next (del)) + { + if (character == GPOINTER_TO_INT (del->data)) + { + field++; + int char_len = ss_first_mblen (cs); + file->cache_starts[field] = cs.string + char_len; + nullify_char (cs); + break; + } + } + } + } + + file->cache_row = n; +} + +const gchar * +psppire_delimited_text_get_header_title (PsppireDelimitedText *file, gint column) +{ + if (file->first_line <= 0) + return NULL; + + split_row_into_fields (file, file->first_line - 1); + + return file->cache_starts [column]; +} + +static void +__get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); + + g_return_if_fail (iter->stamp == file->stamp); + + gint n = GPOINTER_TO_INT (iter->user_data) + file->first_line; + + + if (column == 0) + { + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, n + 1); + return; + } + + g_value_init (value, G_TYPE_STRING); + + split_row_into_fields (file, n); + + g_value_set_string (value, file->cache_starts [column - 1]); +} + + +static void +__tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = __tree_model_get_flags; + iface->get_n_columns = __tree_model_get_n_columns ; + iface->get_column_type = __tree_get_column_type; + iface->get_iter = __tree_get_iter; + iface->iter_next = __tree_iter_next; + iface->get_path = __tree_get_path; + iface->get_value = __get_value; + + iface->iter_children = __iter_children; + iface->iter_has_child = __iter_has_child; + iface->iter_n_children = __tree_model_iter_n_children; + iface->iter_nth_child = __iter_nth_child; + iface->iter_parent = __iter_parent; +} + + +GType +psppire_delimited_text_get_type (void) +{ + static GType text_file_type = 0; + + if (!text_file_type) + { + static const GTypeInfo text_file_info = + { + sizeof (PsppireDelimitedTextClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) psppire_delimited_text_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PsppireDelimitedText), + 0, + (GInstanceInitFunc) psppire_delimited_text_init, + }; + + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) __tree_model_init, + NULL, + NULL + }; + + text_file_type = g_type_register_static (G_TYPE_OBJECT, + "PsppireDelimitedText", + &text_file_info, 0); + + g_type_add_interface_static (text_file_type, GTK_TYPE_TREE_MODEL, + &tree_model_info); + } + + return text_file_type; +} + + +static void +psppire_delimited_text_class_init (PsppireDelimitedTextClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + object_class = (GObjectClass*) class; + + GParamSpec *first_line_spec = + g_param_spec_int ("first-line", + "First Line", + P_("The first line to be considered."), + 0, 1000, 0, + G_PARAM_READWRITE); + + GParamSpec *delimiters_spec = + g_param_spec_pointer ("delimiters", + "Field Delimiters", + P_("A GSList of gunichars which delimit the fields."), + G_PARAM_READWRITE); + + GParamSpec *child_spec = + g_param_spec_object ("child", + "Child Model", + P_("The GtkTextModel which this object wraps."), + GTK_TYPE_TREE_MODEL, + G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE); + + object_class->set_property = psppire_delimited_text_set_property; + object_class->get_property = psppire_delimited_text_get_property; + + g_object_class_install_property (object_class, + PROP_CHILD, + child_spec); + + g_object_class_install_property (object_class, + PROP_DELIMITERS, + delimiters_spec); + + g_object_class_install_property (object_class, + PROP_FIRST_LINE, + first_line_spec); + + object_class->finalize = psppire_delimited_text_finalize; + object_class->dispose = psppire_delimited_text_dispose; +} + + +static void +psppire_delimited_text_init (PsppireDelimitedText *text_file) +{ + text_file->child = NULL; + text_file->first_line = 0; + text_file->delimiters = g_slist_prepend (NULL, GINT_TO_POINTER (':')); + + text_file->const_cache.string = NULL; + text_file->const_cache.length = 0; + text_file->cache_row = -1; + memset (text_file->cache_starts, 0, 512); + + text_file->max_delimiters = 0; + + text_file->dispose_has_run = FALSE; + text_file->stamp = g_random_int (); +} + + +PsppireDelimitedText * +psppire_delimited_text_new (GtkTreeModel *child) +{ + return + g_object_new (PSPPIRE_TYPE_DELIMITED_TEXT, + "child", child, + NULL); +} + +static void +psppire_delimited_text_finalize (GObject *object) +{ + PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object); + + g_slist_free (tf->delimiters); + + ss_dealloc (&tf->const_cache); + + /* must chain up */ + (* parent_class->finalize) (object); +} + + +static void +psppire_delimited_text_dispose (GObject *object) +{ + PsppireDelimitedText *ds = PSPPIRE_DELIMITED_TEXT (object); + + if (ds->dispose_has_run) + return; + + /* must chain up */ + (* parent_class->dispose) (object); + + ds->dispose_has_run = TRUE; +} diff --git a/src/ui/gui/psppire-delimited-text.h b/src/ui/gui/psppire-delimited-text.h new file mode 100644 index 0000000000..d18570a83f --- /dev/null +++ b/src/ui/gui/psppire-delimited-text.h @@ -0,0 +1,91 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2017 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 . */ + +#ifndef __PSPPIRE_DELIMITED_TEXT_H__ +#define __PSPPIRE_DELIMITED_TEXT_H__ + +#include "libpspp/str.h" + +#include +#include + +G_BEGIN_DECLS + + + +#define PSPPIRE_TYPE_DELIMITED_TEXT (psppire_delimited_text_get_type ()) + +#define PSPPIRE_DELIMITED_TEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DELIMITED_TEXT, PsppireDelimitedText)) + +#define PSPPIRE_DELIMITED_TEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DELIMITED_TEXT, \ + PsppireDelimitedTextClass)) + + +#define PSPPIRE_IS_DELIMITED_TEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DELIMITED_TEXT)) + +#define PSPPIRE_IS_DELIMITED_TEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DELIMITED_TEXT)) + +#define PSPPIRE_DELIMITED_TEXT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DELIMITED_TEXT, \ + PsppireDelimitedTextClass)) + +struct _PsppireDelimitedText +{ + GObject parent; + + GtkTreeModel *child; + + /* The first line of the file to be modelled */ + gint first_line; + + GSList *delimiters; + gint max_delimiters; + + /*< private >*/ + gboolean dispose_has_run ; + gint stamp; + + /* caching */ + const char *cache_starts[512]; + struct substring const_cache; + int cache_row; +}; + +struct _PsppireDelimitedTextClass +{ + GObjectClass parent_class; +}; + + +typedef struct _PsppireDelimitedText PsppireDelimitedText; +typedef struct _PsppireDelimitedTextClass PsppireDelimitedTextClass; + +GType psppire_delimited_text_get_type (void) G_GNUC_CONST; +PsppireDelimitedText *psppire_delimited_text_new (GtkTreeModel *); + +const gchar *psppire_delimited_text_get_header_title (PsppireDelimitedText *file, gint column); + + +G_END_DECLS + +#endif /* __PSPPIRE_DELIMITED_TEXT_H__ */ diff --git a/src/ui/gui/psppire-dict.c b/src/ui/gui/psppire-dict.c index 239f3ce2f3..7a8245f2bd 100644 --- a/src/ui/gui/psppire-dict.c +++ b/src/ui/gui/psppire-dict.c @@ -1,5 +1,6 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2004, 2006, 2007, 2009, 2010, 2011, 2012 Free Software Foundation + Copyright (C) 2004, 2006, 2007, 2009, 2010, 2011, 2012, + 2016, 2017 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 @@ -33,13 +34,20 @@ #include "ui/gui/psppire-marshal.h" #include "ui/gui/psppire-var-ptr.h" +#include + #include #define _(msgid) gettext (msgid) #define N_(msgid) msgid -enum { - BACKEND_CHANGED, + +GType align_enum_type; +GType measure_enum_type; +GType role_enum_type; + + +enum { VARIABLE_CHANGED, VARIABLE_INSERTED, VARIABLE_DELETED, @@ -59,6 +67,71 @@ static void psppire_dict_dispose (GObject *object); static void dictionary_tree_model_init (GtkTreeModelIface *iface); + +static guint +gni (GListModel *list) +{ + PsppireDict *dict = PSPPIRE_DICT (list); + + return psppire_dict_get_var_cnt (dict); +} + +static GType +git (GListModel *list) +{ + return GTK_TYPE_BUTTON; +} + +static gpointer +gi (GListModel *list, guint id) +{ + GtkWidget *button = gtk_button_new (); + + PsppireDict *dict = PSPPIRE_DICT (list); + + if (id >= psppire_dict_get_var_cnt (dict)) + { + gtk_button_set_label (GTK_BUTTON (button), _("Var")); + } + else + { + const struct variable *v = psppire_dict_get_variable (dict, id); + + gtk_button_set_label (GTK_BUTTON (button), var_get_name (v)); + gtk_widget_set_tooltip_text (button, var_get_label (v)); + + { + PangoContext *context = gtk_widget_create_pango_context (button); + PangoLayout *layout = pango_layout_new (context); + PangoRectangle rect; + + pango_layout_set_text (layout, "M", 1); + + pango_layout_get_extents (layout, NULL, &rect); + + g_object_unref (G_OBJECT (layout)); + g_object_unref (G_OBJECT (context)); + + gtk_widget_set_size_request (button, + (0.25 + var_get_display_width (v)) + * rect.width / PANGO_SCALE, + -1); + } + } + + return button; +} + + +static void +ssw_init_iface (GListModelInterface *iface) +{ + iface->get_n_items = gni; + iface->get_item = gi; + iface->get_item_type = git; +} + + /* --- variables --- */ static GObjectClass *parent_class = NULL; @@ -94,12 +167,21 @@ psppire_dict_get_type (void) NULL }; + static const GInterfaceInfo list_model_info = { + (GInterfaceInitFunc) ssw_init_iface, + NULL, + NULL + }; + object_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDict", &object_info, 0); g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL, &tree_model_info); + + g_type_add_interface_static (object_type, G_TYPE_LIST_MODEL, + &list_model_info); } return object_type; @@ -115,17 +197,6 @@ psppire_dict_class_init (PsppireDictClass *class) object_class->dispose = psppire_dict_dispose; - signals [BACKEND_CHANGED] = - g_signal_new ("backend-changed", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - signals [VARIABLE_CHANGED] = g_signal_new ("variable-changed", G_TYPE_FROM_CLASS (class), @@ -137,10 +208,7 @@ psppire_dict_class_init (PsppireDictClass *class) 3, G_TYPE_INT, G_TYPE_UINT, - G_TYPE_POINTER - ); - - + G_TYPE_POINTER); signals [VARIABLE_INSERTED] = g_signal_new ("variable-inserted", @@ -153,7 +221,6 @@ psppire_dict_class_init (PsppireDictClass *class) 1, G_TYPE_INT); - signals [VARIABLE_DELETED] = g_signal_new ("variable-deleted", G_TYPE_FROM_CLASS (class), @@ -167,7 +234,6 @@ psppire_dict_class_init (PsppireDictClass *class) G_TYPE_INT, G_TYPE_INT); - signals [WEIGHT_CHANGED] = g_signal_new ("weight-changed", G_TYPE_FROM_CLASS (class), @@ -179,7 +245,6 @@ psppire_dict_class_init (PsppireDictClass *class) 1, G_TYPE_INT); - signals [FILTER_CHANGED] = g_signal_new ("filter-changed", G_TYPE_FROM_CLASS (class), @@ -191,7 +256,6 @@ psppire_dict_class_init (PsppireDictClass *class) 1, G_TYPE_INT); - signals [SPLIT_CHANGED] = g_signal_new ("split-changed", G_TYPE_FROM_CLASS (class), @@ -221,7 +285,10 @@ addcb (struct dictionary *d, int idx, void *pd) PsppireDict *dict = PSPPIRE_DICT (pd); if ( ! dict->disable_insert_signal) - g_signal_emit (dict, signals [VARIABLE_INSERTED], 0, idx); + { + g_signal_emit (dict, signals [VARIABLE_INSERTED], 0, idx); + g_signal_emit_by_name (dict, "items-changed", idx, 1, 1); + } } static void @@ -230,12 +297,14 @@ delcb (struct dictionary *d, const struct variable *var, { g_signal_emit (pd, signals [VARIABLE_DELETED], 0, var, dict_idx, case_idx); + g_signal_emit_by_name (pd, "items-changed", dict_idx, 1, 0); } static void mutcb (struct dictionary *d, int idx, unsigned int what, const struct variable *oldvar, void *pd) { g_signal_emit (pd, signals [VARIABLE_CHANGED], 0, idx, what, oldvar); + g_signal_emit_by_name (pd, "items-changed", idx, 1, 1); } static void @@ -296,6 +365,9 @@ psppire_dict_replace_dictionary (PsppireDict *dict, struct dictionary *d) { struct variable *var = dict_get_weight (d); + guint old_n = dict_get_var_cnt (dict->dict); + guint new_n = dict_get_var_cnt (d); + dict->dict = d; weight_changed_callback (d, var ? var_get_dict_index (var) : -1, dict); @@ -307,7 +379,7 @@ psppire_dict_replace_dictionary (PsppireDict *dict, struct dictionary *d) dict_set_callbacks (dict->dict, &gui_callbacks, dict); - g_signal_emit (dict, signals [BACKEND_CHANGED], 0); + g_signal_emit_by_name (dict, "items-changed", 0, old_n, new_n); } @@ -371,6 +443,7 @@ psppire_dict_insert_variable (PsppireDict *d, gint idx, const gchar *name) d->disable_insert_signal = FALSE; g_signal_emit (d, signals[VARIABLE_INSERTED], 0, idx); + g_signal_emit_by_name (d, "items-changed", idx, 0, 1); return var; } @@ -499,7 +572,7 @@ psppire_dict_clear (PsppireDict *d) } -/* Return true is NAME would be a valid name of a variable to add to the +/* Return true if NAME would be a valid name of a variable to add to the dictionary. False otherwise. If REPORT is true, then invalid names will be reported as such as errors */ @@ -617,24 +690,34 @@ tree_model_column_type (GtkTreeModel *model, gint index) { g_return_val_if_fail (PSPPIRE_IS_DICT (model), (GType) 0); + GType t = 0; + switch (index) { case DICT_TVM_COL_NAME: - return G_TYPE_STRING; + case DICT_TVM_COL_LABEL: + t = G_TYPE_STRING; + break; + case DICT_TVM_COL_DECIMAL: + case DICT_TVM_COL_WIDTH: + case DICT_TVM_COL_COLUMNS: + t = G_TYPE_INT; break; case DICT_TVM_COL_VAR: - return PSPPIRE_VAR_PTR_TYPE; + t = PSPPIRE_VAR_PTR_TYPE; break; - case DICT_TVM_COL_LABEL: - return G_TYPE_STRING; + case DICT_TVM_COL_ALIGNMENT: + t = align_enum_type; break; - default: - g_return_val_if_reached ((GType)0); + case DICT_TVM_COL_MEASURE: + t = measure_enum_type; + break; + case DICT_TVM_COL_ROLE: + t = role_enum_type; break; } - g_assert_not_reached (); - return ((GType)0); + return t; } static gboolean @@ -722,6 +805,7 @@ tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) return path; } +const struct fmt_spec *var_get_write_format (const struct variable *); static void tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, @@ -732,22 +816,51 @@ tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, g_return_if_fail (iter->stamp == dict->stamp); - var = iter->user_data; + var = iter->user_data; + + const struct fmt_spec *fs = var_get_write_format (var); switch (column) { case DICT_TVM_COL_NAME: - { - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, var_get_name (var)); - } + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, var_get_name (var)); + break; + case DICT_TVM_COL_WIDTH: + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, fs->w); + break; + case DICT_TVM_COL_DECIMAL: + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, fs->d); + break; + case DICT_TVM_COL_LABEL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, var_get_label (var)); + break; + case DICT_TVM_COL_COLUMNS: + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, var_get_display_width (var)); + break; + case DICT_TVM_COL_ALIGNMENT: + g_value_init (value, align_enum_type); + g_value_set_enum (value, var_get_alignment (var)); + break; + case DICT_TVM_COL_MEASURE: + g_value_init (value, measure_enum_type); + g_value_set_enum (value, var_get_measure (var)); + break; + case DICT_TVM_COL_ROLE: + g_value_init (value, role_enum_type); + g_value_set_enum (value, var_get_role (var)); break; case DICT_TVM_COL_VAR: g_value_init (value, PSPPIRE_VAR_PTR_TYPE); g_value_set_boxed (value, var); break; default: - g_return_if_reached (); + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, "????"); break; } } @@ -766,7 +879,7 @@ tree_model_n_children (GtkTreeModel *model, { PsppireDict *dict = PSPPIRE_DICT (model); - if ( iter == NULL ) + if (iter == NULL) return psppire_dict_get_var_cnt (dict); return 0; @@ -844,8 +957,6 @@ psppire_dict_dump (const PsppireDict *dict) #endif - - const gchar * psppire_dict_encoding (const PsppireDict *dict) { diff --git a/src/ui/gui/psppire-dict.h b/src/ui/gui/psppire-dict.h index ecd5de063f..4d78807cba 100644 --- a/src/ui/gui/psppire-dict.h +++ b/src/ui/gui/psppire-dict.h @@ -42,7 +42,19 @@ G_BEGIN_DECLS typedef struct _PsppireDict PsppireDict; typedef struct _PsppireDictClass PsppireDictClass; -enum {DICT_TVM_COL_NAME=0, DICT_TVM_COL_VAR, DICT_TVM_COL_LABEL, n_DICT_COLS} ; +enum {DICT_TVM_COL_NAME=0, + DICT_TVM_COL_TYPE, + DICT_TVM_COL_WIDTH, + DICT_TVM_COL_DECIMAL, + DICT_TVM_COL_LABEL, + DICT_TVM_COL_VALUE_LABELS, + DICT_TVM_COL_MISSING_VALUES, + DICT_TVM_COL_COLUMNS, + DICT_TVM_COL_ALIGNMENT, + DICT_TVM_COL_MEASURE, + DICT_TVM_COL_ROLE, + DICT_TVM_COL_VAR, + n_DICT_COLS} ; struct _PsppireDict { diff --git a/src/ui/gui/psppire-empty-list-store.c b/src/ui/gui/psppire-empty-list-store.c deleted file mode 100644 index 1658d1fde5..0000000000 --- a/src/ui/gui/psppire-empty-list-store.c +++ /dev/null @@ -1,383 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#include - -#include "psppire-empty-list-store.h" - -static void psppire_empty_list_store_class_init (PsppireEmptyListStoreClass *); -static void psppire_empty_list_store_init (PsppireEmptyListStore *); - -/* GtkTreeModel interface. */ -static void gtk_tree_model_interface_init (GtkTreeModelIface *iface); -static GtkTreeModelFlags empty_list_store_get_flags (GtkTreeModel *tree_model); -static gint empty_list_store_get_n_columns (GtkTreeModel *tree_model); -static GType empty_list_store_get_column_type (GtkTreeModel *tree_model, - gint index_); -static gboolean empty_list_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath * empty_list_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void empty_list_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value); -static gboolean empty_list_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean empty_list_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean empty_list_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gint empty_list_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean empty_list_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n); -static gboolean empty_list_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); - -GType -psppire_empty_list_store_get_type (void) -{ - static GType type = 0; - if (!type) - { - static const GTypeInfo psppire_empty_list_store_info = - { - sizeof(PsppireEmptyListStoreClass), - NULL, /* base init */ - NULL, /* base finalize */ - (GClassInitFunc) psppire_empty_list_store_class_init, - NULL, /* class finalize */ - NULL, /* class data */ - sizeof(PsppireEmptyListStore), - 0, /* n_preallocs, ignored since 2.10 */ - (GInstanceInitFunc) psppire_empty_list_store_init, - NULL - }; - static const GInterfaceInfo gtk_tree_model_info = - { - (GInterfaceInitFunc) gtk_tree_model_interface_init, - (GInterfaceFinalizeFunc) NULL, - NULL - }; - type = g_type_register_static (G_TYPE_OBJECT, - "PsppireEmptyListStore", - &psppire_empty_list_store_info, 0); - g_type_add_interface_static (type, GTK_TYPE_TREE_MODEL, - >k_tree_model_info); - } - return type; -} - -enum - { - PROP_0, - PROP_N_ROWS - }; - -static void -psppire_empty_list_store_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireEmptyListStore *obj = PSPPIRE_EMPTY_LIST_STORE (object); - - switch (prop_id) - { - case PROP_N_ROWS: - obj->n_rows = g_value_get_int (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_empty_list_store_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireEmptyListStore *obj = PSPPIRE_EMPTY_LIST_STORE (object); - - switch (prop_id) - { - case PROP_N_ROWS: - g_value_set_int (value, obj->n_rows); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_empty_list_store_class_init (PsppireEmptyListStoreClass *class) -{ - GObjectClass *gobject_class; - gobject_class = G_OBJECT_CLASS (class); - - gobject_class->set_property = psppire_empty_list_store_set_property; - gobject_class->get_property = psppire_empty_list_store_get_property; - - g_object_class_install_property (gobject_class, - PROP_N_ROWS, - g_param_spec_int ("n-rows", - ("Number of rows"), - ("Number of rows in GtkTreeModel"), - 0, - G_MAXINT, - 0, - G_PARAM_READWRITE)); -} - -static void -psppire_empty_list_store_init (PsppireEmptyListStore *obj) -{ - obj->n_rows = 0; -} - -PsppireEmptyListStore * -psppire_empty_list_store_new (gint n_rows) -{ - return PSPPIRE_EMPTY_LIST_STORE (g_object_new (PSPPIRE_TYPE_EMPTY_LIST_STORE, - "n-rows", n_rows, - NULL)); -} - -gint -psppire_empty_list_store_get_n_rows (const PsppireEmptyListStore *obj) -{ - return obj->n_rows; -} - -void -psppire_empty_list_store_set_n_rows (PsppireEmptyListStore *obj, - gint n_rows) -{ - obj->n_rows = n_rows; -} - -void -psppire_empty_list_store_row_changed (PsppireEmptyListStore *obj, - gint row) -{ - GtkTreeModel *tree_model = GTK_TREE_MODEL (obj); - GtkTreeIter iter; - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (row, -1); - gtk_tree_model_get_iter (tree_model, &iter, path); - gtk_tree_model_row_changed (tree_model, path, &iter); - gtk_tree_path_free (path); -} - -void -psppire_empty_list_store_row_inserted (PsppireEmptyListStore *obj, - gint row) -{ - GtkTreeModel *tree_model = GTK_TREE_MODEL (obj); - GtkTreeIter iter; - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (row, -1); - gtk_tree_model_get_iter (tree_model, &iter, path); - gtk_tree_model_row_inserted (tree_model, path, &iter); - gtk_tree_path_free (path); -} - -void -psppire_empty_list_store_row_deleted (PsppireEmptyListStore *obj, - gint row) -{ - GtkTreeModel *tree_model = GTK_TREE_MODEL (obj); - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (row, -1); - gtk_tree_model_row_deleted (tree_model, path); - gtk_tree_path_free (path); -} - -/* GtkTreeModel interface. */ - -/* Random number used in 'stamp' member of GtkTreeIter. */ -#define TREE_MODEL_STAMP 0x10c44c13 - -static gboolean -empty_list_store_init_iter (GtkTreeModel *model, gint idx, GtkTreeIter *iter) -{ - const PsppireEmptyListStore *store = PSPPIRE_EMPTY_LIST_STORE (model); - - if (idx < 0 || idx >= store->n_rows) - { - iter->stamp = 0; - iter->user_data = GINT_TO_POINTER (-1); - return FALSE; - } - else - { - iter->stamp = TREE_MODEL_STAMP; - iter->user_data = GINT_TO_POINTER (idx); - return TRUE; - } -} - -static void -gtk_tree_model_interface_init (GtkTreeModelIface *iface) -{ - g_return_if_fail (iface != NULL); - - iface->get_flags = empty_list_store_get_flags; - iface->get_n_columns = empty_list_store_get_n_columns; - iface->get_column_type = empty_list_store_get_column_type; - iface->get_iter = empty_list_store_get_iter; - iface->get_path = empty_list_store_get_path; - iface->get_value = empty_list_store_get_value; - iface->iter_next = empty_list_store_iter_next; - iface->iter_children = empty_list_store_iter_children; - iface->iter_has_child = empty_list_store_iter_has_child; - iface->iter_n_children = empty_list_store_iter_n_children; - iface->iter_nth_child = empty_list_store_iter_nth_child; - iface->iter_parent = empty_list_store_iter_parent; -} - -static GtkTreeModelFlags -empty_list_store_get_flags (GtkTreeModel *tree_model G_GNUC_UNUSED) -{ - return GTK_TREE_MODEL_LIST_ONLY; -} - -static gint -empty_list_store_get_n_columns (GtkTreeModel *tree_model G_GNUC_UNUSED) -{ - return 0; -} - -static GType -empty_list_store_get_column_type (GtkTreeModel *tree_model, - gint index_) -{ - g_return_val_if_reached (G_TYPE_NONE); -} - -static gboolean -empty_list_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - gint *indices, depth; - - g_return_val_if_fail (path, FALSE); - - indices = gtk_tree_path_get_indices (path); - depth = gtk_tree_path_get_depth (path); - - g_return_val_if_fail (depth == 1, FALSE); - - return empty_list_store_init_iter (tree_model, indices[0], iter); -} - -static GtkTreePath * -empty_list_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreePath *path; - - g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE); - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data)); - - return path; -} - -static void -empty_list_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - g_return_if_reached (); -} - -static gboolean -empty_list_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - gint idx; - - g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE); - - idx = GPOINTER_TO_INT (iter->user_data); - return empty_list_store_init_iter (tree_model, idx + (idx >= 0), iter); -} - -static gboolean -empty_list_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - return FALSE; -} - -static gboolean -empty_list_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return FALSE; -} - -static gint -empty_list_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return iter == NULL ? PSPPIRE_EMPTY_LIST_STORE (tree_model)->n_rows : 0; -} - -static gboolean -empty_list_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) -{ - g_return_val_if_fail (parent == NULL, FALSE); - - return empty_list_store_init_iter (tree_model, n, iter); -} - -static gboolean -empty_list_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - return FALSE; -} - -gint -empty_list_store_iter_to_row (const GtkTreeIter *iter) -{ - g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, 0); - return GPOINTER_TO_INT (iter->user_data); -} diff --git a/src/ui/gui/psppire-empty-list-store.h b/src/ui/gui/psppire-empty-list-store.h deleted file mode 100644 index c2078439d1..0000000000 --- a/src/ui/gui/psppire-empty-list-store.h +++ /dev/null @@ -1,70 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#ifndef PSPPIRE_EMPTY_LIST_STORE_H -#define PSPPIRE_EMPTY_LIST_STORE_H 1 - -/* PsppireEmptyListStore is an GtkTreeModel implementation that has a - client-specified number of rows and zero columns. It is a useful model for - GtkTreeView or PsppSheetView when the client can easily synthesize cell data - using a callback set with gtk_tree_view_column_set_cell_data_func(). In - that situation, GtkListStore can be wasteful (because it uses a lot of - memory to store what does not need to be stored) and situation-specific - custom models require additional boilerplate. */ - -#include -#include - -G_BEGIN_DECLS - -#define PSPPIRE_TYPE_EMPTY_LIST_STORE (psppire_empty_list_store_get_type()) -#define PSPPIRE_EMPTY_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),PSPPIRE_TYPE_EMPTY_LIST_STORE,PsppireEmptyListStore)) -#define PSPPIRE_EMPTY_LIST_STORE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),PSPPIRE_TYPE_EMPTY_LIST_STORE,PsppireEmptyListStoreClass)) -#define PSPPIRE_IS_EMPTY_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),PSPPIRE_TYPE_EMPTY_LIST_STORE)) -#define PSPPIRE_IS_EMPTY_LIST_STORE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),PSPPIRE_TYPE_EMPTY_LIST_STORE)) -#define PSPPIRE_EMPTY_LIST_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),PSPPIRE_TYPE_EMPTY_LIST_STORE,PsppireEmptyListStoreClass)) - -typedef struct _PsppireEmptyListStore PsppireEmptyListStore; -typedef struct _PsppireEmptyListStoreClass PsppireEmptyListStoreClass; - -struct _PsppireEmptyListStore { - GObject parent; - gint n_rows; -}; - -struct _PsppireEmptyListStoreClass { - GObjectClass parent_class; -}; - -GType psppire_empty_list_store_get_type (void) G_GNUC_CONST; -PsppireEmptyListStore* psppire_empty_list_store_new (gint n_rows); - -gint psppire_empty_list_store_get_n_rows (const PsppireEmptyListStore *); -void psppire_empty_list_store_set_n_rows (PsppireEmptyListStore *, - gint n_rows); - -void psppire_empty_list_store_row_changed (PsppireEmptyListStore *, - gint row); -void psppire_empty_list_store_row_inserted (PsppireEmptyListStore *, - gint row); -void psppire_empty_list_store_row_deleted (PsppireEmptyListStore *, - gint row); - -gint empty_list_store_iter_to_row (const GtkTreeIter *); - -G_END_DECLS - -#endif /* PSPPIRE_EMPTY_LIST_STORE_H */ diff --git a/src/ui/gui/psppire-import-assistant.c b/src/ui/gui/psppire-import-assistant.c index 487c89b68e..f38438b375 100644 --- a/src/ui/gui/psppire-import-assistant.c +++ b/src/ui/gui/psppire-import-assistant.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2015, 2016 Free Software Foundation + Copyright (C) 2015, 2016, 2017 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 @@ -14,13 +14,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - #include -#include -#include #include -#include #include "data/casereader.h" #include "data/data-in.h" @@ -32,25 +28,29 @@ #include "data/ods-reader.h" #include "data/spreadsheet-reader.h" #include "data/value-labels.h" - -#include "gl/intprops.h" +#include "data/casereader-provider.h" #include "libpspp/i18n.h" #include "libpspp/line-reader.h" #include "libpspp/message.h" +#include "libpspp/hmap.h" +#include "libpspp/hash-functions.h" #include "libpspp/str.h" #include "builder-wrapper.h" -#include "helper.h" -#include "pspp-sheet-view.h" -#include "pspp-sheet-selection.h" -#include "psppire-import-assistant.h" -#include "psppire-scanf.h" + +#include "psppire-data-sheet.h" +#include "psppire-data-store.h" #include "psppire-dialog.h" -#include "psppire-empty-list-store.h" +#include "psppire-delimited-text.h" +#include "psppire-dict.h" #include "psppire-encoding-selector.h" +#include "psppire-import-assistant.h" +#include "psppire-scanf.h" #include "psppire-spreadsheet-model.h" -#include "psppire-var-sheet.h" +#include "psppire-text-file.h" +#include "psppire-variable-sheet.h" + #include "ui/syntax-gen.h" #include @@ -60,37 +60,15 @@ enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */ -/* Sets IA's separators substructure to match the widgets. */ -static void get_separators (PsppireImportAssistant *ia); -static void split_fields (PsppireImportAssistant *ia); - /* Chooses a name for each column on the separators page */ static void choose_column_names (PsppireImportAssistant *ia); - -/* Frees IA's file substructure. */ -static void destroy_file (PsppireImportAssistant *ia); - -static void clear_fields (PsppireImportAssistant *ia); - - static void intro_page_create (PsppireImportAssistant *ia); static void first_line_page_create (PsppireImportAssistant *ia); -static gboolean process_file (PsppireImportAssistant *ia); - - -static GtkWidget * create_data_tree_view (gboolean input, GtkContainer *parent, - PsppireImportAssistant *ia); - static void separators_page_create (PsppireImportAssistant *ia); static void formats_page_create (PsppireImportAssistant *ia); -static void push_watch_cursor (PsppireImportAssistant *ia); -static void pop_watch_cursor (PsppireImportAssistant *ia); - - - static void psppire_import_assistant_init (PsppireImportAssistant *act); static void psppire_import_assistant_class_init (PsppireImportAssistantClass *class); @@ -139,29 +117,20 @@ psppire_import_assistant_get_property (GObject *object, static GObjectClass * parent_class = NULL; -static void destroy_columns (PsppireImportAssistant *ia); - static void psppire_import_assistant_finalize (GObject *object) { PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (object); - if (ia->spreadsheet) spreadsheet_unref (ia->spreadsheet); - // clear_fields (ia); - destroy_columns (ia); - - ds_destroy (&ia->separators); ds_destroy (&ia->quotes); g_object_unref (ia->builder); - destroy_file (ia); - - g_object_unref (ia->prop_renderer); - g_object_unref (ia->fixed_renderer); + ia->response = -1; + g_main_loop_unref (ia->main_loop); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); @@ -201,56 +170,44 @@ on_paste (GtkButton *button, PsppireImportAssistant *ia) close_assistant (ia, PSPPIRE_RESPONSE_PASTE); } + /* Revises the contents of the fields tree view based on the currently chosen set of separators. */ static void revise_fields_preview (PsppireImportAssistant *ia) { - push_watch_cursor (ia); - - get_separators (ia); - split_fields (ia); choose_column_names (ia); - ia->fields_tree_view = - GTK_WIDGET (create_data_tree_view (TRUE, - GTK_CONTAINER (get_widget_assert (ia->builder, "fields-scroller")), - ia)); - - pop_watch_cursor (ia); } -/* Chooses the most common character among those in TARGETS, - based on the frequency data in HISTOGRAM, and stores it in - RESULT. If there is a tie for the most common character among - those in TARGETS, the earliest character is chosen. If none - of the TARGETS appear at all, then DEF is used as a - fallback. */ -static void -find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1], - const char *targets, const char *def, - struct string *result) + +struct separator { - unsigned char max = 0; - unsigned long int max_count = 0; + const char *name; /* Name (for use with get_widget_assert). */ + gunichar c; /* Separator character. */ +}; - for (; *targets != '\0'; targets++) - { - unsigned char c = *targets; - unsigned long int count = histogram[c]; - if (count > max_count) - { - max = c; - max_count = count; - } - } - if (max_count > 0) - { - ds_clear (result); - ds_put_byte (result, max); - } - else - ds_assign_cstr (result, def); -} +/* All the separators in the dialog box. */ +static const struct separator separators[] = + { + {"space", ' '}, + {"tab", '\t'}, + {"bang", '!'}, + {"colon", ':'}, + {"comma", ','}, + {"hyphen", '-'}, + {"pipe", '|'}, + {"semicolon", ';'}, + {"slash", '/'}, + }; + +#define SEPARATOR_CNT (sizeof separators / sizeof *separators) + +struct separator_count_node +{ + struct hmap_node node; + int occurance; /* The number of times the separator occurs in a line */ + int quantity; /* The number of lines with this occurance */ +}; /* Picks the most likely separator and quote characters based on @@ -258,115 +215,176 @@ find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1], static void choose_likely_separators (PsppireImportAssistant *ia) { - unsigned long int histogram[UCHAR_MAX + 1] = { 0 }; - size_t row; + gint first_line = 0; + g_object_get (ia->delimiters_model, "first-line", &first_line, NULL); + + gboolean valid; + GtkTreeIter iter; + int j; + + struct hmap count_map[SEPARATOR_CNT]; + for (j = 0; j < SEPARATOR_CNT; ++j) + hmap_init (count_map + j); - /* Construct a histogram of all the characters used in the - file. */ - for (row = 0; row < ia->line_cnt; row++) + GtkTreePath *p = gtk_tree_path_new_from_indices (first_line, -1); + + for (valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (ia->text_file), &iter, p); + valid; + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->text_file), &iter)) { - struct substring line = ds_ss (&ia->lines[row]); - size_t length = ss_length (line); - size_t i; - for (i = 0; i < length; i++) - histogram[(unsigned char) line.string[i]]++; - } + gchar *line_text = NULL; + gtk_tree_model_get (GTK_TREE_MODEL (ia->text_file), &iter, 1, &line_text, -1); - find_commonest_chars (histogram, "\"'", "", &ia->quotes); - find_commonest_chars (histogram, ",;:/|!\t-", ",", &ia->separators); -} + gint *counts = xzalloc (sizeof *counts * SEPARATOR_CNT); + struct substring cs = ss_cstr (line_text); + for (; + UINT32_MAX != ss_first_mb (cs); + ss_get_mb (&cs)) + { + ucs4_t character = ss_first_mb (cs); -static void set_separators (PsppireImportAssistant *ia); + int s; + for (s = 0; s < SEPARATOR_CNT; ++s) + { + if (character == separators[s].c) + counts[s]++; + } + } + + int j; + for (j = 0; j < SEPARATOR_CNT; ++j) + { + if (counts[j] > 0) + { + struct separator_count_node *cn = NULL; + unsigned int hash = hash_int (counts[j], 0); + HMAP_FOR_EACH_WITH_HASH (cn, struct separator_count_node, node, hash, &count_map[j]) + { + if (cn->occurance == counts[j]) + break; + } + + if (cn == NULL) + { + struct separator_count_node *new_cn = xzalloc (sizeof *new_cn); + new_cn->occurance = counts[j]; + new_cn->quantity = 1; + hmap_insert (&count_map[j], &new_cn->node, hash); + } + else + cn->quantity++; + } + } + + free (line_text); + free (counts); + } + gtk_tree_path_free (p); + + int most_frequent = -1; + int largest = 0; + for (j = 0; j < SEPARATOR_CNT; ++j) + { + struct separator_count_node *cn; + HMAP_FOR_EACH (cn, struct separator_count_node, node, &count_map[j]) + { + if (largest < cn->quantity) + { + largest = cn->quantity; + most_frequent = j; + } + } + hmap_destroy (&count_map[j]); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, separators[most_frequent].name)), TRUE); +} -/* Called just before the separators page becomes visible in the - assistant, and when the Reset button is clicked. */ static void -prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *page) +repopulate_delimiter_columns (PsppireImportAssistant *ia) { - revise_fields_preview (ia); - choose_likely_separators (ia); - set_separators (ia); + /* Remove all the columns */ + while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0) + { + GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0); + gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc); + } + + gint n_fields = + gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)); + + /* ... and put them back again. */ + gint f; + for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)); + f < n_fields; f++) + { + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + + const gchar *title = NULL; + + if (f == 0) + title = _("line"); + else + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb))) + { + title = + psppire_delimited_text_get_header_title + (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1); + } + if (title == NULL) + title = _("var"); + } + + GtkTreeViewColumn *column = + gtk_tree_view_column_new_with_attributes (title, + renderer, + "text", f, + NULL); + g_object_set (column, + "resizable", TRUE, + "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, + NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column); + } } -struct separator +static void +reset_tree_view_model (PsppireImportAssistant *ia) { - const char *name; /* Name (for use with get_widget_assert). */ - int c; /* Separator character. */ -}; + GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view)); + g_object_ref (tm); + gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL); -/* All the separators in the dialog box. */ -static const struct separator separators[] = - { - {"space", ' '}, - {"tab", '\t'}, - {"bang", '!'}, - {"colon", ':'}, - {"comma", ','}, - {"hyphen", '-'}, - {"pipe", '|'}, - {"semicolon", ';'}, - {"slash", '/'}, - }; -#define SEPARATOR_CNT (sizeof separators / sizeof *separators) + repopulate_delimiter_columns (ia); + gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm); + // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view)); + + g_object_unref (tm); +} -/* Sets the widgets to match IA's separators substructure. */ +/* Called just before the separators page becomes visible in the + assistant, and when the Reset button is clicked. */ static void -set_separators (PsppireImportAssistant *ia) +prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *page) { - unsigned int seps; - struct string custom; - bool any_custom; - bool any_quotes; - size_t i; + gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), + GTK_TREE_MODEL (ia->delimiters_model)); - ds_init_empty (&custom); - seps = 0; - for (i = 0; i < ds_length (&ia->separators); i++) - { - unsigned char c = ds_at (&ia->separators, i); - int j; + g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters", + G_CALLBACK (reset_tree_view_model), ia); - for (j = 0; j < SEPARATOR_CNT; j++) - { - const struct separator *s = &separators[j]; - if (s->c == c) - { - seps += 1u << j; - goto next; - } - } - ds_put_byte (&custom, c); - next:; - } + repopulate_delimiter_columns (ia); - for (i = 0; i < SEPARATOR_CNT; i++) - { - const struct separator *s = &separators[i]; - GtkWidget *button = get_widget_assert (ia->builder, s->name); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), - (seps & (1u << i)) != 0); - } - any_custom = !ds_is_empty (&custom); - gtk_entry_set_text (GTK_ENTRY (ia->custom_entry), ds_cstr (&custom)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->custom_cb), - any_custom); - gtk_widget_set_sensitive (ia->custom_entry, any_custom); - ds_destroy (&custom); - - any_quotes = !ds_is_empty (&ia->quotes); - - gtk_entry_set_text (ia->quote_entry, - any_quotes ? ds_cstr (&ia->quotes) : "\""); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->quote_cb), - any_quotes); - gtk_widget_set_sensitive (ia->quote_combo, any_quotes); + revise_fields_preview (ia); + choose_likely_separators (ia); } - /* Resets IA's intro page to its initial state. */ static void reset_intro_page (PsppireImportAssistant *ia) @@ -384,13 +402,6 @@ reset_intro_page (PsppireImportAssistant *ia) static void reset_formats_page (PsppireImportAssistant *ia, GtkWidget *page) { - size_t i; - - for (i = 0; i < ia->modified_var_cnt; i++) - var_destroy (ia->modified_vars[i]); - free (ia->modified_vars); - ia->modified_vars = NULL; - ia->modified_var_cnt = 0; } static void prepare_formats_page (PsppireImportAssistant *ia); @@ -478,215 +489,6 @@ on_close (GtkAssistant *assistant, PsppireImportAssistant *ia) } -/* Frees IA's file substructure. */ -static void -destroy_file (PsppireImportAssistant *ia) -{ - size_t i; - - for (i = 0; i < ia->line_cnt; i++) - ds_destroy (&ia->lines[i]); - - g_free (ia->file_name); - ia->file_name = NULL; - - g_free (ia->encoding); - ia->encoding = NULL; -} - - -/* Increments the "watch cursor" level, setting the cursor for - the assistant window to a watch face to indicate to the user - that the ongoing operation may take some time. */ -static void -push_watch_cursor (PsppireImportAssistant *ia) -{ - if (++ia->watch_cursor == 1) - { - GtkWidget *widget = GTK_WIDGET (ia); - GdkDisplay *display = gtk_widget_get_display (widget); - GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH); - gdk_window_set_cursor (gtk_widget_get_window (widget), cursor); - g_object_unref (cursor); - gdk_display_flush (display); - } -} - -/* Decrements the "watch cursor" level. If the level reaches - zero, the cursor is reset to its default shape. */ -static void -pop_watch_cursor (PsppireImportAssistant *ia) -{ - if (--ia->watch_cursor == 0) - { - GtkWidget *widget = GTK_WIDGET (ia); - gdk_window_set_cursor (gtk_widget_get_window (widget), NULL); - } -} - - -static gboolean -process_file (PsppireImportAssistant *ia) -{ - struct string input; - struct line_reader *reader = line_reader_for_file (ia->encoding, ia->file_name, O_RDONLY); - if (reader == NULL) - { - msg_error (errno, _("Could not open `%s'"), - ia->file_name); - return FALSE; - } - - ds_init_empty (&input); - for (ia->line_cnt = 0; ia->line_cnt < MAX_PREVIEW_LINES; ia->line_cnt++) - { - ds_clear (&input); - if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1) - || ds_length (&input) > MAX_LINE_LEN) - { - if (line_reader_eof (reader)) - break; - else if (line_reader_error (reader)) - msg (ME, _("Error reading `%s': %s"), - ia->file_name, strerror (line_reader_error (reader))); - else - msg (ME, _("Failed to read `%s', because it contains a line " - "over %d bytes long and therefore appears not to be " - "a text file."), - ia->file_name, MAX_LINE_LEN); - line_reader_close (reader); - destroy_file (ia); - ds_destroy (&input); - return FALSE; - } - - char *s = recode_string ("UTF-8", line_reader_get_encoding (reader), ds_cstr (&input), ds_length (&input)); - ds_init_cstr (&ia->lines[ia->line_cnt], s); - free (s); - } - ds_destroy (&input); - if (ia->line_cnt == 0) - { - msg (ME, _("`%s' is empty."), ia->file_name); - line_reader_close (reader); - destroy_file (ia); - return FALSE; - } - - /* Estimate the number of lines in the file. */ - if (ia->line_cnt < MAX_PREVIEW_LINES) - { - ia->total_lines = ia->line_cnt; - ia->total_is_exact = true; - } - else - { - struct stat s; - off_t position = line_reader_tell (reader); - if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0) - { - ia->total_lines = (double) ia->line_cnt / position * s.st_size; - ia->total_is_exact = false; - } - else - { - ia->total_lines = 0; - ia->total_is_exact = true; - } - } - line_reader_close (reader); - return TRUE; -} - - -static void -render_line_number (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - gint row = empty_list_store_iter_to_row (iter); - char s[INT_BUFSIZE_BOUND (int)]; - int first_line = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model), - "first-line")); - sprintf (s, "%d", first_line + row); - g_object_set (cell, "text", s, NULL); -} - - - -static gint -get_string_width (GtkWidget *treeview, GtkCellRenderer *renderer, - const char *string) -{ - gint width; - g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL); - gtk_cell_renderer_get_preferred_width (renderer, treeview, - NULL, &width); - return width; -} - - -static gint -get_monospace_width (GtkWidget *treeview, GtkCellRenderer *renderer, - size_t char_cnt) -{ - struct string s; - gint width; - - ds_init_empty (&s); - ds_put_byte_multiple (&s, '0', char_cnt); - ds_put_byte (&s, ' '); - width = get_string_width (treeview, renderer, ds_cstr (&s)); - ds_destroy (&s); - - return width; -} - -static void -add_line_number_column (const PsppireImportAssistant *ia, - GtkWidget *treeview) -{ - PsppSheetViewColumn *column = - pspp_sheet_view_column_new_with_attributes (_("Line"), ia->prop_renderer, (void *) NULL); - - pspp_sheet_view_column_set_fixed_width (column, get_monospace_width (treeview, ia->prop_renderer, 5)); - - pspp_sheet_view_column_set_resizable (column, TRUE); - - pspp_sheet_view_column_set_cell_data_func (column, ia->prop_renderer, - render_line_number, NULL, NULL); - - pspp_sheet_view_append_column (PSPP_SHEET_VIEW (treeview), column); -} - - -static void -set_model_on_treeview (PsppireImportAssistant *ia, GtkWidget *tree_view, size_t first_line) -{ - GtkTreeModel *model = GTK_TREE_MODEL (psppire_empty_list_store_new (ia->line_cnt - first_line)); - - g_object_set_data (G_OBJECT (model), "lines", &ia->lines + first_line); - g_object_set_data (G_OBJECT (model), "first-line", GINT_TO_POINTER (first_line)); - - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (tree_view), model); - - g_object_unref (model); -} - - -static GtkWidget * -make_tree_view (const PsppireImportAssistant *ia) -{ - GtkWidget *tree_view = pspp_sheet_view_new (); - pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_GRID_LINES_BOTH); - - add_line_number_column (ia, tree_view); - - return tree_view; -} - static GtkWidget * add_page_to_assistant (PsppireImportAssistant *ia, GtkWidget *page, GtkAssistantPageType type, const gchar *); @@ -724,7 +526,6 @@ prepare_sheet_spec_page (PsppireImportAssistant *ia) } - /* Initializes IA's sheet_spec substructure. */ static void sheet_spec_page_create (PsppireImportAssistant *ia) @@ -748,7 +549,6 @@ sheet_spec_page_create (PsppireImportAssistant *ia) g_object_set_data (G_OBJECT (page), "on-entering", prepare_sheet_spec_page); } - static void on_chosen (PsppireImportAssistant *ia, GtkWidget *page) { @@ -761,7 +561,7 @@ on_chosen (PsppireImportAssistant *ia, GtkWidget *page) gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), FALSE); - if (f && !g_file_test (f, G_FILE_TEST_IS_DIR)) + if (f && g_file_test (f, G_FILE_TEST_IS_REGULAR)) { gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), TRUE); @@ -773,15 +573,15 @@ on_chosen (PsppireImportAssistant *ia, GtkWidget *page) if (!ia->spreadsheet) ia->spreadsheet = ods_probe (f, FALSE); - if (!ia->spreadsheet) + if (ia->spreadsheet) { - intro_page_create (ia); - first_line_page_create (ia); - separators_page_create (ia); + sheet_spec_page_create (ia); } else { - sheet_spec_page_create (ia); + intro_page_create (ia); + first_line_page_create (ia); + separators_page_create (ia); } formats_page_create (ia); @@ -795,14 +595,18 @@ on_chosen (PsppireImportAssistant *ia, GtkWidget *page) static void on_map (PsppireImportAssistant *ia, GtkWidget *page) { +#if TEXT_FILE GtkFileChooser *fc = GTK_FILE_CHOOSER (page); if (ia->file_name) gtk_file_chooser_set_filename (fc, ia->file_name); +#endif on_chosen (ia, page); } + + static void chooser_page_enter (PsppireImportAssistant *ia, GtkWidget *page) { @@ -811,17 +615,19 @@ chooser_page_enter (PsppireImportAssistant *ia, GtkWidget *page) static void chooser_page_leave (PsppireImportAssistant *ia, GtkWidget *page) { - - if (ia->file_name) - g_free (ia->file_name); + g_free (ia->file_name); ia->file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (page)); - - if (ia->encoding) - g_free (ia->encoding); - ia->encoding = psppire_encoding_selector_get_encoding (ia->encoding_selector); + gchar *encoding = psppire_encoding_selector_get_encoding (ia->encoding_selector); if (!ia->spreadsheet) - process_file (ia); + { + ia->text_file = psppire_text_file_new (ia->file_name, encoding); + gtk_tree_view_set_model (GTK_TREE_VIEW (ia->first_line_tree_view), + GTK_TREE_MODEL (ia->text_file)); + } + + + g_free (encoding); } static void @@ -835,6 +641,8 @@ chooser_page_reset (PsppireImportAssistant *ia, GtkWidget *page) on_chosen (ia, page); } + + static void chooser_page_create (PsppireImportAssistant *ia) { @@ -909,27 +717,18 @@ chooser_page_create (PsppireImportAssistant *ia) } + static void psppire_import_assistant_init (PsppireImportAssistant *ia) { ia->builder = builder_new ("text-data-import.ui"); ia->current_page = -1 ; - ia->column_cnt = 0; - ia->columns = NULL; - ia->file_name = NULL; - ia->encoding = NULL; + ia->spreadsheet = NULL; - ia->watch_cursor = 0; - ia->prop_renderer = gtk_cell_renderer_text_new (); - g_object_ref_sink (ia->prop_renderer); - ia->fixed_renderer = gtk_cell_renderer_text_new (); - g_object_ref_sink (ia->fixed_renderer); - g_object_set (G_OBJECT (ia->fixed_renderer), - "family", "Monospace", - (void *) NULL); + ia->main_loop = g_main_loop_new (NULL, TRUE); g_signal_connect (ia, "prepare", G_CALLBACK (on_prepare), ia); g_signal_connect (ia, "cancel", G_CALLBACK (on_cancel), ia); @@ -981,157 +780,120 @@ static void on_intro_amount_changed (PsppireImportAssistant *p) { gtk_widget_set_sensitive (p->n_cases_spin, - gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (p->n_cases_button))); + gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (p->n_cases_button))); gtk_widget_set_sensitive (p->percent_spin, - gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (p->percent_button))); + gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (p->percent_button))); } - static void -render_line (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - gint row = empty_list_store_iter_to_row (iter); - struct string *lines; - - lines = g_object_get_data (G_OBJECT (tree_model), "lines"); - g_return_if_fail (lines != NULL); - - g_object_set (cell, "text", ds_cstr (&lines[row]), NULL); -} - -/* Sets the widgets to match IA's first_line substructure. */ -static void -set_first_line (PsppireImportAssistant *ia) -{ - GtkTreePath *path = gtk_tree_path_new_from_indices (ia->skip_lines, -1); - - - set_model_on_treeview (ia, ia->tree_view, 0); - - pspp_sheet_view_set_cursor (PSPP_SHEET_VIEW (ia->tree_view), - path, NULL, false); - gtk_tree_path_free (path); - - gtk_toggle_button_set_active ( - GTK_TOGGLE_BUTTON (ia->variable_names_cb), - ia->variable_names); - gtk_widget_set_sensitive (ia->variable_names_cb, - ia->skip_lines > 0); -} - - -/* Creates and returns a tree view that contains each of the - lines in IA's file as a row. */ -static GtkWidget * -create_lines_tree_view (GtkContainer *parent, PsppireImportAssistant *ia) -{ - size_t max_line_length; - gint content_width, header_width; - size_t i; - const gchar *title = _("Text"); - GtkWidget *tree_view = make_tree_view (ia); - PsppSheetViewColumn *column = - pspp_sheet_view_column_new_with_attributes (title, - ia->fixed_renderer, (void *) NULL); - - pspp_sheet_view_column_set_cell_data_func (column, ia->fixed_renderer, - render_line, NULL, NULL); - pspp_sheet_view_column_set_resizable (column, TRUE); - pspp_sheet_view_column_set_expand (column, TRUE); - - max_line_length = 0; - for (i = 0; i < ia->line_cnt; i++) - { - size_t w = ds_length (&ia->lines[i]); - max_line_length = MAX (max_line_length, w); - } - - content_width = get_monospace_width (tree_view, ia->fixed_renderer, - max_line_length); - header_width = get_string_width (tree_view, ia->prop_renderer, title); - pspp_sheet_view_column_set_fixed_width (column, MAX (content_width, - header_width)); - pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), column); - - GtkWidget *oldtv = gtk_bin_get_child (GTK_BIN (parent)); - if (oldtv) - gtk_container_remove (parent, oldtv); - - gtk_container_add (parent, tree_view); - gtk_widget_show (tree_view); - - return tree_view; -} - - -/* Sets IA's first_line substructure to match the widgets. */ -static void -set_first_line_options (PsppireImportAssistant *ia) +on_treeview_selection_change (PsppireImportAssistant *ia) { + GtkTreeSelection *selection = + gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view)); + GtkTreeModel *model = NULL; GtkTreeIter iter; - GtkTreeModel *model; - - PsppSheetSelection *selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ia->tree_view)); - if (pspp_sheet_selection_get_selected (selection, &model, &iter)) + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gint max_lines; + int n; GtkTreePath *path = gtk_tree_model_get_path (model, &iter); - int row = gtk_tree_path_get_indices (path)[0]; + gint *index = gtk_tree_path_get_indices (path); + n = *index; gtk_tree_path_free (path); - - ia->skip_lines = row; - ia->variable_names = - (ia->skip_lines > 0 - && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb))); + g_object_get (model, "maximum-lines", &max_lines, NULL); + gtk_widget_set_sensitive (ia->variable_names_cb, + (n > 0 && n < max_lines)); + ia->delimiters_model = + psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file)); + g_object_set (ia->delimiters_model, "first-line", n, NULL); } - - gtk_widget_set_sensitive (ia->variable_names_cb, ia->skip_lines > 0); } static void -reset_first_line_page (PsppireImportAssistant *ia) -{ - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb), FALSE); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ia->tree_view)); - pspp_sheet_selection_unselect_all (selection); - gtk_widget_set_sensitive (ia->variable_names_cb, FALSE); +render_text_preview_line (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + /* + Set the text to a "insensitive" state if the row + is greater than what the user declared to be the maximum. + */ + GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter); + gint *ii = gtk_tree_path_get_indices (path); + gint max_lines; + g_object_get (tree_model, "maximum-lines", &max_lines, NULL); + g_object_set (cell, "sensitive", (*ii < max_lines), NULL); + gtk_tree_path_free (path); } - /* Initializes IA's first_line substructure. */ static void first_line_page_create (PsppireImportAssistant *ia) { GtkWidget *w = get_widget_assert (ia->builder, "FirstLine"); - g_object_set_data (G_OBJECT (w), "on-entering", set_first_line); + g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change); add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line")); - ia->tree_view = GTK_WIDGET (create_lines_tree_view ( - GTK_CONTAINER (get_widget_assert (ia->builder, "first-line-scroller")), ia)); - ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names"); - pspp_sheet_selection_set_mode ( - pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ia->tree_view)), - PSPP_SHEET_SELECTION_BROWSE); - pspp_sheet_view_set_rubber_banding (PSPP_SHEET_VIEW (ia->tree_view), TRUE); + GtkWidget *scrolled_window = get_widget_assert (ia->builder, "first-line-scroller"); + + if (ia->first_line_tree_view == NULL) + { + ia->first_line_tree_view = gtk_tree_view_new (); + g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE); - g_signal_connect_swapped (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ia->tree_view)), - "changed", G_CALLBACK (set_first_line_options), ia); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer, + "text", 0, + NULL); - g_signal_connect_swapped (ia->variable_names_cb, "toggled", - G_CALLBACK (set_first_line_options), ia); + gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0); + gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column); + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL); + gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0); - g_object_set_data (G_OBJECT (w), "on-reset", reset_first_line_page); + gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column); + + g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed", + G_CALLBACK (on_treeview_selection_change), ia); + gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view); + } + + gtk_widget_show_all (scrolled_window); + + ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names"); +} + +static void +intro_on_leave (PsppireImportAssistant *ia) +{ + gint lc = 0; + g_object_get (ia->text_file, "line-count", &lc, NULL); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button))) + { + gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)); + g_object_set (ia->text_file, "maximum-lines", max_lines, NULL); + } + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button))) + { + gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)); + g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL); + } + else + { + g_object_set (ia->text_file, "maximum-lines", lc, NULL); + } } @@ -1143,39 +905,39 @@ intro_on_enter (PsppireImportAssistant *ia) struct string s; - if (ia->line_cnt > MAX_PREVIEW_LINES) - ia->line_cnt = MAX_PREVIEW_LINES; - ds_init_empty (&s); ds_put_cstr (&s, _("This assistant will guide you through the process of " "importing data into PSPP from a text file with one line " "per case, in which fields are separated by tabs, " "commas, or other delimiters.\n\n")); - if (ia->total_is_exact) - { - ds_put_format ( - &s, ngettext ("The selected file contains %'lu line of text. ", - "The selected file contains %'lu lines of text. ", - ia->total_lines), - ia->total_lines); - } - else if (ia->total_lines > 0) + if (ia->text_file) { - ds_put_format ( - &s, ngettext ( - "The selected file contains approximately %'lu line of text. ", - "The selected file contains approximately %'lu lines of text. ", - ia->total_lines), - ia->total_lines); - ds_put_format ( - &s, ngettext ( - "Only the first %zu line of the file will be shown for " - "preview purposes in the following screens. ", - "Only the first %zu lines of the file will be shown for " - "preview purposes in the following screens. ", - ia->line_cnt), - ia->line_cnt); + if (ia->text_file->total_is_exact) + { + ds_put_format ( + &s, ngettext ("The selected file contains %'lu line of text. ", + "The selected file contains %'lu lines of text. ", + ia->text_file->total_lines), + ia->text_file->total_lines); + } + else if (ia->text_file->total_lines > 0) + { + ds_put_format ( + &s, ngettext ( + "The selected file contains approximately %'lu line of text. ", + "The selected file contains approximately %'lu lines of text. ", + ia->text_file->total_lines), + ia->text_file->total_lines); + ds_put_format ( + &s, ngettext ( + "Only the first %zu line of the file will be shown for " + "preview purposes in the following screens. ", + "Only the first %zu lines of the file will be shown for " + "preview purposes in the following screens. ", + ia->text_file->line_cnt), + ia->text_file->line_cnt); + } } ds_put_cstr (&s, _("You may choose below how much of the file should " @@ -1185,38 +947,26 @@ intro_on_enter (PsppireImportAssistant *ia) ds_cstr (&s)); ds_destroy (&s); - GtkWidget *w = gtk_grid_get_child_at (GTK_GRID (table), 1, 1); - int old_value = w ? gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) : 1; - if (w) - gtk_container_remove (GTK_CONTAINER (table), w); - - w = gtk_grid_get_child_at (GTK_GRID (table), 1, 2); - if (w) - gtk_container_remove (GTK_CONTAINER (table), w); - - - GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin); + if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL) + { + GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin); + gtk_grid_attach (GTK_GRID (table), hbox_n_cases, + 1, 1, + 1, 1); + } GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin)); gtk_adjustment_set_lower (adj, 1.0); - if (ia->total_is_exact) - gtk_adjustment_set_value (adj, old_value); - if (ia->total_is_exact) - gtk_adjustment_set_upper (adj, ia->total_lines); - else - gtk_adjustment_set_upper (adj, DBL_MAX); - - gtk_grid_attach (GTK_GRID (table), hbox_n_cases, - 1, 1, - 1, 1); + if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL) + { + GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), + &ia->percent_spin); - GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), - &ia->percent_spin); - - gtk_grid_attach (GTK_GRID (table), hbox_percent, - 1, 2, - 1, 1); + gtk_grid_attach (GTK_GRID (table), hbox_percent, + 1, 2, + 1, 1); + } gtk_widget_show_all (table); @@ -1250,6 +1000,7 @@ intro_page_create (PsppireImportAssistant *ia) G_CALLBACK (on_intro_amount_changed), ia); + g_object_set_data (G_OBJECT (w), "on-forward", intro_on_leave); g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter); g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page); } @@ -1259,264 +1010,18 @@ GtkWidget * psppire_import_assistant_new (GtkWindow *toplevel) { return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_IMPORT_ASSISTANT, - "transient-for", toplevel, + /* Some window managers (notably ratpoison) + ignore the maximise command when a window is + transient. This causes problems for this + window. */ + /* "transient-for", toplevel, */ NULL)); } - - -struct column -{ - /* Variable name for this column. This is the variable name - used on the separators page; it can be overridden by the - user on the formats page. */ - char *name; - - /* Maximum length of any row in this column. */ - size_t width; - - /* Contents of this column: contents[row] is the contents for - the given row. - - A null substring indicates a missing column for that row - (because the line contains an insufficient number of - separators). - - contents[] elements may be substrings of the lines[] - strings that represent the whole lines of the file, to - save memory. Other elements are dynamically allocated - with ss_alloc_substring. */ - struct substring *contents; -}; - - -static void -destroy_columns (PsppireImportAssistant *ia) -{ - struct column *col; - for (col = ia->columns; col < &ia->columns[ia->column_cnt]; col++) - { - free (col->name); - free (col->contents); - } - - free (ia->columns); -} - -/* Called to render one of the cells in the fields preview tree - view. */ -static void -render_input_cell (PsppSheetViewColumn *tree_column, GtkCellRenderer *cell, - GtkTreeModel *model, GtkTreeIter *iter, - gpointer ia_) -{ - PsppireImportAssistant *ia = ia_; - struct substring field; - size_t row; - gint column; - - column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), - "column-number")); - row = empty_list_store_iter_to_row (iter) + ia->skip_lines; - field = ia->columns[column].contents[row]; - if (field.string != NULL) - { - GValue text = {0, }; - g_value_init (&text, G_TYPE_STRING); - g_value_take_string (&text, ss_xstrdup (field)); - g_object_set_property (G_OBJECT (cell), "text", &text); - g_value_unset (&text); - g_object_set (cell, "background-set", FALSE, (void *) NULL); - } - else - g_object_set (cell, - "text", "", - "background", "red", - "background-set", TRUE, - (void *) NULL); -} - - -/* Parses the contents of the field at (ROW,COLUMN) according to - its variable format. If OUTPUTP is non-null, then *OUTPUTP - receives the formatted output for that field (which must be - freed with free). If TOOLTIPP is non-null, then *TOOLTIPP - receives a message suitable for use in a tooltip, if one is - needed, or a null pointer otherwise. Returns TRUE if a - tooltip message is needed, otherwise FALSE. */ -static bool -parse_field (PsppireImportAssistant *ia, - size_t row, size_t column, - char **outputp, char **tooltipp) -{ - const struct fmt_spec *in; - struct fmt_spec out; - char *tooltip; - bool ok; - - struct substring field = ia->columns[column].contents[row]; - struct variable *var = dict_get_var (ia->dict, column); - union value val; - - value_init (&val, var_get_width (var)); - in = var_get_print_format (var); - out = fmt_for_output_from_input (in); - tooltip = NULL; - if (field.string != NULL) - { - char *error = data_in (field, "UTF-8", in->type, &val, var_get_width (var), - dict_get_encoding (ia->dict)); - if (error != NULL) - { - tooltip = xasprintf (_("Cannot parse field content `%.*s' as " - "format %s: %s"), - (int) field.length, field.string, - fmt_name (in->type), error); - free (error); - } - } - else - { - tooltip = xstrdup (_("This input line has too few separators " - "to fill in this field.")); - value_set_missing (&val, var_get_width (var)); - } - if (outputp != NULL) - { - *outputp = data_out (&val, dict_get_encoding (ia->dict), &out); - } - value_destroy (&val, var_get_width (var)); - - ok = tooltip == NULL; - if (tooltipp != NULL) - *tooltipp = tooltip; - else - free (tooltip); - return ok; -} - - -/* Called to render one of the cells in the data preview tree - view. */ -static void -render_output_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer ia_) -{ - PsppireImportAssistant *ia = ia_; - char *output; - GValue gvalue = { 0, }; - bool ok = parse_field (ia, - (empty_list_store_iter_to_row (iter) - + ia->skip_lines), - GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), - "column-number")), - &output, NULL); - - g_value_init (&gvalue, G_TYPE_STRING); - g_value_take_string (&gvalue, output); - g_object_set_property (G_OBJECT (cell), "text", &gvalue); - g_value_unset (&gvalue); - - if (ok) - g_object_set (cell, "background-set", FALSE, (void *) NULL); - else - g_object_set (cell, - "background", "red", - "background-set", TRUE, - (void *) NULL); -} - - -/* Utility functions used by multiple pages of the assistant. */ - -static gboolean -get_tooltip_location (GtkWidget *widget, gint wx, gint wy, - const PsppireImportAssistant *ia, - size_t *row, size_t *column) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - gint bx, by; - GtkTreePath *path; - GtkTreeIter iter; - PsppSheetViewColumn *tree_column; - GtkTreeModel *tree_model; - bool ok; - - pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, - wx, wy, &bx, &by); - if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by, - &path, &tree_column, NULL, NULL)) - return FALSE; - - *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), - "column-number")); - - tree_model = pspp_sheet_view_get_model (tree_view); - ok = gtk_tree_model_get_iter (tree_model, &iter, path); - gtk_tree_path_free (path); - if (!ok) - return FALSE; - - *row = empty_list_store_iter_to_row (&iter) + ia->skip_lines; - return TRUE; -} - -/* Called to render a tooltip on one of the cells in the fields - preview tree view. */ -static gboolean -on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy, - gboolean keyboard_mode UNUSED, - GtkTooltip *tooltip, PsppireImportAssistant *ia) -{ - size_t row, column; - - if (!get_tooltip_location (widget, wx, wy, ia, &row, &column)) - return FALSE; - - if (ia->columns[column].contents[row].string != NULL) - return FALSE; - - gtk_tooltip_set_text (tooltip, - _("This input line has too few separators " - "to fill in this field.")); - return TRUE; -} - - -/* Called to render a tooltip for one of the cells in the data - preview tree view. */ -static gboolean -on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy, - gboolean keyboard_mode UNUSED, - GtkTooltip *tooltip, PsppireImportAssistant *ia) -{ - size_t row, column; - char *text; - - if (!gtk_widget_get_mapped (widget)) - return FALSE; - - if (!get_tooltip_location (widget, wx, wy, ia, &row, &column)) - return FALSE; - - if (parse_field (ia, row, column, NULL, &text)) - return FALSE; - - gtk_tooltip_set_text (tooltip, text); - free (text); - return TRUE; -} - - - - static void set_quote_list (GtkComboBox *cb) { @@ -1543,294 +1048,58 @@ set_quote_list (GtkComboBox *cb) gtk_combo_box_set_entry_text_column (cb, 0); } - - - -/* Sets IA's separators substructure to match the widgets. */ -static void -get_separators (PsppireImportAssistant *ia) -{ - int i; - - ds_clear (&ia->separators); - for (i = 0; i < SEPARATOR_CNT; i++) - { - const struct separator *sep = &separators[i]; - GtkWidget *button = get_widget_assert (ia->builder, sep->name); - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) - ds_put_byte (&ia->separators, sep->c); - } - - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->custom_cb))) - ds_put_cstr (&ia->separators, - gtk_entry_get_text (GTK_ENTRY (ia->custom_entry))); - - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb))) - { - const gchar *text = gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (ia->quote_combo)))); - ds_assign_cstr (&ia->quotes, text); - } - else - ds_clear (&ia->quotes); -} - - - - -/* Frees and clears the column data in IA's separators - substructure. */ -static void -clear_fields (PsppireImportAssistant *ia) -{ - if (ia->column_cnt > 0) - { - struct column *col; - size_t row; - - for (row = 0; row < ia->line_cnt; row++) - { - const struct string *line = &ia->lines[row]; - const char *line_start = ds_data (line); - const char *line_end = ds_end (line); - - for (col = ia->columns; col < &ia->columns[ia->column_cnt]; col++) - { - char *s = ss_data (col->contents[row]); - if (!(s >= line_start && s <= line_end)) - ss_dealloc (&col->contents[row]); - } - } - - for (col = ia->columns; col < &ia->columns[ia->column_cnt]; col++) - { - free (col->name); - free (col->contents); - } - - free (ia->columns); - ia->columns = NULL; - ia->column_cnt = 0; - } -} - - -/* Breaks the file data in IA into columns based on the - separators set in IA's separators substructure. */ -static void -split_fields (PsppireImportAssistant *ia) -{ - size_t columns_allocated; - bool space_sep; - size_t row; - - clear_fields (ia); - - /* Is space in the set of separators? */ - space_sep = ss_find_byte (ds_ss (&ia->separators), ' ') != SIZE_MAX; - - /* Split all the lines, not just those from - ia->first_line.skip_lines on, so that we split the line that - contains variables names if ia->first_line.variable_names is - TRUE. */ - columns_allocated = 0; - for (row = 0; row < ia->line_cnt; row++) - { - struct string *line = &ia->lines[row]; - struct substring text = ds_ss (line); - size_t column_idx; - - for (column_idx = 0; ; column_idx++) - { - struct substring field = SS_EMPTY_INITIALIZER; - struct column *column; - - if (space_sep) - { - ss_ltrim (&text, ss_cstr (" ")); - } - if (ss_is_empty (text)) - { - if (column_idx != 0) - break; - field = text; - } - else if (!ds_is_empty (&ia->quotes) - && ds_find_byte (&ia->quotes, text.string[0]) != SIZE_MAX) - { - int quote = ss_get_byte (&text); - struct string s; - int c; - - ds_init_empty (&s); - while ((c = ss_get_byte (&text)) != EOF) - if (c != quote) - ds_put_byte (&s, c); - else if (ss_match_byte (&text, quote)) - ds_put_byte (&s, quote); - else - break; - field = ds_ss (&s); - } - else - { - ss_get_bytes (&text, ss_cspan (text, ds_ss (&ia->separators)), - &field); - } - - if (column_idx >= ia->column_cnt) - { - struct column *column; - - if (ia->column_cnt >= columns_allocated) - { - ia->columns = x2nrealloc (ia->columns, &columns_allocated, - sizeof *ia->columns); - } - column = &ia->columns[ia->column_cnt++]; - column->name = NULL; - column->width = 0; - column->contents = xcalloc (ia->line_cnt, - sizeof *column->contents); - } - column = &ia->columns[column_idx]; - column->contents[row] = field; - if (ss_length (field) > column->width) - column->width = ss_length (field); - - if (space_sep) - ss_ltrim (&text, ss_cstr (" ")); - if (ss_is_empty (text)) - break; - if (ss_find_byte (ds_ss (&ia->separators), ss_first (text)) - != SIZE_MAX) - ss_advance (&text, 1); - } - } -} - -static PsppSheetViewColumn * -make_data_column (PsppireImportAssistant *ia, GtkWidget *tree_view, - bool input, gint dict_idx) -{ - struct variable *var = NULL; - struct column *column = NULL; - size_t char_cnt = 0; - gint content_width, header_width; - PsppSheetViewColumn *tree_column; - char *name = NULL; - - if (input) - { - column = &ia->columns[dict_idx]; - name = escape_underscores (column->name); - char_cnt = column->width; - } - else - { - var = dict_get_var (ia->dict, dict_idx); - name = escape_underscores (var_get_name (var)); - char_cnt = var_get_print_format (var)->w; - } - - content_width = get_monospace_width (tree_view, ia->fixed_renderer, - char_cnt); - header_width = get_string_width (tree_view, ia->prop_renderer, - name); - - tree_column = pspp_sheet_view_column_new (); - g_object_set_data (G_OBJECT (tree_column), "column-number", - GINT_TO_POINTER (dict_idx)); - pspp_sheet_view_column_set_title (tree_column, name); - pspp_sheet_view_column_pack_start (tree_column, ia->fixed_renderer, - FALSE); - pspp_sheet_view_column_set_cell_data_func ( - tree_column, ia->fixed_renderer, - input ? render_input_cell : render_output_cell, ia, NULL); - pspp_sheet_view_column_set_fixed_width (tree_column, MAX (content_width, - header_width)); - - free (name); - - return tree_column; -} - - -static GtkWidget * -create_data_tree_view (gboolean input, GtkContainer *parent, - PsppireImportAssistant *ia) -{ - gint i; - GtkWidget *tree_view = make_tree_view (ia); - - set_model_on_treeview (ia, tree_view, ia->skip_lines); - - pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (tree_view)), - PSPP_SHEET_SELECTION_NONE); - - for (i = 0; i < ia->column_cnt; i++) - { - PsppSheetViewColumn *w = make_data_column (ia, tree_view, input, i); - - pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), w); - } - - g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL); - g_signal_connect (tree_view, "query-tooltip", - G_CALLBACK (input ? on_query_input_tooltip - : on_query_output_tooltip), ia); - - GtkWidget *child = gtk_bin_get_child (GTK_BIN (parent)); - if (child) - { - g_object_ref (child); - gtk_container_remove (parent, child); - } - gtk_container_add (parent, tree_view); - if (child) - g_object_unref (child); - - gtk_widget_show (tree_view); - - return tree_view; -} - - /* Chooses a name for each column on the separators page */ static void choose_column_names (PsppireImportAssistant *ia) { - struct dictionary *dict; + int i; unsigned long int generated_name_count = 0; - struct column *col; - size_t name_row; + dict_clear (ia->dict); - dict = dict_create (get_default_encoding ()); - name_row = ia->variable_names && ia->skip_lines ? ia->skip_lines : 0; - for (col = ia->columns; col < &ia->columns[ia->column_cnt]; col++) + for (i = 0; + i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1; + ++i) { - char *hint, *name; + const gchar *candidate_name = NULL; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb))) + { + candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i); + } - hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL; - name = dict_make_unique_var_name (dict, hint, &generated_name_count); - free (hint); + char *name = dict_make_unique_var_name (ia->dict, + candidate_name, + &generated_name_count); - col->name = name; - dict_create_var_assert (dict, name, 0); + dict_create_var_assert (ia->dict, name, 0); + free (name); } - dict_destroy (dict); } - - /* Called when the user toggles one of the separators checkboxes. */ static void on_separator_toggle (GtkToggleButton *toggle UNUSED, PsppireImportAssistant *ia) { + int i; + GSList *delimiters = NULL; + for (i = 0; i < SEPARATOR_CNT; i++) + { + const struct separator *s = &separators[i]; + GtkWidget *button = get_widget_assert (ia->builder, s->name); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) + { + delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c)); + } + } + + g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL); + revise_fields_preview (ia); } + /* Called when the user changes the entry field for custom separators. */ static void @@ -1857,7 +1126,7 @@ on_separators_custom_cb_toggle (GtkToggleButton *custom_cb, static void on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia) { - revise_fields_preview (ia); + // revise_fields_preview (ia); } /* Called when the user toggles the checkbox that enables @@ -1883,7 +1152,6 @@ separators_page_create (PsppireImportAssistant *ia) g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page); g_object_set_data (G_OBJECT (w), "on-reset", prepare_separators_page); - add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators")); ia->custom_cb = get_widget_assert (builder, "custom-cb"); @@ -1893,7 +1161,15 @@ separators_page_create (PsppireImportAssistant *ia) ia->quote_cb = get_widget_assert (builder, "quote-cb"); set_quote_list (GTK_COMBO_BOX (ia->quote_combo)); - ia->fields_tree_view = NULL; + + if (ia->fields_tree_view == NULL) + { + GtkWidget *scroller = get_widget_assert (ia->builder, "fields-scroller"); + ia->fields_tree_view = gtk_tree_view_new (); + g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL); + gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view)); + gtk_widget_show_all (scroller); + } g_signal_connect (ia->quote_combo, "changed", G_CALLBACK (on_quote_combo_change), ia); @@ -1906,251 +1182,181 @@ separators_page_create (PsppireImportAssistant *ia) for (i = 0; i < SEPARATOR_CNT; i++) g_signal_connect (get_widget_assert (builder, separators[i].name), "toggled", G_CALLBACK (on_separator_toggle), ia); + } -/* Called when the user changes one of the variables in the - dictionary. */ -static void -on_variable_change (PsppireDict *dict, int dict_idx, - unsigned int what, const struct variable *oldvar, - PsppireImportAssistant *ia) + + +static struct casereader_random_class my_casereader_class; + +static struct ccase * +my_read (struct casereader *reader, void *aux, casenumber idx) { - PsppSheetView *tv = PSPP_SHEET_VIEW (ia->data_tree_view); - gint column_idx = dict_idx + 1; + PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux); + GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model); - push_watch_cursor (ia); + GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1); - /* Remove previous column and replace with new column. */ - pspp_sheet_view_remove_column (tv, pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ia->data_tree_view), column_idx)); - pspp_sheet_view_insert_column (tv, PSPP_SHEET_VIEW_COLUMN (make_data_column (ia, ia->data_tree_view, FALSE, dict_idx)), - column_idx); + const struct caseproto *proto = casereader_get_proto (reader); - /* Save a copy of the modified variable in modified_vars, so - that its attributes will be preserved if we back up to the - previous page with the Prev button and then come back - here. */ - if (dict_idx >= ia->modified_var_cnt) + GtkTreeIter iter; + struct ccase *c = NULL; + if (gtk_tree_model_get_iter (tm, &iter, tp)) { - size_t i; - ia->modified_vars = xnrealloc (ia->modified_vars, dict_idx + 1, - sizeof *ia->modified_vars); - for (i = 0; i <= dict_idx; i++) - ia->modified_vars[i] = NULL; - ia->modified_var_cnt = dict_idx + 1; + c = case_create (proto); + int i; + for (i = 0 ; i < caseproto_get_n_widths (proto); ++i) + { + GValue value = {0}; + gtk_tree_model_get_value (tm, &iter, i + 1, &value); + + const struct variable *var = dict_get_var (ia->dict, i); + + const gchar *ss = g_value_get_string (&value); + if (ss) + { + union value *v = case_data_rw (c, var); + char *xx = data_in (ss_cstr (ss), + "UTF-8", + var_get_write_format (var)->type, + v, var_get_width (var), "UTF-8"); + + /* if (xx) */ + /* g_print ("%s:%d Err %s\n", __FILE__, __LINE__, xx); */ + free (xx); + } + g_value_unset (&value); + } } - if (ia->modified_vars[dict_idx]) - var_destroy (ia->modified_vars[dict_idx]); - ia->modified_vars[dict_idx] - = var_clone (psppire_dict_get_variable (dict, dict_idx)); - pop_watch_cursor (ia); + gtk_tree_path_free (tp); + + return c; +} + +static void +my_destroy (struct casereader *reader, void *aux) +{ + g_print ("%s:%d %p\n", __FILE__, __LINE__, reader); +} + +static void +my_advance (struct casereader *reader, void *aux, casenumber cnt) +{ + g_print ("%s:%d\n", __FILE__, __LINE__); } +static void +foo (struct dictionary *dict, void *aux) +{ + PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux); + g_print ("%s:%d\n", __FILE__, __LINE__); + struct caseproto *proto = caseproto_create (); + int i; + for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i) + { + const struct variable *var = dict_get_var (ia->dict, i); + proto = caseproto_add_width (proto, var_get_width (var)); + } + + + gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL); + + struct casereader *reader = + casereader_create_random (proto, n_rows, &my_casereader_class, ia); + + + PsppireDataStore *store = NULL; + + g_object_get (ia->data_sheet, "data-model", &store, NULL); + + psppire_data_store_set_reader (store, reader); +} /* Called just before the formats page of the assistant is displayed. */ static void prepare_formats_page (PsppireImportAssistant *ia) { - PsppireDict *psppire_dict = NULL; - PsppireVarSheet *var_sheet; - GtkBin *vars_scroller; - GtkWidget *old_var_sheet; + PsppireDict *dict = psppire_dict_new_from_dict (ia->dict); + g_object_set (ia->var_sheet, "data-model", dict, NULL); + my_casereader_class.read = my_read; + my_casereader_class.destroy = my_destroy; + my_casereader_class.advance = my_advance; - push_watch_cursor (ia); + struct caseproto *proto = caseproto_create (); + int i; - if (ia->spreadsheet == NULL) + struct fmt_guesser **fg = xcalloc (sizeof *fg, dict_get_var_cnt (ia->dict)); + for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i) { - struct fmt_guesser *fg; - unsigned long int number = 0; - size_t column_idx; + fg[i] = fmt_guesser_create (); + } + gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL); - ia->dict = dict_create (get_default_encoding ()); - fg = fmt_guesser_create (); - for (column_idx = 0; column_idx < ia->column_cnt; column_idx++) + GtkTreeIter iter; + gboolean ok; + for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter); + ok; + ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter)) + { + for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i) { - struct variable *modified_var = - (column_idx < ia->modified_var_cnt ? ia->modified_vars[column_idx] : NULL); - - if (modified_var == NULL) - { - struct column *column = &ia->columns[column_idx]; - struct variable *var; - struct fmt_spec format; - char *name; - size_t row; - - /* Choose variable name. */ - name = dict_make_unique_var_name (ia->dict, column->name, &number); - - /* Choose variable format. */ - fmt_guesser_clear (fg); - for (row = ia->skip_lines; row < ia->line_cnt; row++) - fmt_guesser_add (fg, column->contents[row]); - fmt_guesser_guess (fg, &format); - fmt_fix_input (&format); - - /* Create variable. */ - var = dict_create_var_assert (ia->dict, name, fmt_var_width (&format)); - var_set_both_formats (var, &format); - - free (name); - } - else - { - char *name; - - name = dict_make_unique_var_name (ia->dict, var_get_name (modified_var), - &number); - dict_clone_var_as_assert (ia->dict, modified_var, name); - free (name); - } + gchar *s = NULL; + gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1); + if (s) + fmt_guesser_add (fg[i], ss_cstr (s)); + free (s); } - fmt_guesser_destroy (fg); } - else + + for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i) { - int row_start = -1; - int row_stop = -1; - int col_start = -1; - int col_stop = -1; + struct fmt_spec fs; + fmt_guesser_guess (fg[i], &fs); - GtkBuilder *builder = ia->builder; + fmt_fix (&fs, FMT_FOR_INPUT); - struct casereader *reader = NULL; + struct variable *var = dict_get_var (ia->dict, i); - GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox"); - GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry"); - const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry)); - GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry"); + int width = fmt_var_width (&fs); - gint num = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)); + var_set_width_and_formats (var, width, + &fs, &fs); - struct spreadsheet_read_options sro; + proto = caseproto_add_width (proto, width); + fmt_guesser_destroy (fg[i]); + } - sro.sheet_name = NULL; - sro.cell_range = NULL; - sro.sheet_index = num + 1; + free (fg); - if ( convert_cell_ref (range, &col_start, &row_start, &col_stop, &row_stop)) - { - sro.cell_range = g_strdup (range); - } + // dict_set_change_callback (ia->dict, foo, ia); - sro.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (readnames_checkbox)); - sro.asw = -1; + struct casereader *reader = + casereader_create_random (proto, n_rows, &my_casereader_class, ia); - switch (ia->spreadsheet->type) - { - case SPREADSHEET_ODS: - case SPREADSHEET_GNUMERIC: - { - reader = spreadsheet_make_reader (ia->spreadsheet, &sro); - ia->dict = dict_clone (ia->spreadsheet->dict); - } - break; - default: - g_assert_not_reached (); - break; - } - g_free (sro.cell_range); + PsppireDataStore *store = psppire_data_store_new (dict); + psppire_data_store_set_reader (store, reader); - if (reader && ia->dict) - { - struct ccase *c; - int col; + g_object_set (ia->data_sheet, "data-model", store, NULL); - ia->column_cnt = dict_get_var_cnt (ia->dict); - ia->columns = xcalloc (ia->column_cnt, sizeof (*ia->columns)); - for (col = 0; col < ia->column_cnt ; ++col) - { - const struct variable *var = dict_get_var (ia->dict, col); - ia->columns[col].name = xstrdup (var_get_name (var)); - ia->columns[col].contents = NULL; - } - casenumber rows = 0; - for (; (c = casereader_read (reader)) != NULL; case_unref (c)) - { - rows++; - for (col = 0; col < ia->column_cnt ; ++col) - { - char *ss; - const struct variable *var = dict_get_var (ia->dict, col); + gint pmax; + g_object_get (get_widget_assert (ia->builder, "vpaned1"), + "max-position", &pmax, NULL); - ia->columns[col].contents = xrealloc (ia->columns[col].contents, - sizeof (struct substring) * rows); - ss = data_out (case_data (c, var), dict_get_encoding (ia->dict), - var_get_print_format (var)); - - ia->columns[col].contents[rows - 1] = ss_cstr (ss); - } - - if (rows > MAX_PREVIEW_LINES) - { - case_unref (c); - break; - } - } - casereader_destroy (reader); - ia->line_cnt = rows; - } - else - { - GtkWidget * dialog = gtk_message_dialog_new (NULL, - GTK_DIALOG_MODAL, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("An error occurred reading the spreadsheet file.")); - - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - } - } - - psppire_dict = psppire_dict_new_from_dict (ia->dict); - g_signal_connect (psppire_dict, "variable-changed", - G_CALLBACK (on_variable_change), ia); - ia->psppire_dict = psppire_dict; - - - /* XXX: PsppireVarStore doesn't hold a reference to - psppire_dict for now, but it should. After it does, we - should g_object_ref the psppire_dict here, since we also - hold a reference via ia->formats->dict. */ - var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ()); - g_object_set (var_sheet, - "dictionary", psppire_dict, - "may-create-vars", FALSE, - "may-delete-vars", FALSE, - "format-use", FMT_FOR_INPUT, - "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH, - (void *) NULL); - - vars_scroller = GTK_BIN (get_widget_assert (ia->builder, "vars-scroller")); - old_var_sheet = gtk_bin_get_child (GTK_BIN (vars_scroller)); - if (old_var_sheet != NULL) - gtk_container_remove (GTK_CONTAINER (vars_scroller), old_var_sheet); - gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet)); - gtk_widget_show (GTK_WIDGET (var_sheet)); - - ia->data_tree_view = - GTK_WIDGET (create_data_tree_view ( - FALSE, - GTK_CONTAINER (get_widget_assert (ia->builder, "data-scroller")), - ia)); + g_object_set (get_widget_assert (ia->builder, "vpaned1"), + "position", pmax / 2, NULL); gtk_widget_show (ia->paste_button); - - pop_watch_cursor (ia); } static void @@ -2158,17 +1364,33 @@ formats_page_create (PsppireImportAssistant *ia) { GtkBuilder *builder = ia->builder; - GtkWidget *w = get_widget_assert (builder, "Formats"); g_object_set_data (G_OBJECT (w), "on-entering", prepare_formats_page); g_object_set_data (G_OBJECT (w), "on-reset", reset_formats_page); + GtkWidget *vars_scroller = get_widget_assert (builder, "vars-scroller"); + if (ia->var_sheet == NULL) + { + ia->var_sheet = psppire_variable_sheet_new (); + + gtk_container_add (GTK_CONTAINER (vars_scroller), ia->var_sheet); + + ia->dict = dict_create (get_default_encoding ()); + + gtk_widget_show_all (vars_scroller); + } + GtkWidget *data_scroller = get_widget_assert (builder, "data-scroller"); + if (ia->data_sheet == NULL) + { + ia->data_sheet = psppire_data_sheet_new (); + + gtk_container_add (GTK_CONTAINER (data_scroller), ia->data_sheet); + + gtk_widget_show_all (data_scroller); + } + add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONFIRM, _("Adjust Variable Formats")); - - ia->data_tree_view = NULL; - ia->modified_vars = NULL; - ia->modified_var_cnt = 0; } @@ -2178,25 +1400,28 @@ static void separators_append_syntax (const PsppireImportAssistant *ia, struct string *s) { int i; + ds_put_cstr (s, " /DELIMITERS=\""); - if (ds_find_byte (&ia->separators, '\t') != SIZE_MAX) + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, "tab")))) ds_put_cstr (s, "\\t"); - if (ds_find_byte (&ia->separators, '\\') != SIZE_MAX) - ds_put_cstr (s, "\\\\"); - for (i = 0; i < ds_length (&ia->separators); i++) + for (i = 0; i < SEPARATOR_CNT; i++) { - char c = ds_at (&ia->separators, i); - if (c == '"') - ds_put_cstr (s, "\"\""); - else if (c != '\t' && c != '\\') - ds_put_byte (s, c); + const struct separator *seps = &separators[i]; + GtkWidget *button = get_widget_assert (ia->builder, seps->name); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) + { + if (seps->c == '\t') + continue; + + ds_put_byte (s, seps->c); + } } ds_put_cstr (s, "\"\n"); if (!ds_is_empty (&ia->quotes)) syntax_gen_pspp (s, " /QUALIFIER=%sq\n", ds_cstr (&ia->quotes)); } - static void formats_append_syntax (const PsppireImportAssistant *ia, struct string *s) { @@ -2219,21 +1444,25 @@ formats_append_syntax (const PsppireImportAssistant *ia, struct string *s) } } - static void first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s) { - if (ia->skip_lines > 0) - ds_put_format (s, " /FIRSTCASE=%d\n", ia->skip_lines + 1); -} + gint first_case = 0; + g_object_get (ia->delimiters_model, "first-line", &first_case, NULL); + if (first_case > 0) + ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1); +} static void intro_append_syntax (const PsppireImportAssistant *ia, struct string *s) { + gint first_line = 0; + g_object_get (ia->delimiters_model, "first-line", &first_line, NULL); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button))) - ds_put_format (s, "N OF CASES %d.\n", - gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin))); + ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n", + gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line); else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button))) ds_put_format (s, "SAMPLE %.4g.\n", gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0); @@ -2335,6 +1564,8 @@ sheet_spec_gen_syntax (PsppireImportAssistant *ia) struct string s = DS_EMPTY_INITIALIZER; + char *filename; + g_object_get (ia->text_file, "file-name", &filename, NULL); syntax_gen_pspp (&s, "GET DATA" "\n /TYPE=%ss" @@ -2342,11 +1573,10 @@ sheet_spec_gen_syntax (PsppireImportAssistant *ia) "\n /SHEET=index %d" "\n /READNAMES=%ss", (ia->spreadsheet->type == SPREADSHEET_GNUMERIC) ? "GNM" : "ODS", - ia->file_name, + filename, sheet_index, read_names ? "ON" : "OFF"); - if (range && 0 != strcmp ("", range)) { syntax_gen_pspp (&s, @@ -2365,6 +1595,7 @@ sheet_spec_gen_syntax (PsppireImportAssistant *ia) return ds_cstr (&s); } + gchar * psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia) { @@ -2372,16 +1603,23 @@ psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia) if (!ia->spreadsheet) { - if (ia->file_name == NULL) + gchar *file_name = NULL; + gchar *encoding = NULL; + g_object_get (ia->text_file, + "file-name", &file_name, + "encoding", &encoding, + NULL); + + if (file_name == NULL) return NULL; syntax_gen_pspp (&s, "GET DATA" "\n /TYPE=TXT" "\n /FILE=%sq\n", - ia->file_name); - if (ia->encoding && strcmp (ia->encoding, "Auto")) - syntax_gen_pspp (&s, " /ENCODING=%sq\n", ia->encoding); + file_name); + if (encoding && strcmp (encoding, "Auto")) + syntax_gen_pspp (&s, " /ENCODING=%sq\n", encoding); ds_put_cstr (&s, " /ARRANGEMENT=DELIMITED\n" @@ -2401,3 +1639,12 @@ psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia) return ds_cstr (&s); } + + +int +psppire_import_assistant_run (PsppireImportAssistant *asst) +{ + g_main_loop_run (asst->main_loop); + return asst->response; +} + diff --git a/src/ui/gui/psppire-import-assistant.h b/src/ui/gui/psppire-import-assistant.h index fe8040130d..cdcf8fb73e 100644 --- a/src/ui/gui/psppire-import-assistant.h +++ b/src/ui/gui/psppire-import-assistant.h @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2015 Free Software Foundation + Copyright (C) 2015, 2017 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 @@ -23,9 +23,10 @@ #include -#include "libpspp/str.h" #include "psppire-dict.h" #include "data/spreadsheet-reader.h" +#include "psppire-text-file.h" +#include "psppire-delimited-text.h" G_BEGIN_DECLS @@ -58,12 +59,8 @@ typedef struct _PsppireImportAssistant PsppireImportAssistant; typedef struct _PsppireImportAssistantClass PsppireImportAssistantClass; -struct first_line_page; - typedef void page_func (PsppireImportAssistant *, GtkWidget *page); -enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */ - struct _PsppireImportAssistant { GtkAssistant parent; @@ -72,6 +69,8 @@ struct _PsppireImportAssistant gint current_page; + gchar *file_name; + /* START The chooser page of the assistant. */ GtkWidget *encoding_selector; GtkFileFilter *default_filter; @@ -90,29 +89,21 @@ struct _PsppireImportAssistant /* START Page where the user chooses field separators. */ /* How to break lines into columns. */ - struct string separators; /* Field separators. */ struct string quotes; /* Quote characters. */ GtkWidget *custom_cb; GtkWidget *custom_entry; GtkWidget *quote_cb; GtkWidget *quote_combo; + GtkEntry *quote_entry; GtkWidget *fields_tree_view; /* END Page where the user chooses field separators. */ -/* START Page where the user verifies and adjusts input formats. */ - GtkWidget *data_tree_view; - PsppireDict *psppire_dict; - struct variable **modified_vars; - size_t modified_var_cnt; -/* END Page where the user verifies and adjusts input formats. */ - - /* START first line page */ - GtkWidget *tree_view; + GtkWidget *first_line_tree_view; GtkWidget *variable_names_cb; /* END first line page */ @@ -120,37 +111,17 @@ struct _PsppireImportAssistant GtkWidget *paste_button; GtkWidget *reset_button; int response; - int watch_cursor; - - GtkCellRenderer *prop_renderer; - GtkCellRenderer *fixed_renderer; - // START struct file file; - char *file_name; /* File name. */ - - /* Relevant only for text files */ - - gchar *encoding; /* Encoding. */ - unsigned long int total_lines; /* Number of lines in file. */ - gboolean total_is_exact; /* Is total_lines exact (or an estimate)? */ - - /* The first several lines of the file. */ - struct string lines[MAX_PREVIEW_LINES]; - size_t line_cnt; - - // END struct file file; + PsppireTextFile *text_file; + PsppireDelimitedText *delimiters_model; struct sheet_spec_page *sheet_spec; - struct first_line_page *first_line; - /* The columns produced. */ - struct column *columns; /* Information about each column. */ - size_t column_cnt; /* Number of columns. */ - - int skip_lines; /* Number of initial lines to skip? */ - gboolean variable_names; /* Variable names above first line of data? */ struct dictionary *dict; + GtkWidget *var_sheet; + GtkWidget *data_sheet; + struct spreadsheet *spreadsheet; }; @@ -166,6 +137,8 @@ GtkWidget *psppire_import_assistant_new (GtkWindow *toplevel); gchar *psppire_import_assistant_generate_syntax (PsppireImportAssistant *); +int psppire_import_assistant_run (PsppireImportAssistant *asst); + G_END_DECLS #endif /* __PSPPIRE_IMPORT_ASSISTANT_H__ */ diff --git a/src/ui/gui/psppire-text-file.c b/src/ui/gui/psppire-text-file.c new file mode 100644 index 0000000000..590455d90e --- /dev/null +++ b/src/ui/gui/psppire-text-file.c @@ -0,0 +1,549 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2017 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 . */ + +#include +#include +#define _(msgid) gettext (msgid) +#define P_(msgid) msgid + +#include "psppire-text-file.h" +#include +#include +#include +#include +#include "libpspp/line-reader.h" +#include "libpspp/message.h" +#include "libpspp/str.h" +#include "libpspp/i18n.h" + +#include + +/* Properties */ +enum + { + PROP_0, + PROP_FILE_NAME, + PROP_ENCODING, + PROP_MAXIMUM_LINES, + PROP_LINE_COUNT + }; + +enum {MAX_LINE_LEN = 16384}; /* Max length of an acceptable line. */ + + +static void +read_lines (PsppireTextFile *tf) +{ + if (tf->file_name && 0 != g_strcmp0 ("unset", tf->encoding)) + { + struct line_reader *reader = line_reader_for_file (tf->encoding, tf->file_name, O_RDONLY); + + if (reader == NULL) + { + msg_error (errno, _("Could not open `%s'"), tf->file_name); + return; + } + + struct string input; + ds_init_empty (&input); + for (tf->line_cnt = 0; tf->line_cnt < MAX_PREVIEW_LINES; tf->line_cnt++) + { + ds_clear (&input); + if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1) + || ds_length (&input) > MAX_LINE_LEN) + { + int i; + if (line_reader_eof (reader)) + break; + else if (line_reader_error (reader)) + msg (ME, _("Error reading `%s': %s"), + tf->file_name, strerror (line_reader_error (reader))); + else + msg (ME, _("Failed to read `%s', because it contains a line " + "over %d bytes long and therefore appears not to be " + "a text file."), + tf->file_name, MAX_LINE_LEN); + line_reader_close (reader); + for (i = 0; i < tf->line_cnt; i++) + g_free (&tf->lines[i]); + tf->line_cnt = 0; + ds_destroy (&input); + return; + } + + tf->lines[tf->line_cnt] + = recode_substring_pool ("UTF-8", + line_reader_get_encoding (reader), + input.ss, NULL); + } + ds_destroy (&input); + + if (tf->line_cnt == 0) + { + int i; + msg (ME, _("`%s' is empty."), tf->file_name); + line_reader_close (reader); + for (i = 0; i < tf->line_cnt; i++) + g_free (&tf->lines[i]); + tf->line_cnt = 0; + goto done; + } + + if (tf->line_cnt < MAX_PREVIEW_LINES) + { + tf->total_lines = tf->line_cnt; + tf->total_is_exact = true; + } + else + { + /* Estimate the number of lines in the file. */ + struct stat s; + off_t position = line_reader_tell (reader); + if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0) + { + tf->total_lines = (double) tf->line_cnt / position * s.st_size; + tf->total_is_exact = false; + } + else + { + tf->total_lines = 0; + tf->total_is_exact = true; + } + } + done: + line_reader_close (reader); + } +} + +static void +psppire_text_file_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PsppireTextFile *tf = PSPPIRE_TEXT_FILE (object); + + switch (prop_id) + { + case PROP_MAXIMUM_LINES: + tf->maximum_lines = g_value_get_int (value); + break; + case PROP_FILE_NAME: + tf->file_name = g_value_dup_string (value); + read_lines (tf); + break; + case PROP_ENCODING: + g_free (tf->encoding); + tf->encoding = g_value_dup_string (value); + read_lines (tf); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; + +} + +static void +psppire_text_file_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PsppireTextFile *text_file = PSPPIRE_TEXT_FILE (object); + + switch (prop_id) + { + case PROP_MAXIMUM_LINES: + g_value_set_int (value, text_file->maximum_lines); + break; + case PROP_LINE_COUNT: + g_value_set_int (value, text_file->line_cnt); + break; + case PROP_FILE_NAME: + g_value_set_string (value, text_file->file_name); + break; + case PROP_ENCODING: + g_value_set_string (value, text_file->encoding); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + + +static void psppire_text_file_init (PsppireTextFile *text_file); +static void psppire_text_file_class_init (PsppireTextFileClass *class); + +static void psppire_text_file_finalize (GObject *object); +static void psppire_text_file_dispose (GObject *object); + +static GObjectClass *parent_class = NULL; + +static gboolean +__tree_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model); + + if (path == NULL) + return FALSE; + + gint *indices = gtk_tree_path_get_indices (path); + + gint n = *indices; + + if (n >= file->line_cnt) + return FALSE; + + iter->user_data = GINT_TO_POINTER (n); + iter->stamp = file->stamp; + + return TRUE; +} + + +static gboolean +__tree_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model); + g_return_val_if_fail (file->stamp == iter->stamp, FALSE); + + gint n = GPOINTER_TO_INT (iter->user_data) + 1; + + if (n >= file->line_cnt) + return FALSE; + + iter->user_data = GINT_TO_POINTER (n); + + return TRUE; +} + + +static GType +__tree_get_column_type (GtkTreeModel *tree_model, + gint index) +{ + if (index == 0) + return G_TYPE_INT; + + return G_TYPE_STRING; +} + +static gboolean +__iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return 0; +} + + +static gboolean +__iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return 0; +} + +static GtkTreePath * +__tree_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model); + g_return_val_if_fail (file->stamp == iter->stamp, FALSE); + + gint n = GPOINTER_TO_INT (iter->user_data); + + return gtk_tree_path_new_from_indices (n, -1); +} + + +static gboolean +__iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + return 0; +} + + +static gint +__tree_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model); + g_assert (iter == NULL); + return file->line_cnt; +} + +static GtkTreeModelFlags +__tree_model_get_flags (GtkTreeModel *model) +{ + g_return_val_if_fail (PSPPIRE_IS_TEXT_FILE (model), (GtkTreeModelFlags) 0); + + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint +__tree_model_get_n_columns (GtkTreeModel *tree_model) +{ + PsppireTextFile *tf = PSPPIRE_TEXT_FILE (tree_model); + return 2; +} + + +static gboolean +__iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model); + + g_assert (parent == NULL); + + g_return_val_if_fail (file, FALSE); + + if (n >= file->line_cnt) + { + iter->stamp = -1; + iter->user_data = NULL; + return FALSE; + } + + iter->user_data = GINT_TO_POINTER (n); + iter->stamp = file->stamp; + + return TRUE; +} + + +static void +__get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model); + + g_return_if_fail (iter->stamp == file->stamp); + + gint n = GPOINTER_TO_INT (iter->user_data); + + g_return_if_fail (n < file->line_cnt); + + if (column == 0) + { + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, n + 1); + return; + } + + g_value_init (value, G_TYPE_STRING); + + if (column == 1) + { + char *s = ss_xstrdup (file->lines[n]); + g_value_set_string (value, s); + free (s); + return; + } + + g_assert_not_reached (); +} + + +static void +__tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = __tree_model_get_flags; + iface->get_n_columns = __tree_model_get_n_columns ; + iface->get_column_type = __tree_get_column_type; + iface->get_iter = __tree_get_iter; + iface->iter_next = __tree_iter_next; + iface->get_path = __tree_get_path; + iface->get_value = __get_value; + + iface->iter_children = __iter_children; + iface->iter_has_child = __iter_has_child; + iface->iter_n_children = __tree_model_iter_n_children; + iface->iter_nth_child = __iter_nth_child; + iface->iter_parent = __iter_parent; +} + + +GType +psppire_text_file_get_type (void) +{ + static GType text_file_type = 0; + + if (!text_file_type) + { + static const GTypeInfo text_file_info = + { + sizeof (PsppireTextFileClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) psppire_text_file_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PsppireTextFile), + 0, + (GInstanceInitFunc) psppire_text_file_init, + }; + + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) __tree_model_init, + NULL, + NULL + }; + + text_file_type = g_type_register_static (G_TYPE_OBJECT, + "PsppireTextFile", + &text_file_info, 0); + + g_type_add_interface_static (text_file_type, GTK_TYPE_TREE_MODEL, + &tree_model_info); + } + + return text_file_type; +} + + +static void +psppire_text_file_class_init (PsppireTextFileClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + object_class = (GObjectClass*) class; + + GParamSpec *maximum_lines_spec = + g_param_spec_int ("maximum-lines", + "Maximum Lines", + P_("An upper limit on the number of lines to consider"), + 0, G_MAXINT, G_MAXINT, + G_PARAM_READWRITE); + + GParamSpec *line_count_spec = + g_param_spec_int ("line-count", + "Line Count", + P_("The number of lines in the file"), + 0, G_MAXINT, G_MAXINT, + G_PARAM_READABLE); + + GParamSpec *file_name_spec = + g_param_spec_string ("file-name", + "File Name", + P_("The name of the file from which this object was constructed"), + NULL, + G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE); + + GParamSpec *encoding_spec = + g_param_spec_string ("encoding", + "Character Encoding", + P_("The character encoding of the file from which this object was constructed"), + "unset", + G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE); + + object_class->set_property = psppire_text_file_set_property; + object_class->get_property = psppire_text_file_get_property; + + g_object_class_install_property (object_class, + PROP_MAXIMUM_LINES, + maximum_lines_spec); + + g_object_class_install_property (object_class, + PROP_LINE_COUNT, + line_count_spec); + + g_object_class_install_property (object_class, + PROP_FILE_NAME, + file_name_spec); + + g_object_class_install_property (object_class, + PROP_ENCODING, + encoding_spec); + + object_class->finalize = psppire_text_file_finalize; + object_class->dispose = psppire_text_file_dispose; +} + +static void +psppire_text_file_init (PsppireTextFile *text_file) +{ + text_file->encoding = g_strdup ("unset"); + text_file->file_name = NULL; + + text_file->dispose_has_run = FALSE; + text_file->stamp = g_random_int (); +} + + +PsppireTextFile * +psppire_text_file_new (const gchar *file_name, const gchar *encoding) +{ + PsppireTextFile *retval = + g_object_new (PSPPIRE_TYPE_TEXT_FILE, + "file-name", file_name, + "encoding", encoding, + NULL); + + return retval; +} + +static void +psppire_text_file_finalize (GObject *object) +{ + PsppireTextFile *tf = PSPPIRE_TEXT_FILE (object); + + g_free (tf->encoding); + g_free (tf->file_name); + + /* must chain up */ + (* parent_class->finalize) (object); +} + + +static void +psppire_text_file_dispose (GObject *object) +{ + PsppireTextFile *ds = PSPPIRE_TEXT_FILE (object); + + if (ds->dispose_has_run) + return; + + /* must chain up */ + (* parent_class->dispose) (object); + + ds->dispose_has_run = TRUE; +} + +gboolean +psppire_text_file_get_total_exact (PsppireTextFile *tf) +{ + return tf->total_is_exact; +} + +gulong +psppire_text_file_get_n_lines (PsppireTextFile *tf) +{ + return tf->total_lines; +} diff --git a/src/ui/gui/psppire-text-file.h b/src/ui/gui/psppire-text-file.h new file mode 100644 index 0000000000..486cefb4cc --- /dev/null +++ b/src/ui/gui/psppire-text-file.h @@ -0,0 +1,92 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2017 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 . */ + +#ifndef __PSPPIRE_TEXT_FILE_H__ +#define __PSPPIRE_TEXT_FILE_H__ + +#include "libpspp/str.h" + +#include + +G_BEGIN_DECLS + + + +#define PSPPIRE_TYPE_TEXT_FILE (psppire_text_file_get_type ()) + +#define PSPPIRE_TEXT_FILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_TEXT_FILE, PsppireTextFile)) + +#define PSPPIRE_TEXT_FILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_TEXT_FILE, \ + PsppireTextFileClass)) + + +#define PSPPIRE_IS_TEXT_FILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_TEXT_FILE)) + +#define PSPPIRE_IS_TEXT_FILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_TEXT_FILE)) + +#define PSPPIRE_TEXT_FILE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_TEXT_FILE, \ + PsppireTextFileClass)) + +enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */ + +struct _PsppireTextFile +{ + GObject parent; + + gchar *file_name; + gchar *encoding; + + gint maximum_lines; + + /* The first several lines of the file. These copies which are UTF8 encoded, + regardless of the file encoding. */ + struct substring lines[MAX_PREVIEW_LINES]; + size_t line_cnt; + + gulong total_lines; /* Number of lines in file. */ + gboolean total_is_exact; /* Is total_lines exact (or an estimate)? */ + + /*< private >*/ + gboolean dispose_has_run ; + gint stamp; +}; + +struct _PsppireTextFileClass +{ + GObjectClass parent_class; +}; + + +typedef struct _PsppireTextFile PsppireTextFile; +typedef struct _PsppireTextFileClass PsppireTextFileClass; + +GType psppire_text_file_get_type (void) G_GNUC_CONST; +PsppireTextFile *psppire_text_file_new (const gchar *file_name, const gchar *encoding); + +gboolean psppire_text_file_get_total_exact (PsppireTextFile *tf); +gulong psppire_text_file_get_n_lines (PsppireTextFile *tf); + +G_END_DECLS + +#endif /* __PSPPIRE_TEXT_FILE_H__ */ diff --git a/src/ui/gui/psppire-value-entry.c b/src/ui/gui/psppire-value-entry.c index a21f10b84b..30b18219d7 100644 --- a/src/ui/gui/psppire-value-entry.c +++ b/src/ui/gui/psppire-value-entry.c @@ -48,6 +48,12 @@ enum PROP_WIDTH }; +enum {EDIT_DONE, /* Emitted when the entry has changed and is ready to be fetched */ + n_SIGNALS}; + +static guint signals [n_SIGNALS]; + + static void psppire_value_entry_set_property (GObject *object, guint prop_id, @@ -137,6 +143,12 @@ psppire_value_entry_text_changed (GtkEntryBuffer *buffer, obj->cur_value = NULL; } +static void +on_entry_activate (GtkWidget *w) +{ + PsppireValueEntry *ve = PSPPIRE_VALUE_ENTRY (w); + g_signal_emit (w, signals [EDIT_DONE], 0); +} static void on_realize (GtkWidget *w) @@ -149,6 +161,9 @@ on_realize (GtkWidget *w) g_signal_connect (buffer, "notify::text", G_CALLBACK (psppire_value_entry_text_changed), w); + g_signal_connect_swapped (entry, "activate", + G_CALLBACK (on_entry_activate), w); + gtk_widget_set_can_focus (GTK_WIDGET (entry), TRUE); GTK_WIDGET_CLASS (psppire_value_entry_parent_class)->realize (w); @@ -243,6 +258,16 @@ psppire_value_entry_class_init (PsppireValueEntryClass *class) 0, MAX_STRING, 0, G_PARAM_READABLE | G_PARAM_WRITABLE)); + + signals [EDIT_DONE] = + g_signal_new ("edit-done", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); } static void diff --git a/src/ui/gui/psppire-var-sheet-header.c b/src/ui/gui/psppire-var-sheet-header.c new file mode 100644 index 0000000000..ad0401e6c8 --- /dev/null +++ b/src/ui/gui/psppire-var-sheet-header.c @@ -0,0 +1,135 @@ +/* + A candidate replacement for Pspp's sheet + Copyright (C) 2016 John Darrington + + 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 . +*/ + +#include +#include + +#include "psppire-var-sheet-header.h" + +#include + +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +enum {CHANGED, + n_SIGNALS}; + +static guint signals [n_SIGNALS]; + +static guint +gni (GListModel *list) +{ + return 11; +} + +static GType +git (GListModel *list) +{ + return GTK_TYPE_BUTTON; +} + + +static gpointer +gi (GListModel *list, guint position) +{ + GtkWidget *button = gtk_button_new (); + gchar *text = NULL; + + switch (position) + { + case 0: + text = N_("Name"); + break; + case 1: + text = N_("Type"); + break; + case 2: + text = N_("Width"); + break; + case 3: + text = N_("Decimal"); + break; + case 4: + text = N_("Label"); + break; + case 5: + text = N_("Value Labels"); + break; + case 6: + text = N_("Missing Values"); + break; + case 7: + text = N_("Columns"); + break; + case 8: + text = N_("Align"); + break; + case 9: + text = N_("Measure"); + break; + case 10: + text = N_("Role"); + break; + default: + break; + } + + if (text) + gtk_button_set_label (GTK_BUTTON (button), gettext (text)); + + return button; +} + + +static void +psppire_init_iface (GListModelInterface *iface) +{ + iface->get_n_items = gni; + iface->get_item = gi; + iface->get_item_type = git; +} + + +G_DEFINE_TYPE_WITH_CODE (PsppireVarSheetHeader, psppire_var_sheet_header, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, psppire_init_iface)); + +static void +psppire_var_sheet_header_init (PsppireVarSheetHeader *d) +{ +} + + + +static void +psppire_var_sheet_header_class_init (PsppireVarSheetHeaderClass *dc) +{ + GObjectClass *object_class = G_OBJECT_CLASS (dc); + + /* This signal is never emitted. It is just to satisfy the interface. */ + signals [CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + diff --git a/src/ui/gui/psppire-var-sheet-header.h b/src/ui/gui/psppire-var-sheet-header.h new file mode 100644 index 0000000000..476faaf0c8 --- /dev/null +++ b/src/ui/gui/psppire-var-sheet-header.h @@ -0,0 +1,40 @@ +/* + A candidate replacement for Pspp's sheet + Copyright (C) 2016 John Darrington + + 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 . +*/ + +#ifndef _PSPPIRE_VAR_SHEET_HEADER_H +#define _PSPPIRE_VAR_SHEET_HEADER_H + + +G_DECLARE_FINAL_TYPE (PsppireVarSheetHeader, psppire_var_sheet_header, PSPPIRE, VAR_SHEET_HEADER, GObject) + + +struct _PsppireVarSheetHeader +{ + GObject parent_instance; +}; + +struct _PsppireVarSheetHeaderClass +{ + GObjectClass parent_instance; +}; + + + +#define PSPPIRE_TYPE_VAR_SHEET_HEADER psppire_var_sheet_header_get_type () + +#endif diff --git a/src/ui/gui/psppire-var-sheet.c b/src/ui/gui/psppire-var-sheet.c deleted file mode 100644 index 7686b40af4..0000000000 --- a/src/ui/gui/psppire-var-sheet.c +++ /dev/null @@ -1,1622 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2008, 2009, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#include - -#include "ui/gui/psppire-var-sheet.h" - -#include "data/format.h" -#include "data/value-labels.h" -#include "libpspp/range-set.h" -#include "ui/gui/builder-wrapper.h" -#include "ui/gui/helper.h" -#include "ui/gui/missing-val-dialog.h" -#include "ui/gui/pspp-sheet-selection.h" -#include "ui/gui/psppire-cell-renderer-button.h" -#include "ui/gui/psppire-data-editor.h" -#include "ui/gui/psppire-data-window.h" -#include "ui/gui/psppire-dialog-action-var-info.h" -#include "ui/gui/psppire-dictview.h" -#include "ui/gui/psppire-empty-list-store.h" -#include "ui/gui/psppire-marshal.h" -#include "ui/gui/val-labs-dialog.h" -#include "ui/gui/var-type-dialog.h" -#include "ui/gui/var-display.h" -#include "ui/gui/var-type-dialog.h" - -#include "gl/intprops.h" - -#include -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid - -enum vs_column - { - VS_NAME, - VS_TYPE, - VS_WIDTH, - VS_DECIMALS, - VS_LABEL, - VS_VALUES, - VS_MISSING, - VS_COLUMNS, - VS_ALIGN, - VS_MEASURE, - VS_ROLE - }; - -G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW); - -static void -set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step) -{ - char text[INT_BUFSIZE_BOUND (int)]; - GtkAdjustment *adjust; - - if (max > min) - adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max, - step, step, 0.0)); - else - adjust = NULL; - - sprintf (text, "%d", value); - g_object_set (cell, - "text", text, - "adjustment", adjust, - "editable", TRUE, - NULL); -} - -static void -error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text) -{ - GtkWidget *dialog = - gtk_message_dialog_new (w, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, "%s", primary_text); - - g_object_set (dialog, "icon-name", "psppicon", NULL); - - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), - "%s", secondary_text); - - gtk_dialog_run (GTK_DIALOG (dialog)); - - gtk_widget_destroy (dialog); -} - -static void -on_name_column_editing_started (GtkCellRenderer *cell, - GtkCellEditable *editable, - const gchar *path, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet"); - PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet); - - g_return_if_fail (var_sheet); - g_return_if_fail (dict); - - if (GTK_IS_ENTRY (editable)) - { - GtkEntry *entry = GTK_ENTRY (editable); - if (gtk_entry_get_text (entry)[0] == '\0') - { - gchar name[64]; - if (psppire_dict_generate_name (dict, name, sizeof name)) - gtk_entry_set_text (entry, name); - } - } -} - -static void -scroll_to_bottom (GtkWidget *widget, - GtkRequisition *requisition, - gpointer unused UNUSED) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget); - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget); - GtkAdjustment *vadjust; - - vadjust = pspp_sheet_view_get_vadjustment (sheet_view); - gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust)); - - if (var_sheet->scroll_to_bottom_signal) - { - g_signal_handler_disconnect (var_sheet, - var_sheet->scroll_to_bottom_signal); - var_sheet->scroll_to_bottom_signal = 0; - } -} - -static struct variable * -path_string_to_var (PsppireVarSheet *var_sheet, gchar *path_string) -{ - GtkTreePath *path; - gint row; - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - return psppire_dict_get_variable (var_sheet->dict, row); -} - -static void -on_var_column_edited (GtkCellRendererText *cell, - gchar *path_string, - gchar *new_text, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = user_data; - GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet))); - struct dictionary *dict = var_sheet->dict->dict; - enum vs_column column_id; - struct variable *var; - int width, decimals; - - column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), - "column-id")); - - var = path_string_to_var (var_sheet, path_string); - if (var == NULL) - { - g_return_if_fail (column_id == VS_NAME); - - if (!dict_id_is_valid (dict, new_text, false)) - error_dialog (window, - g_strdup (_("Cannot create variable.")), - g_strdup_printf (_("\"%s\" is not a valid variable " - "name."), new_text)); - else if (dict_lookup_var (dict, new_text) != NULL) - error_dialog (window, - g_strdup (_("Cannot create variable.")), - g_strdup_printf (_("This dictionary already contains " - "a variable named \"%s\"."), - new_text)); - else - { - dict_create_var (var_sheet->dict->dict, new_text, 0); - if (!var_sheet->scroll_to_bottom_signal) - { - gtk_widget_queue_resize (GTK_WIDGET (var_sheet)); - var_sheet->scroll_to_bottom_signal = - g_signal_connect (var_sheet, "size-allocate", - G_CALLBACK (scroll_to_bottom), NULL); - } - } - - return; - } - - switch (column_id) - { - case VS_NAME: - if (!dict_id_is_valid (dict, new_text, false)) - error_dialog (window, - g_strdup (_("Cannot rename variable.")), - g_strdup_printf (_("\"%s\" is not a valid variable " - "name."), new_text)); - else if (dict_lookup_var (dict, new_text) != NULL - && dict_lookup_var (dict, new_text) != var) - error_dialog (window, - g_strdup (_("Cannot rename variable.")), - g_strdup_printf (_("This dictionary already contains " - "a variable named \"%s\"."), - new_text)); - else - dict_rename_var (dict, var, new_text); - break; - - case VS_TYPE: - /* Not reachable. */ - break; - - case VS_WIDTH: - width = atoi (new_text); - if (width > 0) - { - struct fmt_spec format; - - format = *var_get_print_format (var); - fmt_change_width (&format, width, var_sheet->format_use); - var_set_width (var, fmt_var_width (&format)); - var_set_both_formats (var, &format); - } - break; - - case VS_DECIMALS: - decimals = atoi (new_text); - if (decimals >= 0) - { - struct fmt_spec format; - - format = *var_get_print_format (var); - fmt_change_decimals (&format, decimals, var_sheet->format_use); - var_set_print_format (var, &format); - } - break; - - case VS_LABEL: - var_set_label (var, new_text); - break; - - case VS_VALUES: - case VS_MISSING: - break; - - case VS_COLUMNS: - width = atoi (new_text); - if (width > 0 && width < 2 * MAX_STRING) - var_set_display_width (var, width); - break; - - case VS_ALIGN: - if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT))) - var_set_alignment (var, ALIGN_LEFT); - else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE))) - var_set_alignment (var, ALIGN_CENTRE); - else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT))) - var_set_alignment (var, ALIGN_RIGHT); - break; - - case VS_MEASURE: - if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL))) - var_set_measure (var, MEASURE_NOMINAL); - else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL))) - var_set_measure (var, MEASURE_ORDINAL); - else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE))) - var_set_measure (var, MEASURE_SCALE); - break; - - case VS_ROLE: - if (!strcmp (new_text, var_role_to_string (ROLE_INPUT))) - var_set_role (var, ROLE_INPUT); - else if (!strcmp (new_text, var_role_to_string (ROLE_TARGET))) - var_set_role (var, ROLE_TARGET); - else if (!strcmp (new_text, var_role_to_string (ROLE_BOTH))) - var_set_role (var, ROLE_BOTH); - else if (!strcmp (new_text, var_role_to_string (ROLE_NONE))) - var_set_role (var, ROLE_NONE); - else if (!strcmp (new_text, var_role_to_string (ROLE_PARTITION))) - var_set_role (var, ROLE_PARTITION); - else if (!strcmp (new_text, var_role_to_string (ROLE_SPLIT))) - var_set_role (var, ROLE_SPLIT); - break; - } -} - -static void -render_popup_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - void *user_data) -{ - PsppireVarSheet *var_sheet = user_data; - gint row; - - row = GPOINTER_TO_INT (iter->user_data); - g_object_set (cell, - "editable", row < psppire_dict_get_var_cnt (var_sheet->dict), - NULL); -} - -const char * -get_var_align_stock_id (enum alignment alignment) -{ - switch (alignment) - { - case ALIGN_LEFT: return "align-left"; - case ALIGN_CENTRE: return "align-center"; - case ALIGN_RIGHT: return "align-right"; - default: - g_return_val_if_reached (""); - } -} - -const char * -get_var_role_stock_id (enum var_role role) -{ - switch (role) - { - case ROLE_INPUT: return "role-input"; - case ROLE_TARGET: return "role-target"; - case ROLE_BOTH: return "role-both"; - case ROLE_NONE: return "role-none"; - case ROLE_PARTITION: return "role-partition"; - case ROLE_SPLIT: return "role-split"; - default: - g_return_val_if_reached (""); - } -} - -static void -render_var_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - void *user_data) -{ - PsppireVarSheet *var_sheet = user_data; - const struct fmt_spec *print; - enum vs_column column_id; - struct variable *var; - gint row; - - column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), - "column-number")) - 1; - row = GPOINTER_TO_INT (iter->user_data); - - gtk_cell_renderer_set_visible (cell, true); - if (row >= psppire_dict_get_var_cnt (var_sheet->dict)) - { - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - { - g_object_set (cell, - "text", "", - "editable", column_id == VS_NAME, - NULL); - if (column_id == VS_WIDTH - || column_id == VS_DECIMALS - || column_id == VS_COLUMNS) - g_object_set (cell, "adjustment", NULL, NULL); - } - else - { - gtk_cell_renderer_set_visible (cell, false); - } - return; - } - - var = psppire_dict_get_variable (var_sheet->dict, row); - - print = var_get_print_format (var); - switch (column_id) - { - case VS_NAME: - g_object_set (cell, - "text", var_get_name (var), - "editable", TRUE, - NULL); - break; - - case VS_TYPE: - g_object_set (cell, - "text", fmt_gui_name (print->type), - "editable", FALSE, - NULL); - break; - - case VS_WIDTH: - set_spin_cell (cell, print->w, - fmt_min_width (print->type, var_sheet->format_use), - fmt_max_width (print->type, var_sheet->format_use), - fmt_step_width (print->type)); - break; - - case VS_DECIMALS: - if (fmt_takes_decimals (print->type)) - { - int max_w = fmt_max_width (print->type, var_sheet->format_use); - int max_d = fmt_max_decimals (print->type, max_w, - var_sheet->format_use); - set_spin_cell (cell, print->d, 0, max_d, 1); - } - else - g_object_set (cell, - "text", "", - "editable", FALSE, - "adjustment", NULL, - NULL); - break; - - case VS_LABEL: - g_object_set (cell, - "text", var_has_label (var) ? var_get_label (var) : "", - "editable", TRUE, - NULL); - break; - - case VS_VALUES: - g_object_set (cell, "editable", FALSE, NULL); - if ( ! var_has_value_labels (var)) - g_object_set (cell, "text", _("None"), NULL); - else - { - const struct val_labs *vls = var_get_value_labels (var); - const struct val_lab **labels = val_labs_sorted (vls); - const struct val_lab *vl = labels[0]; - gchar *vstr = value_to_text (vl->value, var); - char *text = xasprintf (_("{%s, %s}..."), vstr, - val_lab_get_escaped_label (vl)); - free (vstr); - - g_object_set (cell, "text", text, NULL); - free (text); - free (labels); - } - break; - - case VS_MISSING: - { - char *text = missing_values_to_string (var, NULL); - g_object_set (cell, - "text", text, - "editable", FALSE, - NULL); - free (text); - } - break; - - case VS_COLUMNS: - set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1); - break; - - case VS_ALIGN: - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - g_object_set (cell, - "text", alignment_to_string (var_get_alignment (var)), - "editable", TRUE, - NULL); - else - g_object_set (cell, "icon-name", - get_var_align_stock_id (var_get_alignment (var)), NULL); - break; - - case VS_MEASURE: - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - g_object_set (cell, - "text", measure_to_string (var_get_measure (var)), - "editable", TRUE, - NULL); - else - { - enum fmt_type type = var_get_print_format (var)->type; - enum measure measure = var_get_measure (var); - - g_object_set (cell, "icon-name", - get_var_measurement_stock_id (type, measure), - NULL); - } - break; - - case VS_ROLE: - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - g_object_set (cell, - "text", var_role_to_string (var_get_role (var)), - "editable", TRUE, - NULL); - else - g_object_set (cell, "icon-name", - get_var_role_stock_id (var_get_role (var)), NULL); - break; - } -} - -static struct variable * -path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string) -{ - PsppireDict *dict; - GtkTreePath *path; - gint row; - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - dict = psppire_var_sheet_get_dictionary (var_sheet); - g_return_val_if_fail (dict != NULL, NULL); - - return psppire_dict_get_variable (dict, row); -} - -static void -on_type_click (PsppireCellRendererButton *cell, - gchar *path, - PsppireVarSheet *var_sheet) -{ - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)); - struct fmt_spec format; - struct variable *var; - - var = path_string_to_variable (var_sheet, path); - g_return_if_fail (var != NULL); - - format = *var_get_print_format (var); - psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format); - - var_set_width_and_formats (var, fmt_var_width (&format), &format, &format); -} - -static void -on_value_labels_click (PsppireCellRendererButton *cell, - gchar *path, - PsppireVarSheet *var_sheet) -{ - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)); - struct val_labs *labels; - struct variable *var; - - var = path_string_to_variable (var_sheet, path); - g_return_if_fail (var != NULL); - - labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var); - if (labels) - { - var_set_value_labels (var, labels); - val_labs_destroy (labels); - } -} - -static void -on_missing_values_click (PsppireCellRendererButton *cell, - gchar *path, - PsppireVarSheet *var_sheet) -{ - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)); - struct missing_values mv; - struct variable *var; - - var = path_string_to_variable (var_sheet, path); - g_return_if_fail (var != NULL); - - psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv); - var_set_missing_values (var, &mv); - mv_destroy (&mv); -} - -static gint -get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer, - const char *string) -{ - gint width; - g_object_set (G_OBJECT (renderer), - PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text", - string, (void *) NULL); - - gtk_cell_renderer_get_preferred_width (renderer, GTK_WIDGET (treeview), NULL, &width); - - return width; -} - -static gint -get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer, - size_t char_cnt) -{ - struct string s; - gint width; - - ds_init_empty (&s); - ds_put_byte_multiple (&s, '0', char_cnt); - ds_put_byte (&s, ' '); - width = get_string_width (treeview, renderer, ds_cstr (&s)); - ds_destroy (&s); - - return width; -} - -static PsppSheetViewColumn * -add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer, - enum vs_column column_id, - const char *title, int width) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - int title_width, content_width; - PsppSheetViewColumn *column; - - column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL); - g_object_set_data (G_OBJECT (column), "column-number", - GINT_TO_POINTER (column_id) + 1); - - pspp_sheet_view_column_set_cell_data_func ( - column, renderer, render_var_cell, var_sheet, NULL); - - title_width = get_string_width (sheet_view, renderer, title); - content_width = get_monospace_width (sheet_view, renderer, width); - g_object_set_data (G_OBJECT (column), "content-width", - GINT_TO_POINTER (content_width)); - - pspp_sheet_view_column_set_fixed_width (column, - MAX (title_width, content_width)); - pspp_sheet_view_column_set_resizable (column, TRUE); - - pspp_sheet_view_append_column (sheet_view, column); - - g_signal_connect (renderer, "edited", - G_CALLBACK (on_var_column_edited), - var_sheet); - g_object_set_data (G_OBJECT (renderer), "column-id", - GINT_TO_POINTER (column_id)); - g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet); - - return column; -} - -static PsppSheetViewColumn * -add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id, - const char *title, int width) -{ - return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (), - column_id, title, width); -} - -static PsppSheetViewColumn * -add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id, - const char *title, int width) -{ - return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (), - column_id, title, width); -} - -static const char * -measure_to_stock_id (enum fmt_type type, int measure) -{ - return get_var_measurement_stock_id (type, measure); -} - -static const char * -alignment_to_stock_id (enum fmt_type type, int alignment) -{ - return get_var_align_stock_id (alignment); -} - -static const char * -role_to_stock_id (enum fmt_type type, int role) -{ - return get_var_role_stock_id (role); -} - -static void -render_var_pixbuf (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - const char *(*value_to_stock_id) (enum fmt_type, int value); - enum fmt_type type = GPOINTER_TO_INT (data); - gint value; - - value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id"); - - gtk_tree_model_get (tree_model, iter, 0, &value, -1); - g_object_set (cell, "icon-name", value_to_stock_id (type, value), NULL); -} - -static void -on_combo_editing_started (GtkCellRenderer *renderer, - GtkCellEditable *editable, - gchar *path_string, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = user_data; - - if (GTK_IS_COMBO_BOX (editable)) - { - struct variable *var = path_string_to_variable (var_sheet, path_string); - const struct fmt_spec *format = var_get_print_format (var); - const char *(*value_to_stock_id) (enum fmt_type, int value); - GtkCellRenderer *cell; - - value_to_stock_id = g_object_get_data (G_OBJECT (renderer), - "value-to-stock-id"); - - cell = gtk_cell_renderer_pixbuf_new (); - g_object_set (cell, "width", 16, "height", 16, NULL); - g_object_set_data (G_OBJECT (cell), - "value-to-stock-id", value_to_stock_id); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE); - gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell, - render_var_pixbuf, - GINT_TO_POINTER (format->type), - NULL); - } -} - -static void -add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id, - const char *title, int width, - const char *(*value_to_stock_id) (enum fmt_type, int value), - ...) -{ - PsppSheetViewColumn *column; - GtkCellRenderer *cell; - GtkListStore *store; - const char *name; - va_list args; - - store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING); - va_start (args, value_to_stock_id); - while ((name = va_arg (args, const char *)) != NULL) - { - int value = va_arg (args, int); - gtk_list_store_insert_with_values (store, NULL, G_MAXINT, - 0, value, - 1, name, - -1); - } - va_end (args); - - cell = gtk_cell_renderer_combo_new (); - g_object_set (cell, - "has-entry", FALSE, - "model", GTK_TREE_MODEL (store), - "text-column", 1, - NULL); - if (value_to_stock_id != NULL) - { - g_object_set_data (G_OBJECT (cell), - "value-to-stock-id", value_to_stock_id); - g_signal_connect (cell, "editing-started", - G_CALLBACK (on_combo_editing_started), - var_sheet); - } - - column = add_var_sheet_column (var_sheet, cell, column_id, title, width); - - cell = gtk_cell_renderer_pixbuf_new (); - g_object_set (cell, "width", 16, "height", 16, NULL); - pspp_sheet_view_column_pack_end (column, cell, FALSE); - pspp_sheet_view_column_set_cell_data_func ( - column, cell, render_var_cell, var_sheet, NULL); -} - -static void -add_popup_menu (PsppireVarSheet *var_sheet, - PsppSheetViewColumn *column, - void (*on_click) (PsppireCellRendererButton *, - gchar *path, - PsppireVarSheet *var_sheet)) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - const char *button_label = "..."; - GtkCellRenderer *button_renderer; - gint content_width; - - button_renderer = psppire_cell_renderer_button_new (); - g_object_set (button_renderer, - "label", button_label, - "editable", TRUE, - NULL); - g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click), - var_sheet); - pspp_sheet_view_column_pack_start (column, button_renderer, FALSE); - pspp_sheet_view_column_set_cell_data_func ( - column, button_renderer, render_popup_cell, var_sheet, NULL); - - content_width = GPOINTER_TO_INT (g_object_get_data ( - G_OBJECT (column), "content-width")); - content_width += get_string_width (sheet_view, button_renderer, - button_label); - if (content_width > pspp_sheet_view_column_get_fixed_width (column)) - pspp_sheet_view_column_set_fixed_width (column, content_width); -} - -static gboolean -get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip, - gint wx, gint wy, size_t *row, size_t *column) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - gint bx, by; - GtkTreePath *path; - GtkTreeIter iter; - PsppSheetViewColumn *tree_column; - GtkTreeModel *tree_model; - gpointer column_ptr; - bool ok; - - /* Check that WIDGET is really visible on the screen before we - do anything else. This is a bug fix for a sticky situation: - when text_data_import_assistant() returns, it frees the data - necessary to compose the tool tip message, but there may be - a tool tip under preparation at that point (even if there is - no visible tool tip) that will call back into us a little - bit later. Perhaps the correct solution to this problem is - to make the data related to the tool tips part of a GObject - that only gets destroyed when all references are released, - but this solution appears to be effective too. */ - if (!gtk_widget_get_mapped (widget)) - return FALSE; - - pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, - wx, wy, &bx, &by); - if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by, - &path, &tree_column, NULL, NULL)) - return FALSE; - - column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number"); - if (column_ptr == NULL) - return FALSE; - *column = GPOINTER_TO_INT (column_ptr) - 1; - - pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column, - NULL); - - tree_model = pspp_sheet_view_get_model (tree_view); - ok = gtk_tree_model_get_iter (tree_model, &iter, path); - gtk_tree_path_free (path); - if (!ok) - return FALSE; - - *row = GPOINTER_TO_INT (iter.user_data); - return TRUE; -} - -static gboolean -on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy, - gboolean keyboard_mode UNUSED, - GtkTooltip *tooltip, gpointer *user_data UNUSED) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget); - PsppireDict *dict; - struct variable *var; - size_t row, column; - - if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column)) - return FALSE; - - dict = psppire_var_sheet_get_dictionary (var_sheet); - g_return_val_if_fail (dict != NULL, FALSE); - - if (row >= psppire_dict_get_var_cnt (dict)) - { - gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a " - "new variable.")); - return TRUE; - } - - var = psppire_dict_get_variable (dict, row); - g_return_val_if_fail (var != NULL, FALSE); - - switch (column) - { - case VS_TYPE: - { - char text[FMT_STRING_LEN_MAX + 1]; - - fmt_to_string (var_get_print_format (var), text); - gtk_tooltip_set_text (tooltip, text); - return TRUE; - } - - case VS_VALUES: - if (var_has_value_labels (var)) - { - const struct val_labs *vls = var_get_value_labels (var); - const struct val_lab **labels = val_labs_sorted (vls); - struct string s; - size_t i; - - ds_init_empty (&s); - for (i = 0; i < val_labs_count (vls); i++) - { - const struct val_lab *vl = labels[i]; - gchar *vstr; - - if (i >= 10 || ds_length (&s) > 500) - { - ds_put_cstr (&s, "..."); - break; - } - - vstr = value_to_text (vl->value, var); - ds_put_format (&s, _("{%s, %s}\n"), vstr, - val_lab_get_escaped_label (vl)); - free (vstr); - - } - ds_chomp_byte (&s, '\n'); - - gtk_tooltip_set_text (tooltip, ds_cstr (&s)); - ds_destroy (&s); - free (labels); - - return TRUE; - } - } - - return FALSE; -} - -static void -do_popup_menu (GtkWidget *widget, guint button, guint32 time) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget); - GtkWidget *menu = gtk_menu_new (); - - int i = 0; - - GtkWidget *insert_variable = gtk_menu_item_new_with_mnemonic (_("_Insert Variable")); - GtkWidget *clear_variables = gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables")); - - g_signal_connect_swapped (insert_variable, "activate", G_CALLBACK (psppire_var_sheet_insert_variable), var_sheet); - g_signal_connect_swapped (clear_variables, "activate", G_CALLBACK (psppire_var_sheet_clear_variables), var_sheet); - - gtk_menu_attach (GTK_MENU (menu), insert_variable, 0, 1, i, i + 1); ++i; - gtk_menu_attach (GTK_MENU (menu), clear_variables, 0, 1, i, i + 1); ++i; - - gtk_widget_show_all (menu); - - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time); -} - -static void -on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED) -{ - do_popup_menu (widget, 0, gtk_get_current_event_time ()); -} - -static gboolean -on_button_pressed (GtkWidget *widget, GdkEventButton *event, - gpointer user_data UNUSED) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget); - - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - PsppSheetSelection *selection; - - selection = pspp_sheet_view_get_selection (sheet_view); - if (pspp_sheet_selection_count_selected_rows (selection) <= 1) - { - GtkTreePath *path; - - if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y, - &path, NULL, NULL, NULL)) - { - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - gtk_tree_path_free (path); - } - } - - do_popup_menu (widget, event->button, event->time); - return TRUE; - } - - return FALSE; -} - - -GType -psppire_fmt_use_get_type (void) -{ - static GType etype = 0; - if (etype == 0) - { - static const GEnumValue values[] = - { - { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" }, - { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" }, - { 0, NULL, NULL } - }; - - etype = g_enum_register_static - (g_intern_static_string ("PsppireFmtUse"), values); - } - return etype; -} - -enum - { - PROP_0, - PROP_DICTIONARY, - PROP_MAY_CREATE_VARS, - PROP_MAY_DELETE_VARS, - PROP_FORMAT_TYPE - }; - -static void -psppire_var_sheet_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object); - - switch (prop_id) - { - case PROP_DICTIONARY: - psppire_var_sheet_set_dictionary (obj, - PSPPIRE_DICT (g_value_get_object ( - value))); - break; - - case PROP_MAY_CREATE_VARS: - psppire_var_sheet_set_may_create_vars (obj, - g_value_get_boolean (value)); - break; - - case PROP_MAY_DELETE_VARS: - psppire_var_sheet_set_may_delete_vars (obj, - g_value_get_boolean (value)); - break; - - case PROP_FORMAT_TYPE: - obj->format_use = g_value_get_enum (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_var_sheet_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object); - - switch (prop_id) - { - case PROP_DICTIONARY: - g_value_set_object (value, - G_OBJECT (psppire_var_sheet_get_dictionary (obj))); - break; - - case PROP_MAY_CREATE_VARS: - g_value_set_boolean (value, obj->may_create_vars); - break; - - case PROP_MAY_DELETE_VARS: - g_value_set_boolean (value, obj->may_delete_vars); - break; - - case PROP_FORMAT_TYPE: - g_value_set_enum (value, obj->format_use); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_var_sheet_dispose (GObject *obj) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj); - int i; - - if (var_sheet->dispose_has_run) - return; - - var_sheet->dispose_has_run = TRUE; - - for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++) - if ( var_sheet->dict_signals[i]) - g_signal_handler_disconnect (var_sheet->dict, - var_sheet->dict_signals[i]); - - if (var_sheet->dict) - g_object_unref (var_sheet->dict); - - /* These dialogs are not GObjects (although they should be!) - But for now, unreffing them only causes a GCritical Error - so comment them out for now. (and accept the memory leakage) - - g_object_unref (var_sheet->val_labs_dialog); - g_object_unref (var_sheet->missing_val_dialog); - g_object_unref (var_sheet->var_type_dialog); - */ - - G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj); -} - -static void -psppire_var_sheet_class_init (PsppireVarSheetClass *class) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - GParamSpec *pspec; - - gobject_class->set_property = psppire_var_sheet_set_property; - gobject_class->get_property = psppire_var_sheet_get_property; - gobject_class->dispose = psppire_var_sheet_dispose; - - g_signal_new ("var-double-clicked", - G_OBJECT_CLASS_TYPE (gobject_class), - G_SIGNAL_RUN_LAST, - 0, - g_signal_accumulator_true_handled, NULL, - psppire_marshal_BOOLEAN__INT, - G_TYPE_BOOLEAN, 1, G_TYPE_INT); - - pspec = g_param_spec_object ("dictionary", - "Dictionary displayed by the sheet", - "The PsppireDict that the sheet displays " - "may allow the user to edit", - PSPPIRE_TYPE_DICT, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec); - - pspec = g_param_spec_boolean ("may-create-vars", - "May create variables", - "Whether the user may create more variables", - TRUE, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec); - - pspec = g_param_spec_boolean ("may-delete-vars", - "May delete variables", - "Whether the user may delete variables", - TRUE, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec); - - pspec = g_param_spec_enum ("format-use", - "Use of variable format", - ("Whether variables have input or output " - "formats"), - PSPPIRE_TYPE_FMT_USE, - FMT_FOR_OUTPUT, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec); -} - -static void -render_row_number_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = user_data; - GValue gvalue = { 0, }; - gint row; - - row = GPOINTER_TO_INT (iter->user_data); - - g_value_init (&gvalue, G_TYPE_INT); - g_value_set_int (&gvalue, row + 1); - g_object_set_property (G_OBJECT (cell), "label", &gvalue); - g_value_unset (&gvalue); - - if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict)) - g_object_set (cell, "editable", TRUE, NULL); - else - g_object_set (cell, "editable", FALSE, NULL); -} - -static void -psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button, - gchar *path_string, - PsppireVarSheet *var_sheet) -{ - GtkTreePath *path; - - g_return_if_fail (var_sheet->dict != NULL); - - path = gtk_tree_path_new_from_string (path_string); - if (gtk_tree_path_get_depth (path) == 1) - { - gint *indices = gtk_tree_path_get_indices (path); - if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict)) - { - gboolean handled; - g_signal_emit_by_name (var_sheet, "var-double-clicked", - indices[0], &handled); - } - } - gtk_tree_path_free (path); -} - -static void -psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column, - PsppireVarSheet *var_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - - pspp_sheet_selection_select_all (selection); -} - -static PsppSheetViewColumn * -make_row_number_column (PsppireVarSheet *var_sheet) -{ - PsppSheetViewColumn *column; - GtkCellRenderer *renderer; - - renderer = psppire_cell_renderer_button_new (); - g_object_set (renderer, "xalign", 1.0, NULL); - g_signal_connect (renderer, "double-clicked", - G_CALLBACK (psppire_var_sheet_row_number_double_clicked), - var_sheet); - - column = pspp_sheet_view_column_new_with_attributes (_("Variable"), - renderer, NULL); - pspp_sheet_view_column_set_clickable (column, TRUE); - pspp_sheet_view_column_set_cell_data_func ( - column, renderer, render_row_number_cell, var_sheet, NULL); - pspp_sheet_view_column_set_fixed_width (column, 50); - g_signal_connect (column, "clicked", - G_CALLBACK (psppire_var_sheet_variables_column_clicked), - var_sheet); - - return column; -} - -void -psppire_var_sheet_clear_variables (PsppireVarSheet *var_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = var_sheet->dict; - const struct range_set_node *node; - struct range_set *selected = pspp_sheet_selection_get_range_set (selection); - - for (node = range_set_last (selected); node != NULL; - node = range_set_prev (selected, node)) - { - int i; - - for (i = 1; i <= range_set_node_get_width (node); i++) - { - unsigned long row = range_set_node_get_end (node) - i; - if (row < psppire_dict_get_var_cnt (dict)) - psppire_dict_delete_variables (dict, row, 1); - } - } - range_set_destroy (selected); -} - -static void -on_selection_changed (PsppSheetSelection *selection, - gpointer user_data UNUSED) -{ - PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection); - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view); - gint n_selected_rows = pspp_sheet_selection_count_selected_rows (selection); - gboolean may_delete; - GtkTreePath *path; - - GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)); - if (! PSPPIRE_IS_DATA_WINDOW (top)) - return; - - PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top); - gtk_widget_set_sensitive (dw->mi_insert_var, n_selected_rows > 0); - - switch (n_selected_rows) - { - case 0: - may_delete = FALSE; - break; - - case 1: - /* The row used for inserting new variables cannot be deleted. */ - path = gtk_tree_path_new_from_indices ( - psppire_dict_get_var_cnt (var_sheet->dict), -1); - may_delete = !pspp_sheet_selection_path_is_selected (selection, path); - gtk_tree_path_free (path); - break; - - default: - may_delete = TRUE; - break; - } - - gtk_widget_set_sensitive (dw->mi_clear_variables, var_sheet->may_delete_vars && may_delete); -} - -void -psppire_var_sheet_insert_variable (PsppireVarSheet *var_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = var_sheet->dict; - struct range_set *selected; - unsigned long row; - - selected = pspp_sheet_selection_get_range_set (selection); - row = range_set_scan (selected, 0); - range_set_destroy (selected); - - if (row <= psppire_dict_get_var_cnt (dict)) - { - gchar name[64];; - if (psppire_dict_generate_name (dict, name, sizeof name)) - psppire_dict_insert_variable (dict, row, name); - } -} - -static void -psppire_var_sheet_init (PsppireVarSheet *obj) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj); - PsppSheetViewColumn *column; - GList *list; - - obj->dict = NULL; - obj->format_use = FMT_FOR_OUTPUT; - obj->may_create_vars = TRUE; - obj->may_delete_vars = TRUE; - - obj->scroll_to_bottom_signal = 0; - - obj->container = NULL; - obj->dispose_has_run = FALSE; - - pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj)); - - column = add_text_column (obj, VS_NAME, _("Name"), 12); - list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); - g_signal_connect (list->data, "editing-started", - G_CALLBACK (on_name_column_editing_started), NULL); - g_list_free (list); - - column = add_text_column (obj, VS_TYPE, _("Type"), 8); - add_popup_menu (obj, column, on_type_click); - - add_spin_column (obj, VS_WIDTH, _("Width"), 5); - - add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2); - - add_text_column (obj, VS_LABEL, _("Label"), 20); - - column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20); - add_popup_menu (obj, column, on_value_labels_click); - - column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20); - add_popup_menu (obj, column, on_missing_values_click); - - add_spin_column (obj, VS_COLUMNS, _("Columns"), 3); - - add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id, - alignment_to_string (ALIGN_LEFT), ALIGN_LEFT, - alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE, - alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT, - NULL); - - add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id, - measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL, - measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL, - measure_to_string (MEASURE_SCALE), MEASURE_SCALE, - NULL); - - add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id, - var_role_to_string (ROLE_INPUT), ROLE_INPUT, - var_role_to_string (ROLE_TARGET), ROLE_TARGET, - var_role_to_string (ROLE_BOTH), ROLE_BOTH, - var_role_to_string (ROLE_NONE), ROLE_NONE, - var_role_to_string (ROLE_PARTITION), ROLE_PARTITION, - var_role_to_string (ROLE_SPLIT), ROLE_SPLIT, - NULL); - - pspp_sheet_view_set_rubber_banding (sheet_view, TRUE); - pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view), - PSPP_SHEET_SELECTION_MULTIPLE); - - g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL); - g_signal_connect (obj, "query-tooltip", - G_CALLBACK (on_query_var_tooltip), NULL); - g_signal_connect (obj, "button-press-event", - G_CALLBACK (on_button_pressed), NULL); - - g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL); - - g_signal_connect (pspp_sheet_view_get_selection (sheet_view), - "changed", G_CALLBACK (on_selection_changed), NULL); -} - -GtkWidget * -psppire_var_sheet_new (void) -{ - return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL); -} - -PsppireDict * -psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet) -{ - return var_sheet->dict; -} - -static void -refresh_model (PsppireVarSheet *var_sheet) -{ - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL); - - if (var_sheet->dict != NULL) - { - PsppireEmptyListStore *store; - int n_rows; - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - store = psppire_empty_list_store_new (n_rows); - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), - GTK_TREE_MODEL (store)); - g_object_unref (store); - } -} - -static void -on_var_changed (PsppireDict *dict, glong row, - guint what, const struct variable *oldvar, - PsppireVarSheet *var_sheet) -{ - PsppireEmptyListStore *store; - - g_return_if_fail (dict == var_sheet->dict); - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - psppire_empty_list_store_row_changed (store, row); -} - -static void -on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet) -{ - PsppireEmptyListStore *store; - int n_rows; - - g_return_if_fail (dict == var_sheet->dict); - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - psppire_empty_list_store_set_n_rows (store, n_rows); - psppire_empty_list_store_row_inserted (store, row); -} - -static void -on_var_deleted (PsppireDict *dict, - const struct variable *var, int dict_idx, int case_idx, - PsppireVarSheet *var_sheet) -{ - PsppireEmptyListStore *store; - int n_rows; - - g_return_if_fail (dict == var_sheet->dict); - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - psppire_empty_list_store_set_n_rows (store, n_rows); - psppire_empty_list_store_row_deleted (store, dict_idx); -} - -static void -on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet) -{ - g_return_if_fail (dict == var_sheet->dict); - refresh_model (var_sheet); -} - -void -psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet, - PsppireDict *dict) -{ - if (var_sheet->dict != NULL) - { - int i; - - for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++) - { - if (var_sheet->dict_signals[i]) - g_signal_handler_disconnect (var_sheet->dict, - var_sheet->dict_signals[i]); - - var_sheet->dict_signals[i] = 0; - } - - g_object_unref (var_sheet->dict); - } - - var_sheet->dict = dict; - - if (dict != NULL) - { - g_object_ref (dict); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED] - = g_signal_connect (dict, "backend-changed", - G_CALLBACK (on_backend_changed), var_sheet); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED] - = g_signal_connect (dict, "variable-changed", - G_CALLBACK (on_var_changed), var_sheet); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED] - = g_signal_connect (dict, "variable-inserted", - G_CALLBACK (on_var_inserted), var_sheet); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED] - = g_signal_connect (dict, "variable-deleted", - G_CALLBACK (on_var_deleted), var_sheet); - } - - refresh_model (var_sheet); -} - -gboolean -psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet) -{ - return var_sheet->may_create_vars; -} - -void -psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet, - gboolean may_create_vars) -{ - if (var_sheet->may_create_vars != may_create_vars) - { - PsppireEmptyListStore *store; - gint n_rows; - - var_sheet->may_create_vars = may_create_vars; - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - psppire_empty_list_store_set_n_rows (store, n_rows); - - if (may_create_vars) - psppire_empty_list_store_row_inserted (store, n_rows - 1); - else - psppire_empty_list_store_row_deleted (store, n_rows); - - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (var_sheet)), NULL); - } -} - -gboolean -psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet) -{ - return var_sheet->may_delete_vars; -} - -void -psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet, - gboolean may_delete_vars) -{ - if (var_sheet->may_delete_vars != may_delete_vars) - { - var_sheet->may_delete_vars = may_delete_vars; - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (var_sheet)), NULL); - } -} - -void -psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (dict_index, -1); - pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0); - pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE); - gtk_tree_path_free (path); -} - - diff --git a/src/ui/gui/psppire-var-sheet.h b/src/ui/gui/psppire-var-sheet.h deleted file mode 100644 index 6a57cc6de9..0000000000 --- a/src/ui/gui/psppire-var-sheet.h +++ /dev/null @@ -1,100 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2008, 2011, 2012 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 . */ - -#ifndef __PSPPIRE_VAR_SHEET_H__ -#define __PSPPIRE_VAR_SHEET_H__ - -/* PsppireVarSheet is a PsppSheetView that displays the variables in a - dictionary, one variable per row. - - PsppireDataSheet is usually a child of PsppireDataEditor in the widget - hierarchy. Other widgets can also use it. */ - -#include -#include "data/format.h" -#include "ui/gui/pspp-sheet-view.h" - - -G_BEGIN_DECLS - -#define PSPPIRE_TYPE_FMT_USE (psppire_fmt_use_get_type ()) - -GType psppire_fmt_use_get_type (void) G_GNUC_CONST; - -#define PSPPIRE_VAR_SHEET_TYPE (psppire_var_sheet_get_type ()) -#define PSPPIRE_VAR_SHEET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_VAR_SHEET_TYPE, PsppireVarSheet)) -#define PSPPIRE_VAR_SHEET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PSPPIRE_VAR_SHEET_TYPE, PsppireVarSheetClass)) -#define PSPPIRE_IS_VAR_SHEET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_VAR_SHEET_TYPE)) -#define PSPPIRE_IS_VAR_SHEET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_VAR_SHEET_TYPE)) - - -typedef struct _PsppireVarSheet PsppireVarSheet; -typedef struct _PsppireVarSheetClass PsppireVarSheetClass; - -enum -{ - PSPPIRE_VAR_SHEET_BACKEND_CHANGED, - PSPPIRE_VAR_SHEET_VARIABLE_CHANGED, - PSPPIRE_VAR_SHEET_VARIABLE_INSERTED, - PSPPIRE_VAR_SHEET_VARIABLE_DELETED, - PSPPIRE_VAR_SHEET_N_SIGNALS - }; - -struct _PsppireVarSheet -{ - PsppSheetView parent; - - gboolean may_create_vars; - gboolean may_delete_vars; - enum fmt_use format_use; - - struct _PsppireDict *dict; - - gulong scroll_to_bottom_signal; - gulong dict_signals[PSPPIRE_VAR_SHEET_N_SIGNALS]; - - GtkWidget *container; - gulong on_switch_page_handler; - - gboolean dispose_has_run; -}; - -struct _PsppireVarSheetClass -{ - PsppSheetViewClass parent_class; -}; - -GType psppire_var_sheet_get_type (void); -GtkWidget* psppire_var_sheet_new (void); - -struct _PsppireDict *psppire_var_sheet_get_dictionary (PsppireVarSheet *); -void psppire_var_sheet_set_dictionary (PsppireVarSheet *, - struct _PsppireDict *); - -gboolean psppire_var_sheet_get_may_create_vars (PsppireVarSheet *); -void psppire_var_sheet_set_may_create_vars (PsppireVarSheet *, gboolean); - -gboolean psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *); -void psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *, gboolean); - -void psppire_var_sheet_goto_variable (PsppireVarSheet *, int dict_index); -void psppire_var_sheet_insert_variable (PsppireVarSheet *var_sheet); -void psppire_var_sheet_clear_variables (PsppireVarSheet *var_sheet); - - -G_END_DECLS - -#endif /* __PSPPIRE_VAR_SHEET_H__ */ diff --git a/src/ui/gui/psppire-variable-sheet.c b/src/ui/gui/psppire-variable-sheet.c new file mode 100644 index 0000000000..2adef3f4d5 --- /dev/null +++ b/src/ui/gui/psppire-variable-sheet.c @@ -0,0 +1,522 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2017 John Darrington + + 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 . +*/ + +#include +#include "psppire-variable-sheet.h" + +#include "ui/gui/psppire-var-sheet-header.h" + +#include "psppire-dict.h" +#include "var-type-dialog.h" +#include "missing-val-dialog.h" +#include "val-labs-dialog.h" +#include "var-display.h" +#include "data/format.h" +#include "data/value-labels.h" +#include "helper.h" + +#include +#define _(msgid) gettext (msgid) +#define P_(X) (X) + + +G_DEFINE_TYPE (PsppireVariableSheet, psppire_variable_sheet, SSW_TYPE_SHEET) + +static void +set_var_type (GtkCellRenderer *renderer, + GtkCellEditable *editable, + gchar *path, + gpointer user_data) +{ + PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (user_data); + gint row = -1, col = -1; + ssw_sheet_get_active_cell (SSW_SHEET (sheet), &col, &row); + + PsppireDict *dict = NULL; + g_object_get (sheet, "data-model", &dict, NULL); + + struct variable *var = + psppire_dict_get_variable (PSPPIRE_DICT (dict), row); + + const struct fmt_spec *format = var_get_write_format (var); + struct fmt_spec fmt = *format; + GtkWindow *win = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet))); + if (GTK_RESPONSE_OK == psppire_var_type_dialog_run (win, &fmt)) + { + var_set_width_and_formats (var, fmt_var_width (&fmt), &fmt, &fmt); + } +} + +static void +set_missing_values (GtkCellRenderer *renderer, + GtkCellEditable *editable, + gchar *path, + gpointer user_data) +{ + PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (user_data); + gint row = -1, col = -1; + ssw_sheet_get_active_cell (SSW_SHEET (sheet), &col, &row); + + PsppireDict *dict = NULL; + g_object_get (sheet, "data-model", &dict, NULL); + + struct variable *var = + psppire_dict_get_variable (PSPPIRE_DICT (dict), row); + + struct missing_values mv; + if (GTK_RESPONSE_OK == + psppire_missing_val_dialog_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet))), + var, &mv)) + { + var_set_missing_values (var, &mv); + } + + mv_destroy (&mv); +} + +static void +set_value_labels (GtkCellRenderer *renderer, + GtkCellEditable *editable, + gchar *path, + gpointer user_data) +{ + PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (user_data); + gint row = -1, col = -1; + ssw_sheet_get_active_cell (SSW_SHEET (sheet), &col, &row); + + PsppireDict *dict = NULL; + g_object_get (sheet, "data-model", &dict, NULL); + + struct variable *var = + psppire_dict_get_variable (PSPPIRE_DICT (dict), row); + + struct val_labs *vls = + psppire_val_labs_dialog_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet))), var); + + if (vls) + { + var_set_value_labels (var, vls); + val_labs_destroy (vls); + } +} + +static GtkCellRenderer * +create_spin_renderer (GType type) +{ + GtkCellRenderer *r = gtk_cell_renderer_spin_new (); + + GtkAdjustment *adj = gtk_adjustment_new (0, + 0, G_MAXDOUBLE, + 1, 1, + 0); + g_object_set (r, + "adjustment", adj, + NULL); + + return r; +} + +static GtkCellRenderer * +create_combo_renderer (GType type) +{ + GtkListStore *list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING); + + GEnumClass *ec = g_type_class_ref (type); + + const GEnumValue *ev ; + for (ev = ec->values; ev->value_name; ++ev) + { + GtkTreeIter iter; + + gtk_list_store_append (list_store, &iter); + + gtk_list_store_set (list_store, &iter, + 0, ev->value, + 1, gettext (ev->value_nick), + -1); + } + + GtkCellRenderer *r = gtk_cell_renderer_combo_new (); + + g_object_set (r, + "model", list_store, + "text-column", 1, + "has-entry", TRUE, + NULL); + + return r; +} + +static GtkCellRenderer *spin_renderer; +static GtkCellRenderer *column_width_renderer; +static GtkCellRenderer *measure_renderer; +static GtkCellRenderer *alignment_renderer; + +static GtkCellRenderer * +select_renderer_func (PsppireVariableSheet *sheet, gint col, gint row, GType type, gpointer ud) +{ + if (!spin_renderer) + spin_renderer = create_spin_renderer (type); + + if (col == DICT_TVM_COL_ROLE && !column_width_renderer) + column_width_renderer = create_combo_renderer (type); + + if (col == DICT_TVM_COL_MEASURE && !measure_renderer) + measure_renderer = create_combo_renderer (type); + + if (col == DICT_TVM_COL_ALIGNMENT && !alignment_renderer) + alignment_renderer = create_combo_renderer (type); + + switch (col) + { + case DICT_TVM_COL_WIDTH: + case DICT_TVM_COL_DECIMAL: + case DICT_TVM_COL_COLUMNS: + return spin_renderer; + + case DICT_TVM_COL_TYPE: + return sheet->var_type_renderer; + + case DICT_TVM_COL_VALUE_LABELS: + return sheet->value_label_renderer; + + case DICT_TVM_COL_MISSING_VALUES: + return sheet->missing_values_renderer; + + case DICT_TVM_COL_ALIGNMENT: + return alignment_renderer; + + case DICT_TVM_COL_MEASURE: + return measure_renderer; + + case DICT_TVM_COL_ROLE: + return column_width_renderer; + } + + return NULL; +} + + + +static void +show_variables_row_popup (SswSheet *sheet, int row, uint button, + uint state, gpointer p) +{ + PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet); + GListModel *vmodel = NULL; + g_object_get (sheet, "vmodel", &vmodel, NULL); + if (vmodel == NULL) + return; + + guint n_items = g_list_model_get_n_items (vmodel); + + if (row >= n_items) + return; + + if (button != 3) + return; + + g_object_set_data (G_OBJECT (var_sheet->row_popup), "item", + GINT_TO_POINTER (row)); + + gtk_menu_popup_at_pointer (GTK_MENU (var_sheet->row_popup), NULL); +} + +static void +insert_new_variable_var (PsppireVariableSheet *var_sheet) +{ + gint item = GPOINTER_TO_INT (g_object_get_data + (G_OBJECT (var_sheet->row_popup), + "item")); + + PsppireDict *dict = NULL; + g_object_get (var_sheet, "data-model", &dict, NULL); + + psppire_dict_insert_variable (dict, item, NULL); + + gtk_widget_queue_draw (GTK_WIDGET (var_sheet)); +} + + +static void +delete_variables (SswSheet *sheet) +{ + SswRange *range = sheet->selection; + + PsppireDict *dict = NULL; + g_object_get (sheet, "data-model", &dict, NULL); + + psppire_dict_delete_variables (dict, range->start_y, + (range->end_y - range->start_y + 1)); + + gtk_widget_queue_draw (GTK_WIDGET (sheet)); +} + +static GtkWidget * +create_var_row_header_popup_menu (PsppireVariableSheet *var_sheet) +{ + GtkWidget *menu = gtk_menu_new (); + + GtkWidget *item = + gtk_menu_item_new_with_mnemonic (_("_Insert Variable")); + g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable_var), + var_sheet); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_separator_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + var_sheet->clear_variables_menu_item = + gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables")); + + g_signal_connect_swapped (var_sheet->clear_variables_menu_item, "activate", + G_CALLBACK (delete_variables), var_sheet); + + gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item, FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), + var_sheet->clear_variables_menu_item); + + gtk_widget_show_all (menu); + return menu; +} + + +static void +set_var_popup_sensitivity (SswSheet *sheet, gpointer selection, gpointer p) +{ + PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet); + SswRange *range = selection; + gint width = gtk_tree_model_get_n_columns (sheet->data_model); + + gboolean whole_row_selected = (range->start_x == 0 && + range->end_x == width - 1 - 1); + /* PsppireDict has an "extra" column: TVM_COL_VAR ^^^ */ + gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item, + whole_row_selected); +} + + + +static void +change_var_property (PsppireVariableSheet *var_sheet, gint col, gint row, const GValue *value) +{ + PsppireDict *dict = NULL; + g_object_get (var_sheet, "data-model", &dict, NULL); + + /* Return the IDXth variable */ + struct variable *var = psppire_dict_get_variable (dict, row); + + if (NULL == var) + var = psppire_dict_insert_variable (dict, row, NULL); + + switch (col) + { + case DICT_TVM_COL_NAME: + { + const char *name = g_value_get_string (value); + if (psppire_dict_check_name (dict, name, FALSE)) + dict_rename_var (dict->dict, var, g_value_get_string (value)); + } + break; + case DICT_TVM_COL_WIDTH: + { + gint width = g_value_get_int (value); + if (var_is_numeric (var)) + { + struct fmt_spec format = *var_get_print_format (var); + fmt_change_width (&format, width, FMT_FOR_OUTPUT); + var_set_both_formats (var, &format); + } + else + { + var_set_width (var, width); + } + } + break; + case DICT_TVM_COL_DECIMAL: + { + gint decimals = g_value_get_int (value); + if (decimals >= 0) + { + struct fmt_spec format = *var_get_print_format (var); + fmt_change_decimals (&format, decimals, FMT_FOR_OUTPUT); + var_set_both_formats (var, &format); + } + } + break; + case DICT_TVM_COL_LABEL: + var_set_label (var, g_value_get_string (value)); + break; + case DICT_TVM_COL_COLUMNS: + var_set_display_width (var, g_value_get_int (value)); + break; + case DICT_TVM_COL_MEASURE: + var_set_measure (var, g_value_get_int (value)); + break; + case DICT_TVM_COL_ALIGNMENT: + var_set_alignment (var, g_value_get_int (value)); + break; + case DICT_TVM_COL_ROLE: + var_set_role (var, g_value_get_int (value)); + break; + default: + g_warning ("Changing unknown column %d of variable sheet column not supported", + col); + break; + } +} + +static gchar * +var_sheet_data_to_string (SswSheet *sheet, GtkTreeModel *m, + gint col, gint row, const GValue *in) +{ + if (col >= n_DICT_COLS - 1) /* -1 because psppire-dict has an extra column */ + return NULL; + + const struct variable *var = psppire_dict_get_variable (PSPPIRE_DICT (m), row); + if (var == NULL) + return NULL; + + if (col == DICT_TVM_COL_TYPE) + { + const struct fmt_spec *print = var_get_print_format (var); + return strdup (fmt_gui_name (print->type)); + } + else if (col == DICT_TVM_COL_MISSING_VALUES) + return missing_values_to_string (var, NULL); + else if (col == DICT_TVM_COL_VALUE_LABELS) + { + const struct val_labs *vls = var_get_value_labels (var); + if (vls == NULL || val_labs_count (vls) == 0) + return strdup (_("None")); + const struct val_lab **labels = val_labs_sorted (vls); + const struct val_lab *vl = labels[0]; + gchar *vstr = value_to_text (vl->value, var); + char *text = xasprintf (_("{%s, %s}..."), vstr, + val_lab_get_escaped_label (vl)); + free (vstr); + free (labels); + return text; + } + + return ssw_sheet_default_forward_conversion (sheet, m, col, row, in); +} + + + +static GObjectClass * parent_class = NULL; + +static void +psppire_variable_sheet_dispose (GObject *obj) +{ + PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (obj); + + if (sheet->dispose_has_run) + return; + + sheet->dispose_has_run = TRUE; + + g_object_unref (sheet->value_label_renderer); + g_object_unref (sheet->missing_values_renderer); + g_object_unref (sheet->var_type_renderer); + + /* Chain up to the parent class */ + G_OBJECT_CLASS (parent_class)->dispose (obj); +} + +static void +psppire_variable_sheet_class_init (PsppireVariableSheetClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + object_class->dispose = psppire_variable_sheet_dispose; + + parent_class = g_type_class_peek_parent (class); +} + +GtkWidget* +psppire_variable_sheet_new (void) +{ + PsppireVarSheetHeader *vsh = + g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL); + + GObject *obj = + g_object_new (PSPPIRE_TYPE_VARIABLE_SHEET, + "select-renderer-func", select_renderer_func, + "hmodel", vsh, + "forward-conversion", var_sheet_data_to_string, + NULL); + + return GTK_WIDGET (obj); +} + +static void +move_variable (PsppireVariableSheet *sheet, gint from, gint to, gpointer ud) +{ + PsppireDict *dict = NULL; + g_object_get (sheet, "data-model", &dict, NULL); + + if (dict == NULL) + return; + + struct variable *var = psppire_dict_get_variable (dict, from); + + if (var == NULL) + return; + gint new_pos = to; + /* The index refers to the final position, so if the source + is less than the destination, then we must subtract 1, to + account for the position vacated by the source */ + if (from < to) + new_pos--; + dict_reorder_var (dict->dict, var, new_pos); +} + +static void +psppire_variable_sheet_init (PsppireVariableSheet *sheet) +{ + sheet->dispose_has_run = FALSE; + + sheet->value_label_renderer = gtk_cell_renderer_text_new (); + g_signal_connect (sheet->value_label_renderer, + "editing-started", G_CALLBACK (set_value_labels), + sheet); + + sheet->missing_values_renderer = gtk_cell_renderer_text_new (); + g_signal_connect (sheet->missing_values_renderer, + "editing-started", G_CALLBACK (set_missing_values), + sheet); + + sheet->var_type_renderer = gtk_cell_renderer_text_new (); + g_signal_connect (sheet->var_type_renderer, + "editing-started", G_CALLBACK (set_var_type), + sheet); + + sheet->row_popup = create_var_row_header_popup_menu (sheet); + + + g_signal_connect (sheet, "selection-changed", + G_CALLBACK (set_var_popup_sensitivity), sheet); + + g_signal_connect (sheet, "row-header-pressed", + G_CALLBACK (show_variables_row_popup), sheet); + + g_signal_connect_swapped (sheet, "value-changed", + G_CALLBACK (change_var_property), sheet); + + g_signal_connect (sheet, "row-moved", + G_CALLBACK (move_variable), NULL); +} diff --git a/src/ui/gui/psppire-variable-sheet.h b/src/ui/gui/psppire-variable-sheet.h new file mode 100644 index 0000000000..b77da939d0 --- /dev/null +++ b/src/ui/gui/psppire-variable-sheet.h @@ -0,0 +1,51 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2017 John Darrington + + 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 . +*/ + +#ifndef _PSPPIRE_VARIABLE_SHEET_H +#define _PSPPIRE_VARIABLE_SHEET_H + +#include +#include + + +struct _PsppireVariableSheet +{ + SswSheet parent_instance; + + GtkCellRenderer *value_label_renderer; + GtkCellRenderer *missing_values_renderer; + GtkCellRenderer *var_type_renderer; + + /* Row header popup menu */ + GtkWidget *row_popup; + GtkWidget *clear_variables_menu_item; + + gboolean dispose_has_run; +}; + +struct _PsppireVariableSheetClass +{ + SswSheetClass parent_class; +}; + +#define PSPPIRE_TYPE_VARIABLE_SHEET psppire_variable_sheet_get_type () + +G_DECLARE_FINAL_TYPE (PsppireVariableSheet, psppire_variable_sheet, PSPPIRE, VARIABLE_SHEET, SswSheet) + +GtkWidget *psppire_variable_sheet_new (void); + +#endif diff --git a/src/ui/gui/psppire.gtkrc b/src/ui/gui/psppire.gtkrc deleted file mode 100644 index d6c04787d2..0000000000 --- a/src/ui/gui/psppire.gtkrc +++ /dev/null @@ -1,7 +0,0 @@ -# -# Avoid making buttons inside the PSPPIRE data sheet and variable sheet -# extra-tall. This allows more rows to fit on the screen, making data -# easier to review. -# -style "thinbuttons" { ythickness = 0 } -widget_class "**GtkButton" style : rc "thinbuttons" diff --git a/src/ui/gui/text-data-import.ui b/src/ui/gui/text-data-import.ui index 0b40468ef1..86e6f39906 100644 --- a/src/ui/gui/text-data-import.ui +++ b/src/ui/gui/text-data-import.ui @@ -93,6 +93,7 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical 94 + True True @@ -106,6 +107,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 + 12 @@ -133,6 +135,7 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none + 12 True diff --git a/src/ui/gui/value-variant.c b/src/ui/gui/value-variant.c new file mode 100644 index 0000000000..0789717ab7 --- /dev/null +++ b/src/ui/gui/value-variant.c @@ -0,0 +1,99 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2016 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 . */ + +#include +#include + +#include +#include "value-variant.h" +#include "data/value.h" + + +enum + { + IDX_WIDTH, + IDX_DATA + }; + +/* Returns a GVariant containing the data contained + in IN and WIDTH. The returned GVariant has a floating + reference. + */ +GVariant * +value_variant_new (const union value *in, int width) +{ + GVariant *vv[2] = {NULL, NULL}; + vv[IDX_WIDTH] = g_variant_new_int32 (width); + + if (width == 0) + vv[IDX_DATA] = g_variant_new_double (in->f); + else if (width <= MAX_SHORT_STRING) + { + char xx[MAX_SHORT_STRING + 1]; + memset (xx, '\0', MAX_SHORT_STRING + 1); + memcpy (xx, in->short_string, width); + vv[IDX_DATA] = g_variant_new_bytestring (xx); + } + else + { + gchar *q = xmalloc (width + 1); + memcpy (q, in->long_string, width); + q[width] = '\0'; + vv[IDX_DATA] = g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING, q, + width + 1, FALSE, NULL, NULL); + } + + return g_variant_new_tuple (vv, 2); +} + +/* Destroy the contents of VAL. Also unref V */ +void +value_destroy_from_variant (union value *val, GVariant *v) +{ + GVariant *vwidth = g_variant_get_child_value (v, IDX_WIDTH); + gint32 width = g_variant_get_int32 (vwidth); /* v is unreffed here */ + g_variant_unref (vwidth); + value_destroy (val, width); +} + + +/* Fills VAL with the value data held in V. + When VAL is no longer required it must be destroyed using + value_destroy_from_variant. */ +void +value_variant_get (union value *val, GVariant *v) +{ + GVariant *vwidth = g_variant_get_child_value (v, IDX_WIDTH); + gint32 width = g_variant_get_int32 (vwidth); + g_variant_unref (vwidth); + + GVariant *vdata = g_variant_get_child_value (v, IDX_DATA); + + if (0 == width) + val->f = g_variant_get_double (vdata); + else + { + const gchar *data = g_variant_get_bytestring (vdata); + if (width <= MAX_SHORT_STRING) + memcpy (val->short_string, data, MAX_SHORT_STRING); + else + { + val->long_string = xmemdup (data, width); + } + } + + g_variant_unref (vdata); +} diff --git a/src/ui/gui/value-variant.h b/src/ui/gui/value-variant.h new file mode 100644 index 0000000000..22cd17688b --- /dev/null +++ b/src/ui/gui/value-variant.h @@ -0,0 +1,28 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2016 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 . */ + +#ifndef VALUE_VARIANT_H +#define VALUE_VARIANT_H + +union value; + +GVariant *value_variant_new (const union value *in, int width); + +void value_variant_get (union value *val, GVariant *v); + +void value_destroy_from_variant (union value *val, GVariant *v); + +#endif diff --git a/src/ui/gui/var-type-dialog.c b/src/ui/gui/var-type-dialog.c index 7f1d4aeccd..a2792ef089 100644 --- a/src/ui/gui/var-type-dialog.c +++ b/src/ui/gui/var-type-dialog.c @@ -201,7 +201,7 @@ psppire_var_type_dialog_new (const struct fmt_spec *format) NULL)); } -void +gint psppire_var_type_dialog_run (GtkWindow *parent_window, struct fmt_spec *format) { @@ -212,10 +212,13 @@ psppire_var_type_dialog_run (GtkWindow *parent_window, gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_widget_show (GTK_WIDGET (dialog)); - if (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK) + gint result = psppire_dialog_run (PSPPIRE_DIALOG (dialog)); + if (result == GTK_RESPONSE_OK) *format = *psppire_var_type_dialog_get_format (dialog); gtk_widget_destroy (GTK_WIDGET (dialog)); + + return result; } diff --git a/src/ui/gui/var-type-dialog.h b/src/ui/gui/var-type-dialog.h index 32c160f052..ee9b7b2ac5 100644 --- a/src/ui/gui/var-type-dialog.h +++ b/src/ui/gui/var-type-dialog.h @@ -102,7 +102,7 @@ struct _PsppireVarTypeDialogClass { GType psppire_var_type_dialog_get_type (void) G_GNUC_CONST; PsppireVarTypeDialog* psppire_var_type_dialog_new (const struct fmt_spec *); -void psppire_var_type_dialog_run (GtkWindow *parent_window, +gint psppire_var_type_dialog_run (GtkWindow *parent_window, struct fmt_spec *format); G_END_DECLS diff --git a/src/ui/gui/widgets.c b/src/ui/gui/widgets.c index ec9be33516..26a5dac4fd 100644 --- a/src/ui/gui/widgets.c +++ b/src/ui/gui/widgets.c @@ -22,6 +22,7 @@ along with this program. If not, see . #include "widgets.h" +#include "gettext.h" #include "psppire-dialog.h" #include "psppire-selector.h" @@ -140,6 +141,62 @@ preregister_actions (void) } +static void +tx_string_to_double (const GValue *src, GValue *dest) +{ + const gchar *str = g_value_get_string (src); + gdouble dble = g_strtod (str, NULL); + g_value_set_double (dest, dble); +} + + +static void +tx_string_to_int (const GValue *src, GValue *dest) +{ + const gchar *str = g_value_get_string (src); + gint x = atoi (str); + g_value_set_int (dest, x); +} + +static void +enum_to_string (const GValue *src, GValue *dest) +{ + gint n = g_value_get_enum (src); + GType t = G_VALUE_TYPE (src); + GEnumClass *ec = g_type_class_ref (t); + GEnumValue *ev = g_enum_get_value (ec, n); + + g_value_set_string (dest, gettext (ev->value_nick)); +} + + + +GType align_enum_type; +GType measure_enum_type; +GType role_enum_type; + + +extern const GEnumValue align[]; +extern const GEnumValue measure[]; +extern const GEnumValue role[]; + + + +static void +preregister_misc (void) +{ + align_enum_type = g_enum_register_static ("PsppAlignment", align); + measure_enum_type = g_enum_register_static ("PsppMeasure", measure); + role_enum_type = g_enum_register_static ("PsppRole", role); + + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_DOUBLE, tx_string_to_double); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, tx_string_to_int); + + g_value_register_transform_func (measure_enum_type, G_TYPE_STRING, enum_to_string); + g_value_register_transform_func (align_enum_type, G_TYPE_STRING, enum_to_string); + g_value_register_transform_func (role_enum_type, G_TYPE_STRING, enum_to_string); +} + /* Any custom widgets which are to be used in GtkBuilder ui files need to be preregistered, otherwise GtkBuilder refuses to @@ -160,6 +217,7 @@ preregister_widgets (void) psppire_means_layer_get_type (); preregister_actions (); + preregister_misc (); /* This seems to be necessary on Cygwin. It ought not to be necessary. Having it here can't do any harm. */