From: John Darrington Date: Thu, 23 Jul 2009 06:15:20 +0000 (+0200) Subject: Merge commit 'origin/stable' X-Git-Tag: build37~48 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp-builds.git;a=commitdiff_plain;h=b5c82cc9aabe7e641011130240ae1b2e84348e23;hp=5ee84736663824fe12474b78dace867e42893a14 Merge commit 'origin/stable' Conflicts: src/language/stats/t-test.q --- 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..db7a5910 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,14 +6,15 @@ most of the core libraries which ensure that PSPP runs with optimal speed are his work. * John Darrington wrote the graphical user interface, and the T-TEST, -ONEWAY, EXAMINE, RANK and NPAR TESTS commands, implemented support -for long variable names, psql and gnumeric and made numerous revisions -to other modules. +ONEWAY, EXAMINE, RANK and NPAR TESTS commands, implemented support for +long variable names, PostgreSQL and Gnumeric and made numerous +revisions to other modules. * Jason Stover contributed statistical and numerical functionality, 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..df10031e 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,31 @@ 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-05-24 22:25:04 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. + + * Value labels for long string variables are now supported. + + * Missing values for long string variables are now supported. + +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 29f93365..2697064c 100644 --- a/Smake +++ b/Smake @@ -5,11 +5,13 @@ GNULIB = ../gnulib GNULIB_TOOL = $(GNULIB)/gnulib-tool GNULIB_MODULES = \ + argp \ assert \ byteswap \ c-ctype \ c-strtod \ close \ + count-one-bits \ crypto/md4 \ dirname \ environ \ @@ -74,6 +76,8 @@ GNULIB_MODULES = \ trunc \ unilbrk/ulc-width-linebreaks \ unistd \ + unistr/u8-strlen \ + unistr/u8-strncat \ unlocked-io \ vasprintf-posix \ vfprintf-posix \ @@ -84,6 +88,7 @@ GNULIB_MODULES = \ xalloc \ xalloc-die \ xmalloca \ + xmemdup0 \ xsize \ xstrndup \ xvasprintf @@ -113,6 +118,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..c44fdd56 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") @@ -110,7 +106,7 @@ if test x"$with_libpq" != x"no" ; then fi AM_CONDITIONAL(PSQL_SUPPORT, test -n "$PG_CONFIG") -dnl Checks needed for gnumeric reader +dnl Checks needed for Gnumeric reader gnm_support=yes; PKG_CHECK_MODULES(LIBXML2, libxml-2.0,, [PSPP_OPTIONAL_PREREQ([libxml2]); gnm_support=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..9402d7af 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 \ @@ -22,6 +23,8 @@ doc_pspp_TEXINFOS = doc/version.texi \ doc/not-implemented.texi \ doc/statistics.texi \ doc/transformation.texi \ + doc/tutorial.texi \ + doc/tut.texi \ doc/regression.texi \ doc/utilities.texi \ doc/variables.texi \ @@ -32,6 +35,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 +48,19 @@ 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/tut.texi: + @$(MKDIR_P) doc + echo "@set example-dir $(examplesdir)" > $@ + + +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..1bad334e 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 @@ -131,7 +178,7 @@ situations. @display DATA LIST [FIXED] @{TABLE,NOTABLE@} - [FILE='file-name'] + [FILE='file-name' [ENCODING='encoding']] [RECORDS=record_count] [END=end_var] [SKIP=record_count] @@ -151,6 +198,8 @@ external file. It may be used to specify a file name as a string or a file handle (@pxref{File Handles}). If the FILE subcommand is not used, then input is assumed to be specified within the command file using @cmd{BEGIN DATA}@dots{}@cmd{END DATA} (@pxref{BEGIN DATA}). +The ENCODING subcommand may only be used if the FILE subcommand is also used. +It specifies the character encoding of the file. The optional RECORDS subcommand, which takes a single integer as an argument, is used to specify the number of lines per record. If RECORDS @@ -269,7 +318,7 @@ Defines the following variables: @itemize @bullet @item -@code{NAME}, a 10-character-wide long string variable, in columns 1 +@code{NAME}, a 10-character-wide string variable, in columns 1 through 10. @item @@ -310,15 +359,15 @@ Defines the following variables: @code{ID}, a numeric variable, in columns 1-5 of the first record. @item -@code{NAME}, a 30-character long string variable, in columns 7-36 of the +@code{NAME}, a 30-character string variable, in columns 7-36 of the first record. @item -@code{SURNAME}, a 30-character long string variable, in columns 38-67 of +@code{SURNAME}, a 30-character string variable, in columns 38-67 of the first record. @item -@code{MINITIAL}, a 1-character short string variable, in column 69 of +@code{MINITIAL}, a 1-character string variable, in column 69 of the first record. @item @@ -344,7 +393,7 @@ This example shows keywords abbreviated to their first 3 letters. DATA LIST FREE [(@{TAB,'c'@}, @dots{})] [@{NOTABLE,TABLE@}] - [FILE='file-name'] + [FILE='file-name' [ENCODING='encoding']] [SKIP=record_cnt] /var_spec@dots{} @@ -396,7 +445,7 @@ on field width apply, but they are honored on output. DATA LIST LIST [(@{TAB,'c'@}, @dots{})] [@{NOTABLE,TABLE@}] - [FILE='file-name'] + [FILE='file-name' [ENCODING='encoding']] [SKIP=record_count] /var_spec@dots{} @@ -1095,4 +1144,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/concepts.texi b/doc/dev/concepts.texi index 5876ce36..06652d62 100644 --- a/doc/dev/concepts.texi +++ b/doc/dev/concepts.texi @@ -117,76 +117,88 @@ case when it processes it later. @subsection Runtime Typed Values When a value's type is only known at runtime, it is often represented -as a @union{value}, defined in @file{data/value.h}. @union{value} has -two members: a @code{double} named @samp{f} to store a numeric value -and an array of @code{char} named @samp{s} to a store a string value. -A @union{value} does not identify the type or width of the data it -contains. Code that works with @union{values}s must therefore have -external knowledge of its content, often through the type and width of -a @struct{variable} (@pxref{Variables}). - -@cindex MAX_SHORT_STRING -@cindex short string -@cindex long string -@cindex string value -The array of @code{char} in @union{value} has only a small, fixed -capacity of @code{MAX_SHORT_STRING} bytes. A value that -fits within this capacity is called a @dfn{short string}. Any wider -string value, which must be represented by more than one -@union{value}, is called a @dfn{long string}. - -@deftypefn Macro int MAX_SHORT_STRING -Maximum width of a short string value, never less than 8 bytes. It is -wider than 8 bytes on systems where @code{double} is either larger -than 8 bytes or has stricter alignment than 8 bytes. -@end deftypefn +as a @union{value}, defined in @file{data/value.h}. A @union{value} +does not identify the type or width of the data it contains. Code +that works with @union{values}s must therefore have external knowledge +of its content, often through the type and width of a +@struct{variable} (@pxref{Variables}). + +@union{value} has one member that clients are permitted to access +directly, a @code{double} named @samp{f} that stores the content of a +numeric @union{value}. It has other members that store the content of +string @union{value}, but client code should use accessor functions +instead of referring to these directly. + +PSPP provides some functions for working with @union{value}s. The +most useful are described below. To use these functions, recall that +a numeric value has a width of 0. -@deftypefn Macro int MIN_LONG_STRING -Minimum width of a long string value, that is, @code{MAX_SHORT_STRING -+ 1}. -@end deftypefn +@deftypefun void value_init (union value *@var{value}, int @var{width}) +Initializes @var{value} as a value of the given @var{width}. After +initialization, the data in @var{value} are indeterminate; the caller +is responsible for storing initial data in it. +@end deftypefun -Long string variables are slightly harder to work with than short -string values, because they cannot be conveniently and efficiently -allocated as block scope variables or structure members. The PSPP -language exposes this inconvenience to the user: there are many -circumstances in PSPP syntax where short strings are allowed but not -long strings. Short string variables, for example, may have -user-missing values, but long string variables may not (@pxref{Missing -Observations,,,pspp, PSPP Users Guide}). +@deftypefun void value_destroy (union value *@var{value}, int @var{width}) +Frees auxiliary storage associated with @var{value}, which must have +the given @var{width}. +@end deftypefun -PSPP provides a few functions for working with @union{value}s. The -most useful are described below. To use these functions, recall that -a numeric value has a width of 0. +@deftypefun bool value_needs_init (int @var{width}) +For some widths, @func{value_init} and @func{value_destroy} do not +actually do anything, because no additional storage is needed beyond +the size of @union{value}. This function returns true if @var{width} +is such a width, which case there is no actual need to call those +functions. This can be a useful optimization if a large number of +@union{value}s of such a width are to be initialized or destroyed. + +This function returns false if @func{value_init} and +@func{value_destroy} are actually required for the given @var{width}. +@end deftypefun + +@deftypefun double value_num (const union value *@var{value}) +Returns the numeric value in @var{value}, which must have been +initialized as a numeric value. Equivalent to @code{@var{value}->f}. +@end deftypefun + +@deftypefun {const char *} value_str (const union value *@var{value}, int @var{width}) +@deftypefunx {char *} value_str_rw (union value *@var{value}, int @var{width}) +Returns the string value in @var{value}, which must have been +initialized with positive width @var{width}. The string returned is +not null-terminated. Only @var{width} bytes of returned data may be +accessed. + +The two different functions exist only for @code{const}-correctness. +Otherwise they are identical. -@deftypefun size_t value_cnt_from_width (int @var{width}) -Returns the number of consecutive @union{value}s that must be -allocated to store a value of the given @var{width}. For a numeric or -short string value, the return value is 1; for long string -variables, it is greater than 1. +It is important that @var{width} be the correct value that was passed +to @func{value_init}. Passing a smaller or larger value (e.g.@: +because that number of bytes will be accessed) will not always work +and should be avoided. @end deftypefun @deftypefun void value_copy (union value *@var{dst}, @ const union value *@var{src}, @ int @var{width}) -Copies a value of the given @var{width} from the @union{value} array -starting at @var{src} to the one starting at @var{dst}. The two -arrays must not overlap. +Copies the contents of @union{value} @var{src} to @var{dst}. Both +@var{dst} and @var{src} must have been initialized with the specified +@var{width}. @end deftypefun @deftypefun void value_set_missing (union value *@var{value}, int @var{width}) Sets @var{value} to @code{SYSMIS} if it is numeric or to all spaces if -it is alphanumeric, according to @var{width}. @var{value} must point -to the start of a @union{value} array of the given @var{width}. +it is alphanumeric, according to @var{width}. @var{value} must have +been initialized with the specified @var{width}. @end deftypefun @anchor{value_is_resizable} @deftypefun bool value_is_resizable (const union value *@var{value}, int @var{old_width}, int @var{new_width}) -Determines whether @var{value} may be resized from @var{old_width} to -@var{new_width}. Resizing is possible if the following criteria are -met. First, @var{old_width} and @var{new_width} must be both numeric -or both string widths. Second, if @var{new_width} is a short string -width and less than @var{old_width}, resizing is allowed only if bytes +Determines whether @var{value}, which must have been initialized with +the specified @var{old_width}, may be resized to @var{new_width}. +Resizing is possible if the following criteria are met. First, +@var{old_width} and @var{new_width} must be both numeric or both +string widths. Second, if @var{new_width} is a short string width and +less than @var{old_width}, resizing is allowed only if bytes @var{new_width} through @var{old_width} in @var{value} contain only spaces. @@ -196,9 +208,36 @@ These rules are part of those used by @func{mv_is_resizable} and @deftypefun void value_resize (union value *@var{value}, int @var{old_width}, int @var{new_width}) Resizes @var{value} from @var{old_width} to @var{new_width}, which -must be allowed by the rules stated above. This has an effect only if -@var{new_width} is greater than @var{old_width}, in which case the -bytes newly added to @var{value} are cleared to spaces. +must be allowed by the rules stated above. @var{value} must have been +initialized with the specified @var{old_width} before calling this +function. After resizing, @var{value} has width @var{new_width}. + +If @var{new_width} is greater than @var{old_width}, @var{value} will +be padded on the right with spaces to the new width. If +@var{new_width} is less than @var{old_width}, the rightmost bytes of +@var{value} are truncated. +@end deftypefun + +@deftypefun bool value_equal (const union value *@var{a}, const union value *@var{b}, int @var{width}) +Compares of @var{a} and @var{b}, which must both have width +@var{width}. Returns true if their contents are the same, false if +they differ. +@end deftypefun + +@deftypefun int value_compare_3way (const union value *@var{a}, const union value *@var{b}, int @var{width}) +Compares of @var{a} and @var{b}, which must both have width +@var{width}. Returns -1 if @var{a} is less than @var{b}, 0 if they +are equal, or 1 if @var{a} is greater than @var{b}. + +Numeric values are compared numerically, with @code{SYSMIS} comparing +less than any real number. String values are compared +lexicographically byte-by-byte. +@end deftypefun + +@deftypefun size_t value_hash (const union value *@var{value}, int @var{width}, unsigned int @var{basis}) +Computes and returns a hash of @var{value}, which must have the +specified @var{width}. The value in @var{basis} is folded into the +hash. @end deftypefun @node Input and Output Formats @@ -615,18 +654,17 @@ Returns the name of the given format @var{type}. These functions provide the ability to convert data fields into @union{value}s and vice versa. -@deftypefun bool data_in (struct substring @var{input}, enum legacy_encoding @var{legacy_encoding}, enum fmt_type @var{type}, int @var{implied_decimals}, int @var{first_column}, union value *@var{output}, int @var{width}) +@deftypefun bool data_in (struct substring @var{input}, const char *@var{encoding}, enum fmt_type @var{type}, int @var{implied_decimals}, int @var{first_column}, const struct dictionary *@var{dict}, union value *@var{output}, int @var{width}) Parses @var{input} as a field containing data in the given format -@var{type}. The resulting value is stored in @var{output}, which has -the given @var{width}. For consistency, @var{width} must be 0 if +@var{type}. The resulting value is stored in @var{output}, which the +caller must have initialized with the given @var{width}. For +consistency, @var{width} must be 0 if @var{type} is a numeric format type and greater than 0 if @var{type} is a string format type. - -Ordinarily @var{legacy_encoding} should be @code{LEGACY_NATIVE}, -indicating that @var{input} is encoded in the character set -conventionally used on the host machine. It may be set to -@code{LEGACY_EBCDIC} to cause @var{input} to be re-encoded from EBCDIC -during data parsing. +@var{encoding} should be set to indicate the character +encoding of @var{input}. +@var{dict} must be a pointer to the dictionary with which @var{output} +is associated. If @var{input} is the empty string (with length 0), @var{output} is set to the value set on SET BLANKS (@pxref{SET BLANKS,,,pspp, PSPP @@ -661,21 +699,15 @@ not propagated to the caller as errors. This function is declared in @file{data/data-in.h}. @end deftypefun -@deftypefun void data_out (const union value *@var{input}, const struct fmt_spec *@var{format}, char *@var{output}) -@deftypefunx void data_out_legacy (const union value *@var{input}, enum legacy_encoding @var{legacy_encoding}, const struct fmt_spec *@var{format}, char *@var{output}) -Converts the data pointed to by @var{input} into a data field in -@var{output} according to output format specifier @var{format}, which -must be a valid output format. Exactly @code{@var{format}->w} bytes -are written to @var{output}. The width of @var{input} is also +@deftypefun char * data_out (const union value *@var{input}, const struct fmt_spec *@var{format}) +@deftypefunx char * data_out_legacy (const union value *@var{input}, const char *@var{encoding}, const struct fmt_spec *@var{format}) +Converts the data pointed to by @var{input} into a string value, which +will be encoded in UTF-8, according to output format specifier @var{format}. +Format +must be a valid output format. The width of @var{input} is inferred from @var{format} using an algorithm equivalent to @func{fmt_var_width}. -If @func{data_out} is called, or @func{data_out_legacy} is called with -@var{legacy_encoding} set to @code{LEGACY_NATIVE}, @var{output} will -be encoded in the character set conventionally used on the host -machine. If @var{legacy_encoding} is set to @code{LEGACY_EBCDIC}, -@var{output} will be re-encoded from EBCDIC during data output. - When @var{input} contains data that cannot be represented in the given @var{format}, @func{data_out} may output a message using @func{msg}, @c (@pxref{msg}), @@ -703,28 +735,7 @@ variable, is most conveniently executed through functions on A @struct{missing_values} is essentially a set of @union{value}s that have a common value width (@pxref{Values}). For a set of missing values associated with a variable (the common case), the set's -width is the same as the variable's width. The contents of a set of -missing values is subject to some restrictions. Regardless of width, -a set of missing values is allowed to be empty. Otherwise, its -possible contents depend on its width: - -@table @asis -@item 0 (numeric values) -Up to three discrete numeric values, or a range of numeric values -(which includes both ends of the range), or a range plus one discrete -numeric value. - -@item 1@dots{}@t{MAX_SHORT_STRING} - 1 (short string values) -Up to three discrete string values (with the same width as the set). - -@item @t{MAX_SHORT_STRING}@dots{}@t{MAX_STRING} (long string values) -Always empty. -@end table - -These somewhat arbitrary restrictions are the same as those imposed by -SPSS. In PSPP we could easily eliminate these restrictions, but doing -so would also require us to extend the system file format in an -incompatible way, which we consider a bad tradeoff. +width is the same as the variable's width. Function prototypes and other declarations related to missing values are declared in @file{data/missing-values.h}. @@ -733,18 +744,37 @@ are declared in @file{data/missing-values.h}. Opaque type that represents a set of missing values. @end deftp +The contents of a set of missing values is subject to some +restrictions. Regardless of width, a set of missing values is allowed +to be empty. A set of numeric missing values may contain up to three +discrete numeric values, or a range of numeric values (which includes +both ends of the range), or a range plus one discrete numeric value. +A set of string missing values may contain up to three discrete string +values (with the same width as the set), but ranges are not supported. + +In addition, values in string missing values wider than +@code{MV_MAX_STRING} bytes may contain non-space characters only in +their first @code{MV_MAX_STRING} bytes; all the bytes after the first +@code{MV_MAX_STRING} must be spaces. @xref{mv_is_acceptable}, for a +function that tests a value against these constraints. + +@deftypefn Macro int MV_MAX_STRING +Number of bytes in a string missing value that are not required to be +spaces. The current value is 8, a value which is fixed by the system +file format. In PSPP we could easily eliminate this restriction, but +doing so would also require us to extend the system file format in an +incompatible way, which we consider a bad tradeoff. +@end deftypefn + The most often useful functions for missing values are those for testing whether a given value is missing, described in the following section. Several other functions for creating, inspecting, and modifying @struct{missing_values} objects are described afterward, but -these functions are much more rarely useful. No function for -destroying a @struct{missing_values} is provided, because -@struct{missing_values} does not contain any pointers or other -references to resources that need deallocation. +these functions are much more rarely useful. @menu * Testing for Missing Values:: -* Initializing User-Missing Value Sets:: +* Creating and Destroying User-Missing Values:: * Changing User-Missing Value Set Width:: * Inspecting User-Missing Value Sets:: * Modifying User-Missing Value Sets:: @@ -796,8 +826,10 @@ missing. @end deftp @end deftypefun -@node Initializing User-Missing Value Sets -@subsection Initializing User-Missing Value Sets +@node Creating and Destroying User-Missing Values +@subsection Creation and Destruction + +These functions create and destroy @struct{missing_values} objects. @deftypefun void mv_init (struct missing_values *@var{mv}, int @var{width}) Initializes @var{mv} as a set of user-missing values. The set is @@ -805,6 +837,10 @@ initially empty. Any values added to it must have the specified @var{width}. @end deftypefun +@deftypefun void mv_destroy (struct missing_values *@var{mv}) +Destroys @var{mv}, which must not be referred to again. +@end deftypefun + @deftypefun void mv_copy (struct missing_values *@var{mv}, const struct missing_values *@var{old}) Initializes @var{mv} as a copy of the existing set of user-missing values @var{old}. @@ -834,11 +870,9 @@ the required width, may be used instead. Tests whether @var{mv}'s width may be changed to @var{new_width} using @func{mv_resize}. Returns true if it is allowed, false otherwise. -If @var{new_width} is a long string width, @var{mv} may be resized -only if it is empty. Otherwise, if @var{mv} contains any missing -values, then it may be resized only if each missing value may be -resized, as determined by @func{value_is_resizable} -(@pxref{value_is_resizable}). +If @var{mv} contains any missing values, then it may be resized only +if each missing value may be resized, as determined by +@func{value_is_resizable} (@pxref{value_is_resizable}). @end deftypefun @anchor{mv_resize} @@ -857,8 +891,8 @@ width. These functions inspect the properties and contents of @struct{missing_values} objects. -The first set of functions inspects the discrete values that numeric -and short string sets of user-missing values may contain: +The first set of functions inspects the discrete values that sets of +user-missing values may contain: @deftypefun bool mv_is_empty (const struct missing_values *@var{mv}) Returns true if @var{mv} contains no user-missing values, false if it @@ -883,11 +917,12 @@ values, that is, if @func{mv_n_values} would return nonzero for @var{mv}. @end deftypefun -@deftypefun void mv_get_value (const struct missing_values *@var{mv}, union value *@var{value}, int @var{index}) -Copies the discrete user-missing value in @var{mv} with the given -@var{index} into @var{value}. The index must be less than the number -of discrete user-missing values in @var{mv}, as reported by -@func{mv_n_values}. +@deftypefun {const union value *} mv_get_value (const struct missing_values *@var{mv}, int @var{index}) +Returns the discrete user-missing value in @var{mv} with the given +@var{index}. The caller must not modify or free the returned value or +refer to it after modifying or freeing @var{mv}. The index must be +less than the number of discrete user-missing values in @var{mv}, as +reported by @func{mv_n_values}. @end deftypefun The second set of functions inspects the single range of values that @@ -909,7 +944,7 @@ include a range. These functions modify the contents of @struct{missing_values} objects. -The first set of functions applies to all sets of user-missing values: +The next set of functions applies to all sets of user-missing values: @deftypefun bool mv_add_value (struct missing_values *@var{mv}, const union value *@var{value}) @deftypefunx bool mv_add_str (struct missing_values *@var{mv}, const char @var{value}[]) @@ -917,8 +952,8 @@ The first set of functions applies to all sets of user-missing values: Attempts to add the given discrete @var{value} to set of user-missing values @var{mv}. @var{value} must have the same width as @var{mv}. Returns true if @var{value} was successfully added, false if the set -could not accept any more discrete values. (Always returns false if -@var{mv} is a set of long string user-missing values.) +could not accept any more discrete values or if @var{value} is not an +acceptable user-missing value (see @func{mv_is_acceptable} below). These functions are equivalent, except for the form in which @var{value} is provided, so you may use whichever function is most @@ -930,10 +965,22 @@ Removes a discrete value from @var{mv} (which must contain at least one discrete value) and stores it in @var{value}. @end deftypefun -@deftypefun void mv_replace_value (struct missing_values *@var{mv}, const union value *@var{value}, int @var{index}) -Replaces the discrete value with the given @var{index} in @var{mv} -(which must contain at least @var{index} + 1 discrete values) with -@var{value}. +@deftypefun bool mv_replace_value (struct missing_values *@var{mv}, const union value *@var{value}, int @var{index}) +Attempts to replace the discrete value with the given @var{index} in +@var{mv} (which must contain at least @var{index} + 1 discrete values) +by @var{value}. Returns true if successful, false if @var{value} is +not an acceptable user-missing value (see @func{mv_is_acceptable} +below). +@end deftypefun + +@deftypefun bool mv_is_acceptable (const union value *@var{value}, int @var{width}) +@anchor{mv_is_acceptable} +Returns true if @var{value}, which must have the specified +@var{width}, may be added to a missing value set of the same +@var{width}, false if it cannot. As described above, all numeric +values and string values of width @code{MV_MAX_STRING} or less may be +added, but string value of greater width may be added only if bytes +beyond the first @code{MV_MAX_STRING} are all spaces. @end deftypefun The second set of functions applies only to numeric sets of @@ -965,12 +1012,7 @@ All of the values in a set of value labels have the same width, which for a set of value labels owned by a variable (the common case) is the same as its variable. -Numeric and short string sets of value labels may contain any number -of entries. Long string sets of value labels may not contain any -value labels at all, due to a corresponding restriction in SPSS. In -PSPP we could easily eliminate this restriction, but doing so would -also require us to extend the system file format in an incompatible -way, which we consider a bad tradeoff. +Sets of value labels may contain any number of entries. It is rarely necessary to interact directly with a @struct{val_labs} object. Instead, the most common operation, looking up the label for @@ -1051,31 +1093,24 @@ value in it may be resized to that width, as determined by Changes the width of @var{val_labs}'s values to @var{new_width}, which must be a valid new width as determined by @func{val_labs_can_set_width}. - -If @var{new_width} is a long string width, this function deletes all -value labels from @var{val_labs}. @end deftypefun @node Value Labels Adding and Removing Labels @subsection Adding and Removing Labels These functions add and remove value labels from a @struct{val_labs} -object. These functions apply only to numeric and short string sets -of value labels. They have no effect on long string sets of value -labels, since these sets are always empty. +object. @deftypefun bool val_labs_add (struct val_labs *@var{val_labs}, union value @var{value}, const char *@var{label}) Adds @var{label} to in @var{var_labs} as a label for @var{value}, which must have the same width as the set of value labels. Returns -true if successful, false if @var{value} already has a label or if -@var{val_labs} has long string width. +true if successful, false if @var{value} already has a label. @end deftypefun @deftypefun void val_labs_replace (struct val_labs *@var{val_labs}, union value @var{value}, const char *@var{label}) Adds @var{label} to in @var{var_labs} as a label for @var{value}, which must have the same width as the set of value labels. If @var{value} already has a label in @var{var_labs}, it is replaced. -Has no effect if @var{var_labs} has long string width. @end deftypefun @deftypefun bool val_labs_remove (struct val_labs *@var{val_labs}, union value @var{value}) @@ -1088,75 +1123,65 @@ was removed, false otherwise. @subsection Iterating through Value Labels These functions allow iteration through the set of value labels -represented by a @struct{val_labs} object. They are usually used in -the context of a @code{for} loop: +represented by a @struct{val_labs} object. They may be used in the +context of a @code{for} loop: @example struct val_labs val_labs; -struct val_labs_iterator *i; -struct val_lab *vl; +const struct val_lab *vl; @dots{} -for (vl = val_labs_first (val_labs, &i); vl != NULL; - vl = val_labs_next (val_labs, &i)) +for (vl = val_labs_first (val_labs); vl != NULL; + vl = val_labs_next (val_labs, vl)) @{ @dots{}@r{do something with @code{vl}}@dots{} @} @end example -The value labels in a @struct{val_labs} must not be modified as it is -undergoing iteration. - -@deftp {Structure} {struct val_lab} -Represents a value label for iteration purposes, with two -client-visible members: - -@table @code -@item union value value -Value being labeled, of the same width as the @struct{val_labs} being -iterated. - -@item const char *label -The label, as a null-terminated string. -@end table -@end deftp - -@deftp {Structure} {struct val_labs_iterator} -Opaque object that represents the current state of iteration through a -set of value value labels. Automatically destroyed by successful -completion of iteration. Must be destroyed manually in other -circumstances, by calling @func{val_labs_done}. -@end deftp +Value labels should not be added or deleted from a @struct{val_labs} +as it is undergoing iteration. -@deftypefun {struct val_lab *} val_labs_first (const struct val_labs *@var{val_labs}, struct val_labs_iterator **@var{iterator}) -If @var{val_labs} contains at least one value label, starts an -iteration through @var{val_labs}, initializes @code{*@var{iterator}} -to point to a newly allocated iterator, and returns the first value -label in @var{val_labs}. If @var{val_labs} is empty, sets -@code{*@var{iterator}} to null and returns a null pointer. +@deftypefun {const struct val_lab *} val_labs_first (const struct val_labs *@var{val_labs}) +Returns the first value label in @var{var_labs}, if it contains at +least one value label, or a null pointer if it does not contain any +value labels. +@end deftypefun -This function creates iterators that traverse sets of value labels in -no particular order. +@deftypefun {const struct val_lab *} val_labs_next (const struct val_labs *@var{val_labs}, const struct val_labs_iterator **@var{vl}) +Returns the value label in @var{var_labs} following @var{vl}, if +@var{vl} is not the last value label in @var{val_labs}, or a null +pointer if there are no value labels following @var{vl}. @end deftypefun -@deftypefun {struct val_lab *} val_labs_first_sorted (const struct val_labs *@var{val_labs}, struct val_labs_iterator **@var{iterator}) -Same as @func{val_labs_first}, except that the created iterator -traverses the set of value labels in ascending order of value. +@deftypefun {const struct val_lab **} val_labs_sorted (const struct val_labs *@var{val_labs}) +Allocates and returns an array of pointers to value labels, which are +sorted in increasing order by value. The array has +@code{val_labs_count (@var{val_labs})} elements. The caller is +responsible for freeing the array with @func{free} (but must not free +any of the @struct{val_lab} elements that the array points to). @end deftypefun -@deftypefun {struct val_lab *} val_labs_next (const struct val_labs *@var{val_labs}, struct val_labs_iterator **@var{iterator}) -Advances an iterator created with @func{val_labs_first} or -@func{val_labs_first_sorted} to the next value label, which is -returned. If the set of value labels is exhausted, returns a null -pointer after freeing @code{*@var{iterator}} and setting it to a null -pointer. +The iteration functions above work with pointers to @struct{val_lab} +which is an opaque data structure that users of @struct{val_labs} must +not modify or free directly. The following functions work with +objects of this type: + +@deftypefun {const union value *} val_lab_get_value (const struct val_lab *@var{vl}) +Returns the value of value label @var{vl}. The caller must not modify +or free the returned value. (To achieve a similar result, remove the +value label with @func{val_labs_remove}, then add the new value with +@func{val_labs_add}.) + +The width of the returned value cannot be determined directly from +@var{vl}. It may be obtained by calling @func{val_labs_get_width} on +the @struct{val_labs} that @var{vl} is in. @end deftypefun -@deftypefun void val_labs_done (struct val_labs_iterator **@var{iterator}) -Frees @code{*@var{iterator}} and sets it to a null pointer. Does -not need to be called explicitly if @func{val_labs_next} returns a -null pointer, indicating that all value labels have been visited. +@deftypefun {const char *} val_lab_get_label (const struct val_lab *@var{vl}) +Returns the label in @var{vl} as a null-terminated string. The caller +must not modify or free the returned string. (Use +@func{val_labs_replace} to change a value label.) @end deftypefun @node Variables @@ -1280,22 +1305,6 @@ Returns true if @var{var} is an alphanumeric (string) variable, false otherwise. @end deftypefun -@deftypefun bool var_is_short_string (const struct variable *@var{var}) -Returns true if @var{var} is a string variable of width -@code{MAX_SHORT_STRING} or less, false otherwise. -@end deftypefun - -@deftypefun bool var_is_long_string (const struct variable *@var{var}) -Returns true if @var{var} is a string variable of width greater than -@code{MAX_SHORT_STRING}, false otherwise. -@end deftypefun - -@deftypefun size_t var_get_value_cnt (const struct variable *@var{var}) -Returns the number of @union{value}s needed to hold an instance of -variable @var{var}. @code{var_get_value_cnt (var)} is equivalent to -@code{value_cnt_from_width (var_get_width (var))}. -@end deftypefun - @node Variable Missing Values @subsection Variable Missing Values @@ -1313,8 +1322,8 @@ Tests whether @var{value} is a missing value of the given @var{class} for variable @var{var} and returns true if so, false otherwise. @func{var_is_num_missing} may only be applied to numeric variables; @func{var_is_str_missing} may only be applied to string variables. -For string variables, @var{value} must contain exactly as many -characters as @var{var}'s width. +@var{value} must have been initialized with the same width as +@var{var}. @code{var_is_@var{type}_missing (@var{var}, @var{value}, @var{class})} is equivalent to @code{mv_is_@var{type}_missing @@ -1339,7 +1348,7 @@ resizable to @var{var}'s width (@pxref{mv_resize}). The caller retains ownership of @var{miss}. @end deftypefun -b@deftypefun void var_clear_missing_values (struct variable *@var{var}) +@deftypefun void var_clear_missing_values (struct variable *@var{var}) Clears @var{var}'s missing values. Equivalent to @code{var_set_missing_values (@var{var}, NULL)}. @end deftypefun @@ -1360,11 +1369,13 @@ value: @deftypefun {const char *} var_lookup_value_label (const struct variable *@var{var}, const union value *@var{value}) Looks for a label for @var{value} in @var{var}'s set of value labels. -Returns the label if one exists, otherwise a null pointer. +@var{value} must have the same width as @var{var}. Returns the label +if one exists, otherwise a null pointer. @end deftypefun @deftypefun void var_append_value_name (const struct variable *@var{var}, const union value *@var{value}, struct string *@var{str}) Looks for a label for @var{value} in @var{var}'s set of value labels. +@var{value} must have the same width as @var{var}. If a label exists, it will be appended to the string pointed to by @var{str}. Otherwise, it formats @var{value} using @var{var}'s print format (@pxref{Input and Output Formats}) @@ -1406,20 +1417,19 @@ the variable (making a second copy): @deftypefun bool var_add_value_label (struct variable *@var{var}, const union value *@var{value}, const char *@var{label}) Attempts to add a copy of @var{label} as a label for @var{value} for -the given @var{var}. If @var{value} already has a label, then the old -label is retained. Returns true if a label is added, false if there -was an existing label for @var{value} or if @var{var} is a long string -variable. Either way, the caller retains ownership of @var{value} and -@var{label}. +the given @var{var}. @var{value} must have the same width as +@var{var}. If @var{value} already has a label, then the old label is +retained. Returns true if a label is added, false if there was an +existing label for @var{value}. Either way, the caller retains +ownership of @var{value} and @var{label}. @end deftypefun @deftypefun void var_replace_value_label (struct variable *@var{var}, const union value *@var{value}, const char *@var{label}) Attempts to add a copy of @var{label} as a label for @var{value} for -the given @var{var}. If @var{value} already has a label, then +the given @var{var}. @var{value} must have the same width as +@var{var}. If @var{value} already has a label, then @var{label} replaces the old label. Either way, the caller retains ownership of @var{value} and @var{label}. - -If @var{var} is a long string variable, this function has no effect. @end deftypefun @node Variable Print and Write Formats diff --git a/doc/dev/i18n.texi b/doc/dev/i18n.texi new file mode 100644 index 00000000..3ab86c3d --- /dev/null +++ b/doc/dev/i18n.texi @@ -0,0 +1,138 @@ +@node Internationalisation +@chapter Internationalisation + +Internationalisation in pspp is complicated. +The most annoying aspect is that of character-encoding. +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 locale of the user interface. +@item The locale of the output. +@item The locale of the data. Only the character encoding is relevant. +@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.} +The dictionary pertaining to the data contains a field denoting the encoding. +Any string data stored in a @union{value} will be encoded in the +dictionary's character set. + + +@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}). +However, many +files produced by early versions of spss set this to ``2'' (ASCII) regardless +of the encoding of the data. +Later versions contain an additional +record (@pxref{Character Encoding Record}) describing the encoding. +When a system file is read, the dictionary's encoding is set using information +gleened from the system file. +If the encoding cannot be determined or would be unreliable, then it +remains unset. + + +@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 (except for filenames) +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. + +@subsection Filenames +The GLib API has some special functions for dealing with filenames. +Strings returned from functions like gtk_file_chooser_dialog_get_name are not, +in general, encoded in UTF8, but in ``filename'' encoding. +If that filename is passed to another GLib function which expects a filename, +no conversion is necessary. +If it's passed to a function for the purposes of displaying it (eg. in a +window's title-bar) it must be converted to UTF8 --- there is a special +function for this: g_filename_display_name or g_filename_basename. +If however, a filename needs to be passed outside of GTK+/GLib (for example to fopen) it must be converted to the local system encoding. + + +@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 (const char *@var{to}, const char *@var{from}, const char *@var{text}, int @var{len}); + +Converts the string @var{text}, which is encoded in @var{from} to a new string encoded in @var{to} encoding. +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 + +In order to minimise the number of conversions required, and to simplify +design, PSPP attempts to store all internal strings in UTF8 encoding. +Thus, when reading system and portable files (or any other data source), +the following items are immediately converted to UTF8 encoding: +@itemize +@item Variable names +@item Variable labels +@item Value labels +@end itemize +Conversely, when writing system files, these are converted back to the +encoding of that system file. + +String data stored in union values are left in their original encoding. +These will be converted by the data_in/data_out functions. + + + +@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..a404d0d6 100644 --- a/doc/dev/system-file-format.texi +++ b/doc/dev/system-file-format.texi @@ -96,6 +96,9 @@ Each type of record is described separately below. * Variable Display Parameter Record:: * Long Variable Names Record:: * Very Long String Record:: +* Character Encoding Record:: +* Long String Value Labels Record:: +* Data File and Variable Attributes Records:: * Miscellaneous Informational Records:: * Dictionary Termination Record:: * Data Record:: @@ -286,15 +289,20 @@ length @code{label_len}, rounded up to the nearest multiple of 32 bits. The first @code{label_len} characters are the variable's variable label. @item flt64 missing_values[]; -This field is present only if @code{n_missing_values} is not 0. It has -the same number of elements as the absolute value of -@code{n_missing_values}. For discrete missing values, each element -represents one missing value. When a range is present, the first -element denotes the minimum value in the range, and the second element -denotes the maximum value in the range. When a range plus a value are -present, the third element denotes the additional discrete missing -value. HIGHEST and LOWEST are indicated as described in the chapter -introduction. +This field is present only if @code{n_missing_values} is nonzero. It +has the same number of 8-byte elements as the absolute value of +@code{n_missing_values}. Each element is interpreted as a number for +numeric variables (with HIGHEST and LOWEST indicated as described in +the chapter introduction). For string variables of width less than 8 +bytes, elements are right-padded with spaces; for string variables +wider than 8 bytes, only the first 8 bytes of each missing value are +specified, with the remainder implicitly all spaces. + +For discrete missing values, each element represents one missing +value. When a range is present, the first element denotes the minimum +value in the range, and the second element denotes the maximum value +in the range. When a range plus a value are present, the third +element denotes the additional discrete missing value. @end table The @code{print} and @code{write} members of sysfile_variable are output @@ -396,6 +404,11 @@ Format types are defined as follows: @node Value Labels Records @section Value Labels Records +The value label records documented in this section are used for +numeric and short string variables only. Long string variables may +have value labels, but their value labels are recorded using a +different record type (@pxref{Long String Value Labels Record}). + The value label record has the following format: @example @@ -456,7 +469,7 @@ A list of dictionary indexes of variables to which to apply the value labels (@pxref{Dictionary Index}). There are @code{var_count} elements. -String variables wider than 8 bytes may not have value labels. +String variables wider than 8 bytes may not be specified in this list. @end table @node Document Record @@ -545,9 +558,14 @@ Compression code. Always set to 1. Machine endianness. 1 indicates big-endian, 2 indicates little-endian. @item int32 character_code; +@anchor{character-code} Character code. 1 indicates EBCDIC, 2 indicates 7-bit ASCII, 3 indicates 8-bit ASCII, 4 indicates DEC Kanji. Windows code page numbers are also valid. + +Experience has shown that in many files, this field is ignored or incorrect. +For a more reliable indication of the file's character encoding +see @ref{Character Encoding Record}. @end table @node Machine Floating-Point Info Record @@ -791,6 +809,197 @@ After the last tuple, there may be a single byte 00, or @{00, 09@}. The total length is @code{count} bytes. @end table +@node Character Encoding Record +@section Character Encoding Record + +This record, if present, indicates the character encoding for string data, +long variable names, variable labels, value labels and other strings in the +file. + +@example +/* @r{Header.} */ +int32 rec_type; +int32 subtype; +int32 size; +int32 count; + +/* @r{Exactly @code{count} bytes of data.} */ +char encoding[]; +@end example + +@table @code +@item int32 rec_type; +Record type. Always set to 7. + +@item int32 subtype; +Record subtype. Always set to 20. + +@item int32 size; +The size of each element in the @code{encoding} member. Always set to 1. + +@item int32 count; +The total number of bytes in @code{encoding}. + +@item char encoding[]; +The name of the character encoding. Normally this will be an official IANA characterset name or alias. +See @url{http://www.iana.org/assignments/character-sets}. +@end table + +This record is not present in files generated by older software. +See also @ref{character-code}. + +@node Long String Value Labels Record +@section Long String Value Labels Record + +This record, if present, specifies value labels for long string +variables. + +@example +/* @r{Header.} */ +int32 rec_type; +int32 subtype; +int32 size; +int32 count; + +/* @r{Repeated up to exactly @code{count} bytes.} */ +int32 var_name_len; +char var_name[]; +int32 var_width; +int32 n_labels; +long_string_label labels[]; +@end example + +@table @code +@item int32 rec_type; +Record type. Always set to 7. + +@item int32 subtype; +Record subtype. Always set to 21. + +@item int32 size; +Always set to 1. + +@item int32 count; +The number of bytes following the header until the next header. + +@item int32 var_name_len; +@itemx char var_name[]; +The number of bytes in the name of the variable that has long string +value labels, plus the variable name itself, which consists of exactly +@code{var_name_len} bytes. The variable name is not padded to any +particular boundary, nor is it null-terminated. + +@item int32 var_width; +The width of the variable, in bytes, which will be between 9 and +32767. + +@item int32 n_labels; +@itemx long_string_label labels[]; +The long string labels themselves. The @code{labels} array contains +exactly @code{n_labels} elements, each of which has the following +substructure: + +@example +int32 value_len; +char value[]; +int32 label_len; +char label[]; +@end example + +@table @code +@item int32 value_len; +@itemx char value[]; +The string value being labeled. @code{value_len} is the number of +bytes in @code{value}; it is equal to @code{var_width}. The +@code{value} array is not padded or null-terminated. + +@item int32 label_len; +@itemx char label[]; +The label for the string value. @code{label_len}, which must be +between 0 and 120, is the number of bytes in @code{label}. The +@code{label} array is not padded or null-terminated. +@end table +@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..345e5966 100644 --- a/doc/expressions.texi +++ b/doc/expressions.texi @@ -15,14 +15,14 @@ strings or numbers as operands. With few exceptions, operands may be full-fledged expressions in themselves. @menu -* Boolean Values:: Boolean values. -* Missing Values in Expressions:: Using missing values in expressions. -* Grouping Operators:: parentheses -* Arithmetic Operators:: add sub mul div pow -* Logical Operators:: AND NOT OR -* Relational Operators:: EQ GE GT LE LT NE -* Functions:: More-sophisticated operators. -* Order of Operations:: Operator precedence. +* Boolean Values:: Boolean values +* Missing Values in Expressions:: Using missing values in expressions +* Grouping Operators:: parentheses +* Arithmetic Operators:: add sub mul div pow +* Logical Operators:: AND NOT OR +* Relational Operators:: EQ GE GT LE LT NE +* Functions:: More-sophisticated operators +* Order of Operations:: Operator precedence @end menu @node Boolean Values @@ -259,7 +259,7 @@ The sections below describe each function in detail. * Statistical Functions:: CFVAR MAX MEAN MIN SD SUM VARIANCE * String Functions:: CONCAT INDEX LENGTH LOWER LPAD LTRIM NUMBER RINDEX RPAD RTRIM STRING SUBSTR UPCASE -* Time & Date:: CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx +* Time and Date:: CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx DATEDIFF DATESUM * Miscellaneous Functions:: LAG YRMODA VALUELABEL * Statistical Distribution Functions:: PDF CDF SIG IDF RV NPDF NCDF @@ -691,7 +691,7 @@ has value @code{"cd"}; @code{SUBSTR("nonsense", 4, 10)} has the value Returns @var{string}, changing lowercase letters to uppercase letters. @end deftypefn -@node Time & Date +@node Time and Date @subsection Time & Date Functions @cindex functions, time & date @cindex times @@ -702,17 +702,17 @@ For compatibility, PSPP considers dates before 15 Oct 1582 invalid. Most time and date functions will not accept earlier dates. @menu -* Time & Date Concepts:: How times & dates are defined and represented +* Time and Date Concepts:: How times & dates are defined and represented * Time Construction:: TIME.@{DAYS HMS@} * Time Extraction:: CTIME.@{DAYS HOURS MINUTES SECONDS@} * Date Construction:: DATE.@{DMY MDY MOYR QYR WKYR YRDAY@} * Date Extraction:: XDATE.@{DATE HOUR JDAY MDAY MINUTE MONTH QUARTER SECOND TDAY TIME WEEK WKDAY YEAR@} -* Time & Date Arithmetic:: DATEDIFF DATESUM +* Time and Date Arithmetic:: DATEDIFF DATESUM @end menu -@node Time & Date Concepts +@node Time and Date Concepts @subsubsection How times & dates are defined and represented @cindex time, concepts @@ -1001,7 +1001,7 @@ Returns the year (as an integer 1582 or greater) corresponding to @var{date}. @end deftypefn -@node Time & Date Arithmetic +@node Time and Date Arithmetic @subsubsection Time and Date Arithmetic @cindex time, mathematical properties of @@ -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..3052162f 100644 --- a/doc/invoking.texi +++ b/doc/invoking.texi @@ -1,18 +1,46 @@ @node Invocation -@chapter Invoking PSPP + +@chapter Starting PSPP @cindex invocation @cindex PSPP, invoking +There are two separate user interfaces for PSPP. +There is the command line interface, which responds to commands +typed by the user. +The command line interface is generally available on more platforms +than the graphic user interface and since it doesn't require a +graphics device it can be used from a remote terminal. +Platforms which have a windowing system may also be able to support +the graphic user interface. +The graphic user interface can perform all functionality of the +command line interface. +In addition it gives an instantaneous view of the data, variables and +statistical output. + +Whichever interface you choose, a basic understanding of the concepts +used by PSPP is necessary before effective use of the system can be achieved. + + +@menu +* The command line user interface:: +* The graphic user interface:: +@end menu + + +@node The command line user interface +@section The command line user interface + @cindex command line, options @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 @@ -26,7 +54,7 @@ pspp [ -B @var{dir} | --config-dir=@var{dir} ] [ -o @var{device} | --device=@var @end menu @node Non-option Arguments -@section Non-option Arguments +@subsection Non-option Arguments Syntax files and output device substitutions can be specified on PSPP's command line: @@ -64,7 +92,7 @@ typing its name. You can include any options on the command line as usual. PSPP entirely ignores any lines beginning with @samp{#!}. @node Configuration Options -@section Configuration Options +@subsection Configuration Options Configuration options are used to change PSPP's configuration for the current run. The configuration options are: @@ -95,25 +123,12 @@ option disables all devices besides those mentioned on the command line. @end table @node Input and output options -@section Input and output options +@subsection Input and output options 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 +142,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 @@ -140,7 +149,7 @@ check} and similar scripts. @end table @node Language control options -@section Language control options +@subsection Language control options Language control options control how PSPP syntax files are parsed and interpreted. The available language control options are: @@ -158,16 +167,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 @@ -181,7 +180,7 @@ HOST commands, as well as use of pipes as input and output files. @end table @node Informational options -@section Informational options +@subsection Informational options Informational options cause information about PSPP to be written to the terminal. Here are the available options: @@ -253,4 +252,19 @@ Individual directories included in file searches. Each verbosity level also includes messages from lower verbosity levels. @end table -@setfilename ignored + + +@node The graphic user interface +@section The graphic user interface + +@cindex Graphic user interface +@cindex PSPPIRE + +The graphic user interface can be started by typing @command{psppire} at a +command prompt. +Alternatively many systems have a system of interactive menus or buttons +from which @command{psppire} can be started by a series of mouse clicks. + +Once the principles of the PSPP system are understood, +the graphic user interface is designed to be largely intuitive, and +for this reason is covered only very briefly by this manual. diff --git a/doc/language.texi b/doc/language.texi index 13454336..e23a5580 100644 --- a/doc/language.texi +++ b/doc/language.texi @@ -3,11 +3,9 @@ @cindex language, PSPP @cindex PSPP, language -@quotation -@strong{Please note:} PSPP is not even close to completion. +@note{PSPP is not even close to completion. Only a few statistical procedures are implemented. PSPP -is a work in progress. -@end quotation +is a work in progress.} This chapter discusses elements common to many PSPP commands. Later chapters will describe individual commands in detail. @@ -381,9 +379,7 @@ spaces. Variables, whether numeric or string, can have designated @dfn{user-missing values}. Every user-missing value is an actual value for that variable. However, most of the time user-missing values are -treated in the same way as the system-missing value. String variables -that are wider than a certain width, usually 8 characters (depending on -computer architecture), cannot have user-missing values. +treated in the same way as the system-missing value. For more information on missing values, see the following sections: @ref{Variables}, @ref{MISSING VALUES}, @ref{Expressions}. See also the @@ -449,13 +445,9 @@ Numeric or string. @item Width (string variables only) String variables with a width of 8 characters or fewer are called @dfn{short string variables}. Short string variables -can be used in many procedures where @dfn{long string variables} (those +may be used in a few contexts where @dfn{long string variables} (those with widths greater than 8) are not allowed. -Certain systems may consider strings longer than 8 -characters to be short strings. Eight characters represents a minimum -figure for the maximum length of a short string. - @item Position Variables in the dictionary are arranged in a specific order. @cmd{DISPLAY} can be used to show this order: see @ref{DISPLAY}. @@ -497,6 +489,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 +1164,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 +1486,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..9c50904e 100644 --- a/doc/pspp.texinfo +++ b/doc/pspp.texinfo @@ -6,6 +6,13 @@ @c @setchapternewpage odd @c %**end of header + +@macro note{param1} +@quotation +@strong{Please note:} \param1\ +@end quotation +@end macro + @include version.texi @macro cmd{CMDNAME} @@ -25,7 +32,7 @@ This manual is for GNU PSPP version @value{VERSION}, software for statistical analysis. -Copyright @copyright{} 1997, 1998, 2004, 2005 Free Software Foundation, Inc. +Copyright @copyright{} 1997, 1998, 2004, 2005, 2009 Free Software Foundation, Inc. @quotation Permission is granted to copy, distribute and/or modify this document @@ -51,6 +58,14 @@ modify this GNU manual.'' @insertcopying @end titlepage +@chapheading Acknowledgements +The authors wish to thank +Network Theory Ltd +@url{http://www.network-theory.co.uk} +for their financial support +in the production of this manual. + + @contents @@ -66,11 +81,13 @@ modify this GNU manual.'' * License:: Your rights and obligations. * Invocation:: Starting and running PSPP. +* Using PSPP:: How to use PSPP --- A brief tutorial. * Language:: Basics of the PSPP command language. * 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. @@ -94,10 +111,12 @@ modify this GNU manual.'' @include license.texi @include invoking.texi +@include tutorial.texi @include language.texi @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..76925d7e 100644 --- a/doc/statistics.texi +++ b/doc/statistics.texi @@ -14,6 +14,8 @@ far. * ONEWAY:: One way analysis of variance. * RANK:: Compute rank scores. * REGRESSION:: Linear regression. +* RELIABILITY:: Reliability analysis. +* ROC:: Receiver Operating Characteristic. @end menu @node DESCRIPTIVES @@ -204,12 +206,13 @@ boundaries of the data set divided into the specified number of ranges. For instance, @code{/NTILES=4} would cause quartiles to be reported. The HISTOGRAM subcommand causes the output to include a histogram for -each specified variable. The X axis by default ranges from the +each specified numeric variable. The X axis by default ranges from the minimum to the maximum value observed in the data, but the MINIMUM and MAXIMUM keywords can set an explicit range. The Y axis by default is labeled in frequencies; use the PERCENT keyword to causes it to be labeled in percent of the total observed count. Specify NORMAL to superimpose a normal curve on the histogram. +Histograms are not created for string variables. The PIECHART adds a pie chart for each variable to the data. Each slice represents one value, with the size of the slice proportional to @@ -232,7 +235,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 +274,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%. @@ -340,9 +349,7 @@ is present, the VARIABLES subcommand must precede the TABLES subcommand. In general mode, numeric and string variables may be specified on -TABLES. Although long string variables are allowed, only their -initial short-string parts are used. In integer mode, only numeric -variables are allowed. +TABLES. In integer mode, only numeric variables are allowed. The MISSING subcommand determines the handling of user-missing values. When set to TABLE, the default, missing values are dropped on a table by @@ -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,22 @@ 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 +* SIGN:: The Sign Test @end menu @@ -524,7 +545,7 @@ produces for each variable that is the subject of any test. [ /BINOMIAL[(p)]=var_list[(value1[, value2)] ] ] @end display -The binomial test compares the observed distribution of a dichotomous +The /BINOMIAL subcommand compares the observed distribution of a dichotomous variable with that of a binomial distribution. The variable @var{p} specifies the test proportion of the binomial distribution. @@ -564,7 +585,7 @@ even for very large sample sizes. @node CHISQUARE -@subsection Chisquare test +@subsection Chisquare Test @vindex CHISQUARE @cindex chisquare test @@ -574,7 +595,7 @@ even for very large sample sizes. @end display -The chisquare test produces a chi-square statistic for the differences +The /CHISQUARE subcommand produces a chi-square statistic for the differences between the expected and observed frequencies of the categories of a variable. Optionally, a range of values may appear after the variable list. If a range is given, then non integer values are truncated, and values @@ -591,6 +612,59 @@ sum of the frequencies need not be 1. If no /EXPECTED subcommand is given, then then equal frequencies are expected. +@node WILCOXON +@subsection Wilcoxon Matched Pairs Signed Ranks Test +@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 medians of the +variables listed. +The test does not make any assumptions about the variances of the samples. +It does however assume that the distribution is symetrical. + +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. + + +@node SIGN +@subsection Sign Test +@vindex SIGN +@cindex sign test + +@display + [ /SIGN varlist [ WITH varlist [ (PAIRED) ]]] +@end display + +The /SIGN subcommand tests for differences between medians of the +variables listed. +The test does not make any assumptions about the +distribution of the data. + +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. @node T-TEST @comment node-name, next, previous, up @@ -766,7 +840,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 +904,120 @@ 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. + + + +@node ROC +@section ROC + +@vindex ROC +@cindex Receiver Operating Characterstic +@cindex Area under curve + +@display +ROC @var{var_list} BY @var{state_var} (@var{state_value}) + /PLOT = @{ CURVE [(REFERENCE)], NONE @} + /PRINT = [ SE ] [ COORDINATES ] + /CRITERIA = [ CUTOFF(@{INCLUDE,EXCLUDE@}) ] + [ TESTPOS (@{LARGE,SMALL@}) ] + [ CI (@var{confidence}) ] + [ DISTRIBUTION (@{FREE, NEGEXPO @}) ] + /MISSING=@{EXCLUDE,INCLUDE@} +@end display + + +The @cmd{ROC} command is used to plot the receiver operating characteristic curve +of a dataset, and to estimate the area under the curve. +This is useful for analysing the efficacy of a variable as a predictor of a state of nature. + +The mandatory @var{var_list} is the list of predictor variables. +The variable @var{state_var} is the variable whose values represent the actual states, +and @var{state_value} is the value of this variable which represents the positive state. + +The optional subcommand PLOT is used to determine if and how the ROC curve is drawn. +The keyword CURVE means that the ROC curve should be drawn, and the optional keyword REFERENCE, +which should be enclosed in parentheses, says that the diagonal reference line should be drawn. +If the keyword NONE is given, then no ROC curve is drawn. +By default, the curve is drawn with no reference line. + +The optional subcommand PRINT determines which additional tables should be printed. +Two additional tables are available. +The SE keyword says that standard error of the area under the curve should be printed as well as +the area itself. +In addition, a p-value under the null hypothesis that the area under the curve equals 0.5 will be +printed. +The COORDINATES keyword says that a table of coordinates of the ROC curve should be printed. + +The CRITERIA subcommand has four optional parameters: +@itemize @bullet +@item The TESTPOS parameter may be LARGE or SMALL. +LARGE is the default, and says that larger values in the predictor variables are to be +considered positive. SMALL indicates that smaller values should be considered positive. + +@item The CI parameter specifies the confidence interval that should be printed. +It has no effect if the SE keyword in the PRINT subcommand has not been given. + +@item The DISTRIBUTION parameter determines the method to be used when estimating the area +under the curve. +There are two possibilities, @i{viz}: FREE and NEGEXPO. +The FREE method uses a non-parametric estimate, and the NEGEXPO method a bi-negative +exponential distribution estimate. +The NEGEXPO method should only be used when the number of positive actual states is +equal to the number of negative actual states. +The default is FREE. + +@item The CUTOFF parameter is for compatibility and is ignored. +@end itemize + +The MISSING subcommand determines whether user missing values are to +be included or excluded in the analysis. The default behaviour is to +exclude them. +Cases are excluded on a listwise basis; if any of the variables in @var{var_list} +or if the variable @var{state_var} is missing, then the entire case will be +excluded. + + diff --git a/doc/transformation.texi b/doc/transformation.texi index 27bbb2da..fe08b9cd 100644 --- a/doc/transformation.texi +++ b/doc/transformation.texi @@ -83,9 +83,9 @@ 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. +and string variables. The available aggregation functions are as follows: @@ -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. @@ -236,7 +239,7 @@ COMPUTE vector(index) = expression. @cmd{COMPUTE} assigns the value of an expression to a target variable. For each case, the expression is evaluated and its value -assigned to the target variable. Numeric and short and long string +assigned to the target variable. Numeric and string variables may be assigned. When a string expression's width differs from the target variable's width, the string result of the expression is truncated or padded with spaces on the right as necessary. The @@ -287,7 +290,7 @@ one or more @dfn{test} variables for each case. The target variable values are always nonnegative integers. They are never missing. The target variable is assigned an F8.2 output format. -@xref{Input and Output Formats}. Any variables, including long and short +@xref{Input and Output Formats}. Any variables, including string variables, may be test variables. User-missing values of test variables are treated just like any other @@ -432,7 +435,7 @@ Specify a boolean-valued expression (@pxref{Expressions}) to be tested following the IF keyword. This expression is evaluated for each case. If the value is true, then the value of the expression is computed and assigned to the specified variable. If the value is false or missing, -nothing is done. Numeric and short and long string variables may be +nothing is done. Numeric and string variables may be assigned. When a string expression's width differs from the target variable's width, the string result of the expression is truncated or padded with spaces on the right as necessary. The expression and @@ -478,7 +481,7 @@ dest_value may take the following forms: @cmd{RECODE} translates data from one range of values to another, via flexible user-specified mappings. Data may be remapped -in-place or copied to new variables. Numeric, short string, and long +in-place or copied to new variables. Numeric and string data can be recoded. Specify the list of source variables, followed by one or more mapping @@ -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/tutorial.texi b/doc/tutorial.texi new file mode 100644 index 00000000..540da62a --- /dev/null +++ b/doc/tutorial.texi @@ -0,0 +1,858 @@ +@alias prompt = sansserif + +@include doc/tut.texi + +@node Using PSPP +@chapter Using PSPP + +PSPP is a tool for the statistical analysis of sampled data. +You can use it to discover patterns in the data, +to explain differences in one subset of data in terms of another subset +and to find out +whether certain beliefs about the data are justified. +This chapter does not attempt to introduce the theory behind the +statistical analysis, +but it shows how such analysis can be performed using PSPP. + +For the purposes of this tutorial, it is assumed that you are using PSPP in its +interactive mode from the command line. +However, the example commands can also be typed into a file and executed in +a post-hoc mode by typing @samp{pspp @var{filename}} at a shell prompt, +where @var{filename} is the name of the file containing the commands. +Alternatively, from the graphical interface, you can select +@clicksequence{File @click{} New @click{} Syntax} to open a new syntax window +and use the @clicksequence{Run} menu when a syntax fragment is ready to be +executed. +Whichever method you choose, the syntax is identical. + +When using the interactive method, PSPP tells you that it's waiting for your +data with a string like @prompt{PSPP>} or @prompt{data>}. +In the examples of this chapter, whenever you see text like this, it +indicates the prompt displayed by PSPP, @emph{not} something that you +should type. + +Throughout this chapter reference is made to a number of sample data files. +So that you can try the examples for yourself, +you should have received these files along with your copy of PSPP.@c +@footnote{These files contain purely fictitious data. They should not be used +for research purposes.} +@note{Normally these files are installed in the directory +@file{@value{example-dir}}. +If however your system administrator or operating system vendor has +chosen to install them in a different location, you will have to adjust +the examples accordingly.} + + +@menu +* Preparation of Data Files:: +* Data Screening and Transformation:: +* Hypothesis Testing:: +@end menu + +@node Preparation of Data Files +@section Preparation of Data Files + + +Before analysis can commence, the data must be loaded into PSPP and +arranged such that both PSPP and humans can understand what +the data represents. +There are two aspects of data: + +@itemize @bullet +@item The variables --- these are the parameters of a quantity + which has been measured or estimated in some way. + For example height, weight and geographic location are all variables. +@item The observations (also called `cases') of the variables --- + each observation represents an instance when the variables were measured + or observed. +@end itemize + +@noindent +For example, a data set which has the variables @var{height}, @var{weight}, and +@var{name}, might have the observations: +@example +1881 89.2 Ahmed +1192 107.01 Frank +1230 67 Julie +@end example +@noindent +The following sections explain how to define a dataset. + +@menu +* Defining Variables:: +* Listing the data:: +* Reading data from a text file:: +* Reading data from a pre-prepared PSPP file:: +* Saving data to a PSPP file.:: +* Reading data from other sources:: +@end menu + +@node Defining Variables +@subsection Defining Variables +@cindex variables + +Variables come in two basic types, @i{viz}: @dfn{numeric} and @dfn{string}. +Variables such as age, height and satisfaction are numeric, +whereas name is a string variable. +String variables are best reserved for commentary data to assist the +human observer. +However they can also be used for nominal or categorical data. + + +@ref{data-list} defines two variables @var{forename} and @var{height}, +and reads data into them by manual input. + +@float Example, data-list +@cartouche +@example +@prompt{PSPP>} data list list /forename (A12) height. +@prompt{PSPP>} begin data. +@prompt{data>} Ahmed 188 +@prompt{data>} Bertram 167 +@prompt{data>} Catherine 134.231 +@prompt{data>} David 109.1 +@prompt{data>} end data +@prompt{PSPP>} +@end example +@end cartouche +@caption{Manual entry of data using the @cmd{DATA LIST} command. +Two variables +@var{forename} and @var{height} are defined and subsequently filled +with manually entered data.} +@end float + +There are several things to note about this example. + +@itemize @bullet +@item +The words @samp{data list list} are an example of the @cmd{DATA LIST} +command. @xref{DATA LIST}. +It tells PSPP to prepare for reading data. +The word @samp{list} intentionally appears twice. +The first occurrence is part of the @cmd{DATA LIST} call, +whilst the second +tells PSPP that the data is to be read as free format data with +one record per line. + +@item +The @samp{/} character is important. It marks the start of the list of +variables which you wish to define. + +@item +The text @samp{forename} is the name of the first variable, +and @samp{(A12)} says that the variable @var{forename} is a string +variable and that its maximum length is 12 bytes. +The second variable's name is specified by the text @samp{height}. +Since no format is given, this variable has the default format. +For more information on data formats, @pxref{Input and Output Formats}. + + +@item +Normally, PSPP displays the prompt @prompt{PSPP>} whenever it's +expecting a command. +However, when it's expecting data, the prompt changes to @prompt{data>} +so that you know to enter data and not a command. + +@item +At the end of every command there is a terminating @samp{.} which tells +PSPP that the end of a command has been encountered. +You should not enter @samp{.} when data is expected (@i{ie.} when +the @prompt{data>} prompt is current) since it is appropriate only for +terminating commands. +@end itemize + +@node Listing the data +@subsection Listing the data +@vindex LIST + +Once the data has been entered, +you could type +@example +@prompt{PSPP>} list /format=numbered. +@end example +@noindent +to list the data. +The optional text @samp{/format=numbered} requests the case numbers to be +shown along with the data. +It should show the following output: +@example +@group +Case# forename height +----- ------------ -------- + 1 Ahmed 188.00 + 2 Bertram 167.00 + 3 Catherine 134.23 + 4 David 109.10 +@end group +@end example +@noindent +Note that the numeric variable @var{height} is displayed to 2 decimal +places, because the format for that variable is @samp{F8.2}. +For a complete description of the @cmd{LIST} command, @pxref{LIST}. + +@node Reading data from a text file +@subsection Reading data from a text file +@cindex reading data + +The previous example showed how to define a set of variables and to +manually enter the data for those variables. +Manual entering of data is tedious work, and often +a file containing the data will be have been previously +prepared. +Let us assume that you have a file called @file{mydata.dat} containing the +ascii encoded data: +@example +Ahmed 188.00 +Bertram 167.00 +Catherine 134.23 +David 109.10 +@ . +@ . +@ . +Zachariah 113.02 +@end example +@noindent +You can can tell the @cmd{DATA LIST} command to read the data directly from +this file instead of by manual entry, with a command like: +@example +@prompt{PSPP>} data list file='mydata.dat' list /forename (A12) height. +@end example +@noindent +Notice however, that it is still necessary to specify the names of the +variables and their formats, since this information is not contained +in the file. +It is also possible to specify the file's character encoding and other +parameters. +For full details refer to @pxref{DATA LIST}. + +@node Reading data from a pre-prepared PSPP file +@subsection Reading data from a pre-prepared PSPP file +@cindex system files +@vindex GET + +When working with other PSPP users, or users of other software which +uses the PSPP data format, you may be given the data in +a pre-prepared PSPP file. +Such files contain not only the data, but the variable definitions, +along with their formats, labels and other meta-data. +Conventionally, these files (sometimes called ``system'' files) +have the suffix @file{.sav}, but that is +not mandatory. +The following syntax loads a file called @file{my-file.sav}. +@example +@prompt{PSPP>} get file='my-file.sav'. +@end example +@noindent +You will encounter several instances of this in future examples. + + +@node Saving data to a PSPP file. +@subsection Saving data to a PSPP file. +@cindex saving +@vindex SAVE + +If you want to save your data, along with the variable definitions so +that you or other PSPP users can use it later, you can do this with +the @cmd{SAVE} command. + +The following syntax will save the existing data and variables to a +file called @file{my-new-file.sav}. +@example +@prompt{PSPP>} save outfile='my-new-file.sav'. +@end example +@noindent +If @file{my-new-file.sav} already exists, then it will be overwritten. +Otherwise it will be created. + + +@node Reading data from other sources +@subsection Reading data from other sources +@cindex comma separated values +@cindex spreadsheets +@cindex databases + +Sometimes it's useful to be able to read data from comma +separated text, from spreadsheets, databases or other sources. +In these instances you should +use the @cmd{GET DATA} command (@pxref{GET DATA}). + + +@node Data Screening and Transformation +@section Data Screening and Transformation + +@cindex screening +@cindex transformation + +Once data has been entered, it is often desirable, or even necessary, +to transform it in some way before performing analysis upon it. +At the very least, it's good practice to check for errors. + +@menu +* Identifying incorrect data:: +* Dealing with suspicious data:: +* Inverting negatively coded variables:: +* Testing data consistency:: +* Testing for normality :: +@end menu + +@node Identifying incorrect data +@subsection Identifying incorrect data +@cindex erroneous data +@cindex errors, in data + +Data from real sources is rarely error free. +PSPP has a number of procedures which can be used to help +identify data which might be incorrect. + +The @cmd{DESCRIPTIVES} command (@pxref{DESCRIPTIVES}) is used to generate +simple linear statistics for a dataset. It is also useful for +identifying potential problems in the data. +The example file @file{physiology.sav} contains a number of physiological +measurements of a sample of healthy adults selected at random. +However, the data entry clerk made a number of mistakes when entering +the data. +@ref{descriptives} illustrates the use of @cmd{DESCRIPTIVES} to screen this +data and identify the erroneous values. + +@float Example, descriptives +@cartouche +@example +@prompt{PSPP>} get file='@value{example-dir}/physiology.sav'. +@prompt{PSPP>} descriptives sex, weight, height. +@end example + +Output: +@example +DESCRIPTIVES. Valid cases = 40; cases with missing value(s) = 0. ++--------#--+-------+-------+-------+-------+ +|Variable# N| Mean |Std Dev|Minimum|Maximum| +#========#==#=======#=======#=======#=======# +|sex #40| .45| .50| .00| 1.00| +|height #40|1677.12| 262.87| 179.00|1903.00| +|weight #40| 72.12| 26.70| -55.60| 92.07| ++--------#--+-------+-------+-------+-------+ +@end example +@end cartouche +@caption{Using the @cmd{DESCRIPTIVES} command to display simple +summary information about the data. +In this case, the results show unexpectedly low values in the Minimum +column, suggesting incorrect data entry.} +@end float + +In the output of @ref{descriptives}, +the most interesting column is the minimum value. +The @var{weight} variable has a minimum value of less than zero, +which is clearly erroneous. +Similarly, the @var{height} variable's minimum value seems to be very low. +In fact, it is more than 5 standard deviations from the mean, and is a +seemingly bizarre height for an adult person. +We can examine the data in more detail with the @cmd{EXAMINE} +command (@pxref{EXAMINE}): + +In @ref{examine} you can see that the lowest value of @var{height} is +179 (which we suspect to be erroneous), but the second lowest is 1598 +which +we know from the @cmd{DESCRIPTIVES} command +is within 1 standard deviation from the mean. +Similarly the @var{weight} variable has a lowest value which is +negative but a plausible value for the second lowest value. +This suggests that the two extreme values are outliers and probably +represent data entry errors. + +@float Example, examine +@cartouche +[@dots{} continue from @ref{descriptives}] +@example +@prompt{PSPP>} examine height, weight /statistics=extreme(3). +@end example + +Output: +@example +#===============================#===========#=======# +# #Case Number| Value # +#===============================#===========#=======# +#Height in millimetres Highest 1# 14|1903.00# +# 2# 15|1884.00# +# 3# 12|1801.65# +# ----------#-----------+-------# +# Lowest 1# 30| 179.00# +# 2# 31|1598.00# +# 3# 28|1601.00# +# ----------#-----------+-------# +#Weight in kilograms Highest 1# 13| 92.07# +# 2# 5| 92.07# +# 3# 17| 91.74# +# ----------#-----------+-------# +# Lowest 1# 38| -55.60# +# 2# 39| 54.48# +# 3# 33| 55.45# +#===============================#===========#=======# +@end example +@end cartouche +@caption{Using the @cmd{EXAMINE} command to see the extremities of the data +for different variables. Cases 30 and 38 seem to contain values +very much lower than the rest of the data. +They are possibly erroneous.} +@end float + +@node Dealing with suspicious data +@subsection Dealing with suspicious data + +@cindex SYSMIS +@cindex recoding data +If possible, suspect data should be checked and re-measured. +However, this may not always be feasible, in which case the researcher may +decide to disregard these values. +PSPP has a feature whereby data can assume the special value `SYSMIS', and +will be disregarded in future analysis. @xref{Missing Observations}. +You can set the two suspect values to the `SYSMIS' value using the @cmd{RECODE} +command. +@example +PSPP> recode height (179 = SYSMIS). +PSPP> recode weight (LOWEST THRU 0 = SYSMIS). +@end example +@noindent +The first command says that for any observation which has a +@var{height} value of 179, that value should be changed to the SYSMIS +value. +The second command says that any @var{weight} values of zero or less +should be changed to SYSMIS. +From now on, they will be ignored in analysis. +For detailed information about the @cmd{RECODE} command @pxref{RECODE}. + +If you now re-run the @cmd{DESCRIPTIVES} or @cmd{EXAMINE} commands in +@ref{descriptives} and @ref{examine} you +will see a data summary with more plausible parameters. +You will also notice that the data summaries indicate the two missing values. + +@node Inverting negatively coded variables +@subsection Inverting negatively coded variables + +@cindex Likert scale +@cindex Inverting data +Data entry errors are not the only reason for wanting to recode data. +The sample file @file{hotel.sav} comprises data gathered from a +customer satisfaction survey of clients at a particular hotel. +In @ref{reliability}, this file is loaded for analysis. +The line @code{display dictionary.} tells PSPP to display the +variables and associated data. +The output from this command has been omitted from the example for the sake of clarity, but +you will notice that each of the variables +@var{v1}, @var{v2} @dots{} @var{v5} are measured on a 5 point Likert scale, +with 1 meaning ``Strongly disagree'' and 5 meaning ``Strongly agree''. +Whilst variables @var{v1}, @var{v2} and @var{v4} record responses +to a positively posed question, variables @var{v3} and @var{v5} are +responses to negatively worded questions. +In order to perform meaningful analysis, we need to recode the variables so +that they all measure in the same direction. +We could use the @cmd{RECODE} command, with syntax such as: +@example +recode v3 (1 = 5) (2 = 4) (4 = 2) (5 = 1). +@end example +@noindent +However an easier and more elegant way uses the @cmd{COMPUTE} +command (@pxref{COMPUTE}). +Since the variables are Likert variables in the range (1 @dots{} 5), +subtracting their value from 6 has the effect of inverting them: +@example +compute @var{var} = 6 - @var{var}. +@end example +@noindent +@ref{reliability} uses this technique to recode the variables +@var{v3} and @var{v5}. +After applying @cmd{COMPUTE} for both variables, +all subsequent commands will use the inverted values. + + +@node Testing data consistency +@subsection Testing data consistency + +@cindex reliability +@cindex consistency + +A sensible check to perform on survey data is the calculation of +reliability. +This gives the statistician some confidence that the questionnaires have been +completed thoughtfully. +If you examine the labels of variables @var{v1}, @var{v3} and @var{v5}, +you will notice that they ask very similar questions. +One would therefore expect the values of these variables (after recoding) +to closely follow one another, and we can test that with the @cmd{RELIABILITY} +command (@pxref{RELIABILITY}). +@ref{reliability} shows a PSPP session where the user (after recoding +negatively scaled variables) requests reliability statistics for +@var{v1}, @var{v3} and @var{v5}. + +@float Example, reliability +@cartouche +@example +@prompt{PSPP>} get file='@value{example-dir}/hotel.sav'. +@prompt{PSPP>} display dictionary. +@prompt{PSPP>} * recode negatively worded questions. +@prompt{PSPP>} compute v3 = 6 - v3. +@prompt{PSPP>} compute v5 = 6 - v5. +@prompt{PSPP>} reliability v1, v3, v5. +@end example + +Output (dictionary information omitted for clarity): +@example +1.1 RELIABILITY. Case Processing Summary +#==============#==#======# +# # N| % # +#==============#==#======# +#Cases Valid #17|100.00# +# Excluded# 0| .00# +# Total #17|100.00# +#==============#==#======# + +1.2 RELIABILITY. Reliability Statistics +#================#==========# +#Cronbach's Alpha#N of items# +#================#==========# +# .86# 3# +#================#==========# +@end example +@end cartouche +@caption{Recoding negatively scaled variables, and testing for +reliability with the @cmd{RELIABILITY} command. The Cronbach Alpha +coefficient suggests a high degree of reliability among variables +@var{v1}, @var{v2} and @var{v5}.} +@end float + +As a rule of thumb, many statisticians consider a value of Cronbach's Alpha of +0.7 or higher to indicate reliable data. +Here, the value is 0.86 so the data and the recoding that we performed +are vindicated. + + +@node Testing for normality +@subsection Testing for normality +@cindex normality, testing + +Many statistical tests rely upon certain properties of the data. +One common property, upon which many linear tests depend, is that of +normality --- the data must have been drawn from a normal distribution. +It is necessary then to ensure normality before deciding upon the +test procedure to use. One way to do this uses the @cmd{EXAMINE} command. + +In @ref{normality}, a researcher was examining the failure rates +of equipment produced by an engineering company. +The file @file{repairs.sav} contains the mean time between +failures (@var{mtbf}) of some items of equipment subject to the study. +Before performing linear analysis on the data, +the researcher wanted to ascertain that the data is normally distributed. + +A normal distribution has a skewness and kurtosis of zero. +Looking at the skewness of @var{mtbf} in @ref{normality} it is clear +that the mtbf figures have a lot of positive skew and are therefore +not drawn from a normally distributed variable. +Positive skew can often be compensated for by applying a logarithmic +transformation. +This is done with the @cmd{COMPUTE} command in the line +@example +compute mtbf_ln = ln (mtbf). +@end example +@noindent +Rather than redefining the existing variable, this use of @cmd{COMPUTE} +defines a new variable @var{mtbf_ln} which is +the natural logarithm of @var{mtbf}. +The final command in this example calls @cmd{EXAMINE} on this new variable, +and it can be seen from the results that both the skewness and +kurtosis for @var{mtbf_ln} are very close to zero. +This provides some confidence that the @var{mtbf_ln} variable is +normally distributed and thus safe for linear analysis. +In the event that no suitable transformation can be found, +then it would be worth considering +an appropriate non-parametric test instead of a linear one. +@xref{NPAR TESTS}, for information about non-parametric tests. + +@float Example, normality +@cartouche +@example +@prompt{PSPP>} get file='@value{example-dir}/repairs.sav'. +@prompt{PSPP>} examine mtbf + /statistics=descriptives. +@prompt{PSPP>} compute mtbf_ln = ln (mtbf). +@prompt{PSPP>} examine mtbf_ln + /statistics=descriptives. +@end example + +Output: +@example +1.2 EXAMINE. Descriptives +#====================================================#=========#==========# +# #Statistic|Std. Error# +#====================================================#=========#==========# +#mtbf Mean # 8.32 | 1.62 # +# 95% Confidence Interval for Mean Lower Bound# 4.85 | # +# Upper Bound# 11.79 | # +# 5% Trimmed Mean # 7.69 | # +# Median # 8.12 | # +# Variance # 39.21 | # +# Std. Deviation # 6.26 | # +# Minimum # 1.63 | # +# Maximum # 26.47 | # +# Range # 24.84 | # +# Interquartile Range # 5.83 | # +# Skewness # 1.85 | .58 # +# Kurtosis # 4.49 | 1.12 # +#====================================================#=========#==========# + +2.2 EXAMINE. Descriptives +#====================================================#=========#==========# +# #Statistic|Std. Error# +#====================================================#=========#==========# +#mtbf_ln Mean # 1.88 | .19 # +# 95% Confidence Interval for Mean Lower Bound# 1.47 | # +# Upper Bound# 2.29 | # +# 5% Trimmed Mean # 1.88 | # +# Median # 2.09 | # +# Variance # .54 | # +# Std. Deviation # .74 | # +# Minimum # .49 | # +# Maximum # 3.28 | # +# Range # 2.79 | # +# Interquartile Range # .92 | # +# Skewness # -.16 | .58 # +# Kurtosis # -.09 | 1.12 # +#====================================================#=========#==========# +@end example +@end cartouche +@caption{Testing for normality using the @cmd{EXAMINE} command and applying +a logarithmic transformation. +The @var{mtbf} variable has a large positive skew and is therefore +unsuitable for linear statistical analysis. +However the transformed variable (@var{mtbf_ln}) is close to normal and +would appear to be more suitable.} +@end float + + +@node Hypothesis Testing +@section Hypothesis Testing + +@cindex Hypothesis testing +@cindex p-value +@cindex null hypothesis + +One of the most fundamental purposes of statistical analysis +is hypothesis testing. +Researchers commonly need to test hypotheses about a set of data. +For example, she might want to test whether one set of data comes from +the same distribution as another, +or +whether the mean of a dataset significantly differs from a particular +value. +This section presents just some of the possible tests that PSPP offers. + +The researcher starts by making a @dfn{null hypothesis}. +Often this is a hypothesis which he suspects to be false. +For example, if he suspects that @var{A} is greater than @var{B} he will +state the null hypothesis as @math{ @var{A} = @var{B}}.@c +@footnote{This example assumes that it is already proven that @var{B} is +not greater than @var{A}.} + +The @dfn{p-value} is a recurring concept in hypothesis testing. +It is the highest acceptable probability that the evidence implying a +null hypothesis is false, could have been obtained when the null +hypothesis is in fact true. +Note that this is not the same as ``the probability of making an +error'' nor is it the same as ``the probability of rejecting a +hypothesis when it is true''. + + + +@menu +* Testing for differences of means:: +* Linear Regression:: +@end menu + +@node Testing for differences of means +@subsection Testing for differences of means + +@cindex T-test +@vindex T-TEST + +A common statistical test involves hypotheses about means. +The @cmd{T-TEST} command is used to find out whether or not two separate +subsets have the same mean. + +@ref{t-test} uses the file @file{physiology.sav} previously +encountered. +A researcher suspected that the heights and core body +temperature of persons might be different depending upon their sex. +To investigate this, he posed two null hypotheses: +@itemize @bullet +@item The mean heights of males and females in the population are equal. +@item The mean body temperature of males and + females in the population are equal. +@end itemize +@noindent +For the purposes of the investigation the researcher +decided to use a p-value of 0.05. + +In addition to the T-test, the @cmd{T-TEST} command also performs the +Levene test for equal variances. +If the variances are equal, then a more powerful form of the T-test can be used. +However if it is unsafe to assume equal variances, +then an alternative calculation is necessary. +PSPP performs both calculations. + +For the @var{height} variable, the output shows the significance of the +Levene test to be 0.33 which means there is a +33% probability that the +Levene test produces this outcome when the variances are unequal. +Such a probability is too high +to assume that the variances are equal so the row +for unequal variances should be used. +Examining this row, the two tailed significance for the @var{height} t-test +is less than 0.05, so it is safe to reject the null hypothesis and conclude +that the mean heights of males and females are unequal. + +For the @var{temperature} variable, the significance of the Levene test +is 0.58 so again, it is unsafe to use the row for equal variances. +The unequal variances row indicates that the two tailed significance for +@var{temperature} is 0.19. Since this is greater than 0.05 we must reject +the null hypothesis and conclude that there is insufficient evidence to +suggest that the body temperature of male and female persons are different. + +@float Example, t-test +@cartouche +@example +@prompt{PSPP>} get file='@value{example-dir}/physiology.sav'. +@prompt{PSPP>} recode height (179 = SYSMIS). +@prompt{PSPP>} t-test group=sex(0,1) /variables = height temperature. +@end example +Output: +@example +1.1 T-TEST. Group Statistics +#==================#==#=======#==============#========# +# sex | N| Mean |Std. Deviation|SE. Mean# +#==================#==#=======#==============#========# +#height Male |22|1796.49| 49.71| 10.60# +# Female|17|1610.77| 25.43| 6.17# +#temperature Male |22| 36.68| 1.95| .42# +# Female|18| 37.43| 1.61| .38# +#==================#==#=======#==============#========# +1.2 T-TEST. Independent Samples Test +#===========================#=========#=============================== =# +# # Levene's| t-test for Equality of Means # +# #----+----+------+-----+------+---------+- -# +# # | | | | | | # +# # | | | |Sig. 2| | # +# # F |Sig.| t | df |tailed|Mean Diff| # +#===========================#====#====#======#=====#======#=========#= =# +#height Equal variances# .97| .33| 14.02|37.00| .00| 185.72| ... # +# Unequal variances# | | 15.15|32.71| .00| 185.72| ... # +#temperature Equal variances# .31| .58| -1.31|38.00| .20| -.75| ... # +# Unequal variances# | | -1.33|37.99| .19| -.75| ... # +#===========================#====#====#======#=====#======#=========#= =# +@end example +@end cartouche +@caption{The @cmd{T-TEST} command tests for differences of means. +Here, the @var{height} variable's two tailed significance is less than +0.05, so the null hypothesis can be rejected. +Thus, the evidence suggests there is a difference between the heights of +male and female persons. +However the significance of the test for the @var{temperature} +variable is greater than 0.05 so the null hypothesis cannot be +rejected, and there is insufficient evidence to suggest a difference +in body temperature.} +@end float + +@node Linear Regression +@subsection Linear Regression +@cindex linear regression +@vindex REGRESSION + +Linear regression is a technique used to investigate if and how a variable +is linearly related to others. +If a variable is found to be linearly related, then this can be used to +predict future values of that variable. + +In example @ref{regression}, the service department of the company wanted to +be able to predict the time to repair equipment, in order to improve +the accuracy of their quotations. +It was suggested that the time to repair might be related to the time +between failures and the duty cycle of the equipment. +The p-value of 0.1 was chosen for this investigation. +In order to investigate this hypothesis, the @cmd{REGRESSION} command +was used. +This command not only tests if the variables are related, but also +identifies the potential linear relationship. @xref{REGRESSION}. + + +@float Example, regression +@cartouche +@example +@prompt{PSPP>} get file='@value{example-dir}/repairs.sav'. +@prompt{PSPP>} regression /variables = mtbf duty_cycle /dependent = mttr. +@prompt{PSPP>} regression /variables = mtbf /dependent = mttr. +@end example +Output: +@example +1.3(1) REGRESSION. Coefficients +#=============================================#====#==========#====#=====# +# # B |Std. Error|Beta| t # +#========#====================================#====#==========#====#=====# +# |(Constant) #9.81| 1.50| .00| 6.54# +# |Mean time between failures (months) #3.10| .10| .99|32.43# +# |Ratio of working to non-working time#1.09| 1.78| .02| .61# +# | # | | | # +#========#====================================#====#==========#====#=====# + +1.3(2) REGRESSION. Coefficients +#=============================================#============# +# #Significance# +#========#====================================#============# +# |(Constant) # .10# +# |Mean time between failures (months) # .00# +# |Ratio of working to non-working time# .55# +# | # # +#========#====================================#============# +2.3(1) REGRESSION. Coefficients +#============================================#=====#==========#====#=====# +# # B |Std. Error|Beta| t # +#========#===================================#=====#==========#====#=====# +# |(Constant) #10.50| .96| .00|10.96# +# |Mean time between failures (months)# 3.11| .09| .99|33.39# +# | # | | | # +#========#===================================#=====#==========#====#=====# + +2.3(2) REGRESSION. Coefficients +#============================================#============# +# #Significance# +#========#===================================#============# +# |(Constant) # .06# +# |Mean time between failures (months)# .00# +# | # # +#========#===================================#============# +@end example +@end cartouche +@caption{Linear regression analysis to find a predictor for +@var{mttr}. +The first attempt, including @var{duty_cycle}, produces some +unacceptable high significance values. +However the second attempt, which excludes @var{duty_cycle}, produces +significance values no higher than 0.06. +This suggests that @var{mtbf} alone may be a suitable predictor +for @var{mttr}.} +@end float + +The coefficients in the first table suggest that the formula +@math{@var{mttr} = 9.81 + 3.1 \times @var{mtbf} + 1.09 \times @var{duty_cycle}} +can be used to predict the time to repair. +However, the significance value for the @var{duty_cycle} coefficient +is very high, which would make this an unsafe predictor. +For this reason, the test was repeated, but omitting the +@var{duty_cycle} variable. +This time, the significance of all coefficients no higher than 0.06, +suggesting that at the 0.06 level, the formula +@math{@var{mttr} = 10.5 + 3.11 \times @var{mtbf}} is a reliable +predictor of the time to repair. + + +@c LocalWords: PSPP dir itemize noindent var cindex dfn cartouche samp xref +@c LocalWords: pxref ie sav Std Dev kilograms SYSMIS sansserif pre pspp emph +@c LocalWords: Likert Cronbach's Cronbach mtbf npplot ln myfile cmd NPAR Sig +@c LocalWords: vindex Levene Levene's df Diff clicksequence mydata dat ascii +@c LocalWords: mttr outfile diff --git a/doc/utilities.texi b/doc/utilities.texi index 35ea20e2..6882aa21 100644 --- a/doc/utilities.texi +++ b/doc/utilities.texi @@ -374,8 +374,10 @@ SET /COMPRESSION=@{ON,OFF@} /SCOMPRESSION=@{ON,OFF@} -(security) +(miscellaneous) /SAFER=ON + /LOCALE='string' + (obsolete settings accepted for compatibility, but ignored) /BOXSTRING=@{'xxx','xxxxxxxxxxx'@} @@ -414,10 +416,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 @@ -698,6 +703,37 @@ Be aware that this setting does not guarantee safety (commands can still overwrite files, for instance) but it is an improvement. When set, this setting cannot be reset during the same session, for obvious security reasons. + +@item LOCALE +@cindex locale +@cindex encoding, characters +This item is used to set the default character encoding. +The encoding may be specified either as an encoding name or alias +(see @url{http://www.iana.org/assignments/character-sets}), or +as a locale name. +If given as a locale name, only the character encoding of the +locale is relevant. + +System files written by PSPP will use this encoding. +System files read by PSPP, for which the encoding is unknown, will be +interpreted using this encoding. + +The full list of valid encodings and locale names/alias are operating system +dependent. +The following are all examples of acceptable syntax on common GNU/Linux +systems. +@example + +SET LOCALE='iso-8859-1'. + +SET LOCALE='ru_RU.cp1251'. + +SET LOCALE='japanese'. + +@end example + +Contrary to the intuition, this command does not affect any aspect +of the system's locale. @end table @node SHOW @@ -784,4 +820,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..c9e270bf 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 @@ -200,9 +212,10 @@ As part of a range, LO or LOWEST may take the place of num1; HI or HIGHEST may take the place of num2. @end display -@cmd{MISSING VALUES} sets user-missing values for numeric and -short string variables. Long string variables may not have missing -values. +@cmd{MISSING VALUES} sets user-missing values for numeric and string +variables. Long string variables may have missing values, but +characters after the first 8 bytes of the missing value must be +spaces. Specify a list of variables, followed by a list of their user-missing values in parentheses. Up to three discrete values may be given, or, @@ -330,8 +343,7 @@ stand for a long value. To set up value labels for a set of variables, specify the variable names after a slash (@samp{/}), followed by a list of values -and their associated labels, separated by spaces. Long string -variables may not be specified. +and their associated labels, separated by spaces. Before @cmd{VALUE LABELS} is executed, any existing value labels are cleared from the variables specified. Use @cmd{ADD VALUE LABELS} @@ -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/automake.mk b/examples/automake.mk index cdbec4bb..b69470a4 100644 --- a/examples/automake.mk +++ b/examples/automake.mk @@ -1,8 +1,14 @@ ## Process this file with automake to produce Makefile.in -*- makefile -*- -EXTRA_DIST += \ + +examplesdir = $(pkgdatadir)/examples + +examples_DATA = \ examples/descript.stat \ + examples/hotel.sav \ + examples/physiology.sav \ + examples/repairs.sav \ examples/regress.stat \ examples/regress_categorical.stat -EXTRA_DIST += examples/OChangeLog +EXTRA_DIST += examples/OChangeLog $(examples_DATA) 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/examples/hotel.sav b/examples/hotel.sav new file mode 100644 index 00000000..49f6318b Binary files /dev/null and b/examples/hotel.sav differ diff --git a/examples/physiology.sav b/examples/physiology.sav new file mode 100644 index 00000000..005a88cd Binary files /dev/null and b/examples/physiology.sav differ diff --git a/examples/repairs.sav b/examples/repairs.sav new file mode 100644 index 00000000..a1991624 Binary files /dev/null and b/examples/repairs.sav differ 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..29eba936 --- /dev/null +++ b/lib/gtk-contrib/psppire-sheet.c @@ -0,0 +1,5534 @@ +/* + 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->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) + ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet)); + + if (sheet->select_status == 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, 3, 3}; + +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->select_status = 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->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->select_status; + + if (sheet->select_status == 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->select_status != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->select_status = 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; + + 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->select_status != PSPPIRE_SHEET_NORMAL) + psppire_sheet_real_unselect_range (sheet, NULL); + + sheet->select_status = 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; + + 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); +} + + +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 = MAX (sheet->cell_padding->left, + MAX (sheet->cell_padding->right, + MAX (sheet->cell_padding->top, + sheet->cell_padding->bottom))); + + 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->button->style = gtk_style_attach (sheet->button->style, + sheet->sheet_window); + + + 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); + + gtk_widget_unmap (sheet->entry_widget); + gtk_widget_unmap (sheet->button); + gtk_widget_unmap (sheet->hover_window->window); +} + +/* 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->select_status != PSPPIRE_SHEET_NORMAL && + psppire_sheet_range_isvisible (sheet, &sheet->range)) + psppire_sheet_range_draw_selection (sheet, drawing_range); + + + if (sheet->select_status == 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->select_status; + 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->select_status != 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->select_status != PSPPIRE_SHEET_NORMAL) + { + sheet->select_status = PSPPIRE_SHEET_NORMAL; + psppire_sheet_real_unselect_range (sheet, NULL); + } + + old_row = sheet->active_cell.row; + old_col = sheet->active_cell.col; + + entry_load_text (sheet); + + /* Erase the old cell border */ + psppire_sheet_draw_active_cell (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->select_status != 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); + + area.width ++; + area.height ++; + + gdk_gc_set_clip_rectangle (sheet->xor_gc, &area); + + area.x += sheet->cell_padding->left / 2; + area.y += sheet->cell_padding->top / 2; + area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2; + area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2; + + gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc, + FALSE, + area.x, + area.y, + area.width, + area.height); + + gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL); +} + + +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->select_status; + +#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->select_status != 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->selection_cell.row = range->rowi; + sheet->selection_cell.col = range->coli; + + sheet->select_status = 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->select_status = GTK_STATE_NORMAL; +} + + +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->select_status != 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; + + + 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 (psppire_sheet_click_cell (sheet, row, column)) + 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)) + { + 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)) + { + 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->select_status == 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; + psppire_sheet_select_range (sheet, NULL); + return TRUE; + } + + if (sheet->select_status != PSPPIRE_SHEET_NORMAL) + { + sheet->select_status = PSPPIRE_SHEET_NORMAL; + psppire_sheet_real_unselect_range (sheet, NULL); + } + else + { + change_active_cell (sheet, row, 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->select_status = 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->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); + + 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->select_status == GTK_STATE_NORMAL) sheet->select_status = 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->select_status == 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; + + gdk_threads_enter (); + 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); + } + } + + gdk_threads_leave (); + 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->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; + if (sheet->select_status == 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->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0; + if (sheet->select_status == 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->select_status == 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; + + if (event->type == GDK_LEAVE_NOTIFY) + gtk_widget_hide (sheet->hover_window->window); + + 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->select_status; + + switch (sheet->select_status) + { + 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->select_status = 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->select_status = 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.x += sheet->cell_padding->left; + entry_alloc.y += sheet->cell_padding->right; + entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right; + entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom; + + + 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), + NULL, + 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), + NULL, + 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), + NULL, + 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..e3f77aa8 --- /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->select_status */ +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 select_status; + + /* 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..36300cc8 --- /dev/null +++ b/perl-module/PSPP.xs @@ -0,0 +1,732 @@ +/* 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 +#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); + int width = var_get_width (var); + value_set_missing (val, width); + memcpy (value_str_rw (val, width), 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 + { + int width = var_get_width (var); + return newSVpvn (value_str (val, width), width); + } +} + + +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 void +make_value_from_scalar (union value *uv, SV *val, const struct variable *var) +{ + value_init (uv, var_get_width (var)); + scalar_to_value (uv, val, var); +} + + +MODULE = PSPP + +MODULE = PSPP PACKAGE = PSPP + +void +onBoot (ver) + const char *ver +CODE: + assert (0 == strcmp (ver, bare_version)); + i18n_init (); + 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); + const struct dictionary *dict = var_get_vardict (var)->dict; + union value uv; + char *s; + make_value_from_scalar (&uv, val, var); + s = data_out (&uv, dict_get_encoding (dict), fmt); + value_destroy (&uv, var_get_width (var)); + 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; + int ret; + make_value_from_scalar (&uv, val, var); + ret = var_is_value_missing (var, &uv, MV_ANY); + value_destroy (&uv, var_get_width (var)); + 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); + +SV * +get_write_format (var) + struct variable *var +CODE: + HV *fmthash = (HV *) sv_2mortal ((SV *) newHV()); + const struct fmt_spec *fmt = var_get_write_format (var); + + hv_store (fmthash, "fmt", 3, newSVnv (fmt->type), 0); + hv_store (fmthash, "decimals", 8, newSVnv (fmt->d), 0); + hv_store (fmthash, "width", 5, newSVnv (fmt->w), 0); + + RETVAL = newRV ((SV *) fmthash); + OUTPUT: +RETVAL + +SV * +get_print_format (var) + struct variable *var +CODE: + HV *fmthash = (HV *) sv_2mortal ((SV *) newHV()); + const struct fmt_spec *fmt = var_get_print_format (var); + + hv_store (fmthash, "fmt", 3, newSVnv (fmt->type), 0); + hv_store (fmthash, "decimals", 8, newSVnv (fmt->d), 0); + hv_store (fmthash, "width", 5, newSVnv (fmt->w), 0); + + RETVAL = newRV ((SV *) fmthash); + OUTPUT: +RETVAL + + +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; + int width = var_get_width (var); + int ok; + + value_init (&the_value, width); + if ( var_is_numeric (var)) + { + if ( ! looks_like_number (key)) + { + sv_setpv (errstr, "Cannot add label with string key to a numeric variable"); + value_destroy (&the_value, width); + XSRETURN_IV (0); + } + the_value.f = SvNV (key); + } + else + { + value_copy_str_rpad (&the_value, width, SvPV_nolen(key), ' '); + } + ok = var_add_value_label (var, &the_value, label); + value_destroy (&the_value, width); + if (!ok) + { + 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()); + const 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); + vl; + vl = val_labs_next (labels, vl)) + { + SV *sv = value_to_scalar (&vl->value, var); + STRLEN len; + const char *s = SvPV (sv, len); + hv_store (labelhash, s, len, newSVpv (val_lab_get_label (vl), 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_proto (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, + sfi->dict, + 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); + value_set_missing (val, 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..c8e94846 --- /dev/null +++ b/perl-module/README @@ -0,0 +1,53 @@ +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 install + make test + +For "make test" to succeed, Perl must be able to find +libpspp-core-$VERSION.so. It can do so if "make install" has been run +(as shown above), or if LD_LIBRARY_PATH points to it (e.g. in +src/.libs). Running "make check" from the top-level build directory +will automatically set LD_LIBRARY_PATH. + + +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..e5599908 --- /dev/null +++ b/perl-module/lib/PSPP.pm @@ -0,0 +1,560 @@ +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