From: John Darrington Date: Tue, 7 Apr 2009 11:30:23 +0000 (+0800) Subject: Merge commit 'origin/stable' X-Git-Tag: v0.7.3~176^2 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp-builds.git;a=commitdiff_plain;h=7fbfc32fc3c636959b0a25b3e76609f86519e84a;hp=da299bd9871b178336a440c6ac53aebc3cea672e Merge commit 'origin/stable' Conflicts: src/language/stats/crosstabs.q src/language/stats/examine.q src/language/stats/frequencies.q src/language/stats/oneway.q tests/command/examine-extremes.sh tests/command/examine.sh --- diff --git a/.gitignore b/.gitignore index c29aff49..b50c6a37 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,11 @@ reloc-ldflags stamp-h1 texinfo.tex gitlog-to-changelog +*~ +*.o +*.lo +*.a +*.dirstamp +*.deps +*.la +*.libs diff --git a/AUTHORS b/AUTHORS index 12b25ab7..af7e9075 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,6 +14,7 @@ to other modules. including lib/gslextras and the linear regression features. Jason is also an important contributor to GSL, which is used by PSPP. + We also thank past contributors: * John Williams wrote an initial draft of the T-TEST procedure. @@ -21,3 +22,8 @@ We also thank past contributors: * Michael Kiefte contributed bug fixes and other enhancements. * Patrick Kobly contributed bug fixes and other enhancements. + +* Rob van Son wrote the original version of the routine for + calculation of the significance of the Wilcoxon matched pairs signed + rank statistic used by the NPAR TEST command. + diff --git a/INSTALL b/INSTALL index ea43342e..d3a469cf 100644 --- a/INSTALL +++ b/INSTALL @@ -50,9 +50,6 @@ use the GUI, you must run `configure' with --without-gui. * GTK+ (http://www.gtk.org/), version 2.12.0 or later. - * libglade (http://www.jamesh.id.au/software/libglade/), version - 2.6 or later. - Installing the following packages will allow your PSPP binary to read Gnumeric files. @@ -112,6 +109,10 @@ release. available. The most common of these are listed under "Optional Features", below. + It is best to build and install PSPP in directories whose names do + not contain unusual characters such as spaces or single-quotes, due + to limitations of the tools involved in the build process. + If you installed some of the libraries that PSPP uses in a non-standard location (on many systems, anywhere other than /usr), you may need to provide some special flags to `configure' @@ -148,8 +149,9 @@ release. 4. Type `make install' to install the programs and any data files and documentation. Ordinarily you will need root permissions to - do this; if you cannot get root permissions, see "Installation - Names", below. + do this. The "su" and "sudo" commands are common ways to obtain + root permissions. If you cannot get root permissions, see + "Installation Names", below. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the @@ -232,7 +234,12 @@ Optional Features of libraries are detected. Use of this option is not recommended. If you use it, some features may be missing and the build may fail with obscure error messages. - + +`--enable-relocatable' + This option is useful for building a package which can be installed + into an arbitrary directory and freely copied to any other directory. + If you use this option, you will probably want to install the pspp + with a command similar to "make install DESTDIR=". Defining Variables ================== diff --git a/Makefile.am b/Makefile.am index ab32f996..f606cbf1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,13 +32,15 @@ EXTRA_DIST = OChangeLog ONEWS config.rpath pspp-mode.el CLEANFILES = ACLOCAL_AMFLAGS = -I m4 -I gl/m4 noinst_LIBRARIES= +noinst_LTLIBRARIES= noinst_PROGRAMS= check_PROGRAMS= bin_PROGRAMS= DIST_HOOKS = +INSTALL_DATA_HOOKS = +UNINSTALL_DATA_HOOKS = PHONY = -DIST_HOOKS += generate-changelog generate-changelog: if test -d $(top_srcdir)/.git; then \ $(top_srcdir)/gitlog-to-changelog --since=2008-07-27 \ @@ -47,6 +49,9 @@ generate-changelog: mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi +DIST_HOOKS += generate-changelog + + include $(top_srcdir)/lib/automake.mk include $(top_srcdir)/doc/automake.mk include $(top_srcdir)/config/automake.mk @@ -59,6 +64,19 @@ if WITH_GUI_TOOLS include $(top_srcdir)/glade/automake.mk endif -PHONY += $(DIST_HOOKS) -dist-hook: $(DIST_HOOKS) +if WITH_PERL_MODULE +include $(top_srcdir)/perl-module/automake.mk +endif + +PHONY += $(DIST_HOOKS) $(INSTALL_DATA_HOOKS) $(UNINSTALL_DATA_HOOKS) + .PHONY: $(PHONY) + +dist-hook: $(DIST_HOOKS) + +install-data-hook: $(INSTALL_DATA_HOOKS) + +uninstall-hook: $(UNINSTALL_DATA_HOOKS) + + + diff --git a/NEWS b/NEWS index bff9a787..a83f8310 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,27 @@ PSPP NEWS -- history of user-visible changes. -Time-stamp: <2008-10-09 21:32:07 blp> -Copyright (C) 1996-9, 2000, 2008 Free Software Foundation, Inc. +Time-stamp: <2009-02-05 20:31:51 blp> +Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc. See the end for copying conditions. Please send PSPP bug reports to bug-gnu-pspp@gnu.org. +Changes from 0.7.1 to 0.7.2: + + * Updated Perl module interface. + +Changes from 0.7.0 to 0.7.1: + + * Added a perl module to facilitate reading and writing of pspp system + files from perl programs. + +Changes from 0.6.1 to 0.7.0: + + * Custom variable and data file attributes are now supported. + Commands VARIABLE ATTRIBUTE and DATAFILE ATTRIBUTE have been added + for setting and clear attributes. Support for attributes has also + been added to commands that read and write system files, such as + SAVE and GET, as well as to the DISPLAY command. + Changes from 0.6.0 to 0.6.1: * Statistical bug fixes: diff --git a/Smake b/Smake index 5a76b3c1..4c8d83bc 100644 --- a/Smake +++ b/Smake @@ -5,6 +5,7 @@ GNULIB = ../gnulib GNULIB_TOOL = $(GNULIB)/gnulib-tool GNULIB_MODULES = \ + argp \ assert \ byteswap \ c-ctype \ @@ -112,6 +113,7 @@ gettextize: po/POTFILES.in: for f in `find src \( -name \*.[qc] -o -name \*.glade \) ! -name .\* -print` ; do \ if test $$f = src/libpspp/version.c; then continue; fi; \ + if test $$f = src/ui/gui/psppire-marshal.c; then continue; fi; \ if test -e `dirname $$f`/`basename $$f .c`.q ; then continue; fi; \ echo $$f ; \ done | sort | uniq > $@.tmp diff --git a/configure.ac b/configure.ac index 3284ec81..7e7a4c60 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. dnl Initialize. AC_PREREQ(2.60) -AC_INIT([pspp],[0.6.1],[bug-gnu-pspp@gnu.org]) +AC_INIT([pspp],[0.7.2],[bug-gnu-pspp@gnu.org]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE @@ -15,6 +15,7 @@ AC_LIBTOOL_WIN32_DLL AC_LIBTOOL_DLOPEN AC_PROG_LIBTOOL PKG_PROG_PKG_CONFIG +m4_pattern_forbid([PKG_CHECK_MODULES]) AC_ARG_ENABLE( anachronistic-dependencies, @@ -49,15 +50,10 @@ AC_ARG_WITH( [AS_HELP_STRING([--without-gui], [don't build the PSPPIRE gui])]) required_gtk_version=2.12 -if test x"$enable_anachronistic_dependencies" = x"yes" ; then - required_gtk_version=2.8.20 -fi if test x"$with_gui" != x"no" ; then PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $required_gtk_version,, [PSPP_REQUIRED_PREREQ([gtk+ 2.0 v$required_gtk_version or later (or use --without-gui)])]) - PKG_CHECK_MODULES(GLADE, libglade-2.0 >= 2.6.0,, - [PSPP_REQUIRED_PREREQ([libglade 2.0 v2.6.0 or later (or use --without-gui)])]) fi AM_CONDITIONAL(WITHGUI, test x"$with_gui" != x"no") @@ -125,12 +121,14 @@ AM_CONDITIONAL(GNM_SUPPORT, test x"$gnm_support" = x"yes") AC_ARG_WITH( gui_tools, - [AS_HELP_STRING([--with-gui-tools], [build the gui developer tools])]) + [AS_HELP_STRING([--with-gui-tools], [build the gui developer tools. For DEVELOPERS only! There is no reason why users will need this flag.])]) if test x"$with_gui_tools" = x"yes" ; then PKG_CHECK_MODULES(GLADE_UI, gladeui-1.0) fi AM_CONDITIONAL(WITH_GUI_TOOLS, test x"$with_gui_tools" = x"yes") +AM_CONDITIONAL(WITH_PERL_MODULE, test x"$cross_compiling" != x"yes") + AC_SEARCH_LIBS([cblas_dsdot], [gslcblas],,[PSPP_REQUIRED_PREREQ([libgslcblas])]) AC_SEARCH_LIBS([gsl_cdf_binomial_P], [gsl],,[PSPP_REQUIRED_PREREQ([libgsl (version 1.8 or later)])]) PSPP_GSL_NEEDS_FGNU89_INLINE diff --git a/doc/automake.mk b/doc/automake.mk index 3445379a..67c2d83c 100644 --- a/doc/automake.mk +++ b/doc/automake.mk @@ -11,6 +11,7 @@ doc_pspp_TEXINFOS = doc/version.texi \ doc/data-selection.texi \ doc/expressions.texi \ doc/files.texi \ + doc/combining.texi \ doc/flow-control.texi \ doc/function-index.texi \ doc/installing.texi \ @@ -32,6 +33,7 @@ doc_pspp_dev_TEXINFOS = doc/version-dev.texi \ doc/dev/concepts.texi \ doc/dev/syntax.texi \ doc/dev/data.texi \ + doc/dev/i18n.texi \ doc/dev/output.texi \ doc/dev/system-file-format.texi \ doc/dev/portable-file-format.texi \ @@ -44,22 +46,14 @@ doc/ni.texi: $(top_srcdir)/src/language/command.def doc/get-commands.pl @$(MKDIR_P) doc @PERL@ $(top_srcdir)/doc/get-commands.pl $(top_srcdir)/src/language/command.def > $@ -# It seems that recent versions of yelp, upon which the gui relies to display the reference -# manual, are broken. It only works on compressed info files. So we must compress them. -if WITHGUI -YELP_CHECK = yelp-check -else -YELP_CHECK = -endif -install-data-hook:: $(YELP_CHECK) - for ifile in $(DESTDIR)$(infodir)/pspp.info-[0-9] \ - $(DESTDIR)$(infodir)/pspp.info ; do \ - gzip -f $$ifile ; \ - done +doc/pspp.xml: doc/pspp.texinfo $(doc_pspp_TEXINFOS) + @$(MKDIR_P) doc + $(MAKEINFO) --docbook -I $(top_srcdir) $< -o $@ + $(SED) -i -e 's/Time-&-Date/Time-\&-Date/g' $@ + +docbookdir = $(docdir) +docbook_DATA = doc/pspp.xml -uninstall-hook:: - rm -f $(DESTDIR)$(infodir)/pspp.info-[0-9].gz - rm -f $(DESTDIR)$(infodir)/pspp.info.gz EXTRA_DIST += doc/OChangeLog -CLEANFILES += pspp-dev.dvi +CLEANFILES += pspp-dev.dvi $(docbook_DATA) diff --git a/doc/bugs.texi b/doc/bugs.texi index 86208d92..e597a82c 100644 --- a/doc/bugs.texi +++ b/doc/bugs.texi @@ -8,15 +8,8 @@ visit PSPP's project webpage at @uref{https://savannah.gnu.org/projects/pspp}. You can also submit your own bug report there: click on ``Bugs,'' then on ``Submit a Bug,'' and fill out the form. Alternatively, PSPP bug reports may be -sent by email to -@ifinfo -. -@end ifinfo -@iftex -@code{}. -@end iftex +sent by email to @email{bug-gnu-pspp@@gnu.org}. For known bugs in individual language features, see the documentation for that feature. -@setfilename ignored diff --git a/doc/combining.texi b/doc/combining.texi new file mode 100644 index 00000000..a77c3b07 --- /dev/null +++ b/doc/combining.texi @@ -0,0 +1,336 @@ +@node Combining Data Files +@chapter Combining Data Files + +This chapter describes commands that allow data from system files, +portable file, scratch files, and the active file to be combined to +form a new active file. These commands can combine data files in the +following ways: + +@itemize +@item +@cmd{ADD FILES} interleaves or appends the cases from each input file. +It is used with input files that have variables in common, but +distinct sets of cases. + +@item +@cmd{MATCH FILES} adds the data together in cases that match across +multiple input files. It is used with input files that have cases in +common, but different information about each case. + +@item +@cmd{UPDATE} updates a master data file from data in a set of +transaction files. Each case in a transaction data file modifies a +matching case in the primary data file, or it adds a new case if no +matching case can be found. +@end itemize + +These commands share the majority of their syntax, which is described +in the following section, followed by one section for each command +that describes its specific syntax and semantics. + +@menu +* Combining Files Common Syntax:: +* ADD FILES:: Interleave cases from multiple files. +* MATCH FILES:: Merge cases from multiple files. +* UPDATE:: Update cases using transactional data. +@end menu + +@node Combining Files Common Syntax +@section Common Syntax + +@display +Per input file: + /FILE=@{*,'file-name'@} + [/RENAME=(src_names=target_names)@dots{}] + [/IN=var_name] + [/SORT] + +Once per command: + /BY var_list[(@{D|A@})] [var_list[(@{D|A@}]]@dots{} + [/DROP=var_list] + [/KEEP=var_list] + [/FIRST=var_name] + [/LAST=var_name] + [/MAP] +@end display + +This section describes the syntactical features in common among the +@cmd{ADD FILES}, @cmd{MATCH FILES}, and @cmd{UPDATE} commands. The +following sections describe details specific to each command. + +Each of these commands reads two or more input files and combines +them. The command's output becomes the new active file. The input +files are not changed on disk. + +The syntax of each command begins with a specification of the files to +be read as input. For each input file, specify FILE with a system, +portable, or scratch file's name as a string or a file handle +(@pxref{File Handles}), or specify an asterisk (@samp{*}) to use the +active file as input. Use of portable or scratch files on FILE is a +PSPP extension. + +At least two FILE subcommands must be specified. If the active file +is used as an input source, then @cmd{TEMPORARY} must not be in +effect. + +Each FILE subcommand may be followed by any number of RENAME +subcommands that specify a parenthesized group or groups of variable +names as they appear in the input file, followed by those variables' +new names, separated by an equals sign (@samp{=}), +e.g. @samp{/RENAME=(OLD1=NEW1)(OLD2=NEW2)}. To rename a single +variable, the parentheses may be omitted: @samp{/RENAME=OLD=NEW}. +Within a parenthesized group, variables are renamed simultaneously, so +that @samp{/RENAME=(A B=B A)} exchanges the names of variables A and +B. Otherwise, renaming occurs in left-to-right order. + +Each FILE subcommand may optionally be followed by a single IN +subcommand, which creates a numeric variable with the specified name +and format F1.0. The IN variable takes value 1 in an output case if +the given input file contributed to that output case, and 0 otherwise. +The DROP, KEEP, and RENAME subcommands have no effect on IN variables. + +If BY is used (see below), the SORT keyword must be specified after a +FILE if that input file is not already sorted on the BY variables. +When SORT is specified, PSPP sorts the input file's data on the BY +variables before it applies it to the command. When SORT is used, BY +is required. SORT is a PSPP extension. + +PSPP merges the dictionaries of all of the input files to form the new +active file dictionary, like so: + +@itemize @bullet +@item +The new active file's variables are the union of all the input files' +variables, matched based on their name. When a single input file +contains a variable with a given name, the output file will contain +exactly that variable. When more than one input file contains a +variable with a given name, those variables must all have the same +type (numeric or string) and, for string variables, the same width. +Variables are matched after renaming with the RENAME subcommand. +Thus, RENAME can be used to resolve conflicts. + +@item +The variable label for each output variable is taken from the first +specified input file that has a variable label for that variable, and +similarly for value labels and missing values. + +@item +The new active file's file label (@pxref{FILE LABEL}) is that of the +first specified FILE that has a file label. + +@item +The new active file's documents (@pxref{DOCUMENT}) are the +concatenation of all the input files' documents, in the order in which +the FILE subcommands are specified. + +@item +If all of the input files are weighted on the same variable, then the +new active file is weighted on that variable. Otherwise, the new +active file is not weighted. +@end itemize + +The remaining subcommands apply to the output file as a whole, rather +than to individual input files. They must be specified at the end of +the command specification, following all of the FILE and related +subcommands. The most important of these subcommands is BY, which +specifies a set of one or more variables that may be used to find +corresponding cases in each of the input files. The variables +specified on BY must be present in all of the input files. +Furthermore, if any of the input files are not sorted on the BY +variables, then SORT must be specified for those input files. + +The variables listed on BY may include (A) or (D) annotations to +specify ascending or descending sort order. @xref{SORT CASES}, for +more details on this notation. Adding (A) or (D) to the BY subcommand +specification is a PSPP extension. + +The DROP subcommand can be used to specify a list of variables to +exclude from the output. By contrast, the KEEP subcommand can be used +to specify variables to include in the output; all variables not +listed are dropped. DROP and KEEP are executed in left-to-right order +and may be repeated any number of times. DROP and KEEP do not affect +variables created by the IN, FIRST, and LAST subcommands, which are +always included in the new active file, but they can be used to drop +BY variables. + +The FIRST and LAST subcommands are optional. They may only be +specified on @cmd{MATCH FILES} and @cmd{ADD FILES}, and only when BY +is used. FIRST and LIST each adds a numeric variable to the new +active file, with the name given as the subcommand's argument and F1.0 +print and write formats. The value of the FIRST variable is 1 in the +first output case with a given set of values for the BY variables, and +0 in other cases. Similarly, the LAST variable is 1 in the last case +with a given of BY values, and 0 in other cases. + +When any of these commands creates an output case, variables that are +only in files that are not present for the current case are set to the +system-missing value for numeric variables or spaces for string +variables. + +@node ADD FILES +@section ADD FILES +@vindex ADD FILES + +@display +ADD FILES + +Per input file: + /FILE=@{*,'file-name'@} + [/RENAME=(src_names=target_names)@dots{}] + [/IN=var_name] + [/SORT] + +Once per command: + [/BY var_list[(@{D|A@})] [var_list[(@{D|A@})]@dots{}]] + [/DROP=var_list] + [/KEEP=var_list] + [/FIRST=var_name] + [/LAST=var_name] + [/MAP] +@end display + +@cmd{ADD FILES} adds cases from multiple input files. The output, +which replaces the active file, consists all of the cases in all of +the input files. + +ADD FILES shares the bulk of its syntax with other PSPP commands for +combining multiple data files. @xref{Combining Files Common Syntax}, +above, for an explanation of this common syntax. + +When BY is not used, the output of ADD FILES consists of all the cases +from the first input file specified, followed by all the cases from +the second file specified, and so on. When BY is used, the output is +additionally sorted on the BY variables. + +When ADD FILES creates an output case, variables that are not part of +the input file from which the case was drawn are set to the +system-missing value for numeric variables or spaces for string +variables. + +@node MATCH FILES +@section MATCH FILES +@vindex MATCH FILES + +@display +MATCH FILES + +Per input file: + /@{FILE,TABLE@}=@{*,'file-name'@} + [/RENAME=(src_names=target_names)@dots{}] + [/IN=var_name] + [/SORT] + +Once per command: + /BY var_list[(@{D|A@}] [var_list[(@{D|A@})]@dots{}] + [/DROP=var_list] + [/KEEP=var_list] + [/FIRST=var_name] + [/LAST=var_name] + [/MAP] +@end display + +@cmd{MATCH FILES} merges sets of corresponding cases in multiple +input files into single cases in the output, combining their data. + +MATCH FILES shares the bulk of its syntax with other PSPP commands for +combining multiple data files. @xref{Combining Files Common Syntax}, +above, for an explanation of this common syntax. + +How MATCH FILES matches up cases from the input files depends on +whether BY is specified: + +@itemize @bullet +@item +If BY is not used, MATCH FILES combines the first case from each input +file to produce the first output case, then the second case from each +input file for the second output case, and so on. If some input files +have fewer cases than others, then the shorter files do not contribute +to cases output after their input has been exhausted. + +@item +If BY is used, MATCH FILES combines cases from each input file that +have identical values for the BY variables. + +When BY is used, TABLE subcommands may be used to introduce @dfn{table +lookup file}. TABLE has same syntax as FILE, and the RENAME, IN, and +SORT subcommands may follow a TABLE in the same way as a FILE. +Regardless of the number of TABLEs, at least one FILE must specified. +Table lookup files are treated in the same way as other input files +for most purposes and, in particular, table lookup files must be +sorted on the BY variables or the SORT subcommand must be specified +for that TABLE. + +Cases in table lookup files are not consumed after they have been used +once. This means that data in table lookup files can correspond to +any number of cases in FILE input files. Table lookup files are +analogous to lookup tables in traditional relational database systems. + +If a table lookup file contains more than one case with a given set of +BY variables, only the first case is used. +@end itemize + +When MATCH FILES creates an output case, variables that are only in +files that are not present for the current case are set to the +system-missing value for numeric variables or spaces for string +variables. + +@node UPDATE +@section UPDATE +@vindex UPDATE + +@display +UPDATE + +Per input file: + /FILE=@{*,'file-name'@} + [/RENAME=(src_names=target_names)@dots{}] + [/IN=var_name] + [/SORT] + +Once per command: + /BY var_list[(@{D|A@})] [var_list[(@{D|A@})]]@dots{} + [/DROP=var_list] + [/KEEP=var_list] + [/MAP] +@end display + +@cmd{UPDATE} updates a @dfn{master file} by applying modifications +from one or more @dfn{transaction files}. + +UPDATE shares the bulk of its syntax with other PSPP commands for +combining multiple data files. @xref{Combining Files Common Syntax}, +above, for an explanation of this common syntax. + +At least two FILE subcommands must be specified. The first FILE +subcommand names the master file, and the rest name transaction files. +Every input file must either be sorted on the variables named on the +BY subcommand, or the SORT subcommand must be used just after the FILE +subcommand for that input file. + +UPDATE uses the variables specified on the BY subcommand, which is +required, to attempt to match each case in a transaction file with a +case in the master file: + +@itemize @bullet +@item +When a match is found, then the values of the variables present in the +transaction file replace those variable's values in the new active +file. If there are matching cases in more than more transaction file, +PSPP applies the replacements from the first transaction file, then +from the second transaction file, and so on. Similarly, if a single +transaction file has cases with duplicate BY values, then those are +applied in order to the master file. + +When a variable in a transaction file has a missing value or a string +variable's value is all blanks, that value is never used to update the +master file. + +@item +If a case in the master file has no matching case in any transaction +file, then it is copied unchanged to the output. + +@item +If a case in a transaction file has no matching case in the master +file, then it causes a new case to be added to the output, initialized +from the values in the transaction file. +@end itemize diff --git a/doc/command-index.texi b/doc/command-index.texi index c53b1db6..d26f9074 100644 --- a/doc/command-index.texi +++ b/doc/command-index.texi @@ -1,4 +1,3 @@ @node Command Index @chapter Command Index @printindex vr -@setfilename ignored diff --git a/doc/concept-index.texi b/doc/concept-index.texi index 742488e4..29d8a624 100644 --- a/doc/concept-index.texi +++ b/doc/concept-index.texi @@ -1,4 +1,3 @@ @node Concept Index @chapter Concept Index @printindex cp -@setfilename ignored diff --git a/doc/data-io.texi b/doc/data-io.texi index b6a3a6d2..0b9583b0 100644 --- a/doc/data-io.texi +++ b/doc/data-io.texi @@ -25,6 +25,7 @@ actually be read until a procedure is executed. @menu * BEGIN DATA:: Embed data within a syntax file. * CLOSE FILE HANDLE:: Close a file handle. +* DATAFILE ATTRIBUTE:: Set custom attributes on data files. * DATA LIST:: Fundamental data reading command. * END CASE:: Output the current case. * END FILE:: Terminate the current input program. @@ -89,6 +90,52 @@ DATA} and @cmd{END DATA}, cannot be closed. Attempts to close it with @cmd{CLOSE FILE HANDLE} is a PSPP extension. +@node DATAFILE ATTRIBUTE +@section DATAFILE ATTRIBUTE +@vindex DATAFILE ATTRIBUTE + +@display +DATAFILE ATTRIBUTE + ATTRIBUTE=name('value') [name('value')]@dots{} + ATTRIBUTE=name@b{[}index@b{]}('value') [name@b{[}index@b{]}('value')]@dots{} + DELETE=name [name]@dots{} + DELETE=name@b{[}index@b{]} [name@b{[}index@b{]}]@dots{} +@end display + +@cmd{DATAFILE ATTRIBUTE} adds, modifies, or removes user-defined +attributes associated with the active file. Custom data file +attributes are not interpreted by PSPP, but they are saved as part of +system files and may be used by other software that reads them. + +Use the ATTRIBUTE subcommand to add or modify a custom data file +attribute. Specify the name of the attribute as an identifier +(@pxref{Tokens}), followed by the desired value, in parentheses, as a +quoted string. Attribute names that begin with @code{$} are reserved +for PSPP's internal use, and attribute names that begin with @code{@@} +or @code{$@@} are not displayed by most PSPP commands that display +other attributes. Other attribute names are not treated specially. + +Attributes may also be organized into arrays. To assign to an array +element, add an integer array index enclosed in square brackets +(@code{[} and @code{]}) between the attribute name and value. Array +indexes start at 1, not 0. An attribute array that has a single +element (number 1) is not distinguished from a non-array attribute. + +Use the DELETE subcommand to delete an attribute. Specify an +attribute name by itself to delete an entire attribute, including all +array elements for attribute arrays. Specify an attribute name +followed by an array index in square brackets to delete a single +element of an attribute array. In the latter case, all the array +elements numbered higher than the deleted element are shifted down, +filling the vacated position. + +To associate custom attributes with particular variables, instead of +with the entire active file, use @cmd{VARIABLE ATTRIBUTE} (@pxref{VARIABLE ATTRIBUTE}) instead. + +@cmd{DATAFILE ATTRIBUTE} takes effect immediately. It is not affected +by conditional and looping structures such as @cmd{DO IF} or +@cmd{LOOP}. + @node DATA LIST @section DATA LIST @vindex DATA LIST @@ -1095,4 +1142,3 @@ specified output format, whereas @cmd{WRITE} outputs the system-missing value as a field filled with spaces. Binary formats are an exception. @end itemize -@setfilename ignored diff --git a/doc/data-selection.texi b/doc/data-selection.texi index e7a12853..04269f32 100644 --- a/doc/data-selection.texi +++ b/doc/data-selection.texi @@ -264,4 +264,3 @@ the next procedure (@pxref{TEMPORARY}). @cmd{WEIGHT} does not cause cases in the active file to be replicated in memory. -@setfilename ignored diff --git a/doc/dev/i18n.texi b/doc/dev/i18n.texi new file mode 100644 index 00000000..836ff810 --- /dev/null +++ b/doc/dev/i18n.texi @@ -0,0 +1,134 @@ +@node Internationalisation +@chapter Internationalisation + +Internationalisation in pspp is complicated. +The most annoying aspect is that of character-encoding. +Currently, pspp does not fully deal with the issues. +This chapter attempts to describe the problems and current ways +in which they are addressed. + + +@section The working locales +Pspp has three ``working'' locales: + +@itemize +@item The local of the user interface. +@item The local of the output. +@item The local of the data. +@end itemize + +Each of these locales may, at different times take +separate (or identical) values. +So for example, a French statistician can use pspp to prepare a report +in the English language, using +a datafile which has been created by a Japanese researcher hence +uses a Japanese character set. + +It's rarely, if ever, necessary to interrogate the system to find out +the values of the 3 locales. +However it's important to be aware of the source (destination) locale +when reading (writing) string data. +When transfering data between a source and a destination, the appropriate +recoding must be performed. + + +@subsection The user interface locale +This is the locale which is visible to the person using pspp. +Error messages and confidence indications are written in this locale. +For example ``Cannot open file'' will be written in the user interface locale. + +This locale is set from the environment of the user who starts pspp@{ire@} or +from the system locale if not set. + +@subsection The output locale +This locale is the one that should be visible to the person reading a +report generated by pspp. Non-data related strings (Eg: ``Page number'', +``Standard Deviation'' etc.) will appear in this locale. + +@subsection The data locale +This locale is the one associated with the data being analysed with pspp. +The only important aspect of this locale is the character encoding. +@footnote {It might also be desirable for the LC_COLLATE category to be used for the purposes of sorting data.} +Any string data stored in a @union{value} will be encoded in the character set +of the data locale. + +The data locale defaults to the locale of the user who starts pspp@{ire@}. +Spss has a @cmd{SET LOCALE} command (not currently supported in pspp) which +can be used to specify the character encoding of the data locale. + + +@section System files +@file{*.sav} files contain a field which is supposed to identify the encoding +of the data they contain (@pxref{Machine Integer Info Record}). +This field is currently unused by Pspp. +Probably, would be appropriate to set the data locale from this field when +reading a new data file, and set it back to the default value +upon a @cmd{NEW FILE} command. +However, many +files produced by early versions of spss set this to ``2'' (ASCII) regardless +of the encoding of the data. + + +@section GUI +The psppire graphic user interface is written using the Gtk+ api, for which +all strings must be encoded in UTF8. +All strings passed to the Gtk+/Glib library functions must be UTF-8 encoded +otherwise errors will occur. +Thus, for the purposes of the programming psppire, the user interface locale +should be assumed to be UTF8, even if setlocale and/or nl_langinfo +indicates otherwise. + + +@section Existing locale handling functions +The major aspect of locale handling which the programmer has to consider is +that of character encoding. + +The following function is used to recode strings: + +@deftypefun char * recode_string (enum conv_id @var{how}, const char *@var{text}, int @var{len}); +Converts the string @var{text} to a new encoding according to @var{how}. +@var{How} can (currently) take the values @code{CONV_PSPP_TO_UTF8}, @code{CONV_SYSTEM_TO_PSPP} or @code{CONV_UTF8_TO_PSPP} @footnote{The label ``_PSPP'' ought to be changed to ``_DATA''}. +If @var{len} is not -1, then it must be the number of bytes in @var{text}. +It is the caller's responsibility to free the returned string when no +longer required. +@end deftypefun + + +For example, in order to display a string variable's value in a label widget in the psppire gui one would use code similar to +@example + +struct variable *var = /* assigned from somewhere */ +struct case c = /* from somewhere else */ + +const union value *val = case_data (&c, var); + +char *utf8string = recode_string (CONV_PSPP_TO_UTF8, val->s, + var_get_width (var)); + +GtkWidget *entry = gtk_entry_new(); +gtk_entry_set_text (entry, utf8string); +gtk_widget_show (entry); + +free (utf8string); + +@end example + + + +@section Quirks +For historical reasons, not all locale handling follows posix conventions. +This makes it difficult (impossible?) to elegantly handle the issues. +For example, it would make sense for the gui's datasheet to display +numbers formatted according to the LC_NUMERIC category of the data locale. +Instead however there is the @func{data_out} function +(@pxref{Obtaining Properties of Format Types}) which uses the +@func{settings_get_decimal_char} function instead of the decimal separator +of the locale. Similarly, formatting of monetary values is displayed +in a pspp/spss specific fashion instead of using the LC_MONETARY category. + + + +@c LocalWords: pspp itemize Eg LC Spss cmd sav pxref spss GUI psppire Gtk api +@c LocalWords: UTF gtk setlocale nl langinfo deftypefun enum conv var const +@c LocalWords: int len gui struct val utf GtkWidget posix gui's datasheet +@c LocalWords: func diff --git a/doc/dev/system-file-format.texi b/doc/dev/system-file-format.texi index 70fa385c..3e764c8c 100644 --- a/doc/dev/system-file-format.texi +++ b/doc/dev/system-file-format.texi @@ -96,6 +96,7 @@ Each type of record is described separately below. * Variable Display Parameter Record:: * Long Variable Names Record:: * Very Long String Record:: +* Data File and Variable Attributes Records:: * Miscellaneous Informational Records:: * Dictionary Termination Record:: * Data Record:: @@ -791,6 +792,85 @@ After the last tuple, there may be a single byte 00, or @{00, 09@}. The total length is @code{count} bytes. @end table +@node Data File and Variable Attributes Records +@section Data File and Variable Attributes Records + +The data file and variable attributes records represent custom +attributes for the system file or for individual variables in the +system file, as defined on the DATAFILE ATTRIBUTE (@pxref{DATAFILE +ATTRIBUTE,,,pspp, PSPP Users Guide}) and VARIABLE ATTRIBUTE commands +(@pxref{VARIABLE ATTRIBUTE,,,pspp, PSPP Users Guide}), respectively. + +@example +/* @r{Header.} */ +int32 rec_type; +int32 subtype; +int32 size; +int32 count; + +/* @r{Exactly @code{count} bytes of data.} */ +char attributes[]; +@end example + +@table @code +@item int32 rec_type; +Record type. Always set to 7. + +@item int32 subtype; +Record subtype. Always set to 17 for a data file attribute record or +to 18 for a variable attributes record. + +@item int32 size; +The size of each element in the @code{attributes} member. Always set to 1. + +@item int32 count; +The total number of bytes in @code{attributes}. + +@item char attributes[]; +The attributes, in a text-based format. + +In record type 17, this field contains a single attribute set. An +attribute set is a sequence of one or more attributes concatenated +together. Each attribute consists of a name, which has the same +syntax as a variable name, followed by, inside parentheses, a sequence +of one or more values. Each value consists of a string enclosed in +single quotes (@code{'}) followed by a line feed (byte 0x0a). A value +may contain single quote characters, which are not themselves escaped +or quoted or required to be present in pairs. There is no apparent +way to embed a line feed in a value. There is no distinction between +an attribute with a single value and an attribute array with one +element. + +In record type 18, this field contains a sequence of one or more +variable attribute sets. If more than one variable attribute set is +present, each one after the first is delimited from the previous by +@code{/}. Each variable attribute set consists of a variable name, +followed by @code{:}, followed by an attribute set with the same +syntax as on record type 17. + +The total length is @code{count} bytes. +@end table + +@subheading Example + +A system file produced with the following VARIABLE ATTRIBUTE commands +in effect: + +@example +VARIABLE ATTRIBUTE VARIABLES=dummy ATTRIBUTE=fred[1]('23') fred[2]('34'). +VARIABLE ATTRIBUTE VARIABLES=dummy ATTRIBUTE=bert('123'). +@end example + +@noindent +will contain a variable attribute record with the following contents: + +@example +00000000 07 00 00 00 12 00 00 00 01 00 00 00 22 00 00 00 |............"...| +00000010 64 75 6d 6d 79 3a 66 72 65 64 28 27 32 33 27 0a |dummy:fred('23'.| +00000020 27 33 34 27 0a 29 62 65 72 74 28 27 31 32 33 27 |'34'.)bert('123'| +00000030 0a 29 |.) | +@end example + @node Miscellaneous Informational Records @section Miscellaneous Informational Records diff --git a/doc/expressions.texi b/doc/expressions.texi index 1021708a..32ace6c2 100644 --- a/doc/expressions.texi +++ b/doc/expressions.texi @@ -1516,4 +1516,3 @@ subtraction. @item @code{AND NOT OR} @end enumerate -@setfilename ignored diff --git a/doc/files.texi b/doc/files.texi index 30a023ae..13237490 100644 --- a/doc/files.texi +++ b/doc/files.texi @@ -1,5 +1,5 @@ -@node System and Portable Files -@chapter System Files and Portable Files +@node System and Portable File IO +@chapter System and Portable File I/O The commands in this chapter read, write, and examine system files and portable files. @@ -10,7 +10,6 @@ portable files. * GET:: Read from a system file. * GET DATA:: Read from foreign files. * IMPORT:: Read from a portable file. -* MATCH FILES:: Merge system files. * SAVE:: Write to a system file. * SYSFILE INFO:: Display system file dictionary. * XEXPORT:: Write to a portable file, as a transformation. @@ -39,25 +38,46 @@ Only variables with names that exist in both the active file and the system file are considered. Variables with the same name but different types (numeric, string) will cause an error message. Otherwise, the system file variables' attributes will replace those in their matching -active file variables, as described below. +active file variables: +@itemize @bullet +@item If a system file variable has a variable label, then it will replace the active file variable's variable label. If the system file variable does not have a variable label, then the active file variable's variable -label, if any, will be retained. +label, if any, will be retained. + +@item +If the system file variable has custom attributes (@pxref{VARIABLE +ATTRIBUTE}), then those attributes replace the active file variable's +custom attributes. If the system file variable does not have custom +attributes, then the active file variable's custom attributes, if any, +will be retained. +@item If the active file variable is numeric or short string, then value labels and missing values, if any, will be copied to the active file variable. If the system file variable does not have value labels or missing values, then those in the active file variable, if any, will not be disturbed. +@end itemize -Finally, weighting of the active file is updated (@pxref{WEIGHT}). If -the active file has a weighting variable, and the system file does not, -or if the weighting variable in the system file does not exist in the -active file, then the active file weighting variable, if any, is -retained. Otherwise, the weighting variable in the system file becomes -the active file weighting variable. +In addition to properties of variables, some properties of the active +file dictionary as a whole are updated: + +@itemize @bullet +@item +If the system file has custom attributes (@pxref{DATAFILE ATTRIBUTE}), +then those attributes replace the active file variable's custom +attributes. + +@item +If the active file has a weighting variable (@pxref{WEIGHT}), and the +system file does not, or if the weighting variable in the system file +does not exist in the active file, then the active file weighting +variable, if any, is retained. Otherwise, the weighting variable in +the system file becomes the active file weighting variable. +@end itemize @cmd{APPLY DICTIONARY} takes effect immediately. It does not read the active @@ -630,99 +650,6 @@ data is read later, when a procedure is executed. Use of @cmd{IMPORT} to read a system file or scratch file is a PSPP extension. -@node MATCH FILES -@section MATCH FILES -@vindex MATCH FILES - -@display -MATCH FILES - /@{FILE,TABLE@}=@{*,'file-name'@} - /RENAME=(src_names=target_names)@dots{} - /IN=var_name - - /BY=var_list - /DROP=var_list - /KEEP=var_list - /FIRST=var_name - /LAST=var_name - /MAP -@end display - -@cmd{MATCH FILES} merges one or more system, portable, or scratch files, -optionally -including the active file. Cases with the same values for BY -variables are combined into a single case. Cases with different -values are output in order. Thus, multiple sorted files are -combined into a single sorted file based on the value of the BY -variables. The results of the merge become the new active file. - -Specify FILE with a system, portable, or scratch file as a file name -string or file handle -(@pxref{File Handles}), or with an asterisk (@samp{*}) to -indicate the current active file. The files specified on FILE are -merged together based on the BY variables, or combined case-by-case if -BY is not specified. - -Specify TABLE with a file to use it as a @dfn{table -lookup file}. Cases in table lookup files are not used up after -they've been used once. This means that data in table lookup files can -correspond to any number of cases in FILE files. Table lookup files -correspond to lookup tables in traditional relational database systems. -If a table lookup file contains more than one case with a given set of -BY variables, only the first case is used. - -Any number of FILE and TABLE subcommands may be specified. -Ordinarily, at least two FILE subcommands, or one FILE and at least -one TABLE, should be specified. Each instance of FILE or TABLE can be -followed by any sequence of RENAME subcommands. These have the same -form and meaning as the corresponding subcommands of @cmd{GET} -(@pxref{GET}), but apply only to variables in the given file. - -Each FILE or TABLE may optionally be followed by an IN subcommand, -which creates a numeric variable with the specified name and format -F1.0. The IN variable takes value 1 in a case if the given file -contributed a row to the merged file, 0 otherwise. The DROP, KEEP, -and RENAME subcommands do not affect IN variables. - -When more than one FILE or TABLE contains a variable with a given -name, those variables must all have the same type (numeric or string) -and, for string variables, the same width. This rules applies to -variable names after renaming with RENAME; thus, RENAME can be used to -resolve conflicts. - -FILE and TABLE must be specified at the beginning of the command, with -any RENAME or IN specifications immediately after the corresponding -FILE or TABLE. These subcommands are followed by BY, DROP, KEEP, -FIRST, LAST, and MAP. - -The BY subcommand specifies a list of variables that are used to match -cases from each of the files. When TABLE or IN is used, BY is -required; otherwise, it is optional. When BY is specified, all the -files named on FILE and TABLE subcommands must be sorted in ascending -order of the BY variables. Variables belonging to files that are not -present for the current case are set to the system-missing value for -numeric variables or spaces for string variables. - -The DROP and KEEP subcommands allow variables to be dropped from or -reordered within the new active file. These subcommands have the same -form and meaning as the corresponding subcommands of @cmd{GET} -(@pxref{GET}). They apply to the new active file as a whole, not to -individual input files. The variable names specified on DROP and KEEP -are those after any renaming with RENAME. - -The optional FIRST and LAST subcommands name variables that @cmd{MATCH -FILES} adds to the active file. The new variables are numeric with -print and write format F1.0. The value of the FIRST variable is 1 in -the first case with a given set of values for the BY variables, and 0 -in other cases. Similarly, the LAST variable is 1 in the last case -with a given of BY values, and 0 in other cases. - -@cmd{MATCH FILES} may not be specified following @cmd{TEMPORARY} -(@pxref{TEMPORARY}) if the active file is used as an input source. - -Use of portable or scratch files on @cmd{MATCH FILES} is a PSPP -extension. - @node SAVE @section SAVE @vindex SAVE @@ -873,4 +800,3 @@ the data is read by a procedure or procedure-like command. @end itemize @xref{SAVE}, for more information. -@setfilename ignored diff --git a/doc/flow-control.texi b/doc/flow-control.texi index f501c65a..868143b3 100644 --- a/doc/flow-control.texi +++ b/doc/flow-control.texi @@ -170,4 +170,3 @@ variable as the loop index. When @cmd{LOOP} or @cmd{END LOOP} is specified following @cmd{TEMPORARY} (@pxref{TEMPORARY}), the @cmd{LAG} function may not be used (@pxref{LAG}). -@setfilename ignored diff --git a/doc/function-index.texi b/doc/function-index.texi index 2a63e136..a41e60de 100644 --- a/doc/function-index.texi +++ b/doc/function-index.texi @@ -1,4 +1,3 @@ @node Function Index @chapter Function Index @printindex fn -@setfilename ignored diff --git a/doc/installing.texi b/doc/installing.texi index fe5caa38..5a9c2791 100644 --- a/doc/installing.texi +++ b/doc/installing.texi @@ -56,4 +56,3 @@ but only if that directory already exists. (optional) Type @samp{make clean} to delete the PSPP binaries from the source tree. @end enumerate -@setfilename ignored diff --git a/doc/introduction.texi b/doc/introduction.texi index e87731b5..735a54c5 100644 --- a/doc/introduction.texi +++ b/doc/introduction.texi @@ -31,4 +31,3 @@ The author hopes to fully support all features in the products that PSPP replaces, eventually. The author welcomes questions, comments, donations, and code submissions. @xref{Bugs,,Submitting Bug Reports}, for instructions on contacting the author. -@setfilename ignored diff --git a/doc/invoking.texi b/doc/invoking.texi index b5f157e6..6b45392b 100644 --- a/doc/invoking.texi +++ b/doc/invoking.texi @@ -7,12 +7,12 @@ @cindex options, command-line @example pspp [ -B @var{dir} | --config-dir=@var{dir} ] [ -o @var{device} | --device=@var{device} ] - [ -d @var{var}[=@var{value}] | --define=@var{var}[=@var{value}] ] [-u @var{var} | --undef=@var{var} ] - [ -f @var{file} | --out-file=@var{file} ] [ -p | --pipe ] [ -I- | --no-include ] + [ -a @{compatible|enhanced@} | --algorithm=@{compatible|enhanced@}] + [ -x @{compatible|enhanced@} | --syntax=@{compatible|enhanced@}] + [ -I- | --no-include ] [ -I @var{dir} | --include=@var{dir} ] [ -i | --interactive ] - [ -n | --edit | --dry-run | --just-print | --recon ] [ -r | --no-statrc ] [ -h | --help ] [ -l | --list ] - [ -c @var{command} | --command @var{command} ] [ -s | --safer ] + [ -s | --safer ] [ --testing-mode ] [ -V | --version ] [ -v | --verbose ] [ @var{key}=@var{value} ] @var{file}@enddots{} @end example @@ -101,19 +101,6 @@ Input and output options affect how PSPP reads input and writes output. These are the input and output options: @table @code -@item -f @var{file} -@itemx --out-file=@var{file} - -This overrides the output file name for devices designated as listing -devices. If a file named @var{file} already exists, it is overwritten. - -@item -p -@itemx --pipe - -Allows PSPP to be used as a filter by causing the syntax file to be -read from stdin and output to be written to stdout. Conflicts with the -@code{-f @var{file}} and @code{--file=@var{file}} options. - @item -I- @itemx --no-include @@ -127,12 +114,6 @@ configuring}. Appends directory @var{dir} to the path that is searched for include files in PSPP syntax files. -@item -c @var{command} -@itemx --command=@var{command} - -Execute literal command @var{command}. The command is executed before -startup syntax files, if any. - @item --testing-mode Invoke heuristics to assist with testing PSPP. For use by @code{make @@ -158,16 +139,6 @@ mode, rather than the default batch mode. @xref{Tokenizing lines}, for information on the differences between batch mode and interactive mode command interpretation. -@item -n -@itemx --edit -@itemx --dry-run -@itemx --just-print -@itemx --recon - -Only the syntax of any syntax file specified or of commands entered at -the command line is checked. Transformations are not performed and -procedures are not executed. Not yet implemented. - @item -r @itemx --no-statrc @@ -253,4 +224,3 @@ Individual directories included in file searches. Each verbosity level also includes messages from lower verbosity levels. @end table -@setfilename ignored diff --git a/doc/language.texi b/doc/language.texi index 13454336..62267414 100644 --- a/doc/language.texi +++ b/doc/language.texi @@ -497,6 +497,11 @@ they are displayed. Example: a width of 8, with 2 decimal places. @item Write format Similar to print format, but used by the @cmd{WRITE} command (@pxref{WRITE}). + +@cindex custom attributes +@item Custom attributes +User-defined associations between names and values. @xref{VARIABLE +ATTRIBUTE}. @end table @node System Variables @@ -1167,6 +1172,7 @@ trailing white space. The maximum width for time and date formats is 40 columns. Minimum input and output width for each of the time and date formats is shown below: + @float @multitable {DATETIME} {Min. Input Width} {Min. Output Width} {4-digit year} @headitem Format @tab Min. Input Width @tab Min. Output Width @tab Option @@ -1488,4 +1494,3 @@ The first nonterminal defined in a set of productions is called the @dfn{start symbol}. The start symbol defines the entire syntax for that command. @end itemize -@setfilename ignored diff --git a/doc/not-implemented.texi b/doc/not-implemented.texi index 0dd77d49..42e02b7c 100644 --- a/doc/not-implemented.texi +++ b/doc/not-implemented.texi @@ -9,4 +9,3 @@ implemented. @include doc/ni.texi -@setfilename ignored diff --git a/doc/pspp-dev.texinfo b/doc/pspp-dev.texinfo index c2507412..4b92654d 100644 --- a/doc/pspp-dev.texinfo +++ b/doc/pspp-dev.texinfo @@ -79,6 +79,7 @@ modify this GNU manual.'' * Parsing Command Syntax:: How to parse command syntax. * Processing Data:: Data input, output, and processing. * Presenting Output:: Producing machine- and human-readable output. +* Internationalisation:: Dealing with locale issues. * Function Index:: Index of PSPP functions. * Concept Index:: Index of concepts. @@ -95,6 +96,7 @@ modify this GNU manual.'' @include dev/syntax.texi @include dev/data.texi @include dev/output.texi +@include dev/i18n.texi @include function-index.texi @include concept-index.texi diff --git a/doc/pspp.texinfo b/doc/pspp.texinfo index 6171df6d..975ee660 100644 --- a/doc/pspp.texinfo +++ b/doc/pspp.texinfo @@ -70,7 +70,8 @@ modify this GNU manual.'' * Expressions:: Numeric and string expression syntax. * Data Input and Output:: Reading data from user files. -* System and Portable Files:: Dealing with system & portable files. +* System and Portable File IO:: Reading and writing system & portable files. +* Combining Data Files:: Combining data from multiple files. * Variable Attributes:: Adjusting and examining variables. * Data Manipulation:: Simple operations on data. * Data Selection:: Select certain cases for analysis. @@ -98,6 +99,7 @@ modify this GNU manual.'' @include expressions.texi @include data-io.texi @include files.texi +@include combining.texi @include variables.texi @include transformation.texi @include data-selection.texi diff --git a/doc/regression.texi b/doc/regression.texi index 90caf866..b47416b7 100644 --- a/doc/regression.texi +++ b/doc/regression.texi @@ -110,4 +110,3 @@ list. regression /variables=v0 v1 v2 /statistics defaults /dependent=v2 /save pred resid /method=enter. @end example -@setfilename ignored diff --git a/doc/statistics.texi b/doc/statistics.texi index 8fa93b15..98556060 100644 --- a/doc/statistics.texi +++ b/doc/statistics.texi @@ -14,6 +14,7 @@ far. * ONEWAY:: One way analysis of variance. * RANK:: Compute rank scores. * REGRESSION:: Linear regression. +* RELIABILITY:: Reliability analysis. @end menu @node DESCRIPTIVES @@ -232,7 +233,7 @@ EXAMINE /PLOT=@{BOXPLOT, NPPLOT, HISTOGRAM, ALL, NONE@} /CINTERVAL n /COMPARE=@{GROUPS,VARIABLES@} - /ID=@{case_number, var_name@} + /ID=var_name /@{TOTAL,NOTOTAL@} /PERCENTILE=[value_list]=@{HAVERAGE, WAVERAGE, ROUND, AEMPIRICAL, EMPIRICAL @} /MISSING=@{LISTWISE, PAIRWISE@} [@{EXCLUDE, INCLUDE@}] @@ -271,6 +272,12 @@ If /COMPARE=VARIABLES is specified, then one plot per factor is produced, each each containing one boxplot per dependent variable. If the /COMPARE subcommand is ommitted, then PSPP uses the default value of /COMPARE=GROUPS. + +The ID subcommand also pertains to boxplots. If given, it must +specify a variable name. Outliers and extreme cases plotted in +boxplots will be labelled with the case from that variable. Numeric or +string variables are permissible. If the ID subcommand is not given, +then the casenumber will be used for labelling. The CINTERVAL subcommand specifies the confidence interval to use in calculation of the descriptives command. The default it 95%. @@ -499,6 +506,8 @@ NPAR TESTS [ /STATISTICS=@{DESCRIPTIVES@} ] [ /MISSING=@{ANALYSIS, LISTWISE@} @{INCLUDE, EXCLUDE@} ] + + [ /METHOD=EXACT [ TIMER [(n)] ] ] @end display NPAR TESTS performs nonparametric tests. @@ -508,10 +517,21 @@ One or more tests may be specified by using the corresponding subcommand. If the /STATISTICS subcommand is also specified, then summary statistics are produces for each variable that is the subject of any test. +Certain tests may take a long time to execute, if an exact figure is required. +Therefore, by default asymptotic approximations are used unless the +subcommand /METHOD=EXACT is specified. +Exact tests give more accurate results, but may take an unacceptably long +time to perform. If the TIMER keyword is used, it sets a maximum time, +after which the test will be abandoned, and a warning message printed. +The time, in minutes, should be specified in parentheses after the TIMER keyword. +If the TIMER keyword is given without this figure, then a default value of 5 minutes +is used. + @menu * BINOMIAL:: Binomial Test * CHISQUARE:: Chisquare Test +* WILCOXON:: Wilcoxon Signed Ranks Test @end menu @@ -591,6 +611,34 @@ sum of the frequencies need not be 1. If no /EXPECTED subcommand is given, then then equal frequencies are expected. +@node WILCOXON +@subsection Wilcoxon +@comment node-name, next, previous, up +@vindex WILCOXON +@cindex wilcoxon matched pairs signed ranks test + +@display + [ /WILCOXON varlist [ WITH varlist [ (PAIRED) ]]] +@end display + +The wilcoxon subcommand tests for differences between means of the +variables listed. The test does not make any assumptions about the +variances of the samples. + +If the @code{WITH} keyword is omitted, then tests for all +combinations of the listed variables are performed. +If the @code{WITH} keyword is given, and the @code{(PAIRED)} keyword +is also given, then the number of variables preceding @code{WITH} +must be the same as the number following it. +In this case, tests for each respective pair of variables are +performed. +If the @code{WITH} keyword is given, but the +@code{(PAIRED)} keyword is omitted, then tests for each combination +of variable preceding @code{WITH} against variable following +@code{WITH} are performed. + +If the number of observations is large, and exact tests have been +requested. then the test may take a very long time to complete. @node T-TEST @comment node-name, next, previous, up @@ -766,7 +814,6 @@ If the total sum of the coefficients are not zero, then PSPP will display a warning, but will proceed with the analysis. The @code{CONTRAST} subcommand may be given up to 10 times in order to specify different contrast tests. -@setfilename ignored @node RANK @comment node-name, next, previous, up @@ -831,3 +878,50 @@ user-missing are to be excluded from the rank scores. A setting of INCLUDE means they are to be included. The default is EXCLUDE. @include regression.texi + + +@node RELIABILITY +@section RELIABILITY + +@vindex RELIABILITY +@display +RELIABILITY + /VARIABLES=var_list + /SCALE (@var{name}) = @{var_list, ALL@} + /MODEL=@{ALPHA, SPLIT[(N)]@} + /SUMMARY=@{TOTAL,ALL@} + /MISSING=@{EXCLUDE,INCLUDE@} +@end display + +@cindex Cronbach's Alpha +The @cmd{RELIABILTY} command performs reliablity analysis on the data. + +The VARIABLES subcommand is required. It determines the set of variables +upon which analysis is to be performed. + +The SCALE subcommand determines which variables reliability is to be +calculated for. If it is omitted, then analysis for all variables named +in the VARIABLES subcommand will be used. +Optionally, the @var{name} parameter may be specified to set a string name +for the scale. + +The MODEL subcommand determines the type of analysis. If ALPHA is specified, +then Cronbach's Alpha is calculated for the scale. If the model is SPLIT, +then the variables are divided into 2 subsets. An optional parameter +@var{N} may be given, to specify how many variables to be in the first subset. +If @var{N} is omitted, then it defaults to one half of the variables in the +scale, or one half minus one if there are an odd number of variables. +The default model is ALPHA. + +By default, any cases with user missing, or system missing values for +any variables given +in the VARIABLES subcommand will be omitted from analysis. +The MISSING subcommand determines whether user missing values are to +be included or excluded in the analysis. + +The SUMMARY subcommand determines the type of summary analysis to be performed. +Currently there is only one type: SUMMARY=TOTAL, which displays per-item +analysis tested against the totals. + + + diff --git a/doc/transformation.texi b/doc/transformation.texi index 27bbb2da..5eedba64 100644 --- a/doc/transformation.texi +++ b/doc/transformation.texi @@ -83,7 +83,7 @@ list. Each set must have exactly as many source variables as aggregation variables. Each aggregation variable receives the results of applying the specified aggregation function to the corresponding source -variable. The MEAN, SD, and SUM aggregation functions may only be +variable. The MEAN, MEDIAN, SD, and SUM aggregation functions may only be applied to numeric variables. All the rest may be applied to numeric and short and long string variables. @@ -128,6 +128,9 @@ dictionary information from the source variable. Arithmetic mean. Limited to numeric values. The default format is F8.2. +@item MEDIAN(var_name) +The median value. Limited to numeric values. The default format is F8.2. + @item MIN(var_name) Minimum value. The aggregation variable receives the complete dictionary information from the source variable. @@ -550,4 +553,3 @@ If workspace is exhausted, it falls back to a merge sort algorithm that involves creates numerous temporary files. @cmd{SORT CASES} may not be specified following TEMPORARY. -@setfilename ignored diff --git a/doc/utilities.texi b/doc/utilities.texi index 35ea20e2..95cfa25d 100644 --- a/doc/utilities.texi +++ b/doc/utilities.texi @@ -414,10 +414,13 @@ default. Any real value may be assigned. @item DECIMAL @anchor{SET DECIMAL} -The default DOT setting causes the decimal point character to be -@samp{.} and the grouping character to be @samp{,}. A setting of COMMA +This value may be set to DOT or COMMA. +Setting it to DOT causes the decimal point character to be +@samp{.} and the grouping character to be @samp{,}. +Setting it to COMMA causes the decimal point character to be @samp{,} and the grouping character to be @samp{.}. +The default value is determined from the system locale. @item FORMAT Allows the default numeric input/output format to be specified. The @@ -784,4 +787,3 @@ on the output device. Specify a title as a string in quotes. The alternate syntax that did not require quotes is now obsolete. If it is used then the title is converted to all uppercase. -@setfilename ignored diff --git a/doc/variables.texi b/doc/variables.texi index a66e4232..08c4bdea 100644 --- a/doc/variables.texi +++ b/doc/variables.texi @@ -7,8 +7,7 @@ several utility functions for examining and adjusting them. @menu * ADD VALUE LABELS:: Add value labels to variables. * DELETE VARIABLES:: Delete variables. -* DISPLAY:: Display variable names & descriptions. -* DISPLAY VECTORS:: Display a list of vectors. +* DISPLAY:: Display information about the active file. * FORMATS:: Set print and write formats. * LEAVE:: Don't clear variables between cases. * MISSING VALUES:: Set missing values for variables. @@ -18,6 +17,7 @@ several utility functions for examining and adjusting them. * RENAME VARIABLES:: Rename variables. * VALUE LABELS:: Set value labels for variables. * STRING:: Create new string variables. +* VARIABLE ATTRIBUTE:: Set custom attributes on variables. * VARIABLE LABELS:: Set variable labels for variables. * VARIABLE ALIGNMENT:: Set the alignment for display. * VARIABLE WIDTH:: Set the display width. @@ -61,15 +61,26 @@ effect, it causes the temporary transformations to become permanent. @vindex DISPLAY @display -DISPLAY @{NAMES,INDEX,LABELS,VARIABLES,DICTIONARY,SCRATCH@} - [SORTED] [var_list] +DISPLAY [SORTED] NAMES [[/VARIABLES=]var_list]. +DISPLAY [SORTED] INDEX [[/VARIABLES=]var_list]. +DISPLAY [SORTED] LABELS [[/VARIABLES=]var_list]. +DISPLAY [SORTED] VARIABLES [[/VARIABLES=]var_list]. +DISPLAY [SORTED] DICTIONARY [[/VARIABLES=]var_list]. +DISPLAY [SORTED] SCRATCH [[/VARIABLES=]var_list]. +DISPLAY [SORTED] ATTRIBUTES [[/VARIABLES=]var_list]. +DISPLAY [SORTED] @@ATTRIBUTES [[/VARIABLES=]var_list]. +DISPLAY [SORTED] VECTORS. @end display -@cmd{DISPLAY} displays requested information on variables. Variables can -optionally be sorted alphabetically. The entire dictionary or just -specified variables can be described. +@cmd{DISPLAY} displays information about the active file. A variety +of different forms of information can be requested. -One of the following keywords can be present: +The following keywords primarily cause information about variables to +be displayed. With these keywords, by default information is +displayed about all variable in the active file, in the order that +variables occur in the active file dictionary. The SORTED keyword +causes output to be sorted alphabetically by variable name. The +VARIABLES subcommand limits output to the specified variables. @table @asis @item NAMES @@ -91,23 +102,24 @@ Variable names, positions, print and write formats, missing values, variable labels, and value labels are displayed. @item SCRATCH -Varible names are displayed, for scratch variables only (@pxref{Scratch +Variable names are displayed, for scratch variables only (@pxref{Scratch Variables}). -@end table -If SORTED is specified, then the variables are displayed in ascending -order based on their names; otherwise, they are displayed in the order -that they occur in the active file dictionary. +@item ATTRIBUTES +Datafile and variable attributes are displayed, except that attributes +whose names begin with @code{@@} or @code{$@@} are omitted. -@node DISPLAY VECTORS -@section DISPLAY VECTORS -@vindex DISPLAY VECTORS +@itemx @@ATTRIBUTES +All datafile and variable attributes are displayed. +@end table -@display -DISPLAY VECTORS. -@end display +With the @code{VECTOR} keyword, @cmd{DISPLAY} lists all the currently +declared vectors. If the SORTED keyword is given, the vectors are +listed in alphabetical order; otherwise, they are listed in textual +order of definition within the PSPP syntax file. -@cmd{DISPLAY VECTORS} lists all the currently declared vectors. +For related commands, see @ref{DISPLAY DOCUMENTS} and @ref{DISPLAY +FILE LABEL}. @node FORMATS @section FORMATS @@ -357,6 +369,60 @@ implicitly derived from the specified output formats. Created variables are initialized to spaces. +@node VARIABLE ATTRIBUTE +@section VARIABLE ATTRIBUTE +@vindex VARIABLE ATTRIBUTE + +@display +VARIABLE ATTRIBUTE + VARIABLES=var_list + ATTRIBUTE=name('value') [name('value')]@dots{} + ATTRIBUTE=name@b{[}index@b{]}('value') [name@b{[}index@b{]}('value')]@dots{} + DELETE=name [name]@dots{} + DELETE=name@b{[}index@b{]} [name@b{[}index@b{]}]@dots{} +@end display + +@cmd{VARIABLE ATTRIBUTE} adds, modifies, or removes user-defined +attributes associated with variables in the active file. Custom +variable attributes are not interpreted by PSPP, but they are saved as +part of system files and may be used by other software that reads +them. + +The required VARIABLES subcommand must come first. Specify the +variables to which the following ATTRIBUTE or DELETE subcommand +should apply. + +Use the ATTRIBUTE subcommand to add or modify custom variable +attributes. Specify the name of the attribute as an identifier +(@pxref{Tokens}), followed by the desired value, in parentheses, as a +quoted string. The specified attributes are then added or modified in +the variables specified on VARIABLES. Attribute names that begin with +@code{$} are reserved for PSPP's internal use, and attribute names +that begin with @code{@@} or @code{$@@} are not displayed by most PSPP +commands that display other attributes. Other attribute names are not +treated specially. + +Attributes may also be organized into arrays. To assign to an array +element, add an integer array index enclosed in square brackets +(@code{[} and @code{]}) between the attribute name and value. Array +indexes start at 1, not 0. An attribute array that has a single +element (number 1) is not distinguished from a non-array attribute. + +Use the DELETE subcommand to delete an attribute from the variable +specified on VARIABLES. Specify an attribute name by itself to delete +an entire attribute, including all array elements for attribute +arrays. Specify an attribute name followed by an array index in +square brackets to delete a single element of an attribute array. In +the latter case, all the array elements numbered higher than the +deleted element are shifted down, filling the vacated position. + +To associate custom attributes with the entire active file, instead of +with particular variables, use @cmd{DATAFILE ATTRIBUTE} (@pxref{DATAFILE ATTRIBUTE}) instead. + +@cmd{VARIABLE ATTRIBUTE} takes effect immediately. It is not affected +by conditional and looping structures such as @cmd{DO IF} or +@cmd{LOOP}. + @node VARIABLE LABELS @section VARIABLE LABELS @vindex VARIABLE LABELS @@ -489,4 +555,3 @@ variables to the specified format specification. Its syntax is identical to that of FORMATS (@pxref{FORMATS}), but @cmd{WRITE FORMATS} sets only write formats, not print formats. -@setfilename ignored diff --git a/examples/grid.sps b/examples/grid.sps new file mode 100644 index 00000000..ea7f9d74 --- /dev/null +++ b/examples/grid.sps @@ -0,0 +1,20 @@ +COMMENT -*-pspp-*- . + +COMMENT This program is useful for testing the behaviour of the Data and Variable Sheets. + +input program. +vector var(500 F8.3). + loop #c = 1 to 1000. + loop #v = 1 to 500. + compute var(#v) = #v + #c / 1000. + end loop. + end case. + end loop. + end file. +end input program. + +variable label var1 'First variable' var2 'Second variable' var3 'Third variable'. + +save outfile='grid.sav'. + +execute. diff --git a/glade/automake.mk b/glade/automake.mk index 7827516e..8e51e1f7 100644 --- a/glade/automake.mk +++ b/glade/automake.mk @@ -14,12 +14,15 @@ libglade_psppire_la_SOURCES = \ glade/bbox.c \ glade/selector.c \ glade/acr.c \ + glade/dictview.c \ + src/ui/gui/psppire-conf.c \ src/ui/gui/psppire-acr.c \ src/ui/gui/psppire-buttonbox.c \ src/ui/gui/psppire-hbuttonbox.c \ src/ui/gui/psppire-vbuttonbox.c \ src/ui/gui/psppire-dialog.c \ src/ui/gui/psppire-keypad.c \ + src/ui/gui/psppire-dictview.c \ src/ui/gui/psppire-selector.c dist_catalog_DATA = \ diff --git a/glade/dictview.c b/glade/dictview.c new file mode 100644 index 00000000..5d23b06d --- /dev/null +++ b/glade/dictview.c @@ -0,0 +1,82 @@ +#include + +#include +#include +#include "psppire-dictview.h" + +#include + + +GType +psppire_dict_get_type () +{ + return 0; +} + + + +void +glade_psppire_dictview_post_create (GladeWidgetAdaptor *adaptor, + GObject *object, + GladeCreateReason reason) +{ + GladeWidget *widget ; + + PsppireDictView *dictview = PSPPIRE_DICT_VIEW (object); + + g_return_if_fail (PSPPIRE_IS_DICT_VIEW (dictview)); + + widget = glade_widget_get_from_gobject (GTK_WIDGET (dictview)); + if (!widget) + return; + + if (reason == GLADE_CREATE_USER) + { + /* HIG complient border-width defaults on dictviews */ + glade_widget_property_set (widget, "border-width", 5); + } +} + + +GtkWidget * +glade_psppire_dictview_get_internal_child (GladeWidgetAdaptor *adaptor, + PsppireDictView *dictview, + const gchar *name) +{ +#if DEBUGGING + g_print ("%s\n", __FUNCTION__); +#endif + return GTK_WIDGET (dictview); +} + + + +void +glade_psppire_dictview_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value) +{ +#if DEBUGGING + g_print ("%s(%p) Type=\"%s\" Id=\"%s\"\n", __FUNCTION__, object, + G_OBJECT_TYPE_NAME( object ), + id); +#endif + + GWA_GET_CLASS (GTK_TYPE_WINDOW)->set_property (adaptor, object, + id, value); +} + + +GList * +glade_psppire_dictview_get_children (GladeWidgetAdaptor *adaptor, + PsppireDictView *dv) +{ + GList *list = NULL; + + g_return_val_if_fail (PSPPIRE_IS_DICT_VIEW (dv), NULL); + + list = glade_util_container_get_all_children (GTK_CONTAINER (dv)); + + return list; +} diff --git a/glade/psppire.xml b/glade/psppire.xml index e2dc4b76..50a03661 100644 --- a/glade/psppire.xml +++ b/glade/psppire.xml @@ -197,6 +197,24 @@ + + + + + glade_psppire_dictview_post_create + glade_psppire_dictview_get_children + glade_psppire_dictview_get_internal_child + + + + + + + + + + + @@ -204,8 +222,9 @@ - + + diff --git a/lib/automake.mk b/lib/automake.mk index 6410988b..6b20c674 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -3,7 +3,7 @@ include $(top_srcdir)/lib/linreg/automake.mk if WITHGUI -include $(top_srcdir)/lib/gtksheet/automake.mk +include $(top_srcdir)/lib/gtk-contrib/automake.mk endif EXTRA_DIST += lib/OChangeLog diff --git a/lib/gtk-contrib/COPYING.LESSER b/lib/gtk-contrib/COPYING.LESSER new file mode 100644 index 00000000..8add30ad --- /dev/null +++ b/lib/gtk-contrib/COPYING.LESSER @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/lib/gtk-contrib/OChangeLog b/lib/gtk-contrib/OChangeLog new file mode 100644 index 00000000..b5ba1c82 --- /dev/null +++ b/lib/gtk-contrib/OChangeLog @@ -0,0 +1,149 @@ +2008-05-08 Ben Pfaff + + Patch #6506. Reviewed by John Darrington. + + * gtksheet.c (gtk_sheet_unrealize): Don't call gtk_widget_unparent + on sheet->button if it's null. + +2008-05-06 Ben Pfaff + + * gtksheet.c (gtk_sheet_dispose): Set the sheet's entry_container + and button members to NULL after unref'ing them, so that a later + call to gtk_sheet_for_all will not try to dereference a dangling + pointer. + +2008-03-06 John Darrington + + * gsheet-row-iface.c gsheet-row-iface.h: Delete unused, unneccesary + gpointer variable from the interface. + + * gtksheet.c: Update to match new gsheet-row-iface + +2008-02-27 John Darrington + * gtksheet.c gtksheet.h: Corrected some leaks and other problems + related to de-allocating the sheet. + +2008-02-27 John Darrington + * gtksheet.c: (gtk_sheet_expose) Don't queue a redraw on the entry + widget. Fixes bug #21073 + +2008-02-20 John Darrington + + * gtksheet.c gtksheet.h: Removed some unused signals. + Made the models properties of the widget. + +2008-02-08 John Darrington + + * gtksheet.c: Removed the sheet_locked feature, which we never + used, and interfered with the editability of the entry widget. + + * gtksheet.c: Add one to the row to which we scroll. Seems like + the best way to cope with granularity problems. + +21 Septempber 2007 John Darrington + + * gtksheet.c (range_update_callback): Scroll to cell 0,0 if the + current position is outside the model's range. + +24 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed the `clip' feature, which IMO + is a croc, and we're unlikely to use. In its place, added a primary + selection which supports text and html targets. + +16 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed some legacy functions called from + gtk_sheet_finalize which caused unnecessary delays when shutting down. + +12 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed view member and replaced with + function call. Removed hadjustment_changed and vadjustment_changed + functions which did nothing. Added some whitespace arount != + operators. + +09 July 2007 John Darrington + + * gtksheet.c gtksheet.h (gtk_sheet_get_active_cell): Allowed row, + column to be NULL. + +07 July 2007 John Darrington + + * gsheet-column-iface.c gsheet-column-iface.h gsheet-row-iface.c + gsheet-row-iface.h gtksheet.c gtksheet.h: Added a "subtitle" + feature on row/column titles, which shows tooltip-like popups. + +03 July 2007 John Darrington + + * gtksheet.c gtksheet.h: Removed the autoscroll-on-select feature + that was causing us grief. + +28 June 2007 John Darrington + + * gtksheet.c: Removed some features that we dont use, to get better + speed. + +Sat Feb 17 17:36:56 2007 Ben Pfaff + + * gsheet-column-iface.c gsheet-hetero-column.c gsheet-row-iface.c + gsheet-uniform-column.c gsheet-uniform-row.c gsheetmodel.c + gtkextra-marshal.c gtkextra.c gtkiconlist.c gtkitementry.c + gtksheet.c: Add "#include ". + +Mon Jun 19 18:03:21 WST 2006 John Darrington + + * gsheet-column-iface.c gsheet-column-iface.h + gsheet-hetero-column.c gsheet-row-iface.c gsheet-row-iface.h + gsheet-uniform-column.c gsheet-uniform-row.c gtksheet.c + gtksheet.h: Fixed some warnings. Corrected errors updating + row/column titles + +Di Mai 30 19:51:19 WST 2006 John Darrington + + * gtksheet.c gtksheet.h: constness. Removed dependence on glib2.10 + +Sat May 27 16:29:36 WST 2006 John Darrington + + * gtksheet.c: Removed call to gtk_entry_set_text, which caused warnings + and was unnecessary. + +Thu May 25 17:58:51 WST 2006 John Darrington + + * gsheet-column-iface.c gsheet-column-iface.h gsheet-hetero-column.c + gsheet-row-iface.c gsheet-row-iface.h gsheet-uniform-row.c + gtksheet-extra.h gtksheet.c: Plugged memory leaks. Rationalised the way + that GtkSheetButtons are created. + +Sat May 20 21:02:03 WST 2006 John Darrington + + * gsheetmodel.c gsheetmodel.h: Added columns-inserted and columns-deleted + signals. Added g_sheet_get_{row,column}_count functions. + + * gtksheet.c gtksheet.h: Allowed -1 to be passed to + gtk_sheet_set_active_cell to indicate no active cell. + +Mon May 15 16:10:49 WST 2006 John Darrington + + * gtksheet.c: Removed code which rendered the title buttons a second + time. Cut and Paste error ? + +Sat May 13 07:58:32 WST 2006 John Darrington + + * gsheetmodel.c gsheetmodel.h gtksheet.c gtksheeet.h: Added + free_strings flag to tell the sheet whether to free the string + data passed from the model. + +Thu May 11 22:20:04 WST 2006 John Darrington + + * gtksheet.c, gtksheet.h: Fixed broken deallocation of sheet->pixmap. + +Thu May 4 17:55:48 WST 2006 John Darrington + + * gtksheet.c: Added callback on inserted rows. + +Sat Jan 28 08:48:08 2006 UTC John Darrington + + * Separated the data out of the GtkSheet. The gtksheet should now be + regarded as a way of looking into the data. The data is represented by a + GSheetModel and the rows and columns by GSheetRow and GSheetColumn. diff --git a/lib/gtk-contrib/README b/lib/gtk-contrib/README new file mode 100644 index 00000000..e6e23d94 --- /dev/null +++ b/lib/gtk-contrib/README @@ -0,0 +1,10 @@ +This is not part of the GNU PSPP program, but is used with GNU PSPP. + +This directory contains a version of the GtkXPaned widget. It includes +minor modifications. GtkXPaned is licensed under the GNU Lesser +General Public License. See COPYING.LESSER. + + +This directory also contains the PsppireSheet widget which is a very +heavily modified version of GtkSheet widget. This modified version if +licensed under the GNU General Public License version 3 or later. diff --git a/lib/gtk-contrib/automake.mk b/lib/gtk-contrib/automake.mk new file mode 100644 index 00000000..f3cd96e2 --- /dev/null +++ b/lib/gtk-contrib/automake.mk @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in -*- makefile -*- + +noinst_LIBRARIES += lib/gtk-contrib/libgtksheet.a + +lib_gtk_contrib_libgtksheet_a_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 + +lib_gtk_contrib_libgtksheet_a_SOURCES = \ + lib/gtk-contrib/gtkextra-sheet.h \ + lib/gtk-contrib/psppire-sheet.c \ + lib/gtk-contrib/psppire-sheet.h \ + lib/gtk-contrib/gtkxpaned.c \ + lib/gtk-contrib/gtkxpaned.h + +EXTRA_DIST += lib/gtk-contrib/OChangeLog \ + lib/gtk-contrib/README \ + lib/gtk-contrib/gtk-builder-convert + diff --git a/lib/gtk-contrib/gtk-builder-convert b/lib/gtk-contrib/gtk-builder-convert new file mode 100755 index 00000000..bfcb03c9 --- /dev/null +++ b/lib/gtk-contrib/gtk-builder-convert @@ -0,0 +1,750 @@ +#!/usr/bin/env python +# +# This file was downloaded from +# http://svn.gnome.org/svn/gtk+/tags/GTK_2_14_7/gtk/gtk-builder-convert +# on 11 March 2009 +# +# Copyright (C) 2006-2008 Async Open Source +# Henrique Romano +# Johan Dahlin +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This 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 Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# TODO: +# Toolbars + +"""Usage: gtk-builder-convert [OPTION] [INPUT] [OUTPUT] +Converts Glade files into XML files which can be loaded with GtkBuilder. +The [INPUT] file is + + -w, --skip-windows Convert everything but GtkWindow subclasses. + -r, --root Convert only widget named root and its children + -h, --help display this help and exit + +When OUTPUT is -, write to standard input. + +Examples: + gtk-builder-convert preference.glade preferences.ui + +Report bugs to http://bugzilla.gnome.org/.""" + +import getopt +import os +import sys + +from xml.dom import minidom, Node + +WINDOWS = ['GtkWindow', + 'GtkDialog', + 'GtkFileChooserDialog', + 'GtkMessageDialog'] + +# The subprocess is only available in Python 2.4+ +try: + import subprocess + subprocess # pyflakes +except ImportError: + subprocess = None + +def get_child_nodes(node): + assert node.tagName == 'object' + nodes = [] + for child in node.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue + if child.tagName != 'child': + continue + nodes.append(child) + return nodes + +def get_properties(node): + assert node.tagName == 'object' + properties = {} + for child in node.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue + if child.tagName != 'property': + continue + value = child.childNodes[0].data + properties[child.getAttribute('name')] = value + return properties + +def get_property(node, property_name): + assert node.tagName == 'object' + properties = get_properties(node) + return properties.get(property_name) + +def get_property_node(node, property_name): + assert node.tagName == 'object' + properties = {} + for child in node.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue + if child.tagName != 'property': + continue + if child.getAttribute('name') == property_name: + return child + +def get_signal_nodes(node): + assert node.tagName == 'object' + signals = [] + for child in node.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue + if child.tagName == 'signal': + signals.append(child) + return signals + +def get_property_nodes(node): + assert node.tagName == 'object' + properties = [] + for child in node.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue + # FIXME: handle comments + if child.tagName == 'property': + properties.append(child) + return properties + +def get_accelerator_nodes(node): + assert node.tagName == 'object' + accelerators = [] + for child in node.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue + if child.tagName == 'accelerator': + accelerators.append(child) + return accelerators + +def get_object_node(child_node): + assert child_node.tagName == 'child', child_node + nodes = [] + for node in child_node.childNodes: + if node.nodeType != Node.ELEMENT_NODE: + continue + if node.tagName == 'object': + nodes.append(node) + assert len(nodes) == 1, nodes + return nodes[0] + +def copy_properties(node, props, prop_dict): + assert node.tagName == 'object' + for prop_name in props: + child = get_property_node(node, prop_name) + if child is not None: + prop_dict[prop_name] = child + + return node + +class GtkBuilderConverter(object): + + def __init__(self, skip_windows, root): + self.skip_windows = skip_windows + self.root = root + self.root_objects = [] + self.objects = {} + + # + # Public API + # + + def parse_file(self, file): + self._dom = minidom.parse(file) + self._parse() + + def parse_buffer(self, buffer): + self._dom = minidom.parseString(buffer) + self._parse() + + def to_xml(self): + xml = self._dom.toprettyxml("", "") + return xml.encode('utf-8') + + # + # Private + # + + def _get_object(self, name): + return self.objects.get(name) + + def _get_objects_by_attr(self, attribute, value): + return [w for w in self._dom.getElementsByTagName("object") + if w.getAttribute(attribute) == value] + + def _create_object(self, obj_class, obj_id, template=None, properties=None): + """ + Creates a new tag. + Optionally a name template can be provided which will be used + to avoid naming collisions. + The properties dictionary can either contain string values or Node + values. If a node is provided the name of the node will be overridden + by the dictionary key. + + @param obj_class: class of the object (class tag) + @param obj_id: identifier of the object (id tag) + @param template: name template to use, for example 'button' + @param properties: dictionary of properties + @type properties: string or Node. + @returns: Newly created node of the object + """ + if template is not None: + count = 1 + while True: + obj_id = template + str(count) + widget = self._get_object(obj_id) + if widget is None: + break + + count += 1 + + obj = self._dom.createElement('object') + obj.setAttribute('class', obj_class) + obj.setAttribute('id', obj_id) + if properties: + for name, value in properties.items(): + if isinstance(value, Node): + # Reuse the node, so translatable and context still will be + # set when converting nodes. See also #509153 + prop = value + else: + prop = self._dom.createElement('property') + prop.appendChild(self._dom.createTextNode(value)) + + prop.setAttribute('name', str(name)) + obj.appendChild(prop) + self.objects[obj_id] = obj + return obj + + def _create_root_object(self, obj_class, template, properties=None): + obj = self._create_object(obj_class, None, template, properties) + self.root_objects.append(obj) + return obj + + def _parse(self): + glade_iface = self._dom.getElementsByTagName("glade-interface") + assert glade_iface, ("Badly formed XML, there is " + "no tag.") + # Rename glade-interface to interface + glade_iface[0].tagName = 'interface' + self._interface = glade_iface[0] + + # Remove glade-interface doc type + for node in self._dom.childNodes: + if node.nodeType == Node.DOCUMENT_TYPE_NODE: + if node.name == 'glade-interface': + self._dom.removeChild(node) + + # Strip unsupported tags + for tag in ['requires', 'requires-version']: + for child in self._dom.getElementsByTagName(tag): + child.parentNode.removeChild(child) + + if self.root: + self._strip_root(self.root) + + # Rename widget to object + objects = self._dom.getElementsByTagName("widget") + for node in objects: + node.tagName = "object" + + for node in objects: + self._convert(node.getAttribute("class"), node) + self.objects[node.getAttribute('id')] = node + + # Convert Gazpachos UI tag + for node in self._dom.getElementsByTagName("ui"): + self._convert_ui(node) + + # Convert accessibility tag + for node in self._dom.getElementsByTagName("accessibility"): + self._convert_accessibility(node) + + # Output the newly created root objects and sort them + # by attribute id + for obj in sorted(self.root_objects, + key=lambda n: n.getAttribute('id'), + reverse=True): + self._interface.childNodes.insert(0, obj) + + def _convert(self, klass, node): + if klass == 'GtkNotebook': + self._packing_prop_to_child_attr(node, "type", "tab") + elif klass in ['GtkExpander', 'GtkFrame']: + self._packing_prop_to_child_attr( + node, "type", "label_item", "label") + elif klass == "GtkMenuBar": + self._convert_menu(node) + elif klass == "GtkMenu": + # Only convert toplevel popups + if node.parentNode == self._interface: + self._convert_menu(node, popup=True) + elif klass in WINDOWS and self.skip_windows: + self._remove_window(node) + self._default_widget_converter(node) + + def _default_widget_converter(self, node): + klass = node.getAttribute("class") + for prop in get_property_nodes(node): + prop_name = prop.getAttribute("name") + if prop_name == "sizegroup": + self._convert_sizegroup(node, prop) + elif prop_name == "tooltip" and klass != "GtkAction": + prop.setAttribute("name", "tooltip-text") + elif prop_name in ["response_id", 'response-id']: + # It does not make sense to convert responses when + # we're not going to output dialogs + if self.skip_windows: + continue + object_id = node.getAttribute('id') + response = prop.childNodes[0].data + self._convert_dialog_response(node, object_id, response) + prop.parentNode.removeChild(prop) + elif prop_name == "adjustment": + self._convert_adjustment(prop) + elif prop_name == "items" and klass in ['GtkComboBox', + 'GtkComboBoxEntry']: + self._convert_combobox_items(node, prop) + elif prop_name == "text" and klass == 'GtkTextView': + self._convert_textview_text(prop) + + def _remove_window(self, node): + object_node = get_object_node(get_child_nodes(node)[0]) + parent = node.parentNode + parent.removeChild(node) + parent.appendChild(object_node) + + def _convert_menu(self, node, popup=False): + if node.hasAttribute('constructor'): + return + + uimgr = self._create_root_object('GtkUIManager', + template='uimanager') + + if popup: + name = 'popup' + else: + name = 'menubar' + + menu = self._dom.createElement(name) + menu.setAttribute('name', node.getAttribute('id')) + node.setAttribute('constructor', uimgr.getAttribute('id')) + + for child in get_child_nodes(node): + obj_node = get_object_node(child) + item = self._convert_menuitem(uimgr, obj_node) + menu.appendChild(item) + child.removeChild(obj_node) + child.parentNode.removeChild(child) + + ui = self._dom.createElement('ui') + uimgr.appendChild(ui) + + ui.appendChild(menu) + + def _convert_menuitem(self, uimgr, obj_node): + children = get_child_nodes(obj_node) + name = 'menuitem' + if children: + child_node = children[0] + menu_node = get_object_node(child_node) + # Can be GtkImage, which will take care of later. + if menu_node.getAttribute('class') == 'GtkMenu': + name = 'menu' + + object_class = obj_node.getAttribute('class') + if object_class in ['GtkMenuItem', + 'GtkImageMenuItem', + 'GtkCheckMenuItem', + 'GtkRadioMenuItem']: + menu = self._dom.createElement(name) + elif object_class == 'GtkSeparatorMenuItem': + return self._dom.createElement('separator') + else: + raise NotImplementedError(object_class) + + menu.setAttribute('action', obj_node.getAttribute('id')) + self._add_action_from_menuitem(uimgr, obj_node) + if children: + for child in get_child_nodes(menu_node): + obj_node = get_object_node(child) + item = self._convert_menuitem(uimgr, obj_node) + menu.appendChild(item) + child.removeChild(obj_node) + child.parentNode.removeChild(child) + return menu + + def _menuitem_to_action(self, node, properties): + copy_properties(node, ['label', 'tooltip'], properties) + + def _togglemenuitem_to_action(self, node, properties): + self._menuitem_to_action(node, properties) + copy_properties(node, ['active'], properties) + + def _radiomenuitem_to_action(self, node, properties): + self._togglemenuitem_to_action(node, properties) + copy_properties(node, ['group'], properties) + + def _add_action_from_menuitem(self, uimgr, node): + properties = {} + object_class = node.getAttribute('class') + object_id = node.getAttribute('id') + if object_class == 'GtkMenuItem': + name = 'GtkAction' + self._menuitem_to_action(node, properties) + elif object_class == 'GtkCheckMenuItem': + name = 'GtkToggleAction' + self._togglemenuitem_to_action(node, properties) + elif object_class == 'GtkRadioMenuItem': + name = 'GtkRadioAction' + self._radiomenuitem_to_action(node, properties) + elif object_class == 'GtkImageMenuItem': + name = 'GtkAction' + children = get_child_nodes(node) + if (children and + children[0].getAttribute('internal-child') == 'image'): + image = get_object_node(children[0]) + child = get_property_node(image, 'stock') + if child is not None: + properties['stock_id'] = child + self._menuitem_to_action(node, properties) + elif object_class == 'GtkSeparatorMenuItem': + return + else: + raise NotImplementedError(object_class) + + if get_property(node, 'use_stock') == 'True': + if 'label' in properties: + properties['stock_id'] = properties['label'] + del properties['label'] + + properties['name'] = object_id + action = self._create_object(name, + object_id, + properties=properties) + for signal in get_signal_nodes(node): + signal_name = signal.getAttribute('name') + if signal_name in ['activate', 'toggled']: + action.appendChild(signal) + else: + print 'Unhandled signal %s::%s' % (node.getAttribute('class'), + signal_name) + + if not uimgr.childNodes: + child = self._dom.createElement('child') + uimgr.appendChild(child) + + group = self._create_object('GtkActionGroup', None, + template='actiongroup') + child.appendChild(group) + else: + group = uimgr.childNodes[0].childNodes[0] + + child = self._dom.createElement('child') + group.appendChild(child) + child.appendChild(action) + + for accelerator in get_accelerator_nodes(node): + signal_name = accelerator.getAttribute('signal') + if signal_name != 'activate': + print 'Unhandled accelerator signal for %s::%s' % ( + node.getAttribute('class'), signal_name) + continue + accelerator.removeAttribute('signal') + child.appendChild(accelerator) + + def _convert_sizegroup(self, node, prop): + # This is Gazpacho only + node.removeChild(prop) + obj = self._get_object(prop.childNodes[0].data) + if obj is None: + widgets = self._get_objects_by_attr("class", "GtkSizeGroup") + if widgets: + obj = widgets[-1] + else: + obj = self._create_root_object('GtkSizeGroup', + template='sizegroup') + + widgets = obj.getElementsByTagName("widgets") + if widgets: + assert len(widgets) == 1 + widgets = widgets[0] + else: + widgets = self._dom.createElement("widgets") + obj.appendChild(widgets) + + member = self._dom.createElement("widget") + member.setAttribute("name", node.getAttribute("id")) + widgets.appendChild(member) + + def _convert_dialog_response(self, node, object_name, response): + # 1) Get parent dialog node + while True: + # If we can't find the parent dialog, give up + if node == self._dom: + return + + if (node.tagName == 'object' and + node.getAttribute('class') == 'GtkDialog'): + dialog = node + break + node = node.parentNode + assert node + + # 2) Get dialogs action-widgets tag, create if not found + for child in dialog.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue + if child.tagName == 'action-widgets': + actions = child + break + else: + actions = self._dom.createElement("action-widgets") + dialog.appendChild(actions) + + # 3) Add action-widget tag for the response + action = self._dom.createElement("action-widget") + action.setAttribute("response", response) + action.appendChild(self._dom.createTextNode(object_name)) + actions.appendChild(action) + + def _convert_adjustment(self, prop): + properties = {} + if prop.childNodes: + data = prop.childNodes[0].data + value, lower, upper, step, page, page_size = data.split(' ') + properties.update(value=value, + lower=lower, + upper=upper, + step_increment=step, + page_increment=page, + page_size=page_size) + else: + prop.appendChild(self._dom.createTextNode("")) + + adj = self._create_root_object("GtkAdjustment", + template='adjustment', + properties=properties) + prop.childNodes[0].data = adj.getAttribute('id') + + def _convert_combobox_items(self, node, prop): + parent = prop.parentNode + if not prop.childNodes: + parent.removeChild(prop) + return + value = prop.childNodes[0].data + model = self._create_root_object("GtkListStore", + template="model") + + columns = self._dom.createElement('columns') + model.appendChild(columns) + + column = self._dom.createElement('column') + column.setAttribute('type', 'gchararray') + columns.appendChild(column) + + data = self._dom.createElement('data') + model.appendChild(data) + + for item in value.split('\n'): + row = self._dom.createElement('row') + data.appendChild(row) + + col = self._dom.createElement('col') + col.setAttribute('id', '0') + col.appendChild(self._dom.createTextNode(item)) + row.appendChild(col) + + model_prop = self._dom.createElement('property') + model_prop.setAttribute('name', 'model') + model_prop.appendChild( + self._dom.createTextNode(model.getAttribute('id'))) + parent.appendChild(model_prop) + + parent.removeChild(prop) + + child = self._dom.createElement('child') + node.appendChild(child) + cell_renderer = self._create_object('GtkCellRendererText', None, + template='renderer') + child.appendChild(cell_renderer) + + attributes = self._dom.createElement('attributes') + child.appendChild(attributes) + + attribute = self._dom.createElement('attribute') + attributes.appendChild(attribute) + attribute.setAttribute('name', 'text') + attribute.appendChild(self._dom.createTextNode('0')) + + def _convert_textview_text(self, prop): + if not prop.childNodes: + prop.parentNode.removeChild(prop) + return + + data = prop.childNodes[0].data + if prop.hasAttribute('translatable'): + prop.removeAttribute('translatable') + tbuffer = self._create_root_object("GtkTextBuffer", + template='textbuffer', + properties=dict(text=data)) + prop.childNodes[0].data = tbuffer.getAttribute('id') + prop.setAttribute('name', 'buffer') + + def _packing_prop_to_child_attr(self, node, prop_name, prop_val, + attr_val=None): + for child in get_child_nodes(node): + packing_props = [p for p in child.childNodes if p.nodeName == "packing"] + if not packing_props: + continue + assert len(packing_props) == 1 + packing_prop = packing_props[0] + properties = packing_prop.getElementsByTagName("property") + for prop in properties: + if (prop.getAttribute("name") != prop_name or + prop.childNodes[0].data != prop_val): + continue + packing_prop.removeChild(prop) + child.setAttribute(prop_name, attr_val or prop_val) + if len(properties) == 1: + child.removeChild(packing_prop) + + def _convert_ui(self, node): + cdata = node.childNodes[0] + data = cdata.toxml().strip() + if not data.startswith(""): + return + data = data[9:-3] + child = minidom.parseString(data).childNodes[0] + nodes = child.childNodes[:] + for child_node in nodes: + node.appendChild(child_node) + node.removeChild(cdata) + if not node.hasAttribute("id"): + return + + # Updating references made by widgets + parent_id = node.parentNode.getAttribute("id") + for widget in self._get_objects_by_attr("constructor", + node.getAttribute("id")): + widget.getAttributeNode("constructor").value = parent_id + node.removeAttribute("id") + + def _convert_accessibility(self, node): + objectNode = node.parentNode + parent_id = objectNode.getAttribute("id") + + properties = {} + for node in node.childNodes: + if node.nodeName == 'atkproperty': + node.tagName = 'property' + properties[node.getAttribute('name')] = node + node.parentNode.removeChild(node) + elif node.nodeName == 'atkrelation': + node.tagName = 'relation' + relation_type = node.getAttribute('type') + relation_type = relation_type.replace('_', '-') + node.setAttribute('type', relation_type) + elif node.nodeName == 'atkaction': + node.tagName = 'action' + + if properties: + child = self._dom.createElement('child') + child.setAttribute("internal-child", "accessible") + + atkobject = self._create_object( + "AtkObject", None, + template='a11y-%s' % (parent_id,), + properties=properties) + child.appendChild(atkobject) + objectNode.appendChild(child) + + def _strip_root(self, root_name): + for widget in self._dom.getElementsByTagName("widget"): + if widget.getAttribute('id') == root_name: + break + else: + raise SystemExit("Could not find an object called `%s'" % ( + root_name)) + + for child in self._interface.childNodes[:]: + if child.nodeType != Node.ELEMENT_NODE: + continue + child.parentNode.removeChild(child) + + self._interface.appendChild(widget) + + +def _indent(output): + if not subprocess: + return output + + for directory in os.environ['PATH'].split(os.pathsep): + filename = os.path.join(directory, 'xmllint') + if os.path.exists(filename): + break + else: + return output + + s = subprocess.Popen([filename, '--format', '-'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + s.stdin.write(output) + s.stdin.close() + return s.stdout.read() + +def usage(): + print __doc__ + +def main(args): + try: + opts, args = getopt.getopt(args[1:], "hwr:", + ["help", "skip-windows", "root="]) + except getopt.GetoptError: + usage() + return 2 + + if len(args) != 2: + usage() + return 2 + + input_filename, output_filename = args + + skip_windows = False + split = False + root = None + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit() + elif o in ("-r", "--root"): + root = a + elif o in ("-w", "--skip-windows"): + skip_windows = True + + conv = GtkBuilderConverter(skip_windows=skip_windows, + root=root) + conv.parse_file(input_filename) + + xml = _indent(conv.to_xml()) + if output_filename == "-": + print xml + else: + open(output_filename, 'w').write(xml) + print "Wrote", output_filename + + return 0 + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/lib/gtk-contrib/gtkextra-sheet.h b/lib/gtk-contrib/gtkextra-sheet.h new file mode 100644 index 00000000..6cad27fc --- /dev/null +++ b/lib/gtk-contrib/gtkextra-sheet.h @@ -0,0 +1,62 @@ +/* This version of GtkSheet has been heavily modified, for the specific + * requirements of PSPPIRE. + * + * GtkSheet widget for Gtk+. + * Copyright (C) 1999-2001 Adrian E. Feiguin + * + * Based on GtkClist widget by Jay Painter, but major changes. + * Memory allocation routines inspired on SC (Spreadsheet Calculator) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef PSPPIRE_EXTRA_SHEET_H__ +#define PSPPIRE_EXTRA_SHEET_H__ + + +struct _PsppireSheet ; + +typedef struct _PsppireSheet PsppireSheet; + + +struct _PsppireSheetButton +{ + GtkStateType state; + gchar *label; + + gboolean label_visible; + + GtkJustification justification; + gboolean overstruck; +}; + +struct _PsppireSheetCell +{ + gint row; + gint col; +}; + +typedef struct _PsppireSheetButton PsppireSheetButton; +typedef struct _PsppireSheetCell PsppireSheetCell; + +PsppireSheetButton * psppire_sheet_button_new (void); + +void psppire_sheet_button_free (PsppireSheetButton *button); + + +#endif /* PSPPIRE_EXTRA_SHEET_H__ */ + + diff --git a/lib/gtk-contrib/gtkxpaned.c b/lib/gtk-contrib/gtkxpaned.c new file mode 100644 index 00000000..e350295a --- /dev/null +++ b/lib/gtk-contrib/gtkxpaned.c @@ -0,0 +1,3261 @@ +/******************************************************************************* +**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +** 10 20 30 40 50 60 70 80 +** +** library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+ +** Copyright (C) 2005-2006 Mirco "MacSlow" Müller +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +** +** GtkXPaned is based on GtkPaned which was done by... +** +** "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald" +** +** and later modified by... +** +** "the GTK+ Team and others 1997-2000" +** +*******************************************************************************/ + +#include +#include "gtkxpaned.h" +#include +#include +#include +#include +#include +#include + +enum WidgetProperties +{ + PROP_0, + PROP_X_POSITION, + PROP_Y_POSITION, + PROP_POSITION_SET, + PROP_MIN_X_POSITION, + PROP_MIN_Y_POSITION, + PROP_MAX_X_POSITION, + PROP_MAX_Y_POSITION +}; + +enum ChildProperties +{ + CHILD_PROP_0, + CHILD_PROP_RESIZE, + CHILD_PROP_SHRINK +}; + +enum WidgetSignals +{ + CYCLE_CHILD_FOCUS, + TOGGLE_HANDLE_FOCUS, + MOVE_HANDLE, + CYCLE_HANDLE_FOCUS, + ACCEPT_POSITION, + CANCEL_POSITION, + LAST_SIGNAL +}; + +static void gtk_xpaned_class_init (GtkXPanedClass* klass); + +static void gtk_xpaned_init (GtkXPaned* xpaned); + +static void gtk_xpaned_size_request (GtkWidget* widget, + GtkRequisition* requisition); + +static void gtk_xpaned_size_allocate (GtkWidget* widget, + GtkAllocation* allocation); + +static void gtk_xpaned_set_property (GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_get_property (GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_set_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + const GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_get_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + GValue* value, + GParamSpec* pspec); + +static void gtk_xpaned_finalize (GObject* object); + +static void gtk_xpaned_realize (GtkWidget* widget); + +static void gtk_xpaned_unrealize (GtkWidget* widget); + +static void gtk_xpaned_map (GtkWidget* widget); + +static void gtk_xpaned_unmap (GtkWidget* widget); + +static gboolean gtk_xpaned_expose (GtkWidget* widget, GdkEventExpose* event); + +static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event); + +static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event); + +static gboolean gtk_xpaned_button_press (GtkWidget* widget, + GdkEventButton* event); + +static gboolean gtk_xpaned_button_release (GtkWidget* widget, + GdkEventButton* event); + +static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event); + +static gboolean gtk_xpaned_focus (GtkWidget* widget, + GtkDirectionType direction); + +static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget); + +static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget); + +static void gtk_xpaned_forall (GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +static void gtk_xpaned_set_focus_child (GtkContainer* container, + GtkWidget* child); + +static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget); + +static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned, + GtkXPaned* first_xpaned); + +static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned, + GtkWidget* widget); + +static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned, + gboolean reverse); + +static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned, + gboolean reverse); + +static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned, + GtkScrollType scroll); + +static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned); + +static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned); + +static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned); + +static GType gtk_xpaned_child_type (GtkContainer* container); + +static GtkContainerClass* parent_class = NULL; + +struct _GtkXPanedPrivate +{ + GtkWidget *saved_focus; + GtkXPaned *first_xpaned; +}; + +GType gtk_xpaned_get_type (void) +{ + static GType xpaned_type = 0; + + if (!xpaned_type) + { + static const GTypeInfo xpaned_info = + { + sizeof (GtkXPanedClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_xpaned_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkXPaned), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_xpaned_init + }; + + xpaned_type = g_type_register_static (GTK_TYPE_CONTAINER, + "GtkXPaned", + &xpaned_info, + 0); + } + + return xpaned_type; +} + +GtkWidget* gtk_xpaned_new (void) +{ + GtkXPaned* xpaned; + + xpaned = g_object_new (GTK_TYPE_XPANED, NULL); + + return GTK_WIDGET (xpaned); +} + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void add_tab_bindings (GtkBindingSet* binding_set, + GdkModifierType modifiers) +{ + gtk_binding_entry_add_signal (binding_set, + GDK_Tab, + modifiers, + "toggle_handle_focus", + 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KP_Tab, + modifiers, + "toggle_handle_focus", + 0); +} + +static void add_move_binding (GtkBindingSet* binding_set, + guint keyval, + GdkModifierType mask, + GtkScrollType scroll) +{ + gtk_binding_entry_add_signal (binding_set, + keyval, + mask, + "move_handle", + 1, + GTK_TYPE_SCROLL_TYPE, + scroll); +} + +static void gtk_xpaned_class_init (GtkXPanedClass* class) +{ + GObjectClass* object_class; + GtkWidgetClass* widget_class; + GtkContainerClass* container_class; + GtkXPanedClass* xpaned_class; + GtkBindingSet* binding_set; + + object_class = (GObjectClass *) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; + xpaned_class = (GtkXPanedClass *) class; + + parent_class = g_type_class_peek_parent (class); + + object_class->set_property = gtk_xpaned_set_property; + object_class->get_property = gtk_xpaned_get_property; + object_class->finalize = gtk_xpaned_finalize; + + widget_class->realize = gtk_xpaned_realize; + widget_class->unrealize = gtk_xpaned_unrealize; + widget_class->map = gtk_xpaned_map; + widget_class->unmap = gtk_xpaned_unmap; + widget_class->expose_event = gtk_xpaned_expose; + widget_class->focus = gtk_xpaned_focus; + widget_class->enter_notify_event = gtk_xpaned_enter; + widget_class->leave_notify_event = gtk_xpaned_leave; + widget_class->button_press_event = gtk_xpaned_button_press; + widget_class->button_release_event = gtk_xpaned_button_release; + widget_class->motion_notify_event = gtk_xpaned_motion; + widget_class->size_request = gtk_xpaned_size_request; + widget_class->size_allocate = gtk_xpaned_size_allocate; + + container_class->add = gtk_xpaned_add; + container_class->remove = gtk_xpaned_remove; + container_class->forall = gtk_xpaned_forall; + container_class->child_type = gtk_xpaned_child_type; + container_class->set_focus_child = gtk_xpaned_set_focus_child; + container_class->set_child_property = gtk_xpaned_set_child_property; + container_class->get_child_property = gtk_xpaned_get_child_property; + + xpaned_class->cycle_child_focus = gtk_xpaned_cycle_child_focus; + xpaned_class->toggle_handle_focus = gtk_xpaned_toggle_handle_focus; + xpaned_class->move_handle = gtk_xpaned_move_handle; + xpaned_class->cycle_handle_focus = gtk_xpaned_cycle_handle_focus; + xpaned_class->accept_position = gtk_xpaned_accept_position; + xpaned_class->cancel_position = gtk_xpaned_cancel_position; + + g_object_class_install_property (object_class, + PROP_X_POSITION, + g_param_spec_int ("x-position", + ("x-Position"), + ("x-Position of paned separator in pixels (0 means all the way to the left)"), + 0, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_Y_POSITION, + g_param_spec_int ("y-position", + "y-Position", + "y-Position of paned separator in pixels (0 means all the way to the top)", + 0, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_POSITION_SET, + g_param_spec_boolean ("position-set", + "Position Set", + "TRUE if the Position property should be used", + FALSE, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("handle-size", + "Handle Size", + "Width of handle", + 0, + G_MAXINT, + 3, + G_PARAM_READABLE)); + /** + * GtkXPaned:min-x-position: + * + * The smallest possible value for the x-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MIN_X_POSITION, + g_param_spec_int ("min-x-position", + "Minimal x-Position", + "Smallest possible value for the \"x-position\" property", + 0, + G_MAXINT, + 0, + G_PARAM_READABLE)); + + /** + * GtkXPaned:min-y-position: + * + * The smallest possible value for the y-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MIN_Y_POSITION, + g_param_spec_int ("min-y-position", + "Minimal y-Position", + "Smallest possible value for the \"y-position\" property", + 0, + G_MAXINT, + 0, + G_PARAM_READABLE)); + + /** + * GtkPaned:max-x-position: + * + * The largest possible value for the x-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MAX_X_POSITION, + g_param_spec_int ("max-x-position", + "Maximal x-Position", + "Largest possible value for the \"x-position\" property", + 0, + G_MAXINT, + G_MAXINT, + G_PARAM_READABLE)); + + /** + * GtkPaned:max-y-position: + * + * The largest possible value for the y-position property. This property is derived from the + * size and shrinkability of the widget's children. + * + * Since: 2.4 + */ + g_object_class_install_property (object_class, + PROP_MAX_Y_POSITION, + g_param_spec_int ("max-y-position", + "Maximal y-Position", + "Largest possible value for the \"y-position\" property", + 0, + G_MAXINT, + G_MAXINT, + G_PARAM_READABLE)); + + /** + * GtkPaned:resize: + * + * The "resize" child property determines whether the child expands and + * shrinks along with the paned widget. + * + * Since: 2.4 + */ + gtk_container_class_install_child_property (container_class, + CHILD_PROP_RESIZE, + g_param_spec_boolean ("resize", + "Resize", + "If TRUE, the child expands and shrinks along with the paned widget", + TRUE, + G_PARAM_READWRITE)); + + /** + * GtkPaned:shrink: + * + * The "shrink" child property determines whether the child can be made + * smaller than its requisition. + * + * Since: 2.4 + */ + gtk_container_class_install_child_property (container_class, + CHILD_PROP_SHRINK, + g_param_spec_boolean ("shrink", + "Shrink", + "If TRUE, the child can be made smaller than its requisition", + TRUE, + G_PARAM_READWRITE)); + + signals [CYCLE_CHILD_FOCUS] = g_signal_new ("cycle-child-focus", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, cycle_child_focus), + NULL, NULL, + psppire_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, + G_TYPE_BOOLEAN); + + signals [TOGGLE_HANDLE_FOCUS] = g_signal_new ("toggle-handle-focus", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, toggle_handle_focus), + NULL, NULL, + psppire_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + signals[MOVE_HANDLE] = g_signal_new ("move-handle", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, move_handle), + NULL, NULL, + psppire_marshal_BOOLEAN__ENUM, + G_TYPE_BOOLEAN, 1, + GTK_TYPE_SCROLL_TYPE); + + signals [CYCLE_HANDLE_FOCUS] = g_signal_new ("cycle-handle-focus", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, cycle_handle_focus), + NULL, NULL, + psppire_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, + G_TYPE_BOOLEAN); + + signals [ACCEPT_POSITION] = g_signal_new ("accept-position", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, accept_position), + NULL, NULL, + psppire_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + signals [CANCEL_POSITION] = g_signal_new ("cancel-position", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkXPanedClass, cancel_position), + NULL, NULL, + psppire_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + binding_set = gtk_binding_set_by_class (class); + + /* F6 and friends */ + gtk_binding_entry_add_signal (binding_set, + GDK_F6, 0, + "cycle-child-focus", 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, + GDK_F6, GDK_SHIFT_MASK, + "cycle-child-focus", 1, + G_TYPE_BOOLEAN, TRUE); + + /* F8 and friends */ + gtk_binding_entry_add_signal (binding_set, + GDK_F8, 0, + "cycle-handle-focus", 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, + GDK_F8, GDK_SHIFT_MASK, + "cycle-handle-focus", 1, + G_TYPE_BOOLEAN, TRUE); + + add_tab_bindings (binding_set, 0); + add_tab_bindings (binding_set, GDK_CONTROL_MASK); + add_tab_bindings (binding_set, GDK_SHIFT_MASK); + add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK); + + /* accept and cancel positions */ + gtk_binding_entry_add_signal (binding_set, + GDK_Escape, 0, + "cancel-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_Return, 0, + "accept-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KP_Enter, 0, + "accept-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_space, 0, + "accept-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KP_Space, 0, + "accept-position", 0); + + /* move handle */ + add_move_binding (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_LEFT); + add_move_binding (binding_set, GDK_KP_Left, 0, GTK_SCROLL_STEP_LEFT); + add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); + add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); + + add_move_binding (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_RIGHT); + add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); + add_move_binding (binding_set, GDK_KP_Right, 0, GTK_SCROLL_STEP_RIGHT); + add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); + + add_move_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP); + add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP); + add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP); + + add_move_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN); + add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); + add_move_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN); + add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); + add_move_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); + add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); + + add_move_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START); + add_move_binding (binding_set, GDK_KP_Home, 0, GTK_SCROLL_START); + add_move_binding (binding_set, GDK_End, 0, GTK_SCROLL_END); + add_move_binding (binding_set, GDK_KP_End, 0, GTK_SCROLL_END); +} + +static GType gtk_xpaned_child_type (GtkContainer* container) +{ + if (!GTK_XPANED (container)->top_left_child || + !GTK_XPANED (container)->top_right_child || + !GTK_XPANED (container)->bottom_left_child || + !GTK_XPANED (container)->bottom_right_child) + return GTK_TYPE_WIDGET; + else + return G_TYPE_NONE; +} + +static void gtk_xpaned_init (GtkXPaned* xpaned) +{ + GTK_WIDGET_SET_FLAGS (xpaned, GTK_NO_WINDOW | GTK_CAN_FOCUS); + + xpaned->top_left_child = NULL; + xpaned->top_right_child = NULL; + xpaned->bottom_left_child = NULL; + xpaned->bottom_right_child = NULL; + xpaned->handle_east = NULL; + xpaned->handle_west = NULL; + xpaned->handle_north = NULL; + xpaned->handle_south = NULL; + xpaned->handle_middle = NULL; + xpaned->xor_gc = NULL; + xpaned->cursor_type_east = GDK_SB_V_DOUBLE_ARROW; + xpaned->cursor_type_west = GDK_SB_V_DOUBLE_ARROW; + xpaned->cursor_type_north = GDK_SB_H_DOUBLE_ARROW; + xpaned->cursor_type_south = GDK_SB_H_DOUBLE_ARROW; + xpaned->cursor_type_middle = GDK_FLEUR; + + xpaned->handle_pos_east.width = 5; + xpaned->handle_pos_east.height = 5; + xpaned->handle_pos_west.width = 5; + xpaned->handle_pos_west.height = 5; + xpaned->handle_pos_north.width = 5; + xpaned->handle_pos_north.height = 5; + xpaned->handle_pos_south.width = 5; + xpaned->handle_pos_south.height = 5; + xpaned->handle_pos_middle.width = 5; + xpaned->handle_pos_middle.height = 5; + + xpaned->position_set = FALSE; + xpaned->last_allocation.width = -1; + xpaned->last_allocation.height = -1; + xpaned->in_drag_vert = FALSE; + xpaned->in_drag_horiz = FALSE; + xpaned->in_drag_vert_and_horiz = FALSE; + + xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE; + xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE; + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE; + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE; + + xpaned->priv = g_new0 (GtkXPanedPrivate, 1); + xpaned->last_top_left_child_focus = NULL; + xpaned->last_top_right_child_focus = NULL; + xpaned->last_bottom_left_child_focus = NULL; + xpaned->last_bottom_right_child_focus = NULL; + xpaned->in_recursion = FALSE; + xpaned->handle_prelit = FALSE; + xpaned->original_position.x = -1; + xpaned->original_position.y = -1; + xpaned->unmaximized_position.x = -1; + xpaned->unmaximized_position.y = -1; + + xpaned->handle_pos_east.x = -1; + xpaned->handle_pos_east.y = -1; + xpaned->handle_pos_west.x = -1; + xpaned->handle_pos_west.y = -1; + xpaned->handle_pos_north.x = -1; + xpaned->handle_pos_north.y = -1; + xpaned->handle_pos_south.x = -1; + xpaned->handle_pos_south.y = -1; + xpaned->handle_pos_middle.x = -1; + xpaned->handle_pos_middle.y = -1; + + xpaned->drag_pos.x = -1; + xpaned->drag_pos.y = -1; +} + +static void gtk_xpaned_size_request (GtkWidget* widget, + GtkRequisition* requisition) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + GtkRequisition child_requisition; + + requisition->width = 0; + requisition->height = 0; + + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child)) + { + gtk_widget_size_request (xpaned->top_left_child, &child_requisition); + + requisition->width = child_requisition.width; + requisition->height = child_requisition.height; + } + + if (xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child)) + { + gtk_widget_size_request (xpaned->top_right_child, &child_requisition); + + requisition->width += child_requisition.width; + requisition->height = MAX (requisition->height, child_requisition.height); + } + + if (xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child)) + { + gtk_widget_size_request (xpaned->bottom_left_child, &child_requisition); + + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height += child_requisition.height; + } + + if (xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + gtk_widget_size_request (xpaned->bottom_right_child, &child_requisition); + + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height = MAX (requisition->height, child_requisition.height); + } + + /* add 2 times the set border-width to the GtkXPaneds requisition */ + requisition->width += GTK_CONTAINER (xpaned)->border_width * 2; + requisition->height += GTK_CONTAINER (xpaned)->border_width * 2; + + /* also add the handle "thickness" to GtkXPaneds width- and height-requisitions */ + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + gint handle_size; + + gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); + requisition->width += handle_size; + requisition->height += handle_size; + } +} + +void +gtk_xpaned_compute_position (GtkXPaned* xpaned, + const GtkAllocation* allocation, + GtkRequisition* top_left_child_req, + GtkRequisition* top_right_child_req, + GtkRequisition* bottom_left_child_req, + GtkRequisition* bottom_right_child_req); + + +static void gtk_xpaned_size_allocate (GtkWidget* widget, + GtkAllocation* allocation) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + gint border_width = GTK_CONTAINER (xpaned)->border_width; + GtkAllocation top_left_child_allocation; + GtkAllocation top_right_child_allocation; + GtkAllocation bottom_left_child_allocation; + GtkAllocation bottom_right_child_allocation; + GtkRequisition top_left_child_requisition; + GtkRequisition top_right_child_requisition; + GtkRequisition bottom_left_child_requisition; + GtkRequisition bottom_right_child_requisition; + gint handle_size; + + /* determine size of handle(s) */ + gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); + + widget->allocation = *allocation; + + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + /* what sizes do the children want to be at least at */ + gtk_widget_get_child_requisition (xpaned->top_left_child, + &top_left_child_requisition); + gtk_widget_get_child_requisition (xpaned->top_right_child, + &top_right_child_requisition); + gtk_widget_get_child_requisition (xpaned->bottom_left_child, + &bottom_left_child_requisition); + gtk_widget_get_child_requisition (xpaned->bottom_right_child, + &bottom_right_child_requisition); + + /* determine the total requisition-sum of all requisitions of borders, + * handles, children etc. */ + gtk_xpaned_compute_position (xpaned, + allocation, + &top_left_child_requisition, + &top_right_child_requisition, + &bottom_left_child_requisition, + &bottom_right_child_requisition); + + /* calculate the current positions and sizes of the handles */ + xpaned->handle_pos_east.x = widget->allocation.x + border_width + xpaned->top_left_child_size.width + handle_size; + xpaned->handle_pos_east.y = widget->allocation.y + border_width + xpaned->top_left_child_size.height; + xpaned->handle_pos_east.width = widget->allocation.width - xpaned->top_left_child_size.width - 2 * border_width - handle_size; + xpaned->handle_pos_east.height = handle_size; + + xpaned->handle_pos_west.x = widget->allocation.x + border_width; + xpaned->handle_pos_west.y = xpaned->handle_pos_east.y; + xpaned->handle_pos_west.width = widget->allocation.width - xpaned->handle_pos_east.width - 2 * border_width - handle_size; + xpaned->handle_pos_west.height = handle_size; + + xpaned->handle_pos_north.x = xpaned->handle_pos_east.x - handle_size; + xpaned->handle_pos_north.y = widget->allocation.y + border_width; + xpaned->handle_pos_north.width = handle_size; + xpaned->handle_pos_north.height = xpaned->handle_pos_east.y - widget->allocation.y - border_width; + + xpaned->handle_pos_south.x = xpaned->handle_pos_north.x; + xpaned->handle_pos_south.y = xpaned->handle_pos_east.y + handle_size; + xpaned->handle_pos_south.width = handle_size; + xpaned->handle_pos_south.height = widget->allocation.height - xpaned->handle_pos_north.height - 2 * border_width - handle_size; + + +#define CENTRUM 20 + xpaned->handle_pos_middle.x = xpaned->handle_pos_north.x ; + xpaned->handle_pos_middle.y = xpaned->handle_pos_east.y ; + xpaned->handle_pos_middle.width = handle_size + CENTRUM ; + xpaned->handle_pos_middle.height = handle_size + CENTRUM; + + /* set allocation for top-left child */ + top_left_child_allocation.x = widget->allocation.x + border_width; + top_left_child_allocation.y = widget->allocation.y + border_width; + top_left_child_allocation.width = xpaned->handle_pos_west.width; + top_left_child_allocation.height = xpaned->handle_pos_north.height; + + /* set allocation for top-right child */ + top_right_child_allocation.x = widget->allocation.x + border_width + handle_size + top_left_child_allocation.width; + top_right_child_allocation.y = widget->allocation.y + border_width; + top_right_child_allocation.width = xpaned->handle_pos_east.width; + top_right_child_allocation.height = xpaned->handle_pos_north.height; + + /* set allocation for bottom-left child */ + bottom_left_child_allocation.x = xpaned->handle_pos_west.x; + bottom_left_child_allocation.y = xpaned->handle_pos_south.y; + bottom_left_child_allocation.width = xpaned->handle_pos_west.width; + bottom_left_child_allocation.height = xpaned->handle_pos_south.height; + + /* set allocation for bottom-right child */ + bottom_right_child_allocation.x = top_right_child_allocation.x; + bottom_right_child_allocation.y = bottom_left_child_allocation.y; + bottom_right_child_allocation.width = xpaned->handle_pos_east.width; + bottom_right_child_allocation.height = xpaned->handle_pos_south.height; + + if (GTK_WIDGET_REALIZED (widget)) + { + if (GTK_WIDGET_MAPPED (widget)) + { + gdk_window_show (xpaned->handle_east); + gdk_window_show (xpaned->handle_west); + gdk_window_show (xpaned->handle_north); + gdk_window_show (xpaned->handle_south); + gdk_window_show (xpaned->handle_middle); + } + + gdk_window_move_resize (xpaned->handle_east, + xpaned->handle_pos_east.x, + xpaned->handle_pos_east.y, + xpaned->handle_pos_east.width, + xpaned->handle_pos_east.height); + + gdk_window_move_resize (xpaned->handle_west, + xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y, + xpaned->handle_pos_west.width, + xpaned->handle_pos_west.height); + + gdk_window_move_resize (xpaned->handle_north, + xpaned->handle_pos_north.x, + xpaned->handle_pos_north.y, + xpaned->handle_pos_north.width, + xpaned->handle_pos_north.height); + + gdk_window_move_resize (xpaned->handle_south, + xpaned->handle_pos_south.x, + xpaned->handle_pos_south.y, + xpaned->handle_pos_south.width, + xpaned->handle_pos_south.height); + + gdk_window_move_resize (xpaned->handle_middle, + xpaned->handle_pos_middle.x, + xpaned->handle_pos_middle.y, + xpaned->handle_pos_middle.width, + xpaned->handle_pos_middle.height); + } + + /* Now allocate the childen, making sure, when resizing not to + * overlap the windows + */ + if (GTK_WIDGET_MAPPED (widget)) + { + gtk_widget_size_allocate (xpaned->top_right_child, &top_right_child_allocation); + gtk_widget_size_allocate (xpaned->top_left_child, &top_left_child_allocation); + gtk_widget_size_allocate (xpaned->bottom_left_child, &bottom_left_child_allocation); + gtk_widget_size_allocate (xpaned->bottom_right_child, &bottom_right_child_allocation); + } + } +} + +static void gtk_xpaned_set_property (GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (object); + + switch (prop_id) + { + case PROP_X_POSITION: + gtk_xpaned_set_position_x (xpaned, g_value_get_int (value)); + break; + + case PROP_Y_POSITION: + gtk_xpaned_set_position_y (xpaned, g_value_get_int (value)); + break; + + case PROP_POSITION_SET: + xpaned->position_set = g_value_get_boolean (value); + gtk_widget_queue_resize (GTK_WIDGET (xpaned)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gtk_xpaned_get_property (GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (object); + + switch (prop_id) + { + case PROP_X_POSITION: + g_value_set_int (value, xpaned->top_left_child_size.width); + break; + + case PROP_Y_POSITION: + g_value_set_int (value, xpaned->top_left_child_size.height); + break; + + case PROP_POSITION_SET: + g_value_set_boolean (value, xpaned->position_set); + break; + + case PROP_MIN_X_POSITION: + g_value_set_int (value, xpaned->min_position.x); + break; + + case PROP_MIN_Y_POSITION: + g_value_set_int (value, xpaned->min_position.y); + break; + + case PROP_MAX_X_POSITION: + g_value_set_int (value, xpaned->max_position.x); + break; + + case PROP_MAX_Y_POSITION: + g_value_set_int (value, xpaned->max_position.y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gtk_xpaned_set_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + const GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (container); + gboolean old_value = FALSE; + gboolean new_value = FALSE; + + g_assert (child == xpaned->top_left_child || + child == xpaned->top_right_child || + child == xpaned->bottom_left_child || + child == xpaned->bottom_right_child); + + new_value = g_value_get_boolean (value); + + switch (property_id) + { + case CHILD_PROP_RESIZE: + if (child == xpaned->top_left_child) + { + old_value = xpaned->top_left_child_resize; + xpaned->top_left_child_resize = new_value; + } + else if (child == xpaned->top_right_child) + { + old_value = xpaned->top_right_child_resize; + xpaned->top_right_child_resize = new_value; + } + else if (child == xpaned->bottom_left_child) + { + old_value = xpaned->bottom_left_child_resize; + xpaned->bottom_left_child_resize = new_value; + } + else if (child == xpaned->bottom_right_child) + { + old_value = xpaned->bottom_right_child_resize; + xpaned->bottom_right_child_resize = new_value; + } + break; + + case CHILD_PROP_SHRINK : + if (child == xpaned->top_left_child) + { + old_value = xpaned->top_left_child_shrink; + xpaned->top_left_child_shrink = new_value; + } + else if (child == xpaned->top_right_child) + { + old_value = xpaned->top_right_child_shrink; + xpaned->top_right_child_shrink = new_value; + } + else if (child == xpaned->bottom_left_child) + { + old_value = xpaned->bottom_left_child_shrink; + xpaned->bottom_left_child_shrink = new_value; + } + else if (child == xpaned->bottom_right_child) + { + old_value = xpaned->bottom_right_child_shrink; + xpaned->bottom_right_child_shrink = new_value; + } + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, + property_id, + pspec); + old_value = -1; /* quiet gcc */ + break; + } + + if (old_value != new_value) + gtk_widget_queue_resize (GTK_WIDGET (container)); +} + +static void gtk_xpaned_get_child_property (GtkContainer* container, + GtkWidget* child, + guint property_id, + GValue* value, + GParamSpec* pspec) +{ + GtkXPaned* xpaned = GTK_XPANED (container); + + g_assert (child == xpaned->top_left_child || + child == xpaned->top_right_child || + child == xpaned->bottom_left_child || + child == xpaned->bottom_right_child); + + switch (property_id) + { + case CHILD_PROP_RESIZE : + if (child == xpaned->top_left_child) + g_value_set_boolean (value, xpaned->top_left_child_resize); + else if (child == xpaned->top_right_child) + g_value_set_boolean (value, xpaned->top_right_child_resize); + else if (child == xpaned->bottom_left_child) + g_value_set_boolean (value, xpaned->bottom_left_child_resize); + else if (child == xpaned->bottom_right_child) + g_value_set_boolean (value, xpaned->bottom_right_child_resize); + break; + + case CHILD_PROP_SHRINK : + if (child == xpaned->top_left_child) + g_value_set_boolean (value, xpaned->top_left_child_shrink); + else if (child == xpaned->top_right_child) + g_value_set_boolean (value, xpaned->top_right_child_shrink); + else if (child == xpaned->bottom_left_child) + g_value_set_boolean (value, xpaned->bottom_left_child_shrink); + else if (child == xpaned->bottom_right_child) + g_value_set_boolean (value, xpaned->bottom_right_child_shrink); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, + property_id, + pspec); + break; + } +} + +static void gtk_xpaned_finalize (GObject* object) +{ + GtkXPaned* xpaned = GTK_XPANED (object); + + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + + g_free (xpaned->priv); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void gtk_xpaned_realize (GtkWidget* widget) +{ + GtkXPaned* xpaned; + GdkWindowAttr attributes_east; + GdkWindowAttr attributes_west; + GdkWindowAttr attributes_north; + GdkWindowAttr attributes_south; + GdkWindowAttr attributes_middle; + gint attributes_mask_east; + gint attributes_mask_west; + gint attributes_mask_north; + gint attributes_mask_south; + gint attributes_mask_middle; + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + xpaned = GTK_XPANED (widget); + + widget->window = gtk_widget_get_parent_window (widget); + g_object_ref (widget->window); + + attributes_east.window_type = GDK_WINDOW_CHILD; + attributes_west.window_type = GDK_WINDOW_CHILD; + attributes_north.window_type = GDK_WINDOW_CHILD; + attributes_south.window_type = GDK_WINDOW_CHILD; + attributes_middle.window_type = GDK_WINDOW_CHILD; + + attributes_east.wclass = GDK_INPUT_ONLY; + attributes_west.wclass = GDK_INPUT_ONLY; + attributes_north.wclass = GDK_INPUT_ONLY; + attributes_south.wclass = GDK_INPUT_ONLY; + attributes_middle.wclass = GDK_INPUT_ONLY; + + attributes_east.x = xpaned->handle_pos_east.x; + attributes_east.y = xpaned->handle_pos_east.y; + attributes_east.width = xpaned->handle_pos_east.width; + attributes_east.height = xpaned->handle_pos_east.height; + + attributes_west.x = xpaned->handle_pos_west.x; + attributes_west.y = xpaned->handle_pos_west.y; + attributes_west.width = xpaned->handle_pos_west.width; + attributes_west.height = xpaned->handle_pos_west.height; + + attributes_north.x = xpaned->handle_pos_north.x; + attributes_north.y = xpaned->handle_pos_north.y; + attributes_north.width = xpaned->handle_pos_north.width; + attributes_north.height = xpaned->handle_pos_north.height; + + attributes_south.x = xpaned->handle_pos_south.x; + attributes_south.y = xpaned->handle_pos_south.y; + attributes_south.width = xpaned->handle_pos_south.width; + attributes_south.height = xpaned->handle_pos_south.height; + + attributes_middle.x = xpaned->handle_pos_middle.x; + attributes_middle.y = xpaned->handle_pos_middle.y; + attributes_middle.width = xpaned->handle_pos_middle.width; + attributes_middle.height = xpaned->handle_pos_middle.height; + + attributes_east.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_east); + attributes_west.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_west); + attributes_north.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_north); + attributes_south.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_south); + attributes_middle.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + xpaned->cursor_type_middle); + + attributes_east.event_mask = gtk_widget_get_events (widget); + attributes_west.event_mask = gtk_widget_get_events (widget); + attributes_north.event_mask = gtk_widget_get_events (widget); + attributes_south.event_mask = gtk_widget_get_events (widget); + attributes_middle.event_mask = gtk_widget_get_events (widget); + + attributes_east.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_west.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_north.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_south.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + attributes_middle.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + + attributes_mask_east = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_west = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_north = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_south = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes_mask_middle = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + + xpaned->handle_east = gdk_window_new (widget->window, + &attributes_east, + attributes_mask_east); + xpaned->handle_west = gdk_window_new (widget->window, + &attributes_west, + attributes_mask_west); + xpaned->handle_north = gdk_window_new (widget->window, + &attributes_north, + attributes_mask_north); + xpaned->handle_south = gdk_window_new (widget->window, + &attributes_south, + attributes_mask_south); + xpaned->handle_middle = gdk_window_new (widget->window, + &attributes_middle, + attributes_mask_middle); + + gdk_window_set_user_data (xpaned->handle_east, xpaned); + gdk_window_set_user_data (xpaned->handle_west, xpaned); + gdk_window_set_user_data (xpaned->handle_north, xpaned); + gdk_window_set_user_data (xpaned->handle_south, xpaned); + gdk_window_set_user_data (xpaned->handle_middle, xpaned); + + gdk_cursor_unref (attributes_east.cursor); + gdk_cursor_unref (attributes_west.cursor); + gdk_cursor_unref (attributes_north.cursor); + gdk_cursor_unref (attributes_south.cursor); + gdk_cursor_unref (attributes_middle.cursor); + + widget->style = gtk_style_attach (widget->style, widget->window); + + if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + gdk_window_show (xpaned->handle_east); + gdk_window_show (xpaned->handle_west); + gdk_window_show (xpaned->handle_north); + gdk_window_show (xpaned->handle_south); + gdk_window_show (xpaned->handle_middle); + } +} + +static void gtk_xpaned_unrealize (GtkWidget *widget) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->xor_gc) + { + g_object_unref (xpaned->xor_gc); + xpaned->xor_gc = NULL; + } + + if (xpaned->handle_east) + { + gdk_window_set_user_data (xpaned->handle_east, NULL); + gdk_window_destroy (xpaned->handle_east); + xpaned->handle_east = NULL; + } + + if (xpaned->handle_west) + { + gdk_window_set_user_data (xpaned->handle_west, NULL); + gdk_window_destroy (xpaned->handle_west); + xpaned->handle_west = NULL; + } + + if (xpaned->handle_north) + { + gdk_window_set_user_data (xpaned->handle_north, NULL); + gdk_window_destroy (xpaned->handle_north); + xpaned->handle_north = NULL; + } + + if (xpaned->handle_south) + { + gdk_window_set_user_data (xpaned->handle_south, NULL); + gdk_window_destroy (xpaned->handle_south); + xpaned->handle_south = NULL; + } + + if (xpaned->handle_middle) + { + gdk_window_set_user_data (xpaned->handle_middle, NULL); + gdk_window_destroy (xpaned->handle_middle); + xpaned->handle_middle = NULL; + } + + gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL); + gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL); + gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL); + gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL); + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void gtk_xpaned_map (GtkWidget* widget) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + gdk_window_show (xpaned->handle_east); + gdk_window_show (xpaned->handle_west); + gdk_window_show (xpaned->handle_north); + gdk_window_show (xpaned->handle_south); + gdk_window_show (xpaned->handle_middle); + + GTK_WIDGET_CLASS (parent_class)->map (widget); +} + +static void gtk_xpaned_unmap (GtkWidget* widget) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + gdk_window_hide (xpaned->handle_east); + gdk_window_hide (xpaned->handle_west); + gdk_window_hide (xpaned->handle_north); + gdk_window_hide (xpaned->handle_south); + gdk_window_hide (xpaned->handle_middle); + + GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +static gboolean gtk_xpaned_expose (GtkWidget* widget, + GdkEventExpose* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + gint handle_size; + GdkRectangle horizontalClipArea; + GdkRectangle verticalClipArea; + + /* determine size of handle(s) */ + gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); + + /* I want the handle-"thickness" to be at least 3 */ + g_assert (handle_size >= 3); + + if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) && + xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) && + xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) && + xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) && + xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child)) + { + GtkStateType state; + + if (gtk_widget_is_focus (widget)) + state = GTK_STATE_SELECTED; + else if (xpaned->handle_prelit) + state = GTK_STATE_PRELIGHT; + else + state = GTK_WIDGET_STATE (widget); + + horizontalClipArea.x = xpaned->handle_pos_west.x; + horizontalClipArea.y = xpaned->handle_pos_west.y; + horizontalClipArea.width = xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width; + horizontalClipArea.height = handle_size; + + verticalClipArea.x = xpaned->handle_pos_north.x; + verticalClipArea.y = xpaned->handle_pos_north.y; + verticalClipArea.width = handle_size; + verticalClipArea.height = xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height; + + gtk_paint_handle (widget->style, + widget->window, + state, + GTK_SHADOW_NONE, + &horizontalClipArea, + widget, + "paned", + xpaned->handle_pos_east.x - handle_size - 256 / 2, + xpaned->handle_pos_west.y + 1, + 256 + handle_size, + handle_size - 2, + /*xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y + 1, + xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width, + handle_size - 2,*/ + GTK_ORIENTATION_HORIZONTAL); + gtk_paint_handle (widget->style, + widget->window, + state, + GTK_SHADOW_NONE, + &verticalClipArea, + widget, + "paned", + xpaned->handle_pos_north.x + 1, + xpaned->handle_pos_south.y - handle_size - 256 / 2, + handle_size - 2, + 256 + handle_size, + /*xpaned->handle_pos_north.x + 1, + xpaned->handle_pos_north.y, + handle_size - 2, + xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height,*/ + GTK_ORIENTATION_VERTICAL); + } + + /* Chain up to draw children */ + GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + + return FALSE; +} + +static gboolean is_rtl (GtkXPaned* xpaned) +{ + if (gtk_widget_get_direction (GTK_WIDGET (xpaned)) == GTK_TEXT_DIR_RTL) + return TRUE; + + return FALSE; +} + +static void update_drag (GtkXPaned* xpaned) +{ + GdkPoint pos; + gint handle_size; + GtkRequisition size; + + gtk_widget_get_pointer (GTK_WIDGET (xpaned), &pos.x, &pos.y); + + if (xpaned->in_drag_vert) + { + pos.y -= xpaned->drag_pos.y; + + if (is_rtl (xpaned)) + { + gtk_widget_style_get (GTK_WIDGET (xpaned), + "handle-size", &handle_size, + NULL); + + size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size; + } + else + { + size.height = pos.y; + } + + size.height -= GTK_CONTAINER (xpaned)->border_width; + + size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y); + + if (size.height != xpaned->top_left_child_size.height) + gtk_xpaned_set_position_y (xpaned, size.height); + } + + if (xpaned->in_drag_horiz) + { + pos.x -= xpaned->drag_pos.x; + + if (is_rtl (xpaned)) + { + gtk_widget_style_get (GTK_WIDGET (xpaned), + "handle-size", &handle_size, + NULL); + + size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size; + } + else + { + size.width = pos.x; + } + + size.width -= GTK_CONTAINER (xpaned)->border_width; + + size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x); + + if (size.width != xpaned->top_left_child_size.width) + gtk_xpaned_set_position_x (xpaned, size.width); + } + + if (xpaned->in_drag_vert_and_horiz) + { + pos.x -= xpaned->drag_pos.x; + pos.y -= xpaned->drag_pos.y; + + if (is_rtl (xpaned)) + { + gtk_widget_style_get (GTK_WIDGET (xpaned), + "handle-size", &handle_size, + NULL); + + size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size; + size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size; + } + else + { + size.width = pos.x; + size.height = pos.y; + } + + size.width -= GTK_CONTAINER (xpaned)->border_width; + size.height -= GTK_CONTAINER (xpaned)->border_width; + + size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x); + size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y); + + if (size.width != xpaned->top_left_child_size.width) + gtk_xpaned_set_position_x (xpaned, size.width); + + if (size.height != xpaned->top_left_child_size.height) + gtk_xpaned_set_position_y (xpaned, size.height); + } +} + +static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert || + xpaned->in_drag_horiz || + xpaned->in_drag_vert_and_horiz) + update_drag (xpaned); + else + { + xpaned->handle_prelit = TRUE; + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_east.x, + xpaned->handle_pos_east.y, + xpaned->handle_pos_east.width, + xpaned->handle_pos_east.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y, + xpaned->handle_pos_west.width, + xpaned->handle_pos_west.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_north.x, + xpaned->handle_pos_north.y, + xpaned->handle_pos_north.width, + xpaned->handle_pos_north.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_south.x, + xpaned->handle_pos_south.y, + xpaned->handle_pos_south.width, + xpaned->handle_pos_south.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_middle.x, + xpaned->handle_pos_middle.y, + xpaned->handle_pos_middle.width, + xpaned->handle_pos_middle.height); + } + + return TRUE; +} + +static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert || + xpaned->in_drag_horiz || + xpaned->in_drag_vert_and_horiz) + update_drag (xpaned); + else + { + xpaned->handle_prelit = FALSE; + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_east.x, + xpaned->handle_pos_east.y, + xpaned->handle_pos_east.width, + xpaned->handle_pos_east.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_west.x, + xpaned->handle_pos_west.y, + xpaned->handle_pos_west.width, + xpaned->handle_pos_west.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_north.x, + xpaned->handle_pos_north.y, + xpaned->handle_pos_north.width, + xpaned->handle_pos_north.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_south.x, + xpaned->handle_pos_south.y, + xpaned->handle_pos_south.width, + xpaned->handle_pos_south.height); + + gtk_widget_queue_draw_area (widget, + xpaned->handle_pos_middle.x, + xpaned->handle_pos_middle.y, + xpaned->handle_pos_middle.width, + xpaned->handle_pos_middle.height); + } + + return TRUE; +} + +static gboolean gtk_xpaned_focus (GtkWidget* widget, GtkDirectionType direction) +{ + gboolean retval; + + /* This is a hack, but how can this be done without + * excessive cut-and-paste from gtkcontainer.c? + */ + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); + retval = (* GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction); + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + + return retval; +} + +static gboolean gtk_xpaned_button_press (GtkWidget* widget, + GdkEventButton* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + /* if any child is currently maximized, jump right back */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || + xpaned->maximized[GTK_XPANED_TOP_RIGHT] || + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + return FALSE; + + /* if user is dragging the handles around */ + if (!xpaned->in_drag_vert_and_horiz && + event->window != xpaned->handle_east && + event->window != xpaned->handle_west && + event->window != xpaned->handle_north && + event->window != xpaned->handle_south && + event->window == xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_vert_and_horiz = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_middle, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.x = event->x; + xpaned->drag_pos.y = event->y; + + return TRUE; + } + else if (!xpaned->in_drag_vert && + event->window == xpaned->handle_east && + event->window != xpaned->handle_west && + event->window != xpaned->handle_north && + event->window != xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_vert = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_east, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.y = event->y; + + return TRUE; + } + else if (!xpaned->in_drag_vert && + event->window != xpaned->handle_east && + event->window == xpaned->handle_west && + event->window != xpaned->handle_north && + event->window != xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_vert = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_west, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.y = event->y; + + return TRUE; + } + else if (!xpaned->in_drag_horiz && + event->window != xpaned->handle_east && + event->window != xpaned->handle_west && + event->window == xpaned->handle_north && + event->window != xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_horiz = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_north, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.x = event->x; + + return TRUE; + } + else if (!xpaned->in_drag_horiz && + event->window != xpaned->handle_east && + event->window != xpaned->handle_west && + event->window != xpaned->handle_north && + event->window == xpaned->handle_south && + event->window != xpaned->handle_middle && + event->button == 1) + { + xpaned->in_drag_horiz = TRUE; + + /* We need a server grab here, not gtk_grab_add(), since + * we don't want to pass events on to the widget's children */ + if (gdk_pointer_grab (xpaned->handle_south, + FALSE, + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK, + NULL, + NULL, + event->time) == GDK_GRAB_SUCCESS) + { + } + + xpaned->drag_pos.x = event->x; + + return TRUE; + } + return FALSE; +} + +static gboolean gtk_xpaned_button_release (GtkWidget* widget, + GdkEventButton* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert && (event->button == 1)) + { + xpaned->in_drag_vert = FALSE; + xpaned->drag_pos.y = -1; + xpaned->position_set = TRUE; + gdk_display_pointer_ungrab (gtk_widget_get_display (widget), + event->time); + return TRUE; + } + else if (xpaned->in_drag_horiz && (event->button == 1)) + { + xpaned->in_drag_horiz = FALSE; + xpaned->drag_pos.x = -1; + xpaned->position_set = TRUE; + gdk_display_pointer_ungrab (gtk_widget_get_display (widget), + event->time); + return TRUE; + } + else if (xpaned->in_drag_vert_and_horiz && (event->button == 1)) + { + xpaned->in_drag_vert_and_horiz = FALSE; + xpaned->drag_pos.x = -1; + xpaned->drag_pos.y = -1; + xpaned->position_set = TRUE; + gdk_display_pointer_ungrab (gtk_widget_get_display (widget), + event->time); + return TRUE; + } + + return FALSE; +} + +static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event) +{ + GtkXPaned* xpaned = GTK_XPANED (widget); + + if (xpaned->in_drag_vert || + xpaned->in_drag_horiz || + xpaned->in_drag_vert_and_horiz) + + { + update_drag (xpaned); + return TRUE; + } + + return FALSE; +} + +void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_top_left (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_top_right (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_bottom_left (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget *widget) +{ + gtk_xpaned_pack_bottom_right (xpaned, widget, FALSE, TRUE); +} + +void gtk_xpaned_pack_top_left (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->top_left_child) + { + xpaned->top_left_child = child; + xpaned->top_left_child_resize = resize; + xpaned->top_left_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +void gtk_xpaned_pack_top_right (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->top_right_child) + { + xpaned->top_right_child = child; + xpaned->top_right_child_resize = resize; + xpaned->top_right_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->bottom_left_child) + { + xpaned->bottom_left_child = child; + xpaned->bottom_left_child_resize = resize; + xpaned->bottom_left_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned, + GtkWidget* child, + gboolean resize, + gboolean shrink) +{ + g_return_if_fail (GTK_IS_XPANED (xpaned)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + if (!xpaned->bottom_right_child) + { + xpaned->bottom_right_child = child; + xpaned->bottom_right_child_resize = resize; + xpaned->bottom_right_child_shrink = shrink; + + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + } +} + +static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget) +{ + GtkXPaned* xpaned; + + g_return_if_fail (GTK_IS_XPANED (container)); + + xpaned = GTK_XPANED (container); + + if (!xpaned->top_left_child) + gtk_xpaned_add_top_left (xpaned, widget); + else if (!xpaned->top_right_child) + gtk_xpaned_add_top_right (xpaned, widget); + else if (!xpaned->bottom_left_child) + gtk_xpaned_add_bottom_left (xpaned, widget); + else if (!xpaned->bottom_right_child) + gtk_xpaned_add_bottom_right (xpaned, widget); + else + g_warning ("GtkXPaned cannot have more than 4 children\n"); +} + +static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget) +{ + GtkXPaned* xpaned; + gboolean was_visible; + + xpaned = GTK_XPANED (container); + was_visible = GTK_WIDGET_VISIBLE (widget); + + if (xpaned->top_left_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->top_left_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else if (xpaned->top_right_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->top_right_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else if (xpaned->bottom_left_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->bottom_left_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else if (xpaned->bottom_right_child == widget) + { + gtk_widget_unparent (widget); + + xpaned->bottom_right_child = NULL; + + if (was_visible && GTK_WIDGET_VISIBLE (container)) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else + g_warning ("GtkXPaned has no more children attached\n"); + +} + +static void gtk_xpaned_forall (GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkXPaned* xpaned; + + g_return_if_fail (callback != NULL); + + xpaned = GTK_XPANED (container); + + if (xpaned->top_left_child) + (*callback) (xpaned->top_left_child, callback_data); + if (xpaned->top_right_child) + (*callback) (xpaned->top_right_child, callback_data); + if (xpaned->bottom_left_child) + (*callback) (xpaned->bottom_left_child, callback_data); + if (xpaned->bottom_right_child) + (*callback) (xpaned->bottom_right_child, callback_data); +} + +/** + * gtk_xpaned_get_position_x: + * @paned: a #GtkXPaned widget + * + * Obtains the x-position of the divider. + * + * Return value: x-position of the divider + **/ +gint gtk_xpaned_get_position_x (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0); + + return xpaned->top_left_child_size.width; +} + +/** + * gtk_xpaned_get_position_y: + * @paned: a #GtkXPaned widget + * + * Obtains the y-position of the divider. + * + * Return value: y-position of the divider + **/ +gint gtk_xpaned_get_position_y (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0); + + return xpaned->top_left_child_size.height; +} + +/** + * gtk_xpaned_set_position_x: + * @paned: a #GtkXPaned widget + * @xposition: pixel x-position of divider, a negative values + * of a component mean that the position is unset. + * + * Sets the x-position of the divider between the four panes. + **/ +void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition) +{ + GObject* object; + + g_return_if_fail (GTK_IS_XPANED (xpaned)); + + /* if any child is currently maximized, jump right back */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || + xpaned->maximized[GTK_XPANED_TOP_RIGHT] || + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + return; + + object = G_OBJECT (xpaned); + + if (xposition >= 0) + { + /* We don't clamp here - the assumption is that + * if the total allocation changes at the same time + * as the position, the position set is with reference + * to the new total size. If only the position changes, + * then clamping will occur in gtk_paned_compute_position() + */ + + xpaned->top_left_child_size.width = xposition; + xpaned->position_set = TRUE; + } + else + { + xpaned->position_set = FALSE; + } + + g_object_freeze_notify (object); + g_object_notify (object, "x-position"); + g_object_notify (object, "position-set"); + g_object_thaw_notify (object); + + gtk_widget_queue_resize (GTK_WIDGET (xpaned)); +} + +/** + * gtk_xpaned_set_position_y: + * @paned: a #GtkXPaned widget + * @yposition: pixel y-position of divider, a negative values + * of a component mean that the position is unset. + * + * Sets the y-position of the divider between the four panes. + **/ +void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition) +{ + GObject* object; + + g_return_if_fail (GTK_IS_XPANED (xpaned)); + + /* if any child is currently maximized, jump right back */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT] || + xpaned->maximized[GTK_XPANED_TOP_RIGHT] || + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] || + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + return; + + object = G_OBJECT (xpaned); + + if (yposition >= 0) + { + /* We don't clamp here - the assumption is that + * if the total allocation changes at the same time + * as the position, the position set is with reference + * to the new total size. If only the position changes, + * then clamping will occur in gtk_paned_compute_position() + */ + + xpaned->top_left_child_size.height = yposition; + xpaned->position_set = TRUE; + } + else + { + xpaned->position_set = FALSE; + } + + g_object_freeze_notify (object); + g_object_notify (object, "y-position"); + g_object_notify (object, "position-set"); + g_object_thaw_notify (object); + + gtk_widget_queue_resize (GTK_WIDGET (xpaned)); +} + +/* this call is private and only intended for internal use! */ +void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned) +{ + xpaned->unmaximized_position.x = gtk_xpaned_get_position_x (xpaned); +} + +/* this call is private and only intended for internal use! */ +void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned) +{ + xpaned->unmaximized_position.y = gtk_xpaned_get_position_y (xpaned); +} + +/* this call is private and only intended for internal use! */ +gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned) +{ + return xpaned->unmaximized_position.x; +} + +/* this call is private and only intended for internal use! */ +gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned) +{ + return xpaned->unmaximized_position.y; +} + +/** + * gtk_xpaned_get_top_left_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the top-left child of the xpaned widget. + * + * Return value: top-left child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->top_left_child; +} + +/** + * gtk_xpaned_get_top_right_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the top-right child of the xpaned widget. + * + * Return value: top-right child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->top_right_child; +} + +/** + * gtk_xpaned_get_bottom_left_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the bottom-left child of the xpaned widget. + * + * Return value: bottom-left child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->bottom_left_child; +} + +/** + * gtk_xpaned_get_bottom_right_child: + * @xpaned: a #GtkXPaned widget + * + * Obtains the bottom-right child of the xpaned widget. + * + * Return value: bottom-right child, or %NULL if it is not set. + * + * Since: 2.4 + **/ +GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned) +{ + g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL); + + return xpaned->bottom_right_child; +} + +gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y); + + /* mark maximized flag for top-left child */ + xpaned->maximized[GTK_XPANED_TOP_LEFT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that top-left child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_TOP_LEFT]) + { + /* clear maximized flat for top-left child */ + xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* top-left child is currently not maximized, report error */ + else + return FALSE; + } +} + +gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y); + + /* mark maximized flag for top-right child */ + xpaned->maximized[GTK_XPANED_TOP_RIGHT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that top-right child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_TOP_RIGHT]) + { + /* clear maximized flat for top-right child */ + xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* top-right child is currently not maximized, report error */ + else + return FALSE; + } +} + +gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y); + + /* mark maximized flag for bottom-left child */ + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that bottom-left child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_BOTTOM_LEFT]) + { + /* clear maximized flat for bottom-left child */ + xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* bottom-left child is currently not maximized, report error */ + else + return FALSE; + } +} + +gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize) +{ + if (maximize) + { + /* see if any child is already maximized */ + if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] && + !xpaned->maximized[GTK_XPANED_TOP_RIGHT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] && + !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* save current position */ + gtk_xpaned_save_unmaximized_x (xpaned); + gtk_xpaned_save_unmaximized_y (xpaned); + + /* set new maximized position */ + gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x); + gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y); + + /* mark maximized flag for bottom-right child */ + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = TRUE; + + return TRUE; + } + /* already one child maximized, report error */ + else + return FALSE; + } + else + { + /* verify that bottom-right child is really currently maximized */ + if (xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT]) + { + /* clear maximized flat for bottom-right child */ + xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE; + + /* restore unmaximized position */ + gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned)); + gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned)); + + return TRUE; + } + /* bottom-right child is currently not maximized, report error */ + else + return FALSE; + } +} + +void +gtk_xpaned_compute_position (GtkXPaned* xpaned, + const GtkAllocation* allocation, + GtkRequisition* top_left_child_req, + GtkRequisition* top_right_child_req, + GtkRequisition* bottom_left_child_req, + GtkRequisition* bottom_right_child_req) +{ + GdkPoint old_position; + GdkPoint old_min_position; + GdkPoint old_max_position; + gint handle_size; + gint border_width = GTK_CONTAINER (xpaned)->border_width; + float fX; + float fY; + + g_return_if_fail (GTK_IS_XPANED (xpaned)); + + old_position.x = xpaned->top_left_child_size.width; + old_position.y = xpaned->top_left_child_size.height; + old_min_position.x = xpaned->min_position.x; + old_min_position.y = xpaned->min_position.y; + old_max_position.x = xpaned->max_position.x; + old_max_position.y = xpaned->max_position.y; + + fX = 100.0f * (float) old_position.x / (float) allocation->width; + fY = 100.0f * (float) old_position.y / (float) allocation->height; + + xpaned->min_position.x = xpaned->top_left_child_shrink ? 0 : top_left_child_req->width; + xpaned->min_position.y = xpaned->top_left_child_shrink ? 0 : top_left_child_req->height; + + gtk_widget_style_get (GTK_WIDGET (xpaned), "handle-size", &handle_size, NULL); + + xpaned->max_position.x = allocation->width - 2 * border_width - handle_size; + xpaned->max_position.y = allocation->height - 2 * border_width - handle_size; + if (!xpaned->top_left_child_shrink) + xpaned->max_position.x = MAX (1, xpaned->max_position.x - top_left_child_req->width); + xpaned->max_position.x = MAX (xpaned->min_position.x, xpaned->max_position.x); + + if (!xpaned->position_set) + { + if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width = MAX (0, allocation->width - top_right_child_req->width); + xpaned->top_left_child_size.height = MAX (0, allocation->height - top_right_child_req->height); + } + else if (!xpaned->top_left_child_resize && xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width = top_left_child_req->width; + xpaned->top_left_child_size.height = top_left_child_req->height; + } + else if (top_left_child_req->width + top_right_child_req->width != 0) + { + xpaned->top_left_child_size.width = allocation->width * ((gdouble)top_left_child_req->width / (top_left_child_req->width + top_right_child_req->width)) + 0.5; + } + else if (top_left_child_req->height + top_right_child_req->height != 0) + { + xpaned->top_left_child_size.height = allocation->height * ((gdouble)top_left_child_req->height / (top_left_child_req->height + top_right_child_req->height)) + 0.5; + } + else + { + xpaned->top_left_child_size.width = allocation->width * 0.5 + 0.5; + xpaned->top_left_child_size.height = allocation->height * 0.5 + 0.5; + } + } + else + { + /* If the position was set before the initial allocation. + ** (paned->last_allocation <= 0) just clamp it and leave it. */ + if (xpaned->last_allocation.width > 0) + { + if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width += allocation->width + - xpaned->last_allocation.width; + + xpaned->top_left_child_size.height += allocation->height + - xpaned->last_allocation.height; + } + else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize)) + { + xpaned->top_left_child_size.width = allocation->width + * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width)) + + 0.5; + + xpaned->top_left_child_size.height = allocation->height + * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height)) + + 0.5; + } + } + if (xpaned->last_allocation.height > 0) + { + if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize) + { + xpaned->top_left_child_size.width += allocation->width - xpaned->last_allocation.width; + xpaned->top_left_child_size.height += allocation->height - xpaned->last_allocation.height; + } + else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize)) + { + xpaned->top_left_child_size.width = allocation->width * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width)) + 0.5; + xpaned->top_left_child_size.height = allocation->height * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height)) + 0.5; + } + } + + } + + xpaned->top_left_child_size.width = CLAMP (xpaned->top_left_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->top_left_child_size.height = CLAMP (xpaned->top_left_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + xpaned->top_right_child_size.width = CLAMP (xpaned->top_right_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->top_right_child_size.height = CLAMP (xpaned->top_right_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + xpaned->bottom_left_child_size.width = CLAMP (xpaned->bottom_left_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->bottom_left_child_size.height = CLAMP (xpaned->bottom_left_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + xpaned->bottom_right_child_size.width = CLAMP (xpaned->bottom_right_child_size.width, + xpaned->min_position.x, + xpaned->max_position.x); + xpaned->bottom_right_child_size.height = CLAMP (xpaned->bottom_right_child_size.height, + xpaned->min_position.y, + xpaned->max_position.y); + + gtk_widget_set_child_visible (xpaned->top_left_child, TRUE); + gtk_widget_set_child_visible (xpaned->top_right_child, TRUE); + gtk_widget_set_child_visible (xpaned->bottom_left_child, TRUE); + gtk_widget_set_child_visible (xpaned->bottom_right_child, TRUE); + + g_object_freeze_notify (G_OBJECT (xpaned)); + + if (xpaned->top_left_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->top_left_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->top_right_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->top_right_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->bottom_left_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->bottom_left_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->bottom_right_child_size.width != old_position.x) + g_object_notify (G_OBJECT (xpaned), "x-position"); + if (xpaned->bottom_right_child_size.height != old_position.y) + g_object_notify (G_OBJECT (xpaned), "y-position"); + + if (xpaned->min_position.x != old_min_position.x) + g_object_notify (G_OBJECT (xpaned), "min-x-position"); + if (xpaned->min_position.y != old_min_position.y) + g_object_notify (G_OBJECT (xpaned), "min-y-position"); + + if (xpaned->max_position.x != old_max_position.x) + g_object_notify (G_OBJECT (xpaned), "max-y-position"); + if (xpaned->max_position.y != old_max_position.y) + g_object_notify (G_OBJECT (xpaned), "max-y-position"); + + g_object_thaw_notify (G_OBJECT (xpaned)); + + xpaned->last_allocation.width = allocation->width; + xpaned->last_allocation.height = allocation->height; + + fX = 100.0f * (float) old_position.x / (float) allocation->width; + fY = 100.0f * (float) old_position.y / (float) allocation->height; +} + +static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget) +{ + if (xpaned->priv->saved_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->saved_focus), + (gpointer *)&(xpaned->priv->saved_focus)); + + xpaned->priv->saved_focus = widget; + + if (xpaned->priv->saved_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->priv->saved_focus), + (gpointer *)&(xpaned->priv->saved_focus)); +} + +static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned, + GtkXPaned* first_xpaned) +{ + if (xpaned->priv->first_xpaned) + g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned), + (gpointer *)&(xpaned->priv->first_xpaned)); + + xpaned->priv->first_xpaned = first_xpaned; + + if (xpaned->priv->first_xpaned) + g_object_add_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned), + (gpointer *)&(xpaned->priv->first_xpaned)); +} + +static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned, + GtkWidget* widget) +{ + if (xpaned->last_top_left_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus), + (gpointer *)&(xpaned->last_top_left_child_focus)); + + xpaned->last_top_left_child_focus = widget; + + if (xpaned->last_top_left_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus), + (gpointer *)&(xpaned->last_top_left_child_focus)); +} + +static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned, + GtkWidget *widget) +{ + if (xpaned->last_top_right_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus), + (gpointer *)&(xpaned->last_top_right_child_focus)); + + xpaned->last_top_right_child_focus = widget; + + if (xpaned->last_top_right_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus), + (gpointer *)&(xpaned->last_top_right_child_focus)); +} + +static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned, + GtkWidget *widget) +{ + if (xpaned->last_bottom_left_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus), + (gpointer *)&(xpaned->last_bottom_left_child_focus)); + + xpaned->last_bottom_left_child_focus = widget; + + if (xpaned->last_bottom_left_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus), + (gpointer *)&(xpaned->last_bottom_left_child_focus)); +} + +static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned, + GtkWidget *widget) +{ + if (xpaned->last_bottom_right_child_focus) + g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus), + (gpointer *)&(xpaned->last_bottom_right_child_focus)); + + xpaned->last_bottom_right_child_focus = widget; + + if (xpaned->last_bottom_right_child_focus) + g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus), + (gpointer *)&(xpaned->last_bottom_right_child_focus)); +} + +static GtkWidget* xpaned_get_focus_widget (GtkXPaned* xpaned) +{ + GtkWidget* toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); + if (GTK_WIDGET_TOPLEVEL (toplevel)) + return GTK_WINDOW (toplevel)->focus_widget; + + return NULL; +} + +static void gtk_xpaned_set_focus_child (GtkContainer* container, + GtkWidget* focus_child) +{ + GtkXPaned* xpaned; + + g_return_if_fail (GTK_IS_XPANED (container)); + + xpaned = GTK_XPANED (container); + + if (focus_child == NULL) + { + GtkWidget* last_focus; + GtkWidget* w; + + last_focus = xpaned_get_focus_widget (xpaned); + + if (last_focus) + { + /* If there is one or more paned widgets between us and the + * focus widget, we want the topmost of those as last_focus + */ + for (w = last_focus; w != GTK_WIDGET (xpaned); w = w->parent) + if (GTK_IS_XPANED (w)) + last_focus = w; + + if (container->focus_child == xpaned->top_left_child) + gtk_xpaned_set_last_top_left_child_focus (xpaned, last_focus); + else if (container->focus_child == xpaned->top_right_child) + gtk_xpaned_set_last_top_right_child_focus (xpaned, last_focus); + else if (container->focus_child == xpaned->bottom_left_child) + gtk_xpaned_set_last_bottom_left_child_focus (xpaned, last_focus); + else if (container->focus_child == xpaned->bottom_right_child) + gtk_xpaned_set_last_bottom_right_child_focus (xpaned, last_focus); + } + } + + if (parent_class->set_focus_child) + (* parent_class->set_focus_child) (container, focus_child); +} + +static void gtk_xpaned_get_cycle_chain (GtkXPaned* xpaned, + GtkDirectionType direction, + GList** widgets) +{ + GtkContainer* container = GTK_CONTAINER (xpaned); + GtkWidget* ancestor = NULL; + GList* temp_list = NULL; + GList* list; + + if (xpaned->in_recursion) + return; + + g_assert (widgets != NULL); + + if (xpaned->last_top_left_child_focus && + !gtk_widget_is_ancestor (xpaned->last_top_left_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL); + } + + if (xpaned->last_top_right_child_focus && + !gtk_widget_is_ancestor (xpaned->last_top_right_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL); + } + + if (xpaned->last_bottom_left_child_focus && + !gtk_widget_is_ancestor (xpaned->last_bottom_left_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL); + } + + if (xpaned->last_bottom_right_child_focus && + !gtk_widget_is_ancestor (xpaned->last_bottom_right_child_focus, + GTK_WIDGET (xpaned))) + { + gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL); + } + + if (GTK_WIDGET (xpaned)->parent) + ancestor = gtk_widget_get_ancestor (GTK_WIDGET (xpaned)->parent, + GTK_TYPE_XPANED); + + /* The idea here is that temp_list is a list of widgets we want to cycle + * to. The list is prioritized so that the first element is our first + * choice, the next our second, and so on. + * + * We can't just use g_list_reverse(), because we want to try + * paned->last_child?_focus before paned->child?, both when we + * are going forward and backward. + */ + if (direction == GTK_DIR_TAB_FORWARD) + { + if (container->focus_child == xpaned->top_left_child) + { + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + temp_list = g_list_append (temp_list, ancestor); + } + else if (container->focus_child == xpaned->top_right_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + } + else if (container->focus_child == xpaned->bottom_left_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_right_child); + } + else if (container->focus_child == xpaned->bottom_right_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_left_child); + } + else + { + temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_left_child); + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_right_child); + temp_list = g_list_append (temp_list, ancestor); + } + } + else + { + if (container->focus_child == xpaned->top_left_child) + { + temp_list = g_list_append (temp_list, ancestor); + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + } + else if (container->focus_child == xpaned->top_right_child) + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + else if (container->focus_child == xpaned->bottom_right_child) + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + else if (container->focus_child == xpaned->top_right_child) + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + else + { + temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_right_child); + temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->bottom_left_child); + temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_right_child); + temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus); + temp_list = g_list_append (temp_list, xpaned->top_left_child); + temp_list = g_list_append (temp_list, ancestor); + } + } + + /* Walk the list and expand all the paned widgets. */ + for (list = temp_list; list != NULL; list = list->next) + { + GtkWidget *widget = list->data; + + if (widget) + { + if (GTK_IS_XPANED (widget)) + { + xpaned->in_recursion = TRUE; + gtk_xpaned_get_cycle_chain (GTK_XPANED (widget), + direction, + widgets); + xpaned->in_recursion = FALSE; + } + else + { + *widgets = g_list_append (*widgets, widget); + } + } + } + + g_list_free (temp_list); +} + +static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned, + gboolean reversed) +{ + GList* cycle_chain = NULL; + GList* list; + + GtkDirectionType direction = reversed ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD; + + /* ignore f6 if the handle is focused */ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + return TRUE; + + /* we can't just let the event propagate up the hierarchy, + * because the paned will want to cycle focus _unless_ an + * ancestor paned handles the event + */ + gtk_xpaned_get_cycle_chain (xpaned, direction, &cycle_chain); + + for (list = cycle_chain; list != NULL; list = list->next) + if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction)) + break; + + g_list_free (cycle_chain); + + return TRUE; +} + +static void get_child_xpanes (GtkWidget* widget, GList** xpanes) +{ + if (GTK_IS_XPANED (widget)) + { + GtkXPaned* xpaned = GTK_XPANED (widget); + + get_child_xpanes (xpaned->top_left_child, xpanes); + *xpanes = g_list_prepend (*xpanes, widget); + get_child_xpanes (xpaned->top_right_child, xpanes); + *xpanes = g_list_prepend (*xpanes, widget); + get_child_xpanes (xpaned->bottom_left_child, xpanes); + *xpanes = g_list_prepend (*xpanes, widget); + get_child_xpanes (xpaned->bottom_right_child, xpanes); + } + else if (GTK_IS_CONTAINER (widget)) + { + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback)get_child_xpanes, + xpanes); + } +} + +static GList* get_all_xpanes (GtkXPaned* xpaned) +{ + GtkXPaned* topmost = NULL; + GList* result = NULL; + GtkWidget* w; + + for (w = GTK_WIDGET (xpaned); w != NULL; w = w->parent) + { + if (GTK_IS_XPANED (w)) + topmost = GTK_XPANED (w); + } + + g_assert (topmost); + + get_child_xpanes (GTK_WIDGET (topmost), &result); + + return g_list_reverse (result); +} + +static void gtk_xpaned_find_neighbours (GtkXPaned* xpaned, + GtkXPaned** next, + GtkXPaned** prev) +{ + GList* all_xpanes; + GList* this_link; + + all_xpanes = get_all_xpanes (xpaned); + g_assert (all_xpanes); + + this_link = g_list_find (all_xpanes, xpaned); + + g_assert (this_link); + + if (this_link->next) + *next = this_link->next->data; + else + *next = all_xpanes->data; + + if (this_link->prev) + *prev = this_link->prev->data; + else + *prev = g_list_last (all_xpanes)->data; + + g_list_free (all_xpanes); +} + +static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned, GtkScrollType scroll) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + GdkPoint old_position; + GdkPoint new_position; + gint increment; + + enum + { + SINGLE_STEP_SIZE = 1, + PAGE_STEP_SIZE = 75 + }; + + new_position.x = old_position.x = gtk_xpaned_get_position_x (xpaned); + new_position.y = old_position.y = gtk_xpaned_get_position_y (xpaned); + increment = 0; + + switch (scroll) + { + case GTK_SCROLL_STEP_LEFT: + case GTK_SCROLL_STEP_UP: + case GTK_SCROLL_STEP_BACKWARD: + increment = - SINGLE_STEP_SIZE; + break; + + case GTK_SCROLL_STEP_RIGHT: + case GTK_SCROLL_STEP_DOWN: + case GTK_SCROLL_STEP_FORWARD: + increment = SINGLE_STEP_SIZE; + break; + + case GTK_SCROLL_PAGE_LEFT: + case GTK_SCROLL_PAGE_UP: + case GTK_SCROLL_PAGE_BACKWARD: + increment = - PAGE_STEP_SIZE; + break; + + case GTK_SCROLL_PAGE_RIGHT: + case GTK_SCROLL_PAGE_DOWN: + case GTK_SCROLL_PAGE_FORWARD: + increment = PAGE_STEP_SIZE; + break; + + case GTK_SCROLL_START: + new_position.x = xpaned->min_position.x; + new_position.y = xpaned->min_position.y; + break; + + case GTK_SCROLL_END: + new_position.x = xpaned->max_position.x; + new_position.y = xpaned->max_position.y; + break; + + default: + break; + } + + if (increment) + { + if (is_rtl (xpaned)) + increment = -increment; + + new_position.x = old_position.x + increment; + new_position.y = old_position.y + increment; + } + + new_position.x = CLAMP (new_position.x, + xpaned->min_position.x, + xpaned->max_position.x); + + new_position.y = CLAMP (new_position.y, + xpaned->min_position.y, + xpaned->max_position.y); + + if (old_position.x != new_position.x) + gtk_xpaned_set_position_x (xpaned, new_position.x); + + if (old_position.y != new_position.y) + gtk_xpaned_set_position_y (xpaned, new_position.y); + + return TRUE; + } + + return FALSE; +} + +static void gtk_xpaned_restore_focus (GtkXPaned* xpaned) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + if (xpaned->priv->saved_focus && + GTK_WIDGET_SENSITIVE (xpaned->priv->saved_focus)) + { + gtk_widget_grab_focus (xpaned->priv->saved_focus); + } + else + { + /* the saved focus is somehow not available for focusing, + * try + * 1) tabbing into the paned widget + * if that didn't work, + * 2) unset focus for the window if there is one + */ + + if (!gtk_widget_child_focus (GTK_WIDGET (xpaned), GTK_DIR_TAB_FORWARD)) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); + + if (GTK_IS_WINDOW (toplevel)) + gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); + } + } + + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + } +} + +static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + xpaned->original_position.x = -1; + xpaned->original_position.y = -1; + gtk_xpaned_restore_focus (xpaned); + + return TRUE; + } + + return FALSE; +} + +static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned) +{ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + if (xpaned->original_position.x != -1) + { + gtk_xpaned_set_position_x (xpaned, xpaned->original_position.x); + xpaned->original_position.x = -1; + } + + if (xpaned->original_position.y != -1) + { + gtk_xpaned_set_position_y (xpaned, xpaned->original_position.y); + xpaned->original_position.y = -1; + } + + gtk_xpaned_restore_focus (xpaned); + return TRUE; + } + + return FALSE; +} + +static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned, + gboolean reversed) +{ + GtkXPaned* next; + GtkXPaned* prev; + + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + GtkXPaned* focus = NULL; + + if (!xpaned->priv->first_xpaned) + { + /* The first_pane has disappeared. As an ad-hoc solution, + * we make the currently focused paned the first_paned. To the + * user this will seem like the paned cycling has been reset. + */ + gtk_xpaned_set_first_xpaned (xpaned, xpaned); + } + + gtk_xpaned_find_neighbours (xpaned, &next, &prev); + + if (reversed && prev && + prev != xpaned && xpaned != xpaned->priv->first_xpaned) + { + focus = prev; + } + else if (!reversed && + next && + next != xpaned && + next != xpaned->priv->first_xpaned) + { + focus = next; + } + else + { + gtk_xpaned_accept_position (xpaned); + return TRUE; + } + + g_assert (focus); + + gtk_xpaned_set_saved_focus (focus, xpaned->priv->saved_focus); + gtk_xpaned_set_first_xpaned (focus, xpaned->priv->first_xpaned); + + gtk_xpaned_set_saved_focus (xpaned, NULL); + gtk_xpaned_set_first_xpaned (xpaned, NULL); + + gtk_widget_grab_focus (GTK_WIDGET (focus)); + + if (!gtk_widget_is_focus (GTK_WIDGET (xpaned))) + { + xpaned->original_position.x = -1; + xpaned->original_position.y = -1; + focus->original_position.x = gtk_xpaned_get_position_x (focus); + focus->original_position.y = gtk_xpaned_get_position_y (focus); + } + } + else + { + GtkContainer* container = GTK_CONTAINER (xpaned); + GtkXPaned* focus; + GtkXPaned* first; + GtkXPaned* prev; + GtkXPaned* next; + GtkWidget* toplevel; + + gtk_xpaned_find_neighbours (xpaned, &next, &prev); + + if (container->focus_child == xpaned->top_left_child) + { + if (reversed) + { + focus = prev; + first = xpaned; + } + else + { + focus = xpaned; + first = xpaned; + } + } + else if (container->focus_child == xpaned->top_right_child) + { + if (reversed) + { + focus = xpaned; + first = next; + } + else + { + focus = next; + first = next; + } + } + else + { + /* Focus is not inside this xpaned, and we don't have focus. + * Presumably this happened because the application wants us + * to start keyboard navigating. + */ + focus = xpaned; + + if (reversed) + first = xpaned; + else + first = next; + } + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned)); + + if (GTK_IS_WINDOW (toplevel)) + gtk_xpaned_set_saved_focus (focus, GTK_WINDOW (toplevel)->focus_widget); + gtk_xpaned_set_first_xpaned (focus, first); + focus->original_position.x = gtk_xpaned_get_position_x (focus); + focus->original_position.y = gtk_xpaned_get_position_y (focus); + + gtk_widget_grab_focus (GTK_WIDGET (focus)); + } + + return TRUE; +} + +static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned) +{ + /* This function/signal has the wrong name. It is called when you + * press Tab or Shift-Tab and what we do is act as if + * the user pressed Return and then Tab or Shift-Tab + */ + if (gtk_widget_is_focus (GTK_WIDGET (xpaned))) + gtk_xpaned_accept_position (xpaned); + + return FALSE; +} + +/*#define __GTK_XPANED_C__*/ +/*#include "gtkaliasdef.c"*/ diff --git a/lib/gtk-contrib/gtkxpaned.h b/lib/gtk-contrib/gtkxpaned.h new file mode 100644 index 00000000..47e752ee --- /dev/null +++ b/lib/gtk-contrib/gtkxpaned.h @@ -0,0 +1,175 @@ +/******************************************************************************* +**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +** 10 20 30 40 50 60 70 80 +** +** library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+ +** Copyright (C) 2005-2006 Mirco "MacSlow" Müller +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +** +** GtkXPaned is based on GtkPaned which was done by... +** +** "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald" +** +** and later modified by... +** +** "the GTK+ Team and others 1997-2000" +** +*******************************************************************************/ + +#ifndef GTK_XPANED_H +#define GTK_XPANED_H + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_XPANED (gtk_xpaned_get_type ()) +#define GTK_XPANED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_XPANED, GtkXPaned)) +#define GTK_XPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_XPANED, GtkXPanedClass)) +#define GTK_IS_XPANED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_XPANED)) +#define GTK_IS_XPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XPANED)) +#define GTK_XPANED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XPANED, GtkXPanedClass)) + +typedef struct _GtkXPaned GtkXPaned; +typedef struct _GtkXPanedClass GtkXPanedClass; +typedef struct _GtkXPanedPrivate GtkXPanedPrivate; + +typedef enum _GtkXPanedChild +{ + GTK_XPANED_TOP_LEFT = 0, + GTK_XPANED_TOP_RIGHT, + GTK_XPANED_BOTTOM_LEFT, + GTK_XPANED_BOTTOM_RIGHT +} GtkXPanedChild; + +struct _GtkXPaned +{ + GtkContainer container; + + GtkWidget* top_left_child; + GtkWidget* top_right_child; + GtkWidget* bottom_left_child; + GtkWidget* bottom_right_child; + + GdkWindow* handle_east; + GdkWindow* handle_west; + GdkWindow* handle_north; + GdkWindow* handle_south; + GdkWindow* handle_middle; + GdkGC* xor_gc; + GdkCursorType cursor_type_east; + GdkCursorType cursor_type_west; + GdkCursorType cursor_type_north; + GdkCursorType cursor_type_south; + GdkCursorType cursor_type_middle; + + /*< private >*/ + GdkRectangle handle_pos_east; + GdkRectangle handle_pos_west; + GdkRectangle handle_pos_north; + GdkRectangle handle_pos_south; + GdkRectangle handle_pos_middle; + GtkRequisition top_left_child_size; + GtkRequisition top_right_child_size; + GtkRequisition bottom_left_child_size; + GtkRequisition bottom_right_child_size; + + GtkRequisition last_allocation; + GdkPoint min_position; + GdkPoint max_position; + gboolean maximized[4]; + + guint position_set : 1; + guint in_drag_vert : 1; + guint in_drag_horiz : 1; + guint in_drag_vert_and_horiz : 1; + guint top_left_child_shrink : 1; + guint top_left_child_resize : 1; + guint top_right_child_shrink : 1; + guint top_right_child_resize : 1; + guint bottom_left_child_shrink : 1; + guint bottom_left_child_resize : 1; + guint bottom_right_child_shrink : 1; + guint bottom_right_child_resize : 1; + guint in_recursion : 1; + guint handle_prelit : 1; + + GtkWidget* last_top_left_child_focus; + GtkWidget* last_top_right_child_focus; + GtkWidget* last_bottom_left_child_focus; + GtkWidget* last_bottom_right_child_focus; + GtkXPanedPrivate* priv; + + GdkPoint drag_pos; + GdkPoint original_position; + GdkPoint unmaximized_position; +}; + +struct _GtkXPanedClass +{ + GtkContainerClass parent_class; + gboolean (*cycle_child_focus) (GtkXPaned* xpaned, gboolean reverse); + gboolean (*toggle_handle_focus) (GtkXPaned* xpaned); + gboolean (*move_handle) (GtkXPaned* xpaned, GtkScrollType scroll); + gboolean (*cycle_handle_focus) (GtkXPaned* xpaned, gboolean reverse); + gboolean (*accept_position) (GtkXPaned* xpaned); + gboolean (*cancel_position) (GtkXPaned* xpaned); +}; + +GType gtk_xpaned_get_type (void) G_GNUC_CONST; +GtkWidget* gtk_xpaned_new (void); +void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget* child); +void gtk_xpaned_pack_top_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +void gtk_xpaned_pack_top_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink); +gint gtk_xpaned_get_position_x (GtkXPaned* xpaned); +gint gtk_xpaned_get_position_y (GtkXPaned* xpaned); +void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition); +void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition); +void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned); +void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned); +gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned); +gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned); +GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned); +gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize); +gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize); +gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize); +gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize); + +/* Internal function */ +#if !defined (GTK_DISABLE_DEPRECATED) || defined (GTK_COMPILATION) +void gtk_xpaned_compute_position (GtkXPaned* xpaned, + const GtkAllocation* allocation, + GtkRequisition* top_left_child_req, + GtkRequisition* top_right_child_req, + GtkRequisition* bottom_left_child_req, + GtkRequisition* bottom_right_child_req); +#endif /* !GTK_DISABLE_DEPRECATED || GTK_COMPILATION */ +#ifndef GTK_DISABLE_DEPRECATED +#define gtk_xpaned_gutter_size(p,s) (void) 0 +#define gtk_xpaned_set_gutter_size(p,s) (void) 0 +#endif /* GTK_DISABLE_DEPRECATED */ + +G_END_DECLS + +#endif /* GTK_XPANED_H */ diff --git a/lib/gtk-contrib/psppire-sheet.c b/lib/gtk-contrib/psppire-sheet.c new file mode 100644 index 00000000..33e7b47a --- /dev/null +++ b/lib/gtk-contrib/psppire-sheet.c @@ -0,0 +1,5591 @@ +/* + Copyright (C) 2006, 2008, 2009 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + + This file is derived from the gtksheet.c and extensively modified for the + requirements of PSPPIRE. The changes are copyright by the + Free Software Foundation. The copyright notice for the original work is + below. +*/ + +/* GtkSheet widget for Gtk+. + * Copyright (C) 1999-2001 Adrian E. Feiguin + * + * Based on GtkClist widget by Jay Painter, but major changes. + * Memory allocation routines inspired on SC (Spreadsheet Calculator) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * SECTION:psppiresheet + * @short_description: spreadsheet widget for gtk2 + * + * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of + * cells where you can allocate text. Cell contents can be edited interactively + * through a specially designed entry, GtkItemEntry. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "psppire-sheet.h" +#include +#include +#include +#include + +#include + +/* sheet flags */ +enum + { + PSPPIRE_SHEET_IN_XDRAG = 1 << 1, + PSPPIRE_SHEET_IN_YDRAG = 1 << 2, + PSPPIRE_SHEET_IN_DRAG = 1 << 3, + PSPPIRE_SHEET_IN_SELECTION = 1 << 4, + PSPPIRE_SHEET_IN_RESIZE = 1 << 5 + }; + +#define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags) +#define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag)) +#define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag)) + +#define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG) +#define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG) +#define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG) +#define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION) +#define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE) + +#define CELL_SPACING 1 + +#define TIMEOUT_HOVER 300 +#define COLUMN_MIN_WIDTH 10 +#define COLUMN_TITLES_HEIGHT 4 +#define DEFAULT_COLUMN_WIDTH 80 +#define DEFAULT_ROW_HEIGHT 25 + +static void set_entry_widget_font (PsppireSheet *sheet); + +static void psppire_sheet_update_primary_selection (PsppireSheet *sheet); +static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n); +static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n); +static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range); + + +static void set_row_height (PsppireSheet *sheet, + gint row, + gint height); + +static void destroy_hover_window (PsppireSheetHoverTitle *); +static PsppireSheetHoverTitle *create_hover_window (void); + +static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col); + + +static inline void +dispose_string (const PsppireSheet *sheet, gchar *text) +{ + PsppireSheetModel *model = psppire_sheet_get_model (sheet); + + if ( ! model ) + return; + + if (psppire_sheet_model_free_strings (model)) + g_free (text); +} + + +/* FIXME: Why bother with these two ? */ + +/* returns the column index from a pixel location */ +static inline gint +column_from_xpixel (const PsppireSheet *sheet, gint pixel) +{ + return psppire_axis_unit_at_pixel (sheet->haxis, pixel); +} + +static inline gint +row_from_ypixel (const PsppireSheet *sheet, gint pixel) +{ + return psppire_axis_unit_at_pixel (sheet->vaxis, pixel); +} + + +/* Return the lowest row number which is wholly or partially on + the visible range of the sheet */ +static inline glong +min_visible_row (const PsppireSheet *sheet) +{ + return row_from_ypixel (sheet, sheet->vadjustment->value); +} + +static inline glong +min_fully_visible_row (const PsppireSheet *sheet) +{ + glong row = min_visible_row (sheet); + + if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value) + row++; + + return row; +} + +static inline glong +max_visible_row (const PsppireSheet *sheet) +{ + return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size); +} + + +static inline glong +max_fully_visible_row (const PsppireSheet *sheet) +{ + glong row = max_visible_row (sheet); + + if ( psppire_axis_start_pixel (sheet->vaxis, row) + + + psppire_axis_unit_size (sheet->vaxis, row) + > sheet->vadjustment->value) + row--; + + return row; +} + + +/* Returns the lowest column number which is wholly or partially + on the sheet */ +static inline glong +min_visible_column (const PsppireSheet *sheet) +{ + return column_from_xpixel (sheet, sheet->hadjustment->value); +} + +static inline glong +min_fully_visible_column (const PsppireSheet *sheet) +{ + glong col = min_visible_column (sheet); + + if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value) + col++; + + return col; +} + + +/* Returns the highest column number which is wholly or partially + on the sheet */ +static inline glong +max_visible_column (const PsppireSheet *sheet) +{ + return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size); +} + +static inline glong +max_fully_visible_column (const PsppireSheet *sheet) +{ + glong col = max_visible_column (sheet); + + if ( psppire_axis_start_pixel (sheet->haxis, col) + + + psppire_axis_unit_size (sheet->haxis, col) + > sheet->hadjustment->value) + col--; + + return col; +} + + + +/* The size of the region (in pixels) around the row/column boundaries + where the height/width may be grabbed to change size */ +#define DRAG_WIDTH 6 + +static gboolean +on_column_boundary (const PsppireSheet *sheet, gint x, gint *column) +{ + gint col; + gint pixel; + + x += sheet->hadjustment->value; + + if ( x < 0) + return FALSE; + + col = column_from_xpixel (sheet, x); + + pixel = x - DRAG_WIDTH / 2; + if (pixel < 0) + pixel = 0; + + if ( column_from_xpixel (sheet, pixel) < col ) + { + *column = col - 1; + return TRUE; + } + + if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col ) + { + *column = col; + return TRUE; + } + + return FALSE; +} + +static gboolean +on_row_boundary (const PsppireSheet *sheet, gint y, gint *row) +{ + gint r; + gint pixel; + + y += sheet->vadjustment->value; + + if ( y < 0) + return FALSE; + + r = row_from_ypixel (sheet, y); + + pixel = y - DRAG_WIDTH / 2; + if (pixel < 0) + pixel = 0; + + if ( row_from_ypixel (sheet, pixel) < r ) + { + *row = r - 1; + return TRUE; + } + + if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r ) + { + *row = r; + return TRUE; + } + + return FALSE; +} + + +static inline gboolean +POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint ydrag, xdrag; + + /* Can't drag if nothing is selected */ + if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || + sheet->range.col0 < 0 || sheet->range.coli < 0 ) + return FALSE; + + *drag_column = column_from_xpixel (sheet, x); + *drag_row = row_from_ypixel (sheet, y); + + if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 && + x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) + + psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2) + { + ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0); + if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) + { + *drag_row = sheet->range.row0; + return TRUE; + } + ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + + psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi); + if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) + { + *drag_row = sheet->range.rowi; + return TRUE; + } + } + + if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 && + y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + + psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2) + { + xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0); + if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) + { + *drag_column = sheet->range.col0; + return TRUE; + } + xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) + + psppire_axis_unit_size (sheet->haxis, sheet->range.coli); + if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) + { + *drag_column = sheet->range.coli; + return TRUE; + } + } + + return FALSE; +} + +static inline gboolean +POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y, + gint *drag_row, gint *drag_column) +{ + gint xdrag, ydrag; + + /* Can't drag if nothing is selected */ + if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || + sheet->range.col0 < 0 || sheet->range.coli < 0 ) + return FALSE; + + xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+ + psppire_axis_unit_size (sheet->haxis, sheet->range.coli); + + ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) + + psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi); + + if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) + ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet)); + + if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) + xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet)); + + *drag_column = column_from_xpixel (sheet, x); + *drag_row = row_from_ypixel (sheet, y); + + if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 && + y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE; + + return FALSE; +} + + +static gboolean +rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range, + GdkRectangle *r) +{ + g_return_val_if_fail (range, FALSE); + + r->x = psppire_axis_start_pixel (sheet->haxis, range->col0); + r->x -= round (sheet->hadjustment->value); + + r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0); + r->y -= round (sheet->vadjustment->value); + + r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) - + psppire_axis_start_pixel (sheet->haxis, range->col0) + + psppire_axis_unit_size (sheet->haxis, range->coli); + + r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) - + psppire_axis_start_pixel (sheet->vaxis, range->row0) + + psppire_axis_unit_size (sheet->vaxis, range->rowi); + + if ( sheet->column_titles_visible) + { + r->y += sheet->column_title_area.height; + } + + if ( sheet->row_titles_visible) + { + r->x += sheet->row_title_area.width; + } + + return TRUE; +} + +static gboolean +rectangle_from_cell (PsppireSheet *sheet, gint row, gint col, + GdkRectangle *r) +{ + PsppireSheetRange range; + g_return_val_if_fail (row >= 0, FALSE); + g_return_val_if_fail (col >= 0, FALSE); + + range.row0 = range.rowi = row; + range.col0 = range.coli = col; + + return rectangle_from_range (sheet, &range, r); +} + + +static void psppire_sheet_class_init (PsppireSheetClass *klass); +static void psppire_sheet_init (PsppireSheet *sheet); +static void psppire_sheet_dispose (GObject *object); +static void psppire_sheet_finalize (GObject *object); +static void psppire_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void psppire_sheet_realize (GtkWidget *widget); +static void psppire_sheet_unrealize (GtkWidget *widget); +static void psppire_sheet_map (GtkWidget *widget); +static void psppire_sheet_unmap (GtkWidget *widget); +static gint psppire_sheet_expose (GtkWidget *widget, + GdkEventExpose *event); + +static void psppire_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + +static gint psppire_sheet_button_press (GtkWidget *widget, + GdkEventButton *event); +static gint psppire_sheet_button_release (GtkWidget *widget, + GdkEventButton *event); +static gint psppire_sheet_motion (GtkWidget *widget, + GdkEventMotion *event); +static gboolean psppire_sheet_crossing_notify (GtkWidget *widget, + GdkEventCrossing *event); +static gint psppire_sheet_entry_key_press (GtkWidget *widget, + GdkEventKey *key); +static gboolean psppire_sheet_key_press (GtkWidget *widget, + GdkEventKey *key); +static void psppire_sheet_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void psppire_sheet_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + +static gboolean psppire_sheet_focus_in (GtkWidget *widget, + GdkEventFocus *event); + +/* Sheet queries */ + +static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet, + const PsppireSheetRange *range); +static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet, + gint row, gint column); +/* Drawing Routines */ + +/* draw cell */ +static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column); + + +/* draw visible part of range. */ +static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region); + + +/* highlight the visible part of the selected range */ +static void psppire_sheet_range_draw_selection (PsppireSheet *sheet, + PsppireSheetRange range); + +/* Selection */ + +static void psppire_sheet_real_select_range (PsppireSheet *sheet, + const PsppireSheetRange *range); +static void psppire_sheet_real_unselect_range (PsppireSheet *sheet, + const PsppireSheetRange *range); +static void psppire_sheet_extend_selection (PsppireSheet *sheet, + gint row, gint column); +static void psppire_sheet_new_selection (PsppireSheet *sheet, + PsppireSheetRange *range); +static void psppire_sheet_draw_border (PsppireSheet *sheet, + PsppireSheetRange range); + +/* Active Cell handling */ + +static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet); +static void change_active_cell (PsppireSheet *sheet, gint row, gint col); +static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet); +static void psppire_sheet_show_entry_widget (PsppireSheet *sheet); +static gboolean psppire_sheet_click_cell (PsppireSheet *sheet, + gint row, + gint column); + + +/* Scrollbars */ + +static void adjust_scrollbars (PsppireSheet *sheet); +static void vadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data); +static void hadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data); + + +static void draw_xor_vline (PsppireSheet *sheet); +static void draw_xor_hline (PsppireSheet *sheet); +static void draw_xor_rectangle (PsppireSheet *sheet, + PsppireSheetRange range); + +/* Sheet Button */ + +static void create_global_button (PsppireSheet *sheet); +static void global_button_clicked (GtkWidget *widget, + gpointer data); +/* Sheet Entry */ + +static void create_sheet_entry (PsppireSheet *sheet); +static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet); + +/* Sheet button gadgets */ + +static void draw_column_title_buttons (PsppireSheet *sheet); +static void draw_row_title_buttons (PsppireSheet *sheet); + + +static void size_allocate_global_button (PsppireSheet *sheet); +static void psppire_sheet_button_size_request (PsppireSheet *sheet, + const PsppireSheetButton *button, + GtkRequisition *requisition); + +static void psppire_sheet_real_cell_clear (PsppireSheet *sheet, + gint row, + gint column); + + +/* Signals */ +enum + { + SELECT_ROW, + SELECT_COLUMN, + DOUBLE_CLICK_ROW, + DOUBLE_CLICK_COLUMN, + BUTTON_EVENT_ROW, + BUTTON_EVENT_COLUMN, + SELECT_RANGE, + RESIZE_RANGE, + MOVE_RANGE, + TRAVERSE, + ACTIVATE, + LAST_SIGNAL + }; + +static GtkContainerClass *parent_class = NULL; +static guint sheet_signals[LAST_SIGNAL] = { 0 }; + + +GType +psppire_sheet_get_type () +{ + static GType sheet_type = 0; + + if (!sheet_type) + { + static const GTypeInfo sheet_info = + { + sizeof (PsppireSheetClass), + NULL, + NULL, + (GClassInitFunc) psppire_sheet_class_init, + NULL, + NULL, + sizeof (PsppireSheet), + 0, + (GInstanceInitFunc) psppire_sheet_init, + NULL, + }; + + sheet_type = + g_type_register_static (GTK_TYPE_BIN, "PsppireSheet", + &sheet_info, 0); + } + return sheet_type; +} + + + +static PsppireSheetRange* +psppire_sheet_range_copy (const PsppireSheetRange *range) +{ + PsppireSheetRange *new_range; + + g_return_val_if_fail (range != NULL, NULL); + + new_range = g_new (PsppireSheetRange, 1); + + *new_range = *range; + + return new_range; +} + +static void +psppire_sheet_range_free (PsppireSheetRange *range) +{ + g_return_if_fail (range != NULL); + + g_free (range); +} + +GType +psppire_sheet_range_get_type (void) +{ + static GType sheet_range_type = 0; + + if (!sheet_range_type) + { + sheet_range_type = + g_boxed_type_register_static ("PsppireSheetRange", + (GBoxedCopyFunc) psppire_sheet_range_copy, + (GBoxedFreeFunc) psppire_sheet_range_free); + } + + return sheet_range_type; +} + +static PsppireSheetCell* +psppire_sheet_cell_copy (const PsppireSheetCell *cell) +{ + PsppireSheetCell *new_cell; + + g_return_val_if_fail (cell != NULL, NULL); + + new_cell = g_new (PsppireSheetCell, 1); + + *new_cell = *cell; + + return new_cell; +} + +static void +psppire_sheet_cell_free (PsppireSheetCell *cell) +{ + g_return_if_fail (cell != NULL); + + g_free (cell); +} + +GType +psppire_sheet_cell_get_type (void) +{ + static GType sheet_cell_type = 0; + + if (!sheet_cell_type) + { + sheet_cell_type = + g_boxed_type_register_static ("PsppireSheetCell", + (GBoxedCopyFunc) psppire_sheet_cell_copy, + (GBoxedFreeFunc) psppire_sheet_cell_free); + } + + return sheet_cell_type; +} + + +/* Properties */ +enum + { + PROP_0, + PROP_VAXIS, + PROP_HAXIS, + PROP_CELL_PADDING, + PROP_MODEL + }; + +static void +resize_column (PsppireSheet *sheet, gint unit, glong size) +{ + PsppireSheetRange range; + range.col0 = unit; + range.coli = max_visible_column (sheet); + range.row0 = min_visible_row (sheet); + range.rowi = max_visible_row (sheet); + + redraw_range (sheet, &range); + + draw_column_title_buttons_range (sheet, range.col0, range.coli); +} + + +static void +psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a) +{ + if ( sheet->haxis ) + g_object_unref (sheet->haxis); + + sheet->haxis = a; + g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet); + + if ( sheet->haxis ) + g_object_ref (sheet->haxis); +} + +static void +resize_row (PsppireSheet *sheet, gint unit, glong size) +{ + PsppireSheetRange range; + range.col0 = min_visible_column (sheet); + range.coli = max_visible_column (sheet); + range.row0 = unit; + range.rowi = max_visible_row (sheet); + + redraw_range (sheet, &range); + + draw_row_title_buttons_range (sheet, range.row0, range.rowi); +} + +static void +psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a) +{ + if ( sheet->vaxis ) + g_object_unref (sheet->vaxis); + + sheet->vaxis = a; + + g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet); + + if ( sheet->vaxis ) + g_object_ref (sheet->vaxis); +} + +static const GtkBorder default_cell_padding = { 3, 3, 2, 2 }; + +static void +psppire_sheet_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + +{ + PsppireSheet *sheet = PSPPIRE_SHEET (object); + + switch (prop_id) + { + case PROP_CELL_PADDING: + if ( sheet->cell_padding) + g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding); + + sheet->cell_padding = g_value_dup_boxed (value); + + if (NULL == sheet->cell_padding) + sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER, + &default_cell_padding); + + if (sheet->vaxis) + g_object_set (sheet->vaxis, "padding", + sheet->cell_padding->top + sheet->cell_padding->bottom, + NULL); + + if (sheet->haxis) + g_object_set (sheet->haxis, "padding", + sheet->cell_padding->left + sheet->cell_padding->right, + NULL); + break; + case PROP_VAXIS: + psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value)); + g_object_set (sheet->vaxis, "padding", + sheet->cell_padding->top + sheet->cell_padding->bottom, + NULL); + break; + case PROP_HAXIS: + psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value)); + g_object_set (sheet->haxis, "padding", + sheet->cell_padding->left + sheet->cell_padding->right, + NULL); + break; + case PROP_MODEL: + psppire_sheet_set_model (sheet, g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + +static void +psppire_sheet_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (object); + + switch (prop_id) + { + case PROP_CELL_PADDING: + g_value_set_boxed (value, sheet->cell_padding); + break; + case PROP_VAXIS: + g_value_set_pointer (value, sheet->vaxis); + break; + case PROP_HAXIS: + g_value_set_pointer (value, sheet->haxis); + break; + case PROP_MODEL: + g_value_set_pointer (value, sheet->model); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + + +static void +psppire_sheet_class_init (PsppireSheetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + GParamSpec *haxis_spec ; + GParamSpec *vaxis_spec ; + GParamSpec *model_spec ; + GParamSpec *cell_padding_spec ; + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + /** + * PsppireSheet::select-row + * @sheet: the sheet widget that emitted the signal + * @row: the newly selected row index + * + * A row has been selected. + */ + sheet_signals[SELECT_ROW] = + g_signal_new ("select-row", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, select_row), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::select - column + * @sheet: the sheet widget that emitted the signal + * @column: the newly selected column index + * + * A column has been selected. + */ + sheet_signals[SELECT_COLUMN] = + g_signal_new ("select-column", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, select_column), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::double-click-row + * @sheet: the sheet widget that emitted the signal + * @row: the row that was double clicked. + * + * A row's title button has been double clicked + */ + sheet_signals[DOUBLE_CLICK_ROW] = + g_signal_new ("double-click-row", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::double-click-column + * @sheet: the sheet widget that emitted the signal + * @column: the column that was double clicked. + * + * A column's title button has been double clicked + */ + sheet_signals[DOUBLE_CLICK_COLUMN] = + g_signal_new ("double-click-column", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + + /** + * PsppireSheet::button-event-column + * @sheet: the sheet widget that emitted the signal + * @column: the column on which the event occured. + * + * A button event occured on a column title button + */ + sheet_signals[BUTTON_EVENT_COLUMN] = + g_signal_new ("button-event-column", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + psppire_marshal_VOID__INT_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_POINTER + ); + + + /** + * PsppireSheet::button-event-row + * @sheet: the sheet widget that emitted the signal + * @column: the column on which the event occured. + * + * A button event occured on a row title button + */ + sheet_signals[BUTTON_EVENT_ROW] = + g_signal_new ("button-event-row", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + psppire_marshal_VOID__INT_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_POINTER + ); + + + sheet_signals[SELECT_RANGE] = + g_signal_new ("select-range", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, select_range), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, + PSPPIRE_TYPE_SHEET_RANGE); + + + sheet_signals[RESIZE_RANGE] = + g_signal_new ("resize-range", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, resize_range), + NULL, NULL, + psppire_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, + 2, + PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE + ); + + sheet_signals[MOVE_RANGE] = + g_signal_new ("move-range", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, move_range), + NULL, NULL, + psppire_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, + 2, + PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE + ); + + sheet_signals[TRAVERSE] = + g_signal_new ("traverse", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, traverse), + NULL, NULL, + psppire_marshal_BOOLEAN__BOXED_POINTER, + G_TYPE_BOOLEAN, 2, + PSPPIRE_TYPE_SHEET_CELL, + G_TYPE_POINTER); + + + sheet_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, activate), + NULL, NULL, + psppire_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, G_TYPE_INT, + G_TYPE_INT, G_TYPE_INT); + + widget_class->set_scroll_adjustments_signal = + g_signal_new ("set-scroll-adjustments", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + offsetof (PsppireSheetClass, set_scroll_adjustments), + NULL, NULL, + psppire_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + + + container_class->add = NULL; + container_class->remove = NULL; + container_class->forall = psppire_sheet_forall; + container_class->set_focus_child = NULL; + + object_class->dispose = psppire_sheet_dispose; + object_class->finalize = psppire_sheet_finalize; + + cell_padding_spec = + g_param_spec_boxed ("cell-padding", + "Cell Padding", + "The space between a cell's contents and its border", + GTK_TYPE_BORDER, + G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE); + + vaxis_spec = + g_param_spec_pointer ("vertical-axis", + "Vertical Axis", + "A pointer to the PsppireAxis object for the rows", + G_PARAM_READABLE | G_PARAM_WRITABLE ); + + haxis_spec = + g_param_spec_pointer ("horizontal-axis", + "Horizontal Axis", + "A pointer to the PsppireAxis object for the columns", + G_PARAM_READABLE | G_PARAM_WRITABLE ); + + model_spec = + g_param_spec_pointer ("model", + "Model", + "A pointer to the data model", + G_PARAM_READABLE | G_PARAM_WRITABLE ); + + + object_class->set_property = psppire_sheet_set_property; + object_class->get_property = psppire_sheet_get_property; + + g_object_class_install_property (object_class, + PROP_VAXIS, + vaxis_spec); + + g_object_class_install_property (object_class, + PROP_HAXIS, + haxis_spec); + + g_object_class_install_property (object_class, + PROP_CELL_PADDING, + cell_padding_spec); + + g_object_class_install_property (object_class, + PROP_MODEL, + model_spec); + + + widget_class->realize = psppire_sheet_realize; + widget_class->unrealize = psppire_sheet_unrealize; + widget_class->map = psppire_sheet_map; + widget_class->unmap = psppire_sheet_unmap; + widget_class->style_set = psppire_sheet_style_set; + widget_class->button_press_event = psppire_sheet_button_press; + widget_class->button_release_event = psppire_sheet_button_release; + widget_class->motion_notify_event = psppire_sheet_motion; + widget_class->enter_notify_event = psppire_sheet_crossing_notify; + widget_class->leave_notify_event = psppire_sheet_crossing_notify; + widget_class->key_press_event = psppire_sheet_key_press; + widget_class->expose_event = psppire_sheet_expose; + widget_class->size_request = psppire_sheet_size_request; + widget_class->size_allocate = psppire_sheet_size_allocate; + widget_class->focus_in_event = psppire_sheet_focus_in; + widget_class->focus_out_event = NULL; + + klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments; + klass->select_row = NULL; + klass->select_column = NULL; + klass->select_range = NULL; + klass->resize_range = NULL; + klass->move_range = NULL; + klass->traverse = NULL; + klass->activate = NULL; + klass->changed = NULL; +} + +static void +psppire_sheet_init (PsppireSheet *sheet) +{ + sheet->model = NULL; + sheet->haxis = NULL; + sheet->vaxis = NULL; + + sheet->flags = 0; + sheet->selection_mode = GTK_SELECTION_NONE; + sheet->state = PSPPIRE_SHEET_NORMAL; + + GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW); + GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS); + + sheet->column_title_window = NULL; + sheet->column_title_area.x = 0; + sheet->column_title_area.y = 0; + sheet->column_title_area.width = 0; + sheet->column_title_area.height = DEFAULT_ROW_HEIGHT; + + sheet->row_title_window = NULL; + sheet->row_title_area.x = 0; + sheet->row_title_area.y = 0; + sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH; + sheet->row_title_area.height = 0; + + + sheet->active_cell.row = 0; + sheet->active_cell.col = 0; + sheet->selection_cell.row = 0; + sheet->selection_cell.col = 0; + + sheet->range.row0 = 0; + sheet->range.rowi = 0; + sheet->range.col0 = 0; + sheet->range.coli = 0; + + sheet->state = PSPPIRE_SHEET_NORMAL; + + sheet->sheet_window = NULL; + sheet->entry_widget = NULL; + sheet->button = NULL; + + sheet->hadjustment = NULL; + sheet->vadjustment = NULL; + + sheet->cursor_drag = NULL; + + sheet->xor_gc = NULL; + sheet->fg_gc = NULL; + sheet->bg_gc = NULL; + sheet->x_drag = 0; + sheet->y_drag = 0; + sheet->show_grid = TRUE; + + sheet->motion_timer = 0; + + sheet->row_titles_visible = TRUE; + sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH; + + sheet->column_titles_visible = TRUE; + + + /* create sheet entry */ + sheet->entry_type = GTK_TYPE_ENTRY; + create_sheet_entry (sheet); + + /* create global selection button */ + create_global_button (sheet); +} + + +/* Cause RANGE to be redrawn. If RANGE is null, then the + entire visible range will be redrawn. + */ +static void +redraw_range (PsppireSheet *sheet, PsppireSheetRange *range) +{ + GdkRectangle rect; + + if ( ! GTK_WIDGET_REALIZED (sheet)) + return; + + if ( NULL != range ) + rectangle_from_range (sheet, range, &rect); + else + { + GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window); + gdk_region_get_clipbox (r, &rect); + + if ( sheet->column_titles_visible) + { + rect.y += sheet->column_title_area.height; + rect.height -= sheet->column_title_area.height; + } + + if ( sheet->row_titles_visible) + { + rect.x += sheet->row_title_area.width; + rect.width -= sheet->row_title_area.width; + } + } + + gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE); +} + + +/* Callback which occurs whenever columns are inserted / deleted in the model */ +static void +columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column, + gint n_columns, + gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + PsppireSheetRange range; + gint model_columns = psppire_sheet_model_get_column_count (model); + + + /* Need to update all the columns starting from the first column and onwards. + * Previous column are unchanged, so don't need to be updated. + */ + range.col0 = first_column; + range.row0 = 0; + range.coli = psppire_axis_unit_count (sheet->haxis) - 1; + range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + + adjust_scrollbars (sheet); + + if (sheet->active_cell.col >= model_columns) + change_active_cell (sheet, sheet->active_cell.row, model_columns - 1); + + draw_column_title_buttons_range (sheet, + first_column, max_visible_column (sheet)); + + + redraw_range (sheet, &range); +} + + + + +/* Callback which occurs whenever rows are inserted / deleted in the model */ +static void +rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row, + gint n_rows, gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + PsppireSheetRange range; + + gint model_rows = psppire_sheet_model_get_row_count (model); + + /* Need to update all the rows starting from the first row and onwards. + * Previous rows are unchanged, so don't need to be updated. + */ + range.row0 = first_row; + range.col0 = 0; + range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + range.coli = psppire_axis_unit_count (sheet->haxis) - 1; + + adjust_scrollbars (sheet); + + if (sheet->active_cell.row >= model_rows) + change_active_cell (sheet, model_rows - 1, sheet->active_cell.col); + + draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet)); + + redraw_range (sheet, &range); +} + +/* + If row0 or rowi are negative, then all rows will be updated. + If col0 or coli are negative, then all columns will be updated. +*/ +static void +range_update_callback (PsppireSheetModel *m, gint row0, gint col0, + gint rowi, gint coli, gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + PsppireSheetRange range; + + range.row0 = row0; + range.col0 = col0; + range.rowi = rowi; + range.coli = coli; + + if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) ) + { + redraw_range (sheet, NULL); + adjust_scrollbars (sheet); + + draw_row_title_buttons_range (sheet, min_visible_row (sheet), + max_visible_row (sheet)); + + draw_column_title_buttons_range (sheet, min_visible_column (sheet), + max_visible_column (sheet)); + + return; + } + else if ( row0 < 0 || rowi < 0 ) + { + range.row0 = min_visible_row (sheet); + range.rowi = max_visible_row (sheet); + } + else if ( col0 < 0 || coli < 0 ) + { + range.col0 = min_visible_column (sheet); + range.coli = max_visible_column (sheet); + } + + redraw_range (sheet, &range); +} + + +/** + * psppire_sheet_new: + * @rows: initial number of rows + * @columns: initial number of columns + * @title: sheet title + * @model: the model to use for the sheet data + * + * Creates a new sheet widget with the given number of rows and columns. + * + * Returns: the new sheet widget + */ +GtkWidget * +psppire_sheet_new (PsppireSheetModel *model) +{ + GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET, + "model", model, + NULL); + return widget; +} + + +/** + * psppire_sheet_set_model + * @sheet: the sheet to set the model for + * @model: the model to use for the sheet data + * + * Sets the model for a PsppireSheet + * + */ +void +psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model) +{ + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (sheet->model ) g_object_unref (sheet->model); + + sheet->model = model; + + if ( model) + { + g_object_ref (model); + + sheet->update_handler_id = g_signal_connect (model, "range_changed", + G_CALLBACK (range_update_callback), + sheet); + + g_signal_connect (model, "rows_inserted", + G_CALLBACK (rows_inserted_deleted_callback), sheet); + + g_signal_connect (model, "rows_deleted", + G_CALLBACK (rows_inserted_deleted_callback), sheet); + + g_signal_connect (model, "columns_inserted", + G_CALLBACK (columns_inserted_deleted_callback), sheet); + + g_signal_connect (model, "columns_deleted", + G_CALLBACK (columns_inserted_deleted_callback), sheet); + } +} + + +void +psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type) +{ + gint state; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + state = sheet->state; + + if (sheet->state == PSPPIRE_SHEET_NORMAL) + psppire_sheet_hide_entry_widget (sheet); + + sheet->entry_type = entry_type; + + create_sheet_entry (sheet); + + if (state == PSPPIRE_SHEET_NORMAL) + { + psppire_sheet_show_entry_widget (sheet); + } + +} + +void +psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (show == sheet->show_grid) return; + + sheet->show_grid = show; + + redraw_range (sheet, NULL); +} + +gboolean +psppire_sheet_grid_visible (PsppireSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + return sheet->show_grid; +} + +guint +psppire_sheet_get_columns_count (PsppireSheet *sheet) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + return psppire_axis_unit_count (sheet->haxis); +} + +static void set_column_width (PsppireSheet *sheet, + gint column, + gint width); + + +void +psppire_sheet_show_column_titles (PsppireSheet *sheet) +{ + if (sheet->column_titles_visible) return; + + sheet->column_titles_visible = TRUE; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + gdk_window_show (sheet->column_title_window); + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + + adjust_scrollbars (sheet); + + if (sheet->vadjustment) + g_signal_emit_by_name (sheet->vadjustment, + "value_changed"); + + size_allocate_global_button (sheet); + + if ( sheet->row_titles_visible) + gtk_widget_show (sheet->button); +} + + +void +psppire_sheet_show_row_titles (PsppireSheet *sheet) +{ + if (sheet->row_titles_visible) return; + + sheet->row_titles_visible = TRUE; + + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + { + gdk_window_show (sheet->row_title_window); + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + + adjust_scrollbars (sheet); + } + + if (sheet->hadjustment) + g_signal_emit_by_name (sheet->hadjustment, + "value_changed"); + + size_allocate_global_button (sheet); + + if ( sheet->column_titles_visible) + gtk_widget_show (sheet->button); +} + +void +psppire_sheet_hide_column_titles (PsppireSheet *sheet) +{ + if (!sheet->column_titles_visible) return; + + sheet->column_titles_visible = FALSE; + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + { + if (sheet->column_title_window) + gdk_window_hide (sheet->column_title_window); + + gtk_widget_hide (sheet->button); + + adjust_scrollbars (sheet); + } + + if (sheet->vadjustment) + g_signal_emit_by_name (sheet->vadjustment, + "value_changed"); +} + +void +psppire_sheet_hide_row_titles (PsppireSheet *sheet) +{ + if (!sheet->row_titles_visible) return; + + sheet->row_titles_visible = FALSE; + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + { + if (sheet->row_title_window) + gdk_window_hide (sheet->row_title_window); + + gtk_widget_hide (sheet->button); + + adjust_scrollbars (sheet); + } + + if (sheet->hadjustment) + g_signal_emit_by_name (sheet->hadjustment, + "value_changed"); +} + + +/* Scroll the sheet so that the cell ROW, COLUMN is visible. + If {ROW,COL}_ALIGN is zero, then the cell will be placed + at the {top,left} of the sheet. If it's 1, then it'll + be placed at the {bottom,right}. + ROW or COL may be -1, in which case scrolling in that dimension + does not occur. + */ +void +psppire_sheet_moveto (PsppireSheet *sheet, + gint row, + gint col, + gfloat row_align, + gfloat col_align) +{ + gint width, height; + + g_return_if_fail (row_align >= 0); + g_return_if_fail (col_align >= 0); + + g_return_if_fail (row_align <= 1); + g_return_if_fail (col_align <= 1); + + g_return_if_fail (col < + psppire_axis_unit_count (sheet->haxis)); + g_return_if_fail (row < + psppire_axis_unit_count (sheet->vaxis)); + + gdk_drawable_get_size (sheet->sheet_window, &width, &height); + + + if (row >= 0) + { + gint y = psppire_axis_start_pixel (sheet->vaxis, row); + + gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align); + } + + + if (col >= 0) + { + gint x = psppire_axis_start_pixel (sheet->haxis, col); + + gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align); + } +} + + +void +psppire_sheet_select_row (PsppireSheet *sheet, gint row) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->state = PSPPIRE_SHEET_ROW_SELECTED; + sheet->range.row0 = row; + sheet->range.col0 = 0; + sheet->range.rowi = row; + sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1; + sheet->active_cell.row = row; + + g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row); + psppire_sheet_real_select_range (sheet, NULL); +} + + +void +psppire_sheet_select_column (PsppireSheet *sheet, gint column) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis)) + return; + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED; + sheet->range.row0 = 0; + sheet->range.col0 = column; + sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + sheet->range.coli = column; + sheet->active_cell.col = column; + + g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column); + psppire_sheet_real_select_range (sheet, NULL); +} + + + + +static gboolean +psppire_sheet_range_isvisible (const PsppireSheet *sheet, + const PsppireSheetRange *range) +{ + g_return_val_if_fail (sheet != NULL, FALSE); + + if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis)) + return FALSE; + + if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis)) + return FALSE; + + if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis)) + return FALSE; + + if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis)) + return FALSE; + + if (range->rowi < min_visible_row (sheet)) + return FALSE; + + if (range->row0 > max_visible_row (sheet)) + return FALSE; + + if (range->coli < min_visible_column (sheet)) + return FALSE; + + if (range->col0 > max_visible_column (sheet)) + return FALSE; + + return TRUE; +} + +static gboolean +psppire_sheet_cell_isvisible (PsppireSheet *sheet, + gint row, gint column) +{ + PsppireSheetRange range; + + range.row0 = row; + range.col0 = column; + range.rowi = row; + range.coli = column; + + return psppire_sheet_range_isvisible (sheet, &range); +} + +void +psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ; + g_return_if_fail (range != NULL); + + range->row0 = min_visible_row (sheet); + range->col0 = min_visible_column (sheet); + range->rowi = max_visible_row (sheet); + range->coli = max_visible_column (sheet); +} + + +static gboolean +psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + if ( sheet->vadjustment != vadjustment ) + { + if (sheet->vadjustment) + g_object_unref (sheet->vadjustment); + sheet->vadjustment = vadjustment; + + if ( vadjustment) + { + g_object_ref (vadjustment); + + g_signal_connect (sheet->vadjustment, "value_changed", + G_CALLBACK (vadjustment_value_changed), + sheet); + } + } + + if ( sheet->hadjustment != hadjustment ) + { + if (sheet->hadjustment) + g_object_unref (sheet->hadjustment); + + sheet->hadjustment = hadjustment; + + if ( hadjustment) + { + g_object_ref (hadjustment); + + g_signal_connect (sheet->hadjustment, "value_changed", + G_CALLBACK (hadjustment_value_changed), + sheet); + } + } + return TRUE; +} + +static void +psppire_sheet_finalize (GObject *object) +{ + PsppireSheet *sheet; + + g_return_if_fail (object != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (object)); + + sheet = PSPPIRE_SHEET (object); + + if (G_OBJECT_CLASS (parent_class)->finalize) + (*G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +psppire_sheet_dispose (GObject *object) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (object); + + g_return_if_fail (object != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (object)); + + if ( sheet->dispose_has_run ) + return ; + + sheet->dispose_has_run = TRUE; + + if ( sheet->cell_padding) + g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding); + + if (sheet->model) g_object_unref (sheet->model); + if (sheet->vaxis) g_object_unref (sheet->vaxis); + if (sheet->haxis) g_object_unref (sheet->haxis); + + g_object_unref (sheet->button); + sheet->button = NULL; + + /* unref adjustments */ + if (sheet->hadjustment) + { + g_signal_handlers_disconnect_matched (sheet->hadjustment, + G_SIGNAL_MATCH_DATA, + 0, 0, 0, 0, + sheet); + + g_object_unref (sheet->hadjustment); + sheet->hadjustment = NULL; + } + + if (sheet->vadjustment) + { + g_signal_handlers_disconnect_matched (sheet->vadjustment, + G_SIGNAL_MATCH_DATA, + 0, 0, 0, 0, + sheet); + + g_object_unref (sheet->vadjustment); + + sheet->vadjustment = NULL; + } + + if (G_OBJECT_CLASS (parent_class)->dispose) + (*G_OBJECT_CLASS (parent_class)->dispose) (object); +} + +static void +psppire_sheet_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + PsppireSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + if (GTK_WIDGET_CLASS (parent_class)->style_set) + (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style); + + sheet = PSPPIRE_SHEET (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + gtk_style_set_background (widget->style, widget->window, widget->state); + } + + set_entry_widget_font (sheet); +} + +#define BORDER_WIDTH 3 + +static void +psppire_sheet_realize (GtkWidget *widget) +{ + PsppireSheet *sheet; + GdkWindowAttr attributes; + const gint attributes_mask = + GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR; + + GdkGCValues values; + GdkColormap *colormap; + GdkDisplay *display; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + sheet = PSPPIRE_SHEET (widget); + + colormap = gtk_widget_get_colormap (widget); + display = gtk_widget_get_display (widget); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = colormap; + + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); + + attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW); + + /* main window */ + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); + + gdk_window_set_user_data (widget->window, sheet); + + widget->style = gtk_style_attach (widget->style, widget->window); + + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + + gdk_color_parse ("white", &sheet->color[BG_COLOR]); + gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE, + TRUE); + gdk_color_parse ("gray", &sheet->color[GRID_COLOR]); + gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE, + TRUE); + + attributes.x = 0; + attributes.y = 0; + attributes.width = sheet->column_title_area.width; + attributes.height = sheet->column_title_area.height; + + + /* column - title window */ + sheet->column_title_window = + gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (sheet->column_title_window, sheet); + gtk_style_set_background (widget->style, sheet->column_title_window, + GTK_STATE_NORMAL); + + + attributes.x = 0; + attributes.y = 0; + attributes.width = sheet->row_title_area.width; + attributes.height = sheet->row_title_area.height; + + /* row - title window */ + sheet->row_title_window = gdk_window_new (widget->window, + &attributes, attributes_mask); + gdk_window_set_user_data (sheet->row_title_window, sheet); + gtk_style_set_background (widget->style, sheet->row_title_window, + GTK_STATE_NORMAL); + + /* sheet - window */ + attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS); + + attributes.x = 0; + attributes.y = 0; + + sheet->sheet_window = gdk_window_new (widget->window, + &attributes, attributes_mask); + gdk_window_set_user_data (sheet->sheet_window, sheet); + + gdk_cursor_unref (attributes.cursor); + + gdk_window_set_background (sheet->sheet_window, &widget->style->white); + gdk_window_show (sheet->sheet_window); + + /* GCs */ + sheet->fg_gc = gdk_gc_new (widget->window); + sheet->bg_gc = gdk_gc_new (widget->window); + + values.foreground = widget->style->white; + values.function = GDK_INVERT; + values.subwindow_mode = GDK_INCLUDE_INFERIORS; + values.line_width = BORDER_WIDTH; + + sheet->xor_gc = gdk_gc_new_with_values (widget->window, + &values, + GDK_GC_FOREGROUND | + GDK_GC_FUNCTION | + GDK_GC_SUBWINDOW | + GDK_GC_LINE_WIDTH + ); + + + gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); + gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); + + gtk_widget_set_parent_window (sheet->button, sheet->sheet_window); + gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet)); + + + sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS); + + if (sheet->column_titles_visible) + gdk_window_show (sheet->column_title_window); + if (sheet->row_titles_visible) + gdk_window_show (sheet->row_title_window); + + sheet->hover_window = create_hover_window (); + + draw_row_title_buttons (sheet); + draw_column_title_buttons (sheet); + + psppire_sheet_update_primary_selection (sheet); + + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); +} + +static void +create_global_button (PsppireSheet *sheet) +{ + sheet->button = gtk_button_new_with_label (" "); + + GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS); + + g_object_ref_sink (sheet->button); + + g_signal_connect (sheet->button, + "pressed", + G_CALLBACK (global_button_clicked), + sheet); +} + +static void +size_allocate_global_button (PsppireSheet *sheet) +{ + GtkAllocation allocation; + + if (!sheet->column_titles_visible) return; + if (!sheet->row_titles_visible) return; + + gtk_widget_size_request (sheet->button, NULL); + + allocation.x = 0; + allocation.y = 0; + allocation.width = sheet->row_title_area.width; + allocation.height = sheet->column_title_area.height; + + gtk_widget_size_allocate (sheet->button, &allocation); +} + +static void +global_button_clicked (GtkWidget *widget, gpointer data) +{ + psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1); +} + + +static void +psppire_sheet_unrealize (GtkWidget *widget) +{ + PsppireSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + sheet = PSPPIRE_SHEET (widget); + + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = NULL; + + gdk_colormap_free_colors (gtk_widget_get_colormap (widget), + sheet->color, n_COLORS); + + g_object_unref (sheet->xor_gc); + g_object_unref (sheet->fg_gc); + g_object_unref (sheet->bg_gc); + + destroy_hover_window (sheet->hover_window); + + gdk_window_destroy (sheet->sheet_window); + gdk_window_destroy (sheet->column_title_window); + gdk_window_destroy (sheet->row_title_window); + + gtk_widget_unparent (sheet->entry_widget); + if (sheet->button != NULL) + gtk_widget_unparent (sheet->button); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +psppire_sheet_map (GtkWidget *widget) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + + if (!GTK_WIDGET_MAPPED (widget)) + { + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + + gdk_window_show (widget->window); + gdk_window_show (sheet->sheet_window); + + if (sheet->column_titles_visible) + { + draw_column_title_buttons (sheet); + gdk_window_show (sheet->column_title_window); + } + if (sheet->row_titles_visible) + { + draw_row_title_buttons (sheet); + gdk_window_show (sheet->row_title_window); + } + + if (!GTK_WIDGET_MAPPED (sheet->entry_widget) + && sheet->active_cell.row >= 0 + && sheet->active_cell.col >= 0 ) + { + gtk_widget_show (sheet->entry_widget); + gtk_widget_map (sheet->entry_widget); + } + + if (!GTK_WIDGET_MAPPED (sheet->button)) + { + gtk_widget_show (sheet->button); + gtk_widget_map (sheet->button); + } + + redraw_range (sheet, NULL); + change_active_cell (sheet, + sheet->active_cell.row, + sheet->active_cell.col); + } +} + +static void +psppire_sheet_unmap (GtkWidget *widget) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + if (!GTK_WIDGET_MAPPED (widget)) + return; + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); + + gdk_window_hide (sheet->sheet_window); + if (sheet->column_titles_visible) + gdk_window_hide (sheet->column_title_window); + if (sheet->row_titles_visible) + gdk_window_hide (sheet->row_title_window); + gdk_window_hide (widget->window); + + if (GTK_WIDGET_MAPPED (sheet->entry_widget)) + gtk_widget_unmap (sheet->entry_widget); + + if (GTK_WIDGET_MAPPED (sheet->button)) + gtk_widget_unmap (sheet->button); +} + +/* get cell attributes of the given cell */ +/* TRUE means that the cell is currently allocated */ +static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet, + gint row, gint col, + PsppireSheetCellAttr *attributes); + + + +static void +psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col) +{ + PangoLayout *layout; + PangoRectangle text; + PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc; + gint font_height; + + gchar *label; + + PsppireSheetCellAttr attributes; + GdkRectangle area; + + g_return_if_fail (sheet != NULL); + + /* bail now if we aren't yet drawable */ + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + if (row < 0 || + row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (col < 0 || + col >= psppire_axis_unit_count (sheet->haxis)) + return; + + psppire_sheet_get_attributes (sheet, row, col, &attributes); + + /* select GC for background rectangle */ + gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground); + gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); + + rectangle_from_cell (sheet, row, col, &area); + + gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0); + + if (sheet->show_grid) + { + gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]); + + gdk_draw_rectangle (sheet->sheet_window, + sheet->bg_gc, + FALSE, + area.x, area.y, + area.width, area.height); + } + + + label = psppire_sheet_cell_get_text (sheet, row, col); + if (NULL == label) + return; + + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label); + dispose_string (sheet, label); + + + pango_layout_set_font_description (layout, font_desc); + + pango_layout_get_pixel_extents (layout, NULL, &text); + + gdk_gc_set_clip_rectangle (sheet->fg_gc, &area); + + font_height = pango_font_description_get_size (font_desc); + if ( !pango_font_description_get_size_is_absolute (font_desc)) + font_height /= PANGO_SCALE; + + + if ( sheet->cell_padding ) + { + area.x += sheet->cell_padding->left; + area.width -= sheet->cell_padding->right + + sheet->cell_padding->left; + + area.y += sheet->cell_padding->top; + area.height -= sheet->cell_padding->bottom + + + sheet->cell_padding->top; + } + + /* Centre the text vertically */ + area.y += (area.height - font_height) / 2.0; + + switch (attributes.justification) + { + case GTK_JUSTIFY_RIGHT: + area.x += area.width - text.width; + break; + case GTK_JUSTIFY_CENTER: + area.x += (area.width - text.width) / 2.0; + break; + case GTK_JUSTIFY_LEFT: + /* Do nothing */ + break; + default: + g_critical ("Unhandled justification %d in column %d\n", + attributes.justification, col); + break; + } + + gdk_draw_layout (sheet->sheet_window, sheet->fg_gc, + area.x, + area.y, + layout); + + gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL); + g_object_unref (layout); +} + + +static void +draw_sheet_region (PsppireSheet *sheet, GdkRegion *region) +{ + PsppireSheetRange range; + GdkRectangle area; + gint y, x; + gint i, j; + + PsppireSheetRange drawing_range; + + gdk_region_get_clipbox (region, &area); + + y = area.y + sheet->vadjustment->value; + x = area.x + sheet->hadjustment->value; + + if ( sheet->column_titles_visible) + y -= sheet->column_title_area.height; + + if ( sheet->row_titles_visible) + x -= sheet->row_title_area.width; + + maximize_int (&x, 0); + maximize_int (&y, 0); + + range.row0 = row_from_ypixel (sheet, y); + range.rowi = row_from_ypixel (sheet, y + area.height); + + range.col0 = column_from_xpixel (sheet, x); + range.coli = column_from_xpixel (sheet, x + area.width); + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_SHEET (sheet)); + + if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return; + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return; + + + drawing_range.row0 = MAX (range.row0, min_visible_row (sheet)); + drawing_range.col0 = MAX (range.col0, min_visible_column (sheet)); + drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet)); + drawing_range.coli = MIN (range.coli, max_visible_column (sheet)); + + g_return_if_fail (drawing_range.rowi >= drawing_range.row0); + g_return_if_fail (drawing_range.coli >= drawing_range.col0); + + for (i = drawing_range.row0; i <= drawing_range.rowi; i++) + { + for (j = drawing_range.col0; j <= drawing_range.coli; j++) + psppire_sheet_cell_draw (sheet, i, j); + } + + if (sheet->state != PSPPIRE_SHEET_NORMAL && + psppire_sheet_range_isvisible (sheet, &sheet->range)) + psppire_sheet_range_draw_selection (sheet, drawing_range); + + + if (sheet->state == GTK_STATE_NORMAL && + sheet->active_cell.row >= drawing_range.row0 && + sheet->active_cell.row <= drawing_range.rowi && + sheet->active_cell.col >= drawing_range.col0 && + sheet->active_cell.col <= drawing_range.coli) + psppire_sheet_show_entry_widget (sheet); +} + + +static void +psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range) +{ + GdkRectangle area; + gint i, j; + PsppireSheetRange aux; + + if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 || + range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0) + return; + + if (!psppire_sheet_range_isvisible (sheet, &range)) return; + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + + aux = range; + + range.col0 = MAX (sheet->range.col0, range.col0); + range.coli = MIN (sheet->range.coli, range.coli); + range.row0 = MAX (sheet->range.row0, range.row0); + range.rowi = MIN (sheet->range.rowi, range.rowi); + + range.col0 = MAX (range.col0, min_visible_column (sheet)); + range.coli = MIN (range.coli, max_visible_column (sheet)); + range.row0 = MAX (range.row0, min_visible_row (sheet)); + range.rowi = MIN (range.rowi, max_visible_row (sheet)); + + for (i = range.row0; i <= range.rowi; i++) + { + for (j = range.col0; j <= range.coli; j++) + { + if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED) + { + rectangle_from_cell (sheet, i, j, &area); + + if (i == sheet->range.row0) + { + area.y = area.y + 2; + area.height = area.height - 2; + } + if (i == sheet->range.rowi) area.height = area.height - 3; + if (j == sheet->range.col0) + { + area.x = area.x + 2; + area.width = area.width - 2; + } + if (j == sheet->range.coli) area.width = area.width - 3; + + if (i != sheet->active_cell.row || j != sheet->active_cell.col) + { + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + area.x + 1, area.y + 1, + area.width, area.height); + } + } + + } + } + + psppire_sheet_draw_border (sheet, sheet->range); +} + +static inline gint +safe_strcmp (const gchar *s1, const gchar *s2) +{ + if ( !s1 && !s2) return 0; + if ( !s1) return -1; + if ( !s2) return +1; + return strcmp (s1, s2); +} + +static void +psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col, + GtkJustification justification, + const gchar *text) +{ + PsppireSheetModel *model ; + gchar *old_text ; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (col >= psppire_axis_unit_count (sheet->haxis) + || row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (col < 0 || row < 0) return; + + model = psppire_sheet_get_model (sheet); + + old_text = psppire_sheet_model_get_string (model, row, col); + + if (0 != safe_strcmp (old_text, text)) + { + g_signal_handler_block (sheet->model, sheet->update_handler_id); + psppire_sheet_model_set_string (model, text, row, col); + g_signal_handler_unblock (sheet->model, sheet->update_handler_id); + } + + if ( psppire_sheet_model_free_strings (model)) + g_free (old_text); +} + + +void +psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetRange range; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + if (column >= psppire_axis_unit_count (sheet->haxis) || + row >= psppire_axis_unit_count (sheet->vaxis)) return; + + if (column < 0 || row < 0) return; + + range.row0 = row; + range.rowi = row; + range.col0 = min_visible_column (sheet); + range.coli = max_visible_column (sheet); + + psppire_sheet_real_cell_clear (sheet, row, column); + + redraw_range (sheet, &range); +} + +static void +psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetModel *model = psppire_sheet_get_model (sheet); + + gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column); + + if (old_text && strlen (old_text) > 0 ) + { + psppire_sheet_model_datum_clear (model, row, column); + } + + dispose_string (sheet, old_text); +} + +gchar * +psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col) +{ + PsppireSheetModel *model; + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); + + if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) + return NULL; + if (col < 0 || row < 0) return NULL; + + model = psppire_sheet_get_model (sheet); + + if ( !model ) + return NULL; + + return psppire_sheet_model_get_string (model, row, col); +} + + +static GtkStateType +psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col) +{ + gint state; + PsppireSheetRange *range; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0; + if (col < 0 || row < 0) return 0; + + state = sheet->state; + range = &sheet->range; + + switch (state) + { + case PSPPIRE_SHEET_NORMAL: + return GTK_STATE_NORMAL; + break; + case PSPPIRE_SHEET_ROW_SELECTED: + if (row >= range->row0 && row <= range->rowi) + return GTK_STATE_SELECTED; + break; + case PSPPIRE_SHEET_COLUMN_SELECTED: + if (col >= range->col0 && col <= range->coli) + return GTK_STATE_SELECTED; + break; + case PSPPIRE_SHEET_RANGE_SELECTED: + if (row >= range->row0 && row <= range->rowi && \ + col >= range->col0 && col <= range->coli) + return GTK_STATE_SELECTED; + break; + } + return GTK_STATE_NORMAL; +} + +/* Convert X, Y (in pixels) to *ROW, *COLUMN + If the function returns FALSE, then the results will be unreliable. +*/ +static gboolean +psppire_sheet_get_pixel_info (PsppireSheet *sheet, + gint x, + gint y, + gint *row, + gint *column) +{ + gint trow, tcol; + *row = -G_MAXINT; + *column = -G_MAXINT; + + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + /* bounds checking, return false if the user clicked + on a blank area */ + if (y < 0) + return FALSE; + + if (x < 0) + return FALSE; + + if ( sheet->column_titles_visible) + y -= sheet->column_title_area.height; + + y += sheet->vadjustment->value; + + if ( y < 0 && sheet->column_titles_visible) + { + trow = -1; + } + else + { + trow = row_from_ypixel (sheet, y); + if (trow > psppire_axis_unit_count (sheet->vaxis)) + return FALSE; + } + + *row = trow; + + if ( sheet->row_titles_visible) + x -= sheet->row_title_area.width; + + x += sheet->hadjustment->value; + + if ( x < 0 && sheet->row_titles_visible) + { + tcol = -1; + } + else + { + tcol = column_from_xpixel (sheet, x); + if (tcol > psppire_axis_unit_count (sheet->haxis)) + return FALSE; + } + + *column = tcol; + + return TRUE; +} + +gboolean +psppire_sheet_get_cell_area (PsppireSheet *sheet, + gint row, + gint column, + GdkRectangle *area) +{ + g_return_val_if_fail (sheet != NULL, 0); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0); + + if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis)) + return FALSE; + + area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column); + area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row); + + area->width= (column == -1) ? sheet->row_title_area.width + : psppire_axis_unit_size (sheet->haxis, column); + + area->height= (row == -1) ? sheet->column_title_area.height + : psppire_axis_unit_size (sheet->vaxis, row); + + return TRUE; +} + +void +psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < -1 || col < -1) + return; + + if (row >= psppire_axis_unit_count (sheet->vaxis) + || + col >= psppire_axis_unit_count (sheet->haxis)) + return; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + if ( row == -1 || col == -1) + { + psppire_sheet_hide_entry_widget (sheet); + return; + } + + change_active_cell (sheet, row, col); +} + +void +psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if ( row ) *row = sheet->active_cell.row; + if (column) *column = sheet->active_cell.col; +} + +static void +entry_load_text (PsppireSheet *sheet) +{ + gint row, col; + const char *text; + GtkJustification justification; + PsppireSheetCellAttr attributes; + + if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return; + if (sheet->state != GTK_STATE_NORMAL) return; + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + if (row < 0 || col < 0) return; + + text = gtk_entry_get_text (psppire_sheet_get_entry (sheet)); + + if (text && strlen (text) > 0) + { + psppire_sheet_get_attributes (sheet, row, col, &attributes); + justification = attributes.justification; + psppire_sheet_set_cell (sheet, row, col, justification, text); + } +} + + +static void +psppire_sheet_hide_entry_widget (PsppireSheet *sheet) +{ + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + if (sheet->active_cell.row < 0 || + sheet->active_cell.col < 0) return; + + gtk_widget_hide (sheet->entry_widget); + gtk_widget_unmap (sheet->entry_widget); + + GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); +} + +static void +change_active_cell (PsppireSheet *sheet, gint row, gint col) +{ + gint old_row, old_col; + + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < 0 || col < 0) + return; + + if ( row > psppire_axis_unit_count (sheet->vaxis) + || col > psppire_axis_unit_count (sheet->haxis)) + return; + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + { + sheet->state = PSPPIRE_SHEET_NORMAL; + psppire_sheet_real_unselect_range (sheet, NULL); + } + + old_row = sheet->active_cell.row; + old_col = sheet->active_cell.col; + + /* Erase the old cell */ + psppire_sheet_draw_active_cell (sheet); + + entry_load_text (sheet); + + sheet->range.row0 = row; + sheet->range.col0 = col; + sheet->range.rowi = row; + sheet->range.coli = col; + sheet->active_cell.row = row; + sheet->active_cell.col = col; + sheet->selection_cell.row = row; + sheet->selection_cell.col = col; + + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS); + + psppire_sheet_draw_active_cell (sheet); + psppire_sheet_show_entry_widget (sheet); + + GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS); + + g_signal_emit (sheet, sheet_signals [ACTIVATE], 0, + row, col, old_row, old_col); + +} + +static void +psppire_sheet_show_entry_widget (PsppireSheet *sheet) +{ + GtkEntry *sheet_entry; + PsppireSheetCellAttr attributes; + + gint row, col; + + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + /* Don't show the active cell, if there is no active cell: */ + if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */ + return; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + if (sheet->state != PSPPIRE_SHEET_NORMAL) return; + if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return; + + GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); + + sheet_entry = psppire_sheet_get_entry (sheet); + + psppire_sheet_get_attributes (sheet, row, col, &attributes); + + if (GTK_IS_ENTRY (sheet_entry)) + { + gchar *text = psppire_sheet_cell_get_text (sheet, row, col); + const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry)); + + if ( ! text ) + text = g_strdup (""); + + if (strcmp (old_text, text) != 0) + gtk_entry_set_text (sheet_entry, text); + + dispose_string (sheet, text); + + { + switch (attributes.justification) + { + case GTK_JUSTIFY_RIGHT: + gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0); + break; + case GTK_JUSTIFY_CENTER: + gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5); + break; + case GTK_JUSTIFY_LEFT: + default: + gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0); + break; + } + } + } + + psppire_sheet_size_allocate_entry (sheet); + + gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry), + psppire_sheet_model_is_editable (sheet->model, + row, col)); + gtk_widget_map (sheet->entry_widget); +} + +static gboolean +psppire_sheet_draw_active_cell (PsppireSheet *sheet) +{ + gint row, col; + PsppireSheetRange range; + + row = sheet->active_cell.row; + col = sheet->active_cell.col; + + if (row < 0 || col < 0) return FALSE; + + if (!psppire_sheet_cell_isvisible (sheet, row, col)) + return FALSE; + + range.col0 = range.coli = col; + range.row0 = range.rowi = row; + + psppire_sheet_draw_border (sheet, range); + + return FALSE; +} + + + +static void +psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range) +{ + gint i, j, mask1, mask2; + gint state, selected; + gint x, y, width, height; + PsppireSheetRange new_range, aux_range; + + g_return_if_fail (sheet != NULL); + + if (range == NULL) range=&sheet->range; + + new_range=*range; + + range->row0 = MIN (range->row0, sheet->range.row0); + range->rowi = MAX (range->rowi, sheet->range.rowi); + range->col0 = MIN (range->col0, sheet->range.col0); + range->coli = MAX (range->coli, sheet->range.coli); + + range->row0 = MAX (range->row0, min_visible_row (sheet)); + range->rowi = MIN (range->rowi, max_visible_row (sheet)); + range->col0 = MAX (range->col0, min_visible_column (sheet)); + range->coli = MIN (range->coli, max_visible_column (sheet)); + + aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet)); + aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet)); + aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet)); + aux_range.coli = MIN (new_range.coli, max_visible_column (sheet)); + + for (i = range->row0; i <= range->rowi; i++) + { + for (j = range->col0; j <= range->coli; j++) + { + + state = psppire_sheet_cell_get_state (sheet, i, j); + selected= (i <= new_range.rowi && i >= new_range.row0 && + j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE; + + if (state == GTK_STATE_SELECTED && selected && + (i == sheet->range.row0 || i == sheet->range.rowi || + j == sheet->range.col0 || j == sheet->range.coli || + i == new_range.row0 || i == new_range.rowi || + j == new_range.col0 || j == new_range.coli)) + { + + mask1 = i == sheet->range.row0 ? 1 : 0; + mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1; + mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1; + mask1 = j == sheet->range.coli ? mask1 + 8 : mask1; + + mask2 = i == new_range.row0 ? 1 : 0; + mask2 = i == new_range.rowi ? mask2 + 2 : mask2; + mask2 = j == new_range.col0 ? mask2 + 4 : mask2; + mask2 = j == new_range.coli ? mask2 + 8 : mask2; + + if (mask1 != mask2) + { + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j)- x+ + psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + if (i == sheet->range.row0) + { + y = y - 3; + height = height + 3; + } + if (i == sheet->range.rowi) height = height + 3; + if (j == sheet->range.col0) + { + x = x - 3; + width = width + 3; + } + if (j == sheet->range.coli) width = width + 3; + + if (i != sheet->active_cell.row || j != sheet->active_cell.col) + { + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j)- x+ + psppire_axis_unit_size (sheet->haxis, j); + + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + if (i == new_range.row0) + { + y = y+2; + height = height - 2; + } + if (i == new_range.rowi) height = height - 3; + if (j == new_range.col0) + { + x = x+2; + width = width - 2; + } + if (j == new_range.coli) width = width - 3; + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x + 1, y + 1, + width, height); + } + } + } + } + } + + for (i = range->row0; i <= range->rowi; i++) + { + for (j = range->col0; j <= range->coli; j++) + { + + state = psppire_sheet_cell_get_state (sheet, i, j); + selected= (i <= new_range.rowi && i >= new_range.row0 && + j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE; + + if (state == GTK_STATE_SELECTED && !selected) + { + + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + if (i == sheet->range.row0) + { + y = y - 3; + height = height + 3; + } + if (i == sheet->range.rowi) height = height + 3; + if (j == sheet->range.col0) + { + x = x - 3; + width = width + 3; + } + if (j == sheet->range.coli) width = width + 3; + + } + } + } + + for (i = range->row0; i <= range->rowi; i++) + { + for (j = range->col0; j <= range->coli; j++) + { + + state = psppire_sheet_cell_get_state (sheet, i, j); + selected= (i <= new_range.rowi && i >= new_range.row0 && + j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE; + + if (state != GTK_STATE_SELECTED && selected && + (i != sheet->active_cell.row || j != sheet->active_cell.col)) + { + + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i); + + if (i == new_range.row0) + { + y = y+2; + height = height - 2; + } + if (i == new_range.rowi) height = height - 3; + if (j == new_range.col0) + { + x = x+2; + width = width - 2; + } + if (j == new_range.coli) width = width - 3; + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x + 1, y + 1, + width, height); + + } + + } + } + + for (i = aux_range.row0; i <= aux_range.rowi; i++) + { + for (j = aux_range.col0; j <= aux_range.coli; j++) + { + state = psppire_sheet_cell_get_state (sheet, i, j); + + mask1 = i == sheet->range.row0 ? 1 : 0; + mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1; + mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1; + mask1 = j == sheet->range.coli ? mask1 + 8 : mask1; + + mask2 = i == new_range.row0 ? 1 : 0; + mask2 = i == new_range.rowi ? mask2 + 2 : mask2; + mask2 = j == new_range.col0 ? mask2 + 4 : mask2; + mask2 = j == new_range.coli ? mask2 + 8 : mask2; + if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED)) + { + x = psppire_axis_start_pixel (sheet->haxis, j); + y = psppire_axis_start_pixel (sheet->vaxis, i); + width = psppire_axis_unit_size (sheet->haxis, j); + height = psppire_axis_unit_size (sheet->vaxis, i); + if (mask2 & 1) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x + 1, y - 1, + width, 3); + + + if (mask2 & 2) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x + 1, y + height - 1, + width, 3); + + if (mask2 & 4) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x - 1, y + 1, + 3, height); + + + if (mask2 & 8) + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + TRUE, + x + width - 1, y + 1, + 3, height); + } + } + } + + *range = new_range; +} + + + +static void +psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range) +{ + GdkRectangle area; + + rectangle_from_range (sheet, &new_range, &area); + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + FALSE, + area.x, area.y, + area.width, area.height); +} + + +static void +psppire_sheet_real_select_range (PsppireSheet *sheet, + const PsppireSheetRange *range) +{ + gint state; + + g_return_if_fail (sheet != NULL); + + if (range == NULL) range = &sheet->range; + + memcpy (&sheet->range, range, sizeof (*range)); + + if (range->row0 < 0 || range->rowi < 0) return; + if (range->col0 < 0 || range->coli < 0) return; + + state = sheet->state; + +#if 0 + if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 || + range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0) + { + psppire_sheet_new_selection (sheet, &sheet->range); + } + else + { + psppire_sheet_range_draw_selection (sheet, sheet->range); + } +#endif + + psppire_sheet_update_primary_selection (sheet); + + g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range); +} + + +void +psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + *range = sheet->range; +} + + +void +psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + + if (range == NULL) range=&sheet->range; + + if (range->row0 < 0 || range->rowi < 0) return; + if (range->col0 < 0 || range->coli < 0) return; + + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->range.row0 = range->row0; + sheet->range.rowi = range->rowi; + sheet->range.col0 = range->col0; + sheet->range.coli = range->coli; + sheet->active_cell.row = range->row0; + sheet->active_cell.col = range->col0; + sheet->selection_cell.row = range->rowi; + sheet->selection_cell.col = range->coli; + + sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + psppire_sheet_real_select_range (sheet, NULL); +} + +void +psppire_sheet_unselect_range (PsppireSheet *sheet) +{ + if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + psppire_sheet_real_unselect_range (sheet, NULL); + sheet->state = GTK_STATE_NORMAL; + + change_active_cell (sheet, + sheet->active_cell.row, sheet->active_cell.col); +} + + +static void +psppire_sheet_real_unselect_range (PsppireSheet *sheet, + const PsppireSheetRange *range) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))); + + if ( range == NULL) + range = &sheet->range; + + if (range->row0 < 0 || range->rowi < 0) return; + if (range->col0 < 0 || range->coli < 0) return; + + g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1); + g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1); + + sheet->range.row0 = -1; + sheet->range.rowi = -1; + sheet->range.col0 = -1; + sheet->range.coli = -1; +} + + +static gint +psppire_sheet_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + g_return_val_if_fail (event != NULL, FALSE); + + if (!GTK_WIDGET_DRAWABLE (widget)) + return FALSE; + + /* exposure events on the sheet */ + if (event->window == sheet->row_title_window && + sheet->row_titles_visible) + { + draw_row_title_buttons_range (sheet, + min_visible_row (sheet), + max_visible_row (sheet)); + } + + if (event->window == sheet->column_title_window && + sheet->column_titles_visible) + { + draw_column_title_buttons_range (sheet, + min_visible_column (sheet), + max_visible_column (sheet)); + } + + if (event->window == sheet->sheet_window) + { + draw_sheet_region (sheet, event->region); + +#if 0 + if (sheet->state != PSPPIRE_SHEET_NORMAL) + { + if (psppire_sheet_range_isvisible (sheet, &sheet->range)) + psppire_sheet_range_draw (sheet, &sheet->range); + + if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet)) + psppire_sheet_range_draw (sheet, &sheet->drag_range); + + if (psppire_sheet_range_isvisible (sheet, &sheet->range)) + psppire_sheet_range_draw_selection (sheet, sheet->range); + if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet)) + draw_xor_rectangle (sheet, sheet->drag_range); + } +#endif + + if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet))) + { + GdkRectangle rect; + PsppireSheetRange range; + range.row0 = range.rowi = sheet->active_cell.row; + range.col0 = range.coli = sheet->active_cell.col; + + rectangle_from_range (sheet, &range, &rect); + + if (GDK_OVERLAP_RECTANGLE_OUT != + gdk_region_rect_in (event->region, &rect)) + { + psppire_sheet_draw_active_cell (sheet); + } + } + + } + + (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); + + return FALSE; +} + + +static gboolean +psppire_sheet_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + PsppireSheet *sheet; + GdkModifierType mods; + gint x, y; + gint row, column; + gboolean veto; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + sheet = PSPPIRE_SHEET (widget); + + /* Cancel any pending tooltips */ + if (sheet->motion_timer) + { + g_source_remove (sheet->motion_timer); + sheet->motion_timer = 0; + } + + gtk_widget_get_pointer (widget, &x, &y); + psppire_sheet_get_pixel_info (sheet, x, y, &row, &column); + + + if (event->window == sheet->column_title_window) + { + sheet->x_drag = event->x; + g_signal_emit (sheet, + sheet_signals[BUTTON_EVENT_COLUMN], 0, + column, event); + + if (psppire_sheet_model_get_column_sensitivity (sheet->model, column)) + { + if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) + g_signal_emit (sheet, + sheet_signals[DOUBLE_CLICK_COLUMN], 0, column); + } + } + else if (event->window == sheet->row_title_window) + { + g_signal_emit (sheet, + sheet_signals[BUTTON_EVENT_ROW], 0, + row, event); + + if (psppire_sheet_model_get_row_sensitivity (sheet->model, row)) + { + if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) + g_signal_emit (sheet, + sheet_signals[DOUBLE_CLICK_ROW], 0, row); + } + } + + gdk_window_get_pointer (widget->window, NULL, NULL, &mods); + + if (! (mods & GDK_BUTTON1_MASK)) return TRUE; + + + /* press on resize windows */ + if (event->window == sheet->column_title_window) + { + sheet->x_drag = event->x; + + if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col)) + { + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG); + gdk_pointer_grab (sheet->column_title_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + + draw_xor_vline (sheet); + return TRUE; + } + } + + if (event->window == sheet->row_title_window) + { + sheet->y_drag = event->y; + + if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row)) + { + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG); + gdk_pointer_grab (sheet->row_title_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + + draw_xor_hline (sheet); + return TRUE; + } + } + + /* the sheet itself does not handle other than single click events */ + if (event->type != GDK_BUTTON_PRESS) return FALSE; + + /* selections on the sheet */ + if (event->window == sheet->sheet_window) + { + gtk_widget_get_pointer (widget, &x, &y); + psppire_sheet_get_pixel_info (sheet, x, y, &row, &column); + gdk_pointer_grab (sheet->sheet_window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + gtk_grab_add (GTK_WIDGET (sheet)); + + if (sheet->selection_mode != GTK_SELECTION_SINGLE && + sheet->selection_mode != GTK_SELECTION_NONE && + sheet->cursor_drag->type == GDK_SIZING && + !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet)) + { + if (sheet->state == GTK_STATE_NORMAL) + { + row = sheet->active_cell.row; + column = sheet->active_cell.col; + sheet->active_cell.row = row; + sheet->active_cell.col = column; + sheet->drag_range = sheet->range; + sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + psppire_sheet_select_range (sheet, &sheet->drag_range); + } + sheet->x_drag = x; + sheet->y_drag = y; + if (row > sheet->range.rowi) row--; + if (column > sheet->range.coli) column--; + sheet->drag_cell.row = row; + sheet->drag_cell.col = column; + sheet->drag_range = sheet->range; + draw_xor_rectangle (sheet, sheet->drag_range); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE); + } + else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW && + !PSPPIRE_SHEET_IN_SELECTION (sheet) + && ! PSPPIRE_SHEET_IN_DRAG (sheet) + && sheet->active_cell.row >= 0 + && sheet->active_cell.col >= 0 + ) + { + if (sheet->state == GTK_STATE_NORMAL) + { + row = sheet->active_cell.row; + column = sheet->active_cell.col; + sheet->active_cell.row = row; + sheet->active_cell.col = column; + sheet->drag_range = sheet->range; + sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + psppire_sheet_select_range (sheet, &sheet->drag_range); + } + sheet->x_drag = x; + sheet->y_drag = y; + if (row < sheet->range.row0) row++; + if (row > sheet->range.rowi) row--; + if (column < sheet->range.col0) column++; + if (column > sheet->range.coli) column--; + sheet->drag_cell.row = row; + sheet->drag_cell.col = column; + sheet->drag_range = sheet->range; + draw_xor_rectangle (sheet, sheet->drag_range); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG); + } + else + { + veto = psppire_sheet_click_cell (sheet, row, column); + if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + } + } + + if (event->window == sheet->column_title_window) + { + gtk_widget_get_pointer (widget, &x, &y); + if ( sheet->row_titles_visible) + x -= sheet->row_title_area.width; + + x += sheet->hadjustment->value; + + column = column_from_xpixel (sheet, x); + + if (psppire_sheet_model_get_column_sensitivity (sheet->model, column)) + { + veto = psppire_sheet_click_cell (sheet, -1, column); + gtk_grab_add (GTK_WIDGET (sheet)); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + } + } + + if (event->window == sheet->row_title_window) + { + gtk_widget_get_pointer (widget, &x, &y); + if ( sheet->column_titles_visible) + y -= sheet->column_title_area.height; + + y += sheet->vadjustment->value; + + row = row_from_ypixel (sheet, y); + if (psppire_sheet_model_get_row_sensitivity (sheet->model, row)) + { + veto = psppire_sheet_click_cell (sheet, row, -1); + gtk_grab_add (GTK_WIDGET (sheet)); + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + } + } + + return TRUE; +} + +static gboolean +psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetCell cell; + gboolean forbid_move; + + cell.row = row; + cell.col = column; + + if (row >= psppire_axis_unit_count (sheet->vaxis) + || column >= psppire_axis_unit_count (sheet->haxis)) + { + return FALSE; + } + + g_signal_emit (sheet, sheet_signals[TRAVERSE], 0, + &sheet->active_cell, + &cell, + &forbid_move); + + if (forbid_move) + { + if (sheet->state == GTK_STATE_NORMAL) + return FALSE; + + row = sheet->active_cell.row; + column = sheet->active_cell.col; + + change_active_cell (sheet, row, column); + return FALSE; + } + + if (row == -1 && column >= 0) + { + psppire_sheet_select_column (sheet, column); + return TRUE; + } + + if (column == -1 && row >= 0) + { + psppire_sheet_select_row (sheet, row); + return TRUE; + } + + if (row == -1 && column == -1) + { + sheet->range.row0 = 0; + sheet->range.col0 = 0; + sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1; + sheet->range.coli = + psppire_axis_unit_count (sheet->haxis) - 1; + sheet->active_cell.row = 0; + sheet->active_cell.col = 0; + psppire_sheet_select_range (sheet, NULL); + return TRUE; + } + + if (sheet->state != PSPPIRE_SHEET_NORMAL) + { + sheet->state = PSPPIRE_SHEET_NORMAL; + psppire_sheet_real_unselect_range (sheet, NULL); + } + else + { + change_active_cell (sheet, row, column); + } + + sheet->active_cell.row = row; + sheet->active_cell.col = column; + sheet->selection_cell.row = row; + sheet->selection_cell.col = column; + sheet->range.row0 = row; + sheet->range.col0 = column; + sheet->range.rowi = row; + sheet->range.coli = column; + sheet->state = PSPPIRE_SHEET_NORMAL; + PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget)); + + return TRUE; +} + +static gint +psppire_sheet_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GdkDisplay *display = gtk_widget_get_display (widget); + + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + /* release on resize windows */ + if (PSPPIRE_SHEET_IN_XDRAG (sheet)) + { + gint width; + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + gdk_display_pointer_ungrab (display, event->time); + draw_xor_vline (sheet); + + width = event->x - + psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col) + + sheet->hadjustment->value; + + set_column_width (sheet, sheet->drag_cell.col, width); + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_YDRAG (sheet)) + { + gint height; + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + gdk_display_pointer_ungrab (display, event->time); + draw_xor_hline (sheet); + + height = event->y - + psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) + + sheet->vadjustment->value; + + set_row_height (sheet, sheet->drag_cell.row, height); + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_DRAG (sheet)) + { + PsppireSheetRange old_range; + draw_xor_rectangle (sheet, sheet->drag_range); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG); + gdk_display_pointer_ungrab (display, event->time); + + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->active_cell.row = sheet->active_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->active_cell.col = sheet->active_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + sheet->selection_cell.row = sheet->selection_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->selection_cell.col = sheet->selection_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + old_range = sheet->range; + sheet->range = sheet->drag_range; + sheet->drag_range = old_range; + g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0, + &sheet->drag_range, &sheet->range); + psppire_sheet_select_range (sheet, &sheet->range); + } + + if (PSPPIRE_SHEET_IN_RESIZE (sheet)) + { + PsppireSheetRange old_range; + draw_xor_rectangle (sheet, sheet->drag_range); + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE); + gdk_display_pointer_ungrab (display, event->time); + + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->active_cell.row = sheet->active_cell.row + + (sheet->drag_range.row0 - sheet->range.row0); + sheet->active_cell.col = sheet->active_cell.col + + (sheet->drag_range.col0 - sheet->range.col0); + if (sheet->drag_range.row0 < sheet->range.row0) + sheet->selection_cell.row = sheet->drag_range.row0; + if (sheet->drag_range.rowi >= sheet->range.rowi) + sheet->selection_cell.row = sheet->drag_range.rowi; + if (sheet->drag_range.col0 < sheet->range.col0) + sheet->selection_cell.col = sheet->drag_range.col0; + if (sheet->drag_range.coli >= sheet->range.coli) + sheet->selection_cell.col = sheet->drag_range.coli; + old_range = sheet->range; + sheet->range = sheet->drag_range; + sheet->drag_range = old_range; + + if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0, + &sheet->drag_range, &sheet->range); + psppire_sheet_select_range (sheet, &sheet->range); + } + + if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet)) + { + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + gdk_display_pointer_ungrab (display, event->time); + change_active_cell (sheet, sheet->active_cell.row, + sheet->active_cell.col); + } + + if (PSPPIRE_SHEET_IN_SELECTION) + gdk_display_pointer_ungrab (display, event->time); + gtk_grab_remove (GTK_WIDGET (sheet)); + + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + return TRUE; +} + + + + + +/* Shamelessly lifted from gtktooltips */ +static gboolean +psppire_sheet_subtitle_paint_window (GtkWidget *tip_window) +{ + GtkRequisition req; + + gtk_widget_size_request (tip_window, &req); + gtk_paint_flat_box (tip_window->style, tip_window->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, GTK_WIDGET(tip_window), "tooltip", + 0, 0, req.width, req.height); + + return FALSE; +} + +static void +destroy_hover_window (PsppireSheetHoverTitle *h) +{ + gtk_widget_destroy (h->window); + g_free (h); +} + +static PsppireSheetHoverTitle * +create_hover_window (void) +{ + PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw)); + + hw->window = gtk_window_new (GTK_WINDOW_POPUP); + +#if GTK_CHECK_VERSION (2, 9, 0) + gtk_window_set_type_hint (GTK_WINDOW (hw->window), + GDK_WINDOW_TYPE_HINT_TOOLTIP); +#endif + + gtk_widget_set_app_paintable (hw->window, TRUE); + gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE); + gtk_widget_set_name (hw->window, "gtk-tooltips"); + gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4); + + g_signal_connect (hw->window, + "expose_event", + G_CALLBACK (psppire_sheet_subtitle_paint_window), + NULL); + + hw->label = gtk_label_new (NULL); + + + gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE); + gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5); + + gtk_container_add (GTK_CONTAINER (hw->window), hw->label); + + gtk_widget_show (hw->label); + + g_signal_connect (hw->window, + "destroy", + G_CALLBACK (gtk_widget_destroyed), + &hw->window); + + return hw; +} + +#define HOVER_WINDOW_Y_OFFSET 2 + +static void +show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle) +{ + gint x, y; + gint px, py; + gint width; + + if ( ! subtitle ) + return; + + gtk_label_set_text (GTK_LABEL (sheet->hover_window->label), + subtitle); + + + sheet->hover_window->row = row; + sheet->hover_window->column = column; + + gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y); + + gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py); + + gtk_widget_show (sheet->hover_window->window); + + width = GTK_WIDGET (sheet->hover_window->label)->allocation.width; + + if (row == -1 ) + { + x += px; + x -= width / 2; + y += sheet->column_title_area.y; + y += sheet->column_title_area.height; + y += HOVER_WINDOW_Y_OFFSET; + } + + if ( column == -1 ) + { + y += py; + x += sheet->row_title_area.x; + x += sheet->row_title_area.width * 2 / 3.0; + } + + gtk_window_move (GTK_WINDOW (sheet->hover_window->window), + x, y); +} + +static gboolean +motion_timeout_callback (gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + gint x, y; + gint row, column; + gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y); + + if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) ) + { + if (sheet->row_title_under && row >= 0) + { + gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row); + + show_subtitle (sheet, row, -1, text); + g_free (text); + } + + if (sheet->column_title_under && column >= 0) + { + gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model, + column); + + show_subtitle (sheet, -1, column, text); + + g_free (text); + } + } + + return FALSE; +} + +static gboolean +psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + GdkModifierType mods; + GdkCursorType new_cursor; + gint x, y; + gint row, column; + GdkDisplay *display; + + g_return_val_if_fail (event != NULL, FALSE); + + display = gtk_widget_get_display (widget); + + /* selections on the sheet */ + x = event->x; + y = event->y; + + if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window)) + { + if ( sheet->motion_timer > 0 ) + g_source_remove (sheet->motion_timer); + sheet->motion_timer = + g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet); + } + else + { + gint row, column; + gint wx, wy; + gtk_widget_get_pointer (widget, &wx, &wy); + + if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) ) + { + if ( row != sheet->hover_window->row || + column != sheet->hover_window->column) + { + gtk_widget_hide (sheet->hover_window->window); + } + } + } + + if (event->window == sheet->column_title_window) + { + if (!PSPPIRE_SHEET_IN_SELECTION (sheet) && + on_column_boundary (sheet, x, &column)) + { + new_cursor = GDK_SB_H_DOUBLE_ARROW; + if (new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + + gdk_window_set_cursor (sheet->column_title_window, + sheet->cursor_drag); + } + } + else + { + new_cursor = GDK_TOP_LEFT_ARROW; + if (!PSPPIRE_SHEET_IN_XDRAG (sheet) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + gdk_window_set_cursor (sheet->column_title_window, + sheet->cursor_drag); + } + } + } + else if (event->window == sheet->row_title_window) + { + if (!PSPPIRE_SHEET_IN_SELECTION (sheet) && + on_row_boundary (sheet, y, &row)) + { + new_cursor = GDK_SB_V_DOUBLE_ARROW; + if (new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + gdk_window_set_cursor (sheet->row_title_window, + sheet->cursor_drag); + } + } + else + { + new_cursor = GDK_TOP_LEFT_ARROW; + if (!PSPPIRE_SHEET_IN_YDRAG (sheet) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = + gdk_cursor_new_for_display (display, new_cursor); + gdk_window_set_cursor (sheet->row_title_window, + sheet->cursor_drag); + } + } + } + + new_cursor = GDK_PLUS; + if ( event->window == sheet->sheet_window && + !POSSIBLE_DRAG (sheet, x, y, &row, &column) && + !PSPPIRE_SHEET_IN_DRAG (sheet) && + !POSSIBLE_RESIZE (sheet, x, y, &row, &column) && + !PSPPIRE_SHEET_IN_RESIZE (sheet) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS); + gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); + } + + new_cursor = GDK_TOP_LEFT_ARROW; + if ( event->window == sheet->sheet_window && + ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || + PSPPIRE_SHEET_IN_RESIZE (sheet)) && + (POSSIBLE_DRAG (sheet, x, y, &row, &column) || + PSPPIRE_SHEET_IN_DRAG (sheet)) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW); + gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); + } + + new_cursor = GDK_SIZING; + if ( event->window == sheet->sheet_window && + sheet->selection_mode != GTK_SELECTION_NONE && + !PSPPIRE_SHEET_IN_DRAG (sheet) && + (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || + PSPPIRE_SHEET_IN_RESIZE (sheet)) && + new_cursor != sheet->cursor_drag->type) + { + gdk_cursor_unref (sheet->cursor_drag); + sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING); + gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); + } + + + gdk_window_get_pointer (widget->window, &x, &y, &mods); + if (! (mods & GDK_BUTTON1_MASK)) return FALSE; + + if (PSPPIRE_SHEET_IN_XDRAG (sheet)) + { + if (event->x != sheet->x_drag) + { + draw_xor_vline (sheet); + sheet->x_drag = event->x; + draw_xor_vline (sheet); + } + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_YDRAG (sheet)) + { + if (event->y != sheet->y_drag) + { + draw_xor_hline (sheet); + sheet->y_drag = event->y; + draw_xor_hline (sheet); + } + + return TRUE; + } + + if (PSPPIRE_SHEET_IN_DRAG (sheet)) + { + PsppireSheetRange aux; + column = column_from_xpixel (sheet, x)- sheet->drag_cell.col; + row = row_from_ypixel (sheet, y) - sheet->drag_cell.row; + if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; + if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0; + sheet->x_drag = x; + sheet->y_drag = y; + aux = sheet->range; + if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) && + aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis)) + { + aux = sheet->drag_range; + sheet->drag_range.row0 = sheet->range.row0 + row; + sheet->drag_range.col0 = sheet->range.col0 + column; + sheet->drag_range.rowi = sheet->range.rowi + row; + sheet->drag_range.coli = sheet->range.coli + column; + if (aux.row0 != sheet->drag_range.row0 || + aux.col0 != sheet->drag_range.col0) + { + draw_xor_rectangle (sheet, aux); + draw_xor_rectangle (sheet, sheet->drag_range); + } + } + return TRUE; + } + + if (PSPPIRE_SHEET_IN_RESIZE (sheet)) + { + PsppireSheetRange aux; + gint v_h, current_col, current_row, col_threshold, row_threshold; + v_h = 1; + if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) > + abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2; + + current_col = column_from_xpixel (sheet, x); + current_row = row_from_ypixel (sheet, y); + column = current_col - sheet->drag_cell.col; + row = current_row - sheet->drag_cell.row; + + /*use half of column width resp. row height as threshold to + expand selection*/ + col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) + + psppire_axis_unit_size (sheet->haxis, current_col) / 2; + if (column > 0) + { + if (x < col_threshold) + column -= 1; + } + else if (column < 0) + { + if (x > col_threshold) + column +=1; + } + row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) + + psppire_axis_unit_size (sheet->vaxis, current_row)/2; + if (row > 0) + { + if (y < row_threshold) + row -= 1; + } + else if (row < 0) + { + if (y > row_threshold) + row +=1; + } + + if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; + if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0; + sheet->x_drag = x; + sheet->y_drag = y; + aux = sheet->range; + + if (v_h == 1) + column = 0; + else + row = 0; + + if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) && + aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis)) + { + aux = sheet->drag_range; + sheet->drag_range = sheet->range; + + if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row; + if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row; + if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column; + if (column > 0) sheet->drag_range.coli = sheet->range.coli + column; + + if (aux.row0 != sheet->drag_range.row0 || + aux.rowi != sheet->drag_range.rowi || + aux.col0 != sheet->drag_range.col0 || + aux.coli != sheet->drag_range.coli) + { + draw_xor_rectangle (sheet, aux); + draw_xor_rectangle (sheet, sheet->drag_range); + } + } + return TRUE; + } + + psppire_sheet_get_pixel_info (sheet, x, y, &row, &column); + + if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row && + column == sheet->active_cell.col) return TRUE; + + if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK) + psppire_sheet_extend_selection (sheet, row, column); + + return TRUE; +} + +static gboolean +psppire_sheet_crossing_notify (GtkWidget *widget, + GdkEventCrossing *event) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + if (event->window == sheet->column_title_window) + sheet->column_title_under = event->type == GDK_ENTER_NOTIFY; + else if (event->window == sheet->row_title_window) + sheet->row_title_under = event->type == GDK_ENTER_NOTIFY; + + return TRUE; +} + + +static gboolean +psppire_sheet_focus_in (GtkWidget *w, + GdkEventFocus *event) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (w); + + gtk_widget_grab_focus (sheet->entry_widget); + + return TRUE; +} + + +static void +psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column) +{ + PsppireSheetRange range; + gint state; + gint r, c; + + if (row == sheet->selection_cell.row && column == sheet->selection_cell.col) + return; + + if (sheet->selection_mode == GTK_SELECTION_SINGLE) return; + + gtk_widget_grab_focus (GTK_WIDGET (sheet)); + + if (PSPPIRE_SHEET_IN_DRAG (sheet)) return; + + state = sheet->state; + + switch (sheet->state) + { + case PSPPIRE_SHEET_ROW_SELECTED: + column = psppire_axis_unit_count (sheet->haxis) - 1; + break; + case PSPPIRE_SHEET_COLUMN_SELECTED: + row = psppire_axis_unit_count (sheet->vaxis) - 1; + break; + case PSPPIRE_SHEET_NORMAL: + sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + r = sheet->active_cell.row; + c = sheet->active_cell.col; + sheet->range.col0 = c; + sheet->range.row0 = r; + sheet->range.coli = c; + sheet->range.rowi = r; + psppire_sheet_range_draw_selection (sheet, sheet->range); + case PSPPIRE_SHEET_RANGE_SELECTED: + sheet->state = PSPPIRE_SHEET_RANGE_SELECTED; + } + + sheet->selection_cell.row = row; + sheet->selection_cell.col = column; + + range.col0 = MIN (column, sheet->active_cell.col); + range.coli = MAX (column, sheet->active_cell.col); + range.row0 = MIN (row, sheet->active_cell.row); + range.rowi = MAX (row, sheet->active_cell.row); + + if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi || + range.col0 != sheet->range.col0 || range.coli != sheet->range.coli || + state == PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_select_range (sheet, &range); + +} + +static gint +psppire_sheet_entry_key_press (GtkWidget *widget, + GdkEventKey *key) +{ + gboolean focus; + g_signal_emit_by_name (widget, "key_press_event", key, &focus); + return focus; +} + + +/* Number of rows in a step-increment */ +#define ROWS_PER_STEP 1 + + +static void +page_vertical (PsppireSheet *sheet, GtkScrollType dir) +{ + gint old_row = sheet->active_cell.row ; + glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row); + + gint new_row; + + vpixel -= psppire_axis_start_pixel (sheet->vaxis, + min_visible_row (sheet)); + + switch ( dir) + { + case GTK_SCROLL_PAGE_DOWN: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->value + + sheet->vadjustment->page_increment); + break; + case GTK_SCROLL_PAGE_UP: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->value - + sheet->vadjustment->page_increment); + + break; + default: + g_assert_not_reached (); + break; + } + + + vpixel += psppire_axis_start_pixel (sheet->vaxis, + min_visible_row (sheet)); + + new_row = row_from_ypixel (sheet, vpixel); + + change_active_cell (sheet, new_row, + sheet->active_cell.col); +} + + +static void +step_sheet (PsppireSheet *sheet, GtkScrollType dir) +{ + gint current_row = sheet->active_cell.row; + gint current_col = sheet->active_cell.col; + PsppireSheetCell new_cell ; + gboolean forbidden = FALSE; + + new_cell.row = current_row; + new_cell.col = current_col; + + switch ( dir) + { + case GTK_SCROLL_STEP_DOWN: + new_cell.row++; + break; + case GTK_SCROLL_STEP_UP: + new_cell.row--; + break; + case GTK_SCROLL_STEP_RIGHT: + new_cell.col++; + break; + case GTK_SCROLL_STEP_LEFT: + new_cell.col--; + break; + case GTK_SCROLL_STEP_FORWARD: + new_cell.col++; + if (new_cell.col >= + psppire_sheet_model_get_column_count (sheet->model)) + { + new_cell.col = 0; + new_cell.row++; + } + break; + case GTK_SCROLL_STEP_BACKWARD: + new_cell.col--; + if (new_cell.col < 0) + { + new_cell.col = + psppire_sheet_model_get_column_count (sheet->model) - 1; + new_cell.row--; + } + break; + default: + g_assert_not_reached (); + break; + } + + g_signal_emit (sheet, sheet_signals[TRAVERSE], 0, + &sheet->active_cell, + &new_cell, + &forbidden); + + if (forbidden) + return; + + + maximize_int (&new_cell.row, 0); + maximize_int (&new_cell.col, 0); + + minimize_int (&new_cell.row, + psppire_axis_unit_count (sheet->vaxis) - 1); + + minimize_int (&new_cell.col, + psppire_axis_unit_count (sheet->haxis) - 1); + + change_active_cell (sheet, new_cell.row, new_cell.col); + + + if ( new_cell.col > max_fully_visible_column (sheet)) + { + glong hpos = + psppire_axis_start_pixel (sheet->haxis, + new_cell.col + 1); + hpos -= sheet->hadjustment->page_size; + + gtk_adjustment_set_value (sheet->hadjustment, + hpos); + } + else if ( new_cell.col < min_fully_visible_column (sheet)) + { + glong hpos = + psppire_axis_start_pixel (sheet->haxis, + new_cell.col); + + gtk_adjustment_set_value (sheet->hadjustment, + hpos); + } + + + if ( new_cell.row > max_fully_visible_row (sheet)) + { + glong vpos = + psppire_axis_start_pixel (sheet->vaxis, + new_cell.row + 1); + vpos -= sheet->vadjustment->page_size; + + gtk_adjustment_set_value (sheet->vadjustment, + vpos); + } + else if ( new_cell.row < min_fully_visible_row (sheet)) + { + glong vpos = + psppire_axis_start_pixel (sheet->vaxis, + new_cell.row); + + gtk_adjustment_set_value (sheet->vadjustment, + vpos); + } + + gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget)); +} + + +static gboolean +psppire_sheet_key_press (GtkWidget *widget, + GdkEventKey *key) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (widget); + + PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION); + + switch (key->keyval) + { + case GDK_Tab: + step_sheet (sheet, GTK_SCROLL_STEP_FORWARD); + break; + case GDK_Right: + step_sheet (sheet, GTK_SCROLL_STEP_RIGHT); + break; + case GDK_ISO_Left_Tab: + step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD); + break; + case GDK_Left: + step_sheet (sheet, GTK_SCROLL_STEP_LEFT); + break; + case GDK_Return: + case GDK_Down: + step_sheet (sheet, GTK_SCROLL_STEP_DOWN); + break; + case GDK_Up: + step_sheet (sheet, GTK_SCROLL_STEP_UP); + break; + + case GDK_Page_Down: + page_vertical (sheet, GTK_SCROLL_PAGE_DOWN); + break; + case GDK_Page_Up: + page_vertical (sheet, GTK_SCROLL_PAGE_UP); + break; + + case GDK_Home: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->lower); + + change_active_cell (sheet, 0, + sheet->active_cell.col); + + break; + + case GDK_End: + gtk_adjustment_set_value (sheet->vadjustment, + sheet->vadjustment->upper - + sheet->vadjustment->page_size - + sheet->vadjustment->page_increment); + + /* + change_active_cellx (sheet, + psppire_axis_unit_count (sheet->vaxis) - 1, + sheet->active_cell.col); + */ + break; + case GDK_Delete: + psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col); + break; + default: + return FALSE; + break; + } + + return TRUE; +} + +static void +psppire_sheet_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + PsppireSheet *sheet; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + g_return_if_fail (requisition != NULL); + + sheet = PSPPIRE_SHEET (widget); + + requisition->width = 3 * DEFAULT_COLUMN_WIDTH; + requisition->height = 3 * DEFAULT_ROW_HEIGHT; + + /* compute the size of the column title area */ + if (sheet->column_titles_visible) + requisition->height += sheet->column_title_area.height; + + /* compute the size of the row title area */ + if (sheet->row_titles_visible) + requisition->width += sheet->row_title_area.width; +} + + +static void +psppire_sheet_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + PsppireSheet *sheet; + GtkAllocation sheet_allocation; + gint border_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (widget)); + g_return_if_fail (allocation != NULL); + + sheet = PSPPIRE_SHEET (widget); + widget->allocation = *allocation; + border_width = GTK_CONTAINER (widget)->border_width; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (widget->window, + allocation->x + border_width, + allocation->y + border_width, + allocation->width - 2 * border_width, + allocation->height - 2 * border_width); + + sheet_allocation.x = 0; + sheet_allocation.y = 0; + sheet_allocation.width = allocation->width - 2 * border_width; + sheet_allocation.height = allocation->height - 2 * border_width; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (sheet->sheet_window, + sheet_allocation.x, + sheet_allocation.y, + sheet_allocation.width, + sheet_allocation.height); + + /* position the window which holds the column title buttons */ + sheet->column_title_area.x = 0; + sheet->column_title_area.y = 0; + sheet->column_title_area.width = sheet_allocation.width ; + + + /* position the window which holds the row title buttons */ + sheet->row_title_area.x = 0; + sheet->row_title_area.y = 0; + sheet->row_title_area.height = sheet_allocation.height; + + if (sheet->row_titles_visible) + sheet->column_title_area.x += sheet->row_title_area.width; + + if (sheet->column_titles_visible) + sheet->row_title_area.y += sheet->column_title_area.height; + + if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible) + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + + + if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible) + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + + size_allocate_global_button (sheet); + + if (sheet->haxis) + { + gint width = sheet->column_title_area.width; + + if ( sheet->row_titles_visible) + width -= sheet->row_title_area.width; + + g_object_set (sheet->haxis, + "minimum-extent", width, + NULL); + } + + + if (sheet->vaxis) + { + gint height = sheet->row_title_area.height; + + if ( sheet->column_titles_visible) + height -= sheet->column_title_area.height; + + g_object_set (sheet->vaxis, + "minimum-extent", height, + NULL); + } + + + /* set the scrollbars adjustments */ + adjust_scrollbars (sheet); +} + +static void +draw_column_title_buttons (PsppireSheet *sheet) +{ + gint x, width; + + if (!sheet->column_titles_visible) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + gdk_drawable_get_size (sheet->sheet_window, &width, NULL); + x = 0; + + if (sheet->row_titles_visible) + { + x = sheet->row_title_area.width; + } + + if (sheet->column_title_area.width != width || sheet->column_title_area.x != x) + { + sheet->column_title_area.width = width; + sheet->column_title_area.x = x; + gdk_window_move_resize (sheet->column_title_window, + sheet->column_title_area.x, + sheet->column_title_area.y, + sheet->column_title_area.width, + sheet->column_title_area.height); + } + + if (max_visible_column (sheet) == + psppire_axis_unit_count (sheet->haxis) - 1) + gdk_window_clear_area (sheet->column_title_window, + 0, 0, + sheet->column_title_area.width, + sheet->column_title_area.height); + + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + draw_column_title_buttons_range (sheet, min_visible_column (sheet), + max_visible_column (sheet)); +} + +static void +draw_row_title_buttons (PsppireSheet *sheet) +{ + gint y = 0; + gint height; + + if (!sheet->row_titles_visible) return; + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + gdk_drawable_get_size (sheet->sheet_window, NULL, &height); + + if (sheet->column_titles_visible) + { + y = sheet->column_title_area.height; + } + + if (sheet->row_title_area.height != height || sheet->row_title_area.y != y) + { + sheet->row_title_area.y = y; + sheet->row_title_area.height = height; + gdk_window_move_resize (sheet->row_title_window, + sheet->row_title_area.x, + sheet->row_title_area.y, + sheet->row_title_area.width, + sheet->row_title_area.height); + } + + if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1) + gdk_window_clear_area (sheet->row_title_window, + 0, 0, + sheet->row_title_area.width, + sheet->row_title_area.height); + + if (!GTK_WIDGET_DRAWABLE (sheet)) return; + + draw_row_title_buttons_range (sheet, min_visible_row (sheet), + max_visible_row (sheet)); +} + + +static void +psppire_sheet_size_allocate_entry (PsppireSheet *sheet) +{ + GtkAllocation entry_alloc; + PsppireSheetCellAttr attributes = { 0 }; + GtkEntry *sheet_entry; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return; + + sheet_entry = psppire_sheet_get_entry (sheet); + + if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row, + sheet->active_cell.col, + &attributes) ) + return ; + + if ( GTK_WIDGET_REALIZED (sheet->entry_widget) ) + { + GtkStyle *style = GTK_WIDGET (sheet_entry)->style; + + style->bg[GTK_STATE_NORMAL] = attributes.background; + style->fg[GTK_STATE_NORMAL] = attributes.foreground; + style->text[GTK_STATE_NORMAL] = attributes.foreground; + style->bg[GTK_STATE_ACTIVE] = attributes.background; + style->fg[GTK_STATE_ACTIVE] = attributes.foreground; + style->text[GTK_STATE_ACTIVE] = attributes.foreground; + } + + rectangle_from_cell (sheet, sheet->active_cell.row, + sheet->active_cell.col, &entry_alloc); + + entry_alloc.width -= BORDER_WIDTH ; + entry_alloc.height -= BORDER_WIDTH ; + entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2); + entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2); + + + gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width, + entry_alloc.height); + gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc); +} + + +/* Copy the sheet's font to the entry widget */ +static void +set_entry_widget_font (PsppireSheet *sheet) +{ + GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget); + + pango_font_description_free (style->font_desc); + style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc); + + gtk_widget_modify_style (sheet->entry_widget, style); +} + +static void +create_sheet_entry (PsppireSheet *sheet) +{ + if (sheet->entry_widget) + { + gtk_widget_unparent (sheet->entry_widget); + } + + sheet->entry_widget = g_object_new (sheet->entry_type, NULL); + g_object_ref_sink (sheet->entry_widget); + + gtk_widget_size_request (sheet->entry_widget, NULL); + + if ( GTK_IS_ENTRY (sheet->entry_widget)) + { + g_object_set (sheet->entry_widget, + "has-frame", FALSE, + NULL); + } + + if (GTK_WIDGET_REALIZED (sheet)) + { + gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); + gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); + gtk_widget_realize (sheet->entry_widget); + } + + g_signal_connect_swapped (sheet->entry_widget, "key_press_event", + G_CALLBACK (psppire_sheet_entry_key_press), + sheet); + + set_entry_widget_font (sheet); + + gtk_widget_show (sheet->entry_widget); +} + + +/* Finds the last child widget that happens to be of type GtkEntry */ +static void +find_entry (GtkWidget *w, gpointer user_data) +{ + GtkWidget **entry = user_data; + if ( GTK_IS_ENTRY (w)) + { + *entry = w; + } +} + + +GtkEntry * +psppire_sheet_get_entry (PsppireSheet *sheet) +{ + GtkWidget *w = sheet->entry_widget; + + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); + g_return_val_if_fail (sheet->entry_widget != NULL, NULL); + + while (! GTK_IS_ENTRY (w)) + { + GtkWidget *entry = NULL; + + if (GTK_IS_CONTAINER (w)) + { + gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry); + + if (NULL == entry) + break; + + w = entry; + } + } + + return GTK_ENTRY (w); +} + + +static void +draw_button (PsppireSheet *sheet, GdkWindow *window, + PsppireSheetButton *button, gboolean is_sensitive, + GdkRectangle allocation) +{ + GtkShadowType shadow_type; + gint text_width = 0, text_height = 0; + PangoAlignment align = PANGO_ALIGN_LEFT; + + gboolean rtl ; + + gint state = 0; + + g_return_if_fail (sheet != NULL); + g_return_if_fail (button != NULL); + + + rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL; + + gdk_window_clear_area (window, + allocation.x, allocation.y, + allocation.width, allocation.height); + + gtk_widget_ensure_style (sheet->button); + + gtk_paint_box (sheet->button->style, window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + &allocation, GTK_WIDGET (sheet->button), + "buttondefault", + allocation.x, allocation.y, + allocation.width, allocation.height); + + state = button->state; + if (!is_sensitive) state = GTK_STATE_INSENSITIVE; + + if (state == GTK_STATE_ACTIVE) + shadow_type = GTK_SHADOW_IN; + else + shadow_type = GTK_SHADOW_OUT; + + if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE) + gtk_paint_box (sheet->button->style, window, + button->state, shadow_type, + &allocation, GTK_WIDGET (sheet->button), + "button", + allocation.x, allocation.y, + allocation.width, allocation.height); + + if ( button->overstruck) + { + GdkPoint points[2] = { + {allocation.x, allocation.y}, + {allocation.x + allocation.width, + allocation.y + allocation.height} + }; + + gtk_paint_polygon (sheet->button->style, + window, + button->state, + shadow_type, + NULL, + GTK_WIDGET (sheet), + "button", + points, + 2, + TRUE); + } + + if (button->label_visible) + { + text_height = DEFAULT_ROW_HEIGHT - + 2 * COLUMN_TITLES_HEIGHT; + + gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state], + &allocation); + gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, + &allocation); + + allocation.y += 2 * sheet->button->style->ythickness; + + if (button->label && strlen (button->label) > 0) + { + PangoRectangle rect; + gchar *line = button->label; + + PangoLayout *layout = NULL; + gint real_x = allocation.x; + gint real_y = allocation.y; + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line); + pango_layout_get_extents (layout, NULL, &rect); + + text_width = PANGO_PIXELS (rect.width); + switch (button->justification) + { + case GTK_JUSTIFY_LEFT: + real_x = allocation.x + COLUMN_TITLES_HEIGHT; + align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; + break; + case GTK_JUSTIFY_RIGHT: + real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT; + align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT; + break; + case GTK_JUSTIFY_CENTER: + default: + real_x = allocation.x + (allocation.width - text_width)/2; + align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; + pango_layout_set_justify (layout, TRUE); + } + pango_layout_set_alignment (layout, align); + gtk_paint_layout (GTK_WIDGET (sheet)->style, + window, + state, + FALSE, + &allocation, + GTK_WIDGET (sheet), + "label", + real_x, real_y, + layout); + g_object_unref (layout); + } + + gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state], + NULL); + gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL); + + } + + psppire_sheet_button_free (button); +} + + +/* Draw the column title buttons FIRST through to LAST */ +static void +draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last) +{ + GdkRectangle rect; + gint col; + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + + if (!sheet->column_titles_visible) return; + + g_return_if_fail (first >= min_visible_column (sheet)); + g_return_if_fail (last <= max_visible_column (sheet)); + + rect.y = 0; + rect.height = sheet->column_title_area.height; + rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING; + rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING + + psppire_axis_unit_size (sheet->haxis, last); + + rect.x -= sheet->hadjustment->value; + + minimize_int (&rect.width, sheet->column_title_area.width); + maximize_int (&rect.x, 0); + + gdk_window_begin_paint_rect (sheet->column_title_window, &rect); + + for (col = first ; col <= last ; ++col) + { + GdkRectangle allocation; + gboolean is_sensitive = FALSE; + + PsppireSheetButton * + button = psppire_sheet_model_get_column_button (sheet->model, col); + allocation.y = 0; + allocation.x = psppire_axis_start_pixel (sheet->haxis, col) + + CELL_SPACING; + allocation.x -= sheet->hadjustment->value; + + allocation.height = sheet->column_title_area.height; + allocation.width = psppire_axis_unit_size (sheet->haxis, col); + is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col); + + draw_button (sheet, sheet->column_title_window, + button, is_sensitive, allocation); + } + + gdk_window_end_paint (sheet->column_title_window); +} + + +static void +draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last) +{ + GdkRectangle rect; + gint row; + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; + + if (!sheet->row_titles_visible) return; + + g_return_if_fail (first >= min_visible_row (sheet)); + g_return_if_fail (last <= max_visible_row (sheet)); + + rect.x = 0; + rect.width = sheet->row_title_area.width; + rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING; + rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING + + psppire_axis_unit_size (sheet->vaxis, last); + + rect.y -= sheet->vadjustment->value; + + minimize_int (&rect.height, sheet->row_title_area.height); + maximize_int (&rect.y, 0); + + gdk_window_begin_paint_rect (sheet->row_title_window, &rect); + for (row = first; row <= last; ++row) + { + GdkRectangle allocation; + + gboolean is_sensitive = FALSE; + + PsppireSheetButton *button = + psppire_sheet_model_get_row_button (sheet->model, row); + allocation.x = 0; + allocation.y = psppire_axis_start_pixel (sheet->vaxis, row) + + CELL_SPACING; + allocation.y -= sheet->vadjustment->value; + + allocation.width = sheet->row_title_area.width; + allocation.height = psppire_axis_unit_size (sheet->vaxis, row); + is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row); + + draw_button (sheet, sheet->row_title_window, + button, is_sensitive, allocation); + } + + gdk_window_end_paint (sheet->row_title_window); +} + +/* SCROLLBARS + * + * functions: + * adjust_scrollbars + * vadjustment_value_changed + * hadjustment_value_changed */ + + +static void +update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size) +{ + double position = + (adj->value + adj->page_size) + / + (adj->upper - adj->lower); + + const glong last_item = psppire_axis_unit_count (axis) - 1; + + if (isnan (position) || position < 0) + position = 0; + + adj->upper = + psppire_axis_start_pixel (axis, last_item) + + + psppire_axis_unit_size (axis, last_item) + ; + + adj->lower = 0; + adj->page_size = page_size; + +#if 0 + adj->value = position * (adj->upper - adj->lower) - adj->page_size; + + if ( adj->value < adj->lower) + adj->value = adj->lower; +#endif + + gtk_adjustment_changed (adj); +} + + +static void +adjust_scrollbars (PsppireSheet *sheet) +{ + gint width, height; + + if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + gdk_drawable_get_size (sheet->sheet_window, &width, &height); + + if ( sheet->row_titles_visible) + width -= sheet->row_title_area.width; + + if (sheet->column_titles_visible) + height -= sheet->column_title_area.height; + + if (sheet->vadjustment) + { + glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1; + + sheet->vadjustment->step_increment = + ROWS_PER_STEP * + psppire_axis_unit_size (sheet->vaxis, last_row); + + sheet->vadjustment->page_increment = + height - + sheet->column_title_area.height - + psppire_axis_unit_size (sheet->vaxis, last_row); + + update_adjustment (sheet->vadjustment, sheet->vaxis, height); + } + + if (sheet->hadjustment) + { + gint last_col = psppire_axis_unit_count (sheet->haxis) - 1; + sheet->hadjustment->step_increment = 1; + + sheet->hadjustment->page_increment = width; + + sheet->hadjustment->upper = + psppire_axis_start_pixel (sheet->haxis, last_col) + + + psppire_axis_unit_size (sheet->haxis, last_col) + ; + + update_adjustment (sheet->hadjustment, sheet->haxis, width); + } +} + +/* Subtracts the region of WIDGET from REGION */ +static void +subtract_widget_region (GdkRegion *region, GtkWidget *widget) +{ + GdkRectangle rect; + GdkRectangle intersect; + GdkRegion *region2; + + gdk_region_get_clipbox (region, &rect); + gtk_widget_intersect (widget, + &rect, + &intersect); + + region2 = gdk_region_rectangle (&intersect); + gdk_region_subtract (region, region2); + gdk_region_destroy (region2); +} + +static void +vadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GdkRegion *region; + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + g_return_if_fail (adjustment != NULL); + + if ( ! GTK_WIDGET_REALIZED (sheet)) return; + + gtk_widget_hide (sheet->entry_widget); + + region = + gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window)); + + subtract_widget_region (region, sheet->button); + gdk_window_begin_paint_region (sheet->sheet_window, region); + + draw_sheet_region (sheet, region); + + draw_row_title_buttons (sheet); + psppire_sheet_draw_active_cell (sheet); + + gdk_window_end_paint (sheet->sheet_window); + gdk_region_destroy (region); +} + + +static void +hadjustment_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GdkRegion *region; + PsppireSheet *sheet = PSPPIRE_SHEET (data); + + g_return_if_fail (adjustment != NULL); + + if ( ! GTK_WIDGET_REALIZED (sheet)) return; + + gtk_widget_hide (sheet->entry_widget); + + + region = + gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window)); + + subtract_widget_region (region, sheet->button); + gdk_window_begin_paint_region (sheet->sheet_window, region); + + draw_sheet_region (sheet, region); + + draw_column_title_buttons (sheet); + + psppire_sheet_draw_active_cell (sheet); + + gdk_window_end_paint (sheet->sheet_window); + + gdk_region_destroy (region); +} + + +/* COLUMN RESIZING */ +static void +draw_xor_vline (PsppireSheet *sheet) +{ + gint height; + gint xpos = sheet->x_drag; + gdk_drawable_get_size (sheet->sheet_window, + NULL, &height); + + if (sheet->row_titles_visible) + xpos += sheet->row_title_area.width; + + gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc, + xpos, + sheet->column_title_area.height, + xpos, + height + CELL_SPACING); +} + +/* ROW RESIZING */ +static void +draw_xor_hline (PsppireSheet *sheet) + +{ + gint width; + gint ypos = sheet->y_drag; + + gdk_drawable_get_size (sheet->sheet_window, + &width, NULL); + + + if (sheet->column_titles_visible) + ypos += sheet->column_title_area.height; + + gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc, + sheet->row_title_area.width, + ypos, + width + CELL_SPACING, + ypos); +} + +/* SELECTED RANGE */ +static void +draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range) +{ + gint i = 0; + GdkRectangle clip_area, area; + GdkGCValues values; + + area.x = psppire_axis_start_pixel (sheet->haxis, range.col0); + area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0); + area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+ + psppire_axis_unit_size (sheet->haxis, range.coli); + area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y + + psppire_axis_unit_size (sheet->vaxis, range.rowi); + + clip_area.x = sheet->row_title_area.width; + clip_area.y = sheet->column_title_area.height; + + gdk_drawable_get_size (sheet->sheet_window, + &clip_area.width, &clip_area.height); + + if (!sheet->row_titles_visible) clip_area.x = 0; + if (!sheet->column_titles_visible) clip_area.y = 0; + + if (area.x < 0) + { + area.width = area.width + area.x; + area.x = 0; + } + if (area.width > clip_area.width) area.width = clip_area.width + 10; + if (area.y < 0) + { + area.height = area.height + area.y; + area.y = 0; + } + if (area.height > clip_area.height) area.height = clip_area.height + 10; + + clip_area.x--; + clip_area.y--; + clip_area.width += 3; + clip_area.height += 3; + + gdk_gc_get_values (sheet->xor_gc, &values); + + gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area); + + gdk_draw_rectangle (sheet->sheet_window, + sheet->xor_gc, + FALSE, + area.x + i, area.y + i, + area.width - 2 * i, area.height - 2 * i); + + + gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL); + + gdk_gc_set_foreground (sheet->xor_gc, &values.foreground); +} + + +static void +set_column_width (PsppireSheet *sheet, + gint column, + gint width) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis)) + return; + + if ( width <= 0) + return; + + psppire_axis_resize (sheet->haxis, column, + width - sheet->cell_padding->left - + sheet->cell_padding->right); + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + { + draw_column_title_buttons (sheet); + adjust_scrollbars (sheet); + psppire_sheet_size_allocate_entry (sheet); + redraw_range (sheet, NULL); + } +} + +static void +set_row_height (PsppireSheet *sheet, + gint row, + gint height) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (PSPPIRE_IS_SHEET (sheet)); + + if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis)) + return; + + if (height <= 0) + return; + + psppire_axis_resize (sheet->vaxis, row, + height - sheet->cell_padding->top - + sheet->cell_padding->bottom); + + if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) ) + { + draw_row_title_buttons (sheet); + adjust_scrollbars (sheet); + psppire_sheet_size_allocate_entry (sheet); + redraw_range (sheet, NULL); + } +} + +static gboolean +psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col, + PsppireSheetCellAttr *attr) +{ + GdkColor *fg, *bg; + const GtkJustification *j ; + GdkColormap *colormap; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE); + + if (row < 0 || col < 0) return FALSE; + + attr->foreground = GTK_WIDGET (sheet)->style->black; + attr->background = sheet->color[BG_COLOR]; + + attr->border.width = 0; + attr->border.line_style = GDK_LINE_SOLID; + attr->border.cap_style = GDK_CAP_NOT_LAST; + attr->border.join_style = GDK_JOIN_MITER; + attr->border.mask = 0; + attr->border.color = GTK_WIDGET (sheet)->style->black; + + colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet)); + fg = psppire_sheet_model_get_foreground (sheet->model, row, col); + if ( fg ) + { + gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE); + attr->foreground = *fg; + } + + bg = psppire_sheet_model_get_background (sheet->model, row, col); + if ( bg ) + { + gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE); + attr->background = *bg; + } + + attr->justification = + psppire_sheet_model_get_column_justification (sheet->model, col); + + j = psppire_sheet_model_get_justification (sheet->model, row, col); + if (j) + attr->justification = *j; + + return TRUE; +} + +static void +psppire_sheet_button_size_request (PsppireSheet *sheet, + const PsppireSheetButton *button, + GtkRequisition *button_requisition) +{ + GtkRequisition requisition; + GtkRequisition label_requisition; + + label_requisition.height = DEFAULT_ROW_HEIGHT; + label_requisition.width = COLUMN_MIN_WIDTH; + + requisition.height = DEFAULT_ROW_HEIGHT; + requisition.width = COLUMN_MIN_WIDTH; + + + *button_requisition = requisition; + button_requisition->width = MAX (requisition.width, label_requisition.width); + button_requisition->height = MAX (requisition.height, label_requisition.height); + +} + +static void +psppire_sheet_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (container); + + g_return_if_fail (callback != NULL); + + if (sheet->button && sheet->button->parent) + (* callback) (sheet->button, callback_data); + + if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget)) + (* callback) (sheet->entry_widget, callback_data); +} + + +PsppireSheetModel * +psppire_sheet_get_model (const PsppireSheet *sheet) +{ + g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL); + + return sheet->model; +} + + +PsppireSheetButton * +psppire_sheet_button_new (void) +{ + PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton)); + + button->state = GTK_STATE_NORMAL; + button->label = NULL; + button->label_visible = TRUE; + button->justification = GTK_JUSTIFY_FILL; + button->overstruck = FALSE; + + return button; +} + + +void +psppire_sheet_button_free (PsppireSheetButton *button) +{ + if (!button) return ; + + g_free (button->label); + g_free (button); +} + +static void +append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c) +{ + gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c); + + if ( NULL == celltext) + return; + + g_string_append (string, celltext); + g_free (celltext); +} + + +static GString * +range_to_text (const PsppireSheet *sheet) +{ + gint r, c; + GString *string; + + if ( !psppire_sheet_range_isvisible (sheet, &sheet->range)) + return NULL; + + string = g_string_sized_new (80); + + for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) + { + for (c = sheet->range.col0; c < sheet->range.coli; ++c) + { + append_cell_text (string, sheet, r, c); + g_string_append (string, "\t"); + } + append_cell_text (string, sheet, r, c); + if ( r < sheet->range.rowi) + g_string_append (string, "\n"); + } + + return string; +} + +static GString * +range_to_html (const PsppireSheet *sheet) +{ + gint r, c; + GString *string; + + if ( !psppire_sheet_range_isvisible (sheet, &sheet->range)) + return NULL; + + string = g_string_sized_new (480); + + g_string_append (string, "\n"); + g_string_append (string, "\n"); + g_string_append (string, "\n"); + for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) + { + g_string_append (string, "\n"); + for (c = sheet->range.col0; c <= sheet->range.coli; ++c) + { + g_string_append (string, "\n"); + } + g_string_append (string, "\n"); + } + g_string_append (string, "
"); + append_cell_text (string, sheet, r, c); + g_string_append (string, "
\n"); + g_string_append (string, "\n"); + g_string_append (string, "\n"); + + return string; +} + +enum { + SELECT_FMT_NULL, + SELECT_FMT_TEXT, + SELECT_FMT_HTML +}; + +static void +primary_get_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + GString *string = NULL; + + switch (info) + { + case SELECT_FMT_TEXT: + string = range_to_text (sheet); + break; + case SELECT_FMT_HTML: + string = range_to_html (sheet); + break; + default: + g_assert_not_reached (); + } + + gtk_selection_data_set (selection_data, selection_data->target, + 8, + (const guchar *) string->str, string->len); + g_string_free (string, TRUE); +} + +static void +primary_clear_cb (GtkClipboard *clipboard, + gpointer data) +{ + PsppireSheet *sheet = PSPPIRE_SHEET (data); + if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) + return; + + psppire_sheet_real_unselect_range (sheet, NULL); +} + +static void +psppire_sheet_update_primary_selection (PsppireSheet *sheet) +{ + static const GtkTargetEntry targets[] = { + { "UTF8_STRING", 0, SELECT_FMT_TEXT }, + { "STRING", 0, SELECT_FMT_TEXT }, + { "TEXT", 0, SELECT_FMT_TEXT }, + { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT }, + { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT }, + { "text/plain", 0, SELECT_FMT_TEXT }, + { "text/html", 0, SELECT_FMT_HTML } + }; + + GtkClipboard *clipboard; + + if (!GTK_WIDGET_REALIZED (sheet)) + return; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet), + GDK_SELECTION_PRIMARY); + + if (psppire_sheet_range_isvisible (sheet, &sheet->range)) + { + if (!gtk_clipboard_set_with_owner (clipboard, targets, + G_N_ELEMENTS (targets), + primary_get_cb, primary_clear_cb, + G_OBJECT (sheet))) + primary_clear_cb (clipboard, sheet); + } + else + { + if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet)) + gtk_clipboard_clear (clipboard); + } +} diff --git a/lib/gtk-contrib/psppire-sheet.h b/lib/gtk-contrib/psppire-sheet.h new file mode 100644 index 00000000..27c2ed54 --- /dev/null +++ b/lib/gtk-contrib/psppire-sheet.h @@ -0,0 +1,316 @@ +/* + Copyright (C) 2006, 2008 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + + This file is derived from the gtksheet.c and extensively modified for the + requirements of PSPPIRE. The changes are copyright by the + Free Software Foundation. The copyright notice for the original work is + below. + + + GtkSheet widget for Gtk+. + * Copyright (C) 1999-2001 Adrian E. Feiguin + * + * Based on GtkClist widget by Jay Painter, but major changes. + * Memory allocation routines inspired on SC (Spreadsheet Calculator) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __PSPPIRE_SHEET_H__ +#define __PSPPIRE_SHEET_H__ + +#include + +#include "gtkextra-sheet.h" +#include +#include + +G_BEGIN_DECLS + +/* sheet->state */ +enum +{ + PSPPIRE_SHEET_NORMAL, + PSPPIRE_SHEET_ROW_SELECTED, + PSPPIRE_SHEET_COLUMN_SELECTED, + PSPPIRE_SHEET_RANGE_SELECTED +}; + + +#define PSPPIRE_TYPE_SHEET_RANGE (psppire_sheet_range_get_type ()) +#define PSPPIRE_TYPE_SHEET_CELL (psppire_sheet_cell_get_type ()) +#define PSPPIRE_TYPE_SHEET (psppire_sheet_get_type ()) + +#define PSPPIRE_SHEET(obj) GTK_CHECK_CAST (obj, psppire_sheet_get_type (), PsppireSheet) +#define PSPPIRE_SHEET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, psppire_sheet_get_type (), PsppireSheetClass) +#define PSPPIRE_IS_SHEET(obj) GTK_CHECK_TYPE (obj, psppire_sheet_get_type ()) + + +typedef struct _PsppireSheetClass PsppireSheetClass; +typedef struct _PsppireSheetCellAttr PsppireSheetCellAttr; + +typedef struct _PsppireSheetHoverTitle PsppireSheetHoverTitle; + + +struct _PsppireSheetCellAttr +{ + GtkJustification justification; + GdkColor foreground; + GdkColor background; + PsppireSheetCellBorder border; +}; + +struct _PsppireSheetHoverTitle +{ + GtkWidget *window; + GtkWidget *label; + gint row, column; +}; + +enum + { + BG_COLOR, + GRID_COLOR, + n_COLORS + }; + +struct _PsppireSheet +{ + GtkBin parent; + + gboolean dispose_has_run; + PsppireAxis *haxis; + PsppireAxis *vaxis; + + guint16 flags; + + PsppireSheetModel *model; + + GtkSelectionMode selection_mode; + + /* Component colors */ + GdkColor color[n_COLORS]; + gboolean show_grid; + + /* active cell */ + PsppireSheetCell active_cell; + + /* The GtkEntry used for editing the cells */ + GtkWidget *entry_widget; + + /* The type of entry_widget */ + GtkType entry_type; + + /* expanding selection */ + PsppireSheetCell selection_cell; + + /* global selection button */ + GtkWidget *button; + + /* sheet state */ + gint state; + + /* selected range */ + PsppireSheetRange range; + + /* The space between a cell's contents and its border */ + GtkBorder *cell_padding; + + /* the scrolling window and its height and width to + * make things a little speedier */ + GdkWindow *sheet_window; + + /* border shadow style */ + GtkShadowType shadow_type; + + /* Column Titles */ + GdkRectangle column_title_area; + GdkWindow *column_title_window; + gboolean column_titles_visible; + /* TRUE if the cursor is over the column title window */ + gboolean column_title_under; + + /* Row Titles */ + GdkRectangle row_title_area; + GdkWindow *row_title_window; + gboolean row_titles_visible; + /* TRUE if the cursor is over the row title window */ + gboolean row_title_under; + + /*scrollbars*/ + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + /* xor GC for the verticle drag line */ + GdkGC *xor_gc; + + /* gc for drawing unselected cells */ + GdkGC *fg_gc; + GdkGC *bg_gc; + + /* cursor used to indicate dragging */ + GdkCursor *cursor_drag; + + /* the current x-pixel location of the xor-drag vline */ + gint x_drag; + + /* the current y-pixel location of the xor-drag hline */ + gint y_drag; + + /* current cell being dragged */ + PsppireSheetCell drag_cell; + /* current range being dragged */ + PsppireSheetRange drag_range; + + /* Used for the subtitle (popups) */ + gint motion_timer; + PsppireSheetHoverTitle *hover_window; + + gulong update_handler_id; +}; + +struct _PsppireSheetClass +{ + GtkBinClass parent_class; + + gboolean (*set_scroll_adjustments) (PsppireSheet *sheet, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + + void (*select_row) (PsppireSheet *sheet, gint row); + + void (*select_column) (PsppireSheet *sheet, gint column); + + void (*select_range) (PsppireSheet *sheet, PsppireSheetRange *range); + + void (*resize_range) (PsppireSheet *sheet, + PsppireSheetRange *old_range, + PsppireSheetRange *new_range); + + void (*move_range) (PsppireSheet *sheet, + PsppireSheetRange *old_range, + PsppireSheetRange *new_range); + + gboolean (*traverse) (PsppireSheet *sheet, + gint row, gint column, + gint *new_row, gint *new_column); + + gboolean (*activate) (PsppireSheet *sheet, + gint row, gint column); + + void (*changed) (PsppireSheet *sheet, + gint row, gint column); +}; + +GType psppire_sheet_get_type (void); +GtkType psppire_sheet_range_get_type (void); + + +/* create a new sheet */ +GtkWidget * psppire_sheet_new (PsppireSheetModel *model); + +/* create a new sheet with custom entry */ +GtkWidget * +psppire_sheet_new_with_custom_entry (GtkType entry_type); + +/* Change entry */ +void psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type); + +GtkEntry *psppire_sheet_get_entry (PsppireSheet *sheet); + + +void psppire_sheet_get_selected_range (PsppireSheet *sheet, + PsppireSheetRange *range); + +void psppire_sheet_show_grid (PsppireSheet *sheet, + gboolean show); + +gboolean psppire_sheet_grid_visible (PsppireSheet *sheet); + + +/* scroll the viewing area of the sheet to the given column + * and row; row_align and col_align are between 0-1 representing the + * location the row should appear on the screen, 0.0 being top or left, + * 1.0 being bottom or right; if row or column is negative then there + * is no change */ +void psppire_sheet_moveto (PsppireSheet *sheet, + gint row, + gint column, + gfloat row_align, + gfloat col_align); + + +void psppire_sheet_show_row_titles (PsppireSheet *sheet); +void psppire_sheet_hide_row_titles (PsppireSheet *sheet); +void psppire_sheet_show_column_titles (PsppireSheet *sheet); +void psppire_sheet_hide_column_titles (PsppireSheet *sheet); + +/* select the row. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void psppire_sheet_select_row (PsppireSheet * sheet, gint row); + +/* select the column. The range is then highlighted, and the bounds are stored + * in sheet->range */ +void psppire_sheet_select_column (PsppireSheet * sheet, gint column); + +/* highlight the selected range and store bounds in sheet->range */ +void psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range); + +void psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range); + + +/* obvious */ +void psppire_sheet_unselect_range (PsppireSheet *sheet); + +/* set active cell where the entry will be displayed */ +void psppire_sheet_set_active_cell (PsppireSheet *sheet, + gint row, gint column); + +/* Sets *ROW and *COLUMN to be the coordinates of the active cell. + ROW and/or COLUMN may be null if the caller is not interested in their + values */ +void psppire_sheet_get_active_cell (PsppireSheet *sheet, + gint *row, gint *column); + +/* get cell contents */ +gchar *psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col); + + +void psppire_sheet_set_model (PsppireSheet *sheet, + PsppireSheetModel *model); + +PsppireSheetModel * psppire_sheet_get_model (const PsppireSheet *sheet); + + +G_END_DECLS + + +#endif /* __PSPPIRE_SHEET_H__ */ + + diff --git a/lib/gtksheet/COPYING.LESSER b/lib/gtksheet/COPYING.LESSER deleted file mode 100644 index 8add30ad..00000000 --- a/lib/gtksheet/COPYING.LESSER +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/lib/gtksheet/OChangeLog b/lib/gtksheet/OChangeLog deleted file mode 100644 index b5ba1c82..00000000 --- a/lib/gtksheet/OChangeLog +++ /dev/null @@ -1,149 +0,0 @@ -2008-05-08 Ben Pfaff - - Patch #6506. Reviewed by John Darrington. - - * gtksheet.c (gtk_sheet_unrealize): Don't call gtk_widget_unparent - on sheet->button if it's null. - -2008-05-06 Ben Pfaff - - * gtksheet.c (gtk_sheet_dispose): Set the sheet's entry_container - and button members to NULL after unref'ing them, so that a later - call to gtk_sheet_for_all will not try to dereference a dangling - pointer. - -2008-03-06 John Darrington - - * gsheet-row-iface.c gsheet-row-iface.h: Delete unused, unneccesary - gpointer variable from the interface. - - * gtksheet.c: Update to match new gsheet-row-iface - -2008-02-27 John Darrington - * gtksheet.c gtksheet.h: Corrected some leaks and other problems - related to de-allocating the sheet. - -2008-02-27 John Darrington - * gtksheet.c: (gtk_sheet_expose) Don't queue a redraw on the entry - widget. Fixes bug #21073 - -2008-02-20 John Darrington - - * gtksheet.c gtksheet.h: Removed some unused signals. - Made the models properties of the widget. - -2008-02-08 John Darrington - - * gtksheet.c: Removed the sheet_locked feature, which we never - used, and interfered with the editability of the entry widget. - - * gtksheet.c: Add one to the row to which we scroll. Seems like - the best way to cope with granularity problems. - -21 Septempber 2007 John Darrington - - * gtksheet.c (range_update_callback): Scroll to cell 0,0 if the - current position is outside the model's range. - -24 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed the `clip' feature, which IMO - is a croc, and we're unlikely to use. In its place, added a primary - selection which supports text and html targets. - -16 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed some legacy functions called from - gtk_sheet_finalize which caused unnecessary delays when shutting down. - -12 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed view member and replaced with - function call. Removed hadjustment_changed and vadjustment_changed - functions which did nothing. Added some whitespace arount != - operators. - -09 July 2007 John Darrington - - * gtksheet.c gtksheet.h (gtk_sheet_get_active_cell): Allowed row, - column to be NULL. - -07 July 2007 John Darrington - - * gsheet-column-iface.c gsheet-column-iface.h gsheet-row-iface.c - gsheet-row-iface.h gtksheet.c gtksheet.h: Added a "subtitle" - feature on row/column titles, which shows tooltip-like popups. - -03 July 2007 John Darrington - - * gtksheet.c gtksheet.h: Removed the autoscroll-on-select feature - that was causing us grief. - -28 June 2007 John Darrington - - * gtksheet.c: Removed some features that we dont use, to get better - speed. - -Sat Feb 17 17:36:56 2007 Ben Pfaff - - * gsheet-column-iface.c gsheet-hetero-column.c gsheet-row-iface.c - gsheet-uniform-column.c gsheet-uniform-row.c gsheetmodel.c - gtkextra-marshal.c gtkextra.c gtkiconlist.c gtkitementry.c - gtksheet.c: Add "#include ". - -Mon Jun 19 18:03:21 WST 2006 John Darrington - - * gsheet-column-iface.c gsheet-column-iface.h - gsheet-hetero-column.c gsheet-row-iface.c gsheet-row-iface.h - gsheet-uniform-column.c gsheet-uniform-row.c gtksheet.c - gtksheet.h: Fixed some warnings. Corrected errors updating - row/column titles - -Di Mai 30 19:51:19 WST 2006 John Darrington - - * gtksheet.c gtksheet.h: constness. Removed dependence on glib2.10 - -Sat May 27 16:29:36 WST 2006 John Darrington - - * gtksheet.c: Removed call to gtk_entry_set_text, which caused warnings - and was unnecessary. - -Thu May 25 17:58:51 WST 2006 John Darrington - - * gsheet-column-iface.c gsheet-column-iface.h gsheet-hetero-column.c - gsheet-row-iface.c gsheet-row-iface.h gsheet-uniform-row.c - gtksheet-extra.h gtksheet.c: Plugged memory leaks. Rationalised the way - that GtkSheetButtons are created. - -Sat May 20 21:02:03 WST 2006 John Darrington - - * gsheetmodel.c gsheetmodel.h: Added columns-inserted and columns-deleted - signals. Added g_sheet_get_{row,column}_count functions. - - * gtksheet.c gtksheet.h: Allowed -1 to be passed to - gtk_sheet_set_active_cell to indicate no active cell. - -Mon May 15 16:10:49 WST 2006 John Darrington - - * gtksheet.c: Removed code which rendered the title buttons a second - time. Cut and Paste error ? - -Sat May 13 07:58:32 WST 2006 John Darrington - - * gsheetmodel.c gsheetmodel.h gtksheet.c gtksheeet.h: Added - free_strings flag to tell the sheet whether to free the string - data passed from the model. - -Thu May 11 22:20:04 WST 2006 John Darrington - - * gtksheet.c, gtksheet.h: Fixed broken deallocation of sheet->pixmap. - -Thu May 4 17:55:48 WST 2006 John Darrington - - * gtksheet.c: Added callback on inserted rows. - -Sat Jan 28 08:48:08 2006 UTC John Darrington - - * Separated the data out of the GtkSheet. The gtksheet should now be - regarded as a way of looking into the data. The data is represented by a - GSheetModel and the rows and columns by GSheetRow and GSheetColumn. diff --git a/lib/gtksheet/README b/lib/gtksheet/README deleted file mode 100644 index ebfce1dc..00000000 --- a/lib/gtksheet/README +++ /dev/null @@ -1,11 +0,0 @@ -This is not part of the GNU PSPP program, but is used with GNU PSPP. - -This directory contains a version of the GtkSheet widget from the gtk-extra -project at http://gtkextra.sourceforge.net The version found here has -major modifications developed for the needs of PSPP. Every effort has been -made to keep GtkSheet application independent. Thus, it should be possible -to use this modified software for other applications. However, the API is -substantially different from the original. - -Files in this directory ONLY are licensed under the GNU Lesser General Public -License. See COPYING.LGPL diff --git a/lib/gtksheet/automake.mk b/lib/gtksheet/automake.mk deleted file mode 100644 index babcb2aa..00000000 --- a/lib/gtksheet/automake.mk +++ /dev/null @@ -1,31 +0,0 @@ -## Process this file with automake to produce Makefile.in -*- makefile -*- - -noinst_LIBRARIES += lib/gtksheet/libgtksheet.a - -lib_gtksheet_libgtksheet_a_CFLAGS = $(GTK_CFLAGS) -Wall - - -lib_gtksheet_libgtksheet_a_SOURCES = \ - lib/gtksheet/gsheet-column-iface.c \ - lib/gtksheet/gsheet-column-iface.h \ - lib/gtksheet/gsheet-hetero-column.c \ - lib/gtksheet/gsheet-hetero-column.h \ - lib/gtksheet/gsheetmodel.c \ - lib/gtksheet/gsheetmodel.h \ - lib/gtksheet/gsheet-row-iface.c \ - lib/gtksheet/gsheet-row-iface.h \ - lib/gtksheet/gsheet-uniform-column.c \ - lib/gtksheet/gsheet-uniform-column.h \ - lib/gtksheet/gsheet-uniform-row.c \ - lib/gtksheet/gsheet-uniform-row.h \ - lib/gtksheet/gtkextra.c \ - lib/gtksheet/gtkextrafeatures.h \ - lib/gtksheet/gtkextra-marshal.c \ - lib/gtksheet/gtkextra-marshal.h \ - lib/gtksheet/gtkextra-sheet.h \ - lib/gtksheet/gtkitementry.h \ - lib/gtksheet/gtkitementry.c \ - lib/gtksheet/gtksheet.c \ - lib/gtksheet/gtksheet.h - -EXTRA_DIST += lib/gtksheet/OChangeLog diff --git a/lib/gtksheet/gsheet-column-iface.c b/lib/gtksheet/gsheet-column-iface.c deleted file mode 100644 index 1cd7996d..00000000 --- a/lib/gtksheet/gsheet-column-iface.c +++ /dev/null @@ -1,284 +0,0 @@ -/* GSheetColumn --- an abstract model of the column geometry of a - GSheet widget. - - * Copyright (C) 2006 Free Software Foundation - * - * 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 - */ - -#include - -#include -#include -#include -#include -#include -#include "gsheet-column-iface.h" -#include "gtkextra-marshal.h" -#include "gtkextra-sheet.h" - -enum { - COLUMNS_CHANGED, - LAST_SIGNAL -}; - -static guint sheet_column_signals[LAST_SIGNAL]; - - - -static void g_sheet_column_base_init (gpointer g_class); - - -GType -g_sheet_column_get_type (void) -{ - static GType sheet_column_type = 0; - - if (! sheet_column_type) - { - static const GTypeInfo sheet_column_info = - - { - sizeof (GSheetColumnIface), /* class_size */ - g_sheet_column_base_init, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; - - sheet_column_type = - g_type_register_static (G_TYPE_INTERFACE, "GSheetColumn", - &sheet_column_info, 0); - - g_assert(sheet_column_type); - - g_type_interface_add_prerequisite (sheet_column_type, G_TYPE_OBJECT); - } - - return sheet_column_type; -} - - -static void -g_sheet_column_base_init (gpointer g_class) -{ - static gboolean initialized = FALSE; - - if (! initialized) - { - - sheet_column_signals[COLUMNS_CHANGED] = - g_signal_new ("columns_changed", - G_TYPE_SHEET_COLUMN, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSheetColumnIface, columns_changed), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_INT); - - - initialized = TRUE; - } -} - - -void -g_sheet_column_set_width (GSheetColumn *column, glong col, gint size) -{ - g_return_if_fail (G_IS_SHEET_COLUMN (column)); - - if ((G_SHEET_COLUMN_GET_IFACE (column)->set_width) ) - (G_SHEET_COLUMN_GET_IFACE (column)->set_width) (column, col, size); -} - - -gint -g_sheet_column_get_width (const GSheetColumn *column, glong col) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), -1); - - g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_width); - - return (G_SHEET_COLUMN_GET_IFACE (column)->get_width) (column, col); -} - - - -gboolean -g_sheet_column_get_visibility(const GSheetColumn *column, - glong col) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE); - - g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_visibility); - - return (G_SHEET_COLUMN_GET_IFACE (column)->get_visibility) (column, - col); - -} - -gboolean -g_sheet_column_get_sensitivity(const GSheetColumn *column, - glong col) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE); - - g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_sensitivity); - - return (G_SHEET_COLUMN_GET_IFACE (column)->get_sensitivity) (column, - col); - -} - - -GtkSheetButton * -g_sheet_column_get_button(const GSheetColumn *column, - glong col) -{ - GtkSheetButton *button = gtk_sheet_button_new(); - - GSheetColumnIface *iface = G_SHEET_COLUMN_GET_IFACE (column); - - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE); - - if ( iface->get_button_label) - button->label = iface->get_button_label(column, col); - - return button; -} - -GtkJustification -g_sheet_column_get_justification(const GSheetColumn *column, - glong col) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE); - - g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_justification); - - return (G_SHEET_COLUMN_GET_IFACE (column)->get_justification) (column, col); -} - -gchar * -g_sheet_column_get_subtitle (const GSheetColumn *column, glong col) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), NULL); - - if ( ! G_SHEET_COLUMN_GET_IFACE (column)->get_subtitle) - return NULL; - - return (G_SHEET_COLUMN_GET_IFACE (column)->get_subtitle) (column, col); -} - - - -gint -g_sheet_column_get_left_text_column (const GSheetColumn *column, - glong col) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), -1); - - if ( ! G_SHEET_COLUMN_GET_IFACE (column)->get_left_text_column) - return col; - - return (G_SHEET_COLUMN_GET_IFACE (column)->get_left_text_column) (column, col); - -} - -gint -g_sheet_column_get_right_text_column (const GSheetColumn *column, - glong col) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (column), -1); - - if ( ! G_SHEET_COLUMN_GET_IFACE (column)->get_right_text_column) - return col; - - return (G_SHEET_COLUMN_GET_IFACE (column)->get_right_text_column) (column, col); - -} - -void -g_sheet_column_set_left_text_column (const GSheetColumn *column, - glong col, gint i) -{ - g_return_if_fail (G_IS_SHEET_COLUMN (column)); - - if ( G_SHEET_COLUMN_GET_IFACE (column)->set_left_text_column) - (G_SHEET_COLUMN_GET_IFACE (column)->set_left_text_column) (column, col, i); - -} - - -void -g_sheet_column_set_right_text_column (const GSheetColumn *column, - glong col, gint i) -{ - g_return_if_fail (G_IS_SHEET_COLUMN (column)); - - if ( G_SHEET_COLUMN_GET_IFACE (column)->set_right_text_column) - (G_SHEET_COLUMN_GET_IFACE (column)->set_right_text_column) (column, col, i); -} - -glong -g_sheet_column_get_column_count(const GSheetColumn *geo) -{ - g_return_val_if_fail (G_IS_SHEET_COLUMN (geo), -1); - - g_assert ( G_SHEET_COLUMN_GET_IFACE (geo)->get_column_count); - - return (G_SHEET_COLUMN_GET_IFACE (geo)->get_column_count) (geo); -} - -gint -g_sheet_column_start_pixel(const GSheetColumn *geo, glong col) -{ - gint i; - gint start_pixel = 0; - - g_return_val_if_fail (G_IS_SHEET_COLUMN (geo), -1); - g_return_val_if_fail (col < - g_sheet_column_get_column_count(geo),-1); - - for ( i = 0 ; i < col ; ++i ) - { - if ( g_sheet_column_get_visibility(geo, i)) - start_pixel += g_sheet_column_get_width(geo, i); - } - - return start_pixel; - -} - - - -void -g_sheet_column_columns_changed(GSheetColumn *geo, - glong first, glong n_columns) -{ - g_return_if_fail (G_IS_SHEET_COLUMN (geo)); - - g_signal_emit (geo, sheet_column_signals[COLUMNS_CHANGED], 0, - first, n_columns); -} - - - - diff --git a/lib/gtksheet/gsheet-column-iface.h b/lib/gtksheet/gsheet-column-iface.h deleted file mode 100644 index 5503393a..00000000 --- a/lib/gtksheet/gsheet-column-iface.h +++ /dev/null @@ -1,135 +0,0 @@ -/* GSheetColumn --- an abstract model of the column geometry of a - * GSheet widget. - * Copyright (C) 2006 Free Software Foundation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GSHEET_COLUMN_IFACE_H -#define __GSHEET_COLUMN_IFACE_H - -#include -#include -#include - -#include "gtkextra-sheet.h" - - -G_BEGIN_DECLS - -#define G_TYPE_SHEET_COLUMN (g_sheet_column_get_type ()) -#define G_SHEET_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SHEET_COLUMN, GSheetColumn)) -#define G_IS_SHEET_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SHEET_COLUMN)) -#define G_SHEET_COLUMN_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SHEET_COLUMN, GSheetColumnIface)) - - -typedef struct _GSheetColumn GSheetColumn; -typedef struct _GSheetColumnIface GSheetColumnIface; -struct _GSheetColumnIface -{ - GTypeInterface g_iface; - - - /* Signals */ - void (* columns_changed) (GSheetColumn *geo, - glong col, glong n_columns); - - /* Virtual Table */ - gint (* get_width) (const GSheetColumn *gcolumn, glong col); - void (* set_width) (GSheetColumn *gcolumn, glong col, gint width); - - gboolean (* get_visibility) (const GSheetColumn *gcolumn, glong col); - gboolean (* get_sensitivity) (const GSheetColumn *gcolumn, glong col); - const GtkSheetButton * (* get_button) (const GSheetColumn *gcolumn, glong col); - GtkJustification (* get_justification) (const GSheetColumn *gcolumn, glong col); - - gint (*get_left_text_column) (const GSheetColumn *gcolumn, - glong col); - - gint (*get_right_text_column) (const GSheetColumn *gcolumn, - glong col); - - void (* set_left_text_column) (const GSheetColumn *gcolumn, - glong col, gint i); - - void (* set_right_text_column) (const GSheetColumn *gcolumn, - glong col, gint i); - - glong (* get_column_count) (const GSheetColumn *geo); - - - GtkStateType (*get_button_state)(const GSheetColumn *geo, glong col); - gchar * (*get_button_label)(const GSheetColumn *geo, glong col); - gchar * (*get_subtitle)(const GSheetColumn *geo, glong col); - - gboolean (*get_button_visibility)(const GSheetColumn *geo, - glong col); - const GtkSheetChild * (*get_button_child)(const GSheetColumn *geo, - glong col); - GtkJustification * (*get_button_justification)(const GSheetColumn *geo, - glong col); -}; - - -inline GType g_sheet_column_get_type (void) G_GNUC_CONST; - - -inline gint g_sheet_column_get_width (const GSheetColumn *gcolumn, - glong col); - - -inline void g_sheet_column_set_width (GSheetColumn *gcolumn, - glong col, gint size); - - -inline gboolean g_sheet_column_get_visibility (const GSheetColumn *gcolumn, - glong col); - -inline gboolean g_sheet_column_get_sensitivity (const GSheetColumn *gcolumn, - glong col); - - -inline GtkSheetButton *g_sheet_column_get_button (const GSheetColumn *gcolumn, - glong col); - -gchar *g_sheet_column_get_subtitle (const GSheetColumn *, glong); - -inline GtkJustification g_sheet_column_get_justification (const GSheetColumn *gcolumn, glong col); - - -inline gint g_sheet_column_get_left_text_column (const GSheetColumn *gcolumn, - glong col); - -inline gint g_sheet_column_get_right_text_column (const GSheetColumn *gcolumn, - glong col); - -inline void g_sheet_column_set_left_text_column (const GSheetColumn *gcolumn, - glong col, gint i); - - -inline void g_sheet_column_set_right_text_column (const GSheetColumn *gcolumn, - glong col, gint i); - - -inline glong g_sheet_column_get_column_count (const GSheetColumn *geo); - -inline gint g_sheet_column_start_pixel (const GSheetColumn *geo, glong col); - -inline void g_sheet_column_columns_changed (GSheetColumn *geo, - glong first, glong n_columns); - -G_END_DECLS - -#endif /* __G_SHEET_COLUMN_IFACE_H__ */ diff --git a/lib/gtksheet/gsheet-hetero-column.c b/lib/gtksheet/gsheet-hetero-column.c deleted file mode 100644 index ecc06cc5..00000000 --- a/lib/gtksheet/gsheet-hetero-column.c +++ /dev/null @@ -1,238 +0,0 @@ -/* gsheet-hetero-column.c - * PSPPIRE --- A Graphical User Interface for PSPP - * Copyright (C) 2006 Free Software Foundation - * - * 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 - */ -#include - -#include "gsheet-column-iface.h" -#include "gsheet-hetero-column.h" -#include - - -static void g_sheet_hetero_column_init (GSheetHeteroColumn *hg); -static void g_sheet_hetero_column_class_init (GSheetHeteroColumnClass *class); -static void g_sheet_hetero_column_finalize (GObject *object); - -static void g_sheet_column_init (GSheetColumnIface *iface); - - -static GObjectClass *parent_class = NULL; - -GType -g_sheet_hetero_column_get_type (void) -{ - static GType hetero_column_type = 0; - - if (!hetero_column_type) - { - static const GTypeInfo hetero_column_info = - { - sizeof (GSheetHeteroColumnClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) g_sheet_hetero_column_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GSheetHeteroColumn), - 0, - (GInstanceInitFunc) g_sheet_hetero_column_init, - }; - - static const GInterfaceInfo column_info = - { - (GInterfaceInitFunc) g_sheet_column_init, - NULL, - NULL - }; - - hetero_column_type = - g_type_register_static (G_TYPE_OBJECT, "g_sheet_hetero_column", - &hetero_column_info, 0); - - g_type_add_interface_static (hetero_column_type, - G_TYPE_SHEET_COLUMN, - &column_info); - } - - return hetero_column_type; -} - - -static GtkSheetButton default_button; - - - -/** - * g_sheet_hetero_column_new: - * @width: The size of columns in this hetero column - * - * Return value: a new #g_sheet_hetero_column - **/ -GObject * -g_sheet_hetero_column_new (gint default_width, gint n_columns) -{ - gint i; - GSheetHeteroColumn *hg; - GObject *retval; - - retval = g_object_new (G_TYPE_SHEET_HETERO_COLUMN, NULL); - - hg = G_SHEET_HETERO_COLUMN (retval); - hg->n_columns = n_columns; - hg->default_width = default_width; - hg->col = g_new0 (struct GSheetHeteroColumnUnit, n_columns); - - for (i = 0 ; i < hg->n_columns; ++i ) - { - hg->col[i].button = default_button; - } - - return retval; -} - -static gint -g_sheet_hetero_column_get_width (const GSheetColumn *geom, glong i) -{ - GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geom); - - g_return_val_if_fail (i < hg->n_columns, -1); - - return hg->col[i].width; -} - -static gboolean -g_sheet_hetero_column_get_sensitivity (const GSheetColumn *geom, glong u) -{ - return TRUE; -} - - -static gboolean -g_sheet_hetero_column_get_visibility (const GSheetColumn *geom, glong u) -{ - return TRUE; -} - - - -static gchar * -g_sheet_hetero_column_get_button_label (const GSheetColumn *geom, glong u) -{ - GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geom); - - return g_locale_to_utf8 (hg->col[u].button.label, -1, 0, 0, 0); -} - - -static GtkJustification -g_sheet_hetero_column_get_justification (const GSheetColumn *geom, glong u) -{ - return GTK_JUSTIFY_FILL; -} - - - -static glong -g_sheet_hetero_column_get_column_count (const GSheetColumn *geom) -{ - GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geom); - - return hg->n_columns; -} - -static void -g_sheet_hetero_column_class_init (GSheetHeteroColumnClass *class) -{ - GObjectClass *object_class; - - parent_class = g_type_class_peek_parent (class); - object_class = (GObjectClass*) class; - - object_class->finalize = g_sheet_hetero_column_finalize; - - default_button.label=NULL; - default_button.child=NULL; - default_button.state=GTK_STATE_NORMAL; - default_button.justification=GTK_JUSTIFY_CENTER; - default_button.label_visible = TRUE; -} - - -static void -g_sheet_hetero_column_init (GSheetHeteroColumn *o) -{ -} - -static void -g_sheet_hetero_column_finalize (GObject *object) -{ - GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (object); - - g_free (hg->col); -} - -static void -hetero_column_set_width (GSheetColumn *geo, glong i, gint size) -{ - GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geo); - - g_return_if_fail (i < hg->n_columns); - - hg->col[i].width = size; -} - - - -static void -g_sheet_column_init (GSheetColumnIface *iface) -{ - iface->get_width = g_sheet_hetero_column_get_width ; - iface->set_width = hetero_column_set_width ; - iface->get_sensitivity = g_sheet_hetero_column_get_sensitivity ; - iface->get_visibility = g_sheet_hetero_column_get_visibility ; - iface->get_justification = g_sheet_hetero_column_get_justification; - iface->get_column_count = g_sheet_hetero_column_get_column_count; - - iface->get_button_label = g_sheet_hetero_column_get_button_label; -} - - -void -g_sheet_hetero_column_set_button_label (GSheetHeteroColumn *geo, - glong i, const gchar *label) -{ - g_return_if_fail (i < geo->n_columns); - - g_free (geo->col[i].button.label); - geo->col[i].button.label = g_malloc (strlen (label) + 1); - - g_stpcpy (geo->col[i].button.label, label); -} - - - - -void -g_sheet_hetero_column_set_width (GSheetHeteroColumn *geo, glong i, gint size) -{ - GSheetColumn *iface = G_SHEET_COLUMN (geo); - - hetero_column_set_width (iface, i, size); -} - - - diff --git a/lib/gtksheet/gsheet-hetero-column.h b/lib/gtksheet/gsheet-hetero-column.h deleted file mode 100644 index 507bc20a..00000000 --- a/lib/gtksheet/gsheet-hetero-column.h +++ /dev/null @@ -1,88 +0,0 @@ -/* GtkSheet widget for Gtk+. - * Copyright (C) 2006 Free Software Foundation - - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __G_SHEET_HETERO_COLUMN_H__ -#define __G_SHEET_HETERO_COLUMN_H__ - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -#define G_TYPE_SHEET_HETERO_COLUMN (g_sheet_hetero_column_get_type ()) - -#define G_SHEET_HETERO_COLUMN(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, G_TYPE_SHEET_HETERO_COLUMN, GSheetHeteroColumn ) -#define G_SHEET_HETERO_COLUMN_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, g_sheet_hetero_column_get_type (), GSheetHeteroColumnClass) -#define G_IS_SHEET_HETERO_COLUMN(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_SHEET_HETERO_COLUMN) - - - struct GSheetHeteroColumnUnit - { - GtkSheetButton button; - - gint width; - gboolean is_sensitive; - gboolean is_visible; - }; - - - struct _GSheetHeteroColumn{ - GObject parent; - - gint n_columns; - gint default_width; - - struct GSheetHeteroColumnUnit *col; - - }; - - struct _GSheetHeteroColumnClass - { - GObjectClass parent_class; - }; - - - - - /* create a new column */ - GObject * g_sheet_hetero_column_new (gint default_width, gint n_columns); - - GType g_sheet_hetero_column_get_type (void); - - - typedef struct _GSheetHeteroColumn GSheetHeteroColumn; - typedef struct _GSheetHeteroColumnClass GSheetHeteroColumnClass; - - - void g_sheet_hetero_column_set_button_label (GSheetHeteroColumn *geo, - glong i, const gchar *label); - - void g_sheet_hetero_column_set_width (GSheetHeteroColumn *geo, - glong i, gint size); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __G_SHEET_HETERO_COLUMN_H__ */ - - diff --git a/lib/gtksheet/gsheet-row-iface.c b/lib/gtksheet/gsheet-row-iface.c deleted file mode 100644 index 1512737f..00000000 --- a/lib/gtksheet/gsheet-row-iface.c +++ /dev/null @@ -1,271 +0,0 @@ -/* GSheetRow --- an abstract model of the row geometry of a - * GSheet widget. - * Copyright (C) 2006 Free Software Foundation - * - * 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 - */ -#include - -#include -#include -#include -#include -#include -#include "gsheet-row-iface.h" -#include "gtkextra-marshal.h" - - -enum { - ROWS_CHANGED, - LAST_SIGNAL -}; - -static guint sheet_row_signals[LAST_SIGNAL]; - - - -static void g_sheet_row_base_init (gpointer g_class); - - -GType -g_sheet_row_get_type (void) -{ - static GType sheet_row_type = 0; - - if (! sheet_row_type) - { - static const GTypeInfo sheet_row_info = - - { - sizeof (GSheetRowIface), /* class_size */ - g_sheet_row_base_init, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; - - sheet_row_type = - g_type_register_static (G_TYPE_INTERFACE, "GSheetRow", - &sheet_row_info, 0); - - g_type_interface_add_prerequisite (sheet_row_type, G_TYPE_OBJECT); - } - - return sheet_row_type; -} - - -static GtkSheetButton default_button; - -static void -g_sheet_row_base_init (gpointer g_class) -{ - static gboolean initialized = FALSE; - - if (! initialized) - { - - sheet_row_signals[ROWS_CHANGED] = - g_signal_new ("rows_changed", - G_TYPE_SHEET_ROW, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSheetRowIface, rows_changed), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_INT); - - - default_button.state = GTK_STATE_NORMAL; - default_button.label = NULL; - default_button.label_visible = TRUE; - default_button.child = NULL; - default_button.justification = GTK_JUSTIFY_FILL; - - initialized = TRUE; - } -} - -void -g_sheet_row_set_height (GSheetRow *row_geo, - glong row, gint size) -{ - g_return_if_fail (G_IS_SHEET_ROW (row_geo)); - - if ((G_SHEET_ROW_GET_IFACE (row_geo)->set_height) ) - (G_SHEET_ROW_GET_IFACE (row_geo)->set_height) (row_geo, row, - size); -} - - -gint -g_sheet_row_get_height (const GSheetRow *row_geo, - glong row) -{ - g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), -1); - - g_assert (G_SHEET_ROW_GET_IFACE (row_geo)->get_height); - - return (G_SHEET_ROW_GET_IFACE (row_geo)->get_height) (row_geo, row); -} - - - -gboolean -g_sheet_row_get_visibility(const GSheetRow *row_geo, - glong row) -{ - g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), FALSE); - - g_assert (G_SHEET_ROW_GET_IFACE (row_geo)->get_visibility); - - return (G_SHEET_ROW_GET_IFACE (row_geo)->get_visibility) (row_geo, - row); - -} - -gboolean -g_sheet_row_get_sensitivity(const GSheetRow *row_geo, - glong row) -{ - g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), FALSE); - - g_assert (G_SHEET_ROW_GET_IFACE (row_geo)->get_sensitivity); - - return (G_SHEET_ROW_GET_IFACE (row_geo)->get_sensitivity) (row_geo, - row); - -} - - -GtkSheetButton * -g_sheet_row_get_button(const GSheetRow *row_geo, - glong row) -{ - GtkSheetButton *button = gtk_sheet_button_new(); - - GSheetRowIface *iface = G_SHEET_ROW_GET_IFACE (row_geo); - - g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), FALSE); - - if ( iface->get_button_label) - button->label = iface->get_button_label(row_geo, row); - - return button; -} - -gchar * -g_sheet_row_get_subtitle (const GSheetRow *row_geo, glong row) -{ - g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), NULL); - - if ( ! G_SHEET_ROW_GET_IFACE (row_geo)->get_subtitle ) - return NULL; - - return (G_SHEET_ROW_GET_IFACE (row_geo)->get_subtitle) (row_geo, row); -} - - - - -glong -g_sheet_row_get_row_count (const GSheetRow *geo) -{ - g_return_val_if_fail (G_IS_SHEET_ROW (geo), -1); - - g_assert ( G_SHEET_ROW_GET_IFACE (geo)->get_row_count); - - return (G_SHEET_ROW_GET_IFACE (geo)->get_row_count) (geo); -} - -/** - * g_sheet_row_start_pixel: - * @geo: the row model - * @row: the row number - * @sheet: pointer to the sheet - * - * Returns the top y pixel for ROW. - * Instances may override this method in order to achieve time and/or memory - * optmisation. - * - * Returns: the y coordinate of the top of the row. - */ - -gint -g_sheet_row_start_pixel(const GSheetRow *geo, glong row) -{ - gint i; - gint start_pixel = 0; - - g_return_val_if_fail (G_IS_SHEET_ROW (geo), -1); - g_return_val_if_fail (row >= 0, -1); - g_return_val_if_fail (row < - g_sheet_row_get_row_count(geo),-1); - - if ( G_SHEET_ROW_GET_IFACE(geo)->top_ypixel) - return (G_SHEET_ROW_GET_IFACE(geo)->top_ypixel)(geo, row); - - for ( i = 0 ; i < row ; ++i ) - { - if ( g_sheet_row_get_visibility(geo, i)) - start_pixel += g_sheet_row_get_height(geo, i); - } - - return start_pixel; -} - - -glong -g_sheet_row_pixel_to_row (const GSheetRow *geo, gint pixel) -{ - gint i, cy; - g_return_val_if_fail (G_IS_SHEET_ROW (geo), -1); - g_return_val_if_fail (pixel >= 0, -1) ; - - if ( G_SHEET_ROW_GET_IFACE(geo)->pixel_to_row) - return (G_SHEET_ROW_GET_IFACE(geo)->pixel_to_row)(geo, pixel); - - cy = 0; - for (i = 0; i < g_sheet_row_get_row_count (geo); ++i ) - { - if (pixel >= cy && - pixel <= (cy + g_sheet_row_get_height (geo, i)) && - g_sheet_row_get_visibility (geo, i)) - return i; - - if(g_sheet_row_get_visibility (geo, i)) - cy += g_sheet_row_get_height (geo, i); - } - - /* no match */ - return g_sheet_row_get_row_count (geo) - 1; -} - - - -void -g_sheet_row_rows_deleted (GSheetRow *geo, - glong first, glong n_rows) -{ - g_return_if_fail (G_IS_SHEET_ROW (geo)); - - g_signal_emit (geo, sheet_row_signals[ROWS_CHANGED], 0, - first, n_rows); -} diff --git a/lib/gtksheet/gsheet-row-iface.h b/lib/gtksheet/gsheet-row-iface.h deleted file mode 100644 index 921f3699..00000000 --- a/lib/gtksheet/gsheet-row-iface.h +++ /dev/null @@ -1,122 +0,0 @@ -/* GSheetRow --- an abstract model of the row geometry of a - * GSheet widget. - * Copyright (C) 2006 Free Software Foundation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __G_SHEET_ROW_IFACE_H__ -#define __G_SHEET_ROW_IFACE_H__ - -#include -#include -#include - -#include "gtkextra-sheet.h" - - -G_BEGIN_DECLS - -#define G_TYPE_SHEET_ROW (g_sheet_row_get_type ()) -#define G_SHEET_ROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SHEET_ROW, GSheetRow)) -#define G_IS_SHEET_ROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SHEET_ROW)) -#define G_SHEET_ROW_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SHEET_ROW, GSheetRowIface)) - - - - -typedef struct _GSheetRow GSheetRow; -typedef struct _GSheetRowIface GSheetRowIface; - -struct _GSheetRowIface -{ - GTypeInterface g_iface; - - - /* Signals */ - void (* rows_changed) (GSheetRow *geo, - glong row, glong n_rows); - - /* Virtual Table */ - gint (* get_height) (const GSheetRow *grow, glong row); - void (* set_height) (GSheetRow *grow, glong row, gint height); - - gboolean (* get_visibility) (const GSheetRow *grow, glong row); - - gboolean (* get_sensitivity) (const GSheetRow *grow, glong row); - - const GtkSheetButton * (* get_button) (const GSheetRow *grow, glong row); - - glong (* get_row_count) (const GSheetRow *geo); - - GtkStateType (*get_button_state) (const GSheetRow *geo, glong row); - - gchar * (*get_button_label) (const GSheetRow *geo, glong row); - - gchar * (*get_subtitle) (const GSheetRow *geo, glong row); - - gboolean (*get_button_visibility) (const GSheetRow *geo, - glong row); - - const GtkSheetChild * (*get_button_child) (const GSheetRow *geo, - glong row); - - guint (*top_ypixel) (const GSheetRow *geo, glong row); - glong (*pixel_to_row) (const GSheetRow *geo, guint pixel); -}; - - -GType g_sheet_row_get_type (void) G_GNUC_CONST; - - -gint g_sheet_row_get_height (const GSheetRow *grow, - glong row); - - -void g_sheet_row_set_height (GSheetRow *grow, - glong row, gint size); - - -gboolean g_sheet_row_get_visibility (const GSheetRow *grow, - glong row); - -gboolean g_sheet_row_get_sensitivity (const GSheetRow *grow, - glong row); - - -GtkSheetButton *g_sheet_row_get_button (const GSheetRow *grow, - glong row); - - -glong g_sheet_row_get_row_count (const GSheetRow *geo); - -/* Return the top pixel of row ROW */ -gint g_sheet_row_start_pixel (const GSheetRow *geo, glong row); - - -/* Return the row contained by pixel PIXEL */ -glong g_sheet_row_pixel_to_row (const GSheetRow *geo, gint pixel); - - -void g_sheet_row_rows_deleted (GSheetRow *geo, - glong first, glong n_rows); - - -gchar *g_sheet_row_get_subtitle (const GSheetRow *row_geo, glong row); - - -G_END_DECLS - -#endif /* __G_SHEET_ROW_IFACE_H__ */ diff --git a/lib/gtksheet/gsheet-uniform-column.c b/lib/gtksheet/gsheet-uniform-column.c deleted file mode 100644 index 5093da2f..00000000 --- a/lib/gtksheet/gsheet-uniform-column.c +++ /dev/null @@ -1,184 +0,0 @@ -/* gsheet-uniform-column.c - * - * PSPPIRE --- A Graphical User Interface for PSPP - * Copyright (C) 2006 Free Software Foundation - * - * 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 - */ - -#include - -#include "gsheet-column-iface.h" -#include "gsheet-uniform-column.h" - - -static void g_sheet_uniform_column_init (GSheetUniformColumn *ug); -static void g_sheet_uniform_column_class_init (GSheetUniformColumnClass *class); -static void g_sheet_uniform_column_finalize (GObject *object); - -static void g_sheet_column_init (GSheetColumnIface *iface); - - -static GObjectClass *parent_class = NULL; - -GType -g_sheet_uniform_column_get_type (void) -{ - static GType uniform_column_type = 0; - - if (!uniform_column_type) - { - static const GTypeInfo uniform_column_info = - { - sizeof (GSheetUniformColumnClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) g_sheet_uniform_column_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GSheetUniformColumn), - 0, - (GInstanceInitFunc) g_sheet_uniform_column_init, - }; - - static const GInterfaceInfo column_info = - { - (GInterfaceInitFunc) g_sheet_column_init, - NULL, - NULL - }; - - uniform_column_type = - g_type_register_static (G_TYPE_OBJECT, "g_sheet_uniform_column", - &uniform_column_info, 0); - - g_type_add_interface_static (uniform_column_type, - G_TYPE_SHEET_COLUMN, - &column_info); - } - - return uniform_column_type; -} - - -/** - * g_sheet_uniform_column_new: - * @width: The size of columns in this uniform column - * - * Return value: a new #g_sheet_uniform_column - **/ -GObject * -g_sheet_uniform_column_new (gint width, gint n_columns) -{ - GSheetUniformColumn *ug; - GObject *retval; - - retval = g_object_new (G_TYPE_SHEET_UNIFORM_COLUMN, NULL); - - ug = G_SHEET_UNIFORM_COLUMN(retval); - ug->n_columns = n_columns; - ug->width = width; - ug->is_visible = TRUE; - ug->is_sensitive = FALSE; - - return retval; -} - -static gint -g_sheet_uniform_column_get_width (const GSheetColumn *geom, glong u) -{ - GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom); - - return ug->width; -} - -static gboolean -g_sheet_uniform_column_get_sensitivity (const GSheetColumn *geom, glong u) -{ - GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom); - - return ug->is_sensitive; -} - - -static gboolean -g_sheet_uniform_column_get_visibility (const GSheetColumn *geom, glong u) -{ - GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom); - - return ug->is_visible; -} - - -static gchar * -g_sheet_uniform_column_get_button_label (const GSheetColumn *geom, glong u) -{ - gchar *label = g_strdup_printf ("%ld", u); - - return label; -} - - -static GtkJustification -g_sheet_uniform_column_get_justification (const GSheetColumn *geom, glong u) -{ - return GTK_JUSTIFY_FILL; -} - - - -static glong -g_sheet_uniform_column_get_column_count (const GSheetColumn *geom) -{ - GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom); - - return ug->n_columns; -} - -static void -g_sheet_uniform_column_class_init (GSheetUniformColumnClass *class) -{ - GObjectClass *object_class; - - parent_class = g_type_class_peek_parent (class); - object_class = (GObjectClass*) class; - - object_class->finalize = g_sheet_uniform_column_finalize; - -} - - -static void -g_sheet_uniform_column_init (GSheetUniformColumn *o) -{ -} - -static void -g_sheet_uniform_column_finalize (GObject *object) -{ -} - - -static void -g_sheet_column_init (GSheetColumnIface *iface) -{ - iface->get_width = g_sheet_uniform_column_get_width ; - iface->get_sensitivity = g_sheet_uniform_column_get_sensitivity ; - iface->get_visibility = g_sheet_uniform_column_get_visibility ; - iface->get_justification = g_sheet_uniform_column_get_justification; - iface->get_column_count = g_sheet_uniform_column_get_column_count; - iface->get_button_label = g_sheet_uniform_column_get_button_label; -} - diff --git a/lib/gtksheet/gsheet-uniform-column.h b/lib/gtksheet/gsheet-uniform-column.h deleted file mode 100644 index e56037b4..00000000 --- a/lib/gtksheet/gsheet-uniform-column.h +++ /dev/null @@ -1,68 +0,0 @@ -/* GtkSheet widget for Gtk+. - * Copyright (C) 2006 Free Software Foundation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __G_SHEET_UNIFORM_COLUMN_H__ -#define __G_SHEET_UNIFORM_COLUMN_H__ - - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -#define G_TYPE_SHEET_UNIFORM_COLUMN (g_sheet_uniform_column_get_type ()) - -#define G_SHEET_UNIFORM_COLUMN(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, G_TYPE_SHEET_UNIFORM_COLUMN, GSheetUniformColumn ) -#define G_SHEET_UNIFORM_COLUMN_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, g_sheet_uniform_column_get_type (), GSheetUniformColumnClass) -#define G_IS_SHEET_UNIFORM_COLUMN(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_SHEET_UNIFORM_COLUMN) - - - struct _GSheetUniformColumn{ - GObject parent; - - gint n_columns; - gint width; - gboolean is_sensitive; - gboolean is_visible; - }; - - struct _GSheetUniformColumnClass - { - GObjectClass parent_class; - }; - - /* create a new column */ - GObject * g_sheet_uniform_column_new (gint width, gint n_columns); - - GType g_sheet_uniform_column_get_type (void); - - - typedef struct _GSheetUniformColumn GSheetUniformColumn; - typedef struct _GSheetUniformColumnClass GSheetUniformColumnClass; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __G_SHEET_UNIFORM_COLUMN_H__ */ - - diff --git a/lib/gtksheet/gsheet-uniform-row.c b/lib/gtksheet/gsheet-uniform-row.c deleted file mode 100644 index 7ab9b600..00000000 --- a/lib/gtksheet/gsheet-uniform-row.c +++ /dev/null @@ -1,201 +0,0 @@ -/* gsheet-uniform-row.c - * - * PSPPIRE --- A Graphical User Interface for PSPP - * Copyright (C) 2006 Free Software Foundation - * - * 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 - */ - -#include - -#include "gsheet-row-iface.h" -#include "gsheet-uniform-row.h" - - -static void g_sheet_uniform_row_init (GSheetUniformRow *ug); -static void g_sheet_uniform_row_class_init (GSheetUniformRowClass *class); -static void g_sheet_uniform_row_finalize (GObject *object); - -static void g_sheet_row_init (GSheetRowIface *iface); - - -static GObjectClass *parent_class = NULL; - -GType -g_sheet_uniform_row_get_type (void) -{ - static GType uniform_row_type = 0; - - if (!uniform_row_type) - { - static const GTypeInfo uniform_row_info = - { - sizeof (GSheetUniformRowClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) g_sheet_uniform_row_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GSheetUniformRow), - 0, - (GInstanceInitFunc) g_sheet_uniform_row_init, - }; - - static const GInterfaceInfo row_info = - { - (GInterfaceInitFunc) g_sheet_row_init, - NULL, - NULL - }; - - uniform_row_type = - g_type_register_static (G_TYPE_OBJECT, "g_sheet_uniform_row", - &uniform_row_info, 0); - - g_type_add_interface_static (uniform_row_type, - G_TYPE_SHEET_ROW, - &row_info); - } - - return uniform_row_type; -} - - -/** - * g_sheet_uniform_row_new: - * @height: The size of rows in this uniform row - * - * Return value: a new #g_sheet_uniform_row - **/ -GObject * -g_sheet_uniform_row_new (gint height, gint n_rows) -{ - GSheetUniformRow *ug; - GObject *retval; - - retval = g_object_new (G_TYPE_SHEET_UNIFORM_ROW, NULL); - - ug = G_SHEET_UNIFORM_ROW(retval); - ug->n_rows = n_rows; - ug->height = height; - ug->is_visible = TRUE; - - return retval; -} - -static gint -g_sheet_uniform_row_get_height (const GSheetRow *geom, glong u) -{ - GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geom); - - return ug->height; -} - -static gboolean -g_sheet_uniform_row_get_sensitivity (const GSheetRow *geom, glong u) -{ - GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geom); - - return (u < ug->n_rows); -} - - -static gboolean -g_sheet_uniform_row_get_visibility (const GSheetRow *geom, glong u) -{ - GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW (geom); - - return ug->is_visible; -} - - -static gchar * -g_sheet_uniform_row_get_button_label (const GSheetRow *geom, glong u) -{ - gchar *label = g_strdup_printf("%ld", u); - - return label; -} - - - -static glong -g_sheet_uniform_row_get_row_count (const GSheetRow *geom) -{ - GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geom); - - return ug->n_rows; -} - - -static void -g_sheet_uniform_row_class_init (GSheetUniformRowClass *class) -{ - GObjectClass *object_class; - - parent_class = g_type_class_peek_parent (class); - object_class = (GObjectClass*) class; - - object_class->finalize = g_sheet_uniform_row_finalize; - -} - - -static void -g_sheet_uniform_row_init (GSheetUniformRow *o) -{ -} - -static void -g_sheet_uniform_row_finalize (GObject *object) -{ -} - - -static guint -g_sheet_uniform_row_top_ypixel (const GSheetRow *geo, glong row) -{ - GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geo); - - return row * ug->height; -} - -static glong -g_sheet_uniform_row_pixel_to_row (const GSheetRow *geo, guint pixel) -{ - GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geo); - - gint row = pixel / ug->height; - - if (row >= g_sheet_uniform_row_get_row_count(geo)) - row = g_sheet_uniform_row_get_row_count(geo) - 1; - - return row; -} - - - -static void -g_sheet_row_init (GSheetRowIface *iface) -{ - iface->get_height = g_sheet_uniform_row_get_height; - iface->get_sensitivity = g_sheet_uniform_row_get_sensitivity ; - iface->get_visibility = g_sheet_uniform_row_get_visibility; - iface->get_row_count = g_sheet_uniform_row_get_row_count; - iface->get_button_label = g_sheet_uniform_row_get_button_label; - iface->top_ypixel = g_sheet_uniform_row_top_ypixel; - iface->pixel_to_row = g_sheet_uniform_row_pixel_to_row; -} - diff --git a/lib/gtksheet/gsheet-uniform-row.h b/lib/gtksheet/gsheet-uniform-row.h deleted file mode 100644 index 845dbf6b..00000000 --- a/lib/gtksheet/gsheet-uniform-row.h +++ /dev/null @@ -1,66 +0,0 @@ -/* GtkSheet widget for Gtk+. - * Copyright (C) 2006 Free Software Foundation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __G_SHEET_UNIFORM_ROW_H__ -#define __G_SHEET_UNIFORM_ROW_H__ - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -#define G_TYPE_SHEET_UNIFORM_ROW (g_sheet_uniform_row_get_type ()) - -#define G_SHEET_UNIFORM_ROW(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, G_TYPE_SHEET_UNIFORM_ROW, GSheetUniformRow ) -#define G_SHEET_UNIFORM_ROW_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, g_sheet_uniform_row_get_type (), GSheetUniformRowClass) -#define G_IS_SHEET_UNIFORM_ROW(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_SHEET_UNIFORM_ROW) - - - struct _GSheetUniformRow{ - GObject parent; - - gint n_rows; - gint height; - gboolean is_visible; - }; - - struct _GSheetUniformRowClass - { - GObjectClass parent_class; - }; - - /* create a new row */ - GObject * g_sheet_uniform_row_new (gint height, gint n_rows); - - GType g_sheet_uniform_row_get_type (void); - - - typedef struct _GSheetUniformRow GSheetUniformRow; - typedef struct _GSheetUniformRowClass GSheetUniformRowClass; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __G_SHEET_UNIFORM_ROW_H__ */ - - diff --git a/lib/gtksheet/gsheetmodel.c b/lib/gtksheet/gsheetmodel.c deleted file mode 100644 index 0d1a3f56..00000000 --- a/lib/gtksheet/gsheetmodel.c +++ /dev/null @@ -1,510 +0,0 @@ -/* GSheetModel --- an abstract model for the GSheet widget. - * Copyright (C) 2006 Free Software Foundation - * - * 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 - */ - -#include - -#include -#include "gsheetmodel.h" -#include "gtkextra-marshal.h" - -enum { - RANGE_CHANGED, - ROWS_INSERTED, - ROWS_DELETED, - COLUMNS_INSERTED, - COLUMNS_DELETED, - LAST_SIGNAL -}; - -static guint sheet_model_signals[LAST_SIGNAL] = { 0 }; - - -static void g_sheet_model_base_init (gpointer g_class); - - -GType -g_sheet_model_get_type (void) -{ - static GType sheet_model_type = 0; - - if (! sheet_model_type) - { - static const GTypeInfo sheet_model_info = - { - sizeof (GSheetModelIface), /* class_size */ - g_sheet_model_base_init, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; - - sheet_model_type = - g_type_register_static (G_TYPE_INTERFACE, "GSheetModel", - &sheet_model_info, 0); - - g_type_interface_add_prerequisite (sheet_model_type, G_TYPE_OBJECT); - } - - return sheet_model_type; -} - -static void -g_sheet_model_base_init (gpointer g_class) -{ - static gboolean initialized = FALSE; - - if (! initialized) - { - sheet_model_signals[RANGE_CHANGED] = - g_signal_new ("range_changed", - G_TYPE_SHEET_MODEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSheetModelIface, range_changed), - NULL, NULL, - gtkextra_VOID__INT_INT_INT_INT, - G_TYPE_NONE, 4, - G_TYPE_INT, - G_TYPE_INT, - G_TYPE_INT, - G_TYPE_INT); - - - - sheet_model_signals[ROWS_INSERTED] = - g_signal_new ("rows_inserted", - G_TYPE_SHEET_MODEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSheetModelIface, rows_inserted), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_INT); - - - sheet_model_signals[ROWS_DELETED] = - g_signal_new ("rows_deleted", - G_TYPE_SHEET_MODEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSheetModelIface, rows_deleted), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_INT); - - sheet_model_signals[COLUMNS_INSERTED] = - g_signal_new ("columns_inserted", - G_TYPE_SHEET_MODEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSheetModelIface, columns_inserted), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_INT); - - - sheet_model_signals[COLUMNS_DELETED] = - g_signal_new ("columns_deleted", - G_TYPE_SHEET_MODEL, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSheetModelIface, columns_deleted), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_INT); - - - initialized = TRUE; - } -} - - -/** - * g_sheet_model_free_strings - * @sheet_model: A #GSheetModel - * - * Returns: True if strings obtained with get_string should be freed by the - * sheet when no longer required. - **/ -gboolean -g_sheet_model_free_strings (const GSheetModel *sheet_model) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), FALSE); - - return G_SHEET_MODEL_GET_IFACE (sheet_model)->free_strings; -} - - -/** - * g_sheet_model_get_string: - * @sheet_model: A #GSheetModel - * @row: The row of the cell to be retrieved. - * @column: The column of the cell to be retrieved. - * - * Retrieves the datum at location ROW, COLUMN in the form of a string. - * Returns: The string representation of the datum, or NULL on error. - **/ -gchar * -g_sheet_model_get_string (const GSheetModel *sheet_model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), 0); - - g_assert (G_SHEET_MODEL_GET_IFACE (sheet_model)->get_string); - - return (G_SHEET_MODEL_GET_IFACE (sheet_model)->get_string) (sheet_model, row, column); -} - -/** - * g_sheet_model_set_string - * @sheet_model: A #GSheetModel - * @text: The text describing the datum to be set. - * @row: The row of the cell to be cleared. - * @column: The column of the cell to be cleared. - * - * Sets the datum at a location from a string. - * Returns: TRUE if the datum was changed, FALSE otherwise. - **/ -gboolean -g_sheet_model_set_string (GSheetModel *sheet_model, - const gchar *text, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), FALSE); - - g_assert (G_SHEET_MODEL_GET_IFACE (sheet_model)->set_string); - - return G_SHEET_MODEL_GET_IFACE (sheet_model)->set_string (sheet_model, - text, row, column); -} - - - -/** - * g_sheet_model_datum_clear: - * @sheet_model: A #GSheetModel - * @row: The row of the cell to be cleared. - * @column: The column of the cell to be cleared. - * - * Called when the datum at a location is to be cleared. - * Returns: TRUE if the datum was cleared, FALSE otherwise. - **/ -gboolean -g_sheet_model_datum_clear (GSheetModel *sheet_model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), FALSE); - - g_assert (G_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum); - - return G_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum (sheet_model, - row, column); -} - - -/** - * g_sheet_model_range_changed: - * @sheet_model: A #GSheetModel - * @range: The #GSheetRange range of cells which have changed. - * - * Emits the "range_changed" signal on @sheet_model. - **/ -void -g_sheet_model_range_changed (GSheetModel *sheet_model, - glong row0, glong col0, - glong rowi, glong coli) -{ - g_return_if_fail (G_IS_SHEET_MODEL (sheet_model)); - - g_signal_emit (sheet_model, sheet_model_signals[RANGE_CHANGED], 0, - row0, col0, rowi, coli); -} - - - - -/** - * g_sheet_model_rows_inserted: - * @sheet_model: A #GSheetModel - * @row: The row before which the new rows should be inserted. - * @n_rows: The number of rows to insert. - * - * Emits the "rows_inserted" signal on @sheet_model. - **/ -void -g_sheet_model_rows_inserted (GSheetModel *sheet_model, - glong row, glong n_rows) -{ - g_return_if_fail (G_IS_SHEET_MODEL (sheet_model)); - - g_signal_emit (sheet_model, sheet_model_signals[ROWS_INSERTED], 0, - row, n_rows); -} - - -/** - * g_sheet_model_columns_inserted: - * @sheet_model: A #GSheetModel - * @column: The column before which the new columns should be inserted. - * @n_columns: The number of columns to insert. - * - * Emits the "columns_inserted" signal on @sheet_model. - **/ -void -g_sheet_model_columns_inserted (GSheetModel *sheet_model, - glong column, glong n_columns) -{ - g_return_if_fail (G_IS_SHEET_MODEL (sheet_model)); - - g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_INSERTED], 0, - column, n_columns); -} - - - - -/** - * g_sheet_model_rows_deleted: - * @sheet_model: A #GSheetModel - * @row: The first row to be deleted. - * @n_rows: The number of rows to delete. - * - * Emits the "rows_deleted" signal on @sheet_model. - **/ -void -g_sheet_model_rows_deleted (GSheetModel *sheet_model, - glong row, glong n_rows) -{ - g_return_if_fail (G_IS_SHEET_MODEL (sheet_model)); - - g_signal_emit (sheet_model, sheet_model_signals[ROWS_DELETED], 0, - row, n_rows); -} - - - -/** - * g_sheet_model_columns_deleted: - * @sheet_model: A #GSheetModel - * @column: The first column to be deleted. - * @n_columns: The number of columns to delete. - * - * Emits the "columns_deleted" signal on @sheet_model. - **/ -void -g_sheet_model_columns_deleted (GSheetModel *sheet_model, - glong column, glong n_columns) -{ - g_return_if_fail (G_IS_SHEET_MODEL (sheet_model)); - - g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_DELETED], 0, - column, n_columns); -} - - - - - -/** - * g_sheet_model_is_editable: - * @sheet_model: A #GSheetModel - * @row: The row - * @column: The column - * - * Returns: TRUE if the cell is editable, FALSE otherwise - **/ -gboolean -g_sheet_model_is_editable (const GSheetModel *model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), TRUE); - - if ( ! G_SHEET_MODEL_GET_IFACE (model)->is_editable ) - return TRUE; - - return G_SHEET_MODEL_GET_IFACE (model)->is_editable (model, - row, column); -} - -/** - * g_sheet_model_is_visible: - * @sheet_model: A #GSheetModel - * @row: The row - * @column: The column - * - * Returns: TRUE if the cell is visible, FALSE otherwise - **/ -gboolean -g_sheet_model_is_visible (const GSheetModel *model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), TRUE); - - if ( ! G_SHEET_MODEL_GET_IFACE (model)->is_visible ) - return TRUE; - - return G_SHEET_MODEL_GET_IFACE (model)->is_visible (model, - row, column); -} - - -/** - * g_sheet_model_get_foreground: - * @sheet_model: A #GSheetModel - * @row: The row - * @column: The column - * - * Returns the foreground colour of the cell at @row, @column - * Returns: the foreground colour, or NULL on error. - **/ -const GdkColor * -g_sheet_model_get_foreground (const GSheetModel *model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL); - - if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_foreground ) - return NULL; - - return G_SHEET_MODEL_GET_IFACE (model)->get_foreground (model, - row, column); -} - -/** - * g_sheet_model_get_background: - * @sheet_model: A #GSheetModel - * @row: The row - * @column: The column - * - * Returns the background colour of the cell at @row, @column - * Returns: the background colour, or NULL on error. - **/ -const GdkColor * -g_sheet_model_get_background (const GSheetModel *model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL); - - if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_background ) - return NULL; - - return G_SHEET_MODEL_GET_IFACE (model)->get_background (model, - row, column); -} - -/** - * g_sheet_model_get_justification: - * @sheet_model: A #GSheetModel - * @row: The row - * @column: The column - * - * Returns the justification of the cell at @row, @column - * Returns: the justification, or NULL on error. - **/ -const GtkJustification * -g_sheet_model_get_justification (const GSheetModel *model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL); - - if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_justification) - return NULL; - - return G_SHEET_MODEL_GET_IFACE (model)->get_justification (model, - row, column); -} - -/** - * g_sheet_model_get_font_desc: - * @sheet_model: A #GSheetModel - * @row: The row - * @column: The column - * - * Returns the font description of the cell at @row, @column - * Returns: the font description, or NULL on error. - **/ -const PangoFontDescription * -g_sheet_model_get_font_desc(const GSheetModel *model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL); - if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_font_desc) - return NULL; - - return G_SHEET_MODEL_GET_IFACE (model)->get_font_desc (model, - row, column); -} - -/** - * g_sheet_model_get_cell_border: - * @sheet_model: A #GSheetModel - * @row: The row - * @column: The column - * - * Returns the cell border of the cell at @row, @column - * Returns: the cell border, or NULL on error. - **/ -const GtkSheetCellBorder * -g_sheet_model_get_cell_border (const GSheetModel *model, - glong row, glong column) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL); - if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_cell_border) - return NULL; - - return G_SHEET_MODEL_GET_IFACE (model)->get_cell_border (model, - row, column); -} - - - -/** - * g_sheet_model_get_column_count: - * @model: A #GSheetModel - * - * Returns the total number of columns represented by the model - **/ -glong -g_sheet_model_get_column_count (const GSheetModel *model) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), -1); - - return G_SHEET_MODEL_GET_IFACE (model)->get_column_count (model); -} - -/** - * g_sheet_model_get_row_count: - * @model: A #GSheetModel - * - * Returns the total number of rows represented by the model - **/ -gint -g_sheet_model_get_row_count(const GSheetModel *model) -{ - g_return_val_if_fail (G_IS_SHEET_MODEL (model), -1); - - - return G_SHEET_MODEL_GET_IFACE (model)->get_row_count (model); -} diff --git a/lib/gtksheet/gsheetmodel.h b/lib/gtksheet/gsheetmodel.h deleted file mode 100644 index 6d60e032..00000000 --- a/lib/gtksheet/gsheetmodel.h +++ /dev/null @@ -1,197 +0,0 @@ -/* GSheetModel --- an abstract model for the GtkSheet widget. - * Copyright (C) 2006 Free Software Foundation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __G_SHEET_MODEL_H__ -#define __G_SHEET_MODEL_H__ - - -/* This file provides an abstract interface or the data displayed by the - GtkSheet widget */ - -#include -#include -#include - - -G_BEGIN_DECLS - -#define G_TYPE_SHEET_MODEL (g_sheet_model_get_type ()) -#define G_SHEET_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SHEET_MODEL, GSheetModel)) -#define G_IS_SHEET_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SHEET_MODEL)) -#define G_SHEET_MODEL_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SHEET_MODEL, GSheetModelIface)) - -typedef enum -{ - GTK_SHEET_LEFT_BORDER = 1 << 0, - GTK_SHEET_RIGHT_BORDER = 1 << 1, - GTK_SHEET_TOP_BORDER = 1 << 2, - GTK_SHEET_BOTTOM_BORDER = 1 << 3 -} GtkSheetBorderType ; - - -typedef struct _GSheetModel GSheetModel; /* Dummy typedef */ -typedef struct _GSheetModelIface GSheetModelIface; -typedef struct _GtkSheetRange GtkSheetRange; -typedef struct _GtkSheetCellBorder GtkSheetCellBorder; - -struct _GtkSheetRange -{ - glong row0,col0; /* upper-left cell */ - glong rowi,coli; /* lower-right cell */ -}; - -struct _GtkSheetCellBorder -{ - GtkSheetBorderType mask; - guint width; - GdkLineStyle line_style; - GdkCapStyle cap_style; - GdkJoinStyle join_style; - GdkColor color; -}; - - - -struct _GSheetModelIface -{ - GTypeInterface g_iface; - - gboolean free_strings; - - /* Signals */ - void (* range_changed) (GSheetModel *sheet_model, - glong row0, glong col0, - glong rowi, glong coli); - - void (* rows_inserted) (GSheetModel *sheet_model, - glong row, glong n_rows); - - void (* rows_deleted) (GSheetModel *sheet_model, - glong row, glong n_rows); - - void (* columns_inserted) (GSheetModel *sheet_model, - glong column, glong n_columns); - - void (* columns_deleted) (GSheetModel *sheet_model, - glong column, glong n_columns); - - - - - /* Virtual Table */ - - gchar * (* get_string) (const GSheetModel *sheet_model, - glong row, glong column); - - gboolean (* set_string) (GSheetModel *sheet_model, - const gchar *s, glong row, glong column); - - gboolean (* clear_datum) (GSheetModel *sheet_model, - glong row, glong column); - - gboolean (* is_visible) (const GSheetModel *sheet_model, glong row, glong column); - gboolean (* is_editable) (const GSheetModel *sheet_model, glong row, glong column); - - const GdkColor * (* get_foreground) (const GSheetModel *sheet_model, - glong row, glong column); - - const GdkColor * (* get_background) (const GSheetModel *sheet_model, - glong row, glong column); - - const GtkJustification * (* get_justification) (const GSheetModel *sheet_model, - glong row, glong column); - - const PangoFontDescription * (* get_font_desc) (const GSheetModel *sheet_model, - glong row, glong column); - - const GtkSheetCellBorder * (* get_cell_border) (const GSheetModel *sheet_model, - glong row, glong column); - - - glong (*get_column_count) (const GSheetModel *model); - - glong (*get_row_count) (const GSheetModel *model); - -}; - - - -GType g_sheet_model_get_type (void) G_GNUC_CONST; - - -inline gchar * g_sheet_model_get_string (const GSheetModel *sheet_model, - glong row, glong column); - -inline gboolean g_sheet_model_set_string (GSheetModel *sheet_model, - const gchar *s, - glong row, glong column); - -inline gboolean g_sheet_model_datum_clear (GSheetModel *sheet_model, - glong row, glong column); - - -inline void g_sheet_model_range_changed (GSheetModel *sheet_model, - glong row0, glong col0, - glong rowi, glong coli); - -inline void g_sheet_model_rows_deleted (GSheetModel *sheet_model, - glong row, glong n_rows); - -inline void g_sheet_model_rows_inserted (GSheetModel *sheet_model, - glong row, glong n_rows); - -inline void g_sheet_model_columns_inserted (GSheetModel *sheet_model, - glong column, glong n_columns); - -inline void g_sheet_model_columns_deleted (GSheetModel *sheet_model, - glong column, glong n_columns); - - -inline gboolean g_sheet_model_is_editable (const GSheetModel *model, - glong row, glong column); - -inline gboolean g_sheet_model_is_visible - (const GSheetModel *model, glong row, glong column); - - -inline const GdkColor *g_sheet_model_get_foreground - (const GSheetModel *model, glong row, glong column); - -inline const GdkColor *g_sheet_model_get_background - (const GSheetModel *model, glong row, glong column); - - -inline const GtkJustification *g_sheet_model_get_justification - (const GSheetModel *model, glong row, glong column); - - -inline const PangoFontDescription *g_sheet_model_get_font_desc - (const GSheetModel *model, glong row, glong column); - -inline const GtkSheetCellBorder * g_sheet_model_get_cell_border - (const GSheetModel *model, glong row, glong column); - -inline gboolean g_sheet_model_free_strings (const GSheetModel *sheet_model); - -inline glong g_sheet_model_get_column_count (const GSheetModel *sheet_model); - -inline gint g_sheet_model_get_row_count (const GSheetModel *sheet_model); - -G_END_DECLS - -#endif /* __G_SHEET_MODEL_H__ */ diff --git a/lib/gtksheet/gtkextra-marshal.c b/lib/gtksheet/gtkextra-marshal.c deleted file mode 100644 index f8c84481..00000000 --- a/lib/gtksheet/gtkextra-marshal.c +++ /dev/null @@ -1,893 +0,0 @@ -#include - -#include - - -#ifdef G_ENABLE_DEBUG -#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) -#define g_marshal_value_peek_char(v) g_value_get_char (v) -#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) -#define g_marshal_value_peek_int(v) g_value_get_int (v) -#define g_marshal_value_peek_uint(v) g_value_get_uint (v) -#define g_marshal_value_peek_long(v) g_value_get_long (v) -#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) -#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) -#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) -#define g_marshal_value_peek_enum(v) g_value_get_enum (v) -#define g_marshal_value_peek_flags(v) g_value_get_flags (v) -#define g_marshal_value_peek_float(v) g_value_get_float (v) -#define g_marshal_value_peek_double(v) g_value_get_double (v) -#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) -#define g_marshal_value_peek_param(v) g_value_get_param (v) -#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) -#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) -#define g_marshal_value_peek_object(v) g_value_get_object (v) -#else /* !G_ENABLE_DEBUG */ -/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. - * Do not access GValues directly in your code. Instead, use the - * g_value_get_*() functions - */ -#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int -#define g_marshal_value_peek_char(v) (v)->data[0].v_int -#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint -#define g_marshal_value_peek_int(v) (v)->data[0].v_int -#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint -#define g_marshal_value_peek_long(v) (v)->data[0].v_long -#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong -#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 -#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 -#define g_marshal_value_peek_enum(v) (v)->data[0].v_int -#define g_marshal_value_peek_flags(v) (v)->data[0].v_uint -#define g_marshal_value_peek_float(v) (v)->data[0].v_float -#define g_marshal_value_peek_double(v) (v)->data[0].v_double -#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer -#endif /* !G_ENABLE_DEBUG */ - - -/* BOOL:INT,INT,POINTER,POINTER (gtkextra-marshal.list:1) */ -void -gtkextra_BOOLEAN__INT_INT_POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER) (gpointer data1, - gint arg_1, - gint arg_2, - gpointer arg_3, - gpointer arg_4, - gpointer data2); - register GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 5); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_int (param_values + 1), - g_marshal_value_peek_int (param_values + 2), - g_marshal_value_peek_pointer (param_values + 3), - g_marshal_value_peek_pointer (param_values + 4), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:BOXED,POINTER (gtkextra-marshal.list:2) */ -void -gtkextra_BOOLEAN__BOXED_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_POINTER) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_BOOLEAN__BOXED_POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_boxed (param_values + 1), - g_marshal_value_peek_pointer (param_values + 2), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:BOXED,STRING (gtkextra-marshal.list:3) */ -void -gtkextra_BOOLEAN__BOXED_STRING (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_STRING) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_BOOLEAN__BOXED_STRING callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__BOXED_STRING) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_boxed (param_values + 1), - g_marshal_value_peek_string (param_values + 2), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:BOXED,BOXED (gtkextra-marshal.list:4) */ -void -gtkextra_BOOLEAN__BOXED_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_BOXED) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_BOOLEAN__BOXED_BOXED callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_boxed (param_values + 1), - g_marshal_value_peek_boxed (param_values + 2), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:BOXED,DOUBLE,DOUBLE (gtkextra-marshal.list:5) */ -void -gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE) (gpointer data1, - gpointer arg_1, - gdouble arg_2, - gdouble arg_3, - gpointer data2); - register GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 4); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_boxed (param_values + 1), - g_marshal_value_peek_double (param_values + 2), - g_marshal_value_peek_double (param_values + 3), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:POINTER,POINTER (gtkextra-marshal.list:6) */ -void -gtkextra_BOOLEAN__POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_BOOLEAN__POINTER_POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_pointer (param_values + 1), - g_marshal_value_peek_pointer (param_values + 2), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:POINTER,BOXED (gtkextra-marshal.list:7) */ -void -gtkextra_BOOLEAN__POINTER_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_BOXED) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_BOOLEAN__POINTER_BOXED callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__POINTER_BOXED) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_pointer (param_values + 1), - g_marshal_value_peek_boxed (param_values + 2), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:POINTER,STRING (gtkextra-marshal.list:8) */ -void -gtkextra_BOOLEAN__POINTER_STRING (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_STRING) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_BOOLEAN__POINTER_STRING callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__POINTER_STRING) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_pointer (param_values + 1), - g_marshal_value_peek_string (param_values + 2), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:POINTER (gtkextra-marshal.list:9) */ -void -gtkextra_BOOLEAN__POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1, - gpointer arg_1, - gpointer data2); - register GMarshalFunc_BOOLEAN__POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 2); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_pointer (param_values + 1), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:BOXED (gtkextra-marshal.list:10) */ -void -gtkextra_BOOLEAN__BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer data1, - gpointer arg_1, - gpointer data2); - register GMarshalFunc_BOOLEAN__BOXED callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 2); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_boxed (param_values + 1), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* BOOL:INT,INT (gtkextra-marshal.list:11) */ -void -gtkextra_BOOLEAN__INT_INT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT) (gpointer data1, - gint arg_1, - gint arg_2, - gpointer data2); - register GMarshalFunc_BOOLEAN__INT_INT callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__INT_INT) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_int (param_values + 1), - g_marshal_value_peek_int (param_values + 2), - data2); - - g_value_set_boolean (return_value, v_return); -} - -/* VOID:INT (gtkextra-marshal.list:12) */ - -/* VOID:INT,STRING (gtkextra-marshal.list:13) */ -void -gtkextra_VOID__INT_STRING (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__INT_STRING) (gpointer data1, - gint arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_VOID__INT_STRING callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__INT_STRING) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_int (param_values + 1), - g_marshal_value_peek_string (param_values + 2), - data2); -} - -/* VOID:BOXED (gtkextra-marshal.list:14) */ - -/* VOID:VOID (gtkextra-marshal.list:15) */ - -/* VOID:BOOL (gtkextra-marshal.list:16) */ - -/* VOID:POINTER (gtkextra-marshal.list:17) */ - -/* VOID:INT,INT (gtkextra-marshal.list:18) */ -void -gtkextra_VOID__INT_INT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer data1, - gint arg_1, - gint arg_2, - gpointer data2); - register GMarshalFunc_VOID__INT_INT callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__INT_INT) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_int (param_values + 1), - g_marshal_value_peek_int (param_values + 2), - data2); -} - - -/* VOID:INT,INT,INT,INT (Added by JMD 1/1/2006) */ -void -gtkextra_VOID__INT_INT_INT_INT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__INT_INT_INT_INT) (gpointer data1, - gint arg_1, - gint arg_2, - gint arg_3, - gint arg_4, - gpointer data2); - register GMarshalFunc_VOID__INT_INT_INT_INT callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 5); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_int (param_values + 1), - g_marshal_value_peek_int (param_values + 2), - g_marshal_value_peek_int (param_values + 3), - g_marshal_value_peek_int (param_values + 4), - data2); -} - - -/* VOID:INT,POINTER (gtkextra-marshal.list:19) */ -void -gtkextra_VOID__INT_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__INT_POINTER) (gpointer data1, - gint arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_VOID__INT_POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__INT_POINTER) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_int (param_values + 1), - g_marshal_value_peek_pointer (param_values + 2), - data2); -} - -/* VOID:INT,BOXED (gtkextra-marshal.list:20) */ -void -gtkextra_VOID__INT_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__INT_BOXED) (gpointer data1, - gint arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_VOID__INT_BOXED callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__INT_BOXED) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_int (param_values + 1), - g_marshal_value_peek_boxed (param_values + 2), - data2); -} - -/* VOID:POINTER,POINTER (gtkextra-marshal.list:21) */ -void -gtkextra_VOID__POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_VOID__POINTER_POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_pointer (param_values + 1), - g_marshal_value_peek_pointer (param_values + 2), - data2); -} - -/* VOID:BOXED,POINTER (gtkextra-marshal.list:22) */ -void -gtkextra_VOID__BOXED_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_VOID__BOXED_POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_boxed (param_values + 1), - g_marshal_value_peek_pointer (param_values + 2), - data2); -} - -/* VOID:BOXED,BOXED (gtkextra-marshal.list:23) */ -void -gtkextra_VOID__BOXED_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_VOID__BOXED_BOXED callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_boxed (param_values + 1), - g_marshal_value_peek_boxed (param_values + 2), - data2); -} - -/* VOID:OBJECT,OBJECT (gtkextra-marshal.list:24) */ -void -gtkextra_VOID__OBJECT_OBJECT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer data2); - register GMarshalFunc_VOID__OBJECT_OBJECT callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 3); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_object (param_values + 1), - g_marshal_value_peek_object (param_values + 2), - data2); -} - -/* VOID:DOUBLE,DOUBLE,DOUBLE,DOUBLE (gtkextra-marshal.list:25) */ -void -gtkextra_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE) (gpointer data1, - gdouble arg_1, - gdouble arg_2, - gdouble arg_3, - gdouble arg_4, - gpointer data2); - register GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 5); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_double (param_values + 1), - g_marshal_value_peek_double (param_values + 2), - g_marshal_value_peek_double (param_values + 3), - g_marshal_value_peek_double (param_values + 4), - data2); -} - diff --git a/lib/gtksheet/gtkextra-marshal.h b/lib/gtksheet/gtkextra-marshal.h deleted file mode 100644 index ea9ed5e2..00000000 --- a/lib/gtksheet/gtkextra-marshal.h +++ /dev/null @@ -1,208 +0,0 @@ - -#ifndef __gtkextra_MARSHAL_H__ -#define __gtkextra_MARSHAL_H__ - -#include - -G_BEGIN_DECLS - -/* BOOL:INT,INT,POINTER,POINTER (gtkextra-marshal.list:1) */ -extern void gtkextra_BOOLEAN__INT_INT_POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__INT_INT_POINTER_POINTER gtkextra_BOOLEAN__INT_INT_POINTER_POINTER - -/* BOOL:BOXED,POINTER (gtkextra-marshal.list:2) */ -extern void gtkextra_BOOLEAN__BOXED_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__BOXED_POINTER gtkextra_BOOLEAN__BOXED_POINTER - -/* BOOL:BOXED,STRING (gtkextra-marshal.list:3) */ -extern void gtkextra_BOOLEAN__BOXED_STRING (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__BOXED_STRING gtkextra_BOOLEAN__BOXED_STRING - -/* BOOL:BOXED,BOXED (gtkextra-marshal.list:4) */ -extern void gtkextra_BOOLEAN__BOXED_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__BOXED_BOXED gtkextra_BOOLEAN__BOXED_BOXED - -/* BOOL:BOXED,DOUBLE,DOUBLE (gtkextra-marshal.list:5) */ -extern void gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__BOXED_DOUBLE_DOUBLE gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE - -/* BOOL:POINTER,POINTER (gtkextra-marshal.list:6) */ -extern void gtkextra_BOOLEAN__POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__POINTER_POINTER gtkextra_BOOLEAN__POINTER_POINTER - -/* BOOL:POINTER,BOXED (gtkextra-marshal.list:7) */ -extern void gtkextra_BOOLEAN__POINTER_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__POINTER_BOXED gtkextra_BOOLEAN__POINTER_BOXED - -/* BOOL:POINTER,STRING (gtkextra-marshal.list:8) */ -extern void gtkextra_BOOLEAN__POINTER_STRING (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__POINTER_STRING gtkextra_BOOLEAN__POINTER_STRING - -/* BOOL:POINTER (gtkextra-marshal.list:9) */ -extern void gtkextra_BOOLEAN__POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__POINTER gtkextra_BOOLEAN__POINTER - -/* BOOL:BOXED (gtkextra-marshal.list:10) */ -extern void gtkextra_BOOLEAN__BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__BOXED gtkextra_BOOLEAN__BOXED - -/* BOOL:INT,INT (gtkextra-marshal.list:11) */ -extern void gtkextra_BOOLEAN__INT_INT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -#define gtkextra_BOOL__INT_INT gtkextra_BOOLEAN__INT_INT - -/* VOID:INT (gtkextra-marshal.list:12) */ -#define gtkextra_VOID__INT g_cclosure_marshal_VOID__INT - -/* VOID:INT,STRING (gtkextra-marshal.list:13) */ -extern void gtkextra_VOID__INT_STRING (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:BOXED (gtkextra-marshal.list:14) */ -#define gtkextra_VOID__BOXED g_cclosure_marshal_VOID__BOXED - -/* VOID:VOID (gtkextra-marshal.list:15) */ -#define gtkextra_VOID__VOID g_cclosure_marshal_VOID__VOID - -/* VOID:BOOL (gtkextra-marshal.list:16) */ -#define gtkextra_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN -#define gtkextra_VOID__BOOL gtkextra_VOID__BOOLEAN - -/* VOID:POINTER (gtkextra-marshal.list:17) */ -#define gtkextra_VOID__POINTER g_cclosure_marshal_VOID__POINTER - -/* VOID:INT,INT (gtkextra-marshal.list:18) */ -extern void gtkextra_VOID__INT_INT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:INT,INT,INT,INT (Added by JMD 1/1/26) */ -extern void gtkextra_VOID__INT_INT_INT_INT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - - -/* VOID:INT,POINTER (gtkextra-marshal.list:19) */ -extern void gtkextra_VOID__INT_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:INT,BOXED (gtkextra-marshal.list:20) */ -extern void gtkextra_VOID__INT_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:POINTER,POINTER (gtkextra-marshal.list:21) */ -extern void gtkextra_VOID__POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:BOXED,POINTER (gtkextra-marshal.list:22) */ -extern void gtkextra_VOID__BOXED_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:BOXED,BOXED (gtkextra-marshal.list:23) */ -extern void gtkextra_VOID__BOXED_BOXED (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:OBJECT,OBJECT (gtkextra-marshal.list:24) */ -extern void gtkextra_VOID__OBJECT_OBJECT (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -/* VOID:DOUBLE,DOUBLE,DOUBLE,DOUBLE (gtkextra-marshal.list:25) */ -extern void gtkextra_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -G_END_DECLS - -#endif /* __gtkextra_MARSHAL_H__ */ - diff --git a/lib/gtksheet/gtkextra-sheet.h b/lib/gtksheet/gtkextra-sheet.h deleted file mode 100644 index 0a5fb70f..00000000 --- a/lib/gtksheet/gtkextra-sheet.h +++ /dev/null @@ -1,78 +0,0 @@ -/* This version of GtkSheet has been heavily modified, for the specific - * requirements of PSPPIRE. - * - * GtkSheet widget for Gtk+. - * Copyright (C) 1999-2001 Adrian E. Feiguin - * - * Based on GtkClist widget by Jay Painter, but major changes. - * Memory allocation routines inspired on SC (Spreadsheet Calculator) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef __GTK_EXTRA_SHEET_H__ -#define __GTK_EXTRA_SHEET_H__ - - -struct _GtkSheet ; - -typedef struct _GtkSheet GtkSheet; - - -struct _GtkSheetChild -{ - GtkWidget *widget; - gint x,y ; - gboolean attached_to_cell; - gboolean floating; - gint row, col; - guint16 xpadding; - guint16 ypadding; - gboolean xexpand; - gboolean yexpand; - gboolean xshrink; - gboolean yshrink; - gboolean xfill; - gboolean yfill; -}; - -typedef struct _GtkSheetChild GtkSheetChild; - - - -struct _GtkSheetButton -{ - GtkStateType state; - gchar *label; - - gboolean label_visible; - GtkSheetChild *child; - - GtkJustification justification; -}; - -typedef struct _GtkSheetButton GtkSheetButton; - - - -GtkSheetButton * gtk_sheet_button_new(void); - -inline void gtk_sheet_button_free(GtkSheetButton *button); - - -#endif /* __GTK_EXTRA_SHEET_H__ */ - - diff --git a/lib/gtksheet/gtkextra.c b/lib/gtksheet/gtkextra.c deleted file mode 100644 index 4d792685..00000000 --- a/lib/gtksheet/gtkextra.c +++ /dev/null @@ -1,137 +0,0 @@ -/* gtkextra - * Copyright 1999-2001 Adrian E. Feiguin - * - * 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 -#include -#include "gtkextrafeatures.h" -#include - -const guint gtkextra_major_version = GTKEXTRA_MAJOR_VERSION; -const guint gtkextra_minor_version = GTKEXTRA_MINOR_VERSION; -const guint gtkextra_micro_version = GTKEXTRA_MICRO_VERSION; -const guint gtkextra_binary_age = GTKEXTRA_BINARY_AGE; -const guint gtkextra_interface_age = GTKEXTRA_INTERFACE_AGE; - -gchar * -gtkextra_check_version (guint required_major, - guint required_minor, - guint required_micro) -{ - if (required_major > GTKEXTRA_MAJOR_VERSION) - return "GtkExtra version too old (major mismatch)"; - if (required_major < GTKEXTRA_MAJOR_VERSION) - return "GtkExtra version too new (major mismatch)"; - if (required_minor > GTKEXTRA_MINOR_VERSION) - return "GtkExtra version too old (minor mismatch)"; - if (required_minor < GTKEXTRA_MINOR_VERSION) - return "GtkExtra version too new (minor mismatch)"; - if (required_micro < GTKEXTRA_MICRO_VERSION - GTKEXTRA_BINARY_AGE) - return "GtkExtra version too new (micro mismatch)"; - if (required_micro > GTKEXTRA_MICRO_VERSION) - return "GtkExtra version too old (micro mismatch)"; - return NULL; -} - -/* -void -_gtkextra_signal_test(GtkObject *object, guint signal_id, gint arg1, gint arg2, gboolean *default_ret) -{ - gboolean result; - GValue ret = { 0, }; - GValue instance_and_param[3] = { { 0, }, {0, }, {0, } }; - - g_value_init(instance_and_param + 0, GTK_OBJECT_TYPE(object)); - g_value_set_instance(instance_and_param + 0, G_OBJECT(object)); - - g_value_init(instance_and_param + 1, G_TYPE_INT); - g_value_set_int(instance_and_param + 1, arg1); - - g_value_init(instance_and_param + 2, G_TYPE_INT); - g_value_set_int(instance_and_param + 2, arg2); - - g_value_init(&ret, G_TYPE_BOOLEAN); - g_value_set_boolean(&ret, *default_ret); - - g_signal_emitv(instance_and_param, signal_id, 0, &ret); - *default_ret = g_value_get_boolean(&ret); - - g_value_unset(instance_and_param + 0); - g_value_unset(instance_and_param + 1); - g_value_unset(instance_and_param + 2); -} -*/ - -void -_gtkextra_signal_emit(GtkObject *object, guint signal_id, ...) -{ - gboolean *result; - GValue ret = { 0, }; - GValue instance_and_params [10] = { {0, }, }; - va_list var_args; - GSignalQuery query; - gchar *error; - int i; - - va_start (var_args, signal_id); - - g_value_init(instance_and_params + 0, GTK_OBJECT_TYPE(object)); - g_value_set_instance (instance_and_params + 0, G_OBJECT(object)); - - g_signal_query(signal_id, &query); - - for (i = 0; i < query.n_params; i++) - { - gboolean static_scope = query.param_types[i]&~G_SIGNAL_TYPE_STATIC_SCOPE; - g_value_init(instance_and_params + i + 1, query.param_types[i]); - - - G_VALUE_COLLECT (instance_and_params + i + 1, - var_args, - static_scope ? G_VALUE_NOCOPY_CONTENTS : 0, - &error); - - if (error) - { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - while (i-- > 0) - g_value_unset (instance_and_params + i); - - va_end (var_args); - return; - } - - - } - - g_value_init(&ret, query.return_type); - result = va_arg(var_args,gboolean *); - g_value_set_boolean(&ret, *result); - g_signal_emitv(instance_and_params, signal_id, 0, &ret); - *result = g_value_get_boolean(&ret); - g_value_unset (&ret); - - for (i = 0; i < query.n_params; i++) - g_value_unset (instance_and_params + 1 + i); - g_value_unset (instance_and_params + 0); - - va_end (var_args); -} diff --git a/lib/gtksheet/gtkextrafeatures.h b/lib/gtksheet/gtkextrafeatures.h deleted file mode 100644 index 8b526a88..00000000 --- a/lib/gtksheet/gtkextrafeatures.h +++ /dev/null @@ -1,57 +0,0 @@ -/* gtkextra - set of widgets for gtk+ - * Copyright 1999-2001 Adrian E. Feiguin - * - * 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_EXTRA_FEATURES_H -#define GTK_EXTRA_FEATURES_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -/* GtkExtra version. - */ - -#define GTKEXTRA_MAJOR_VERSION (2) -#define GTKEXTRA_MINOR_VERSION (1) -#define GTKEXTRA_MICRO_VERSION (1) -#define GTKEXTRA_BINARY_AGE (0) -#define GTKEXTRA_INTERFACE_AGE (0) -#define GTKEXTRA_CHECK_VERSION(major,minor,micro) \ - (GTKEXTRA_MAJOR_VERSION > (major) || \ - (GTKEXTRA_MAJOR_VERSION == (major) && GTKEXTRA_MINOR_VERSION > (minor)) || \ - (GTKEXTRA_MAJOR_VERSION == (major) && GTKEXTRA_MINOR_VERSION == (minor) && \ - GTKEXTRA_MICRO_VERSION >= (micro))) - - -extern const guint gtkextra_major_version; -extern const guint gtkextra_minor_version; -extern const guint gtkextra_micro_version; -extern const guint gtkextra_binary_age; -extern const guint gtkextra_interface_age; -gchar* gtkextra_check_version (guint required_major, - guint required_minor, - guint required_micro); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -#endif /* GTK_EXTRA_FEATURES_H */ diff --git a/lib/gtksheet/gtkitementry.c b/lib/gtksheet/gtkitementry.c deleted file mode 100644 index efc22e2e..00000000 --- a/lib/gtksheet/gtkitementry.c +++ /dev/null @@ -1,2406 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include - -#include - -#include - -#include -#include -#include "gtkitementry.h" - -#define MIN_ENTRY_WIDTH 150 -#define DRAW_TIMEOUT 20 -#define INNER_BORDER 0 - -/* Initial size of buffer, in bytes */ -#define MIN_SIZE 16 - -/* Maximum size of text buffer, in bytes */ -#define MAX_SIZE G_MAXUSHORT - -typedef enum { - CURSOR_STANDARD, - CURSOR_DND -} CursorType; - -/* GObject, GtkObject methods - */ -static void gtk_item_entry_class_init (GtkItemEntryClass *klass); -static void gtk_item_entry_init (GtkItemEntry *entry); -static void gtk_item_entry_editable_init (GtkEditableClass *iface); - -/* GtkWidget methods - */ -static void gtk_entry_realize (GtkWidget *widget); -static void gtk_entry_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void gtk_entry_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); -static void gtk_entry_draw_frame (GtkWidget *widget); -static gint gtk_entry_expose (GtkWidget *widget, - GdkEventExpose *event); -static void gtk_entry_grab_focus (GtkWidget *widget); -static void gtk_entry_style_set (GtkWidget *widget, - GtkStyle *previous_style); -static void gtk_entry_direction_changed (GtkWidget *widget, - GtkTextDirection previous_dir); -static void gtk_entry_state_changed (GtkWidget *widget, - GtkStateType previous_state); - -/* GtkEditable method implementations - */ -static void gtk_entry_insert_text (GtkEditable *editable, - const gchar *new_text, - gint new_text_length, - gint *position); -static void gtk_entry_delete_text (GtkEditable *editable, - gint start_pos, - gint end_pos); - -static void gtk_entry_real_set_position (GtkEditable *editable, - gint position); -static gint gtk_entry_get_position (GtkEditable *editable); - -/* Default signal handlers - */ -static void gtk_entry_real_insert_text (GtkEditable *editable, - const gchar *new_text, - gint new_text_length, - gint *position); -static void gtk_entry_real_delete_text (GtkEditable *editable, - gint start_pos, - gint end_pos); -static void gtk_entry_move_cursor (GtkEntry *entry, - GtkMovementStep step, - gint count, - gboolean extend_selection); -static void gtk_entry_insert_at_cursor (GtkEntry *entry, - const gchar *str); -static void gtk_entry_delete_from_cursor (GtkEntry *entry, - GtkDeleteType type, - gint count); - -/* IM Context Callbacks - */ -static void gtk_entry_commit_cb (GtkIMContext *context, - const gchar *str, - GtkEntry *entry); -static void gtk_entry_preedit_changed_cb (GtkIMContext *context, - GtkEntry *entry); -static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, - GtkEntry *entry); -static gboolean gtk_entry_delete_surrounding_cb (GtkIMContext *context, - gint offset, - gint n_chars, - GtkEntry *entry); - -/* Internal routines - */ -static void gtk_entry_enter_text (GtkEntry *entry, - const gchar *str); -static void gtk_entry_set_positions (GtkEntry *entry, - gint current_pos, - gint selection_bound); -static void gtk_entry_draw_text (GtkEntry *entry); -static void gtk_entry_draw_cursor (GtkEntry *entry, - CursorType type); -static PangoLayout *gtk_entry_ensure_layout (GtkEntry *entry, - gboolean include_preedit); -static void gtk_entry_queue_draw (GtkEntry *entry); -static void gtk_entry_reset_im_context (GtkEntry *entry); -static void gtk_entry_recompute (GtkEntry *entry); -static void gtk_entry_get_cursor_locations (GtkEntry *entry, - CursorType type, - gint *strong_x, - gint *weak_x); -static void gtk_entry_adjust_scroll (GtkEntry *entry); -static gint gtk_entry_move_visually (GtkEntry *editable, - gint start, - gint count); -static gint gtk_entry_move_logically (GtkEntry *entry, - gint start, - gint count); -static gint gtk_entry_move_forward_word (GtkEntry *entry, - gint start); -static gint gtk_entry_move_backward_word (GtkEntry *entry, - gint start); -static void gtk_entry_delete_whitespace (GtkEntry *entry); -static char * gtk_entry_get_public_chars (GtkEntry *entry, - gint start, - gint end); -static void gtk_entry_update_primary_selection (GtkEntry *entry); -static void gtk_entry_state_changed (GtkWidget *widget, - GtkStateType previous_state); -static void gtk_entry_check_cursor_blink (GtkEntry *entry); -static void gtk_entry_pend_cursor_blink (GtkEntry *entry); -static void get_text_area_size (GtkEntry *entry, - gint *x, - gint *y, - gint *width, - gint *height); -static void get_widget_window_size (GtkEntry *entry, - gint *x, - gint *y, - gint *width, - gint *height); - -static GtkEntryClass *parent_class = NULL; - -GtkType -gtk_item_entry_get_type (void) -{ - static GtkType item_entry_type = 0; - - if (!item_entry_type) - { - static const GtkTypeInfo item_entry_info = - { - "GtkItemEntry", - sizeof (GtkItemEntry), - sizeof (GtkItemEntryClass), - (GtkClassInitFunc) gtk_item_entry_class_init, - (GtkObjectInitFunc) gtk_item_entry_init, - /* reserved_1 */ NULL, - /* reserved_2 */ NULL, - (GtkClassInitFunc) NULL, - }; - - static const GInterfaceInfo item_editable_info = - { - (GInterfaceInitFunc) gtk_item_entry_editable_init, /* interface_init */ - NULL, /* interface_finalize */ - NULL /* interface_data */ - }; - - - item_entry_type = gtk_type_unique (GTK_TYPE_ENTRY, &item_entry_info); - - g_type_add_interface_static (item_entry_type, - GTK_TYPE_EDITABLE, - &item_editable_info); - - } - - return item_entry_type; -} - -static void -gtk_item_entry_class_init (GtkItemEntryClass *class) -{ - GtkObjectClass *object_class; - GtkWidgetClass *widget_class; - GtkEntryClass *entry_class; - - object_class = (GtkObjectClass*) class; - widget_class = (GtkWidgetClass*) class; - parent_class = gtk_type_class (GTK_TYPE_ENTRY); - entry_class = (GtkEntryClass *) class; - - widget_class->realize = gtk_entry_realize; - widget_class->size_request = gtk_entry_size_request; - widget_class->size_allocate = gtk_entry_size_allocate; - widget_class->expose_event = gtk_entry_expose; - widget_class->grab_focus = gtk_entry_grab_focus; - widget_class->style_set = gtk_entry_style_set; - widget_class->direction_changed = gtk_entry_direction_changed; - widget_class->state_changed = gtk_entry_state_changed; - - entry_class->move_cursor = gtk_entry_move_cursor; - entry_class->insert_at_cursor = gtk_entry_insert_at_cursor; - entry_class->delete_from_cursor = gtk_entry_delete_from_cursor; - -} - -static void -gtk_item_entry_editable_init (GtkEditableClass *iface) -{ - iface->do_insert_text = gtk_entry_insert_text; - iface->do_delete_text = gtk_entry_delete_text; - iface->insert_text = gtk_entry_real_insert_text; - iface->delete_text = gtk_entry_real_delete_text; - iface->set_position = gtk_entry_real_set_position; - iface->get_position = gtk_entry_get_position; -} - -static void -gtk_item_entry_init (GtkItemEntry *entry) -{ - entry->justification = GTK_JUSTIFY_LEFT; - entry->text_max_size = 0; - GTK_ENTRY(entry)->has_frame = FALSE; - - g_object_unref(G_OBJECT(GTK_ENTRY(entry)->im_context)); - - GTK_ENTRY(entry)->im_context = gtk_im_multicontext_new (); - - g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "commit", - G_CALLBACK (gtk_entry_commit_cb), entry); - g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "preedit_changed", - G_CALLBACK (gtk_entry_preedit_changed_cb), entry); - g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "retrieve_surrounding", - G_CALLBACK (gtk_entry_retrieve_surrounding_cb), entry); - g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "delete_surrounding", - G_CALLBACK (gtk_entry_delete_surrounding_cb), entry); - -} - -static void -gtk_entry_realize (GtkWidget *widget) -{ - GtkEntry *entry; - GtkEditable *editable; - GdkWindowAttr attributes; - gint attributes_mask; - - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); - entry = GTK_ENTRY (widget); - editable = GTK_EDITABLE (widget); - - attributes.window_type = GDK_WINDOW_CHILD; - - get_widget_window_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height); - - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual (widget); - attributes.colormap = gtk_widget_get_colormap (widget); - attributes.event_mask = gtk_widget_get_events (widget); - attributes.event_mask |= (GDK_EXPOSURE_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON1_MOTION_MASK | - GDK_BUTTON3_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK | - GDK_POINTER_MOTION_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK); - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; - - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); - gdk_window_set_user_data (widget->window, entry); - - get_text_area_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height); - - attributes.cursor = gdk_cursor_new (GDK_XTERM); - attributes_mask |= GDK_WA_CURSOR; - - entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask); - gdk_window_set_user_data (entry->text_area, entry); - - gdk_cursor_unref (attributes.cursor); - - widget->style = gtk_style_attach (widget->style, widget->window); - - gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]); - gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]); - - gdk_window_show (entry->text_area); - - gtk_im_context_set_client_window (entry->im_context, entry->text_area); - - gtk_entry_adjust_scroll (entry); -} - -static void -get_borders (GtkEntry *entry, - gint *xborder, - gint *yborder) -{ - GtkWidget *widget = GTK_WIDGET (entry); - gint focus_width; - gboolean interior_focus; - - gtk_widget_style_get (widget, - "interior-focus", &interior_focus, - "focus-line-width", &focus_width, - NULL); - - if (entry->has_frame) - { - *xborder = widget->style->xthickness; - *yborder = widget->style->ythickness; - } - else - { - *xborder = 0; - *yborder = 0; - } - - if (!interior_focus) - { - *xborder += focus_width; - *yborder += focus_width; - } - -} - -static void -gtk_entry_size_request (GtkWidget *widget, - GtkRequisition *requisition) -{ - GtkEntry *entry = GTK_ENTRY (widget); - PangoFontMetrics *metrics; - gint xborder, yborder; - PangoContext *context; - - context = gtk_widget_get_pango_context (widget); - metrics = pango_context_get_metrics (context, - widget->style->font_desc, - pango_context_get_language (context)); - - entry->ascent = pango_font_metrics_get_ascent (metrics); - entry->descent = pango_font_metrics_get_descent (metrics); - - get_borders (entry, &xborder, &yborder); - - xborder += INNER_BORDER; - yborder += INNER_BORDER; - - if (entry->width_chars < 0) - requisition->width = MIN_ENTRY_WIDTH + xborder * 2; - else - { - gint char_width = pango_font_metrics_get_approximate_char_width (metrics); - requisition->width = PANGO_PIXELS (char_width) * entry->width_chars + xborder * 2; - } - - requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2; - - pango_font_metrics_unref (metrics); -} - -static void -get_text_area_size (GtkEntry *entry, - gint *x, - gint *y, - gint *width, - gint *height) -{ - gint xborder, yborder; - GtkRequisition requisition; - GtkWidget *widget = GTK_WIDGET (entry); - - gtk_widget_get_child_requisition (widget, &requisition); - - get_borders (entry, &xborder, &yborder); - - if (x) - *x = xborder; - - if (y) - *y = yborder; - - if (width) - *width = GTK_WIDGET (entry)->allocation.width - xborder * 2; - - if (height) - *height = requisition.height - yborder * 2; -} - -static void -get_widget_window_size (GtkEntry *entry, - gint *x, - gint *y, - gint *width, - gint *height) -{ - GtkRequisition requisition; - GtkWidget *widget = GTK_WIDGET (entry); - - gtk_widget_get_child_requisition (widget, &requisition); - - if (x) - *x = widget->allocation.x; - - if (y) - { - if (entry->is_cell_renderer) - *y = widget->allocation.y; - else - *y = widget->allocation.y + (widget->allocation.height - requisition.height) / 2; - } - - if (width) - *width = widget->allocation.width; - - if (height) - { - if (entry->is_cell_renderer) - *height = widget->allocation.height; - else - *height = requisition.height; - } -} - -static void -gtk_entry_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - GtkEntry *entry = GTK_ENTRY (widget); - GtkItemEntry *ientry = GTK_ITEM_ENTRY (widget); - - if(ientry->text_max_size > 0) - allocation->width = MIN(ientry->text_max_size, allocation->width); - - widget->allocation = *allocation; - - if (GTK_WIDGET_REALIZED (widget)) - { - /* We call gtk_widget_get_child_requisition, since we want (for - * backwards compatibility reasons) the realization here to - * be affected by the usize of the entry, if set - */ - gint x, y, width, height; - - get_widget_window_size (entry, &x, &y, &width, &height); - - gdk_window_move_resize (widget->window, - allocation->x, allocation->y, allocation->width, allocation->height); - - get_text_area_size (entry, &x, &y, &width, &height); - - gdk_window_move_resize (entry->text_area, - 0, allocation->height - height, allocation->width, height); - - gtk_entry_recompute (entry); - } -} - -static void -gtk_entry_draw_frame (GtkWidget *widget) -{ -} - -static gint -gtk_entry_expose (GtkWidget *widget, - GdkEventExpose *event) -{ - GtkEntry *entry = GTK_ENTRY (widget); - - if (widget->window == event->window) - gtk_entry_draw_frame (widget); - else if (entry->text_area == event->window) - { - gint area_width, area_height; - - get_text_area_size (entry, NULL, NULL, &area_width, &area_height); - - gdk_draw_rectangle (entry->text_area, - widget->style->bg_gc[GTK_WIDGET_STATE(widget)], - TRUE, - 0, 0, area_width, area_height); - - if ((entry->visible || entry->invisible_char != 0) && - GTK_WIDGET_HAS_FOCUS (widget) && - entry->selection_bound == entry->current_pos && entry->cursor_visible) - gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD); - - if (entry->dnd_position != -1) - gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND); - - gtk_entry_draw_text (GTK_ENTRY (widget)); - } - - return FALSE; -} - -static void -gtk_entry_grab_focus (GtkWidget *widget) -{ - GtkEntry *entry = GTK_ENTRY (widget); - gboolean select_on_focus; - - GTK_WIDGET_CLASS (parent_class)->grab_focus (widget); - - g_object_get (G_OBJECT (gtk_settings_get_default ()), - "gtk-entry-select-on-focus", - &select_on_focus, - NULL); - - if (select_on_focus && entry->editable && !entry->in_click) - gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1); -} - -static void -gtk_entry_direction_changed (GtkWidget *widget, - GtkTextDirection previous_dir) -{ - GtkEntry *entry = GTK_ENTRY (widget); - - gtk_entry_recompute (entry); - - GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir); -} - -static void -gtk_entry_state_changed (GtkWidget *widget, - GtkStateType previous_state) -{ - GtkEntry *entry = GTK_ENTRY (widget); - - if (GTK_WIDGET_REALIZED (widget)) - { - gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE (widget)]); - gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]); - } - - if (!GTK_WIDGET_IS_SENSITIVE (widget)) - { - /* Clear any selection */ - gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos); - } - - gtk_widget_queue_clear (widget); -} - -/* GtkEditable method implementations - */ -static void -gtk_entry_insert_text (GtkEditable *editable, - const gchar *new_text, - gint new_text_length, - gint *position) -{ - GtkEntry *entry = GTK_ENTRY (editable); - gchar buf[64]; - gchar *text; - - if (*position < 0 || *position > entry->text_length) - *position = entry->text_length; - - g_object_ref (G_OBJECT (editable)); - - if (new_text_length <= 63) - text = buf; - else - text = g_new (gchar, new_text_length + 1); - - text[new_text_length] = '\0'; - strncpy (text, new_text, new_text_length); - - g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position); - - if (new_text_length > 63) - g_free (text); - - g_object_unref (G_OBJECT (editable)); -} - -static void -gtk_entry_delete_text (GtkEditable *editable, - gint start_pos, - gint end_pos) -{ - GtkEntry *entry = GTK_ENTRY (editable); - - if (end_pos < 0 || end_pos > entry->text_length) - end_pos = entry->text_length; - if (start_pos < 0) - start_pos = 0; - if (start_pos > end_pos) - start_pos = end_pos; - - g_object_ref (G_OBJECT (editable)); - - g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos); - - g_object_unref (G_OBJECT (editable)); -} - -static void -gtk_entry_style_set (GtkWidget *widget, - GtkStyle *previous_style) -{ - GtkEntry *entry = GTK_ENTRY (widget); - - if (previous_style && GTK_WIDGET_REALIZED (widget)) - { - gtk_entry_recompute (entry); - - gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]); - gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]); - } -} - -static void -gtk_entry_real_set_position (GtkEditable *editable, - gint position) -{ - GtkEntry *entry = GTK_ENTRY (editable); - - if (position < 0 || position > entry->text_length) - position = entry->text_length; - - if (position != entry->current_pos || - position != entry->selection_bound) - { - gtk_entry_reset_im_context (entry); - gtk_entry_set_positions (entry, position, position); - } -} - -static gint -gtk_entry_get_position (GtkEditable *editable) -{ - return GTK_ENTRY (editable)->current_pos; -} - - -/* Default signal handlers - */ -static void -gtk_entry_real_insert_text (GtkEditable *editable, - const gchar *new_text, - gint new_text_length, - gint *position) -{ - gint index; - gint n_chars; - - GtkEntry *entry = GTK_ENTRY (editable); - - if (new_text_length < 0) - new_text_length = strlen (new_text); - - n_chars = g_utf8_strlen (new_text, new_text_length); - if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length) - { - gdk_beep (); - n_chars = entry->text_max_length - entry->text_length; - new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text; - } - - if (new_text_length + entry->n_bytes + 1 > entry->text_size) - { - while (new_text_length + entry->n_bytes + 1 > entry->text_size) - { - if (entry->text_size == 0) - entry->text_size = MIN_SIZE; - else - { - if (2 * (guint)entry->text_size < MAX_SIZE && - 2 * (guint)entry->text_size > entry->text_size) - entry->text_size *= 2; - else - { - entry->text_size = MAX_SIZE; - if (new_text_length > (gint)entry->text_size - (gint)entry->n_bytes - 1) - { - new_text_length = (gint)entry->text_size - (gint)entry->n_bytes - 1; - new_text_length = g_utf8_find_prev_char (new_text, new_text + new_text_length + 1) - new_text; - n_chars = g_utf8_strlen (new_text, new_text_length); - } - break; - } - } - } - - entry->text = g_realloc (entry->text, entry->text_size); - } - - index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text; - - g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index); - memcpy (entry->text + index, new_text, new_text_length); - - entry->n_bytes += new_text_length; - entry->text_length += n_chars; - - /* NUL terminate for safety and convenience */ - entry->text[entry->n_bytes] = '\0'; - - if (entry->current_pos > *position) - entry->current_pos += n_chars; - - if (entry->selection_bound > *position) - entry->selection_bound += n_chars; - - *position += n_chars; - - gtk_entry_recompute (entry); - - g_signal_emit_by_name (editable, "changed"); - g_object_notify (G_OBJECT (editable), "text"); -} - -static void -gtk_entry_real_delete_text (GtkEditable *editable, - gint start_pos, - gint end_pos) -{ - GtkEntry *entry = GTK_ENTRY (editable); - - if (start_pos < 0) - start_pos = 0; - if (end_pos < 0 || end_pos > entry->text_length) - end_pos = entry->text_length; - - if (start_pos < end_pos) - { - gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text; - gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text; - - g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index); - entry->text_length -= (end_pos - start_pos); - entry->n_bytes -= (end_index - start_index); - - if (entry->current_pos > start_pos) - entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos; - - if (entry->selection_bound > start_pos) - entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos; - /* We might have deleted the selection - */ - gtk_entry_update_primary_selection (entry); - - gtk_entry_recompute (entry); - - g_signal_emit_by_name (editable, "changed"); - g_object_notify (G_OBJECT (editable), "text"); - } -} - -/* Compute the X position for an offset that corresponds to the "more important - * cursor position for that offset. We use this when trying to guess to which - * end of the selection we should go to when the user hits the left or - * right arrow key. - */ -static gint -get_better_cursor_x (GtkEntry *entry, - gint offset) -{ - GtkTextDirection keymap_direction = - (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? - GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; - GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry)); - gboolean split_cursor; - - PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); - gint index = g_utf8_offset_to_pointer (entry->text, offset) - entry->text; - - PangoRectangle strong_pos, weak_pos; - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)), - "gtk-split-cursor", &split_cursor, - NULL); - - pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos); - - if (split_cursor) - return strong_pos.x / PANGO_SCALE; - else - return (keymap_direction == widget_direction) ? strong_pos.x / PANGO_SCALE : weak_pos.x / PANGO_SCALE; -} - -static void -gtk_entry_move_cursor (GtkEntry *entry, - GtkMovementStep step, - gint count, - gboolean extend_selection) -{ - gint new_pos = entry->current_pos; - - gtk_entry_reset_im_context (entry); - - if (entry->current_pos != entry->selection_bound && !extend_selection) - { - /* If we have a current selection and aren't extending it, move to the - * start/or end of the selection as appropriate - */ - switch (step) - { - case GTK_MOVEMENT_VISUAL_POSITIONS: - { - gint current_x = get_better_cursor_x (entry, entry->current_pos); - gint bound_x = get_better_cursor_x (entry, entry->selection_bound); - - if (count < 0) - new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound; - else - new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound; - - break; - } - case GTK_MOVEMENT_LOGICAL_POSITIONS: - case GTK_MOVEMENT_WORDS: - if (count < 0) - new_pos = MIN (entry->current_pos, entry->selection_bound); - else - new_pos = MAX (entry->current_pos, entry->selection_bound); - break; - case GTK_MOVEMENT_DISPLAY_LINE_ENDS: - case GTK_MOVEMENT_PARAGRAPH_ENDS: - case GTK_MOVEMENT_BUFFER_ENDS: - new_pos = count < 0 ? 0 : entry->text_length; - break; - case GTK_MOVEMENT_DISPLAY_LINES: - case GTK_MOVEMENT_PARAGRAPHS: - case GTK_MOVEMENT_PAGES: - break; - default: - break; - } - } - else - { - switch (step) - { - case GTK_MOVEMENT_LOGICAL_POSITIONS: - new_pos = gtk_entry_move_logically (entry, new_pos, count); - break; - case GTK_MOVEMENT_VISUAL_POSITIONS: - new_pos = gtk_entry_move_visually (entry, new_pos, count); - break; - case GTK_MOVEMENT_WORDS: - while (count > 0) - { - new_pos = gtk_entry_move_forward_word (entry, new_pos); - count--; - } - while (count < 0) - { - new_pos = gtk_entry_move_backward_word (entry, new_pos); - count++; - } - break; - case GTK_MOVEMENT_DISPLAY_LINE_ENDS: - case GTK_MOVEMENT_PARAGRAPH_ENDS: - case GTK_MOVEMENT_BUFFER_ENDS: - new_pos = count < 0 ? 0 : entry->text_length; - break; - case GTK_MOVEMENT_DISPLAY_LINES: - case GTK_MOVEMENT_PARAGRAPHS: - case GTK_MOVEMENT_PAGES: - break; - default: - break; - } - } - - if (extend_selection) - gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos); - else - gtk_editable_set_position (GTK_EDITABLE (entry), new_pos); - - gtk_entry_pend_cursor_blink (entry); -} - -static void -gtk_entry_insert_at_cursor (GtkEntry *entry, - const gchar *str) -{ - GtkEditable *editable = GTK_EDITABLE (entry); - gint pos = entry->current_pos; - - if (entry->editable) - { - gtk_entry_reset_im_context (entry); - - gtk_editable_insert_text (editable, str, -1, &pos); - gtk_editable_set_position (editable, pos); - } -} - -static void -gtk_entry_delete_from_cursor (GtkEntry *entry, - GtkDeleteType type, - gint count) -{ - GtkEditable *editable = GTK_EDITABLE (entry); - gint start_pos = entry->current_pos; - gint end_pos = entry->current_pos; - - gtk_entry_reset_im_context (entry); - - if (!entry->editable) - return; - - if (entry->selection_bound != entry->current_pos) - { - gtk_editable_delete_selection (editable); - return; - } - - switch (type) - { - case GTK_DELETE_CHARS: - end_pos = gtk_entry_move_logically (entry, entry->current_pos, count); - gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos)); - break; - case GTK_DELETE_WORDS: - if (count < 0) - { - /* Move to end of current word, or if not on a word, end of previous word */ - end_pos = gtk_entry_move_backward_word (entry, end_pos); - end_pos = gtk_entry_move_forward_word (entry, end_pos); - } - else if (count > 0) - { - /* Move to beginning of current word, or if not on a word, begining of next word */ - start_pos = gtk_entry_move_forward_word (entry, start_pos); - start_pos = gtk_entry_move_backward_word (entry, start_pos); - } - - /* Fall through */ - case GTK_DELETE_WORD_ENDS: - while (count < 0) - { - start_pos = gtk_entry_move_backward_word (entry, start_pos); - count++; - } - while (count > 0) - { - end_pos = gtk_entry_move_forward_word (entry, end_pos); - count--; - } - gtk_editable_delete_text (editable, start_pos, end_pos); - break; - case GTK_DELETE_DISPLAY_LINE_ENDS: - case GTK_DELETE_PARAGRAPH_ENDS: - if (count < 0) - gtk_editable_delete_text (editable, 0, entry->current_pos); - else - gtk_editable_delete_text (editable, entry->current_pos, -1); - break; - case GTK_DELETE_DISPLAY_LINES: - case GTK_DELETE_PARAGRAPHS: - gtk_editable_delete_text (editable, 0, -1); - break; - case GTK_DELETE_WHITESPACE: - gtk_entry_delete_whitespace (entry); - break; - } - - gtk_entry_pend_cursor_blink (entry); -} - -/* IM Context Callbacks - */ - -static void -gtk_entry_commit_cb (GtkIMContext *context, - const gchar *str, - GtkEntry *entry) -{ - gtk_entry_enter_text (entry, str); -} - -static void -gtk_entry_preedit_changed_cb (GtkIMContext *context, - GtkEntry *entry) -{ - gchar *preedit_string; - gint cursor_pos; - - gtk_im_context_get_preedit_string (entry->im_context, - &preedit_string, NULL, - &cursor_pos); - entry->preedit_length = strlen (preedit_string); - cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1)); - entry->preedit_cursor = cursor_pos; - g_free (preedit_string); - - gtk_entry_recompute (entry); -} - -static gboolean -gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, - GtkEntry *entry) -{ - gtk_im_context_set_surrounding (context, - entry->text, - entry->n_bytes, - g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text); - - return TRUE; -} - -static gboolean -gtk_entry_delete_surrounding_cb (GtkIMContext *slave, - gint offset, - gint n_chars, - GtkEntry *entry) -{ - gtk_editable_delete_text (GTK_EDITABLE (entry), - entry->current_pos + offset, - entry->current_pos + offset + n_chars); - - return TRUE; -} - - -/* Internal functions - */ - -/* Used for im_commit_cb and inserting Unicode chars */ -static void -gtk_entry_enter_text (GtkEntry *entry, - const gchar *str) -{ - GtkEditable *editable = GTK_EDITABLE (entry); - gint tmp_pos; - - if (gtk_editable_get_selection_bounds (editable, NULL, NULL)) - gtk_editable_delete_selection (editable); - else - { - if (entry->overwrite_mode) - gtk_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1); - } - - tmp_pos = entry->current_pos; - gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos); - gtk_editable_set_position (editable, tmp_pos); -} - -/* All changes to entry->current_pos and entry->selection_bound - * should go through this function. - */ -static void -gtk_entry_set_positions (GtkEntry *entry, - gint current_pos, - gint selection_bound) -{ - gboolean changed = FALSE; - - g_object_freeze_notify (G_OBJECT (entry)); - - if (current_pos != -1 && - entry->current_pos != current_pos) - { - entry->current_pos = current_pos; - changed = TRUE; - - g_object_notify (G_OBJECT (entry), "cursor_position"); - } - - if (selection_bound != -1 && - entry->selection_bound != selection_bound) - { - entry->selection_bound = selection_bound; - changed = TRUE; - - g_object_notify (G_OBJECT (entry), "selection_bound"); - } - - g_object_thaw_notify (G_OBJECT (entry)); - - if (changed) - gtk_entry_recompute (entry); -} - -static void -gtk_entry_reset_layout (GtkEntry *entry) -{ - if (entry->cached_layout) - { - g_object_unref (G_OBJECT (entry->cached_layout)); - entry->cached_layout = NULL; - } -} - -static void -update_im_cursor_location (GtkEntry *entry) -{ - GdkRectangle area; - gint strong_x; - gint strong_xoffset; - gint x, y, area_width, area_height; - - gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL) -; - get_text_area_size (entry, &x, &y, &area_width, &area_height); - - strong_xoffset = strong_x - entry->scroll_offset; - if (strong_xoffset < 0) - { - strong_xoffset = 0; - } - else if (strong_xoffset > area_width) - { - strong_xoffset = area_width; - } - area.x = x + strong_xoffset; - area.y = y + area_height; - area.width = area_width; - area.height = area_height; - - gtk_im_context_set_cursor_location (entry->im_context, &area); -} - -static gboolean -recompute_idle_func (gpointer data) -{ - GtkEntry *entry; - - GDK_THREADS_ENTER (); - - entry = GTK_ENTRY (data); - - gtk_entry_adjust_scroll (entry); - gtk_entry_queue_draw (entry); - - entry->recompute_idle = FALSE; - - update_im_cursor_location (entry); - - GDK_THREADS_LEAVE (); - - return FALSE; -} - -static void -gtk_entry_recompute (GtkEntry *entry) -{ - gtk_entry_reset_layout (entry); - gtk_entry_check_cursor_blink (entry); - - - if (!entry->recompute_idle) - { - entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */ - recompute_idle_func, entry, NULL); - } -} - -static void -append_char (GString *str, - gunichar ch, - gint count) -{ - gint i; - gint char_len; - gchar buf[7]; - - char_len = g_unichar_to_utf8 (ch, buf); - - i = 0; - while (i < count) - { - g_string_append_len (str, buf, char_len); - ++i; - } -} - -static PangoLayout * -gtk_entry_create_layout (GtkEntry *entry, - gboolean include_preedit) -{ - PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL); - PangoAttrList *tmp_attrs = pango_attr_list_new (); - - gchar *preedit_string = NULL; - gint preedit_length = 0; - PangoAttrList *preedit_attrs = NULL; - - pango_layout_set_single_paragraph_mode (layout, TRUE); - - if (include_preedit) - { - gtk_im_context_get_preedit_string (entry->im_context, - &preedit_string, &preedit_attrs, NULL); - preedit_length = entry->preedit_length; - } - - if (preedit_length) - { - GString *tmp_string = g_string_new (NULL); - - gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text; - - if (entry->visible) - { - g_string_prepend_len (tmp_string, entry->text, entry->n_bytes); - g_string_insert (tmp_string, cursor_index, preedit_string); - } - else - { - gint ch_len; - gint preedit_len_chars; - gunichar invisible_char; - - ch_len = g_utf8_strlen (entry->text, entry->n_bytes); - preedit_len_chars = g_utf8_strlen (preedit_string, -1); - ch_len += preedit_len_chars; - - if (entry->invisible_char != 0) - invisible_char = entry->invisible_char; - else - invisible_char = ' '; /* just pick a char */ - - append_char (tmp_string, invisible_char, ch_len); - - /* Fix cursor index to point to invisible char corresponding - * to the preedit, fix preedit_length to be the length of - * the invisible chars representing the preedit - */ - cursor_index = - g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) - - tmp_string->str; - preedit_length = - preedit_len_chars * - g_unichar_to_utf8 (invisible_char, NULL); - } - - pango_layout_set_text (layout, tmp_string->str, tmp_string->len); - - pango_attr_list_splice (tmp_attrs, preedit_attrs, - cursor_index, preedit_length); - - g_string_free (tmp_string, TRUE); - } - else - { - if (entry->visible) - { - pango_layout_set_text (layout, entry->text, entry->n_bytes); - } - else - { - GString *str = g_string_new (NULL); - gunichar invisible_char; - - if (entry->invisible_char != 0) - invisible_char = entry->invisible_char; - else - invisible_char = ' '; /* just pick a char */ - - append_char (str, invisible_char, entry->text_length); - pango_layout_set_text (layout, str->str, str->len); - g_string_free (str, TRUE); - } - } - - pango_layout_set_attributes (layout, tmp_attrs); - - if (preedit_string) - g_free (preedit_string); - if (preedit_attrs) - pango_attr_list_unref (preedit_attrs); - - pango_attr_list_unref (tmp_attrs); - - return layout; -} - -static PangoLayout * -gtk_entry_ensure_layout (GtkEntry *entry, - gboolean include_preedit) -{ - if (entry->preedit_length > 0 && - !include_preedit != !entry->cache_includes_preedit) - gtk_entry_reset_layout (entry); - - if (!entry->cached_layout) - { - entry->cached_layout = gtk_entry_create_layout (entry, include_preedit); - entry->cache_includes_preedit = include_preedit; - } - - return entry->cached_layout; -} - -static void -get_layout_position (GtkEntry *entry, - gint *x, - gint *y) -{ - PangoLayout *layout; - PangoRectangle logical_rect; - gint area_width, area_height; - gint y_pos; - PangoLayoutLine *line; - - layout = gtk_entry_ensure_layout (entry, TRUE); - - get_text_area_size (entry, NULL, NULL, &area_width, &area_height); - - area_height = PANGO_SCALE * (area_height); - - line = pango_layout_get_lines (layout)->data; - pango_layout_line_get_extents (line, NULL, &logical_rect); - - /* Align primarily for locale's ascent/descent */ - - y_pos = ((area_height - entry->ascent - entry->descent) / 2 + - entry->ascent + logical_rect.y); - - - /* Now see if we need to adjust to fit in actual drawn string */ - - if (logical_rect.height > area_height) - y_pos = (area_height - logical_rect.height) / 2; - else if (y_pos < 0) - y_pos = 0; - else if (y_pos + logical_rect.height > area_height) - y_pos = area_height - logical_rect.height; - - y_pos = y_pos / PANGO_SCALE; - - if (x) - *x = - entry->scroll_offset; - - if (y) - *y = y_pos; -} - -static void -gtk_entry_draw_text (GtkEntry *entry) -{ - GtkWidget *widget; - PangoLayoutLine *line; - - if (!entry->visible && entry->invisible_char == 0) - return; - - if (GTK_WIDGET_DRAWABLE (entry)) - { - PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); - gint area_width, area_height; - - gint x, y; - gint start_pos, end_pos; - - widget = GTK_WIDGET (entry); - - get_layout_position (entry, &x, &y); - - get_text_area_size (entry, NULL, NULL, &area_width, &area_height); - - - gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], - x, y, - layout); - - - if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos)) - { - gint *ranges; - gint n_ranges, i; - PangoRectangle logical_rect; - const gchar *text = pango_layout_get_text (layout); - gint start_index = g_utf8_offset_to_pointer (text, start_pos) - text; - gint end_index = g_utf8_offset_to_pointer (text, end_pos) - text; - GdkRegion *clip_region = gdk_region_new (); - GdkGC *text_gc; - GdkGC *selection_gc; - - line = pango_layout_get_lines (layout)->data; - - pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); - - pango_layout_get_extents (layout, NULL, &logical_rect); - - if (GTK_WIDGET_HAS_FOCUS (entry)) - { - selection_gc = widget->style->base_gc [GTK_STATE_SELECTED]; - text_gc = widget->style->text_gc [GTK_STATE_SELECTED]; - } - else - { - selection_gc = widget->style->base_gc [GTK_STATE_ACTIVE]; - text_gc = widget->style->text_gc [GTK_STATE_ACTIVE]; - } - - for (i=0; i < n_ranges; i++) - { - GdkRectangle rect; - - rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE; - rect.y = y; - rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE; - rect.height = logical_rect.height / PANGO_SCALE; - - gdk_draw_rectangle (entry->text_area, selection_gc, TRUE, - rect.x, rect.y, rect.width, rect.height); - - gdk_region_union_with_rect (clip_region, &rect); - } - - gdk_gc_set_clip_region (text_gc, clip_region); - gdk_draw_layout (entry->text_area, text_gc, - x, y, - layout); - gdk_gc_set_clip_region (text_gc, NULL); - - gdk_region_destroy (clip_region); - g_free (ranges); - } - } -} - -/* - * From _gtk_get_insertion_cursor_gc - */ - -typedef struct _CursorInfo CursorInfo; - -struct _CursorInfo -{ - GType for_type; - GdkGC *primary_gc; - GdkGC *secondary_gc; -}; - -static GdkGC * -make_cursor_gc (GtkWidget *widget, - const gchar *property_name, - GdkColor *fallback) -{ - GdkGCValues gc_values; - GdkGCValuesMask gc_values_mask; - GdkColor *cursor_color; - - gtk_widget_style_get (widget, property_name, &cursor_color, NULL); - - gc_values_mask = GDK_GC_FOREGROUND; - if (cursor_color) - { - gc_values.foreground = *cursor_color; - gdk_color_free (cursor_color); - } - else - gc_values.foreground = *fallback; - - gdk_rgb_find_color (widget->style->colormap, &gc_values.foreground); - return gtk_gc_get (widget->style->depth, widget->style->colormap, - &gc_values, gc_values_mask); -} - -static GdkGC * -_gtkextra_get_insertion_cursor_gc (GtkWidget *widget, - gboolean is_primary) -{ - CursorInfo *cursor_info; - - cursor_info = g_object_get_data (G_OBJECT (widget->style), "gtk-style-cursor-info"); - if (!cursor_info) - { - cursor_info = g_new (CursorInfo, 1); - g_object_set_data (G_OBJECT (widget->style), "gtk-style-cursor-info", cursor_info); - cursor_info->primary_gc = NULL; - cursor_info->secondary_gc = NULL; - cursor_info->for_type = G_TYPE_INVALID; - } - - /* We have to keep track of the type because gtk_widget_style_get() - * can return different results when called on the same property and - * same style but for different widgets. :-(. That is, - * GtkEntry::cursor-color = "red" in a style will modify the cursor - * color for entries but not for text view. - */ - if (cursor_info->for_type != G_OBJECT_TYPE (widget)) - { - cursor_info->for_type = G_OBJECT_TYPE (widget); - if (cursor_info->primary_gc) - { - gtk_gc_release (cursor_info->primary_gc); - cursor_info->primary_gc = NULL; - } - if (cursor_info->secondary_gc) - { - gtk_gc_release (cursor_info->secondary_gc); - cursor_info->secondary_gc = NULL; - } - } - - if (is_primary) - { - if (!cursor_info->primary_gc) - cursor_info->primary_gc = make_cursor_gc (widget, - "cursor-color", - &widget->style->black); - - return g_object_ref (cursor_info->primary_gc); - } - else - { - static GdkColor gray = { 0, 0x8888, 0x8888, 0x8888 }; - - if (!cursor_info->secondary_gc) - cursor_info->secondary_gc = make_cursor_gc (widget, - "secondary-cursor-color", - &gray); - - return g_object_ref (cursor_info->secondary_gc); - } -} - -/* - * From _gtk_draw_insertion_cursor - */ -static void -_gtkextra_draw_insertion_cursor (GtkWidget *widget, - GdkDrawable *drawable, - GdkGC *gc, - GdkRectangle *location, - GtkTextDirection direction, - gboolean draw_arrow) -{ - gint stem_width; - gint arrow_width; - gint x, y; - gint i; - gfloat cursor_aspect_ratio; - gint offset; - - g_return_if_fail (direction != GTK_TEXT_DIR_NONE); - - gtk_widget_style_get (widget, "cursor-aspect-ratio", &cursor_aspect_ratio, NULL); - - stem_width = location->height * cursor_aspect_ratio + 1; - arrow_width = stem_width + 1; - - /* put (stem_width % 2) on the proper side of the cursor */ - if (direction == GTK_TEXT_DIR_LTR) - offset = stem_width / 2; - else - offset = stem_width - stem_width / 2; - - for (i = 0; i < stem_width; i++) - gdk_draw_line (drawable, gc, - location->x + i - offset, location->y, - location->x + i - offset, location->y + location->height - 1); - - if (draw_arrow) - { - if (direction == GTK_TEXT_DIR_RTL) - { - x = location->x - offset - 1; - y = location->y + location->height - arrow_width * 2 - arrow_width + 1; - - for (i = 0; i < arrow_width; i++) - { - gdk_draw_line (drawable, gc, - x, y + i + 1, - x, y + 2 * arrow_width - i - 1); - x --; - } - } - else if (direction == GTK_TEXT_DIR_LTR) - { - x = location->x + stem_width - offset; - y = location->y + location->height - arrow_width * 2 - arrow_width + 1; - - for (i = 0; i < arrow_width; i++) - { - gdk_draw_line (drawable, gc, - x, y + i + 1, - x, y + 2 * arrow_width - i - 1); - x++; - } - } - } -} - -static void -gtk_entry_draw_cursor (GtkEntry *entry, - CursorType type) -{ - GtkTextDirection keymap_direction = - (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? - GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; - GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry)); - - if (GTK_WIDGET_DRAWABLE (entry) && GTK_ENTRY(entry)->cursor_visible) - { - GtkWidget *widget = GTK_WIDGET (entry); - GdkRectangle cursor_location; - gboolean split_cursor; - - gint xoffset = INNER_BORDER - entry->scroll_offset; - gint strong_x, weak_x; - gint text_area_height; - GtkTextDirection dir1 = GTK_TEXT_DIR_NONE; - GtkTextDirection dir2 = GTK_TEXT_DIR_NONE; - gint x1 = 0; - gint x2 = 0; - GdkGC *gc; - - gdk_window_get_size (entry->text_area, NULL, &text_area_height); - - gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x); - - g_object_get (gtk_widget_get_settings (widget), - "gtk-split-cursor", &split_cursor, - NULL); - - dir1 = widget_direction; - - if (split_cursor) - { - x1 = strong_x; - - if (weak_x != strong_x) - { - dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR; - x2 = weak_x; - } - } - else - { - if (keymap_direction == widget_direction) - x1 = strong_x; - else - x1 = weak_x; - } - - cursor_location.x = xoffset + x1; - cursor_location.y = INNER_BORDER; - cursor_location.width = 0; - cursor_location.height = text_area_height - 2 * INNER_BORDER ; - - gc = _gtkextra_get_insertion_cursor_gc (widget, TRUE); - _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc, - &cursor_location, dir1, - dir2 != GTK_TEXT_DIR_NONE); - g_object_unref (gc); - - if (dir2 != GTK_TEXT_DIR_NONE) - { - cursor_location.x = xoffset + x2; - gc = _gtkextra_get_insertion_cursor_gc (widget, FALSE); - _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc, - &cursor_location, dir2, - TRUE); - g_object_unref (gc); - } - } -} - -static void -gtk_entry_queue_draw (GtkEntry *entry) -{ - if (GTK_WIDGET_REALIZED (entry)) - gdk_window_invalidate_rect (entry->text_area, NULL, FALSE); -} - -static void -gtk_entry_reset_im_context (GtkEntry *entry) -{ - if (entry->need_im_reset) - { - entry->need_im_reset = 0; - gtk_im_context_reset (entry->im_context); - } -} - -static void -gtk_entry_get_cursor_locations (GtkEntry *entry, - CursorType type, - gint *strong_x, - gint *weak_x) -{ - PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); - const gchar *text; - PangoRectangle strong_pos, weak_pos; - gint index; - - if (type == CURSOR_STANDARD) - { - text = pango_layout_get_text (layout); - index = g_utf8_offset_to_pointer (text, entry->current_pos + entry->preedit_cursor) - text; - } - else /* type == CURSOR_DND */ - { - index = g_utf8_offset_to_pointer (entry->text, entry->dnd_position) - entry->text; - if (entry->dnd_position > entry->current_pos) - index += entry->preedit_length; - } - - pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos); - - if (strong_x) - *strong_x = strong_pos.x / PANGO_SCALE; - - if (weak_x) - *weak_x = weak_pos.x / PANGO_SCALE; -} - -static void -gtk_entry_adjust_scroll (GtkEntry *entry) -{ - gint min_offset, max_offset; - gint text_area_width; - gint strong_x, weak_x; - PangoLayout *layout; - PangoLayoutLine *line; - PangoRectangle logical_rect; - GtkItemEntry *item_entry; - gint text_width; - - if (!GTK_WIDGET_REALIZED (entry)) - return; - - item_entry = GTK_ITEM_ENTRY(entry); - - gdk_window_get_size (entry->text_area, &text_area_width, NULL); - text_area_width -= 2 * INNER_BORDER; - - layout = gtk_entry_ensure_layout (entry, TRUE); - line = pango_layout_get_lines (layout)->data; - - pango_layout_line_get_extents (line, NULL, &logical_rect); - text_width = logical_rect.width / PANGO_SCALE + 2; /* 2 for cursor */ - - gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x); - - /* Display as much text as we can */ - - if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_LTR) - { - entry->scroll_offset = 0; - switch(item_entry->justification){ - - case GTK_JUSTIFY_FILL: - case GTK_JUSTIFY_LEFT: - -/* LEFT JUSTIFICATION */ - - strong_x -= entry->scroll_offset; - if (strong_x < 0) - entry->scroll_offset += strong_x; - else if (strong_x > text_area_width){ - if(item_entry->text_max_size != 0 && - text_area_width + 2 <= item_entry->text_max_size){ - GtkAllocation allocation; - allocation = GTK_WIDGET(entry)->allocation; - allocation.width += text_width - text_area_width; - entry->scroll_offset = 0; - gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation); - }else{ - entry->scroll_offset += (strong_x - text_area_width) + 1; - } - } - - break; - - case GTK_JUSTIFY_RIGHT: - - /* RIGHT JUSTIFICATION FOR NUMBERS */ - if(entry->text){ - - entry->scroll_offset= -(text_area_width - text_width) + 1; - if(entry->scroll_offset > 0){ - if(item_entry->text_max_size != 0 && - text_area_width + 2 <= item_entry->text_max_size){ - GtkAllocation allocation; - allocation = GTK_WIDGET(entry)->allocation; - allocation.x -= text_width - text_area_width; - allocation.width += text_width - text_area_width; - entry->scroll_offset = 0; - gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation); - } - else - { - entry->scroll_offset= -(text_area_width - strong_x) + 1; - if(entry->scroll_offset < 0) entry->scroll_offset = 0; - } - } - } - else - entry->scroll_offset=0; - - break; - case GTK_JUSTIFY_CENTER: - - if(entry->text){ - - entry->scroll_offset= -(text_area_width - text_width)/2; - if(entry->scroll_offset > 0){ - if(item_entry->text_max_size != 0 && - text_area_width+1<=item_entry->text_max_size){ - GtkAllocation allocation; - allocation = GTK_WIDGET(entry)->allocation; - allocation.x += (text_area_width/2 - text_width/2); - allocation.width += text_width - text_area_width; - entry->scroll_offset = 0; - gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation); - } - else - { - entry->scroll_offset= -(text_area_width - strong_x) + 1; - if(entry->scroll_offset < 0) entry->scroll_offset = 0; - } - } - } - else - entry->scroll_offset=0; - - break; - - } - - } - else - { - max_offset = text_width - text_area_width; - min_offset = MIN (0, max_offset); - entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset); - } - - g_object_notify (G_OBJECT (entry), "scroll_offset"); -} - -static gint -gtk_entry_move_visually (GtkEntry *entry, - gint start, - gint count) -{ - gint index; - PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); - const gchar *text; - - text = pango_layout_get_text (layout); - - index = g_utf8_offset_to_pointer (text, start) - text; - - while (count != 0) - { - int new_index, new_trailing; - gboolean split_cursor; - gboolean strong; - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)), - "gtk-split-cursor", &split_cursor, - NULL); - - if (split_cursor) - strong = TRUE; - else - { - GtkTextDirection keymap_direction = - (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? - GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; - - strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (entry)); - } - - if (count > 0) - { - pango_layout_move_cursor_visually (layout, strong, index, 0, 1, &new_index, &new_trailing); - count--; - } - else - { - pango_layout_move_cursor_visually (layout, strong, index, 0, -1, &new_index, &new_trailing); - count++; - } - - if (new_index < 0 || new_index == G_MAXINT) - break; - - index = new_index; - - while (new_trailing--) - index = g_utf8_next_char (entry->text + new_index) - entry->text; - } - - return g_utf8_pointer_to_offset (text, text + index); -} - -static gint -gtk_entry_move_logically (GtkEntry *entry, - gint start, - gint count) -{ - gint new_pos = start; - - /* Prevent any leak of information */ - if (!entry->visible) - { - new_pos = CLAMP (start + count, 0, entry->text_length); - } - else if (entry->text) - { - PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); - PangoLogAttr *log_attrs; - gint n_attrs; - - pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); - - while (count > 0 && new_pos < entry->text_length) - { - do - new_pos++; - while (new_pos < entry->text_length && !log_attrs[new_pos].is_cursor_position); - - count--; - } - while (count < 0 && new_pos > 0) - { - do - new_pos--; - while (new_pos > 0 && !log_attrs[new_pos].is_cursor_position); - - count++; - } - - g_free (log_attrs); - } - - return new_pos; -} - -static gint -gtk_entry_move_forward_word (GtkEntry *entry, - gint start) -{ - gint new_pos = start; - - /* Prevent any leak of information */ - if (!entry->visible) - { - new_pos = entry->text_length; - } - else if (entry->text && (new_pos < entry->text_length)) - { - PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); - PangoLogAttr *log_attrs; - gint n_attrs; - - pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); - - /* Find the next word end */ - new_pos++; - while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end) - new_pos++; - - g_free (log_attrs); - } - - return new_pos; -} - - -static gint -gtk_entry_move_backward_word (GtkEntry *entry, - gint start) -{ - gint new_pos = start; - - /* Prevent any leak of information */ - if (!entry->visible) - { - new_pos = 0; - } - else if (entry->text && start > 0) - { - PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); - PangoLogAttr *log_attrs; - gint n_attrs; - - pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); - - new_pos = start - 1; - - /* Find the previous word beginning */ - while (new_pos > 0 && !log_attrs[new_pos].is_word_start) - new_pos--; - - g_free (log_attrs); - } - - return new_pos; -} - -static void -gtk_entry_delete_whitespace (GtkEntry *entry) -{ - PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); - PangoLogAttr *log_attrs; - gint n_attrs; - gint start, end; - - pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); - - start = end = entry->current_pos; - - while (start > 0 && log_attrs[start-1].is_white) - start--; - - while (end < n_attrs && log_attrs[end].is_white) - end++; - - g_free (log_attrs); - - if (start != end) - gtk_editable_delete_text (GTK_EDITABLE (entry), start, end); -} - - -/* - * Like gtk_editable_get_chars, but if the editable is not - * visible, return asterisks; also convert result to UTF-8. - */ -static char * -gtk_entry_get_public_chars (GtkEntry *entry, - gint start, - gint end) -{ - if (end < 0) - end = entry->text_length; - - if (entry->visible) - return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end); - else - { - gchar *str; - gint i; - gint n_chars = end - start; - - str = g_malloc (n_chars + 1); - for (i = 0; i < n_chars; i++) - str[i] = '*'; - str[i] = '\0'; - - return str; - } - -} - -static void -primary_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) -{ - GtkEntry *entry = GTK_ENTRY (data); - gint start, end; - - if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) - { - gchar *str = gtk_entry_get_public_chars (entry, start, end); - gtk_selection_data_set_text (selection_data, str, -1); - g_free (str); - } -} - -static void -primary_clear_cb (GtkClipboard *clipboard, - gpointer data) -{ - GtkEntry *entry = GTK_ENTRY (data); - - gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos); -} - -static void -gtk_entry_update_primary_selection (GtkEntry *entry) -{ - static const GtkTargetEntry targets[] = { - { "UTF8_STRING", 0, 0 }, - { "STRING", 0, 0 }, - { "TEXT", 0, 0 }, - { "COMPOUND_TEXT", 0, 0 } - }; - - GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); - gint start, end; - - if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) - { - if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets), - primary_get_cb, primary_clear_cb, G_OBJECT (entry))) - primary_clear_cb (clipboard, entry); - } - else - { - if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry)) - gtk_clipboard_clear (clipboard); - } -} - -/* Public API - */ - -GtkWidget* -gtk_item_entry_new (void) -{ - GtkWidget *widget; - - widget = GTK_WIDGET (gtk_type_new (GTK_TYPE_ITEM_ENTRY)); - return widget; -} - -GtkWidget* -gtk_item_entry_new_with_max_length (gint max) -{ - GtkItemEntry *entry; - - entry = gtk_type_new (GTK_TYPE_ITEM_ENTRY); - gtk_entry_set_max_length(GTK_ENTRY(entry), max); - - return GTK_WIDGET (entry); -} - -void -gtk_item_entry_set_text (GtkItemEntry *entry, - const gchar *text, - GtkJustification justification) -{ - gint tmp_pos; - - g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); - g_return_if_fail (text != NULL); - - entry->justification = justification; - - /* Actually setting the text will affect the cursor and selection; - * if the contents don't actually change, this will look odd to the user. - */ - if (strcmp (GTK_ENTRY(entry)->text, text) == 0) - return; - - if (GTK_ENTRY(entry)->recompute_idle){ - g_source_remove (GTK_ENTRY(entry)->recompute_idle); - GTK_ENTRY(entry)->recompute_idle = 0; - } - if (GTK_ENTRY(entry)->blink_timeout){ - g_source_remove (GTK_ENTRY(entry)->blink_timeout); - GTK_ENTRY(entry)->blink_timeout = 0; - } - - gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1); - - tmp_pos = 0; - gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos); -} - -/** - * gtk_entry_get_layout_offsets: - * @entry: a #GtkEntry - * @x: location to store X offset of layout, or %NULL - * @y: location to store Y offset of layout, or %NULL - * - * - * Obtains the position of the #PangoLayout used to render text - * in the entry, in widget coordinates. Useful if you want to line - * up the text in an entry with some other text, e.g. when using the - * entry to implement editable cells in a sheet widget. - * - * Also useful to convert mouse events into coordinates inside the - * #PangoLayout, e.g. to take some action if some part of the entry text - * is clicked. - * - * Note that as the user scrolls around in the entry the offsets will - * change; you'll need to connect to the "notify::scroll_offset" - * signal to track this. Remember when using the #PangoLayout - * functions you need to convert to and from pixels using - * PANGO_PIXELS() or #PANGO_SCALE. - * - * Keep in mind that the layout text may contain a preedit string, so - * gtk_entry_layout_index_to_text_index() and - * gtk_entry_text_index_to_layout_index() are needed to convert byte - * indices in the layout to byte indices in the entry contents. - * - **/ -void -gtk_item_entry_get_layout_offsets (GtkItemEntry *entry, - gint *x, - gint *y) -{ - gint text_area_x, text_area_y; - - g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); - - /* this gets coords relative to text area */ - get_layout_position (GTK_ENTRY(entry), x, y); - - /* convert to widget coords */ - get_text_area_size (GTK_ENTRY(entry), &text_area_x, &text_area_y, NULL, NULL); - - if (x) - *x += text_area_x; - - if (y) - *y += text_area_y; -} - -void -gtk_item_entry_set_justification(GtkItemEntry *entry, GtkJustification just) -{ - g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); - - entry->justification = just; -} - - -/* We display the cursor when - * - * - the selection is empty, AND - * - the widget has focus - */ - -#define CURSOR_ON_MULTIPLIER 0.66 -#define CURSOR_OFF_MULTIPLIER 0.34 -#define CURSOR_PEND_MULTIPLIER 1.0 - -static gboolean -cursor_blinks (GtkEntry *entry) -{ - GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry)); - gboolean blink; - - if (GTK_WIDGET_HAS_FOCUS (entry) && - entry->selection_bound == entry->current_pos) - { - g_object_get (G_OBJECT (settings), "gtk-cursor-blink", &blink, NULL); - return blink; - } - else - return FALSE; -} - -static gint -get_cursor_time (GtkEntry *entry) -{ - GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry)); - gint time; - - g_object_get (G_OBJECT (settings), "gtk-cursor-blink-time", &time, NULL); - - return time; -} - -static void -show_cursor (GtkEntry *entry) -{ - if (!entry->cursor_visible) - { - entry->cursor_visible = TRUE; - - if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos) - gtk_widget_queue_draw (GTK_WIDGET (entry)); - } -} - -static void -hide_cursor (GtkEntry *entry) -{ - if (entry->cursor_visible) - { - entry->cursor_visible = FALSE; - - if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos) - gtk_widget_queue_draw (GTK_WIDGET (entry)); - } -} - -/* - * Blink! - */ -static gint -blink_cb (gpointer data) -{ - GtkEntry *entry; - - GDK_THREADS_ENTER (); - - entry = GTK_ENTRY (data); - - g_assert (GTK_WIDGET_HAS_FOCUS (entry)); - g_assert (entry->selection_bound == entry->current_pos); - - if (entry->cursor_visible) - { - hide_cursor (entry); - entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER, - blink_cb, - entry); - } - else - { - show_cursor (entry); - entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER, - blink_cb, - entry); - } - - GDK_THREADS_LEAVE (); - - /* Remove ourselves */ - return FALSE; -} - -static void -gtk_entry_check_cursor_blink (GtkEntry *entry) -{ - if (cursor_blinks (entry)) - { - if (!entry->blink_timeout) - { - entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER, - blink_cb, - entry); - show_cursor (entry); - } - } - else - { - if (entry->blink_timeout) - { - gtk_timeout_remove (entry->blink_timeout); - entry->blink_timeout = 0; - } - - entry->cursor_visible = TRUE; - } - -} - -static void -gtk_entry_pend_cursor_blink (GtkEntry *entry) -{ - if (cursor_blinks (entry)) - { - if (entry->blink_timeout != 0) - gtk_timeout_remove (entry->blink_timeout); - - entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER, - blink_cb, - entry); - show_cursor (entry); - } -} - -void -gtk_item_entry_set_cursor_visible(GtkItemEntry *entry, gboolean visible) -{ - g_return_if_fail (GTK_IS_ITEM_ENTRY (entry)); - - GTK_ENTRY(entry)->cursor_visible = visible; -} - -gboolean -gtk_item_entry_get_cursor_visible(GtkItemEntry *entry) -{ - g_return_val_if_fail (GTK_IS_ITEM_ENTRY (entry), FALSE); - - return(GTK_ENTRY(entry)->cursor_visible); -} diff --git a/lib/gtksheet/gtkitementry.h b/lib/gtksheet/gtkitementry.h deleted file mode 100644 index 11fab7ae..00000000 --- a/lib/gtksheet/gtkitementry.h +++ /dev/null @@ -1,76 +0,0 @@ -/* GtkItemEntry - widget for gtk+ - * Copyright (C) 1999-2001 Adrian E. Feiguin - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * GtkItemEntry widget by Adrian E. Feiguin - * Based on GtkEntry widget - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#ifndef __GTK_ITEM_ENTRY_H__ -#define __GTK_ITEM_ENTRY_H__ - - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -#define GTK_TYPE_ITEM_ENTRY (gtk_item_entry_get_type ()) -#define GTK_ITEM_ENTRY(obj) (GTK_CHECK_CAST (obj, gtk_item_entry_get_type (), GtkItemEntry)) -#define GTK_ITEM_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, gtk_item_entry_get_type (), GtkItemEntryClass)) -#define GTK_IS_ITEM_ENTRY(obj) (GTK_CHECK_TYPE (obj, gtk_item_entry_get_type ())) -#define GTK_IS_ITEM_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY)) - - -typedef struct _GtkItemEntry GtkItemEntry; -typedef struct _GtkItemEntryClass GtkItemEntryClass; - -struct _GtkItemEntry -{ - GtkEntry parent; - - gint text_max_size; - - GtkJustification justification; -}; - -struct _GtkItemEntryClass -{ - GtkEntryClass parent_class; -}; - -GtkType gtk_item_entry_get_type (void); -GtkWidget* gtk_item_entry_new (void); -GtkWidget* gtk_item_entry_new_with_max_length (gint max); -void gtk_item_entry_set_text (GtkItemEntry *item_entry, - const gchar *text, - GtkJustification justification); - -void gtk_item_entry_set_justification (GtkItemEntry *item_entry, - GtkJustification justification); - -void gtk_item_entry_set_cursor_visible (GtkItemEntry *entry, - gboolean visible); -gboolean gtk_item_entry_get_cursor_visible (GtkItemEntry *entry); - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -#endif /* __GTK_ITEM_ENTRY_H__ */ diff --git a/lib/gtksheet/gtksheet.c b/lib/gtksheet/gtksheet.c deleted file mode 100644 index 942b0233..00000000 --- a/lib/gtksheet/gtksheet.c +++ /dev/null @@ -1,8005 +0,0 @@ -/* - * Copyright (C) 2006, 2008 Free Software Foundation - * - * This version of GtkSheet has been *heavily* modified, for the specific - * requirements of PSPPIRE. The changes are copyright by the - * Free Software Foundation. The copyright notice for the original work is - * below. - */ - -/* GtkSheet widget for Gtk+. - * Copyright (C) 1999-2001 Adrian E. Feiguin - * - * Based on GtkClist widget by Jay Painter, but major changes. - * Memory allocation routines inspired on SC (Spreadsheet Calculator) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * SECTION:gtksheet - * @short_description: spreadsheet widget for gtk2 - * - * GtkSheet is a matrix widget for GTK+. It consists of an scrollable grid of - * cells where you can allocate text. Cell contents can be edited interactively - * through a specially designed entry, GtkItemEntry. It is also a container - * subclass, allowing you to display buttons, curves, pixmaps and any other - * widgets in it. - * - * You can also set many attributes as: border, foreground and background color, - * text justification, and more. - * - * The testgtksheet program shows how easy is to create a spreadsheet-like GUI - * using this widget. - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gtkitementry.h" -#include "gtksheet.h" -#include "gtkextra-marshal.h" -#include "gsheetmodel.h" - -/* sheet flags */ -enum - { - GTK_SHEET_IS_FROZEN = 1 << 1, - GTK_SHEET_IN_XDRAG = 1 << 2, - GTK_SHEET_IN_YDRAG = 1 << 3, - GTK_SHEET_IN_DRAG = 1 << 4, - GTK_SHEET_IN_SELECTION = 1 << 5, - GTK_SHEET_IN_RESIZE = 1 << 6, - GTK_SHEET_REDRAW_PENDING = 1 << 7, - }; - -#define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags) -#define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag)) -#define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~ (flag)) - -#define GTK_SHEET_IS_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IS_FROZEN) -#define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG) -#define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG) -#define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG) -#define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION) -#define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE) -#define GTK_SHEET_REDRAW_PENDING(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_REDRAW_PENDING) - -#define CELL_SPACING 1 -#define DRAG_WIDTH 6 -#define TIMEOUT_HOVER 300 -#define COLUMN_MIN_WIDTH 10 -#define CELLOFFSET 4 -#define DEFAULT_COLUMN_WIDTH 80 - -static void gtk_sheet_update_primary_selection (GtkSheet *sheet); -static void gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column); - -static void gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row); - - -static gboolean gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col); - -static inline void -dispose_string (const GtkSheet *sheet, gchar *text) -{ - GSheetModel *model = gtk_sheet_get_model (sheet); - - if ( ! model ) - return; - - if (g_sheet_model_free_strings (model)) - g_free (text); -} - -static inline -guint DEFAULT_ROW_HEIGHT (GtkWidget *widget) -{ - if (!widget->style->font_desc) return 24; - else - { - PangoContext *context = gtk_widget_get_pango_context (widget); - PangoFontMetrics *metrics = - pango_context_get_metrics (context, - widget->style->font_desc, - pango_context_get_language (context)); - - guint val = pango_font_metrics_get_descent (metrics) + - pango_font_metrics_get_ascent (metrics); - - pango_font_metrics_unref (metrics); - - return PANGO_PIXELS (val) + 2 * CELLOFFSET; - } -} - -static inline -guint DEFAULT_FONT_ASCENT (GtkWidget *widget) -{ - if (!widget->style->font_desc) return 12; - else - { - PangoContext *context = gtk_widget_get_pango_context (widget); - PangoFontMetrics *metrics = - pango_context_get_metrics (context, - widget->style->font_desc, - pango_context_get_language (context)); - guint val = pango_font_metrics_get_ascent (metrics); - pango_font_metrics_unref (metrics); - return PANGO_PIXELS (val); - } -} - -static inline -guint STRING_WIDTH (GtkWidget *widget, - const PangoFontDescription *font, const gchar *text) -{ - PangoRectangle rect; - PangoLayout *layout; - - layout = gtk_widget_create_pango_layout (widget, text); - pango_layout_set_font_description (layout, font); - - pango_layout_get_extents (layout, NULL, &rect); - - g_object_unref (layout); - return PANGO_PIXELS (rect.width); -} - -static inline -guint DEFAULT_FONT_DESCENT (GtkWidget *widget) -{ - if (!widget->style->font_desc) return 12; - else - { - PangoContext *context = gtk_widget_get_pango_context (widget); - PangoFontMetrics *metrics = - pango_context_get_metrics (context, - widget->style->font_desc, - pango_context_get_language (context)); - guint val = pango_font_metrics_get_descent (metrics); - pango_font_metrics_unref (metrics); - return PANGO_PIXELS (val); - } -} - - -static gint -yyy_row_is_visible (const GtkSheet *sheet, gint row) -{ - GSheetRow *row_geo = sheet->row_geometry; - - return g_sheet_row_get_visibility (row_geo, row); -} - - -static gint -yyy_row_is_sensitive (const GtkSheet *sheet, gint row) -{ - GSheetRow *row_geo = sheet->row_geometry; - - return g_sheet_row_get_sensitivity (row_geo, row); -} - - - -static inline gint -yyy_row_count (const GtkSheet *sheet) -{ - GSheetRow *row_geo = sheet->row_geometry; - - return g_sheet_row_get_row_count (row_geo); -} - -static inline gint -yyy_row_height (const GtkSheet *sheet, gint row) -{ - GSheetRow *row_geo = sheet->row_geometry; - - return g_sheet_row_get_height (row_geo, row); -} - -static gint -yyy_row_top_ypixel (const GtkSheet *sheet, gint row) -{ - GSheetRow *geo = sheet->row_geometry; - - gint y = g_sheet_row_start_pixel (geo, row); - - if ( sheet->column_titles_visible ) - y += sheet->column_title_area.height; - - return y; -} - - -/* Return the row containing pixel Y */ -static gint -yyy_row_ypixel_to_row (const GtkSheet *sheet, gint y) -{ - GSheetRow *geo = sheet->row_geometry; - - gint cy = sheet->voffset; - - if (sheet->column_titles_visible) - cy += sheet->column_title_area.height; - - if (y < cy) return 0; - - return g_sheet_row_pixel_to_row (geo, y - cy); -} - - -/* gives the top pixel of the given row in context of - * the sheet's voffset */ -static inline gint -ROW_TOP_YPIXEL (const GtkSheet *sheet, gint row) -{ - return (sheet->voffset + yyy_row_top_ypixel (sheet, row)); -} - - -/* returns the row index from a y pixel location in the - * context of the sheet's voffset */ -static inline gint -ROW_FROM_YPIXEL (const GtkSheet *sheet, gint y) -{ - return (yyy_row_ypixel_to_row (sheet, y)); -} - -static inline GtkSheetButton * -xxx_column_button (const GtkSheet *sheet, gint col) -{ - GSheetColumn *col_geo = sheet->column_geometry; - if ( col < 0 ) return NULL ; - - return g_sheet_column_get_button (col_geo, col); -} - - -static inline gint -xxx_column_left_xpixel (const GtkSheet *sheet, gint col) -{ - GSheetColumn *geo = sheet->column_geometry; - - gint x = g_sheet_column_start_pixel (geo, col); - - if ( sheet->row_titles_visible ) - x += sheet->row_title_area.width; - - return x; -} - -static inline gint -xxx_column_width (const GtkSheet *sheet, gint col) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - return g_sheet_column_get_width (col_geo, col); -} - - -static inline void -xxx_set_column_width (GtkSheet *sheet, gint col, gint width) -{ - if ( sheet->column_geometry ) - g_sheet_column_set_width (sheet->column_geometry, col, width); -} - -static inline void -xxx_column_set_left_column (GtkSheet *sheet, gint col, gint i) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - g_sheet_column_set_left_text_column (col_geo, col, i); -} - -static inline gint -xxx_column_left_column (const GtkSheet *sheet, gint col) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - return g_sheet_column_get_left_text_column (col_geo, col); -} - -static inline void -xxx_column_set_right_column (GtkSheet *sheet, gint col, gint i) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - g_sheet_column_set_right_text_column (col_geo, col, i); -} - -static inline gint -xxx_column_right_column (const GtkSheet *sheet, gint col) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - return g_sheet_column_get_right_text_column (col_geo, col); -} - -static inline GtkJustification -xxx_column_justification (const GtkSheet *sheet, gint col) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - return g_sheet_column_get_justification (col_geo, col); -} - -static inline gint -xxx_column_is_visible (const GtkSheet *sheet, gint col) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - return g_sheet_column_get_visibility (col_geo, col); -} - - -static inline gint -xxx_column_is_sensitive (const GtkSheet *sheet, gint col) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - return g_sheet_column_get_sensitivity (col_geo, col); -} - - -/* gives the left pixel of the given column in context of - * the sheet's hoffset */ -static inline gint -COLUMN_LEFT_XPIXEL (const GtkSheet *sheet, gint ncol) -{ - return (sheet->hoffset + xxx_column_left_xpixel (sheet, ncol)); -} - -static inline gint -xxx_column_count (const GtkSheet *sheet) -{ - GSheetColumn *col_geo = sheet->column_geometry; - - return g_sheet_column_get_column_count (col_geo); -} - -/* returns the column index from a x pixel location in the - * context of the sheet's hoffset */ -static inline gint -COLUMN_FROM_XPIXEL (const GtkSheet * sheet, - gint x) -{ - gint i, cx; - - cx = sheet->hoffset; - if ( sheet->row_titles_visible ) - cx += sheet->row_title_area.width; - - if (x < cx) return 0; - for (i = 0; i < xxx_column_count (sheet); i++) - { - if (x >= cx && x <= (cx + xxx_column_width (sheet, i)) && - xxx_column_is_visible (sheet, i)) - return i; - if ( xxx_column_is_visible (sheet, i)) - cx += xxx_column_width (sheet, i); - } - - /* no match */ - return xxx_column_count (sheet) - 1; -} - -/* returns the total height of the sheet */ -static inline gint SHEET_HEIGHT (GtkSheet *sheet) -{ - const gint n_rows = yyy_row_count (sheet); - - return yyy_row_top_ypixel (sheet, n_rows - 1) + - yyy_row_height (sheet, n_rows - 1); -} - - -static inline GtkSheetButton * -yyy_row_button (GtkSheet *sheet, gint row) -{ - GSheetRow *row_geo = sheet->row_geometry; - - return g_sheet_row_get_button (row_geo, row); -} - - - - -static inline void -yyy_set_row_height (GtkSheet *sheet, gint row, gint height) -{ - if ( sheet->row_geometry ) - g_sheet_row_set_height (sheet->row_geometry, row, height); -} - - - -/* returns the total width of the sheet */ -static inline gint SHEET_WIDTH (GtkSheet *sheet) -{ - gint i, cx; - - cx = ( sheet->row_titles_visible ? sheet->row_title_area.width : 0); - - for (i = 0; i < xxx_column_count (sheet); i++) - if (xxx_column_is_visible (sheet, i)) - cx += xxx_column_width (sheet, i); - - return cx; -} - -#define MIN_VISIBLE_ROW(sheet) \ - ROW_FROM_YPIXEL (sheet, sheet->column_title_area.height + 1) - -#define MAX_VISIBLE_ROW(sheet) \ - ROW_FROM_YPIXEL (sheet, sheet->sheet_window_height - 1) - -#define MIN_VISIBLE_COLUMN(sheet) \ - COLUMN_FROM_XPIXEL (sheet, sheet->row_title_area.width + 1) - -#define MAX_VISIBLE_COLUMN(sheet) \ - COLUMN_FROM_XPIXEL (sheet, sheet->sheet_window_width) - - - -static inline gboolean -POSSIBLE_XDRAG (const GtkSheet *sheet, gint x, gint *drag_column) -{ - gint column, xdrag; - - column = COLUMN_FROM_XPIXEL (sheet, x); - *drag_column = column; - - xdrag = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING; - if (x <= xdrag + DRAG_WIDTH / 2 && column != 0) - { - while (! xxx_column_is_visible (sheet, column - 1) && column > 0) column--; - *drag_column = column - 1; - return xxx_column_is_sensitive (sheet, column - 1); - } - - xdrag += xxx_column_width (sheet, column); - if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) - return xxx_column_is_sensitive (sheet, column); - - return FALSE; -} - -static inline gboolean -POSSIBLE_YDRAG (const GtkSheet *sheet, gint y, gint *drag_row) -{ - gint row, ydrag; - row = ROW_FROM_YPIXEL (sheet, y); - *drag_row = row; - - ydrag = ROW_TOP_YPIXEL (sheet, row)+CELL_SPACING; - if (y <= ydrag + DRAG_WIDTH / 2 && row != 0) - { - while (!yyy_row_is_visible (sheet, row - 1) && row > 0) row--; - *drag_row = row - 1; - return yyy_row_is_sensitive (sheet, row - 1); - } - - ydrag +=yyy_row_height (sheet, row); - - if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) - return yyy_row_is_sensitive (sheet, row); - - - return FALSE; -} - -static inline gboolean -POSSIBLE_DRAG (const GtkSheet *sheet, gint x, gint y, - gint *drag_row, gint *drag_column) -{ - gint ydrag, xdrag; - - /* Can't drag if nothing is selected */ - if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || - sheet->range.col0 < 0 || sheet->range.coli < 0 ) - return FALSE; - - *drag_column = COLUMN_FROM_XPIXEL (sheet, x); - *drag_row = ROW_FROM_YPIXEL (sheet, y); - - if (x >= COLUMN_LEFT_XPIXEL (sheet, sheet->range.col0) - DRAG_WIDTH / 2 && - x <= COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli) + - xxx_column_width (sheet, sheet->range.coli) + DRAG_WIDTH / 2) - { - ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.row0); - if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) - { - *drag_row = sheet->range.row0; - return TRUE; - } - ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.rowi) + - yyy_row_height (sheet, sheet->range.rowi); - if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) - { - *drag_row = sheet->range.rowi; - return TRUE; - } - } - - if (y >= ROW_TOP_YPIXEL (sheet, sheet->range.row0) - DRAG_WIDTH / 2 && - y <= ROW_TOP_YPIXEL (sheet, sheet->range.rowi) + - yyy_row_height (sheet, sheet->range.rowi) + DRAG_WIDTH / 2) - { - xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.col0); - if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) - { - *drag_column = sheet->range.col0; - return TRUE; - } - xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli) + - xxx_column_width (sheet, sheet->range.coli); - if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2) - { - *drag_column = sheet->range.coli; - return TRUE; - } - } - - return FALSE; -} - -static inline gboolean -POSSIBLE_RESIZE (const GtkSheet *sheet, gint x, gint y, - gint *drag_row, gint *drag_column) -{ - gint xdrag, ydrag; - - /* Can't drag if nothing is selected */ - if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 || - sheet->range.col0 < 0 || sheet->range.coli < 0 ) - return FALSE; - - xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli)+ - xxx_column_width (sheet, sheet->range.coli); - - ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.rowi)+ - yyy_row_height (sheet, sheet->range.rowi); - - if (sheet->state == GTK_SHEET_COLUMN_SELECTED) - ydrag = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet)); - - if (sheet->state == GTK_SHEET_ROW_SELECTED) - xdrag = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet)); - - *drag_column = COLUMN_FROM_XPIXEL (sheet, x); - *drag_row = ROW_FROM_YPIXEL (sheet, y); - - if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 && - y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE; - - return FALSE; -} - -static void gtk_sheet_class_init (GtkSheetClass * klass); -static void gtk_sheet_init (GtkSheet * sheet); -static void gtk_sheet_dispose (GObject * object); -static void gtk_sheet_finalize (GObject * object); -static void gtk_sheet_style_set (GtkWidget *widget, - GtkStyle *previous_style); -static void gtk_sheet_realize (GtkWidget * widget); -static void gtk_sheet_unrealize (GtkWidget * widget); -static void gtk_sheet_map (GtkWidget * widget); -static void gtk_sheet_unmap (GtkWidget * widget); -static gint gtk_sheet_expose (GtkWidget * widget, - GdkEventExpose * event); -static void gtk_sheet_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data); - -static void gtk_sheet_set_scroll_adjustments (GtkSheet *sheet, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); - -static gint gtk_sheet_button_press (GtkWidget * widget, - GdkEventButton * event); -static gint gtk_sheet_button_release (GtkWidget * widget, - GdkEventButton * event); -static gint gtk_sheet_motion (GtkWidget * widget, - GdkEventMotion * event); -static gint gtk_sheet_entry_key_press (GtkWidget *widget, - GdkEventKey *key); -static gint gtk_sheet_key_press (GtkWidget *widget, - GdkEventKey *key); -static void gtk_sheet_size_request (GtkWidget * widget, - GtkRequisition * requisition); -static void gtk_sheet_size_allocate (GtkWidget * widget, - GtkAllocation * allocation); - -/* Sheet queries */ - -static gboolean gtk_sheet_range_isvisible (const GtkSheet * sheet, - GtkSheetRange range); -static gboolean gtk_sheet_cell_isvisible (GtkSheet * sheet, - gint row, gint column); -/* Drawing Routines */ - -/* draw cell background and frame */ -static void gtk_sheet_cell_draw_default (GtkSheet *sheet, - gint row, gint column); - -/* draw cell contents */ -static void gtk_sheet_cell_draw_label (GtkSheet *sheet, - gint row, gint column); - -/* draw visible part of range. If range == NULL then draw the whole screen */ -static void gtk_sheet_range_draw (GtkSheet *sheet, - const GtkSheetRange *range); - -/* highlight the visible part of the selected range */ -static void gtk_sheet_range_draw_selection (GtkSheet *sheet, - GtkSheetRange range); - -/* Selection */ - -static gboolean gtk_sheet_move_query (GtkSheet *sheet, - gint row, gint column); -static void gtk_sheet_real_select_range (GtkSheet * sheet, - const GtkSheetRange * range); -static void gtk_sheet_real_unselect_range (GtkSheet * sheet, - const GtkSheetRange * range); -static void gtk_sheet_extend_selection (GtkSheet *sheet, - gint row, gint column); -static void gtk_sheet_new_selection (GtkSheet *sheet, - GtkSheetRange *range); -static void gtk_sheet_draw_border (GtkSheet *sheet, - GtkSheetRange range); -static void gtk_sheet_draw_corners (GtkSheet *sheet, - GtkSheetRange range); - - -/* Active Cell handling */ - -static void gtk_sheet_entry_changed (GtkWidget *widget, - gpointer data); -static void gtk_sheet_deactivate_cell (GtkSheet *sheet); -static void gtk_sheet_hide_active_cell (GtkSheet *sheet); -static gboolean gtk_sheet_activate_cell (GtkSheet *sheet, - gint row, gint col); -static void gtk_sheet_draw_active_cell (GtkSheet *sheet); -static void gtk_sheet_show_active_cell (GtkSheet *sheet); -static void gtk_sheet_click_cell (GtkSheet *sheet, - gint row, - gint column, - gboolean *veto); - -/* Backing Pixmap */ - -static void gtk_sheet_make_backing_pixmap (GtkSheet *sheet, - guint width, guint height); -static void gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, - GtkSheetRange range); -/* Scrollbars */ - -static void adjust_scrollbars (GtkSheet * sheet); -static void vadjustment_value_changed (GtkAdjustment * adjustment, - gpointer data); -static void hadjustment_value_changed (GtkAdjustment * adjustment, - gpointer data); - - -static void draw_xor_vline (GtkSheet * sheet); -static void draw_xor_hline (GtkSheet * sheet); -static void draw_xor_rectangle (GtkSheet *sheet, - GtkSheetRange range); - -static guint new_column_width (GtkSheet * sheet, - gint column, - gint * x); -static guint new_row_height (GtkSheet * sheet, - gint row, - gint * y); -/* Sheet Button */ - -static void create_global_button (GtkSheet *sheet); -static void global_button_clicked (GtkWidget *widget, - gpointer data); -/* Sheet Entry */ - -static void create_sheet_entry (GtkSheet *sheet); -static void gtk_sheet_size_allocate_entry (GtkSheet *sheet); -static void gtk_sheet_entry_set_max_size (GtkSheet *sheet); - -/* Sheet button gadgets */ - -static void size_allocate_column_title_buttons (GtkSheet * sheet); -static void size_allocate_row_title_buttons (GtkSheet * sheet); - - -static void size_allocate_global_button (GtkSheet *sheet); -static void gtk_sheet_button_size_request (GtkSheet *sheet, - const GtkSheetButton *button, - GtkRequisition *requisition); - -/* Attributes routines */ -static void init_attributes (const GtkSheet *sheet, gint col, - GtkSheetCellAttr *attributes); - - -/* Memory allocation routines */ -static void gtk_sheet_real_range_clear (GtkSheet *sheet, - const GtkSheetRange *range); - -static void gtk_sheet_real_cell_clear (GtkSheet *sheet, - gint row, - gint column); - - -/* Container Functions */ -static void gtk_sheet_remove (GtkContainer *container, - GtkWidget *widget); -static void gtk_sheet_realize_child (GtkSheet *sheet, - GtkSheetChild *child); -static void gtk_sheet_position_child (GtkSheet *sheet, - GtkSheetChild *child); -static void gtk_sheet_position_children (GtkSheet *sheet); -static void gtk_sheet_child_show (GtkSheetChild *child); -static void gtk_sheet_child_hide (GtkSheetChild *child); -static void gtk_sheet_column_size_request (GtkSheet *sheet, - gint col, - guint *requisition); -static void gtk_sheet_row_size_request (GtkSheet *sheet, - gint row, - guint *requisition); - - -/* Signals */ - -extern void -_gtkextra_signal_emit (GtkObject *object, guint signal_id, ...); - -enum - { - SELECT_ROW, - SELECT_COLUMN, - DOUBLE_CLICK_ROW, - DOUBLE_CLICK_COLUMN, - BUTTON_EVENT_ROW, - BUTTON_EVENT_COLUMN, - SELECT_RANGE, - RESIZE_RANGE, - MOVE_RANGE, - TRAVERSE, - DEACTIVATE, - ACTIVATE, - CHANGED, - LAST_SIGNAL - }; - -static GtkContainerClass *parent_class = NULL; -static guint sheet_signals[LAST_SIGNAL] = { 0 }; - - -GType -gtk_sheet_get_type () -{ - static GType sheet_type = 0; - - if (!sheet_type) - { - static const GTypeInfo sheet_info = - { - sizeof (GtkSheetClass), - NULL, - NULL, - (GClassInitFunc) gtk_sheet_class_init, - NULL, - NULL, - sizeof (GtkSheet), - 0, - (GInstanceInitFunc) gtk_sheet_init, - NULL, - }; - sheet_type = - g_type_register_static (GTK_TYPE_CONTAINER, "GtkSheet", - &sheet_info, 0); - } - return sheet_type; -} - -static GtkSheetRange* -gtk_sheet_range_copy (const GtkSheetRange *range) -{ - GtkSheetRange *new_range; - - g_return_val_if_fail (range != NULL, NULL); - - new_range = g_new (GtkSheetRange, 1); - - *new_range = *range; - - return new_range; -} - -static void -gtk_sheet_range_free (GtkSheetRange *range) -{ - g_return_if_fail (range != NULL); - - g_free (range); -} - -GType -gtk_sheet_range_get_type (void) -{ - static GType sheet_range_type = 0; - - if (!sheet_range_type) - { - sheet_range_type = - g_boxed_type_register_static ("GtkSheetRange", - (GBoxedCopyFunc) gtk_sheet_range_copy, - (GBoxedFreeFunc) gtk_sheet_range_free); - } - - return sheet_range_type; -} - - -static void column_titles_changed (GtkWidget *w, gint first, gint n_columns, gpointer data); - -/* Properties */ -enum - { - PROP_0, - PROP_ROW_GEO, - PROP_COL_GEO, - PROP_MODEL - }; - -static void -gtk_sheet_set_row_geometry (GtkSheet *sheet, GSheetRow *geo) -{ - if ( sheet->row_geometry ) g_object_unref (sheet->row_geometry); - - sheet->row_geometry = geo; - - if ( sheet->row_geometry ) g_object_ref (sheet->row_geometry); -} - -static void -gtk_sheet_set_column_geometry (GtkSheet *sheet, GSheetColumn *geo) -{ - if ( sheet->column_geometry ) g_object_unref (sheet->column_geometry); - - sheet->column_geometry = geo; - - if ( sheet->column_geometry ) g_object_ref (sheet->column_geometry); -} - - -static void -gtk_sheet_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) - -{ - GtkSheet *sheet = GTK_SHEET (object); - - switch (prop_id) - { - case PROP_ROW_GEO: - gtk_sheet_set_row_geometry (sheet, g_value_get_pointer (value)); - break; - case PROP_COL_GEO: - gtk_sheet_set_column_geometry (sheet, g_value_get_pointer (value)); - if ( sheet->column_geometry) - g_signal_connect (sheet->column_geometry, "columns_changed", - G_CALLBACK (column_titles_changed), sheet); - break; - case PROP_MODEL: - gtk_sheet_set_model (sheet, g_value_get_pointer (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - }; -} - -static void -gtk_sheet_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkSheet *sheet = GTK_SHEET (object); - - switch (prop_id) - { - case PROP_ROW_GEO: - g_value_set_pointer (value, sheet->row_geometry); - break; - case PROP_COL_GEO: - g_value_set_pointer (value, sheet->column_geometry); - break; - case PROP_MODEL: - g_value_set_pointer (value, sheet->model); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - }; -} - - -static void -gtk_sheet_class_init (GtkSheetClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - GParamSpec *row_geo_spec ; - GParamSpec *col_geo_spec ; - GParamSpec *model_spec ; - - GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; - GtkContainerClass *container_class = (GtkContainerClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - /** - * GtkSheet::select-row - * @sheet: the sheet widget that emitted the signal - * @row: the newly selected row index - * - * A row has been selected. - */ - sheet_signals[SELECT_ROW] = - g_signal_new ("select-row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, select_row), - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * GtkSheet::select - column - * @sheet: the sheet widget that emitted the signal - * @column: the newly selected column index - * - * A column has been selected. - */ - sheet_signals[SELECT_COLUMN] = - g_signal_new ("select-column", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, select_column), - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * GtkSheet::double-click-row - * @sheet: the sheet widget that emitted the signal - * @row: the row that was double clicked. - * - * A row's title button has been double clicked - */ - sheet_signals[DOUBLE_CLICK_ROW] = - g_signal_new ("double-click-row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * GtkSheet::double-click-column - * @sheet: the sheet widget that emitted the signal - * @column: the column that was double clicked. - * - * A column's title button has been double clicked - */ - sheet_signals[DOUBLE_CLICK_COLUMN] = - g_signal_new ("double-click-column", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - - /** - * GtkSheet::button-event-column - * @sheet: the sheet widget that emitted the signal - * @column: the column on which the event occured. - * - * A button event occured on a column title button - */ - sheet_signals[BUTTON_EVENT_COLUMN] = - g_signal_new ("button-event-column", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - gtkextra_VOID__INT_POINTER, - G_TYPE_NONE, - 2, - G_TYPE_INT, - G_TYPE_POINTER - ); - - - /** - * GtkSheet::button-event-row - * @sheet: the sheet widget that emitted the signal - * @column: the column on which the event occured. - * - * A button event occured on a row title button - */ - sheet_signals[BUTTON_EVENT_ROW] = - g_signal_new ("button-event-row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - gtkextra_VOID__INT_POINTER, - G_TYPE_NONE, - 2, - G_TYPE_INT, - G_TYPE_POINTER - ); - - - sheet_signals[SELECT_RANGE] = - g_signal_new ("select-range", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, select_range), - NULL, NULL, - g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, - 1, - GTK_TYPE_SHEET_RANGE); - - - sheet_signals[RESIZE_RANGE] = - g_signal_new ("resize-range", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, resize_range), - NULL, NULL, - gtkextra_VOID__BOXED_BOXED, - G_TYPE_NONE, - 2, - GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE - ); - - sheet_signals[MOVE_RANGE] = - g_signal_new ("move-range", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, move_range), - NULL, NULL, - gtkextra_VOID__BOXED_BOXED, - G_TYPE_NONE, - 2, - GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE - ); - - sheet_signals[TRAVERSE] = - g_signal_new ("traverse", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, traverse), - NULL, NULL, - gtkextra_BOOLEAN__INT_INT_POINTER_POINTER, - G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, - G_TYPE_POINTER, G_TYPE_POINTER); - - - sheet_signals[DEACTIVATE] = - g_signal_new ("deactivate", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, deactivate), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); - - sheet_signals[ACTIVATE] = - g_signal_new ("activate", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, activate), - NULL, NULL, - gtkextra_BOOLEAN__INT_INT, - G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT); - - sheet_signals[CHANGED] = - g_signal_new ("changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, changed), - NULL, NULL, - gtkextra_VOID__INT_INT, - G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); - - widget_class->set_scroll_adjustments_signal = - g_signal_new ("set-scroll-adjustments", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - offsetof (GtkSheetClass, set_scroll_adjustments), - NULL, NULL, - gtkextra_VOID__OBJECT_OBJECT, - G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); - - - container_class->add = NULL; - container_class->remove = gtk_sheet_remove; - container_class->forall = gtk_sheet_forall; - - object_class->dispose = gtk_sheet_dispose; - object_class->finalize = gtk_sheet_finalize; - - - row_geo_spec = - g_param_spec_pointer ("row-geometry", - "Row Geometry", - "A pointer to the model of the row geometry", - G_PARAM_READABLE | G_PARAM_WRITABLE ); - - col_geo_spec = - g_param_spec_pointer ("column-geometry", - "Column Geometry", - "A pointer to the model of the column geometry", - G_PARAM_READABLE | G_PARAM_WRITABLE ); - - model_spec = - g_param_spec_pointer ("model", - "Model", - "A pointer to the data model", - G_PARAM_READABLE | G_PARAM_WRITABLE ); - - - object_class->set_property = gtk_sheet_set_property; - object_class->get_property = gtk_sheet_get_property; - - g_object_class_install_property (object_class, - PROP_ROW_GEO, - row_geo_spec); - - g_object_class_install_property (object_class, - PROP_COL_GEO, - col_geo_spec); - - g_object_class_install_property (object_class, - PROP_MODEL, - model_spec); - - - widget_class->realize = gtk_sheet_realize; - widget_class->unrealize = gtk_sheet_unrealize; - widget_class->map = gtk_sheet_map; - widget_class->unmap = gtk_sheet_unmap; - widget_class->style_set = gtk_sheet_style_set; - widget_class->button_press_event = gtk_sheet_button_press; - widget_class->button_release_event = gtk_sheet_button_release; - widget_class->motion_notify_event = gtk_sheet_motion; - widget_class->key_press_event = gtk_sheet_key_press; - widget_class->expose_event = gtk_sheet_expose; - widget_class->size_request = gtk_sheet_size_request; - widget_class->size_allocate = gtk_sheet_size_allocate; - widget_class->focus_in_event = NULL; - widget_class->focus_out_event = NULL; - - klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments; - klass->select_row = NULL; - klass->select_column = NULL; - klass->select_range = NULL; - klass->resize_range = NULL; - klass->move_range = NULL; - klass->traverse = NULL; - klass->deactivate = NULL; - klass->activate = NULL; - klass->changed = NULL; -} - -static void -gtk_sheet_init (GtkSheet *sheet) -{ - sheet->model = NULL; - sheet->column_geometry = NULL; - sheet->row_geometry = NULL; - - sheet->children = NULL; - - sheet->flags = 0; - sheet->selection_mode = GTK_SELECTION_NONE; - sheet->freeze_count = 0; - sheet->state = GTK_SHEET_NORMAL; - - GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW); - GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS); - - sheet->column_title_window = NULL; - sheet->column_title_area.x = 0; - sheet->column_title_area.y = 0; - sheet->column_title_area.width = 0; - sheet->column_title_area.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)); - - sheet->row_title_window = NULL; - sheet->row_title_area.x = 0; - sheet->row_title_area.y = 0; - sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH; - sheet->row_title_area.height = 0; - - - sheet->active_cell.row = 0; - sheet->active_cell.col = 0; - sheet->selection_cell.row = 0; - sheet->selection_cell.col = 0; - - sheet->pixmap = NULL; - - sheet->range.row0 = 0; - sheet->range.rowi = 0; - sheet->range.col0 = 0; - sheet->range.coli = 0; - - sheet->state = GTK_SHEET_NORMAL; - - sheet->sheet_window = NULL; - sheet->sheet_window_width = 0; - sheet->sheet_window_height = 0; - sheet->entry_widget = NULL; - sheet->entry_container = NULL; - sheet->button = NULL; - - sheet->hoffset = 0; - sheet->voffset = 0; - - sheet->hadjustment = NULL; - sheet->vadjustment = NULL; - - sheet->cursor_drag = gdk_cursor_new (GDK_PLUS); - sheet->xor_gc = NULL; - sheet->fg_gc = NULL; - sheet->bg_gc = NULL; - sheet->x_drag = 0; - sheet->y_drag = 0; - gdk_color_parse ("white", &sheet->bg_color); - gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->bg_color, FALSE, - TRUE); - gdk_color_parse ("gray", &sheet->grid_color); - gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->grid_color, FALSE, - TRUE); - - sheet->show_grid = TRUE; - - sheet->motion_timer = 0; - - sheet->columns_resizable = TRUE; - sheet->rows_resizable = TRUE; - - sheet->row_titles_visible = TRUE; - sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH; - - sheet->column_titles_visible = TRUE; - sheet->autoscroll = TRUE; - sheet->justify_entry = TRUE; - - - /* create sheet entry */ - sheet->entry_type = 0; - create_sheet_entry (sheet); - - /* create global selection button */ - create_global_button (sheet); -} - - -/* Callback which occurs whenever columns are inserted / deleted in the model */ -static void -columns_inserted_deleted_callback (GSheetModel *model, gint first_column, - gint n_columns, - gpointer data) -{ - gint i; - GtkSheet *sheet = GTK_SHEET (data); - - GtkSheetRange range; - gint model_columns = g_sheet_model_get_column_count (model); - - - /* Need to update all the columns starting from the first column and onwards. - * Previous column are unchanged, so don't need to be updated. - */ - range.col0 = first_column; - range.row0 = 0; - range.coli = xxx_column_count (sheet) - 1; - range.rowi = yyy_row_count (sheet) - 1; - - adjust_scrollbars (sheet); - - if (sheet->active_cell.col >= model_columns) - gtk_sheet_activate_cell (sheet, sheet->active_cell.row, model_columns - 1); - - for (i = first_column; i <= MAX_VISIBLE_COLUMN (sheet); i++) - gtk_sheet_column_title_button_draw (sheet, i); - - gtk_sheet_range_draw (sheet, &range); -} - - -/* Callback which occurs whenever rows are inserted / deleted in the model */ -static void -rows_inserted_deleted_callback (GSheetModel *model, gint first_row, - gint n_rows, gpointer data) -{ - gint i; - GtkSheet *sheet = GTK_SHEET (data); - - GtkSheetRange range; - - gint model_rows = g_sheet_model_get_row_count (model); - - /* Need to update all the rows starting from the first row and onwards. - * Previous rows are unchanged, so don't need to be updated. - */ - range.row0 = first_row; - range.col0 = 0; - range.rowi = yyy_row_count (sheet) - 1; - range.coli = xxx_column_count (sheet) - 1; - - adjust_scrollbars (sheet); - - if (sheet->active_cell.row >= model_rows) - gtk_sheet_activate_cell (sheet, model_rows - 1, sheet->active_cell.col); - - for (i = first_row; i <= MAX_VISIBLE_ROW (sheet); i++) - gtk_sheet_row_title_button_draw (sheet, i); - - gtk_sheet_range_draw (sheet, &range); -} - -/* - If row0 or rowi are negative, then all rows will be updated. - If col0 or coli are negative, then all columns will be updated. -*/ -static void -range_update_callback (GSheetModel *m, gint row0, gint col0, - gint rowi, gint coli, gpointer data) -{ - GtkSheet *sheet = GTK_SHEET (data); - - GtkSheetRange range; - - range.row0 = row0; - range.col0 = col0; - range.rowi = rowi; - range.coli = coli; - - if ( MAX_VISIBLE_ROW (sheet) > - g_sheet_model_get_row_count (sheet->model) - || - MAX_VISIBLE_COLUMN (sheet) > - g_sheet_model_get_column_count (sheet->model)) - { - gtk_sheet_move_query (sheet, 0, 0); - } - - if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) ) - { - gint i; - gtk_sheet_range_draw (sheet, NULL); - adjust_scrollbars (sheet); - - for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++) - gtk_sheet_row_title_button_draw (sheet, i); - - for (i = MIN_VISIBLE_COLUMN (sheet); - i <= MAX_VISIBLE_COLUMN (sheet); i++) - gtk_sheet_column_title_button_draw (sheet, i); - - return; - } - else if ( row0 < 0 || rowi < 0 ) - { - range.row0 = MIN_VISIBLE_ROW (sheet); - range.rowi = MAX_VISIBLE_ROW (sheet); - } - else if ( col0 < 0 || coli < 0 ) - { - range.col0 = MIN_VISIBLE_COLUMN (sheet); - range.coli = MAX_VISIBLE_COLUMN (sheet); - } - - gtk_sheet_range_draw (sheet, &range); -} - - -/** - * gtk_sheet_new: - * @rows: initial number of rows - * @columns: initial number of columns - * @title: sheet title - * @model: the model to use for the sheet data - * - * Creates a new sheet widget with the given number of rows and columns. - * - * Returns: the new sheet widget - */ -GtkWidget * -gtk_sheet_new (GSheetRow *vgeo, GSheetColumn *hgeo, GSheetModel *model) -{ - GtkWidget *widget = g_object_new (GTK_TYPE_SHEET, - "row-geometry", vgeo, - "column-geometry", hgeo, - "model", model, - NULL); - return widget; -} - - -/** - * gtk_sheet_set_model - * @sheet: the sheet to set the model for - * @model: the model to use for the sheet data - * - * Sets the model for a GtkSheet - * - */ -void -gtk_sheet_set_model (GtkSheet *sheet, GSheetModel *model) -{ - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (sheet->model ) g_object_unref (sheet->model); - - sheet->model = model; - - if ( model) - { - g_object_ref (model); - - g_signal_connect (model, "range_changed", - G_CALLBACK (range_update_callback), sheet); - - g_signal_connect (model, "rows_inserted", - G_CALLBACK (rows_inserted_deleted_callback), sheet); - - g_signal_connect (model, "rows_deleted", - G_CALLBACK (rows_inserted_deleted_callback), sheet); - - g_signal_connect (model, "columns_inserted", - G_CALLBACK (columns_inserted_deleted_callback), sheet); - - g_signal_connect (model, "columns_deleted", - G_CALLBACK (columns_inserted_deleted_callback), sheet); - } -} - - -/* Call back for when the column titles have changed. - FIRST is the first column changed. - N_COLUMNS is the number of columns which have changed, or - 1, which - indicates that the column has changed to its right - most extremity -*/ -static void -column_titles_changed (GtkWidget *w, gint first, gint n_columns, gpointer data) -{ - GtkSheet *sheet = GTK_SHEET (data); - gboolean extremity = FALSE; - - if ( n_columns == -1 ) - { - extremity = TRUE; - n_columns = xxx_column_count (sheet) - 1 ; - } - - if (!GTK_SHEET_IS_FROZEN (sheet)) - { - gint i; - for ( i = first ; i <= first + n_columns ; ++i ) - { - gtk_sheet_column_title_button_draw (sheet, i); - g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, i); - } - } - - if ( extremity) - gtk_sheet_column_title_button_draw (sheet, -1); - -} - -void -gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type) -{ - gint state; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - state = sheet->state; - - if (sheet->state == GTK_SHEET_NORMAL) - gtk_sheet_hide_active_cell (sheet); - - sheet->entry_type = entry_type; - - create_sheet_entry (sheet); - - if (state == GTK_SHEET_NORMAL) - { - gtk_sheet_show_active_cell (sheet); - g_signal_connect (gtk_sheet_get_entry (sheet), - "changed", - G_CALLBACK (gtk_sheet_entry_changed), - sheet); - } -} - -void -gtk_sheet_show_grid (GtkSheet *sheet, gboolean show) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (show == sheet->show_grid) return; - - sheet->show_grid = show; - - if (!GTK_SHEET_IS_FROZEN (sheet)) - gtk_sheet_range_draw (sheet, NULL); -} - -gboolean -gtk_sheet_grid_visible (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - - return sheet->show_grid; -} - -void -gtk_sheet_set_background (GtkSheet *sheet, GdkColor *color) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (!color) - { - gdk_color_parse ("white", &sheet->bg_color); - gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->bg_color, FALSE, TRUE); - } - else - sheet->bg_color = *color; - - if (!GTK_SHEET_IS_FROZEN (sheet)) - gtk_sheet_range_draw (sheet, NULL); -} - -void -gtk_sheet_set_grid (GtkSheet *sheet, GdkColor *color) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (!color) - { - gdk_color_parse ("black", &sheet->grid_color); - gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->grid_color, FALSE, TRUE); - } - else - sheet->grid_color = *color; - - if (!GTK_SHEET_IS_FROZEN (sheet)) - gtk_sheet_range_draw (sheet, NULL); -} - -guint -gtk_sheet_get_columns_count (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - - return xxx_column_count (sheet); -} - -guint -gtk_sheet_get_rows_count (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - - return yyy_row_count (sheet); -} - -gint -gtk_sheet_get_state (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - - return (sheet->state); -} - -void -gtk_sheet_set_selection_mode (GtkSheet *sheet, gint mode) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (GTK_WIDGET_REALIZED (sheet)) - gtk_sheet_real_unselect_range (sheet, NULL); - - sheet->selection_mode = mode; -} - -void -gtk_sheet_set_autoresize (GtkSheet *sheet, gboolean autoresize) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - sheet->autoresize = autoresize; -} - -gboolean -gtk_sheet_autoresize (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - - return sheet->autoresize; -} - -static void -gtk_sheet_set_column_width (GtkSheet * sheet, - gint column, - guint width); - - -static void -gtk_sheet_autoresize_column (GtkSheet *sheet, gint column) -{ - gint text_width = 0; - gint row; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - if (column >= xxx_column_count (sheet) || column < 0) return; - - for (row = 0; row < yyy_row_count (sheet); row++) - { - gchar *text = gtk_sheet_cell_get_text (sheet, row, column); - if (text && strlen (text) > 0) - { - GtkSheetCellAttr attributes; - - gtk_sheet_get_attributes (sheet, row, column, &attributes); - if (attributes.is_visible) - { - gint width = STRING_WIDTH (GTK_WIDGET (sheet), - attributes.font_desc, - text) - + 2 * CELLOFFSET + attributes.border.width; - text_width = MAX (text_width, width); - } - } - dispose_string (sheet, text); - } - - if (text_width > xxx_column_width (sheet, column) ) - { - gtk_sheet_set_column_width (sheet, column, text_width); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING); - } -} - - -void -gtk_sheet_set_autoscroll (GtkSheet *sheet, gboolean autoscroll) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - sheet->autoscroll = autoscroll; -} - -gboolean -gtk_sheet_autoscroll (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - - return sheet->autoscroll; -} - - -void -gtk_sheet_set_justify_entry (GtkSheet *sheet, gboolean justify) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - sheet->justify_entry = justify; -} - -gboolean -gtk_sheet_justify_entry (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - - return sheet->justify_entry; -} - - - -void -gtk_sheet_freeze (GtkSheet *sheet) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - sheet->freeze_count++; - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN); -} - -void -gtk_sheet_thaw (GtkSheet *sheet) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (sheet->freeze_count == 0) return; - - sheet->freeze_count--; - if (sheet->freeze_count > 0) return; - - adjust_scrollbars (sheet); - - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN); - - sheet->old_vadjustment = -1.; - sheet->old_hadjustment = -1.; - - if (sheet->hadjustment) - g_signal_emit_by_name (sheet->hadjustment, - "value_changed"); - if (sheet->vadjustment) - g_signal_emit_by_name (sheet->vadjustment, - "value_changed"); - - if (sheet->state == GTK_STATE_NORMAL) - if (sheet->entry_widget && GTK_WIDGET_MAPPED (sheet->entry_widget)) - { - gtk_sheet_activate_cell (sheet, sheet->active_cell.row, - sheet->active_cell.col); - } - -} - -void -gtk_sheet_set_row_titles_width (GtkSheet *sheet, guint width) -{ - if (width < COLUMN_MIN_WIDTH) return; - - sheet->row_title_area.width = width; - - adjust_scrollbars (sheet); - - sheet->old_hadjustment = -1.; - if (sheet->hadjustment) - g_signal_emit_by_name (sheet->hadjustment, - "value_changed"); - size_allocate_global_button (sheet); -} - -void -gtk_sheet_set_column_titles_height (GtkSheet *sheet, guint height) -{ - if (height < DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))) return; - - sheet->column_title_area.height = height; - - adjust_scrollbars (sheet); - - sheet->old_vadjustment = -1.; - if (sheet->vadjustment) - g_signal_emit_by_name (sheet->vadjustment, - "value_changed"); - size_allocate_global_button (sheet); -} - -void -gtk_sheet_show_column_titles (GtkSheet *sheet) -{ - gint col; - - if (sheet->column_titles_visible) return; - - sheet->column_titles_visible = TRUE; - - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - gdk_window_show (sheet->column_title_window); - gdk_window_move_resize (sheet->column_title_window, - sheet->column_title_area.x, - sheet->column_title_area.y, - sheet->column_title_area.width, - sheet->column_title_area.height); - - for (col = MIN_VISIBLE_COLUMN (sheet); - col <= MAX_VISIBLE_COLUMN (sheet); - col++) - { - GtkSheetButton *button = xxx_column_button (sheet, col); - GtkSheetChild *child = button->child; - if (child) - gtk_sheet_child_show (child); - gtk_sheet_button_free (button); - } - adjust_scrollbars (sheet); - } - - sheet->old_vadjustment = -1.; - if (sheet->vadjustment) - g_signal_emit_by_name (sheet->vadjustment, - "value_changed"); - size_allocate_global_button (sheet); -} - - -void -gtk_sheet_show_row_titles (GtkSheet *sheet) -{ - gint row; - - if (sheet->row_titles_visible) return; - - sheet->row_titles_visible = TRUE; - - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - gdk_window_show (sheet->row_title_window); - gdk_window_move_resize (sheet->row_title_window, - sheet->row_title_area.x, - sheet->row_title_area.y, - sheet->row_title_area.width, - sheet->row_title_area.height); - - for (row = MIN_VISIBLE_ROW (sheet); - row <= MAX_VISIBLE_ROW (sheet); - row++) - { - const GtkSheetButton *button = yyy_row_button (sheet, row); - GtkSheetChild *child = button->child; - - if (child) - { - gtk_sheet_child_show (child); - } - } - adjust_scrollbars (sheet); - } - - sheet->old_hadjustment = -1.; - if (sheet->hadjustment) - g_signal_emit_by_name (sheet->hadjustment, - "value_changed"); - size_allocate_global_button (sheet); -} - -void -gtk_sheet_hide_column_titles (GtkSheet *sheet) -{ - gint col; - - if (!sheet->column_titles_visible) return; - - sheet->column_titles_visible = FALSE; - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - if (sheet->column_title_window) - gdk_window_hide (sheet->column_title_window); - if (GTK_WIDGET_VISIBLE (sheet->button)) - gtk_widget_hide (sheet->button); - - for (col = MIN_VISIBLE_COLUMN (sheet); - col <= MAX_VISIBLE_COLUMN (sheet); - col++) - { - GtkSheetButton *button = xxx_column_button (sheet, col); - GtkSheetChild *child = button->child; - if (child) - gtk_sheet_child_hide (child); - gtk_sheet_button_free (button); - } - adjust_scrollbars (sheet); - } - - sheet->old_vadjustment = -1.; - if (sheet->vadjustment) - g_signal_emit_by_name (sheet->vadjustment, - "value_changed"); -} - -void -gtk_sheet_hide_row_titles (GtkSheet *sheet) -{ - gint row; - - if (!sheet->row_titles_visible) return; - - sheet->row_titles_visible = FALSE; - - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - if (sheet->row_title_window) - gdk_window_hide (sheet->row_title_window); - if (GTK_WIDGET_VISIBLE (sheet->button)) - gtk_widget_hide (sheet->button); - for (row = MIN_VISIBLE_ROW (sheet); - row <= MAX_VISIBLE_ROW (sheet); - row++) - { - const GtkSheetButton *button = yyy_row_button (sheet, row); - GtkSheetChild *child = button->child; - - if (child) - gtk_sheet_child_hide (child); - } - adjust_scrollbars (sheet); - } - - sheet->old_hadjustment = -1.; - if (sheet->hadjustment) - g_signal_emit_by_name (sheet->hadjustment, - "value_changed"); -} - -gboolean -gtk_sheet_column_titles_visible (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - return sheet->column_titles_visible; -} - -gboolean -gtk_sheet_row_titles_visible (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - return sheet->row_titles_visible; -} - -void -gtk_sheet_moveto (GtkSheet *sheet, - gint row, - gint column, - gfloat row_align, - gfloat col_align) -{ - gint x, y; - guint width, height; - gint adjust; - gint min_row, min_col; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - g_return_if_fail (sheet->hadjustment != NULL); - g_return_if_fail (sheet->vadjustment != NULL); - - if (row < 0 || row >= yyy_row_count (sheet)) - return; - if (column < 0 || column >= xxx_column_count (sheet)) - return; - - height = sheet->sheet_window_height; - width = sheet->sheet_window_width; - - /* adjust vertical scrollbar */ - if (row >= 0 && row_align >= 0.0) - { - y = ROW_TOP_YPIXEL (sheet, row) - sheet->voffset - - (gint) ( row_align * height + (1.0 - row_align) - * yyy_row_height (sheet, row)); - - /* This forces the sheet to scroll when you don't see the entire cell */ - min_row = row; - adjust = 0; - if (row_align >= 1.0) - { - while (min_row >= 0 && min_row > MIN_VISIBLE_ROW (sheet)) - { - if (yyy_row_is_visible (sheet, min_row)) - adjust += yyy_row_height (sheet, min_row); - - if (adjust >= height) - { - break; - } - min_row--; - } - min_row = MAX (min_row, 0); - - min_row ++; - - y = ROW_TOP_YPIXEL (sheet, min_row) - sheet->voffset + - yyy_row_height (sheet, min_row) - 1; - } - - if (y < 0) - sheet->vadjustment->value = 0.0; - else - sheet->vadjustment->value = y; - - sheet->old_vadjustment = -1.; - g_signal_emit_by_name (sheet->vadjustment, - "value_changed"); - - } - - /* adjust horizontal scrollbar */ - if (column >= 0 && col_align >= 0.0) - { - x = COLUMN_LEFT_XPIXEL (sheet, column) - sheet->hoffset - - (gint) ( col_align*width + (1.0 - col_align)* - xxx_column_width (sheet, column)); - - /* This forces the sheet to scroll when you don't see the entire cell */ - min_col = column; - adjust = 0; - if (col_align == 1.0) - { - while (min_col >= 0 && min_col > MIN_VISIBLE_COLUMN (sheet)) - { - if (xxx_column_is_visible (sheet, min_col)) - adjust += xxx_column_width (sheet, min_col); - - if (adjust >= width) - { - break; - } - min_col--; - } - min_col = MAX (min_col, 0); - x = COLUMN_LEFT_XPIXEL (sheet, min_col) - sheet->hoffset + - xxx_column_width (sheet, min_col) - 1; - } - - if (x < 0) - sheet->hadjustment->value = 0.0; - else - sheet->hadjustment->value = x; - - sheet->old_vadjustment = -1.; - g_signal_emit_by_name (sheet->hadjustment, - "value_changed"); - } -} - - -void -gtk_sheet_columns_set_resizable (GtkSheet *sheet, gboolean resizable) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - sheet->columns_resizable = resizable; -} - -gboolean -gtk_sheet_columns_resizable (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - - return sheet->columns_resizable; -} - - -void -gtk_sheet_rows_set_resizable (GtkSheet *sheet, gboolean resizable) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - sheet->rows_resizable = resizable; -} - -gboolean -gtk_sheet_rows_resizable (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - - return sheet->rows_resizable; -} - - -void -gtk_sheet_select_row (GtkSheet * sheet, - gint row) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (row < 0 || row >= yyy_row_count (sheet)) - return; - - if (sheet->state != GTK_SHEET_NORMAL) - gtk_sheet_real_unselect_range (sheet, NULL); - else - gtk_sheet_deactivate_cell (sheet); - - sheet->state = GTK_SHEET_ROW_SELECTED; - sheet->range.row0 = row; - sheet->range.col0 = 0; - sheet->range.rowi = row; - sheet->range.coli = xxx_column_count (sheet) - 1; - sheet->active_cell.row = row; - sheet->active_cell.col = 0; - - g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row); - gtk_sheet_real_select_range (sheet, NULL); -} - - -void -gtk_sheet_select_column (GtkSheet * sheet, gint column) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (column < 0 || column >= xxx_column_count (sheet)) - return; - - if (sheet->state != GTK_SHEET_NORMAL) - gtk_sheet_real_unselect_range (sheet, NULL); - else - gtk_sheet_deactivate_cell (sheet); - - - sheet->state = GTK_SHEET_COLUMN_SELECTED; - sheet->range.row0 = 0; - sheet->range.col0 = column; - sheet->range.rowi = yyy_row_count (sheet) - 1; - sheet->range.coli = column; - sheet->active_cell.row = 0; - sheet->active_cell.col = column; - - g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column); - gtk_sheet_real_select_range (sheet, NULL); -} - - - - -static gboolean -gtk_sheet_range_isvisible (const GtkSheet * sheet, - GtkSheetRange range) -{ - g_return_val_if_fail (sheet != NULL, FALSE); - - if (range.row0 < 0 || range.row0 >= yyy_row_count (sheet)) - return FALSE; - - if (range.rowi < 0 || range.rowi >= yyy_row_count (sheet)) - return FALSE; - - if (range.col0 < 0 || range.col0 >= xxx_column_count (sheet)) - return FALSE; - - if (range.coli < 0 || range.coli >= xxx_column_count (sheet)) - return FALSE; - - if (range.rowi < MIN_VISIBLE_ROW (sheet)) - return FALSE; - - if (range.row0 > MAX_VISIBLE_ROW (sheet)) - return FALSE; - - if (range.coli < MIN_VISIBLE_COLUMN (sheet)) - return FALSE; - - if (range.col0 > MAX_VISIBLE_COLUMN (sheet)) - return FALSE; - - return TRUE; -} - -static gboolean -gtk_sheet_cell_isvisible (GtkSheet * sheet, - gint row, gint column) -{ - GtkSheetRange range; - - range.row0 = row; - range.col0 = column; - range.rowi = row; - range.coli = column; - - return gtk_sheet_range_isvisible (sheet, range); -} - -void -gtk_sheet_get_visible_range (GtkSheet *sheet, GtkSheetRange *range) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)) ; - g_return_if_fail (range != NULL); - - range->row0 = MIN_VISIBLE_ROW (sheet); - range->col0 = MIN_VISIBLE_COLUMN (sheet); - range->rowi = MAX_VISIBLE_ROW (sheet); - range->coli = MAX_VISIBLE_COLUMN (sheet); -} - -GtkAdjustment * -gtk_sheet_get_vadjustment (GtkSheet * sheet) -{ - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - - return sheet->vadjustment; -} - -GtkAdjustment * -gtk_sheet_get_hadjustment (GtkSheet * sheet) -{ - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - - return sheet->hadjustment; -} - -void -gtk_sheet_set_vadjustment (GtkSheet *sheet, - GtkAdjustment *adjustment) -{ - GtkAdjustment *old_adjustment; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - if (adjustment) - g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); - - if (sheet->vadjustment == adjustment) - return; - - old_adjustment = sheet->vadjustment; - - if (sheet->vadjustment) - { - g_signal_handlers_disconnect_matched (sheet->vadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, - sheet); - g_object_unref (sheet->vadjustment); - } - - sheet->vadjustment = adjustment; - - if (sheet->vadjustment) - { - g_object_ref (sheet->vadjustment); - g_object_ref_sink (sheet->vadjustment); - - g_signal_connect (sheet->vadjustment, "value_changed", - G_CALLBACK (vadjustment_value_changed), - sheet); - } - - if (!sheet->vadjustment || !old_adjustment) - { - gtk_widget_queue_resize (GTK_WIDGET (sheet)); - return; - } - - sheet->old_vadjustment = sheet->vadjustment->value; -} - -void -gtk_sheet_set_hadjustment (GtkSheet *sheet, - GtkAdjustment *adjustment) -{ - GtkAdjustment *old_adjustment; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - if (adjustment) - g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); - - if (sheet->hadjustment == adjustment) - return; - - old_adjustment = sheet->hadjustment; - - if (sheet->hadjustment) - { - g_signal_handlers_disconnect_matched (sheet->hadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, - sheet); - g_object_unref (sheet->hadjustment); - } - - sheet->hadjustment = adjustment; - - if (sheet->hadjustment) - { - g_object_ref (sheet->hadjustment); - g_object_ref_sink (sheet->hadjustment); - - g_signal_connect (sheet->hadjustment, "value_changed", - G_CALLBACK (hadjustment_value_changed), - sheet); - } - - if (!sheet->hadjustment || !old_adjustment) - { - gtk_widget_queue_resize (GTK_WIDGET (sheet)); - return; - } - - sheet->old_hadjustment = sheet->hadjustment->value; -} - -static void -gtk_sheet_set_scroll_adjustments (GtkSheet *sheet, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment) -{ - if (sheet->hadjustment != hadjustment) - gtk_sheet_set_hadjustment (sheet, hadjustment); - - if (sheet->vadjustment != vadjustment) - gtk_sheet_set_vadjustment (sheet, vadjustment); -} - -static void -gtk_sheet_finalize (GObject * object) -{ - GtkSheet *sheet; - - g_return_if_fail (object != NULL); - g_return_if_fail (GTK_IS_SHEET (object)); - - sheet = GTK_SHEET (object); - - if (G_OBJECT_CLASS (parent_class)->finalize) - (*G_OBJECT_CLASS (parent_class)->finalize) (object); -} - -static void -gtk_sheet_dispose (GObject *object) -{ - GtkSheet *sheet = GTK_SHEET (object); - GList *children; - - g_return_if_fail (object != NULL); - g_return_if_fail (GTK_IS_SHEET (object)); - - if ( sheet->dispose_has_run ) - return ; - - sheet->dispose_has_run = TRUE; - - if (sheet->model) g_object_unref (sheet->model); - if (sheet->row_geometry) g_object_unref (sheet->row_geometry); - if (sheet->column_geometry) g_object_unref (sheet->column_geometry); - - g_object_unref (sheet->entry_container); - sheet->entry_container = NULL; - - g_object_unref (sheet->button); - sheet->button = NULL; - - /* unref adjustments */ - if (sheet->hadjustment) - { - g_signal_handlers_disconnect_matched (sheet->hadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, - sheet); - - g_object_unref (sheet->hadjustment); - sheet->hadjustment = NULL; - } - - if (sheet->vadjustment) - { - g_signal_handlers_disconnect_matched (sheet->vadjustment, - G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, - sheet); - - g_object_unref (sheet->vadjustment); - - sheet->vadjustment = NULL; - } - - children = sheet->children; - while (children) - { - GtkSheetChild *child = (GtkSheetChild *)children->data; - if (child && child->widget) - gtk_sheet_remove (GTK_CONTAINER (sheet), child->widget); - children = sheet->children; - } - sheet->children = NULL; - - if (G_OBJECT_CLASS (parent_class)->dispose) - (*G_OBJECT_CLASS (parent_class)->dispose) (object); -} - -static void -gtk_sheet_style_set (GtkWidget *widget, - GtkStyle *previous_style) -{ - GtkSheet *sheet; - - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_SHEET (widget)); - - if (GTK_WIDGET_CLASS (parent_class)->style_set) - (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style); - - sheet = GTK_SHEET (widget); - - if (GTK_WIDGET_REALIZED (widget)) - { - gtk_style_set_background (widget->style, widget->window, widget->state); - } - -} - -static void -gtk_sheet_realize (GtkWidget * widget) -{ - GtkSheet *sheet; - GdkWindowAttr attributes; - gint attributes_mask; - GdkGCValues values, auxvalues; - GdkColormap *colormap; - GtkSheetChild *child; - GList *children; - - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_SHEET (widget)); - - sheet = GTK_SHEET (widget); - - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.x = widget->allocation.x; - attributes.y = widget->allocation.y; - attributes.width = widget->allocation.width; - attributes.height = widget->allocation.height; - attributes.wclass = GDK_INPUT_OUTPUT; - - attributes.visual = gtk_widget_get_visual (widget); - attributes.colormap = gtk_widget_get_colormap (widget); - - attributes.event_mask = gtk_widget_get_events (widget); - attributes.event_mask |= (GDK_EXPOSURE_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_KEY_PRESS_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK); - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | - GDK_WA_CURSOR; - - attributes.cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW); - - /* main window */ - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); - - gdk_window_set_user_data (widget->window, sheet); - - widget->style = gtk_style_attach (widget->style, widget->window); - - gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); - - attributes.x = 0; - if (sheet->row_titles_visible) - attributes.x = sheet->row_title_area.width; - attributes.y = 0; - attributes.width = sheet->column_title_area.width; - attributes.height = sheet->column_title_area.height; - - /* column - title window */ - sheet->column_title_window = gdk_window_new (widget->window, &attributes, attributes_mask); - gdk_window_set_user_data (sheet->column_title_window, sheet); - gtk_style_set_background (widget->style, sheet->column_title_window, GTK_STATE_NORMAL); - - attributes.x = 0; - attributes.y = 0; - if (sheet->column_titles_visible) - attributes.y = sheet->column_title_area.height; - attributes.width = sheet->row_title_area.width; - attributes.height = sheet->row_title_area.height; - - /* row - title window */ - sheet->row_title_window = gdk_window_new (widget->window, &attributes, attributes_mask); - gdk_window_set_user_data (sheet->row_title_window, sheet); - gtk_style_set_background (widget->style, sheet->row_title_window, GTK_STATE_NORMAL); - - /* sheet - window */ - attributes.cursor = gdk_cursor_new (GDK_PLUS); - - attributes.x = 0; - attributes.y = 0; - attributes.width = sheet->sheet_window_width, - attributes.height = sheet->sheet_window_height; - - sheet->sheet_window = gdk_window_new (widget->window, &attributes, attributes_mask); - gdk_window_set_user_data (sheet->sheet_window, sheet); - - gdk_cursor_unref (attributes.cursor); - - gdk_window_set_background (sheet->sheet_window, &widget->style->white); - gdk_window_show (sheet->sheet_window); - - /* backing_pixmap */ - gtk_sheet_make_backing_pixmap (sheet, 0, 0); - - /* GCs */ - if (sheet->fg_gc) - g_object_unref (sheet->fg_gc); - if (sheet->bg_gc) - g_object_unref (sheet->bg_gc); - sheet->fg_gc = gdk_gc_new (widget->window); - sheet->bg_gc = gdk_gc_new (widget->window); - - colormap = gtk_widget_get_colormap (widget); - - gdk_gc_get_values (sheet->fg_gc, &auxvalues); - - values.foreground = widget->style->white; - values.function = GDK_INVERT; - values.subwindow_mode = GDK_INCLUDE_INFERIORS; - if (sheet->xor_gc) - g_object_unref (sheet->xor_gc); - sheet->xor_gc = gdk_gc_new_with_values (widget->window, - &values, - GDK_GC_FOREGROUND | - GDK_GC_FUNCTION | - GDK_GC_SUBWINDOW); - - - gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); - gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); - - gtk_widget_set_parent_window (sheet->button, sheet->sheet_window); - gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet)); - - - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_PLUS); - - if (sheet->column_titles_visible) - gdk_window_show (sheet->column_title_window); - if (sheet->row_titles_visible) - gdk_window_show (sheet->row_title_window); - - size_allocate_row_title_buttons (sheet); - size_allocate_column_title_buttons (sheet); - - children = sheet->children; - while (children) - { - child = children->data; - children = children->next; - - gtk_sheet_realize_child (sheet, child); - } - - gtk_sheet_update_primary_selection (sheet); -} - -static void -create_global_button (GtkSheet *sheet) -{ - sheet->button = gtk_button_new_with_label (" "); - - g_object_ref_sink (sheet->button); - - g_signal_connect (sheet->button, - "pressed", - G_CALLBACK (global_button_clicked), - sheet); -} - -static void -size_allocate_global_button (GtkSheet *sheet) -{ - GtkAllocation allocation; - - if (!sheet->column_titles_visible) return; - if (!sheet->row_titles_visible) return; - - gtk_widget_size_request (sheet->button, NULL); - - allocation.x = 0; - allocation.y = 0; - allocation.width = sheet->row_title_area.width; - allocation.height = sheet->column_title_area.height; - - gtk_widget_size_allocate (sheet->button, &allocation); - gtk_widget_show (sheet->button); -} - -static void -global_button_clicked (GtkWidget *widget, gpointer data) -{ - gboolean veto; - - gtk_sheet_click_cell (GTK_SHEET (data), - 1, - 1, &veto); - gtk_widget_grab_focus (GTK_WIDGET (data)); -} - - -static void -gtk_sheet_unrealize (GtkWidget * widget) -{ - GtkSheet *sheet; - - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_SHEET (widget)); - - sheet = GTK_SHEET (widget); - - gdk_cursor_unref (sheet->cursor_drag); - - g_object_unref (sheet->xor_gc); - g_object_unref (sheet->fg_gc); - g_object_unref (sheet->bg_gc); - - gdk_window_destroy (sheet->sheet_window); - gdk_window_destroy (sheet->column_title_window); - gdk_window_destroy (sheet->row_title_window); - - if (sheet->pixmap) - { - g_object_unref (sheet->pixmap); - sheet->pixmap = NULL; - } - - sheet->column_title_window = NULL; - sheet->sheet_window = NULL; - sheet->xor_gc = NULL; - sheet->fg_gc = NULL; - sheet->bg_gc = NULL; - - gtk_widget_unparent (sheet->entry_widget); - if (sheet->button != NULL) - gtk_widget_unparent (sheet->button); - - if (GTK_WIDGET_CLASS (parent_class)->unrealize) - (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); -} - -static void -gtk_sheet_map (GtkWidget * widget) -{ - GtkSheet *sheet = GTK_SHEET (widget); - GtkSheetChild *child; - GList *children; - - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_SHEET (widget)); - - if (!GTK_WIDGET_MAPPED (widget)) - { - GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); - - gdk_window_show (widget->window); - gdk_window_show (sheet->sheet_window); - - if (sheet->column_titles_visible) - { - size_allocate_column_title_buttons (sheet); - gdk_window_show (sheet->column_title_window); - } - if (sheet->row_titles_visible) - { - size_allocate_row_title_buttons (sheet); - gdk_window_show (sheet->row_title_window); - } - - if (!GTK_WIDGET_MAPPED (sheet->entry_widget) - && sheet->active_cell.row >= 0 - && sheet->active_cell.col >= 0 ) - { - gtk_widget_show (sheet->entry_widget); - gtk_widget_map (sheet->entry_widget); - } - - if (GTK_WIDGET_VISIBLE (sheet->button) && - !GTK_WIDGET_MAPPED (sheet->button)) - { - gtk_widget_show (sheet->button); - gtk_widget_map (sheet->button); - } - - if (GTK_BIN (sheet->button)->child) - if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) && - !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child)) - gtk_widget_map (GTK_BIN (sheet->button)->child); - - gtk_sheet_range_draw (sheet, NULL); - gtk_sheet_activate_cell (sheet, - sheet->active_cell.row, - sheet->active_cell.col); - - children = sheet->children; - while (children) - { - child = children->data; - children = children->next; - - if (GTK_WIDGET_VISIBLE (child->widget) && - !GTK_WIDGET_MAPPED (child->widget)) - { - gtk_widget_map (child->widget); - gtk_sheet_position_child (sheet, child); - } - } - - } -} - -static void -gtk_sheet_unmap (GtkWidget * widget) -{ - GtkSheet *sheet; - GtkSheetChild *child; - GList *children; - - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_SHEET (widget)); - - sheet = GTK_SHEET (widget); - - if (GTK_WIDGET_MAPPED (widget)) - { - GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); - - gdk_window_hide (sheet->sheet_window); - if (sheet->column_titles_visible) - gdk_window_hide (sheet->column_title_window); - if (sheet->row_titles_visible) - gdk_window_hide (sheet->row_title_window); - gdk_window_hide (widget->window); - - if (GTK_WIDGET_MAPPED (sheet->entry_widget)) - gtk_widget_unmap (sheet->entry_widget); - - if (GTK_WIDGET_MAPPED (sheet->button)) - gtk_widget_unmap (sheet->button); - - children = sheet->children; - while (children) - { - child = children->data; - children = children->next; - - if (GTK_WIDGET_VISIBLE (child->widget) && - GTK_WIDGET_MAPPED (child->widget)) - { - gtk_widget_unmap (child->widget); - } - } - - } -} - - -static void -gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col) -{ - GtkWidget *widget; - GdkGC *fg_gc, *bg_gc; - GtkSheetCellAttr attributes; - GdkRectangle area; - - g_return_if_fail (sheet != NULL); - - /* bail now if we arn't drawable yet */ - if (!GTK_WIDGET_DRAWABLE (sheet)) return; - - if (row < 0 || row >= yyy_row_count (sheet)) return; - if (col < 0 || col >= xxx_column_count (sheet)) return; - if (! xxx_column_is_visible (sheet, col)) return; - if (! yyy_row_is_visible (sheet, row)) return; - - widget = GTK_WIDGET (sheet); - - gtk_sheet_get_attributes (sheet, row, col, &attributes); - - /* select GC for background rectangle */ - gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground); - gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); - - fg_gc = sheet->fg_gc; - bg_gc = sheet->bg_gc; - - area.x = COLUMN_LEFT_XPIXEL (sheet, col); - area.y = ROW_TOP_YPIXEL (sheet, row); - area.width= xxx_column_width (sheet, col); - area.height = yyy_row_height (sheet, row); - - gdk_draw_rectangle (sheet->pixmap, - bg_gc, - TRUE, - area.x, - area.y, - area.width, - area.height); - - gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0); - - if (sheet->show_grid) - { - gdk_gc_set_foreground (sheet->bg_gc, &sheet->grid_color); - - gdk_draw_rectangle (sheet->pixmap, - sheet->bg_gc, - FALSE, - area.x, area.y, - area.width, area.height); - } -} - -static void -gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col) -{ - GtkWidget *widget; - GdkRectangle area; - gint i; - gint text_width, text_height, y; - gint xoffset = 0; - gint size, sizel, sizer; - GdkGC *fg_gc, *bg_gc; - GtkSheetCellAttr attributes; - PangoLayout *layout; - PangoRectangle rect; - PangoRectangle logical_rect; - PangoLayoutLine *line; - PangoFontMetrics *metrics; - PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (sheet)); - gint ascent, descent, y_pos; - - gchar *label; - - g_return_if_fail (sheet != NULL); - - /* bail now if we aren't drawable yet */ - if (!GTK_WIDGET_DRAWABLE (sheet)) - return; - - label = gtk_sheet_cell_get_text (sheet, row, col); - if (!label) - return; - - if (row < 0 || row >= yyy_row_count (sheet)) return; - if (col < 0 || col >= xxx_column_count (sheet)) return; - if (! xxx_column_is_visible (sheet, col)) return; - if (!yyy_row_is_visible (sheet, row)) return; - - - widget = GTK_WIDGET (sheet); - - gtk_sheet_get_attributes (sheet, row, col, &attributes); - - /* select GC for background rectangle */ - gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground); - gdk_gc_set_foreground (sheet->bg_gc, &attributes.background); - - fg_gc = sheet->fg_gc; - bg_gc = sheet->bg_gc; - - area.x = COLUMN_LEFT_XPIXEL (sheet, col); - area.y = ROW_TOP_YPIXEL (sheet, row); - area.width = xxx_column_width (sheet, col); - area.height = yyy_row_height (sheet, row); - - - layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label); - dispose_string (sheet, label); - pango_layout_set_font_description (layout, attributes.font_desc); - - pango_layout_get_pixel_extents (layout, NULL, &rect); - - line = pango_layout_get_lines (layout)->data; - pango_layout_line_get_extents (line, NULL, &logical_rect); - - metrics = pango_context_get_metrics (context, - attributes.font_desc, - pango_context_get_language (context)); - - ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE; - descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE; - - pango_font_metrics_unref (metrics); - - /* Align primarily for locale's ascent / descent */ - - logical_rect.height /= PANGO_SCALE; - logical_rect.y /= PANGO_SCALE; - y_pos = area.height - logical_rect.height; - - if (logical_rect.height > area.height) - y_pos = (logical_rect.height - area.height - 2 * CELLOFFSET) / 2; - else if (y_pos < 0) - y_pos = 0; - else if (y_pos + logical_rect.height > area.height) - y_pos = area.height - logical_rect.height; - - text_width = rect.width; - text_height = rect.height; - y = area.y + y_pos - CELLOFFSET; - - switch (attributes.justification) - { - case GTK_JUSTIFY_RIGHT: - size = area.width; - area.x +=area.width; - { - for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--) - { - if ( !gtk_sheet_cell_empty (sheet, row, i)) break; - if (size >= text_width + CELLOFFSET) break; - size +=xxx_column_width (sheet, i); - xxx_column_set_right_column (sheet, i, - MAX (col, - xxx_column_right_column (sheet, i))); - } - area.width = size; - } - area.x -= size; - xoffset += area.width - text_width - 2 * CELLOFFSET - - attributes.border.width / 2; - break; - case GTK_JUSTIFY_CENTER: - sizel = area.width / 2; - sizer = area.width / 2; - area.x += area.width / 2; - { - for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++) - { - if ( ! gtk_sheet_cell_empty (sheet, row, i)) break; - if (sizer >= text_width / 2) break; - sizer += xxx_column_width (sheet, i); - xxx_column_set_left_column (sheet, i, - MIN ( - col, - xxx_column_left_column (sheet, i))); - } - for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--) - { - if ( ! gtk_sheet_cell_empty (sheet, row, i)) break; - if (sizel >= text_width / 2) break; - sizel +=xxx_column_width (sheet, i); - xxx_column_set_right_column (sheet, i, - MAX (col, - xxx_column_right_column (sheet, i))); - } - size = MIN (sizel, sizer); - } - area.x -= sizel; - xoffset += sizel - text_width / 2 - CELLOFFSET; - area.width = sizel + sizer; - break; - case GTK_JUSTIFY_LEFT: - default: - size = area.width; - { - for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++) - { - if (! gtk_sheet_cell_empty (sheet, row, i)) break; - if (size >= text_width + CELLOFFSET) break; - size +=xxx_column_width (sheet, i); - xxx_column_set_left_column (sheet, i, - MIN ( - col, - xxx_column_left_column (sheet, i))); - - } - area.width = size; - } - xoffset += attributes.border.width / 2; - break; - } - - gdk_gc_set_clip_rectangle (fg_gc, &area); - - - gdk_draw_layout (sheet->pixmap, fg_gc, - area.x + xoffset + CELLOFFSET, - y, - layout); - - gdk_gc_set_clip_rectangle (fg_gc, NULL); - g_object_unref (layout); - - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - area.x, - area.y, - area.x, - area.y, - area.width, - area.height); - -} - -static void -gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range) -{ - gint i, j; - GtkSheetRange drawing_range; - GdkRectangle area; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_SHEET (sheet)); - - if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return; - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return; - - if (range == NULL) - { - drawing_range.row0 = MIN_VISIBLE_ROW (sheet); - drawing_range.col0 = MIN_VISIBLE_COLUMN (sheet); - drawing_range.rowi = MIN (MAX_VISIBLE_ROW (sheet), - yyy_row_count (sheet) - 1); - drawing_range.coli = MAX_VISIBLE_COLUMN (sheet); - - - gdk_draw_rectangle (sheet->pixmap, - GTK_WIDGET (sheet)->style->white_gc, - TRUE, - 0, 0, - sheet->sheet_window_width, - sheet->sheet_window_height); - } - else - { - drawing_range.row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet)); - drawing_range.col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet)); - drawing_range.rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet)); - drawing_range.coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet)); - } - - if (drawing_range.coli == xxx_column_count (sheet) - 1) - { - area.x = COLUMN_LEFT_XPIXEL (sheet, - xxx_column_count (sheet) - 1) + - xxx_column_width (sheet, xxx_column_count (sheet) - 1) + 1; - - area.y = 0; - - gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color); - - gdk_draw_rectangle (sheet->pixmap, - sheet->fg_gc, - TRUE, - area.x, area.y, - sheet->sheet_window_width - area.x, - sheet->sheet_window_height); - - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - area.x, - area.y, - area.x, - area.y, - sheet->sheet_window_width - area.x, - sheet->sheet_window_height); - } - - if (drawing_range.rowi == yyy_row_count (sheet) - 1) - { - area.x = 0; - area.y = ROW_TOP_YPIXEL (sheet, - yyy_row_count (sheet) - 1) + - yyy_row_height (sheet, yyy_row_count (sheet) - 1) + 1; - - gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color); - - gdk_draw_rectangle (sheet->pixmap, - sheet->fg_gc, - TRUE, - area.x, area.y, - sheet->sheet_window_width, - sheet->sheet_window_height - area.y); - - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - area.x, - area.y, - area.x, - area.y, - sheet->sheet_window_width, - sheet->sheet_window_height - area.y); - } - - for (i = drawing_range.row0; i <= drawing_range.rowi; i++) - for (j = drawing_range.col0; j <= drawing_range.coli; j++) - { - gtk_sheet_cell_draw_default (sheet, i, j); - gtk_sheet_cell_draw_label (sheet, i, j); - } - - gtk_sheet_draw_backing_pixmap (sheet, drawing_range); - - if (sheet->state != GTK_SHEET_NORMAL && - gtk_sheet_range_isvisible (sheet, sheet->range)) - gtk_sheet_range_draw_selection (sheet, drawing_range); - - if (sheet->state == GTK_STATE_NORMAL && - sheet->active_cell.row >= drawing_range.row0 && - sheet->active_cell.row <= drawing_range.rowi && - sheet->active_cell.col >= drawing_range.col0 && - sheet->active_cell.col <= drawing_range.coli) - gtk_sheet_show_active_cell (sheet); -} - -static void -gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range) -{ - GdkRectangle area; - gint i, j; - GtkSheetRange aux; - - if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 || - range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0) - return; - - if (!gtk_sheet_range_isvisible (sheet, range)) return; - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - aux = range; - - range.col0 = MAX (sheet->range.col0, range.col0); - range.coli = MIN (sheet->range.coli, range.coli); - range.row0 = MAX (sheet->range.row0, range.row0); - range.rowi = MIN (sheet->range.rowi, range.rowi); - - range.col0 = MAX (range.col0, MIN_VISIBLE_COLUMN (sheet)); - range.coli = MIN (range.coli, MAX_VISIBLE_COLUMN (sheet)); - range.row0 = MAX (range.row0, MIN_VISIBLE_ROW (sheet)); - range.rowi = MIN (range.rowi, MAX_VISIBLE_ROW (sheet)); - - for (i = range.row0; i <= range.rowi; i++) - { - for (j = range.col0; j <= range.coli; j++) - { - - if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED && - xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i)) - { - - area.x = COLUMN_LEFT_XPIXEL (sheet, j); - area.y = ROW_TOP_YPIXEL (sheet, i); - area.width= xxx_column_width (sheet, j); - area.height = yyy_row_height (sheet, i); - - if (i == sheet->range.row0) - { - area.y = area.y + 2; - area.height = area.height - 2; - } - if (i == sheet->range.rowi) area.height = area.height - 3; - if (j == sheet->range.col0) - { - area.x = area.x + 2; - area.width = area.width - 2; - } - if (j == sheet->range.coli) area.width = area.width - 3; - - if (i != sheet->active_cell.row || j != sheet->active_cell.col) - { - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - area.x + 1, area.y + 1, - area.width, area.height); - } - } - - } - } - - gtk_sheet_draw_border (sheet, sheet->range); -} - -static void -gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, GtkSheetRange range) -{ - gint x, y, width, height; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - x = COLUMN_LEFT_XPIXEL (sheet, range.col0); - y = ROW_TOP_YPIXEL (sheet, range.row0); - width = COLUMN_LEFT_XPIXEL (sheet, range.coli) - x + - xxx_column_width (sheet, range.coli); - - height = ROW_TOP_YPIXEL (sheet, range.rowi)- y + yyy_row_height (sheet, range.rowi); - - if (range.row0 == sheet->range.row0) - { - y = y - 5; - height = height + 5; - } - if (range.rowi == sheet->range.rowi) height = height + 5; - if (range.col0 == sheet->range.col0) - { - x = x - 5; - width = width + 5; - } - if (range.coli == sheet->range.coli) width = width + 5; - - width = MIN (width, sheet->sheet_window_width - x); - height = MIN (height, sheet->sheet_window_height - y); - - x--; - y--; - width +=2; - height +=2; - - x = (sheet->row_titles_visible) - ? MAX (x, sheet->row_title_area.width) : MAX (x, 0); - y = (sheet->column_titles_visible) - ? MAX (y, sheet->column_title_area.height) : MAX (y, 0); - - if (range.coli == xxx_column_count (sheet) - 1) - width = sheet->sheet_window_width - x; - if (range.rowi == yyy_row_count (sheet) - 1) - height = sheet->sheet_window_height - y; - - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - x, - y, - x, - y, - width + 1, - height + 1); -} - - -void -gtk_sheet_set_cell_text (GtkSheet *sheet, gint row, gint col, const gchar *text) -{ - GtkSheetCellAttr attributes; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return; - if (col < 0 || row < 0) return; - - gtk_sheet_get_attributes (sheet, row, col, &attributes); - gtk_sheet_set_cell (sheet, row, col, attributes.justification, text); -} - -static inline gint -safe_strcmp (const gchar *s1, const gchar *s2) -{ - if ( !s1 && !s2) return 0; - if ( !s1) return - 1; - if ( !s2) return +1; - return strcmp (s1, s2); -} - -void -gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col, - GtkJustification justification, - const gchar *text) -{ - GSheetModel *model ; - gboolean changed ; - gchar *old_text ; - - GtkSheetRange range; - gint text_width; - GtkSheetCellAttr attributes; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return; - if (col < 0 || row < 0) return; - - gtk_sheet_get_attributes (sheet, row, col, &attributes); - - attributes.justification = justification; - - model = gtk_sheet_get_model (sheet); - - old_text = g_sheet_model_get_string (model, row, col); - - changed = FALSE; - - if (0 != safe_strcmp (old_text, text)) - changed = g_sheet_model_set_string (model, text, row, col); - - if ( g_sheet_model_free_strings (model)) - g_free (old_text); - - - if (changed && attributes.is_visible) - { - gchar *s = gtk_sheet_cell_get_text (sheet, row, col); - text_width = 0; - if (s && strlen (s) > 0) - { - text_width = STRING_WIDTH (GTK_WIDGET (sheet), - attributes.font_desc, text); - } - dispose_string (sheet, s); - - range.row0 = row; - range.rowi = row; - range.col0 = MIN_VISIBLE_COLUMN (sheet); - range.coli = MAX_VISIBLE_COLUMN (sheet); - - if (gtk_sheet_autoresize (sheet) && - text_width > xxx_column_width (sheet, col) - - 2 * CELLOFFSET- attributes.border.width) - { - gtk_sheet_set_column_width (sheet, col, text_width + 2 * CELLOFFSET - + attributes.border.width); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING); - } - else - if (!GTK_SHEET_IS_FROZEN (sheet)) - gtk_sheet_range_draw (sheet, &range); - } - - if ( changed ) - g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, col); - -} - - -void -gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column) -{ - GtkSheetRange range; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - if (column >= xxx_column_count (sheet) || - row >= yyy_row_count (sheet)) return; - - if (column < 0 || row < 0) return; - - range.row0 = row; - range.rowi = row; - range.col0 = MIN_VISIBLE_COLUMN (sheet); - range.coli = MAX_VISIBLE_COLUMN (sheet); - - gtk_sheet_real_cell_clear (sheet, row, column); - - if (!GTK_SHEET_IS_FROZEN (sheet)) - { - gtk_sheet_range_draw (sheet, &range); - } -} - -static void -gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column) -{ - GSheetModel *model = gtk_sheet_get_model (sheet); - - gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column); - - if (old_text && strlen (old_text) > 0 ) - { - g_sheet_model_datum_clear (model, row, column); - } - - dispose_string (sheet, old_text); -} - -void -gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - gtk_sheet_real_range_clear (sheet, range); -} - -static void -gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range) -{ - gint i, j; - GtkSheetRange clear; - - if (!range) - { - clear.row0 = 0; - clear.rowi = yyy_row_count (sheet) - 1; - clear.col0 = 0; - clear.coli = xxx_column_count (sheet) - 1; - } - else - clear=*range; - - clear.row0 = MAX (clear.row0, 0); - clear.col0 = MAX (clear.col0, 0); - clear.rowi = MIN (clear.rowi, yyy_row_count (sheet) - 1 ); - clear.coli = MIN (clear.coli, xxx_column_count (sheet) - 1 ); - - for (i = clear.row0; i <= clear.rowi; i++) - for (j = clear.col0; j <= clear.coli; j++) - { - gtk_sheet_real_cell_clear (sheet, i, j); - } - - gtk_sheet_range_draw (sheet, NULL); -} - - -static gboolean -gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col) -{ - gboolean empty; - char *text = gtk_sheet_cell_get_text (sheet, row, col); - empty = (text == NULL ); - - dispose_string (sheet, text); - - return empty; -} - - -gchar * -gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col) -{ - GSheetModel *model; - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - - if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) - return NULL; - if (col < 0 || row < 0) return NULL; - - model = gtk_sheet_get_model (sheet); - - if ( !model ) - return NULL; - - return g_sheet_model_get_string (model, row, col); -} - - -GtkStateType -gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col) -{ - gint state; - GtkSheetRange *range; - - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return 0; - if (col < 0 || row < 0) return 0; - - state = sheet->state; - range = &sheet->range; - - switch (state) - { - case GTK_SHEET_NORMAL: - return GTK_STATE_NORMAL; - break; - case GTK_SHEET_ROW_SELECTED: - if (row >= range->row0 && row <= range->rowi) - return GTK_STATE_SELECTED; - break; - case GTK_SHEET_COLUMN_SELECTED: - if (col >= range->col0 && col <= range->coli) - return GTK_STATE_SELECTED; - break; - case GTK_SHEET_RANGE_SELECTED: - if (row >= range->row0 && row <= range->rowi && \ - col >= range->col0 && col <= range->coli) - return GTK_STATE_SELECTED; - break; - } - return GTK_STATE_NORMAL; -} - -/* Convert X, Y (in pixels) to *ROW, *COLUMN (in cell coords) - -1 indicates the title buttons. - If the function returns FALSE, then the results will be unreliable. -*/ -gboolean -gtk_sheet_get_pixel_info (GtkSheet *sheet, - gint x, - gint y, - gint *row, - gint *column) -{ - gint trow, tcol; - *row = -G_MAXINT; - *column = -G_MAXINT; - - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - - /* bounds checking, return false if the user clicked - on a blank area */ - if (y < 0) - return FALSE; - - if (x < 0) - return FALSE; - - if ( y < sheet->column_title_area.height + sheet->column_title_area.y) - *row = -1; - - else - { - trow = ROW_FROM_YPIXEL (sheet, y); - if (trow > yyy_row_count (sheet)) - return FALSE; - - *row = trow; - } - - if ( x < sheet->row_title_area.width + sheet->row_title_area.x) - *column = -1; - else - { - tcol = COLUMN_FROM_XPIXEL (sheet, x); - if (tcol > xxx_column_count (sheet)) - return FALSE; - - *column = tcol; - } - - return TRUE; -} - -gboolean -gtk_sheet_get_cell_area (GtkSheet * sheet, - gint row, - gint column, - GdkRectangle *area) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - - if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet)) - return FALSE; - - area->x = (column == -1) ? 0 : (COLUMN_LEFT_XPIXEL (sheet, column) - - (sheet->row_titles_visible - ? sheet->row_title_area.width - : 0)); - area->y = (row == -1) ? 0 : (ROW_TOP_YPIXEL (sheet, row) - - (sheet->column_titles_visible - ? sheet->column_title_area.height - : 0)); - area->width= (column == -1) ? sheet->row_title_area.width - : xxx_column_width (sheet, column); - - area->height= (row == -1) ? sheet->column_title_area.height - : yyy_row_height (sheet, row); - - return TRUE; -} - -gboolean -gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column) -{ - g_return_val_if_fail (sheet != NULL, 0); - g_return_val_if_fail (GTK_IS_SHEET (sheet), 0); - - if (row < - 1 || column < - 1) return FALSE; - if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet)) - return FALSE; - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - gtk_sheet_deactivate_cell (sheet); - - sheet->active_cell.row = row; - sheet->active_cell.col = column; - - if ( row == -1 || column == -1) - { - gtk_sheet_hide_active_cell (sheet); - return TRUE; - } - - if (!gtk_sheet_activate_cell (sheet, row, column)) return FALSE; - - if (gtk_sheet_autoscroll (sheet)) - gtk_sheet_move_query (sheet, row, column); - - return TRUE; -} - -void -gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if ( row ) *row = sheet->active_cell.row; - if (column) *column = sheet->active_cell.col; -} - -static void -gtk_sheet_entry_changed (GtkWidget *widget, gpointer data) -{ - GtkSheet *sheet; - gint row, col; - const char *text; - GtkJustification justification; - GtkSheetCellAttr attributes; - - g_return_if_fail (data != NULL); - g_return_if_fail (GTK_IS_SHEET (data)); - - sheet = GTK_SHEET (data); - - if (!GTK_WIDGET_VISIBLE (widget)) return; - if (sheet->state != GTK_STATE_NORMAL) return; - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - if (row < 0 || col < 0) return; - - sheet->active_cell.row = -1; - sheet->active_cell.col = -1; - - text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet))); - - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN); - - if (text && strlen (text) > 0) - { - gtk_sheet_get_attributes (sheet, row, col, &attributes); - justification = attributes.justification; - gtk_sheet_set_cell (sheet, row, col, justification, text); - } - - if (sheet->freeze_count == 0) - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN); - - sheet->active_cell.row = row;; - sheet->active_cell.col = col; -} - - -static void -gtk_sheet_deactivate_cell (GtkSheet *sheet) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return ; - if (sheet->state != GTK_SHEET_NORMAL) return ; - - if ( sheet->active_cell.row == -1 || sheet->active_cell.col == -1 ) - return ; - - g_signal_emit (sheet, sheet_signals[DEACTIVATE], 0, - sheet->active_cell.row, - sheet->active_cell.col); - - - g_signal_handlers_disconnect_by_func (gtk_sheet_get_entry (sheet), - G_CALLBACK (gtk_sheet_entry_changed), - sheet); - - gtk_sheet_hide_active_cell (sheet); - sheet->active_cell.row = -1; - sheet->active_cell.col = -1; - - if (GTK_SHEET_REDRAW_PENDING (sheet)) - { - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING); - gtk_sheet_range_draw (sheet, NULL); - } -} - -static void -gtk_sheet_hide_active_cell (GtkSheet *sheet) -{ - const char *text; - gint row, col; - GtkJustification justification; - GtkSheetCellAttr attributes; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - if (row < 0 || col < 0) return; - - if (sheet->freeze_count == 0) - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN); - - text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet))); - - gtk_sheet_get_attributes (sheet, row, col, &attributes); - justification = attributes.justification; - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - gtk_widget_hide (sheet->entry_widget); - gtk_widget_unmap (sheet->entry_widget); - - if (row != -1 && col != -1) - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - COLUMN_LEFT_XPIXEL (sheet, col)- 1, - ROW_TOP_YPIXEL (sheet, row)- 1, - COLUMN_LEFT_XPIXEL (sheet, col)- 1, - ROW_TOP_YPIXEL (sheet, row)- 1, - xxx_column_width (sheet, col) + 4, - yyy_row_height (sheet, row)+4); - - gtk_widget_grab_focus (GTK_WIDGET (sheet)); - - GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); - -} - -static gboolean -gtk_sheet_activate_cell (GtkSheet *sheet, gint row, gint col) -{ - gboolean veto = TRUE; - - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - - if (row < 0 || col < 0) return FALSE; - - if ( row > yyy_row_count (sheet) || col > xxx_column_count (sheet)) - return FALSE; - - if (!veto) return FALSE; - if (sheet->state != GTK_SHEET_NORMAL) - { - sheet->state = GTK_SHEET_NORMAL; - gtk_sheet_real_unselect_range (sheet, NULL); - } - - sheet->range.row0 = row; - sheet->range.col0 = col; - sheet->range.rowi = row; - sheet->range.coli = col; - sheet->active_cell.row = row; - sheet->active_cell.col = col; - sheet->selection_cell.row = row; - sheet->selection_cell.col = col; - - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - - gtk_sheet_show_active_cell (sheet); - - g_signal_connect (gtk_sheet_get_entry (sheet), - "changed", - G_CALLBACK (gtk_sheet_entry_changed), - sheet); - - _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals [ACTIVATE], row, col, &veto); - - return TRUE; -} - -static void -gtk_sheet_show_active_cell (GtkSheet *sheet) -{ - GtkEntry *sheet_entry; - GtkSheetCellAttr attributes; - gchar *text = NULL; - const gchar *old_text; - GtkJustification justification; - gint row, col; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - /* Don't show the active cell, if there is no active cell: */ - if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */ - return; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - if (sheet->state != GTK_SHEET_NORMAL) return; - if (GTK_SHEET_IN_SELECTION (sheet)) return; - - GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE); - - sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet)); - - gtk_sheet_get_attributes (sheet, row, col, &attributes); - - justification = GTK_JUSTIFY_LEFT; - - if (gtk_sheet_justify_entry (sheet)) - justification = attributes.justification; - - text = gtk_sheet_cell_get_text (sheet, row, col); - if ( ! text ) - text = g_strdup (""); - - gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible); - - - /*** Added by John Gotts. Mar 25, 2005 *********/ - old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry)); - if (strcmp (old_text, text) != 0) - { - if (!GTK_IS_ITEM_ENTRY (sheet_entry)) - gtk_entry_set_text (GTK_ENTRY (sheet_entry), text); - else - gtk_item_entry_set_text (GTK_ITEM_ENTRY (sheet_entry), text, justification); - } - - gtk_sheet_entry_set_max_size (sheet); - gtk_sheet_size_allocate_entry (sheet); - - gtk_widget_map (sheet->entry_widget); - - gtk_widget_grab_focus (GTK_WIDGET (sheet_entry)); - - dispose_string (sheet, text); -} - -static void -gtk_sheet_draw_active_cell (GtkSheet *sheet) -{ - gint row, col; - GtkSheetRange range; - - if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return; - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - if (row < 0 || col < 0) return; - - if (!gtk_sheet_cell_isvisible (sheet, row, col)) return; - - range.col0 = range.coli = col; - range.row0 = range.rowi = row; - - gtk_sheet_draw_border (sheet, range); -} - - -static void -gtk_sheet_make_backing_pixmap (GtkSheet *sheet, guint width, guint height) -{ - gint pixmap_width, pixmap_height; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - if (width == 0 && height == 0) - { - width = sheet->sheet_window_width + 80; - height = sheet->sheet_window_height + 80; - } - - if (!sheet->pixmap) - { - /* allocate */ - sheet->pixmap = gdk_pixmap_new (sheet->sheet_window, - width, height, - - 1); - if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL); - } - else - { - /* reallocate if sizes don't match */ - gdk_drawable_get_size (sheet->pixmap, - &pixmap_width, &pixmap_height); - if ( (pixmap_width != width) || (pixmap_height != height)) - { - g_object_unref (sheet->pixmap); - sheet->pixmap = gdk_pixmap_new (sheet->sheet_window, - width, height, - - 1); - if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL); - } - } -} - -static void -gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range) -{ - gint i, j, mask1, mask2; - gint state, selected; - gint x, y, width, height; - GtkSheetRange new_range, aux_range; - - g_return_if_fail (sheet != NULL); - - if (range == NULL) range=&sheet->range; - - new_range=*range; - - range->row0 = MIN (range->row0, sheet->range.row0); - range->rowi = MAX (range->rowi, sheet->range.rowi); - range->col0 = MIN (range->col0, sheet->range.col0); - range->coli = MAX (range->coli, sheet->range.coli); - - range->row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet)); - range->rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet)); - range->col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet)); - range->coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet)); - - aux_range.row0 = MAX (new_range.row0, MIN_VISIBLE_ROW (sheet)); - aux_range.rowi = MIN (new_range.rowi, MAX_VISIBLE_ROW (sheet)); - aux_range.col0 = MAX (new_range.col0, MIN_VISIBLE_COLUMN (sheet)); - aux_range.coli = MIN (new_range.coli, MAX_VISIBLE_COLUMN (sheet)); - - for (i = range->row0; i <= range->rowi; i++) - { - for (j = range->col0; j <= range->coli; j++) - { - - state = gtk_sheet_cell_get_state (sheet, i, j); - selected= (i <= new_range.rowi && i >= new_range.row0 && - j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE; - - if (state == GTK_STATE_SELECTED && selected && - xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) && - (i == sheet->range.row0 || i == sheet->range.rowi || - j == sheet->range.col0 || j == sheet->range.coli || - i == new_range.row0 || i == new_range.rowi || - j == new_range.col0 || j == new_range.coli)) - { - - mask1 = i == sheet->range.row0 ? 1 : 0; - mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1; - mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1; - mask1 = j == sheet->range.coli ? mask1 + 8 : mask1; - - mask2 = i == new_range.row0 ? 1 : 0; - mask2 = i == new_range.rowi ? mask2 + 2 : mask2; - mask2 = j == new_range.col0 ? mask2 + 4 : mask2; - mask2 = j == new_range.coli ? mask2 + 8 : mask2; - - if (mask1 != mask2) - { - x = COLUMN_LEFT_XPIXEL (sheet, j); - y = ROW_TOP_YPIXEL (sheet, i); - width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ - xxx_column_width (sheet, j); - height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i); - - if (i == sheet->range.row0) - { - y = y - 3; - height = height + 3; - } - if (i == sheet->range.rowi) height = height + 3; - if (j == sheet->range.col0) - { - x = x - 3; - width = width + 3; - } - if (j == sheet->range.coli) width = width + 3; - - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - x + 1, - y + 1, - x + 1, - y + 1, - width, - height); - - if (i != sheet->active_cell.row || j != sheet->active_cell.col) - { - x = COLUMN_LEFT_XPIXEL (sheet, j); - y = ROW_TOP_YPIXEL (sheet, i); - width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ - xxx_column_width (sheet, j); - - height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i); - - if (i == new_range.row0) - { - y = y+2; - height = height - 2; - } - if (i == new_range.rowi) height = height - 3; - if (j == new_range.col0) - { - x = x+2; - width = width - 2; - } - if (j == new_range.coli) width = width - 3; - - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x + 1, y + 1, - width, height); - } - } - } - } - } - - for (i = range->row0; i <= range->rowi; i++) - { - for (j = range->col0; j <= range->coli; j++) - { - - state = gtk_sheet_cell_get_state (sheet, i, j); - selected= (i <= new_range.rowi && i >= new_range.row0 && - j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE; - - if (state == GTK_STATE_SELECTED && !selected && - xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i)) - { - - x = COLUMN_LEFT_XPIXEL (sheet, j); - y = ROW_TOP_YPIXEL (sheet, i); - width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j); - height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i); - - if (i == sheet->range.row0) - { - y = y - 3; - height = height + 3; - } - if (i == sheet->range.rowi) height = height + 3; - if (j == sheet->range.col0) - { - x = x - 3; - width = width + 3; - } - if (j == sheet->range.coli) width = width + 3; - - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - x + 1, - y + 1, - x + 1, - y + 1, - width, - height); - } - } - } - - for (i = range->row0; i <= range->rowi; i++) - { - for (j = range->col0; j <= range->coli; j++) - { - - state = gtk_sheet_cell_get_state (sheet, i, j); - selected= (i <= new_range.rowi && i >= new_range.row0 && - j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE; - - if (state != GTK_STATE_SELECTED && selected && - xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) && - (i != sheet->active_cell.row || j != sheet->active_cell.col)) - { - - x = COLUMN_LEFT_XPIXEL (sheet, j); - y = ROW_TOP_YPIXEL (sheet, i); - width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j); - height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i); - - if (i == new_range.row0) - { - y = y+2; - height = height - 2; - } - if (i == new_range.rowi) height = height - 3; - if (j == new_range.col0) - { - x = x+2; - width = width - 2; - } - if (j == new_range.coli) width = width - 3; - - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x + 1, y + 1, - width, height); - - } - - } - } - - for (i = aux_range.row0; i <= aux_range.rowi; i++) - { - for (j = aux_range.col0; j <= aux_range.coli; j++) - { - - if (xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i)) - { - - state = gtk_sheet_cell_get_state (sheet, i, j); - - mask1 = i == sheet->range.row0 ? 1 : 0; - mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1; - mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1; - mask1 = j == sheet->range.coli ? mask1 + 8 : mask1; - - mask2 = i == new_range.row0 ? 1 : 0; - mask2 = i == new_range.rowi ? mask2 + 2 : mask2; - mask2 = j == new_range.col0 ? mask2 + 4 : mask2; - mask2 = j == new_range.coli ? mask2 + 8 : mask2; - if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED)) - { - x = COLUMN_LEFT_XPIXEL (sheet, j); - y = ROW_TOP_YPIXEL (sheet, i); - width = xxx_column_width (sheet, j); - height = yyy_row_height (sheet, i); - if (mask2 & 1) - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x + 1, y - 1, - width, 3); - - - if (mask2 & 2) - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x + 1, y + height - 1, - width, 3); - - if (mask2 & 4) - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x - 1, y + 1, - 3, height); - - - if (mask2 & 8) - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x + width - 1, y + 1, - 3, height); - - - - } - - } - - } - } - - - *range = new_range; - gtk_sheet_draw_corners (sheet, new_range); - -} - -static void -gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range) -{ - GtkWidget *widget; - GdkRectangle area; - gint i; - gint x, y, width, height; - - widget = GTK_WIDGET (sheet); - - x = COLUMN_LEFT_XPIXEL (sheet, new_range.col0); - y = ROW_TOP_YPIXEL (sheet, new_range.row0); - width = COLUMN_LEFT_XPIXEL (sheet, new_range.coli) - x + - xxx_column_width (sheet, new_range.coli); - - height = ROW_TOP_YPIXEL (sheet, new_range.rowi) - y + - yyy_row_height (sheet, new_range.rowi); - - area.x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet)); - area.y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet)); - area.width = sheet->sheet_window_width; - area.height = sheet->sheet_window_height; - - if (x < 0) - { - width = width + x; - x = 0; - } - if (width > area.width) width = area.width + 10; - if (y < 0) - { - height = height + y; - y = 0; - } - if (height > area.height) height = area.height + 10; - - gdk_gc_set_clip_rectangle (sheet->xor_gc, &area); - - for (i = -1; i <= 1; ++i) - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - FALSE, - x + i, - y + i, - width - 2 * i, - height - 2 * i); - - gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL); - - - gtk_sheet_draw_corners (sheet, new_range); -} - -static void -gtk_sheet_draw_corners (GtkSheet *sheet, GtkSheetRange range) -{ - gint x, y; - guint width = 1; - - if (gtk_sheet_cell_isvisible (sheet, range.row0, range.col0)) - { - x = COLUMN_LEFT_XPIXEL (sheet, range.col0); - y = ROW_TOP_YPIXEL (sheet, range.row0); - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - x - 1, - y - 1, - x - 1, - y - 1, - 3, - 3); - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x - 1, y - 1, - 3, 3); - } - - if (gtk_sheet_cell_isvisible (sheet, range.row0, range.coli) || - sheet->state == GTK_SHEET_COLUMN_SELECTED) - { - x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+ - xxx_column_width (sheet, range.coli); - y = ROW_TOP_YPIXEL (sheet, range.row0); - width = 1; - if (sheet->state == GTK_SHEET_COLUMN_SELECTED) - { - y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet))+3; - width = 3; - } - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - x - width, - y - width, - x - width, - y - width, - 2 * width + 1, - 2 * width + 1); - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x - width + width / 2, y - width + width / 2, - 2 + width, 2 + width); - } - - if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.col0) || - sheet->state == GTK_SHEET_ROW_SELECTED) - { - x = COLUMN_LEFT_XPIXEL (sheet, range.col0); - y = ROW_TOP_YPIXEL (sheet, range.rowi)+ - yyy_row_height (sheet, range.rowi); - width = 1; - if (sheet->state == GTK_SHEET_ROW_SELECTED) - { - x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet))+3; - width = 3; - } - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - x - width, - y - width, - x - width, - y - width, - 2 * width + 1, - 2 * width + 1); - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x - width + width / 2, y - width + width / 2, - 2 + width, 2 + width); - } - - if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.coli)) - { - x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+ - xxx_column_width (sheet, range.coli); - y = ROW_TOP_YPIXEL (sheet, range.rowi)+ - yyy_row_height (sheet, range.rowi); - width = 1; - if (sheet->state == GTK_SHEET_RANGE_SELECTED) width = 3; - if (sheet->state == GTK_SHEET_NORMAL) width = 3; - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - x - width, - y - width, - x - width, - y - width, - 2 * width + 1, - 2 * width + 1); - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - TRUE, - x - width + width / 2, y - width + width / 2, - 2 + width, 2 + width); - - } - -} - - -static void -gtk_sheet_real_select_range (GtkSheet * sheet, - const GtkSheetRange * range) -{ - gint state; - - g_return_if_fail (sheet != NULL); - - if (range == NULL) range = &sheet->range; - - memcpy (&sheet->range, range, sizeof (*range)); - - if (range->row0 < 0 || range->rowi < 0) return; - if (range->col0 < 0 || range->coli < 0) return; - - state = sheet->state; - - if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 || - range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0) - { - gtk_sheet_new_selection (sheet, &sheet->range); - } - else - { - gtk_sheet_draw_backing_pixmap (sheet, sheet->range); - gtk_sheet_range_draw_selection (sheet, sheet->range); - } - - gtk_sheet_update_primary_selection (sheet); - - g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range); -} - - -void -gtk_sheet_get_selected_range (GtkSheet *sheet, - GtkSheetRange *range) -{ - g_return_if_fail (sheet != NULL); - *range = sheet->range; -} - - -void -gtk_sheet_select_range (GtkSheet * sheet, const GtkSheetRange *range) -{ - g_return_if_fail (sheet != NULL); - - if (range == NULL) range=&sheet->range; - - if (range->row0 < 0 || range->rowi < 0) return; - if (range->col0 < 0 || range->coli < 0) return; - - - if (sheet->state != GTK_SHEET_NORMAL) - gtk_sheet_real_unselect_range (sheet, NULL); - else - gtk_sheet_deactivate_cell (sheet); - - sheet->range.row0 = range->row0; - sheet->range.rowi = range->rowi; - sheet->range.col0 = range->col0; - sheet->range.coli = range->coli; - sheet->active_cell.row = range->row0; - sheet->active_cell.col = range->col0; - sheet->selection_cell.row = range->rowi; - sheet->selection_cell.col = range->coli; - - sheet->state = GTK_SHEET_RANGE_SELECTED; - gtk_sheet_real_select_range (sheet, NULL); - -} - -void -gtk_sheet_unselect_range (GtkSheet * sheet) -{ - if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - gtk_sheet_real_unselect_range (sheet, NULL); - sheet->state = GTK_STATE_NORMAL; - - gtk_sheet_activate_cell (sheet, - sheet->active_cell.row, sheet->active_cell.col); -} - - -static void -gtk_sheet_real_unselect_range (GtkSheet * sheet, - const GtkSheetRange *range) -{ - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))); - - if ( range == NULL) - range = &sheet->range; - - if (range->row0 < 0 || range->rowi < 0) return; - if (range->col0 < 0 || range->coli < 0) return; - - g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1); - g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1); - - if (gtk_sheet_range_isvisible (sheet, *range)) - gtk_sheet_draw_backing_pixmap (sheet, *range); - - sheet->range.row0 = -1; - sheet->range.rowi = -1; - sheet->range.col0 = -1; - sheet->range.coli = -1; - - gtk_sheet_position_children (sheet); -} - - -static gint -gtk_sheet_expose (GtkWidget * widget, - GdkEventExpose * event) -{ - GtkSheet *sheet; - GtkSheetRange range; - - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - - - sheet = GTK_SHEET (widget); - - if (GTK_WIDGET_DRAWABLE (widget)) - { - range.row0 = ROW_FROM_YPIXEL (sheet, event->area.y); - range.col0 = COLUMN_FROM_XPIXEL (sheet, event->area.x); - range.rowi = ROW_FROM_YPIXEL (sheet, event->area.y + event->area.height); - range.coli = COLUMN_FROM_XPIXEL (sheet, event->area.x + event->area.width); - - /* exposure events on the sheet */ - if (event->window == sheet->row_title_window && - sheet->row_titles_visible) - { - gint i; - for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++) - gtk_sheet_row_title_button_draw (sheet, i); - } - - if (event->window == sheet->column_title_window && - sheet->column_titles_visible) - { - gint i; - for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++) - gtk_sheet_column_title_button_draw (sheet, i); - } - - if (event->window == sheet->sheet_window) - { - gtk_sheet_draw_backing_pixmap (sheet, range); - - if (sheet->state != GTK_SHEET_NORMAL) - { - if (gtk_sheet_range_isvisible (sheet, sheet->range)) - gtk_sheet_draw_backing_pixmap (sheet, sheet->range); - if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet)) - gtk_sheet_draw_backing_pixmap (sheet, sheet->drag_range); - - if (gtk_sheet_range_isvisible (sheet, sheet->range)) - gtk_sheet_range_draw_selection (sheet, sheet->range); - if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet)) - draw_xor_rectangle (sheet, sheet->drag_range); - } - - if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet))) - { - if (sheet->state == GTK_SHEET_NORMAL) - gtk_sheet_draw_active_cell (sheet); - } - } - } - - if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet)) - gtk_widget_grab_focus (GTK_WIDGET (sheet)); - - (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); - - return FALSE; -} - - -static gboolean -gtk_sheet_button_press (GtkWidget * widget, - GdkEventButton * event) -{ - GtkSheet *sheet; - GdkModifierType mods; - gint x, y, row, column; - gboolean veto; - - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - - sheet = GTK_SHEET (widget); - - /* Cancel any pending tooltips */ - if (sheet->motion_timer) - { - g_source_remove (sheet->motion_timer); - sheet->motion_timer = 0; - } - - gtk_widget_get_pointer (widget, &x, &y); - gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); - - - if (event->window == sheet->column_title_window) - { - g_signal_emit (sheet, - sheet_signals[BUTTON_EVENT_COLUMN], 0, - column, event); - - if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) - g_signal_emit (sheet, - sheet_signals[DOUBLE_CLICK_COLUMN], 0, column); - - } - else if (event->window == sheet->row_title_window) - { - g_signal_emit (sheet, - sheet_signals[BUTTON_EVENT_ROW], 0, - row, event); - - if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) - g_signal_emit (sheet, - sheet_signals[DOUBLE_CLICK_ROW], 0, row); - } - - - gdk_window_get_pointer (widget->window, NULL, NULL, &mods); - - if (! (mods & GDK_BUTTON1_MASK)) return TRUE; - - - /* press on resize windows */ - if (event->window == sheet->column_title_window && - gtk_sheet_columns_resizable (sheet)) - { - gtk_widget_get_pointer (widget, &sheet->x_drag, NULL); - if (POSSIBLE_XDRAG (sheet, sheet->x_drag, &sheet->drag_cell.col)) - { - guint req; - if (event->type == GDK_2BUTTON_PRESS) - { - gtk_sheet_autoresize_column (sheet, sheet->drag_cell.col); - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG); - return TRUE; - } - gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG); - gdk_pointer_grab (sheet->column_title_window, FALSE, - GDK_POINTER_MOTION_HINT_MASK | - GDK_BUTTON1_MOTION_MASK | - GDK_BUTTON_RELEASE_MASK, - NULL, NULL, event->time); - - draw_xor_vline (sheet); - return TRUE; - } - } - - if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet)) - { - gtk_widget_get_pointer (widget, NULL, &sheet->y_drag); - - if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row)) - { - guint req; - gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG); - gdk_pointer_grab (sheet->row_title_window, FALSE, - GDK_POINTER_MOTION_HINT_MASK | - GDK_BUTTON1_MOTION_MASK | - GDK_BUTTON_RELEASE_MASK, - NULL, NULL, event->time); - - draw_xor_hline (sheet); - return TRUE; - } - } - - /* the sheet itself does not handle other than single click events */ - if (event->type != GDK_BUTTON_PRESS) return FALSE; - - /* selections on the sheet */ - if (event->window == sheet->sheet_window) - { - gtk_widget_get_pointer (widget, &x, &y); - gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); - gdk_pointer_grab (sheet->sheet_window, FALSE, - GDK_POINTER_MOTION_HINT_MASK | - GDK_BUTTON1_MOTION_MASK | - GDK_BUTTON_RELEASE_MASK, - NULL, NULL, event->time); - gtk_grab_add (GTK_WIDGET (sheet)); - - /* This seems to be a kludge to work around a problem where the sheet - scrolls to another position. The timeout scrolls it back to its - original posn. JMD 3 July 2007 - */ - gtk_widget_grab_focus (GTK_WIDGET (sheet)); - - if (sheet->selection_mode != GTK_SELECTION_SINGLE && - sheet->selection_mode != GTK_SELECTION_NONE && - sheet->cursor_drag->type == GDK_SIZING && - !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet)) - { - if (sheet->state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - column = sheet->active_cell.col; - gtk_sheet_deactivate_cell (sheet); - sheet->active_cell.row = row; - sheet->active_cell.col = column; - sheet->drag_range = sheet->range; - sheet->state = GTK_SHEET_RANGE_SELECTED; - gtk_sheet_select_range (sheet, &sheet->drag_range); - } - sheet->x_drag = x; - sheet->y_drag = y; - if (row > sheet->range.rowi) row--; - if (column > sheet->range.coli) column--; - sheet->drag_cell.row = row; - sheet->drag_cell.col = column; - sheet->drag_range = sheet->range; - draw_xor_rectangle (sheet, sheet->drag_range); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE); - } - else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW && - !GTK_SHEET_IN_SELECTION (sheet) - && ! GTK_SHEET_IN_DRAG (sheet) - && sheet->active_cell.row >= 0 - && sheet->active_cell.col >= 0 - ) - { - if (sheet->state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - column = sheet->active_cell.col; - gtk_sheet_deactivate_cell (sheet); - sheet->active_cell.row = row; - sheet->active_cell.col = column; - sheet->drag_range = sheet->range; - sheet->state = GTK_SHEET_RANGE_SELECTED; - gtk_sheet_select_range (sheet, &sheet->drag_range); - } - sheet->x_drag = x; - sheet->y_drag = y; - if (row < sheet->range.row0) row++; - if (row > sheet->range.rowi) row--; - if (column < sheet->range.col0) column++; - if (column > sheet->range.coli) column--; - sheet->drag_cell.row = row; - sheet->drag_cell.col = column; - sheet->drag_range = sheet->range; - draw_xor_rectangle (sheet, sheet->drag_range); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG); - } - else - { - gtk_sheet_click_cell (sheet, row, column, &veto); - if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - } - } - - if (event->window == sheet->column_title_window) - { - gtk_widget_get_pointer (widget, &x, &y); - column = COLUMN_FROM_XPIXEL (sheet, x); - - if (xxx_column_is_sensitive (sheet, column)) - { - gtk_sheet_click_cell (sheet, - 1, column, &veto); - gtk_grab_add (GTK_WIDGET (sheet)); - gtk_widget_grab_focus (GTK_WIDGET (sheet)); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - } - } - - if (event->window == sheet->row_title_window) - { - gtk_widget_get_pointer (widget, &x, &y); - row = ROW_FROM_YPIXEL (sheet, y); - if (yyy_row_is_sensitive (sheet, row)) - { - gtk_sheet_click_cell (sheet, row, - 1, &veto); - gtk_grab_add (GTK_WIDGET (sheet)); - gtk_widget_grab_focus (GTK_WIDGET (sheet)); - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - } - } - - return TRUE; -} - -static void -gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column, gboolean *veto) -{ - *veto = TRUE; - - if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet)) - { - *veto = FALSE; - return; - } - - if (column >= 0 && row >= 0) - if (! xxx_column_is_visible (sheet, column) || !yyy_row_is_visible (sheet, row)) - { - *veto = FALSE; - return; - } - - _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals[TRAVERSE], - sheet->active_cell.row, sheet->active_cell.col, - &row, &column, veto); - - if (!*veto) - { - if (sheet->state == GTK_STATE_NORMAL) return; - - row = sheet->active_cell.row; - column = sheet->active_cell.col; - - gtk_sheet_activate_cell (sheet, row, column); - return; - } - - if (row == -1 && column >= 0) - { - if (gtk_sheet_autoscroll (sheet)) - gtk_sheet_move_query (sheet, row, column); - gtk_sheet_select_column (sheet, column); - return; - } - if (column == -1 && row >= 0) - { - if (gtk_sheet_autoscroll (sheet)) - gtk_sheet_move_query (sheet, row, column); - gtk_sheet_select_row (sheet, row); - return; - } - - if (row == - 1 && column == - 1) - { - sheet->range.row0 = 0; - sheet->range.col0 = 0; - sheet->range.rowi = yyy_row_count (sheet) - 1; - sheet->range.coli = xxx_column_count (sheet) - 1; - sheet->active_cell.row = 0; - sheet->active_cell.col = 0; - gtk_sheet_select_range (sheet, NULL); - return; - } - - if (row != -1 && column != -1) - { - if (sheet->state != GTK_SHEET_NORMAL) - { - sheet->state = GTK_SHEET_NORMAL; - gtk_sheet_real_unselect_range (sheet, NULL); - } - else - { - gtk_sheet_deactivate_cell (sheet); - gtk_sheet_activate_cell (sheet, row, column); - } - - if (gtk_sheet_autoscroll (sheet)) - gtk_sheet_move_query (sheet, row, column); - sheet->active_cell.row = row; - sheet->active_cell.col = column; - sheet->selection_cell.row = row; - sheet->selection_cell.col = column; - sheet->range.row0 = row; - sheet->range.col0 = column; - sheet->range.rowi = row; - sheet->range.coli = column; - sheet->state = GTK_SHEET_NORMAL; - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - gtk_sheet_draw_active_cell (sheet); - return; - } - - g_assert_not_reached (); - gtk_sheet_activate_cell (sheet, sheet->active_cell.row, - sheet->active_cell.col); -} - -static gint -gtk_sheet_button_release (GtkWidget * widget, - GdkEventButton * event) -{ - GtkSheet *sheet; - gint x, y; - - sheet = GTK_SHEET (widget); - - /* release on resize windows */ - if (GTK_SHEET_IN_XDRAG (sheet)) - { - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG); - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - gtk_widget_get_pointer (widget, &x, NULL); - gdk_pointer_ungrab (event->time); - draw_xor_vline (sheet); - - gtk_sheet_set_column_width (sheet, sheet->drag_cell.col, - new_column_width (sheet, sheet->drag_cell.col, &x)); - sheet->old_hadjustment = -1.; - g_signal_emit_by_name (sheet->hadjustment, "value_changed"); - return TRUE; - } - - if (GTK_SHEET_IN_YDRAG (sheet)) - { - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG); - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - gtk_widget_get_pointer (widget, NULL, &y); - gdk_pointer_ungrab (event->time); - draw_xor_hline (sheet); - - gtk_sheet_set_row_height (sheet, sheet->drag_cell.row, new_row_height (sheet, sheet->drag_cell.row, &y)); - sheet->old_vadjustment = -1.; - g_signal_emit_by_name (sheet->vadjustment, "value_changed"); - return TRUE; - } - - - if (GTK_SHEET_IN_DRAG (sheet)) - { - GtkSheetRange old_range; - draw_xor_rectangle (sheet, sheet->drag_range); - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG); - gdk_pointer_ungrab (event->time); - - gtk_sheet_real_unselect_range (sheet, NULL); - - sheet->active_cell.row = sheet->active_cell.row + - (sheet->drag_range.row0 - sheet->range.row0); - sheet->active_cell.col = sheet->active_cell.col + - (sheet->drag_range.col0 - sheet->range.col0); - sheet->selection_cell.row = sheet->selection_cell.row + - (sheet->drag_range.row0 - sheet->range.row0); - sheet->selection_cell.col = sheet->selection_cell.col + - (sheet->drag_range.col0 - sheet->range.col0); - old_range = sheet->range; - sheet->range = sheet->drag_range; - sheet->drag_range = old_range; - g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0, - &sheet->drag_range, &sheet->range); - gtk_sheet_select_range (sheet, &sheet->range); - } - - if (GTK_SHEET_IN_RESIZE (sheet)) - { - GtkSheetRange old_range; - draw_xor_rectangle (sheet, sheet->drag_range); - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE); - gdk_pointer_ungrab (event->time); - - gtk_sheet_real_unselect_range (sheet, NULL); - - sheet->active_cell.row = sheet->active_cell.row + - (sheet->drag_range.row0 - sheet->range.row0); - sheet->active_cell.col = sheet->active_cell.col + - (sheet->drag_range.col0 - sheet->range.col0); - if (sheet->drag_range.row0 < sheet->range.row0) - sheet->selection_cell.row = sheet->drag_range.row0; - if (sheet->drag_range.rowi >= sheet->range.rowi) - sheet->selection_cell.row = sheet->drag_range.rowi; - if (sheet->drag_range.col0 < sheet->range.col0) - sheet->selection_cell.col = sheet->drag_range.col0; - if (sheet->drag_range.coli >= sheet->range.coli) - sheet->selection_cell.col = sheet->drag_range.coli; - old_range = sheet->range; - sheet->range = sheet->drag_range; - sheet->drag_range = old_range; - - if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED; - g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0, - &sheet->drag_range, &sheet->range); - gtk_sheet_select_range (sheet, &sheet->range); - } - - if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet)) - { - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - gdk_pointer_ungrab (event->time); - gtk_sheet_activate_cell (sheet, sheet->active_cell.row, - sheet->active_cell.col); - } - - if (GTK_SHEET_IN_SELECTION) - gdk_pointer_ungrab (event->time); - gtk_grab_remove (GTK_WIDGET (sheet)); - - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - - return TRUE; -} - -/* Shamelessly lifted from gtktooltips */ -static gboolean -gtk_sheet_subtitle_paint_window (GtkWidget *tip_window) -{ - GtkRequisition req; - - gtk_widget_size_request (tip_window, &req); - gtk_paint_flat_box (tip_window->style, tip_window->window, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, - NULL, GTK_WIDGET(tip_window), "tooltip", - 0, 0, req.width, req.height); - - return FALSE; -} - -static GtkSheetHoverTitle * -create_hover_window (void) -{ - GtkSheetHoverTitle *hw = malloc (sizeof (*hw)); - - hw->window = gtk_window_new (GTK_WINDOW_POPUP); - -#if GTK_CHECK_VERSION (2, 9, 0) - gtk_window_set_type_hint (GTK_WINDOW (hw->window), - GDK_WINDOW_TYPE_HINT_TOOLTIP); -#endif - - gtk_widget_set_app_paintable (hw->window, TRUE); - gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE); - gtk_widget_set_name (hw->window, "gtk-tooltips"); - gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4); - - g_signal_connect (hw->window, - "expose_event", - G_CALLBACK (gtk_sheet_subtitle_paint_window), - NULL); - - hw->label = gtk_label_new (NULL); - - - gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE); - gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5); - - gtk_container_add (GTK_CONTAINER (hw->window), hw->label); - - gtk_widget_show (hw->label); - - g_signal_connect (hw->window, - "destroy", - G_CALLBACK (gtk_widget_destroyed), - &hw->window); - - return hw; -} - -#define HOVER_WINDOW_Y_OFFSET 2 - -static void -show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle) -{ - gint x, y; - gint px, py; - gint width; - - if ( ! subtitle ) - return; - - if ( ! sheet->hover_window) - { - sheet->hover_window = create_hover_window (); - gtk_widget_add_events (GTK_WIDGET (sheet), GDK_LEAVE_NOTIFY_MASK); - - g_signal_connect_swapped (sheet, "leave-notify-event", - G_CALLBACK (gtk_widget_hide), - sheet->hover_window->window); - } - - gtk_label_set_text (GTK_LABEL (sheet->hover_window->label), - subtitle); - - - sheet->hover_window->row = row; - sheet->hover_window->column = column; - - gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y); - - gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py); - - gtk_widget_show (sheet->hover_window->window); - - width = GTK_WIDGET (sheet->hover_window->label)->allocation.width; - - if (row == -1 ) - { - x += px; - x -= width / 2; - y += sheet->column_title_area.y; - y += sheet->column_title_area.height; - y += HOVER_WINDOW_Y_OFFSET; - } - - if ( column == -1 ) - { - y += py; - x += sheet->row_title_area.x; - x += sheet->row_title_area.width * 2 / 3.0; - } - - gtk_window_move (GTK_WINDOW (sheet->hover_window->window), - x, y); -} - -static gboolean -motion_timeout_callback (gpointer data) -{ - GtkSheet *sheet = GTK_SHEET (data); - gint x, y; - gint row, column; - gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y); - - if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) ) - { - if ( column == -1 && row == -1 ) - return FALSE; - - if ( column == -1) - { - GSheetRow *row_geo = sheet->row_geometry; - gchar *text; - - text = g_sheet_row_get_subtitle (row_geo, row); - - show_subtitle (sheet, row, column, text); - g_free (text); - } - - if ( row == -1) - { - GSheetColumn *col_geo = sheet->column_geometry; - gchar *text; - - text = g_sheet_column_get_subtitle (col_geo, column); - - show_subtitle (sheet, row, column, text ); - - g_free (text); - } - } - - return FALSE; -} - -static gint -gtk_sheet_motion (GtkWidget * widget, - GdkEventMotion * event) -{ - GtkSheet *sheet; - GdkModifierType mods; - GdkCursorType new_cursor; - gint x, y; - gint row, column; - - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - - sheet = GTK_SHEET (widget); - - /* selections on the sheet */ - x = event->x; - y = event->y; - - if (!sheet->hover_window || ! GTK_WIDGET_VISIBLE (sheet->hover_window->window)) - { - if ( sheet->motion_timer > 0 ) - g_source_remove (sheet->motion_timer); - sheet->motion_timer = g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet); - } - else - { - gint row, column; - gint wx, wy; - gtk_widget_get_pointer (widget, &wx, &wy); - - if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) ) - { - if ( row != sheet->hover_window->row || column != sheet->hover_window->column) - { - gtk_widget_hide (sheet->hover_window->window); - } - } - } - - if (event->window == sheet->column_title_window && - gtk_sheet_columns_resizable (sheet)) - { - gtk_widget_get_pointer (widget, &x, &y); - if (!GTK_SHEET_IN_SELECTION (sheet) && - POSSIBLE_XDRAG (sheet, x, &column)) - { - new_cursor = GDK_SB_H_DOUBLE_ARROW; - if (new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); - gdk_window_set_cursor (sheet->column_title_window, - sheet->cursor_drag); - } - } - else - { - new_cursor = GDK_TOP_LEFT_ARROW; - if (!GTK_SHEET_IN_XDRAG (sheet) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW); - gdk_window_set_cursor (sheet->column_title_window, - sheet->cursor_drag); - } - } - } - - if (event->window == sheet->row_title_window && - gtk_sheet_rows_resizable (sheet)) - { - gtk_widget_get_pointer (widget, &x, &y); - if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column)) - { - new_cursor = GDK_SB_V_DOUBLE_ARROW; - if (new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW); - gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag); - } - } - else - { - new_cursor = GDK_TOP_LEFT_ARROW; - if (!GTK_SHEET_IN_YDRAG (sheet) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW); - gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag); - } - } - } - - new_cursor = GDK_PLUS; - if ( event->window == sheet->sheet_window && - !POSSIBLE_DRAG (sheet, x, y, &row, &column) && - !GTK_SHEET_IN_DRAG (sheet) && - !POSSIBLE_RESIZE (sheet, x, y, &row, &column) && - !GTK_SHEET_IN_RESIZE (sheet) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_PLUS); - gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); - } - - new_cursor = GDK_TOP_LEFT_ARROW; - if ( event->window == sheet->sheet_window && - ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || GTK_SHEET_IN_RESIZE (sheet)) && (POSSIBLE_DRAG (sheet, x, y, &row, &column) || GTK_SHEET_IN_DRAG (sheet)) && - - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW); - gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); - } - - new_cursor = GDK_SIZING; - if ( event->window == sheet->sheet_window && - sheet->selection_mode != GTK_SELECTION_NONE && - !GTK_SHEET_IN_DRAG (sheet) && - (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || - GTK_SHEET_IN_RESIZE (sheet)) && - new_cursor != sheet->cursor_drag->type) - { - gdk_cursor_unref (sheet->cursor_drag); - sheet->cursor_drag = gdk_cursor_new (GDK_SIZING); - gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag); - } - - - gdk_window_get_pointer (widget->window, &x, &y, &mods); - if (! (mods & GDK_BUTTON1_MASK)) return FALSE; - - if (GTK_SHEET_IN_XDRAG (sheet)) - { - if (event->is_hint || event->window != widget->window) - gtk_widget_get_pointer (widget, &x, NULL); - else - x = event->x; - - new_column_width (sheet, sheet->drag_cell.col, &x); - if (x != sheet->x_drag) - { - draw_xor_vline (sheet); - sheet->x_drag = x; - draw_xor_vline (sheet); - } - return TRUE; - } - - if (GTK_SHEET_IN_YDRAG (sheet)) - { - if (event->is_hint || event->window != widget->window) - gtk_widget_get_pointer (widget, NULL, &y); - else - y = event->y; - - new_row_height (sheet, sheet->drag_cell.row, &y); - if (y != sheet->y_drag) - { - draw_xor_hline (sheet); - sheet->y_drag = y; - draw_xor_hline (sheet); - } - return TRUE; - } - - if (GTK_SHEET_IN_DRAG (sheet)) - { - GtkSheetRange aux; - column = COLUMN_FROM_XPIXEL (sheet, x)- sheet->drag_cell.col; - row = ROW_FROM_YPIXEL (sheet, y)- sheet->drag_cell.row; - if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0; - if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0; - sheet->x_drag = x; - sheet->y_drag = y; - aux = sheet->range; - if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) && - aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet)) - { - aux = sheet->drag_range; - sheet->drag_range.row0 = sheet->range.row0 + row; - sheet->drag_range.col0 = sheet->range.col0 + column; - sheet->drag_range.rowi = sheet->range.rowi + row; - sheet->drag_range.coli = sheet->range.coli + column; - if (aux.row0 != sheet->drag_range.row0 || - aux.col0 != sheet->drag_range.col0) - { - draw_xor_rectangle (sheet, aux); - draw_xor_rectangle (sheet, sheet->drag_range); - } - } - return TRUE; - } - - if (GTK_SHEET_IN_RESIZE (sheet)) - { - GtkSheetRange aux; - gint v_h, current_col, current_row, col_threshold, row_threshold; - v_h = 1; - - if (abs (x - COLUMN_LEFT_XPIXEL (sheet, sheet->drag_cell.col)) > - abs (y - ROW_TOP_YPIXEL (sheet, sheet->drag_cell.row))) v_h = 2; - - current_col = COLUMN_FROM_XPIXEL (sheet, x); - current_row = ROW_FROM_YPIXEL (sheet, y); - column = current_col - sheet->drag_cell.col; - row = current_row - sheet->drag_cell.row; - - /*use half of column width resp. row height as threshold to - expand selection*/ - col_threshold = COLUMN_LEFT_XPIXEL (sheet, current_col) + - xxx_column_width (sheet, current_col) / 2; - if (column > 0) - { - if (x < col_threshold) - column -= 1; - } - else if (column < 0) - { - if (x > col_threshold) - column +=1; - } - row_threshold = ROW_TOP_YPIXEL (sheet, current_row) + - yyy_row_height (sheet, current_row)/2; - if (row > 0) - { - if (y < row_threshold) - row -= 1; - } - else if (row < 0) - { - if (y > row_threshold) - row +=1; - } - - if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0; - if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0; - sheet->x_drag = x; - sheet->y_drag = y; - aux = sheet->range; - - if (v_h == 1) - column = 0; - else - row = 0; - - if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) && - aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet)) - { - aux = sheet->drag_range; - sheet->drag_range = sheet->range; - - if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row; - if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row; - if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column; - if (column > 0) sheet->drag_range.coli = sheet->range.coli + column; - - if (aux.row0 != sheet->drag_range.row0 || - aux.rowi != sheet->drag_range.rowi || - aux.col0 != sheet->drag_range.col0 || - aux.coli != sheet->drag_range.coli) - { - draw_xor_rectangle (sheet, aux); - draw_xor_rectangle (sheet, sheet->drag_range); - } - } - return TRUE; - } - - gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); - - if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row && - column == sheet->active_cell.col) return TRUE; - - if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK) - gtk_sheet_extend_selection (sheet, row, column); - - return TRUE; -} - -static gboolean -gtk_sheet_move_query (GtkSheet *sheet, gint row, gint column) -{ - gint row_move, column_move; - gfloat row_align, col_align; - guint height, width; - gint new_row = row; - gint new_col = column; - - row_move = FALSE; - column_move = FALSE; - row_align = -1.; - col_align = -1.; - - height = sheet->sheet_window_height; - width = sheet->sheet_window_width; - - if (row >= MAX_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED) - { - row_align = 1.; - new_row = MIN (yyy_row_count (sheet) - 1, row + 1); - row_move = TRUE; - if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet) - 1 && - ROW_TOP_YPIXEL (sheet, yyy_row_count (sheet)- 1) + - yyy_row_height (sheet, yyy_row_count (sheet)- 1) < height) - { - row_move = FALSE; - row_align = -1.; - } - } - if (row < MIN_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED) - { - row_align= 0.; - row_move = TRUE; - } - if (column >= MAX_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED) - { - col_align = 1.; - new_col = MIN (xxx_column_count (sheet) - 1, column + 1); - column_move = TRUE; - if (MAX_VISIBLE_COLUMN (sheet) == (xxx_column_count (sheet) - 1) && - COLUMN_LEFT_XPIXEL (sheet, xxx_column_count (sheet) - 1) + - xxx_column_width (sheet, xxx_column_count (sheet) - 1) < width) - { - column_move = FALSE; - col_align = -1.; - } - } - if (column < MIN_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED) - { - col_align = 0.; - column_move = TRUE; - } - - if (row_move || column_move) - { - gtk_sheet_moveto (sheet, new_row, new_col, row_align, col_align); - } - - return (row_move || column_move); -} - -static void -gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column) -{ - GtkSheetRange range; - gint state; - gint r, c; - - if (row == sheet->selection_cell.row && column == sheet->selection_cell.col) - return; - - if (sheet->selection_mode == GTK_SELECTION_SINGLE) return; - - gtk_sheet_move_query (sheet, row, column); - gtk_widget_grab_focus (GTK_WIDGET (sheet)); - - if (GTK_SHEET_IN_DRAG (sheet)) return; - - state = sheet->state; - - switch (sheet->state) - { - case GTK_SHEET_ROW_SELECTED: - column = xxx_column_count (sheet) - 1; - break; - case GTK_SHEET_COLUMN_SELECTED: - row = yyy_row_count (sheet) - 1; - break; - case GTK_SHEET_NORMAL: - sheet->state = GTK_SHEET_RANGE_SELECTED; - r = sheet->active_cell.row; - c = sheet->active_cell.col; - sheet->range.col0 = c; - sheet->range.row0 = r; - sheet->range.coli = c; - sheet->range.rowi = r; - gdk_draw_drawable (sheet->sheet_window, - GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL], - sheet->pixmap, - COLUMN_LEFT_XPIXEL (sheet, c)- 1, - ROW_TOP_YPIXEL (sheet, r)- 1, - COLUMN_LEFT_XPIXEL (sheet, c)- 1, - ROW_TOP_YPIXEL (sheet, r)- 1, - xxx_column_width (sheet, c)+4, - yyy_row_height (sheet, r)+4); - gtk_sheet_range_draw_selection (sheet, sheet->range); - case GTK_SHEET_RANGE_SELECTED: - sheet->state = GTK_SHEET_RANGE_SELECTED; - } - - sheet->selection_cell.row = row; - sheet->selection_cell.col = column; - - range.col0 = MIN (column, sheet->active_cell.col); - range.coli = MAX (column, sheet->active_cell.col); - range.row0 = MIN (row, sheet->active_cell.row); - range.rowi = MAX (row, sheet->active_cell.row); - - if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi || - range.col0 != sheet->range.col0 || range.coli != sheet->range.coli || - state == GTK_SHEET_NORMAL) - gtk_sheet_real_select_range (sheet, &range); - -} - -static gint -gtk_sheet_entry_key_press (GtkWidget *widget, - GdkEventKey *key) -{ - gboolean focus; - g_signal_emit_by_name (widget, "key_press_event", key, &focus); - return focus; -} - -static gint -gtk_sheet_key_press (GtkWidget *widget, - GdkEventKey *key) -{ - GtkSheet *sheet; - gint row, col; - gint state; - gboolean extend_selection = FALSE; - gboolean force_move = FALSE; - gboolean in_selection = FALSE; - gboolean veto = TRUE; - gint scroll = 1; - - sheet = GTK_SHEET (widget); - - if (key->state & GDK_CONTROL_MASK || key->keyval == GDK_Control_L || - key->keyval == GDK_Control_R) return FALSE; - - extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval == GDK_Shift_L - || key->keyval == GDK_Shift_R; - - state = sheet->state; - in_selection = GTK_SHEET_IN_SELECTION (sheet); - GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - - switch (key->keyval) - { - case GDK_Return: case GDK_KP_Enter: - if (sheet->state == GTK_SHEET_NORMAL && - !GTK_SHEET_IN_SELECTION (sheet)) - g_signal_stop_emission_by_name (gtk_sheet_get_entry (sheet), - "key-press-event"); - row = sheet->active_cell.row; - col = sheet->active_cell.col; - if (sheet->state == GTK_SHEET_COLUMN_SELECTED) - row = MIN_VISIBLE_ROW (sheet)- 1; - if (sheet->state == GTK_SHEET_ROW_SELECTED) - col = MIN_VISIBLE_COLUMN (sheet); - if (row < yyy_row_count (sheet) - 1) - { - row = row + scroll; - while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) - row++; - } - gtk_sheet_click_cell (sheet, row, col, &veto); - extend_selection = FALSE; - break; - case GDK_ISO_Left_Tab: - row = sheet->active_cell.row; - col = sheet->active_cell.col; - if (sheet->state == GTK_SHEET_ROW_SELECTED) - col = MIN_VISIBLE_COLUMN (sheet)- 1; - if (sheet->state == GTK_SHEET_COLUMN_SELECTED) - row = MIN_VISIBLE_ROW (sheet); - if (col > 0) - { - col = col - scroll; - while (! xxx_column_is_visible (sheet, col) && col > 0) col--; - col = MAX (0, col); - } - gtk_sheet_click_cell (sheet, row, col, &veto); - extend_selection = FALSE; - break; - case GDK_Tab: - row = sheet->active_cell.row; - col = sheet->active_cell.col; - if (sheet->state == GTK_SHEET_ROW_SELECTED) - col = MIN_VISIBLE_COLUMN (sheet)- 1; - if (sheet->state == GTK_SHEET_COLUMN_SELECTED) - row = MIN_VISIBLE_ROW (sheet); - if (col < xxx_column_count (sheet) - 1) - { - col = col + scroll; - while (! xxx_column_is_visible (sheet, col) && - col < xxx_column_count (sheet) - 1) - col++; - } - gtk_sheet_click_cell (sheet, row, col, &veto); - extend_selection = FALSE; - break; - case GDK_Page_Up: - scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1; - case GDK_Up: - if (extend_selection) - { - if (state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - col = sheet->active_cell.col; - gtk_sheet_click_cell (sheet, row, col, &veto); - if (!veto) break; - } - if (sheet->selection_cell.row > 0) - { - row = sheet->selection_cell.row - scroll; - while (!yyy_row_is_visible (sheet, row) && row > 0) row--; - row = MAX (0, row); - gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col); - } - return TRUE; - } - col = sheet->active_cell.col; - row = sheet->active_cell.row; - if (state == GTK_SHEET_COLUMN_SELECTED) - row = MIN_VISIBLE_ROW (sheet); - if (state == GTK_SHEET_ROW_SELECTED) - col = MIN_VISIBLE_COLUMN (sheet); - row = row - scroll; - while (!yyy_row_is_visible (sheet, row) && row > 0) row--; - row = MAX (0, row); - gtk_sheet_click_cell (sheet, row, col, &veto); - extend_selection = FALSE; - break; - case GDK_Page_Down: - scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1; - case GDK_Down: - if (extend_selection) - { - if (state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - col = sheet->active_cell.col; - gtk_sheet_click_cell (sheet, row, col, &veto); - if (!veto) break; - } - if (sheet->selection_cell.row < yyy_row_count (sheet)- 1) - { - row = sheet->selection_cell.row + scroll; - while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++; - row = MIN (yyy_row_count (sheet)- 1, row); - gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col); - } - return TRUE; - } - col = sheet->active_cell.col; - row = sheet->active_cell.row; - if (sheet->active_cell.row < yyy_row_count (sheet)- 1) - { - if (state == GTK_SHEET_COLUMN_SELECTED) - row = MIN_VISIBLE_ROW (sheet)- 1; - if (state == GTK_SHEET_ROW_SELECTED) - col = MIN_VISIBLE_COLUMN (sheet); - row = row + scroll; - while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++; - row = MIN (yyy_row_count (sheet)- 1, row); - } - gtk_sheet_click_cell (sheet, row, col, &veto); - extend_selection = FALSE; - break; - case GDK_Right: - if (extend_selection) - { - if (state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - col = sheet->active_cell.col; - gtk_sheet_click_cell (sheet, row, col, &veto); - if (!veto) break; - } - if (sheet->selection_cell.col < xxx_column_count (sheet) - 1) - { - col = sheet->selection_cell.col + 1; - while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1) - col++; - gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col); - } - return TRUE; - } - col = sheet->active_cell.col; - row = sheet->active_cell.row; - if (sheet->active_cell.col < xxx_column_count (sheet) - 1) - { - col ++; - if (state == GTK_SHEET_ROW_SELECTED) - col = MIN_VISIBLE_COLUMN (sheet)- 1; - if (state == GTK_SHEET_COLUMN_SELECTED) - row = MIN_VISIBLE_ROW (sheet); - while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1) col++; - if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0 - || force_move) - { - gtk_sheet_click_cell (sheet, row, col, &veto); - } - else - return FALSE; - } - extend_selection = FALSE; - break; - case GDK_Left: - if (extend_selection) - { - if (state == GTK_STATE_NORMAL) - { - row = sheet->active_cell.row; - col = sheet->active_cell.col; - gtk_sheet_click_cell (sheet, row, col, &veto); - if (!veto) break; - } - if (sheet->selection_cell.col > 0) - { - col = sheet->selection_cell.col - 1; - while (! xxx_column_is_visible (sheet, col) && col > 0) col--; - gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col); - } - return TRUE; - } - col = sheet->active_cell.col - 1; - row = sheet->active_cell.row; - if (state == GTK_SHEET_ROW_SELECTED) - col = MIN_VISIBLE_COLUMN (sheet)- 1; - if (state == GTK_SHEET_COLUMN_SELECTED) - row = MIN_VISIBLE_ROW (sheet); - while (! xxx_column_is_visible (sheet, col) && col > 0) col--; - col = MAX (0, col); - - if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0 - || force_move) - { - gtk_sheet_click_cell (sheet, row, col, &veto); - } - else - return FALSE; - extend_selection = FALSE; - break; - case GDK_Home: - row = 0; - while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet) - 1) row++; - gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto); - extend_selection = FALSE; - break; - case GDK_End: - row = yyy_row_count (sheet) - 1; - while (!yyy_row_is_visible (sheet, row) && row > 0) row--; - gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto); - extend_selection = FALSE; - break; - default: - if (in_selection) - { - GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION); - if (extend_selection) return TRUE; - } - if (state == GTK_SHEET_ROW_SELECTED) - sheet->active_cell.col = MIN_VISIBLE_COLUMN (sheet); - if (state == GTK_SHEET_COLUMN_SELECTED) - sheet->active_cell.row = MIN_VISIBLE_ROW (sheet); - return FALSE; - } - - if (extend_selection) return TRUE; - - gtk_sheet_activate_cell (sheet, sheet->active_cell.row, - sheet->active_cell.col); - - return TRUE; -} - -static void -gtk_sheet_size_request (GtkWidget * widget, - GtkRequisition * requisition) -{ - GtkSheet *sheet; - GList *children; - GtkSheetChild *child; - GtkRequisition child_requisition; - - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_SHEET (widget)); - g_return_if_fail (requisition != NULL); - - sheet = GTK_SHEET (widget); - - requisition->width = 3*DEFAULT_COLUMN_WIDTH; - requisition->height = 3*DEFAULT_ROW_HEIGHT (widget); - - /* compute the size of the column title area */ - if (sheet->column_titles_visible) - requisition->height += sheet->column_title_area.height; - - /* compute the size of the row title area */ - if (sheet->row_titles_visible) - requisition->width += sheet->row_title_area.width; - - children = sheet->children; - while (children) - { - child = children->data; - children = children->next; - - gtk_widget_size_request (child->widget, &child_requisition); - } -} - - -static void -gtk_sheet_size_allocate (GtkWidget * widget, - GtkAllocation * allocation) -{ - GtkSheet *sheet; - GtkAllocation sheet_allocation; - gint border_width; - - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_SHEET (widget)); - g_return_if_fail (allocation != NULL); - - sheet = GTK_SHEET (widget); - widget->allocation = *allocation; - border_width = GTK_CONTAINER (widget)->border_width; - - if (GTK_WIDGET_REALIZED (widget)) - gdk_window_move_resize (widget->window, - allocation->x + border_width, - allocation->y + border_width, - allocation->width - 2 * border_width, - allocation->height - 2 * border_width); - - /* use internal allocation structure for all the math - * because it's easier than always subtracting the container - * border width */ - sheet->internal_allocation.x = 0; - sheet->internal_allocation.y = 0; - sheet->internal_allocation.width = allocation->width - 2 * border_width; - sheet->internal_allocation.height = allocation->height - 2 * border_width; - - sheet_allocation.x = 0; - sheet_allocation.y = 0; - sheet_allocation.width = allocation->width - 2 * border_width; - sheet_allocation.height = allocation->height - 2 * border_width; - - sheet->sheet_window_width = sheet_allocation.width; - sheet->sheet_window_height = sheet_allocation.height; - - if (GTK_WIDGET_REALIZED (widget)) - gdk_window_move_resize (sheet->sheet_window, - sheet_allocation.x, - sheet_allocation.y, - sheet_allocation.width, - sheet_allocation.height); - - /* position the window which holds the column title buttons */ - sheet->column_title_area.x = 0; - sheet->column_title_area.y = 0; - if (sheet->row_titles_visible) - sheet->column_title_area.x = sheet->row_title_area.width; - sheet->column_title_area.width = sheet_allocation.width - - sheet->column_title_area.x; - if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible) - gdk_window_move_resize (sheet->column_title_window, - sheet->column_title_area.x, - sheet->column_title_area.y, - sheet->column_title_area.width, - sheet->column_title_area.height); - - sheet->sheet_window_width = sheet_allocation.width; - sheet->sheet_window_height = sheet_allocation.height; - - /* column button allocation */ - size_allocate_column_title_buttons (sheet); - - /* position the window which holds the row title buttons */ - sheet->row_title_area.x = 0; - sheet->row_title_area.y = 0; - if (sheet->column_titles_visible) - sheet->row_title_area.y = sheet->column_title_area.height; - sheet->row_title_area.height = sheet_allocation.height - - sheet->row_title_area.y; - - if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible) - gdk_window_move_resize (sheet->row_title_window, - sheet->row_title_area.x, - sheet->row_title_area.y, - sheet->row_title_area.width, - sheet->row_title_area.height); - - - /* row button allocation */ - size_allocate_row_title_buttons (sheet); - size_allocate_column_title_buttons (sheet); - - /* re - scale backing pixmap */ - gtk_sheet_make_backing_pixmap (sheet, 0, 0); - gtk_sheet_position_children (sheet); - - /* set the scrollbars adjustments */ - adjust_scrollbars (sheet); -} - -static void -size_allocate_column_title_buttons (GtkSheet * sheet) -{ - gint i; - gint x, width; - - if (!sheet->column_titles_visible) return; - if (!GTK_WIDGET_REALIZED (sheet)) - return; - - width = sheet->sheet_window_width; - x = 0; - - if (sheet->row_titles_visible) - { - width -= sheet->row_title_area.width; - x = sheet->row_title_area.width; - } - - if (sheet->column_title_area.width != width || sheet->column_title_area.x != x) - { - sheet->column_title_area.width = width; - sheet->column_title_area.x = x; - gdk_window_move_resize (sheet->column_title_window, - sheet->column_title_area.x, - sheet->column_title_area.y, - sheet->column_title_area.width, - sheet->column_title_area.height); - } - - - if (MAX_VISIBLE_COLUMN (sheet) == xxx_column_count (sheet) - 1) - gdk_window_clear_area (sheet->column_title_window, - 0, 0, - sheet->column_title_area.width, - sheet->column_title_area.height); - - if (!GTK_WIDGET_DRAWABLE (sheet)) return; - - size_allocate_global_button (sheet); - - for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++) - gtk_sheet_column_title_button_draw (sheet, i); -} - -static void -size_allocate_row_title_buttons (GtkSheet * sheet) -{ - gint i; - gint y, height; - - if (!sheet->row_titles_visible) return; - if (!GTK_WIDGET_REALIZED (sheet)) - return; - - height = sheet->sheet_window_height; - y = 0; - - if (sheet->column_titles_visible) - { - height -= sheet->column_title_area.height; - y = sheet->column_title_area.height; - } - - if (sheet->row_title_area.height != height || sheet->row_title_area.y != y) - { - sheet->row_title_area.y = y; - sheet->row_title_area.height = height; - gdk_window_move_resize (sheet->row_title_window, - sheet->row_title_area.x, - sheet->row_title_area.y, - sheet->row_title_area.width, - sheet->row_title_area.height); - } - if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet)- 1) - gdk_window_clear_area (sheet->row_title_window, - 0, 0, - sheet->row_title_area.width, - sheet->row_title_area.height); - - if (!GTK_WIDGET_DRAWABLE (sheet)) return; - - size_allocate_global_button (sheet); - - for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++) - { - if ( i >= yyy_row_count (sheet)) - break; - gtk_sheet_row_title_button_draw (sheet, i); - } -} - - -static void -gtk_sheet_size_allocate_entry (GtkSheet *sheet) -{ - GtkAllocation shentry_allocation; - GtkSheetCellAttr attributes = { 0 }; - GtkEntry *sheet_entry; - GtkStyle *style = NULL, *previous_style = NULL; - gint row, col; - gint size, max_size, text_size, column_width; - const gchar *text; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return; - - sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet)); - - if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row, - sheet->active_cell.col, - &attributes) ) - return ; - - if ( GTK_WIDGET_REALIZED (sheet->entry_widget) ) - { - if (!GTK_WIDGET (sheet_entry)->style) - gtk_widget_ensure_style (GTK_WIDGET (sheet_entry)); - - previous_style = GTK_WIDGET (sheet_entry)->style; - - style = gtk_style_copy (previous_style); - style->bg[GTK_STATE_NORMAL] = attributes.background; - style->fg[GTK_STATE_NORMAL] = attributes.foreground; - style->text[GTK_STATE_NORMAL] = attributes.foreground; - style->bg[GTK_STATE_ACTIVE] = attributes.background; - style->fg[GTK_STATE_ACTIVE] = attributes.foreground; - style->text[GTK_STATE_ACTIVE] = attributes.foreground; - - pango_font_description_free (style->font_desc); - g_assert (attributes.font_desc); - style->font_desc = pango_font_description_copy (attributes.font_desc); - - GTK_WIDGET (sheet_entry)->style = style; - gtk_widget_size_request (sheet->entry_widget, NULL); - GTK_WIDGET (sheet_entry)->style = previous_style; - - if (style != previous_style) - { - if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget)) - { - style->bg[GTK_STATE_NORMAL] = previous_style->bg[GTK_STATE_NORMAL]; - style->fg[GTK_STATE_NORMAL] = previous_style->fg[GTK_STATE_NORMAL]; - style->bg[GTK_STATE_ACTIVE] = previous_style->bg[GTK_STATE_ACTIVE]; - style->fg[GTK_STATE_ACTIVE] = previous_style->fg[GTK_STATE_ACTIVE]; - } - gtk_widget_set_style (GTK_WIDGET (sheet_entry), style); - g_object_unref (style); - } - } - - if (GTK_IS_ITEM_ENTRY (sheet_entry)) - max_size = GTK_ITEM_ENTRY (sheet_entry)->text_max_size; - else - max_size = 0; - - text_size = 0; - text = gtk_entry_get_text (GTK_ENTRY (sheet_entry)); - if (text && strlen (text) > 0) - text_size = STRING_WIDTH (GTK_WIDGET (sheet), attributes.font_desc, text); - - column_width = xxx_column_width (sheet, sheet->active_cell.col); - - size = MIN (text_size, max_size); - size = MAX (size, column_width - 2 * CELLOFFSET); - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - shentry_allocation.x = COLUMN_LEFT_XPIXEL (sheet, sheet->active_cell.col); - shentry_allocation.y = ROW_TOP_YPIXEL (sheet, sheet->active_cell.row); - shentry_allocation.width = column_width; - shentry_allocation.height = yyy_row_height (sheet, sheet->active_cell.row); - - if (GTK_IS_ITEM_ENTRY (sheet->entry_widget)) - { - shentry_allocation.height -= 2 * CELLOFFSET; - shentry_allocation.y += CELLOFFSET; - shentry_allocation.width = size; - - switch (GTK_ITEM_ENTRY (sheet_entry)->justification) - { - case GTK_JUSTIFY_CENTER: - shentry_allocation.x += column_width / 2 - size / 2; - break; - case GTK_JUSTIFY_RIGHT: - shentry_allocation.x += column_width - size - CELLOFFSET; - break; - case GTK_JUSTIFY_LEFT: - case GTK_JUSTIFY_FILL: - shentry_allocation.x += CELLOFFSET; - break; - } - } - - if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget)) - { - shentry_allocation.x += 2; - shentry_allocation.y += 2; - shentry_allocation.width -= MIN (shentry_allocation.width, 3); - shentry_allocation.height -= MIN (shentry_allocation.height, 3); - } - - gtk_widget_size_allocate (sheet->entry_widget, &shentry_allocation); - - if (previous_style == style) g_object_unref (previous_style); -} - -static void -gtk_sheet_entry_set_max_size (GtkSheet *sheet) -{ - gint i; - gint size = 0; - gint sizel = 0, sizer = 0; - gint row, col; - GtkJustification justification; - gchar *s = NULL; - - row = sheet->active_cell.row; - col = sheet->active_cell.col; - - if ( ! GTK_IS_ITEM_ENTRY (sheet->entry_widget) ) - return; - - justification = GTK_ITEM_ENTRY (sheet->entry_widget)->justification; - - switch (justification) - { - case GTK_JUSTIFY_FILL: - case GTK_JUSTIFY_LEFT: - for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++) - { - if ((s = gtk_sheet_cell_get_text (sheet, row, i))) - { - g_free (s); - break; - } - size +=xxx_column_width (sheet, i); - } - size = MIN (size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL (sheet, col)); - break; - case GTK_JUSTIFY_RIGHT: - for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--) - { - if ((s = gtk_sheet_cell_get_text (sheet, row, i))) - { - g_free (s); - break; - } - size +=xxx_column_width (sheet, i); - } - break; - case GTK_JUSTIFY_CENTER: - for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++) - { - sizer += xxx_column_width (sheet, i); - } - for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--) - { - if ((s = gtk_sheet_cell_get_text (sheet, row, i))) - { - g_free (s); - break; - } - sizel +=xxx_column_width (sheet, i); - } - size = 2 * MIN (sizel, sizer); - break; - } - - if (size != 0) - size += xxx_column_width (sheet, col); - GTK_ITEM_ENTRY (sheet->entry_widget)->text_max_size = size; -} - - -static void -create_sheet_entry (GtkSheet *sheet) -{ - if (sheet->entry_widget) - { - gtk_widget_unparent (sheet->entry_widget); - } - - if (sheet->entry_type) - { - sheet->entry_container = g_object_new (sheet->entry_type, NULL); - g_object_ref_sink (sheet->entry_container); - sheet->entry_widget = gtk_sheet_get_entry (sheet); - - if ( NULL == sheet->entry_widget) - { - g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. " - "Using default", g_type_name (sheet->entry_type)); - g_object_unref (sheet->entry_container); - sheet->entry_widget = sheet->entry_container = gtk_item_entry_new (); - } - else - { - sheet->entry_widget = sheet->entry_container ; - } - } - else - { - sheet->entry_widget = sheet->entry_container = gtk_item_entry_new (); - g_object_ref_sink (sheet->entry_container); - } - - gtk_widget_size_request (sheet->entry_widget, NULL); - - if (GTK_WIDGET_REALIZED (sheet)) - { - gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window); - gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet)); - gtk_widget_realize (sheet->entry_widget); - } - - g_signal_connect_swapped (sheet->entry_widget, "key_press_event", - G_CALLBACK (gtk_sheet_entry_key_press), - sheet); - - gtk_widget_show (sheet->entry_widget); -} - - -/* Finds the last child widget that happens to be of type GtkEntry */ -static void -find_entry (GtkWidget *w, gpointer user_data) -{ - GtkWidget **entry = user_data; - if ( GTK_IS_ENTRY (w)) - { - *entry = w; - } -} - -GtkWidget * -gtk_sheet_get_entry (GtkSheet *sheet) -{ - GtkWidget *parent; - GtkWidget *entry = NULL; - GtkTableChild *table_child; - GtkBoxChild *box_child; - GList *children = NULL; - - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - g_return_val_if_fail (sheet->entry_widget != NULL, NULL); - - if (GTK_IS_ENTRY (sheet->entry_container)) - return (sheet->entry_container); - - parent = sheet->entry_container; - - if (GTK_IS_TABLE (parent)) children = GTK_TABLE (parent)->children; - if (GTK_IS_BOX (parent)) children = GTK_BOX (parent)->children; - - if (GTK_IS_CONTAINER (parent)) - { - gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry); - - if (GTK_IS_ENTRY (entry)) - return entry; - } - - if (!children) return NULL; - - while (children) - { - if (GTK_IS_TABLE (parent)) - { - table_child = children->data; - entry = table_child->widget; - } - if (GTK_IS_BOX (parent)) - { - box_child = children->data; - entry = box_child->widget; - } - - if (GTK_IS_ENTRY (entry)) - break; - children = children->next; - } - - - if (!GTK_IS_ENTRY (entry)) return NULL; - - return (entry); - -} - -GtkWidget * -gtk_sheet_get_entry_widget (GtkSheet *sheet) -{ - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - g_return_val_if_fail (sheet->entry_widget != NULL, NULL); - - return (sheet->entry_widget); -} - - -static void -gtk_sheet_button_draw (GtkSheet *sheet, GdkWindow *window, - GtkSheetButton *button, gboolean is_sensitive, - GdkRectangle allocation) -{ - GtkShadowType shadow_type; - gint text_width = 0, text_height = 0; - GtkSheetChild *child = NULL; - PangoAlignment align = PANGO_ALIGN_LEFT; - - gboolean rtl ; - - gint state = 0; - gint len = 0; - gchar *line = 0; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (button != NULL); - - rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL; - - gdk_window_clear_area (window, - allocation.x, allocation.y, - allocation.width, allocation.height); - - gtk_paint_box (sheet->button->style, window, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, - &allocation, GTK_WIDGET (sheet->button), - "buttondefault", - allocation.x, allocation.y, - allocation.width, allocation.height); - - state = button->state; - if (!is_sensitive) state = GTK_STATE_INSENSITIVE; - - if (state == GTK_STATE_ACTIVE) - shadow_type = GTK_SHADOW_IN; - else - shadow_type = GTK_SHADOW_OUT; - - if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE) - gtk_paint_box (sheet->button->style, window, - button->state, shadow_type, - &allocation, GTK_WIDGET (sheet->button), - "button", - allocation.x, allocation.y, - allocation.width, allocation.height); - - if (button->label_visible) - { - - text_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))- 2 * CELLOFFSET; - - gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state], - &allocation); - gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, &allocation); - - allocation.y += 2 * sheet->button->style->ythickness; - - - if (button->label && strlen (button->label)>0) - { - gchar *words = 0; - PangoLayout *layout = NULL; - gint real_x = allocation.x, real_y = allocation.y; - - words = button->label; - line = g_new (gchar, 1); - line[0]='\0'; - - while (words && *words != '\0') - { - if (*words != '\n') - { - len = strlen (line); - line = g_realloc (line, len + 2); - line[len]=*words; - line[len + 1]='\0'; - } - if (*words == '\n' || * (words + 1) == '\0') - { - text_width = STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, line); - - layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line); - switch (button->justification) - { - case GTK_JUSTIFY_LEFT: - real_x = allocation.x + CELLOFFSET; - align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; - break; - case GTK_JUSTIFY_RIGHT: - real_x = allocation.x + allocation.width - text_width - CELLOFFSET; - align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT; - break; - case GTK_JUSTIFY_CENTER: - default: - real_x = allocation.x + (allocation.width - text_width)/2; - align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT; - pango_layout_set_justify (layout, TRUE); - } - pango_layout_set_alignment (layout, align); - gtk_paint_layout (GTK_WIDGET (sheet)->style, - window, - state, - FALSE, - &allocation, - GTK_WIDGET (sheet), - "label", - real_x, real_y, - layout); - g_object_unref (layout); - - real_y += text_height + 2; - - g_free (line); - line = g_new (gchar, 1); - line[0]='\0'; - } - words++; - } - g_free (line); - } - - gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state], - NULL); - gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL); - - } - - if ((child = button->child) && (child->widget)) - { - child->x = allocation.x; - child->y = allocation.y; - - child->x += (allocation.width - child->widget->requisition.width) / 2; - child->y += (allocation.height - child->widget->requisition.height) / 2; - allocation.x = child->x; - allocation.y = child->y; - allocation.width = child->widget->requisition.width; - allocation.height = child->widget->requisition.height; - - allocation.x = child->x; - allocation.y = child->y; - - gtk_widget_set_state (child->widget, button->state); - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && - GTK_WIDGET_MAPPED (child->widget)) - { - gtk_widget_size_allocate (child->widget, - &allocation); - gtk_widget_queue_draw (child->widget); - } - } - - gtk_sheet_button_free (button); -} - - -/* COLUMN value of - 1 indicates that the area to the right of the rightmost - button should be redrawn */ -static void -gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column) -{ - GdkWindow *window = NULL; - GdkRectangle allocation; - - gboolean is_sensitive = FALSE; - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - if (column >= 0 && ! xxx_column_is_visible (sheet, column)) return; - if (column >= 0 && !sheet->column_titles_visible) return; - if (column >= 0 && column < MIN_VISIBLE_COLUMN (sheet)) return; - if (column >= 0 && column > MAX_VISIBLE_COLUMN (sheet)) return; - - window = sheet->column_title_window; - allocation.y = 0; - allocation.height = sheet->column_title_area.height; - - if ( column == -1 ) - { - const gint cols = xxx_column_count (sheet) ; - allocation.x = COLUMN_LEFT_XPIXEL (sheet, cols - 1) - ; - allocation.width = sheet->column_title_area.width - + sheet->column_title_area.x - - allocation.x; - - gdk_window_clear_area (window, - allocation.x, allocation.y, - allocation.width, allocation.height); - } - else - { - GtkSheetButton *button = xxx_column_button (sheet, column); - allocation.x = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING; - if (sheet->row_titles_visible) - allocation.x -= sheet->row_title_area.width; - - allocation.width = xxx_column_width (sheet, column); - - is_sensitive = xxx_column_is_sensitive (sheet, column); - gtk_sheet_button_draw (sheet, window, button, - is_sensitive, allocation); - - /* FIXME: Not freeing this button is correct (sort of), - because in PSPP the model always returns a static copy */ - - /* gtk_sheet_button_free (button); */ - - } -} - -static void -gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row) -{ - GdkWindow *window = NULL; - GdkRectangle allocation; - GtkSheetButton *button = NULL; - gboolean is_sensitive = FALSE; - - - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return; - - if (row >= 0 && !yyy_row_is_visible (sheet, row)) return; - if (row >= 0 && !sheet->row_titles_visible) return; - if (row >= 0 && row < MIN_VISIBLE_ROW (sheet)) return; - if (row >= 0 && row > MAX_VISIBLE_ROW (sheet)) return; - - - window = sheet->row_title_window; - button = yyy_row_button (sheet, row); - allocation.x = 0; - allocation.y = ROW_TOP_YPIXEL (sheet, row) + CELL_SPACING; - if (sheet->column_titles_visible) - allocation.y -= sheet->column_title_area.height; - allocation.width = sheet->row_title_area.width; - allocation.height = yyy_row_height (sheet, row); - is_sensitive = yyy_row_is_sensitive (sheet, row); - - gtk_sheet_button_draw (sheet, window, button, is_sensitive, allocation); -} - -/* SCROLLBARS - * - * functions: - * adjust_scrollbars - * vadjustment_value_changed - * hadjustment_value_changed */ - -static void -adjust_scrollbars (GtkSheet * sheet) -{ - if (sheet->vadjustment) - { - sheet->vadjustment->page_size = sheet->sheet_window_height; - sheet->vadjustment->page_increment = sheet->sheet_window_height / 2; - sheet->vadjustment->step_increment = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)); - sheet->vadjustment->lower = 0; - sheet->vadjustment->upper = SHEET_HEIGHT (sheet) + 80; - g_signal_emit_by_name (sheet->vadjustment, "changed"); - - } - - if (sheet->hadjustment) - { - sheet->hadjustment->page_size = sheet->sheet_window_width; - sheet->hadjustment->page_increment = sheet->sheet_window_width / 2; - sheet->hadjustment->step_increment = DEFAULT_COLUMN_WIDTH; - sheet->hadjustment->lower = 0; - sheet->hadjustment->upper = SHEET_WIDTH (sheet)+ 80; - g_signal_emit_by_name (sheet->hadjustment, "changed"); - - } -} - -static void -vadjustment_value_changed (GtkAdjustment * adjustment, - gpointer data) -{ - GtkSheet *sheet; - gint diff, value, old_value; - gint row, new_row; - gint y = 0; - - g_return_if_fail (adjustment != NULL); - g_return_if_fail (data != NULL); - g_return_if_fail (GTK_IS_SHEET (data)); - - sheet = GTK_SHEET (data); - - if (GTK_SHEET_IS_FROZEN (sheet)) return; - - row = ROW_FROM_YPIXEL (sheet, CELL_SPACING); - - old_value = - sheet->voffset; - - new_row = g_sheet_row_pixel_to_row (sheet->row_geometry, - adjustment->value); - - y = g_sheet_row_start_pixel (sheet->row_geometry, new_row); - - if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. && - yyy_row_height (sheet, row) > sheet->vadjustment->step_increment) - { - /* This avoids embarrassing twitching */ - if (row == new_row && row != yyy_row_count (sheet) - 1 && - adjustment->value - sheet->old_vadjustment >= - sheet->vadjustment->step_increment && - new_row + 1 != MIN_VISIBLE_ROW (sheet)) - { - new_row +=1; - y = y+yyy_row_height (sheet, row); - } - } - - /* Negative old_adjustment enforces the redraw, otherwise avoid - spureous redraw */ - if (sheet->old_vadjustment >= 0. && row == new_row) - { - sheet->old_vadjustment = sheet->vadjustment->value; - return; - } - - sheet->old_vadjustment = sheet->vadjustment->value; - adjustment->value = y; - - - if (new_row == 0) - { - sheet->vadjustment->step_increment = yyy_row_height (sheet, 0); - } - else - { - sheet->vadjustment->step_increment = - MIN (yyy_row_height (sheet, new_row), yyy_row_height (sheet, new_row - 1)); - } - - sheet->vadjustment->value = adjustment->value; - - value = adjustment->value; - - if (value >= - sheet->voffset) - { - /* scroll down */ - diff = value + sheet->voffset; - } - else - { - /* scroll up */ - diff = - sheet->voffset - value; - } - - sheet->voffset = - value; - - if (GTK_WIDGET_REALIZED (sheet->entry_widget) && - sheet->state == GTK_SHEET_NORMAL && - sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 && - !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row, - sheet->active_cell.col)) - { - const gchar *text; - - text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet))); - - if (!text || strlen (text) == 0) - gtk_sheet_cell_clear (sheet, - sheet->active_cell.row, - sheet->active_cell.col); - gtk_widget_unmap (sheet->entry_widget); - } - - gtk_sheet_position_children (sheet); - - gtk_sheet_range_draw (sheet, NULL); - size_allocate_row_title_buttons (sheet); - size_allocate_global_button (sheet); -} - -static void -hadjustment_value_changed (GtkAdjustment * adjustment, - gpointer data) -{ - GtkSheet *sheet; - gint i, diff, value, old_value; - gint column, new_column; - gint x = 0; - - g_return_if_fail (adjustment != NULL); - g_return_if_fail (data != NULL); - g_return_if_fail (GTK_IS_SHEET (data)); - - sheet = GTK_SHEET (data); - - if (GTK_SHEET_IS_FROZEN (sheet)) return; - - column = COLUMN_FROM_XPIXEL (sheet, CELL_SPACING); - - old_value = - sheet->hoffset; - - for (i = 0; i < xxx_column_count (sheet); i++) - { - if (xxx_column_is_visible (sheet, i)) x += xxx_column_width (sheet, i); - if (x > adjustment->value) break; - } - x -= xxx_column_width (sheet, i); - new_column = i; - - if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 && - xxx_column_width (sheet, i) > sheet->hadjustment->step_increment) - { - /* This avoids embarrassing twitching */ - if (column == new_column && column != xxx_column_count (sheet) - 1 && - adjustment->value - sheet->old_hadjustment >= - sheet->hadjustment->step_increment && - new_column + 1 != MIN_VISIBLE_COLUMN (sheet)) - { - new_column += 1; - x += xxx_column_width (sheet, column); - } - } - - /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */ - if (sheet->old_hadjustment >= 0. && new_column == column) - { - sheet->old_hadjustment = sheet->hadjustment->value; - return; - } - - sheet->old_hadjustment = sheet->hadjustment->value; - adjustment->value = x; - - if (new_column == 0) - { - sheet->hadjustment->step_increment = xxx_column_width (sheet, 0); - } - else - { - sheet->hadjustment->step_increment = - MIN (xxx_column_width (sheet, new_column), xxx_column_width (sheet, new_column - 1)); - } - - - sheet->hadjustment->value = adjustment->value; - - value = adjustment->value; - - if (value >= - sheet->hoffset) - { - /* scroll right */ - diff = value + sheet->hoffset; - } - else - { - /* scroll left */ - diff = - sheet->hoffset - value; - } - - sheet->hoffset = - value; - if (GTK_WIDGET_REALIZED (sheet->entry_widget) && - sheet->state == GTK_SHEET_NORMAL && - sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 && - !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row, - sheet->active_cell.col)) - { - const gchar *text; - - text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet))); - if (!text || strlen (text) == 0) - gtk_sheet_cell_clear (sheet, - sheet->active_cell.row, - sheet->active_cell.col); - - gtk_widget_unmap (sheet->entry_widget); - } - - gtk_sheet_position_children (sheet); - - gtk_sheet_range_draw (sheet, NULL); - size_allocate_column_title_buttons (sheet); -} - - -/* COLUMN RESIZING */ -static void -draw_xor_vline (GtkSheet * sheet) -{ - GtkWidget *widget; - - g_return_if_fail (sheet != NULL); - - widget = GTK_WIDGET (sheet); - - gdk_draw_line (widget->window, sheet->xor_gc, - sheet->x_drag, - sheet->column_title_area.height, - sheet->x_drag, - sheet->sheet_window_height + 1); -} - -/* ROW RESIZING */ -static void -draw_xor_hline (GtkSheet * sheet) -{ - GtkWidget *widget; - - g_return_if_fail (sheet != NULL); - - widget = GTK_WIDGET (sheet); - - gdk_draw_line (widget->window, sheet->xor_gc, - sheet->row_title_area.width, - sheet->y_drag, - - sheet->sheet_window_width + 1, - sheet->y_drag); -} - -/* SELECTED RANGE */ -static void -draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range) -{ - gint i; - GdkRectangle clip_area, area; - GdkGCValues values; - - area.x = COLUMN_LEFT_XPIXEL (sheet, range.col0); - area.y = ROW_TOP_YPIXEL (sheet, range.row0); - area.width = COLUMN_LEFT_XPIXEL (sheet, range.coli)- area.x+ - xxx_column_width (sheet, range.coli); - area.height = ROW_TOP_YPIXEL (sheet, range.rowi)- area.y+ - yyy_row_height (sheet, range.rowi); - - clip_area.x = sheet->row_title_area.width; - clip_area.y = sheet->column_title_area.height; - clip_area.width = sheet->sheet_window_width; - clip_area.height = sheet->sheet_window_height; - - if (!sheet->row_titles_visible) clip_area.x = 0; - if (!sheet->column_titles_visible) clip_area.y = 0; - - if (area.x < 0) - { - area.width = area.width + area.x; - area.x = 0; - } - if (area.width > clip_area.width) area.width = clip_area.width + 10; - if (area.y < 0) - { - area.height = area.height + area.y; - area.y = 0; - } - if (area.height > clip_area.height) area.height = clip_area.height + 10; - - clip_area.x--; - clip_area.y--; - clip_area.width += 3; - clip_area.height += 3; - - gdk_gc_get_values (sheet->xor_gc, &values); - - gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area); - - for (i = -1; i <= 1; ++i) - gdk_draw_rectangle (sheet->sheet_window, - sheet->xor_gc, - FALSE, - area.x + i, area.y + i, - area.width - 2 * i, area.height - 2 * i); - - - gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL); - - gdk_gc_set_foreground (sheet->xor_gc, &values.foreground); - -} - - -/* this function returns the new width of the column being resized given - * the column and x position of the cursor; the x cursor position is passed - * in as a pointer and automaticaly corrected if it's beyond min / max limits */ -static guint -new_column_width (GtkSheet * sheet, - gint column, - gint * x) -{ - gint cx, width; - guint min_width; - - cx = *x; - - min_width = sheet->column_requisition; - - /* you can't shrink a column to less than its minimum width */ - if (cx < COLUMN_LEFT_XPIXEL (sheet, column) + min_width) - { - *x = cx = COLUMN_LEFT_XPIXEL (sheet, column) + min_width; - } - - /* calculate new column width making sure it doesn't end up - * less than the minimum width */ - width = cx - COLUMN_LEFT_XPIXEL (sheet, column); - if (width < min_width) - width = min_width; - - xxx_set_column_width (sheet, column, width); - size_allocate_column_title_buttons (sheet); - - return width; -} - -/* this function returns the new height of the row being resized given - * the row and y position of the cursor; the y cursor position is passed - * in as a pointer and automaticaly corrected if it's beyond min / max limits */ -static guint -new_row_height (GtkSheet * sheet, - gint row, - gint * y) -{ - gint cy, height; - guint min_height; - - cy = *y; - min_height = sheet->row_requisition; - - /* you can't shrink a row to less than its minimum height */ - if (cy < ROW_TOP_YPIXEL (sheet, row) + min_height) - - { - *y = cy = ROW_TOP_YPIXEL (sheet, row) + min_height; - } - - /* calculate new row height making sure it doesn't end up - * less than the minimum height */ - height = (cy - ROW_TOP_YPIXEL (sheet, row)); - if (height < min_height) - height = min_height; - - yyy_set_row_height (sheet, row, height); - size_allocate_row_title_buttons (sheet); - - return height; -} - -static void -gtk_sheet_set_column_width (GtkSheet * sheet, - gint column, - guint width) -{ - guint min_width; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (column < 0 || column >= xxx_column_count (sheet)) - return; - - gtk_sheet_column_size_request (sheet, column, &min_width); - if (width < min_width) return; - - xxx_set_column_width (sheet, column, width); - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet)) - { - size_allocate_column_title_buttons (sheet); - adjust_scrollbars (sheet); - gtk_sheet_size_allocate_entry (sheet); - gtk_sheet_range_draw (sheet, NULL); - } - - g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, column); -} - - - -void -gtk_sheet_set_row_height (GtkSheet * sheet, - gint row, - guint height) -{ - guint min_height; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - if (row < 0 || row >= yyy_row_count (sheet)) - return; - - gtk_sheet_row_size_request (sheet, row, &min_height); - if (height < min_height) return; - - yyy_set_row_height (sheet, row, height); - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet)) - { - size_allocate_row_title_buttons (sheet); - adjust_scrollbars (sheet); - gtk_sheet_size_allocate_entry (sheet); - gtk_sheet_range_draw (sheet, NULL); - } - - g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, - 1); -} -gboolean -gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col, - GtkSheetCellAttr *attributes) -{ - const GdkColor *fg, *bg; - const GtkJustification *j ; - const PangoFontDescription *font_desc ; - const GtkSheetCellBorder *border ; - - g_return_val_if_fail (sheet != NULL, FALSE); - g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE); - - if (row < 0 || col < 0) return FALSE; - - init_attributes (sheet, col, attributes); - - if ( !sheet->model) - return FALSE; - - attributes->is_editable = g_sheet_model_is_editable (sheet->model, row, col); - attributes->is_visible = g_sheet_model_is_visible (sheet->model, row, col); - - fg = g_sheet_model_get_foreground (sheet->model, row, col); - if ( fg ) - attributes->foreground = *fg; - - bg = g_sheet_model_get_background (sheet->model, row, col); - if ( bg ) - attributes->background = *bg; - - j = g_sheet_model_get_justification (sheet->model, row, col); - if (j) attributes->justification = *j; - - font_desc = g_sheet_model_get_font_desc (sheet->model, row, col); - if ( font_desc ) attributes->font_desc = font_desc; - - border = g_sheet_model_get_cell_border (sheet->model, row, col); - - if ( border ) attributes->border = *border; - - return TRUE; -} - -static void -init_attributes (const GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes) -{ - /* DEFAULT VALUES */ - attributes->foreground = GTK_WIDGET (sheet)->style->black; - attributes->background = sheet->bg_color; - if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - GdkColormap *colormap; - colormap = gdk_colormap_get_system (); - attributes->background = sheet->bg_color; - } - attributes->justification = xxx_column_justification (sheet, col); - attributes->border.width = 0; - attributes->border.line_style = GDK_LINE_SOLID; - attributes->border.cap_style = GDK_CAP_NOT_LAST; - attributes->border.join_style = GDK_JOIN_MITER; - attributes->border.mask = 0; - attributes->border.color = GTK_WIDGET (sheet)->style->black; - attributes->is_editable = TRUE; - attributes->is_visible = TRUE; - attributes->font_desc = GTK_WIDGET (sheet)->style->font_desc; -} - - -/******************************************************************** - * Container Functions: - * gtk_sheet_add - * gtk_sheet_put - * gtk_sheet_attach - * gtk_sheet_remove - * gtk_sheet_move_child - * gtk_sheet_position_child - * gtk_sheet_position_children - * gtk_sheet_realize_child - * gtk_sheet_get_child_at - ********************************************************************/ - -GtkSheetChild * -gtk_sheet_put (GtkSheet *sheet, GtkWidget *child, gint x, gint y) -{ - GtkRequisition child_requisition; - GtkSheetChild *child_info; - - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - g_return_val_if_fail (child != NULL, NULL); - g_return_val_if_fail (child->parent == NULL, NULL); - - child_info = g_new (GtkSheetChild, 1); - child_info->widget = child; - child_info->x = x; - child_info->y = y; - child_info->attached_to_cell = FALSE; - child_info->floating = TRUE; - child_info->xpadding = child_info->ypadding = 0; - child_info->xexpand = child_info->yexpand = FALSE; - child_info->xshrink = child_info->yshrink = FALSE; - child_info->xfill = child_info->yfill = FALSE; - - sheet->children = g_list_append (sheet->children, child_info); - - gtk_widget_set_parent (child, GTK_WIDGET (sheet)); - - gtk_widget_size_request (child, &child_requisition); - - if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet))) - { - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && - (!GTK_WIDGET_REALIZED (child) || GTK_WIDGET_NO_WINDOW (child))) - gtk_sheet_realize_child (sheet, child_info); - - if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) && - !GTK_WIDGET_MAPPED (child)) - gtk_widget_map (child); - } - - gtk_sheet_position_child (sheet, child_info); - - /* This will avoid drawing on the titles */ - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - if (sheet->row_titles_visible) - gdk_window_show (sheet->row_title_window); - if (sheet->column_titles_visible) - gdk_window_show (sheet->column_title_window); - } - - return (child_info); -} - -void -gtk_sheet_attach_floating (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col) -{ - GdkRectangle area; - GtkSheetChild *child; - - if (row < 0 || col < 0) - { - gtk_sheet_button_attach (sheet, widget, row, col); - return; - } - - gtk_sheet_get_cell_area (sheet, row, col, &area); - child = gtk_sheet_put (sheet, widget, area.x, area.y); - child->attached_to_cell = TRUE; - child->row = row; - child->col = col; -} - -void -gtk_sheet_attach_default (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col) -{ - if (row < 0 || col < 0) - { - gtk_sheet_button_attach (sheet, widget, row, col); - return; - } - - gtk_sheet_attach (sheet, widget, row, col, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); -} - -void -gtk_sheet_attach (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col, - gint xoptions, - gint yoptions, - gint xpadding, - gint ypadding) -{ - GdkRectangle area; - GtkSheetChild *child = NULL; - - if (row < 0 || col < 0) - { - gtk_sheet_button_attach (sheet, widget, row, col); - return; - } - - child = g_new0 (GtkSheetChild, 1); - child->attached_to_cell = TRUE; - child->floating = FALSE; - child->widget = widget; - child->row = row; - child->col = col; - child->xpadding = xpadding; - child->ypadding = ypadding; - child->xexpand = (xoptions & GTK_EXPAND) != 0; - child->yexpand = (yoptions & GTK_EXPAND) != 0; - child->xshrink = (xoptions & GTK_SHRINK) != 0; - child->yshrink = (yoptions & GTK_SHRINK) != 0; - child->xfill = (xoptions & GTK_FILL) != 0; - child->yfill = (yoptions & GTK_FILL) != 0; - - sheet->children = g_list_append (sheet->children, child); - - gtk_sheet_get_cell_area (sheet, row, col, &area); - - child->x = area.x + child->xpadding; - child->y = area.y + child->ypadding; - - if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet))) - { - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && - (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget))) - gtk_sheet_realize_child (sheet, child); - - if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) && - !GTK_WIDGET_MAPPED (widget)) - gtk_widget_map (widget); - } - - gtk_sheet_position_child (sheet, child); - - /* This will avoid drawing on the titles */ - - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - { - if (GTK_SHEET_ROW_TITLES_VISIBLE (sheet)) - gdk_window_show (sheet->row_title_window); - if (GTK_SHEET_COL_TITLES_VISIBLE (sheet)) - gdk_window_show (sheet->column_title_window); - } - -} - -void -gtk_sheet_button_attach (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col) -{ - GtkSheetButton *button = 0; - GtkSheetChild *child; - GtkRequisition button_requisition; - - if (row >= 0 && col >= 0) return; - if (row < 0 && col < 0) return; - - child = g_new (GtkSheetChild, 1); - child->widget = widget; - child->x = 0; - child->y = 0; - child->attached_to_cell = TRUE; - child->floating = FALSE; - child->row = row; - child->col = col; - child->xpadding = child->ypadding = 0; - child->xshrink = child->yshrink = FALSE; - child->xfill = child->yfill = FALSE; - - - sheet->children = g_list_append (sheet->children, child); - - gtk_sheet_button_size_request (sheet, button, &button_requisition); - - - if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet))) - { - if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && - (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget))) - gtk_sheet_realize_child (sheet, child); - - if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) && - !GTK_WIDGET_MAPPED (widget)) - gtk_widget_map (widget); - } - - if (row == -1) size_allocate_column_title_buttons (sheet); - if (col == -1) size_allocate_row_title_buttons (sheet); - -} - -static void -label_size_request (GtkSheet *sheet, gchar *label, GtkRequisition *req) -{ - gchar *words; - gchar word[1000]; - gint n = 0; - gint row_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)) - 2 * CELLOFFSET + 2; - - req->height = 0; - req->width = 0; - words = label; - - while (words && *words != '\0') - { - if (*words == '\n' || * (words + 1) == '\0') - { - req->height += row_height; - - word[n] = '\0'; - req->width = MAX (req->width, STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, word)); - n = 0; - } - else - { - word[n++] = *words; - } - words++; - } - - if (n > 0) req->height -= 2; -} - -static void -gtk_sheet_button_size_request (GtkSheet *sheet, - const GtkSheetButton *button, - GtkRequisition *button_requisition) -{ - GtkRequisition requisition; - GtkRequisition label_requisition; - - if (gtk_sheet_autoresize (sheet) && button->label && strlen (button->label) > 0) - { - label_size_request (sheet, button->label, &label_requisition); - label_requisition.width += 2 * CELLOFFSET; - label_requisition.height += 2 * CELLOFFSET; - } - else - { - label_requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)); - label_requisition.width = COLUMN_MIN_WIDTH; - } - - if (button->child) - { - gtk_widget_size_request (button->child->widget, &requisition); - requisition.width += 2 * button->child->xpadding; - requisition.height += 2 * button->child->ypadding; - requisition.width += 2 * sheet->button->style->xthickness; - requisition.height += 2 * sheet->button->style->ythickness; - } - else - { - requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)); - requisition.width = COLUMN_MIN_WIDTH; - } - - *button_requisition = requisition; - button_requisition->width = MAX (requisition.width, label_requisition.width); - button_requisition->height = MAX (requisition.height, label_requisition.height); - -} - -static void -gtk_sheet_row_size_request (GtkSheet *sheet, - gint row, - guint *requisition) -{ - GtkRequisition button_requisition; - GList *children; - - gtk_sheet_button_size_request (sheet, - yyy_row_button (sheet, row), - &button_requisition); - - *requisition = button_requisition.height; - - children = sheet->children; - while (children) - { - GtkSheetChild *child = (GtkSheetChild *)children->data; - GtkRequisition child_requisition; - - if (child->attached_to_cell && child->row == row && child->col != -1 && !child->floating && !child->yshrink) - { - gtk_widget_get_child_requisition (child->widget, &child_requisition); - - if (child_requisition.height + 2 * child->ypadding > *requisition) - *requisition = child_requisition.height + 2 * child->ypadding; - } - children = children->next; - } - - sheet->row_requisition = * requisition; -} - -static void -gtk_sheet_column_size_request (GtkSheet *sheet, - gint col, - guint *requisition) -{ - GtkRequisition button_requisition; - GList *children; - GtkSheetButton *button = xxx_column_button (sheet, col); - - gtk_sheet_button_size_request (sheet, - button, - &button_requisition); - - gtk_sheet_button_free (button); - - *requisition = button_requisition.width; - - children = sheet->children; - while (children) - { - GtkSheetChild *child = (GtkSheetChild *)children->data; - GtkRequisition child_requisition; - - if (child->attached_to_cell && child->col == col && child->row != -1 && !child->floating && !child->xshrink) - { - gtk_widget_get_child_requisition (child->widget, &child_requisition); - - if (child_requisition.width + 2 * child->xpadding > *requisition) - *requisition = child_requisition.width + 2 * child->xpadding; - } - children = children->next; - } - - sheet->column_requisition = *requisition; -} - -void -gtk_sheet_move_child (GtkSheet *sheet, GtkWidget *widget, gint x, gint y) -{ - GtkSheetChild *child; - GList *children; - - g_return_if_fail (sheet != NULL); - g_return_if_fail (GTK_IS_SHEET (sheet)); - - children = sheet->children; - while (children) - { - child = children->data; - - if (child->widget == widget) - { - child->x = x; - child->y = y; - child->row = ROW_FROM_YPIXEL (sheet, y); - child->col = COLUMN_FROM_XPIXEL (sheet, x); - gtk_sheet_position_child (sheet, child); - return; - } - - children = children->next; - } - - g_warning ("Widget must be a GtkSheet child"); - -} - -static void -gtk_sheet_position_child (GtkSheet *sheet, GtkSheetChild *child) -{ - GtkRequisition child_requisition; - GtkAllocation child_allocation; - gint xoffset = 0; - gint yoffset = 0; - gint x = 0, y = 0; - GdkRectangle area; - - gtk_widget_get_child_requisition (child->widget, &child_requisition); - - if (sheet->column_titles_visible) - yoffset = sheet->column_title_area.height; - - if (sheet->row_titles_visible) - xoffset = sheet->row_title_area.width; - - if (child->attached_to_cell) - { - gtk_sheet_get_cell_area (sheet, child->row, child->col, &area); - child->x = area.x + child->xpadding; - child->y = area.y + child->ypadding; - - if (!child->floating) - { - if (child_requisition.width + 2 * child->xpadding <= xxx_column_width (sheet, child->col)) - { - if (child->xfill) - { - child_requisition.width = child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding; - } - else - { - if (child->xexpand) - { - child->x = area.x + xxx_column_width (sheet, child->col) / 2 - - child_requisition.width / 2; - } - child_allocation.width = child_requisition.width; - } - } - else - { - if (!child->xshrink) - { - gtk_sheet_set_column_width (sheet, child->col, child_requisition.width + 2 * child->xpadding); - } - child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding; - } - - if (child_requisition.height + - 2 * child->ypadding <= yyy_row_height (sheet, child->row)) - { - if (child->yfill) - { - child_requisition.height = child_allocation.height = - yyy_row_height (sheet, child->row) - 2 * child->ypadding; - } - else - { - if (child->yexpand) - { - child->y = area.y + yyy_row_height (sheet, child->row) / 2 - - child_requisition.height / 2; - } - child_allocation.height = child_requisition.height; - } - } - else - { - if (!child->yshrink) - { - gtk_sheet_set_row_height (sheet, child->row, child_requisition.height + 2 * child->ypadding); - } - child_allocation.height = yyy_row_height (sheet, child->row) - - 2 * child->ypadding; - } - } - else - { - child_allocation.width = child_requisition.width; - child_allocation.height = child_requisition.height; - } - - x = child_allocation.x = child->x + xoffset; - y = child_allocation.y = child->y + yoffset; - } - else - { - x = child_allocation.x = child->x + sheet->hoffset + xoffset; - x = child_allocation.x = child->x + xoffset; - y = child_allocation.y = child->y + sheet->voffset + yoffset; - y = child_allocation.y = child->y + yoffset; - child_allocation.width = child_requisition.width; - child_allocation.height = child_requisition.height; - } - - gtk_widget_size_allocate (child->widget, &child_allocation); - gtk_widget_queue_draw (child->widget); -} - -static void -gtk_sheet_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data) -{ - GtkSheet *sheet; - GtkSheetChild *child; - GList *children; - - g_return_if_fail (GTK_IS_SHEET (container)); - g_return_if_fail (callback != NULL); - - sheet = GTK_SHEET (container); - children = sheet->children; - while (children) - { - child = children->data; - children = children->next; - - (* callback) (child->widget, callback_data); - } - - if (sheet->button && sheet->button->parent) - (* callback) (sheet->button, callback_data); - - if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container)) - (* callback) (sheet->entry_container, callback_data); -} - - -static void -gtk_sheet_position_children (GtkSheet *sheet) -{ - GList *children; - GtkSheetChild *child; - - children = sheet->children; - - while (children) - { - child = (GtkSheetChild *)children->data; - - if (child->col != -1 && child->row != -1) - gtk_sheet_position_child (sheet, child); - - if (child->row == -1) - { - if (child->col < MIN_VISIBLE_COLUMN (sheet) || - child->col > MAX_VISIBLE_COLUMN (sheet)) - gtk_sheet_child_hide (child); - else - gtk_sheet_child_show (child); - } - if (child->col == -1) - { - if (child->row < MIN_VISIBLE_ROW (sheet) || - child->row > MAX_VISIBLE_ROW (sheet)) - gtk_sheet_child_hide (child); - else - gtk_sheet_child_show (child); - } - - children = children->next; - } -} - -static void -gtk_sheet_remove (GtkContainer *container, GtkWidget *widget) -{ - GtkSheet *sheet; - GList *children; - GtkSheetChild *child = 0; - - g_return_if_fail (container != NULL); - g_return_if_fail (GTK_IS_SHEET (container)); - - sheet = GTK_SHEET (container); - - children = sheet->children; - - while (children) - { - child = (GtkSheetChild *)children->data; - - if (child->widget == widget) break; - - children = children->next; - } - - if (children) - { - gtk_widget_unparent (widget); - child->widget = NULL; - - sheet->children = g_list_remove_link (sheet->children, children); - g_list_free_1 (children); - g_free (child); - } - - gtk_widget_unparent (sheet->button); -} - -static void -gtk_sheet_realize_child (GtkSheet *sheet, GtkSheetChild *child) -{ - GtkWidget *widget; - - widget = GTK_WIDGET (sheet); - - if (GTK_WIDGET_REALIZED (widget)) - { - if (child->row == -1) - gtk_widget_set_parent_window (child->widget, sheet->column_title_window); - else if (child->col == -1) - gtk_widget_set_parent_window (child->widget, sheet->row_title_window); - else - gtk_widget_set_parent_window (child->widget, sheet->sheet_window); - } - - gtk_widget_set_parent (child->widget, widget); -} - - - -GtkSheetChild * -gtk_sheet_get_child_at (GtkSheet *sheet, gint row, gint col) -{ - GList *children; - GtkSheetChild *child = 0; - - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - - children = sheet->children; - - while (children) - { - child = (GtkSheetChild *)children->data; - - if (child->attached_to_cell) - if (child->row == row && child->col == col) break; - - children = children->next; - } - - if (children) return child; - - return NULL; -} - -static void -gtk_sheet_child_hide (GtkSheetChild *child) -{ - g_return_if_fail (child != NULL); - gtk_widget_hide (child->widget); -} - -static void -gtk_sheet_child_show (GtkSheetChild *child) -{ - g_return_if_fail (child != NULL); - - gtk_widget_show (child->widget); -} - -GSheetModel * -gtk_sheet_get_model (const GtkSheet *sheet) -{ - g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL); - - return sheet->model; -} - - -GtkSheetButton * -gtk_sheet_button_new (void) -{ - GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton)); - - button->state = GTK_STATE_NORMAL; - button->label = NULL; - button->label_visible = TRUE; - button->child = NULL; - button->justification = GTK_JUSTIFY_FILL; - - return button; -} - - -void -gtk_sheet_button_free (GtkSheetButton *button) -{ - if (!button) return ; - - g_free (button->label); - g_free (button); -} - - -static void -append_cell_text (GString *string, const GtkSheet *sheet, gint r, gint c) -{ - gchar *celltext = gtk_sheet_cell_get_text (sheet, r, c); - - if ( NULL == celltext) - return; - - g_string_append (string, celltext); - g_free (celltext); -} - -static GString * -range_to_text (const GtkSheet *sheet) -{ - gint r, c; - GString *string; - - if ( !gtk_sheet_range_isvisible (sheet, sheet->range)) - return NULL; - - string = g_string_sized_new (80); - - for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) - { - for (c = sheet->range.col0; c < sheet->range.coli; ++c) - { - append_cell_text (string, sheet, r, c); - g_string_append (string, "\t"); - } - append_cell_text (string, sheet, r, c); - if ( r < sheet->range.rowi) - g_string_append (string, "\n"); - } - - return string; -} - -static GString * -range_to_html (const GtkSheet *sheet) -{ - gint r, c; - GString *string; - - if ( !gtk_sheet_range_isvisible (sheet, sheet->range)) - return NULL; - - string = g_string_sized_new (480); - - g_string_append (string, "\n"); - g_string_append (string, "\n"); - g_string_append (string, "\n"); - for (r = sheet->range.row0; r <= sheet->range.rowi; ++r) - { - g_string_append (string, "\n"); - for (c = sheet->range.col0; c <= sheet->range.coli; ++c) - { - g_string_append (string, "\n"); - } - g_string_append (string, "\n"); - } - g_string_append (string, "
"); - append_cell_text (string, sheet, r, c); - g_string_append (string, "
\n"); - g_string_append (string, "\n"); - g_string_append (string, "\n"); - - return string; -} - -enum { - SELECT_FMT_NULL, - SELECT_FMT_TEXT, - SELECT_FMT_HTML -}; - -static void -primary_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) -{ - GtkSheet *sheet = GTK_SHEET (data); - GString *string = NULL; - - switch (info) - { - case SELECT_FMT_TEXT: - string = range_to_text (sheet); - break; - case SELECT_FMT_HTML: - string = range_to_html (sheet); - break; - default: - g_assert_not_reached (); - } - - gtk_selection_data_set (selection_data, selection_data->target, - 8, - (const guchar *) string->str, string->len); - g_string_free (string, TRUE); -} - -static void -primary_clear_cb (GtkClipboard *clipboard, - gpointer data) -{ - GtkSheet *sheet = GTK_SHEET (data); - if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) - return; - - gtk_sheet_real_unselect_range (sheet, NULL); -} - -static void -gtk_sheet_update_primary_selection (GtkSheet *sheet) -{ - static const GtkTargetEntry targets[] = { - { "UTF8_STRING", 0, SELECT_FMT_TEXT }, - { "STRING", 0, SELECT_FMT_TEXT }, - { "TEXT", 0, SELECT_FMT_TEXT }, - { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT }, - { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT }, - { "text/plain", 0, SELECT_FMT_TEXT }, - { "text/html", 0, SELECT_FMT_HTML } - }; - - GtkClipboard *clipboard; - - if (!GTK_WIDGET_REALIZED (sheet)) - return; - - clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet), - GDK_SELECTION_PRIMARY); - - if (gtk_sheet_range_isvisible (sheet, sheet->range)) - { - if (!gtk_clipboard_set_with_owner (clipboard, targets, - G_N_ELEMENTS (targets), - primary_get_cb, primary_clear_cb, - G_OBJECT (sheet))) - primary_clear_cb (clipboard, sheet); - } - else - { - if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet)) - gtk_clipboard_clear (clipboard); - } -} - diff --git a/lib/gtksheet/gtksheet.h b/lib/gtksheet/gtksheet.h deleted file mode 100644 index 4f402252..00000000 --- a/lib/gtksheet/gtksheet.h +++ /dev/null @@ -1,694 +0,0 @@ -/* This version of GtkSheet has been heavily modified, for the specific - requirements of PSPPIRE. */ - - -/* GtkSheet widget for Gtk+. - * Copyright (C) 1999-2001 Adrian E. Feiguin - * - * Based on GtkClist widget by Jay Painter, but major changes. - * Memory allocation routines inspired on SC (Spreadsheet Calculator) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GTK_SHEET_H__ -#define __GTK_SHEET_H__ - -#include - -#include "gtkextra-sheet.h" -#include "gsheetmodel.h" -#include "gsheet-column-iface.h" -#include "gsheet-row-iface.h" - - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -typedef enum -{ - GTK_SHEET_FOREGROUND, - GTK_SHEET_BACKGROUND, - GTK_SHEET_FONT, - GTK_SHEET_JUSTIFICATION, - GTK_SHEET_BORDER, - GTK_SHEET_BORDER_COLOR, - GTK_SHEET_IS_EDITABLE, - GTK_SHEET_IS_VISIBLE -} GtkSheetAttrType; - -/* sheet->state */ - -enum -{ - GTK_SHEET_NORMAL, - GTK_SHEET_ROW_SELECTED, - GTK_SHEET_COLUMN_SELECTED, - GTK_SHEET_RANGE_SELECTED -}; - - -#define GTK_TYPE_SHEET_RANGE (gtk_sheet_range_get_type ()) -#define GTK_TYPE_SHEET (gtk_sheet_get_type ()) - -#define GTK_SHEET(obj) GTK_CHECK_CAST (obj, gtk_sheet_get_type (), GtkSheet) -#define GTK_SHEET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_sheet_get_type (), GtkSheetClass) -#define GTK_IS_SHEET(obj) GTK_CHECK_TYPE (obj, gtk_sheet_get_type ()) - -/* Public flags, for compatibility */ - -#define GTK_SHEET_ROW_FROZEN(sheet) !gtk_sheet_rows_resizable (sheet) -#define GTK_SHEET_COLUMN_FROZEN(sheet) !gtk_sheet_columns_resizable (sheet) -#define GTK_SHEET_AUTORESIZE(sheet) gtk_sheet_autoresize (sheet) -#define GTK_SHEET_ROW_TITLES_VISIBLE(sheet) gtk_sheet_row_titles_visible (sheet) -#define GTK_SHEET_COL_TITLES_VISIBLE(sheet) gtk_sheet_column_titles_visible (sheet) -#define GTK_SHEET_AUTO_SCROLL(sheet) gtk_sheet_autoscroll (sheet) -#define GTK_SHEET_JUSTIFY_ENTRY(sheet) gtk_sheet_justify_entry (sheet) - - -typedef struct _GtkSheetClass GtkSheetClass; -typedef struct _GtkSheetCellAttr GtkSheetCellAttr; -typedef struct _GtkSheetCell GtkSheetCell; -typedef struct _GtkSheetHoverTitle GtkSheetHoverTitle; - - -struct _GtkSheetCellAttr -{ - GtkJustification justification; - const PangoFontDescription *font_desc; - GdkColor foreground; - GdkColor background; - GtkSheetCellBorder border; - gboolean is_editable; - gboolean is_visible; -}; - -struct _GtkSheetCell -{ - gint row; - gint col; -}; - -struct _GtkSheetHoverTitle -{ - GtkWidget *window; - GtkWidget *label; - gint row, column; -}; - -struct _GtkSheet -{ - GtkContainer container; - - - gboolean dispose_has_run; - GSheetColumn *column_geometry; - GSheetRow *row_geometry; - - guint16 flags; - - GSheetModel *model; - - GtkSelectionMode selection_mode; - gboolean autoresize; - gboolean autoscroll; - gboolean justify_entry; - - /* Background colors */ - GdkColor bg_color; - GdkColor grid_color; - gboolean show_grid; - - /* sheet children */ - GList *children; - - /* allocation rectangle after the container_border_width - and the width of the shadow border */ - GdkRectangle internal_allocation; - - gint16 column_requisition; - gint16 row_requisition; - - gboolean rows_resizable; - gboolean columns_resizable; - - /* active cell */ - GtkSheetCell active_cell; - - /* The GtkEntry used for editing the cells */ - GtkWidget *entry_widget; - - /* The widget containing entry_widget, or - entry_widget itself if no container */ - GtkWidget *entry_container; - - /* The type of entry_widget */ - GtkType entry_type; - - /* expanding selection */ - GtkSheetCell selection_cell; - - /* global selection button */ - GtkWidget *button; - - /* sheet state */ - gint state; - - /* selected range */ - GtkSheetRange range; - - /*the scrolling window and it's height and width to - * make things a little speedier */ - GdkWindow *sheet_window; - guint sheet_window_width; - guint sheet_window_height; - - /* sheet backing pixmap */ - GdkPixmap *pixmap; - - /* offsets for scrolling */ - gint hoffset; - gint voffset; - gfloat old_hadjustment; - gfloat old_vadjustment; - - /* border shadow style */ - GtkShadowType shadow_type; - - /* Column Titles */ - GdkRectangle column_title_area; - GdkWindow *column_title_window; - gboolean column_titles_visible; - - /* Row Titles */ - GdkRectangle row_title_area; - GdkWindow *row_title_window; - gboolean row_titles_visible; - - /*scrollbars*/ - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - - gint freeze_count; - - /* xor GC for the verticle drag line */ - GdkGC *xor_gc; - - /* gc for drawing unselected cells */ - GdkGC *fg_gc; - GdkGC *bg_gc; - - /* cursor used to indicate dragging */ - GdkCursor *cursor_drag; - - /* the current x-pixel location of the xor-drag vline */ - gint x_drag; - - /* the current y-pixel location of the xor-drag hline */ - gint y_drag; - - /* current cell being dragged */ - GtkSheetCell drag_cell; - /* current range being dragged */ - GtkSheetRange drag_range; - - /* Used for the subtitle (popups) */ - gint motion_timer; - GtkSheetHoverTitle *hover_window; -}; - -struct _GtkSheetClass -{ - GtkContainerClass parent_class; - - void (*set_scroll_adjustments) (GtkSheet *sheet, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); - - void (*select_row) (GtkSheet *sheet, gint row); - - void (*select_column) (GtkSheet *sheet, gint column); - - void (*select_range) (GtkSheet *sheet, GtkSheetRange *range); - - void (*resize_range) (GtkSheet *sheet, - GtkSheetRange *old_range, - GtkSheetRange *new_range); - - void (*move_range) (GtkSheet *sheet, - GtkSheetRange *old_range, - GtkSheetRange *new_range); - - gboolean (*traverse) (GtkSheet *sheet, - gint row, gint column, - gint *new_row, gint *new_column); - - gboolean (*deactivate) (GtkSheet *sheet, - gint row, gint column); - - gboolean (*activate) (GtkSheet *sheet, - gint row, gint column); - - void (*changed) (GtkSheet *sheet, - gint row, gint column); -}; - -GType gtk_sheet_get_type (void); -GtkType gtk_sheet_range_get_type (void); - - -/* create a new sheet */ -GtkWidget * gtk_sheet_new (GSheetRow *vgeo, GSheetColumn *hgeo, - GSheetModel *model); - - - - -/* create a new browser sheet. It cells can not be edited */ -GtkWidget * -gtk_sheet_new_browser (guint rows, guint columns, const gchar *title); - -void -gtk_sheet_construct_browser (GtkSheet *sheet, - guint rows, guint columns, const gchar *title); - -/* create a new sheet with custom entry */ -GtkWidget * -gtk_sheet_new_with_custom_entry (GSheetRow *vgeo, - GSheetColumn *hgeo, - GtkType entry_type); -void -gtk_sheet_construct_with_custom_entry (GtkSheet *sheet, - GSheetRow *vgeo, - GSheetColumn *hgeo, - GtkType entry_type); -/* change scroll adjustments */ -void -gtk_sheet_set_hadjustment (GtkSheet *sheet, - GtkAdjustment *adjustment); -void -gtk_sheet_set_vadjustment (GtkSheet *sheet, - GtkAdjustment *adjustment); -/* Change entry */ -void -gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type); - -/* Returns sheet's entry widget */ -GtkWidget * -gtk_sheet_get_entry (GtkSheet *sheet); -GtkWidget * -gtk_sheet_get_entry_widget (GtkSheet *sheet); - -/* Returns sheet->state - * Added by Steven Rostedt */ -gint -gtk_sheet_get_state (GtkSheet *sheet); - -/* Returns sheet's ranges - * Added by Murray Cumming */ -guint -gtk_sheet_get_columns_count (GtkSheet *sheet); - -guint -gtk_sheet_get_rows_count (GtkSheet *sheet); - -void -gtk_sheet_get_visible_range (GtkSheet *sheet, - GtkSheetRange *range); - -void -gtk_sheet_get_selected_range (GtkSheet *sheet, - GtkSheetRange *range); - -void -gtk_sheet_set_selection_mode (GtkSheet *sheet, gint mode); - -void -gtk_sheet_set_autoresize (GtkSheet *sheet, gboolean autoresize); - -gboolean -gtk_sheet_autoresize (GtkSheet *sheet); - -void -gtk_sheet_set_autoscroll (GtkSheet *sheet, gboolean autoscroll); - -gboolean -gtk_sheet_autoscroll (GtkSheet *sheet); - -void -gtk_sheet_set_justify_entry (GtkSheet *sheet, gboolean justify); - -gboolean -gtk_sheet_justify_entry (GtkSheet *sheet); - -void -gtk_sheet_set_locked (GtkSheet *sheet, gboolean lock); - -gboolean -gtk_sheet_locked (const GtkSheet *sheet); - -/* set sheet title */ -void -gtk_sheet_set_title (GtkSheet *sheet, const gchar *title); - -/* freeze all visual updates of the sheet. - * Then thaw the sheet after you have made a number of changes. - * The updates will occure in a more efficent way than if - * you made them on a unfrozen sheet */ -void -gtk_sheet_freeze (GtkSheet *sheet); -void -gtk_sheet_thaw (GtkSheet *sheet); -/* Background colors */ -void -gtk_sheet_set_background (GtkSheet *sheet, - GdkColor *bg_color); -void -gtk_sheet_set_grid (GtkSheet *sheet, - GdkColor *grid_color); -void -gtk_sheet_show_grid (GtkSheet *sheet, - gboolean show); -gboolean -gtk_sheet_grid_visible (GtkSheet *sheet); - -/* set/get column title */ -void -gtk_sheet_set_column_title (GtkSheet * sheet, - gint column, - const gchar * title); - -const gchar * -gtk_sheet_get_column_title (GtkSheet * sheet, - gint column); - -/* set/get row title */ -void -gtk_sheet_set_row_title (GtkSheet * sheet, - gint row, - const gchar * title); -const gchar * -gtk_sheet_get_row_title (GtkSheet * sheet, - gint row); - - -/* set/get button label */ -void -gtk_sheet_row_button_add_label (GtkSheet *sheet, - gint row, const gchar *label); -const gchar * -gtk_sheet_row_button_get_label (GtkSheet *sheet, - gint row); -void -gtk_sheet_row_button_justify (GtkSheet *sheet, - gint row, GtkJustification justification); - - - -/* scroll the viewing area of the sheet to the given column - * and row; row_align and col_align are between 0-1 representing the - * location the row should appear on the screen, 0.0 being top or left, - * 1.0 being bottom or right; if row or column is negative then there - * is no change */ -void -gtk_sheet_moveto (GtkSheet *sheet, - gint row, - gint column, - gfloat row_align, - gfloat col_align); - - -void -gtk_sheet_show_row_titles (GtkSheet *sheet); -void -gtk_sheet_hide_row_titles (GtkSheet *sheet); -void -gtk_sheet_show_column_titles (GtkSheet *sheet); -void -gtk_sheet_hide_column_titles (GtkSheet *sheet); - -gboolean -gtk_sheet_row_titles_visible (GtkSheet *sheet); - - -/* set row button sensitivity. If sensitivity is TRUE can be toggled, - * otherwise it acts as a title */ -void -gtk_sheet_row_set_sensitivity (GtkSheet *sheet, - gint row, gboolean sensitive); - -/* set sensitivity for all row buttons */ -void -gtk_sheet_rows_set_sensitivity (GtkSheet *sheet, gboolean sensitive); -void -gtk_sheet_rows_set_resizable (GtkSheet *sheet, gboolean resizable); -gboolean -gtk_sheet_rows_resizable (GtkSheet *sheet); - -/* set row visibility. The default value is TRUE. If FALSE, the - * row is hidden */ -void -gtk_sheet_row_set_visibility (GtkSheet *sheet, - gint row, gboolean visible); -void -gtk_sheet_row_label_set_visibility (GtkSheet *sheet, - gint row, gboolean visible); -void -gtk_sheet_rows_labels_set_visibility (GtkSheet *sheet, gboolean visible); - - -/* select the row. The range is then highlighted, and the bounds are stored - * in sheet->range */ -void -gtk_sheet_select_row (GtkSheet * sheet, - gint row); - -/* select the column. The range is then highlighted, and the bounds are stored - * in sheet->range */ -void -gtk_sheet_select_column (GtkSheet * sheet, - gint column); - -/* get scrollbars adjustment */ -GtkAdjustment * -gtk_sheet_get_vadjustment (GtkSheet * sheet); -GtkAdjustment * -gtk_sheet_get_hadjustment (GtkSheet * sheet); - -/* highlight the selected range and store bounds in sheet->range */ -void gtk_sheet_select_range (GtkSheet *sheet, - const GtkSheetRange *range); - -/* obvious */ -void gtk_sheet_unselect_range (GtkSheet *sheet); - -/* set active cell where the entry will be displayed - * returns FALSE if current cell can't be deactivated or - * requested cell can't be activated */ -gboolean -gtk_sheet_set_active_cell (GtkSheet *sheet, - gint row, gint column); - -/* Sets *ROW and *COLUMN to be the coordinates of the active cell. - ROW and/or COLUMN may be null if the caller is not interested in their - values */ -void -gtk_sheet_get_active_cell (GtkSheet *sheet, - gint *row, gint *column); - -/* set cell contents and allocate memory if needed */ -void -gtk_sheet_set_cell (GtkSheet *sheet, - gint row, gint col, - GtkJustification justification, - const gchar *text); -void -gtk_sheet_set_cell_text (GtkSheet *sheet, - gint row, gint col, - const gchar *text); -/* get cell contents */ -gchar * -gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col); - -/* clear cell contents */ -void -gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint col); - -/* clear range contents. If range==NULL the whole sheet will be cleared */ -void -gtk_sheet_range_clear (GtkSheet *sheet, - const GtkSheetRange *range); - -/* get cell state: GTK_STATE_NORMAL, GTK_STATE_SELECTED */ -GtkStateType -gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col); - -/* get row and column correspondig to the given position in the screen */ -gboolean -gtk_sheet_get_pixel_info (GtkSheet * sheet, - gint x, - gint y, - gint * row, - gint * column); - -/* get area of a given cell */ -gboolean -gtk_sheet_get_cell_area (GtkSheet *sheet, - gint row, - gint column, - GdkRectangle *area); - -/* set row height */ -void -gtk_sheet_set_row_height (GtkSheet * sheet, - gint row, - guint height); - - -/* delete nrows rows starting in row */ -void -gtk_sheet_delete_rows (GtkSheet *sheet, guint row, guint nrows); - -/* append nrows row to the end of the sheet */ -void -gtk_sheet_add_row (GtkSheet *sheet, guint nrows); - -/* insert nrows rows before the given row and pull right */ -void -gtk_sheet_insert_rows (GtkSheet *sheet, guint row, guint nrows); - -/* set abckground color of the given range */ -void -gtk_sheet_range_set_background (GtkSheet *sheet, - const GtkSheetRange *range, - const GdkColor *color); - -/* set foreground color (text color) of the given range */ -void -gtk_sheet_range_set_foreground (GtkSheet *sheet, - const GtkSheetRange *range, - const GdkColor *color); - -/* set text justification (GTK_JUSTIFY_LEFT, RIGHT, CENTER) of the given range. - * The default value is GTK_JUSTIFY_LEFT. If autoformat is on, the - * default justification for numbers is GTK_JUSTIFY_RIGHT */ -void -gtk_sheet_range_set_justification (GtkSheet *sheet, - const GtkSheetRange *range, - GtkJustification justification); -void -gtk_sheet_column_set_justification (GtkSheet *sheet, - gint column, - GtkJustification justification); -/* set if cell contents can be edited or not in the given range: - * accepted values are TRUE or FALSE. */ -void -gtk_sheet_range_set_editable (GtkSheet *sheet, - const GtkSheetRange *range, - gint editable); - -/* set if cell contents are visible or not in the given range: - * accepted values are TRUE or FALSE.*/ -void -gtk_sheet_range_set_visible (GtkSheet *sheet, - const GtkSheetRange *range, - gboolean visible); - -/* set cell border style in the given range. - * mask values are CELL_LEFT_BORDER, CELL_RIGHT_BORDER, CELL_TOP_BORDER, - * CELL_BOTTOM_BORDER - * width is the width of the border line in pixels - * line_style is the line_style for the border line */ -void -gtk_sheet_range_set_border (GtkSheet *sheet, - const GtkSheetRange *range, - gint mask, - guint width, - gint line_style); - -/* set border color for the given range */ -void -gtk_sheet_range_set_border_color (GtkSheet *sheet, - const GtkSheetRange *range, - const GdkColor *color); - -/* set font for the given range */ -void -gtk_sheet_range_set_font (GtkSheet *sheet, - const GtkSheetRange *range, - PangoFontDescription *font); - -/* get cell attributes of the given cell */ -/* TRUE means that the cell is currently allocated */ -gboolean -gtk_sheet_get_attributes (const GtkSheet *sheet, - gint row, gint col, - GtkSheetCellAttr *attributes); - - -GtkSheetChild * -gtk_sheet_put (GtkSheet *sheet, - GtkWidget *widget, - gint x, gint y); -void -gtk_sheet_attach_floating (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col); -void -gtk_sheet_attach_default (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col); -void -gtk_sheet_attach (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col, - gint xoptions, - gint yoptions, - gint xpadding, - gint ypadding); - - -void -gtk_sheet_move_child (GtkSheet *sheet, - GtkWidget *widget, - gint x, gint y); - -GtkSheetChild * -gtk_sheet_get_child_at (GtkSheet *sheet, - gint row, gint col); - -void -gtk_sheet_button_attach (GtkSheet *sheet, - GtkWidget *widget, - gint row, gint col); - - - -void gtk_sheet_set_model (GtkSheet *sheet, - GSheetModel *model); - -GSheetModel * gtk_sheet_get_model (const GtkSheet *sheet); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -#endif /* __GTK_SHEET_H__ */ - - diff --git a/lib/linreg/automake.mk b/lib/linreg/automake.mk index 650646c6..30fd2e59 100644 --- a/lib/linreg/automake.mk +++ b/lib/linreg/automake.mk @@ -1,8 +1,8 @@ ## Process this file with automake to produce Makefile.in -*- makefile -*- -noinst_LIBRARIES += lib/linreg/liblinreg.a +noinst_LTLIBRARIES += lib/linreg/liblinreg.la -lib_linreg_liblinreg_a_SOURCES = \ +lib_linreg_liblinreg_la_SOURCES = \ lib/linreg/sweep.c lib/linreg/sweep.h EXTRA_DIST += lib/linreg/OChangeLog diff --git a/perl-module/COPYING b/perl-module/COPYING new file mode 100644 index 00000000..44325404 --- /dev/null +++ b/perl-module/COPYING @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/perl-module/Changes b/perl-module/Changes new file mode 100644 index 00000000..5919ad62 --- /dev/null +++ b/perl-module/Changes @@ -0,0 +1,13 @@ +Revision history for Perl extension Pspp. + +0.7.0 Sun Jan 11 10:35:56 2009 + Integrated the module into Pspp's build environment. Added support for + reading from an existing system file. + +0.4.3 Sat May 19 14:24:05 2007 + - first release + +0.01 Fri Apr 6 14:13:45 2007 + - original version; created by h2xs 1.23 with options + -A -n Pspp pspp/src/libpspp/version.h + diff --git a/perl-module/Examples.pod b/perl-module/Examples.pod new file mode 100644 index 00000000..cb286ca1 --- /dev/null +++ b/perl-module/Examples.pod @@ -0,0 +1,146 @@ +=pod + +=head1 PSPP::Examples + +This page shows some simple examples of using the PSPP module. +See L for details on each of the subroutines. + +=head2 A Simple example + +This example creates a system file called F, containing one +variable called "id". It contains no data. + + use PSPP; + + my $dict = PSPP::Dict->new (); + my $var = PSPP::Var->new ($dict, "id"); + + my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict); + $sysfile->close(); + + +=head2 A slightly more complex example + +In this example there are three variables, called "id", "name" and "dob". +Their formats are F2.0, A80 and DATETIME17 respectively. + + use PSPP; + + my $dict = PSPP::Dict->new (); + PSPP::Var->new ($dict, "id", + (fmt=>PSPP::Fmt::F, width=>2, decimals=>0) ); + + PSPP::Var->new ($dict, "name", (fmt=>PSPP::Fmt::A, width=>80) ); + PSPP::Var->new ($dict, "dob", (fmt=>PSPP::Fmt::DATETIME) ); + + my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict); + $sysfile->close(); + +=head2 Changing the properties of variables + +After a variable has been created, parameters may be set for it. + + use PSPP; + + my $dict = PSPP::Dict->new (); + my $var1 = PSPP::Var->new ($dict, "id"); + + $var1->set_label ("A unique identifier"); + $var1->add_value_label (0, "Zero"); + $var1->add_value_label (1, "One"); + + +=head2 Appending data to the file + +When a file is created, it contains no data. Data is added by +appending cases to the file. + +This example creates a file with 3 cases. + + use PSPP; + + my $dict = PSPP::Dict->new (); + PSPP::Var->new ($dict, "id", + (fmt=>PSPP::Fmt::F, width=>2, decimals=>0) ); + + PSPP::Var->new ($dict, "name", (fmt=>PSPP::Fmt::A, width=>8) ); + + my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict); + + $sysfile->append_case ( [1, "Alf"] ); + $sysfile->append_case ( [2, "Bert"] ); + $sysfile->append_case ( [3, "Charlie"] ); + + $sysfile->close(); + +=head2 Variables with differing input and output formats + +By default, a variable's output format corresponds to the input format. +However, the output format may be changed after the variable has +been created. + +This example shows how to create a DATETIME variable using the current time +as its value. Since pspp uses a different epoch to perl, the constant +PSPP::PERL_EPOCH needs to be added to the value returned from time(), in order +that it be correctly represented by pspp. + + use PSPP; + + my $dict = PSPP::Dict->new (); + + my $var1 = PSPP::Var->new ($dict, "entrytime", + (fmt=>PSPP::Fmt::F) ); + + $var1->set_output_format ( (fmt=>PSPP::Fmt::DATETIME, width=>20) ); + + my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict); + + my $now = time (); + + $sysfile->append_case ( [ $now + PSPP::PERL_EPOCH] ) + || die "Cant write case"; + + $sysfile->close(); + +=head2 Reading data + +Data can be read from a system file or other source: + + use PSPP; + + my $sf = PSPP::Reader->open ("foo.sav"); + + my $dict = $sf->get_dict (); + + +Once opened, the dictionary can be used like any other. + + for ($v = 0 ; $v < $dict->get_var_cnt() ; $v++) + { + my $var = $dict->get_var ($v); + + # Print the variables + my $name = $var->get_name (); + my $label = $var->get_label (); + print "Var: $name, Label: $label\n"; + + # Retrieve and print the value labels + my $vl = $var->get_value_labels (); + print "$_: $vl->{$_}\n" for keys %$vl; + } + + +Reading of data must be done sequentially using the C method. + + while (my $c = $sf->get_next_case () ) + { + my $v; + for ($v = 0; $v < $dict->get_var_cnt(); $v++) + { + print "val$v: @$c[$v] "; + } + print "\n"; + } + + +=cut \ No newline at end of file diff --git a/perl-module/MANIFEST b/perl-module/MANIFEST new file mode 100644 index 00000000..25469055 --- /dev/null +++ b/perl-module/MANIFEST @@ -0,0 +1,13 @@ +Changes +const-c.inc +const-xs.inc +COPYING +Examples.pod +lib/PSPP.pm +Makefile.PL +MANIFEST +ppport.h +PSPP.xs +README +t/Pspp.t +typemap diff --git a/perl-module/Makefile.PL b/perl-module/Makefile.PL new file mode 100644 index 00000000..045b4e76 --- /dev/null +++ b/perl-module/Makefile.PL @@ -0,0 +1,52 @@ +use 5.008008; +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. + + +do 'pspp-module-config' || do { + my $build = prompt ("Enter the location of the build directory of the configured pspp source:", "" ); + my $src = $top_srcdir; + + %Locations = (SourceDir => "$src", BuildDir => "$build"); +}; + +WriteMakefile( + FULLPERL => "PSPP_TEST_CMD=\"$Locations{BuildDir}/src/ui/terminal/pspp -B $Locations{SourceDir}/config\" \$(PERL)", + NAME => 'PSPP', + DISTNAME => 'PSPP-Perl', + VERSION_FROM => "$Locations{BuildDir}/src/libpspp/version.c", + PREREQ_PM => {POSIX=>0}, + PM => {"lib/PSPP.pm", "\$(INST_LIBDIR)/PSPP.pm"}, + ($] >= 5.005 ? ## Add these new keywords supported since 5.005 + (ABSTRACT_FROM => 'lib/PSPP.pm', # retrieve abstract from module + AUTHOR => 'John Darrington ') : ()), + INC => "-I $Locations{SourceDir} -I $Locations{SourceDir}/src -I $Locations{SourceDir}/gl -I $Locations{BuildDir}/gl -I $Locations{BuildDir}", + MYEXTLIB => "$Locations{BuildDir}/src/.libs/libpspp-core.\$(DLEXT)", + MAN3PODS => {"lib/PSPP.pm", "\$(INST_MAN3DIR)/PSPP.3pm", + "Examples.pod", "\$(INST_MAN3DIR)/PSPP::Examples.3pm"} +); + +if (eval {require ExtUtils::Constant; 1}) { + # If you edit these definitions to change the constants used by this module, + # you will need to use the generated const-c.inc and const-xs.inc + # files to replace their "fallback" counterparts before distributing your + # changes. + my @names = (qw()); + ExtUtils::Constant::WriteConstants( + NAME => 'PSPP', + NAMES => \@names, + DEFAULT_TYPE => 'IV', + C_FILE => 'const-c.inc', + XS_FILE => 'const-xs.inc', + ); + +} +else { + use File::Copy; + use File::Spec; + foreach my $file ('const-c.inc', 'const-xs.inc') { + my $fallback = File::Spec->catfile('fallback', $file); + copy ($fallback, $file) or die "Can't copy $fallback to $file: $!"; + } +} diff --git a/perl-module/PSPP.bs b/perl-module/PSPP.bs new file mode 100644 index 00000000..e69de29b diff --git a/perl-module/PSPP.xs b/perl-module/PSPP.xs new file mode 100644 index 00000000..237ae95f --- /dev/null +++ b/perl-module/PSPP.xs @@ -0,0 +1,694 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ + + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include + +#include "ppport.h" + +#include "minmax.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct fmt_spec input_format ; +typedef struct fmt_spec output_format ; + + +/* A thin wrapper around sfm_writer */ +struct sysfile_info +{ + bool opened; + + /* A pointer to the writer. The writer is owned by the struct */ + struct casewriter *writer; + + /* A pointer to the dictionary. Owned externally */ + const struct dictionary *dict; + + /* The scalar containing the dictionary */ + SV *dict_sv; +}; + + +/* A thin wrapper around sfm_reader */ +struct sysreader_info +{ + struct sfm_read_info opts; + + /* A pointer to the reader. The reader is owned by the struct */ + struct casereader *reader; + + /* A pointer to the dictionary. */ + struct dictionary *dict; +}; + + + +/* A message handler which writes messages to PSPP::errstr */ +static void +message_handler (const struct msg *m) +{ + SV *errstr = get_sv("PSPP::errstr", TRUE); + sv_setpv (errstr, m->text); +} + +static int +sysfile_close (struct sysfile_info *sfi) +{ + int retval ; + if ( ! sfi->opened ) + return 0; + + retval = casewriter_destroy (sfi->writer); + if (retval > 0 ) + sfi->opened = false; + + return retval; +} + +static void +scalar_to_value (union value *val, SV *scalar, const struct variable *var) +{ + if ( var_is_numeric (var)) + { + if ( SvNOK (scalar) || SvIOK (scalar) ) + val->f = SvNV (scalar); + else + val->f = SYSMIS; + } + else + { + STRLEN len; + const char *p = SvPV (scalar, len); + memset (val->s, ' ', var_get_width (var)); + memcpy (val->s, p, len); + } +} + + +static SV * +value_to_scalar (const union value *val, const struct variable *var) +{ + if ( var_is_numeric (var)) + { + if ( var_is_value_missing (var, val, MV_SYSTEM)) + return newSVpvn ("", 0); + + return newSVnv (val->f); + } + else + return newSVpvn (val->s, var_get_width (var)); +} + + +static void +var_set_input_format (struct variable *v, input_format ip_fmt) +{ + struct fmt_spec *if_copy = malloc (sizeof (*if_copy)); + memcpy (if_copy, &ip_fmt, sizeof (ip_fmt)); + var_attach_aux (v, if_copy, var_dtor_free); +} + +static union value * +make_value_from_scalar (SV *val, const struct variable *var) +{ + union value *uv = value_create (var_get_width (var)); + scalar_to_value (uv, val, var); + return uv; +} + + +MODULE = PSPP + +MODULE = PSPP PACKAGE = PSPP + +void +onBoot (ver) + const char *ver +CODE: + assert (0 == strcmp (ver, bare_version)); + msg_init (NULL, message_handler); + settings_init (0, 0); + fh_init (); + +SV * +format_value (val, var) + SV *val + struct variable *var +CODE: + SV *ret; + const struct fmt_spec *fmt = var_get_print_format (var); + union value *uv = make_value_from_scalar (val, var); + char *s; + s = malloc (fmt->w); + memset (s, '\0', fmt->w); + data_out (uv, fmt, s); + free (uv); + ret = newSVpv (s, fmt->w); + free (s); + RETVAL = ret; + OUTPUT: +RETVAL + + +int +value_is_missing (val, var) + SV *val + struct variable *var +CODE: + union value *uv = make_value_from_scalar (val, var); + int ret = var_is_value_missing (var, uv, MV_ANY); + free (uv); + RETVAL = ret; + OUTPUT: +RETVAL + + + +MODULE = PSPP PACKAGE = PSPP::Dict + +struct dictionary * +pxs_dict_new() +CODE: + RETVAL = dict_create (); +OUTPUT: + RETVAL + + +void +DESTROY (dict) + struct dictionary *dict +CODE: + dict_destroy (dict); + + +int +get_var_cnt (dict) + struct dictionary *dict +CODE: + RETVAL = dict_get_var_cnt (dict); +OUTPUT: +RETVAL + +void +set_label (dict, label) + struct dictionary *dict + char *label +CODE: + dict_set_label (dict, label); + +void +set_documents (dict, docs) + struct dictionary *dict + char *docs +CODE: + dict_set_documents (dict, docs); + + +void +add_document (dict, doc) + struct dictionary *dict + char *doc +CODE: + dict_add_document_line (dict, doc); + + +void +clear_documents (dict) + struct dictionary *dict +CODE: + dict_clear_documents (dict); + + +void +set_weight (dict, var) + struct dictionary *dict + struct variable *var +CODE: + dict_set_weight (dict, var); + + +struct variable * +pxs_get_variable (dict, idx) + struct dictionary *dict + SV *idx +INIT: + SV *errstr = get_sv("PSPP::errstr", TRUE); + sv_setpv (errstr, ""); + if ( SvIV (idx) >= dict_get_var_cnt (dict)) + { + sv_setpv (errstr, "The dictionary doesn't have that many variables."); + XSRETURN_UNDEF; + } +CODE: + RETVAL = dict_get_var (dict, SvIV (idx)); + OUTPUT: +RETVAL + + +struct variable * +pxs_get_var_by_name (dict, name) + struct dictionary *dict + const char *name +INIT: + SV *errstr = get_sv("PSPP::errstr", TRUE); + sv_setpv (errstr, ""); +CODE: + struct variable *var = dict_lookup_var (dict, name); + if ( ! var ) + sv_setpv (errstr, "No such variable."); + RETVAL = var; + OUTPUT: +RETVAL + + +MODULE = PSPP PACKAGE = PSPP::Var + + +struct variable * +pxs_dict_create_var (dict, name, ip_fmt) + struct dictionary * dict + char *name + input_format ip_fmt +INIT: + SV *errstr = get_sv("PSPP::errstr", TRUE); + sv_setpv (errstr, ""); + if ( ! var_is_plausible_name (name, false)) + { + sv_setpv (errstr, "The variable name is not valid."); + XSRETURN_UNDEF; + } +CODE: + struct fmt_spec op_fmt; + + struct variable *v; + op_fmt = fmt_for_output_from_input (&ip_fmt); + v = dict_create_var (dict, name, + fmt_is_string (op_fmt.type) ? op_fmt.w : 0); + if ( NULL == v ) + { + sv_setpv (errstr, "The variable could not be created (probably already exists)."); + XSRETURN_UNDEF; + } + var_set_both_formats (v, &op_fmt); + var_set_input_format (v, ip_fmt); + RETVAL = v; +OUTPUT: + RETVAL + + +int +set_missing_values (var, v1, ...) + struct variable *var; + SV *v1; +INIT: + int i; + union value val[3]; + + if ( items > 4 ) + croak ("No more than 3 missing values are permitted"); + + for (i = 0; i < items - 1; ++i) + scalar_to_value (&val[i], ST(i+1), var); +CODE: + struct missing_values mv; + mv_init (&mv, var_get_width (var)); + for (i = 0 ; i < items - 1; ++i ) + mv_add_value (&mv, &val[i]); + var_set_missing_values (var, &mv); + + +void +set_label (var, label) + struct variable *var; + char *label +CODE: + var_set_label (var, label); + + +void +clear_value_labels (var) + struct variable *var; +CODE: + var_clear_value_labels (var); + +void +pxs_set_write_format (var, fmt) + struct variable *var + output_format fmt +CODE: + var_set_write_format (var, &fmt); + + +void +pxs_set_print_format (var, fmt) + struct variable *var + output_format fmt +CODE: + var_set_print_format (var, &fmt); + +void +pxs_set_output_format (var, fmt) + struct variable *var + output_format fmt +CODE: + var_set_both_formats (var, &fmt); + + +int +add_value_label (var, key, label) + struct variable *var + SV *key + char *label +INIT: + SV *errstr = get_sv("PSPP::errstr", TRUE); + sv_setpv (errstr, ""); +CODE: + union value the_value; + + if ( var_is_numeric (var)) + { + if ( ! looks_like_number (key)) + { + sv_setpv (errstr, "Cannot add label with string key to a numeric variable"); + XSRETURN_IV (0); + } + the_value.f = SvNV (key); + } + else + { + if ( var_is_long_string (var) ) + { + sv_setpv (errstr, "Cannot add label to a long string variable"); + XSRETURN_IV (0); + } + strncpy (the_value.s, SvPV_nolen(key), MAX_SHORT_STRING); + } + if (! var_add_value_label (var, &the_value, label) ) + { + sv_setpv (errstr, "Something went wrong"); + XSRETURN_IV (0); + } + XSRETURN_IV (1); + + +SV * +get_attributes (var) + struct variable *var +CODE: + HV *attrhash = (HV *) sv_2mortal ((SV *) newHV()); + + struct attrset *as = var_get_attributes (var); + + if ( as ) + { + struct attrset_iterator iter; + struct attribute *attr; + + for (attr = attrset_first (as, &iter); + attr; + attr = attrset_next (as, &iter)) + { + int i; + const char *name = attribute_get_name (attr); + + AV *values = newAV (); + + for (i = 0 ; i < attribute_get_n_values (attr); ++i ) + { + const char *value = attribute_get_value (attr, i); + av_push (values, newSVpv (value, 0)); + } + + hv_store (attrhash, name, strlen (name), + newRV_noinc ((SV*) values), 0); + } + } + + RETVAL = newRV ((SV *) attrhash); + OUTPUT: +RETVAL + + +const char * +get_name (var) + struct variable * var +CODE: + RETVAL = var_get_name (var); + OUTPUT: +RETVAL + + +const char * +get_label (var) + struct variable * var +CODE: + RETVAL = var_get_label (var); + OUTPUT: +RETVAL + + +SV * +get_value_labels (var) + struct variable *var +CODE: + HV *labelhash = (HV *) sv_2mortal ((SV *) newHV()); + struct val_lab *vl; + struct val_labs_iterator *viter = NULL; + const struct val_labs *labels = var_get_value_labels (var); + + if ( labels ) + { + for (vl = val_labs_first (labels, &viter); + vl; + vl = val_labs_next (labels, &viter)) + { + SV *sv = value_to_scalar (&vl->value, var); + STRLEN len; + const char *s = SvPV (sv, len); + hv_store (labelhash, s, len, newSVpv (vl->label, 0), 0); + } + } + + RETVAL = newRV ((SV *) labelhash); + OUTPUT: +RETVAL + + + +MODULE = PSPP PACKAGE = PSPP::Sysfile + + +struct sysfile_info * +pxs_create_sysfile (name, dict_ref, opts_hr) + char *name + SV *dict_ref + SV *opts_hr +INIT: + SV *dict_sv = SvRV (dict_ref); + struct dictionary *dict = (void *) SvIV (dict_sv); + struct sfm_write_options opts; + if (!SvROK (opts_hr)) + { + opts = sfm_writer_default_options (); + } + else + { + HV *opt_h = (HV *) SvRV (opts_hr); + SV** readonly = hv_fetch(opt_h, "readonly", 8, 0); + SV** compress = hv_fetch(opt_h, "compress", 8, 0); + SV** version = hv_fetch(opt_h, "version", 7, 0); + + opts.create_writeable = readonly ? ! SvIV (*readonly) : true; + opts.compress = compress ? SvIV (*compress) : false; + opts.version = version ? SvIV (*version) : 3 ; + } +CODE: + struct file_handle *fh = + fh_create_file (NULL, name, fh_default_properties () ); + struct sysfile_info *sfi = xmalloc (sizeof (*sfi)); + sfi->writer = sfm_open_writer (fh, dict, opts); + sfi->dict = dict; + sfi->opened = true; + sfi->dict_sv = dict_sv; + SvREFCNT_inc (sfi->dict_sv); + + RETVAL = sfi; + OUTPUT: +RETVAL + +int +close (sfi) + struct sysfile_info *sfi +CODE: + RETVAL = sysfile_close (sfi); +OUTPUT: + RETVAL + +void +DESTROY (sfi) + struct sysfile_info *sfi +CODE: + sysfile_close (sfi); + SvREFCNT_dec (sfi->dict_sv); + free (sfi); + +int +append_case (sfi, ccase) + struct sysfile_info *sfi + SV *ccase +INIT: + SV *errstr = get_sv("PSPP::errstr", TRUE); + sv_setpv (errstr, ""); + if ( (!SvROK(ccase))) + { + XSRETURN_UNDEF; + } +CODE: + int i = 0; + AV *av_case = (AV*) SvRV (ccase); + + const struct variable **vv; + size_t nv; + struct ccase *c; + SV *sv; + + if ( av_len (av_case) >= dict_get_var_cnt (sfi->dict)) + XSRETURN_UNDEF; + + c = case_create (dict_get_next_value_idx (sfi->dict)); + + dict_get_vars (sfi->dict, &vv, &nv, 1u << DC_ORDINARY | 1u << DC_SYSTEM); + + for (sv = av_shift (av_case); SvOK (sv); sv = av_shift (av_case)) + { + const struct variable *v = vv[i++]; + const struct fmt_spec *ifmt = var_get_aux (v); + + /* If an input format has been set, then use it. + Otherwise just convert the raw value. + */ + if ( ifmt ) + { + struct substring ss = ss_cstr (SvPV_nolen (sv)); + if ( ! data_in (ss, LEGACY_NATIVE, ifmt->type, 0, 0, 0, + case_data_rw (c, v), + var_get_width (v)) ) + { + RETVAL = 0; + goto finish; + } + } + else + { + scalar_to_value (case_data_rw (c, v), sv, v); + } + } + + /* The remaining variables must be sysmis or blank string */ + while (i < dict_get_var_cnt (sfi->dict)) + { + const struct variable *v = vv[i++]; + union value *val = case_data_rw (c, v); + if ( var_is_numeric (v)) + val->f = SYSMIS; + else + memset (val->s, ' ', var_get_width (v)); + } + RETVAL = casewriter_write (sfi->writer, c); + finish: + free (vv); +OUTPUT: + RETVAL + + + + +MODULE = PSPP PACKAGE = PSPP::Reader + +struct sysreader_info * +pxs_open_sysfile (name) + char * name +CODE: + struct casereader *reader; + struct sysreader_info *sri = NULL; + struct file_handle *fh = + fh_create_file (NULL, name, fh_default_properties () ); + + sri = xmalloc (sizeof (*sri)); + sri->reader = sfm_open_reader (fh, &sri->dict, &sri->opts); + + if ( NULL == sri->reader) + { + free (sri); + sri = NULL; + } + + RETVAL = sri; + OUTPUT: +RETVAL + + +struct dictionary * +pxs_get_dict (reader) + struct sysreader_info *reader; +CODE: + RETVAL = reader->dict; + OUTPUT: +RETVAL + + +void +get_next_case (sfr) + struct sysreader_info *sfr; +PPCODE: + struct ccase *c; + + if (c = casereader_read (sfr->reader)) + { + int v; + + EXTEND (SP, dict_get_var_cnt (sfr->dict)); + for (v = 0; v < dict_get_var_cnt (sfr->dict); ++v ) + { + const struct variable *var = dict_get_var (sfr->dict, v); + const union value *val = case_data (c, var); + + PUSHs (sv_2mortal (value_to_scalar (val, var))); + } + + case_unref (c); + } diff --git a/perl-module/README b/perl-module/README new file mode 100644 index 00000000..74d6220e --- /dev/null +++ b/perl-module/README @@ -0,0 +1,48 @@ +PSPP version 0.7 +================ + +This module provides an interface allowing perl programs to create pspp +system files. + +INSTALLATION + +To install you must have first installed and built pspp 0.7.2 or +later. Pspp is not required to use this module, only to install +it. + +To install this module type the following: + + perl Makefile.PL + make + make test + make install + + + +DEPENDENCIES + +This module requires the POSIX module. + +The modules Test::More, Text::Diff, File::Temp and the pspp source are +required during installation, but are not needed to run the module. + + +COPYRIGHT AND LICENCE + +Copyright (C) 2007, 2009 by 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 2 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + diff --git a/perl-module/automake.mk b/perl-module/automake.mk new file mode 100644 index 00000000..9527dbbd --- /dev/null +++ b/perl-module/automake.mk @@ -0,0 +1,70 @@ +## Process this file with automake to produce Makefile.in -*- makefile -*- + +# PSPP + +module_sources = \ + perl-module/Changes \ + perl-module/COPYING \ + perl-module/Examples.pod \ + perl-module/Makefile.PL \ + perl-module/MANIFEST \ + perl-module/ppport.h \ + perl-module/PSPP.xs \ + perl-module/README \ + perl-module/typemap \ + perl-module/lib/PSPP.pm \ + perl-module/t/Pspp.t + +perl-module/pspp-module-config: Makefile + target=`mktemp`;\ + echo '%Locations = (' > $$target ;\ + printf " SourceDir => '" >> $$target ;\ + (cd $(top_srcdir) && echo `pwd`\', ) >> $$target ;\ + printf " BuildDir => '" >> $$target ;\ + (cd $(top_builddir) && echo `pwd`\' ) >> $$target ;\ + echo ');' >> $$target ;\ + cp $$target $(top_builddir)/perl-module/pspp-module-config + +perl-module/Makefile: perl-module/Makefile.PL perl-module/pspp-module-config + cd perl-module && $(PERL) Makefile.PL PREFIX=$(prefix) + +perl-module/PSPP-Perl-$(VERSION).tar.gz: $(module_sources) + $(RM) $@ + cd perl-module && $(MAKE) $(AM_MAKEFLAGS) tardist + +PHONY += module-make +module-make: perl-module/Makefile src/libpspp-core.la + cd perl-module && $(MAKE) $(AM_MAKEFLAGS) + +all-local: + if test x"$(top_builddir)" != x"$(top_srcdir)" ; then \ + for f in $(module_sources); do \ + destdir=`dirname $$f` ;\ + mkdir -p $$destdir ;\ + if test "$(top_srcdir)/$$f" -nt "$(top_builddir)/$$f" ; then \ + cp $(top_srcdir)/$$f $$destdir ; \ + echo cp $(top_srcdir)/$$f $$destdir ; \ + fi ; \ + done \ + fi + $(MAKE) $(AM_MAKEFLAGS) module-make perl-module/PSPP-Perl-$(VERSION).tar.gz + +check-local: + loc=`pwd` ; cd $(top_builddir)/src/.libs ; llp=`pwd` ; cd $$loc ; \ + LANG=C LD_LIBRARY_PATH=$$llp sh -c "cd perl-module && $(MAKE) $(AM_MAKEFLAGS) test" + + +clean-local: + cd perl-module && $(MAKE) $(AM_MAKEFLAGS) clean || true + if test x"$(top_builddir)" != x"$(top_srcdir)" ; then \ + $(RM) $(module_sources) ; \ + fi + $(RM) perl-module/Makefile.old + +CLEANFILES += \ + perl-module/PSPP-Perl-$(VERSION).tar.gz \ + perl-module/pspp-module-config \ + perl-module/const-c.inc \ + perl-module/const-xs.inc + +EXTRA_DIST += $(module_sources) diff --git a/perl-module/lib/PSPP.pm b/perl-module/lib/PSPP.pm new file mode 100644 index 00000000..3cb9f3be --- /dev/null +++ b/perl-module/lib/PSPP.pm @@ -0,0 +1,550 @@ +use 5.008008; +use strict; +use warnings; + +=head1 NAME + +PSPP-Perl - Perl extension to PSPP + +=head1 SYNOPSIS + + use PSPP; + +=head1 DESCRIPTION + +PSPP-Perl provides an interface to the libraries used by pspp to read and +write system files. + +=head1 EXPORT + +None by default. + +=cut +BEGIN { + $PSPP::VERSION='0.7.2'; + require XSLoader; + XSLoader::load('PSPP', $PSPP::VERSION); +} + +PSPP::onBoot($PSPP::VERSION); + +=pod + +=head1 PROGRAMMER'S INTERFACE + +The subroutines in this package return zero or unref on error. +When errors occur, a string describing the error is written +to C<$PSPP::errstr>. + +=cut + +package PSPP; +use POSIX ; + +use constant { SYSMIS => -(POSIX::DBL_MAX), + PERL_EPOCH => 12219379200 # Number of seconds between + # 14th October 1582 + # and + # 1st January 1970 + }; + + + +package PSPP::Dict; + +=pod + +=head2 PSPP::Dict::new + +Creates a new dictionary. This returned dictionary will be empty. +Returns undef on failure. + +=head3 set_documents ($string) + +Sets the documents (comments) to C. + +=head3 add_document ($string) + +Appends C to the documents. + +=head3 clear_documents () + +Removes all documents. + +=head3 set_weight ($var) + +Sets the weighting variable to C. + +=cut + +sub new +{ + my $class = shift; + my $self = pxs_dict_new (); + bless ($self, $class); + return $self; +} + +=pod + +=head3 get_var_cnt () + +Returns the number of variables in the dictionary. + +=head3 get_var ($idx) + +Returns the Cth variable from the dictionary. +Returns undef if C is greater than or equal to the number +of variables in the dictionary. + +=cut + +sub get_var +{ + my $dict = shift; + my $idx = shift; + my $var = pxs_get_variable ($dict, $idx); + + if ( ref $var ) + { + bless ($var, "PSPP::Var"); + } + return $var; +} + +=pod + +=head3 get_var_by_name ($name) + +Returns the variable from the dictionary whose name is C. +If there is no such variable, a null reference will be returned. + +=cut + +sub get_var_by_name +{ + my $dict = shift; + my $name = shift; + my $var = pxs_get_var_by_name ($dict, $name); + + if ( ref $var ) + { + bless ($var, "PSPP::Var"); + } + return $var; +} + + +package PSPP::Fmt; + +=pod + +=head2 PSPP::Fmt + +Contains constants used to denote variable format types. +The identifiers are the same as those used in pspp to denote formats. +For example C defines floating point format, and +C denotes string format. + +=cut + +# These must correspond to the values in src/data/format.h +use constant { + F => 0, + COMMA => 1, + DOT => 2, + DOLLAR => 3, + PCT => 4, + E => 5, + CCA => 6, + CCB => 7, + CCC => 8, + CCD => 9, + CCE => 10, + N => 11, + Z => 12, + P => 13, + PK => 14, + IB => 15, + PIB => 16, + PIBHEX => 17, + RB => 18, + RBHEX => 19, + DATE => 20, + ADATE => 21, + EDATE => 22, + JDATE => 23, + SDATE => 24, + QYR => 25, + MOYR => 26, + WKYR => 27, + DATETIME => 28, + TIME => 29, + DTIME => 30, + WKDAY => 31, + MONTH => 32, + A => 33, + AHEX => 34 +}; + + +=head2 PSPP::Var + +=cut + +package PSPP::Var; + +=head3 new ($dict, $name, %input_fmt) + +Creates and returns a new variable in the dictionary C. The +new variable will have the name C. +The input format is set by the C parameter +(See L). +By default, the write and print formats are the same as the input format. +The write and print formats may be changed (See L), +L). The input format may not be changed after +the variable has been created. +If the variable cannot be created, undef is returned. + +=cut + +sub new +{ + my $class = shift; + my $dict = shift; + my $name = shift; + my %format = @_; + my $self = pxs_dict_create_var ($dict, $name, \%format); + if ( ref $self ) + { + bless ($self, $class); + } + return $self; +} + +=pod + +=head3 set_label ($label) + +Sets the variable label to C