overlay-scrollbar-0.2.17.1+16.04.20151117/ 0000755 0000153 0000161 00000000000 12622657427 017752 5 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/data/ 0000755 0000153 0000161 00000000000 12622657427 020663 5 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/data/com.canonical.desktop.interface.gschema.xml 0000644 0000153 0000161 00000000410 12622657062 031114 0 ustar pbuser pbgroup 0000000 0000000
'overlay-auto'
overlay-scrollbar-0.2.17.1+16.04.20151117/data/81overlay-scrollbar 0000644 0000153 0000161 00000000306 12622657062 024413 0 ustar pbuser pbgroup 0000000 0000000 # This file is sourced by Xsession(5), not executed.
if [ -z "$GTK2_MODULES" ] ; then
GTK2_MODULES="overlay-scrollbar"
else
GTK2_MODULES="$GTK2_MODULES:overlay-scrollbar"
fi
export GTK2_MODULES
overlay-scrollbar-0.2.17.1+16.04.20151117/data/Makefile.am 0000644 0000153 0000161 00000000550 12622657062 022712 0 ustar pbuser pbgroup 0000000 0000000 EXTRA_DIST =
gsettings_ENUM_NAMESPACE = com.canonical.desktop.interface
gsettings_ENUM_FILES = $(top_srcdir)/os/os-scrollbar.h
gsettings_SCHEMAS = com.canonical.desktop.interface.gschema.xml
@GSETTINGS_RULES@
EXTRA_DIST += $(gsettings_SCHEMAS)
xsessiondir = $(sysconfdir)/X11/Xsession.d
xsession_DATA = 81overlay-scrollbar
EXTRA_DIST += $(xsession_DATA)
overlay-scrollbar-0.2.17.1+16.04.20151117/TODO 0000644 0000153 0000161 00000000000 12622657062 020423 0 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/NEWS 0000644 0000153 0000161 00000024110 12622657062 020442 0 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar 0.2.16 (March 21, 2012)
-----------------------------------------
* Always use bg[SELECTED] for the bar in Gtk+ 2.
* Bugs fixed:
907111 nautilus crashed with SIGSEGV in show_thumb_cb()
934123 Low visibility impaired users can't easily disable overlay scrollbars
951121 scroll *on* thumb not functional
overlay-scrollbar 0.2.15 (February 22, 2012)
--------------------------------------------
* Resize using thumbs.
* Fixes for Coverity bugs and support new Gtk+ versions.
overlay-scrollbar 0.2.14 (January 16, 2012)
-------------------------------------------
* New visuals.
* Bigger thumb, easier to grab.
* Bugs fixed:
890986 Lotus Notes is incompatible with ayatana scrollbars
overlay-scrollbar 0.2.13 (December 7, 2011)
-------------------------------------------
* Proximity area enlarged to the whole page.
* New thumb positioning according to mouse pointer and bar position.
* The thumb is more freely movable while touching an edge.
* Press Control while dragging to enter fine-scroll mode.
* Press Control while doing page up/down to do step up/down instead.
* Jumping is now triggered via Middle Click or via Shift + Button1.
* It is now possible to disable tests from the configure.
* Other tweaks to animations and fixes, small performance improvements.
* Bugs fixed:
752338 Can no longer jump to a specific position in a document/window
758576 Overlay Scrollbar should be revealed at any place on the edge of the...
763247 Overlay ScrollBar not easily findable in a large document
868739 Overlay scrollbars rendered in the wrong place when inside another...
overlay-scrollbar 0.2.12 (October 19, 2011)
-------------------------------------------
* Updated blacklist:
added acroread.
overlay-scrollbar 0.2.11 (September 13, 2011)
---------------------------------------------
* Updated blacklist:
added gimp-2.6, gimp-2.7, gimp-2.8.
removed apport-gtk, codeblocks, codelite, pgadmin3, update-manager.
overlay-scrollbar 0.2.10 (September 7, 2011)
--------------------------------------------
* Use proper object checks on public functions.
overlay-scrollbar 0.2.9 (September 1, 2011)
-------------------------------------------
* Bugs fixed:
838733 Page scroll kinetic should depend on the actual scroll
overlay-scrollbar 0.2.8 (September 1, 2011)
-------------------------------------------
* Bugs fixed:
838258 thumb does not always change look when dragged
overlay-scrollbar 0.2.7 (August 24, 2011)
-----------------------------------------
* Blacklist firefox, firefox-trunk, gimp.
overlay-scrollbar 0.2.6 (August 9, 2011)
----------------------------------------
* Bugs fixed:
822806 overlay scrollbar does not work in some windows
overlay-scrollbar 0.2.5 (July 14, 2011)
---------------------------------------
* Support insensitive state.
overlay-scrollbar 0.2.4 (July 8, 2011)
--------------------------------------
* Animate page up and page down.
* Fine scroll after paging.
* Enable glib assertions on releases.
* Fix thumb rendering on page up and down.
* Bugs fixed:
778891 The "Timeout before the fade-out" value of 250ms for cursor on thumb...
overlay-scrollbar 0.2.3 (June 30, 2011)
---------------------------------------
* New thumb design.
* Minor bug fixes.
overlay-scrollbar 0.2.2 (June 29, 2011)
---------------------------------------
* Scrollbars now use Gtk+ colors.
* Scrollbars are now less rounded.
* Support RTL locales and different scrolled window placements.
* Added tolerance to pageup and down buttons before dragging.
* Adjusted timings before hiding the thumb.
* Shrink the thumb size on small scrolled windows.
* Bugs fixed:
771450 Cannot scroll in small areas
761138 support left (and top) scrollbar placements
782022 Overlay scrollbars do not work in right to left locales
800387 emacs23-x crashing in liboverlay-scrollbar-0.2.so.0
overlay-scrollbar 0.2.1 (June 15, 2011)
---------------------------------------
* Gtk+ 3 support, based on the work by Andrea Azzarone.
* Performance boost with updated versions of both Gtk+ 2 and 3.
* Refinements in the code and coding style fixes.
* Various general fixes and improvements.
* Bugs fixed:
781432 In nvidia-settings overlay scrollbars are kept displayed when they...
767823 Overlay scrollbar hidden underneath bottom panel in Ubuntu Classic mode
773930 Overlay scrollbar not working correctly with font selection dialog
796828 overlay-scrollbar requires two mouse targetings
* Contributors:
Andrea Azzarone
overlay-scrollbar 0.2.0 (May 18, 2011)
--------------------------------------
* Major reshuffle in the code, hopefully easier to read.
* Added a patch which enables the scrollbars almost globally.
* Bugs fixed:
728838 Add visual connection after pageup/down via thumb
773263 scrolling doesn't work at the edge of the screen (fitts' law is broken)
774610 Overlay Scrollbar arrows are off-center for certain themes (e.g....
765719 hide the thumb when selecting text or on general interaction
735867 slowly moving the thumb when it's the the beginning, results in...
783261 Using scrollbar can break windows while list search is taking place
overlay-scrollbar 0.1.12 (April 27, 2011)
-----------------------------------------
* Bugs fixed:
771563 anjuta doesn't display scrollbar after resetting the layout
overlay-scrollbar 0.1.11 (April 27, 2011)
-----------------------------------------
* Un-blacklisted apps:
deja-dup, inkscape, lshw-gtk
* Bugs fixed:
771511 some apps show grey overlay instead orange even if the window is focused
overlay-scrollbar 0.1.10 (April 26, 2011)
-----------------------------------------
* Bugs fixed:
763707 Scrollbar's slider appears away from the mouse
769427 liboverlay doesn't work in detached tabs
769460 scrollbar doesn't work in liferea
770625 overlay-scrollbar_0.1.9-0ubuntu1 update causes vmware workstation to...
769217 Cardapio doesn't work propely with new scrollbars (Ubuntu 11.04)
overlay-scrollbar 0.1.9 (April 21, 2011)
----------------------------------------
* Install a xsession script to enable the scrollbars globally.
* Contributors:
Ken VanDine
overlay-scrollbar 0.1.8 (April 20, 2011)
----------------------------------------
* Few stability fixes, blacklist apps that expose functionality issues.
overlay-scrollbar 0.1.7 (April 13, 2011)
----------------------------------------
* Active windows have focused overlay/bar/pager, inactive windows follow mouse.
* Bugs fixed:
754717 ccsm crashed with SIGSEGV in os_pager_draw()
754927 Banshee.exe crashed with SIGABRT in g_main_context_dispatch()
754306 The last line of files in a directory is being 'duplicated' when...
754736 should not stay on screen when alt-tabbing to another application
758581 Scrollbar does not work with vinagre
758046 Cannot use the thumb to PgUp/PgDown multiple times, because it fades...
756243 Enabling Desktop-based Viewport Switching can cause fade on scroll...
overlay-scrollbar 0.1.6 (April 7, 2011)
---------------------------------------
* Bugs fixed:
737518 with compiz: if the window is unfocused and you click on one of its...
746189 Black Background on Overlay Scrollbar in Banshee
751444 hide the thumb after a timeout, with a fade-out animation
750572 Package gives wrong website
731653 Thumb blocks the right side of a maximized window
737661 in wxWidgets, pager is not colored properly
746862 animate the pager's colorization
746864 colorize the pager following mouse movement
750879 Add mouse-wheel functionality to the thumb
751440 use g_source_remove instead g_object_ref/unref for g_timeout_add in...
751442 lock position of the thumb when it's internal
751652 Thumb doesn't fade-out after click (and no motion after release) in...
752705 pager should immediately change color when changing gtk+ theme
752836 wrong colorization on restacking
753440 Panel's scrollbar is left behind
overlay-scrollbar 0.1.5 (March 30, 2011)
----------------------------------------
* Performance tweaks
* Bugs fixed:
728412 Compiz: make it sibling of the parent toplevel window
736992 Rendering error on multiple monitors
743229 overlay scrollbar thumbs not hidden if different window has focus
744431 Scrollbar stays on top after closing window
741885 use proper allocation, not from the parent
* Contributors:
Andrea Azzarone
overlay-scrollbar 0.1.4 (March 23, 2011)
----------------------------------------
* Bugs fixed:
740276 Add whitelist support
728418 Improve scrolling speed
731369 Scrollbars are not working with wxWidgets
741071 blacklist RTL languages
737627 stop events when the thumb is unmapped
overlay-scrollbar 0.1.3 (March 17, 2011)
----------------------------------------
* Performance tweaks
* Bugs fixed:
728664 Scrollbar (thumb) is not working in modal dialogs
732091 unity's restore from maximize is breaking the positioning of overlay...
735618 with jjardon's performance tweak branch, sometimes the pager is not...
735906 You can scroll even if the thumb is not shown
736076 rendering issues for the pager
733031 Issues when reallocating widgets
736280 The thumb has black borders after switching back to metacity
733212 scrollbar should be shifted depending on the scrolled window's...
overlay-scrollbar 0.1.2 (March 10, 2011)
----------------------------------------
* Bugs fixed:
728411 Correctly show the pager when opening the app
731366 BadAlloc in apps with many scrollbars
730822 crash with evolution and overlay 0.1.1
730849 Scrollbar overlay crashes Filezilla
730892 gnome-system-monitor crash when switching to a tab with scrollbar
731006 The scrollbar (pager+thumb) should be moved 1px left/up
731459 Proximity does not work with empathy
731650 Scroll position indicator is not visible in gedit Plugins list
732712 issue with gtk-demo without configure event
overlay-scrollbar 0.1.1 (March 7, 2011)
---------------------------------------
* Bugs fixed:
728416 Fix crashes in hide_thumb
728415 Maximized windows: Move thumb internally when there's not enough...
728407 Unwanted scrollbars are appearing for hidden scrolled windows...
overlay-scrollbar 0.1.0 (March 2, 2011)
---------------------------------------
Initial release
overlay-scrollbar-0.2.17.1+16.04.20151117/AUTHORS 0000644 0000153 0000161 00000000133 12622657062 021012 0 ustar pbuser pbgroup 0000000 0000000 Andrea Cimitan
Loïc Molinari
overlay-scrollbar-0.2.17.1+16.04.20151117/build/ 0000755 0000153 0000161 00000000000 12622657427 021051 5 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/build/Makefile.am 0000644 0000153 0000161 00000000041 12622657062 023073 0 ustar pbuser pbgroup 0000000 0000000 EXTRA_DIST = as-compiler-flag.m4
overlay-scrollbar-0.2.17.1+16.04.20151117/build/as-compiler-flag.m4 0000644 0000153 0000161 00000002736 12622657062 024440 0 ustar pbuser pbgroup 0000000 0000000 dnl as-compiler-flag.m4 0.1.0
dnl autostars m4 macro for detection of compiler flags
dnl David Schleef
dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $
dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
dnl Tries to compile with the given CFLAGS.
dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
dnl and ACTION-IF-NOT-ACCEPTED otherwise.
AC_DEFUN([AS_COMPILER_FLAG],
[
AC_MSG_CHECKING([to see if compiler understands $1])
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $1"
AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
CFLAGS="$save_CFLAGS"
if test "X$flag_ok" = Xyes ; then
m4_ifvaln([$2],[$2])
true
else
m4_ifvaln([$3],[$3])
true
fi
AC_MSG_RESULT([$flag_ok])
])
dnl AS_COMPILER_FLAGS(VAR, FLAGS)
dnl Tries to compile with the given CFLAGS.
AC_DEFUN([AS_COMPILER_FLAGS],
[
list=$2
flags_supported=""
flags_unsupported=""
AC_MSG_CHECKING([for supported compiler flags])
for each in $list
do
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $each"
AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
CFLAGS="$save_CFLAGS"
if test "X$flag_ok" = Xyes ; then
flags_supported="$flags_supported $each"
else
flags_unsupported="$flags_unsupported $each"
fi
done
AC_MSG_RESULT([$flags_supported])
if test "X$flags_unsupported" != X ; then
AC_MSG_WARN([unsupported compiler flags: $flags_unsupported])
fi
$1="$$1 $flags_supported"
])
overlay-scrollbar-0.2.17.1+16.04.20151117/Makefile.am 0000644 0000153 0000161 00000001017 12622657062 022000 0 ustar pbuser pbgroup 0000000 0000000 SUBDIRS = \
build \
data \
os
if ENABLE_TESTS
SUBDIRS += tests
endif
ACLOCAL_AMFLAGS = -I build
EXTRA_DIST = autogen.sh COPYING RELEASE
DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper
MAINTAINERCLEANFILES = \
build/compile \
build/config.guess \
build/config.sub \
build/depcomp \
build/install-sh \
build/ltmain.sh \
build/missing \
Makefile.in \
config.h.in \
configure \
aclocal.m4
release: dist
make $(PACKAGE)-$(VERSION).tar.gz.md5
make $(PACKAGE)-$(VERSION).tar.bz2.md5
%.md5: %
md5sum $< > $@
overlay-scrollbar-0.2.17.1+16.04.20151117/RELEASE 0000644 0000153 0000161 00000000000 12622657062 020736 0 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/COPYING 0000644 0000153 0000161 00000063642 12622657062 021013 0 ustar pbuser pbgroup 0000000 0000000 GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, 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 Street, 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!
overlay-scrollbar-0.2.17.1+16.04.20151117/README 0000644 0000153 0000161 00000000000 12622657062 020613 0 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/os/ 0000755 0000153 0000161 00000000000 12622657427 020373 5 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-log.c 0000644 0000153 0000161 00000003004 12622657062 021727 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
*/
#ifndef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "os-private.h"
#if !defined(NDEBUG)
OsLogLevel threshold = OS_INFO;
#else
OsLogLevel threshold = OS_WARN;
#endif
/* Public functions. */
void
os_log_message (OsLogLevel level, const gchar *function, const gchar *file,
gint32 line, const gchar *format, ...)
{
static const gchar *prefix[3] = {
"\033[37;01m", /* OS_INFO. */
"\033[33;01m", /* OS_WARN. */
"\033[31;01m" /* OS_ERROR. */
};
gchar buffer[512];
va_list args;
va_start (args, format);
vsnprintf (buffer, 512, format, args);
va_end (args);
fprintf (stderr, "%s%s\033[00m %s() %s %d\n", prefix[level], buffer, function,
file, line);
}
overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-bar.c 0000644 0000153 0000161 00000044066 12622657062 021727 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
*/
#ifndef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "os-private.h"
#include
#include
/* Duration of the fade-in. */
#define DURATION_FADE_IN 200
/* Duration of the fade-out. */
#define DURATION_FADE_OUT 400
/* Max duration of the retracting tail. */
#define MAX_DURATION_TAIL 300
/* Min duration of the retracting tail. */
#define MIN_DURATION_TAIL 100
struct _OsBarPrivate {
GdkRectangle bar_mask;
GdkRectangle tail_mask; /* In theory not needed, but easier to read. */
GdkRectangle allocation;
GdkWindow *bar_window;
GdkWindow *tail_window;
GtkWidget *parent;
OsAnimation *state_animation;
OsAnimation *tail_animation;
gboolean active;
gboolean detached;
gboolean visible;
gfloat weight;
};
static void os_bar_dispose (GObject *object);
static void os_bar_finalize (GObject *object);
/* Draw on the bar_window. */
static void
draw_bar (OsBar *bar)
{
GdkColor color;
GtkStyle *style;
OsBarPrivate *priv;
gfloat weight;
priv = bar->priv;
weight = priv->weight;
style = gtk_widget_get_style (priv->parent);
color = style->bg[GTK_STATE_SELECTED];
gdk_colormap_alloc_color (gdk_drawable_get_colormap (priv->bar_window), &color, FALSE, TRUE);
gdk_window_set_background (priv->bar_window, &color);
gdk_window_invalidate_rect (gtk_widget_get_window (priv->parent), &priv->allocation, TRUE);
}
/* Draw on the tail_window. */
static void
draw_tail (OsBar *bar)
{
GdkColor color;
GtkStyle *style;
OsBarPrivate *priv;
priv = bar->priv;
style = gtk_widget_get_style (priv->parent);
color = style->bg[GTK_STATE_ACTIVE];
gdk_colormap_alloc_color (gdk_drawable_get_colormap (priv->tail_window), &color, FALSE, TRUE);
gdk_window_set_background (priv->tail_window, &color);
gdk_window_invalidate_rect (gtk_widget_get_window (priv->parent), &priv->allocation, TRUE);
}
/* Callback called by the change-state animation. */
static void
change_state_cb (gfloat weight,
gpointer user_data)
{
OsBar *bar;
OsBarPrivate *priv;
bar = OS_BAR (user_data);
priv = bar->priv;
priv->weight = weight;
if (priv->parent == NULL)
return;
draw_bar (bar);
}
/* Stop function called by the change-state animation. */
static void
change_state_stop_cb (gpointer user_data)
{
OsBar *bar;
OsBarPrivate *priv;
bar = OS_BAR (user_data);
priv = bar->priv;
priv->weight = 1.0f;
draw_bar (bar);
}
/* Callback called when the Gtk+ theme changes. */
static void
notify_gtk_theme_name_cb (GObject* gobject,
GParamSpec* pspec,
gpointer user_data)
{
OsBar *bar;
OsBarPrivate *priv;
bar = OS_BAR (user_data);
priv = bar->priv;
if (priv->parent == NULL ||
priv->bar_window == NULL ||
priv->tail_window == NULL)
return;
draw_tail (bar);
draw_bar (bar);
}
/* Check if two GdkRectangle are different. */
static gboolean
rectangle_changed (GdkRectangle rectangle1,
GdkRectangle rectangle2)
{
if (rectangle1.x != rectangle2.x) return TRUE;
if (rectangle1.y != rectangle2.y) return TRUE;
if (rectangle1.width != rectangle2.width) return TRUE;
if (rectangle1.height != rectangle2.height) return TRUE;
return FALSE;
}
/* wrapper around gdk_window_shape_combine_region() */
static void
os_bar_window_shape_combine_region (GdkWindow * window,
const GdkRectangle * shape_rect,
gint offset_x,
gint offset_y)
{
GdkRegion * shape_region = gdk_region_rectangle (shape_rect);
gdk_window_shape_combine_region (window, shape_region, offset_x, offset_y);
gdk_region_destroy (shape_region);
}
/* Callback called by the retract-tail animation. */
static void
retract_tail_cb (gfloat weight,
gpointer user_data)
{
GdkRectangle tail_mask;
OsBar *bar;
OsBarPrivate *priv;
bar = OS_BAR (user_data);
priv = bar->priv;
if (priv->parent == NULL)
return;
tail_mask = priv->tail_mask;
if (priv->allocation.height >= priv->allocation.width)
{
tail_mask.height = tail_mask.height * (1.0 - weight);
if (priv->tail_mask.y + priv->tail_mask.height < priv->bar_mask.y + priv->bar_mask.height)
tail_mask.y = priv->tail_mask.y + priv->tail_mask.height - tail_mask.height;
}
else
{
tail_mask.width = tail_mask.width * (1.0 - weight);
if (priv->tail_mask.x + priv->tail_mask.width < priv->bar_mask.x + priv->bar_mask.width)
tail_mask.x = priv->tail_mask.x + priv->tail_mask.width - tail_mask.width;
}
if (weight < 1.0)
{
os_bar_window_shape_combine_region (priv->tail_window,
&tail_mask,
0, 0);
}
else
{
/* Store the new tail_mask and hide the tail_window. */
priv->tail_mask = tail_mask;
gdk_window_hide (priv->tail_window);
}
}
/* Stop function called by the retract-tail animation. */
static void
retract_tail_stop_cb (gpointer user_data)
{
OsBar *bar;
OsBarPrivate *priv;
bar = OS_BAR (user_data);
priv = bar->priv;
if (priv->parent == NULL)
return;
os_bar_window_shape_combine_region (priv->tail_window,
&priv->tail_mask,
0, 0);
}
G_DEFINE_TYPE (OsBar, os_bar, G_TYPE_OBJECT);
static void
os_bar_class_init (OsBarClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->dispose = os_bar_dispose;
gobject_class->finalize = os_bar_finalize;
g_type_class_add_private (gobject_class, sizeof (OsBarPrivate));
}
static void
os_bar_init (OsBar *bar)
{
GdkRectangle allocation, mask;
OsBarPrivate *priv;
bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (bar,
OS_TYPE_BAR,
OsBarPrivate);
priv = bar->priv;
allocation.x = 0;
allocation.y = 0;
allocation.width = 1;
allocation.height = 1;
priv->allocation = allocation;
mask.x = 0;
mask.y = 0;
mask.width = 1;
mask.height = 1;
priv->bar_mask = mask;
priv->tail_mask = mask;
priv->weight = 1.0f;
priv->state_animation = os_animation_new (RATE_ANIMATION, DURATION_FADE_OUT,
change_state_cb, NULL, bar);
priv->tail_animation = os_animation_new (RATE_ANIMATION, MAX_DURATION_TAIL,
retract_tail_cb, NULL, bar);
g_signal_connect (gtk_settings_get_default (), "notify::gtk-theme-name",
G_CALLBACK (notify_gtk_theme_name_cb), bar);
}
static void
os_bar_dispose (GObject *object)
{
OsBar *bar;
OsBarPrivate *priv;
bar = OS_BAR (object);
priv = bar->priv;
if (priv->tail_animation != NULL)
{
g_object_unref (priv->tail_animation);
priv->tail_animation = NULL;
}
if (priv->state_animation != NULL)
{
g_object_unref (priv->state_animation);
priv->state_animation = NULL;
}
if (priv->tail_window != NULL)
{
/* From the Gdk documentation:
* "Note that a window will not be destroyed
* automatically when its reference count
* reaches zero. You must call
* gdk_window_destroy ()
* yourself before that happens". */
gdk_window_destroy (priv->tail_window);
g_object_unref (priv->tail_window);
priv->tail_window = NULL;
}
if (priv->bar_window != NULL)
{
/* From the Gdk documentation:
* "Note that a window will not be destroyed
* automatically when its reference count
* reaches zero. You must call
* gdk_window_destroy ()
* yourself before that happens". */
gdk_window_destroy (priv->bar_window);
g_object_unref (priv->bar_window);
priv->bar_window = NULL;
}
g_signal_handlers_disconnect_by_func (gtk_settings_get_default (), notify_gtk_theme_name_cb, object);
os_bar_set_parent (bar, NULL);
G_OBJECT_CLASS (os_bar_parent_class)->dispose (object);
}
static void
os_bar_finalize (GObject *object)
{
G_OBJECT_CLASS (os_bar_parent_class)->finalize (object);
}
/* Public functions. */
/**
* os_bar_new:
*
* Creates a new #OsBar instance.
*
* Returns: the new #OsBar instance.
**/
OsBar*
os_bar_new (void)
{
return g_object_new (OS_TYPE_BAR, NULL);
}
/* Move a mask on the tail_window, fake movement. */
static void
mask_tail (OsBar *bar)
{
OsBarPrivate *priv;
priv = bar->priv;
os_bar_window_shape_combine_region (priv->tail_window,
&priv->tail_mask,
0, 0);
}
/**
* os_bar_connect:
* @bar: a #OsBar
* @mask: a #GdkRectangle with the position and dimension of the tail
*
* Moves and resizes tail.
**/
void
os_bar_connect (OsBar *bar,
GdkRectangle mask)
{
OsBarPrivate *priv;
g_return_if_fail (OS_IS_BAR (bar));
priv = bar->priv;
if (!os_animation_is_running (priv->tail_animation) &&
!rectangle_changed (priv->tail_mask, mask))
return;
/* If there's an animation currently running, stop it. */
os_animation_stop (priv->tail_animation, NULL);
priv->tail_mask = mask;
if (priv->parent == NULL)
return;
mask_tail (bar);
}
/**
* os_bar_hide:
* @bar: a #OsBar
*
* Hides the #OsBar.
**/
void
os_bar_hide (OsBar *bar)
{
OsBarPrivate *priv;
g_return_if_fail (OS_IS_BAR (bar));
priv = bar->priv;
priv->visible = FALSE;
if (priv->parent == NULL)
return;
/* Immediately hide, then stop animations. */
gdk_window_hide (priv->tail_window);
gdk_window_hide (priv->bar_window);
os_animation_stop (priv->tail_animation, retract_tail_stop_cb);
os_animation_stop (priv->state_animation, change_state_stop_cb);
}
/* Move a mask on the bar_window, fake movement. */
static void
mask_bar (OsBar *bar)
{
OsBarPrivate *priv;
priv = bar->priv;
os_bar_window_shape_combine_region (priv->bar_window,
&priv->bar_mask,
0, 0);
}
/**
* os_bar_move_resize:
* @bar: a #OsBar
* @mask: a #GdkRectangle with the position and dimension of the #OsBar
*
* Moves and resizes @bar.
**/
void
os_bar_move_resize (OsBar *bar,
GdkRectangle mask)
{
OsBarPrivate *priv;
g_return_if_fail (OS_IS_BAR (bar));
priv = bar->priv;
if (!rectangle_changed (priv->bar_mask, mask))
return;
priv->bar_mask = mask;
if (priv->parent == NULL)
return;
mask_bar (bar);
}
/**
* os_bar_set_detached:
* @bar: a #OsBar
* @detached: whether the bar is detached or not
* @animate: whether animate it or not
*
* Changes the detached state of @bar.
**/
void
os_bar_set_detached (OsBar *bar,
gboolean detached,
gboolean animate)
{
OsBarPrivate *priv;
g_return_if_fail (OS_IS_BAR (bar));
priv = bar->priv;
if (priv->detached != detached)
{
priv->detached = detached;
if (priv->parent == NULL)
return;
if (priv->detached)
{
/* If there's a tail animation currently running, stop it. */
os_animation_stop (priv->tail_animation, retract_tail_stop_cb);
/* No tail connection animation yet. */
gdk_window_show (priv->tail_window);
gdk_window_raise (priv->bar_window);
}
else if (animate)
{
gint32 duration;
/* The detached state should already stop this. */
OS_DCHECK (!os_animation_is_running (priv->tail_animation));
/* Calculate and set the duration. */
if (priv->allocation.height >= priv->allocation.width)
duration = MIN_DURATION_TAIL + ((gdouble) priv->tail_mask.height / priv->allocation.height) *
(MAX_DURATION_TAIL - MIN_DURATION_TAIL);
else
duration = MIN_DURATION_TAIL + ((gdouble) priv->tail_mask.width / priv->allocation.width) *
(MAX_DURATION_TAIL - MIN_DURATION_TAIL);
os_animation_set_duration (priv->tail_animation, duration);
os_animation_start (priv->tail_animation);
}
else
gdk_window_hide (priv->tail_window);
}
}
/* Create tail_window and bar_window. */
static void
create_windows (OsBar *bar)
{
GdkWindowAttr attributes;
OsBarPrivate *priv;
priv = bar->priv;
/* Instead reparenting,
* which doesn't seem to work well,
* destroy the two windows. */
if (priv->tail_window != NULL)
{
/* From the Gdk documentation:
* "Note that a window will not be destroyed
* automatically when its reference count
* reaches zero. You must call
* gdk_window_destroy ()
* yourself before that happens". */
gdk_window_destroy (priv->tail_window);
g_object_unref (priv->tail_window);
priv->tail_window = NULL;
}
if (priv->bar_window != NULL)
{
/* From the Gdk documentation:
* "Note that a window will not be destroyed
* automatically when its reference count
* reaches zero. You must call
* gdk_window_destroy ()
* yourself before that happens". */
gdk_window_destroy (priv->bar_window);
g_object_unref (priv->bar_window);
priv->bar_window = NULL;
}
attributes.event_mask = 0;
attributes.width = priv->allocation.width;
attributes.height = priv->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.visual = gtk_widget_get_visual (priv->parent);
attributes.colormap = gtk_widget_get_colormap (priv->parent);
/* tail_window. */
priv->tail_window = gdk_window_new (gtk_widget_get_window (priv->parent),
&attributes,
GDK_WA_VISUAL | GDK_WA_COLORMAP);
g_object_ref_sink (priv->tail_window);
gdk_window_set_transient_for (priv->tail_window,
gtk_widget_get_window (priv->parent));
/* FIXME(Cimi) maybe this is not required with 0 as event mask. */
gdk_window_input_shape_combine_region (priv->tail_window,
gdk_region_new (),
0, 0);
/* bar_window. */
priv->bar_window = gdk_window_new (gtk_widget_get_window (priv->parent),
&attributes,
GDK_WA_VISUAL | GDK_WA_COLORMAP);
g_object_ref_sink (priv->bar_window);
gdk_window_set_transient_for (priv->bar_window,
gtk_widget_get_window (priv->parent));
/* FIXME(Cimi) maybe this is not required with 0 as event mask. */
gdk_window_input_shape_combine_region (priv->bar_window,
gdk_region_new (),
0, 0);
mask_tail (bar);
}
/**
* os_bar_set_parent:
* @bar: a #OsBar
* @parent: a #GtkWidget
*
* Sets the parent widget
**/
void
os_bar_set_parent (OsBar *bar,
GtkWidget *parent)
{
OsBarPrivate *priv;
g_return_if_fail (OS_IS_BAR (bar));
priv = bar->priv;
/* Stop currently running animations. */
if (priv->tail_animation != NULL)
os_animation_stop (priv->tail_animation, retract_tail_stop_cb);
if (priv->state_animation != NULL)
os_animation_stop (priv->state_animation, NULL);
if (priv->parent != NULL)
g_object_unref (priv->parent);
priv->parent = parent;
if (priv->parent != NULL)
{
g_object_ref_sink (priv->parent);
priv->weight = 1.0f;
create_windows (bar);
draw_tail (bar);
draw_bar (bar);
mask_bar (bar);
gdk_window_move_resize (priv->tail_window,
priv->allocation.x,
priv->allocation.y,
priv->allocation.width,
priv->allocation.height);
gdk_window_move_resize (priv->bar_window,
priv->allocation.x,
priv->allocation.y,
priv->allocation.width,
priv->allocation.height);
if (priv->visible)
gdk_window_show (priv->bar_window);
}
}
/**
* os_bar_show:
* @bar: a #OsBar
*
* Shows @bar.
**/
void
os_bar_show (OsBar *bar)
{
OsBarPrivate *priv;
g_return_if_fail (OS_IS_BAR (bar));
priv = bar->priv;
priv->visible = TRUE;
if (priv->parent == NULL)
return;
gdk_window_show (priv->bar_window);
}
/**
* os_bar_size_allocate:
* @bar: a #OsBar
* @rectangle: a #GdkRectangle
*
* Sets the position and dimension of the whole area.
**/
void
os_bar_size_allocate (OsBar *bar,
GdkRectangle rectangle)
{
OsBarPrivate *priv;
g_return_if_fail (OS_IS_BAR (bar));
priv = bar->priv;
if (!rectangle_changed (priv->allocation, rectangle))
return;
priv->allocation = rectangle;
if (priv->parent == NULL)
return;
gdk_window_move_resize (priv->tail_window,
rectangle.x,
rectangle.y,
rectangle.width,
rectangle.height);
gdk_window_move_resize (priv->bar_window,
rectangle.x,
rectangle.y,
rectangle.width,
rectangle.height);
}
overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-private.h 0000644 0000153 0000161 00000021012 12622657062 022624 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
*/
#ifndef __OS_PRIVATE_H__
#define __OS_PRIVATE_H__
#include
/* Tell GCC not to export internal functions. */
#ifdef __GNUC__
#pragma GCC visibility push(hidden)
#endif /* __GNUC__ */
/* Rate of the animations (frames per second). */
#define RATE_ANIMATION 30
/* Size of the thumb in pixels. */
#define MIN_THUMB_HEIGHT 35
#define THUMB_WIDTH 21
#define THUMB_HEIGHT 68
/* Number of tolerance pixels on drag, while intercepting a motion-notify-event. */
#define TOLERANCE_DRAG 9
/* Number of tolerance pixels on pageup/down, while intercepting a motion-notify-event. */
#define TOLERANCE_MOTION 2
G_BEGIN_DECLS
typedef struct
{
gint x;
gint y;
} OsCoordinate;
typedef enum
{
OS_EVENT_NONE = 0,
OS_EVENT_BUTTON_PRESS = 1,
OS_EVENT_ENTER_NOTIFY = 2,
OS_EVENT_MOTION_NOTIFY = 4
} OsEventFlags;
/* os-log.c */
/* Severity levels. */
typedef enum {
OS_INFO, /* Informative message (white colored). */
OS_WARN, /* Warning message (yellow colored). */
OS_ERROR /* Error message (red colored). */
} OsLogLevel;
/* Logging level threshold, message logged with a lower level are discarded.
* Initialized to OS_INFO for debug builds and OS_WARN for release builds. */
extern OsLogLevel threshold;
/* Log a message to stderr at the given level following the printf() syntax. */
void
G_GNUC_NO_INSTRUMENT
G_GNUC_PRINTF (5, 6)
os_log_message (OsLogLevel level, const gchar *function, const gchar *file,
gint32 line, const gchar *format, ...);
/* Macro logging a message to stderr using the given level. */
#define OS_LOG(level,...) \
G_STMT_START { \
if (level >= threshold) \
os_log_message ((level), __func__, __FILE__, __LINE__, __VA_ARGS__); \
} G_STMT_END
/* Macro conditionally logging a message to stderr using the given level. */
#define OS_LOG_IF(level,cond,...) \
G_STMT_START { \
if (level >= threshold) && (cond == TRUE)) { \
os_log_message ((level), __func__, __FILE__, __LINE__, __VA_ARGS__); \
} \
} G_STMT_END
/* Macro logging an error message to stderr and breaking the program execution
* if the assertion fails. */
#define OS_CHECK(cond) \
G_STMT_START { \
if (G_UNLIKELY((cond) == FALSE)) { \
os_log_message (OS_ERROR, __func__, __FILE__, __LINE__, \
"assertion `"#cond"' failed"); \
G_BREAKPOINT (); \
} \
} G_STMT_END
/* Debug mode logging macros, compiled away to nothing for release builds. */
#if !defined(NDEBUG)
#define OS_DLOG(level,...) OS_LOG((level), __VA_ARGS__)
#define OS_DLOG_IF(level,cond,...) OS_LOG_IF((level), (cond), __VA_ARGS__)
#define OS_DCHECK(cond) OS_CHECK((cond))
#else
#define OS_DLOG(level,...)
#define OS_DLOG_IF(level,cond,...)
#define OS_DCHECK(cond)
#endif
/* os-animation.c */
#define OS_TYPE_ANIMATION (os_animation_get_type ())
#define OS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OS_TYPE_ANIMATION, OsAnimation))
#define OS_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), OS_TYPE_ANIMATION, OsAnimationClass))
#define OS_IS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OS_TYPE_ANIMATION))
#define OS_IS_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), OS_TYPE_ANIMATION))
#define OS_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), OS_TYPE_ANIMATION, OsAnimationClass))
typedef void (*OsAnimationUpdateFunc) (gfloat weight, gpointer user_data);
typedef void (*OsAnimationEndFunc) (gpointer user_data);
typedef void (*OsAnimationStopFunc) (gpointer user_data);
typedef struct _OsAnimation OsAnimation;
typedef struct _OsAnimationClass OsAnimationClass;
typedef struct _OsAnimationPrivate OsAnimationPrivate;
struct _OsAnimation {
GObject parent_instance;
OsAnimationPrivate *priv;
};
struct _OsAnimationClass {
GObjectClass parent_class;
};
GType os_animation_get_type (void);
OsAnimation* os_animation_new (gint32 rate,
gint32 duration,
OsAnimationUpdateFunc update_func,
OsAnimationEndFunc end_func,
gpointer user_data);
gboolean os_animation_is_running (OsAnimation *animation);
void os_animation_set_duration (OsAnimation *animation,
gint32 duration);
void os_animation_start (OsAnimation *animation);
void os_animation_stop (OsAnimation *animation,
OsAnimationStopFunc stop_func);
/* os-bar.c */
#define OS_TYPE_BAR (os_bar_get_type ())
#define OS_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OS_TYPE_BAR, OsBar))
#define OS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), OS_TYPE_BAR, OsBarClass))
#define OS_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OS_TYPE_BAR))
#define OS_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), OS_TYPE_BAR))
#define OS_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), OS_TYPE_BAR, OsBarClass))
typedef struct _OsBar OsBar;
typedef struct _OsBarClass OsBarClass;
typedef struct _OsBarPrivate OsBarPrivate;
struct _OsBar {
GObject parent_instance;
OsBarPrivate *priv;
};
struct _OsBarClass {
GObjectClass parent_class;
};
GType os_bar_get_type (void) G_GNUC_CONST;
OsBar* os_bar_new (void);
void os_bar_hide (OsBar *bar);
void os_bar_connect (OsBar *bar,
GdkRectangle mask);
void os_bar_move_resize (OsBar *bar,
GdkRectangle mask);
void os_bar_set_detached (OsBar *bar,
gboolean detached,
gboolean animate);
void os_bar_set_parent (OsBar *bar,
GtkWidget *parent);
void os_bar_show (OsBar *bar);
void os_bar_size_allocate (OsBar *bar,
GdkRectangle rectangle);
/* os-thumb.c */
#define OS_TYPE_THUMB (os_thumb_get_type ())
#define OS_THUMB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OS_TYPE_THUMB, OsThumb))
#define OS_THUMB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), OS_TYPE_THUMB, OsThumbClass))
#define OS_IS_THUMB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OS_TYPE_THUMB))
#define OS_IS_THUMB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), OS_TYPE_THUMB))
#define OS_THUMB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), OS_TYPE_THUMB, OsThumbClass))
typedef struct _OsThumb OsThumb;
typedef struct _OsThumbClass OsThumbClass;
typedef struct _OsThumbPrivate OsThumbPrivate;
struct _OsThumb {
GtkWindow parent_object;
OsThumbPrivate *priv;
};
struct _OsThumbClass {
GtkWindowClass parent_class;
};
GType os_thumb_get_type (void) G_GNUC_CONST;
GtkWidget* os_thumb_new (GtkOrientation orientation);
void os_thumb_resize (OsThumb *thumb,
gint width,
gint height);
void os_thumb_set_detached (OsThumb *thumb,
gboolean detached);
G_END_DECLS
#ifdef __GNUC__
#pragma GCC visibility pop
#endif /* __GNUC__ */
#endif /* __OS_PRIVATE_H__ */
overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-thumb.c 0000644 0000153 0000161 00000074021 12622657062 022274 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
*/
#ifndef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "os-private.h"
#include
#include
/* Duration of the fade-out. */
#define DURATION_FADE_OUT 2000
/* Timeout before the fade-out. */
#define TIMEOUT_FADE_OUT 250
/* Thumb radius in pixels (higher values are automatically clamped). */
#define THUMB_RADIUS 3
/* Number of tolerance pixels, before hiding the thumb. */
#define TOLERANCE_FADE 3
typedef struct {
gdouble red;
gdouble green;
gdouble blue;
gdouble alpha;
} GdkRGBA;
struct _OsThumbPrivate {
GtkOrientation orientation;
GtkWidget *grabbed_widget;
OsAnimation *animation;
OsCoordinate pointer;
OsCoordinate pointer_root;
OsEventFlags event;
gboolean rgba;
gboolean detached;
gboolean tolerance;
guint32 source_id;
};
enum {
PROP_0,
PROP_ORIENTATION,
LAST_ARG
};
static gboolean os_thumb_button_press_event (GtkWidget *widget, GdkEventButton *event);
static gboolean os_thumb_button_release_event (GtkWidget *widget, GdkEventButton *event);
static void os_thumb_composited_changed (GtkWidget *widget);
static gboolean os_thumb_expose (GtkWidget *widget, GdkEventExpose *event);
static gboolean os_thumb_leave_notify_event (GtkWidget *widget, GdkEventCrossing *event);
static gboolean os_thumb_motion_notify_event (GtkWidget *widget, GdkEventMotion *event);
static void os_thumb_map (GtkWidget *widget);
static void os_thumb_screen_changed (GtkWidget *widget, GdkScreen *old_screen);
static gboolean os_thumb_scroll_event (GtkWidget *widget, GdkEventScroll *event);
static void os_thumb_unmap (GtkWidget *widget);
static GObject* os_thumb_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties);
static void os_thumb_dispose (GObject *object);
static void os_thumb_finalize (GObject *object);
static void os_thumb_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void os_thumb_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
/* Callback called by the fade-out animation. */
static void
fade_out_cb (gfloat weight,
gpointer user_data)
{
OsThumb *thumb;
thumb = OS_THUMB (user_data);
if (weight < 1.0f)
gtk_window_set_opacity (GTK_WINDOW (thumb), fabs (weight - 1.0f));
else
gtk_widget_hide (GTK_WIDGET (thumb));
}
/* Stop function called by the fade-out animation. */
static void
fade_out_stop_cb (gpointer user_data)
{
OsThumb *thumb;
thumb = OS_THUMB (user_data);
gtk_window_set_opacity (GTK_WINDOW (thumb), 1.0f);
}
/* Timeout before starting the fade-out animation. */
static gboolean
timeout_fade_out_cb (gpointer user_data)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (user_data);
priv = thumb->priv;
os_animation_start (priv->animation);
priv->source_id = 0;
return FALSE;
}
G_DEFINE_TYPE (OsThumb, os_thumb, GTK_TYPE_WINDOW);
static void
os_thumb_class_init (OsThumbClass *class)
{
GObjectClass *gobject_class;
GtkWidgetClass *widget_class;
gobject_class = G_OBJECT_CLASS (class);
widget_class = GTK_WIDGET_CLASS (class);
widget_class->button_press_event = os_thumb_button_press_event;
widget_class->button_release_event = os_thumb_button_release_event;
widget_class->composited_changed = os_thumb_composited_changed;
widget_class->expose_event = os_thumb_expose;
widget_class->leave_notify_event = os_thumb_leave_notify_event;
widget_class->map = os_thumb_map;
widget_class->motion_notify_event = os_thumb_motion_notify_event;
widget_class->screen_changed = os_thumb_screen_changed;
widget_class->scroll_event = os_thumb_scroll_event;
widget_class->unmap = os_thumb_unmap;
gobject_class->constructor = os_thumb_constructor;
gobject_class->dispose = os_thumb_dispose;
gobject_class->finalize = os_thumb_finalize;
gobject_class->get_property = os_thumb_get_property;
gobject_class->set_property = os_thumb_set_property;
g_object_class_install_property
(gobject_class, PROP_ORIENTATION,
g_param_spec_enum ("orientation", "Orientation",
"GtkOrientation of the OsThumb",
GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
g_type_class_add_private (gobject_class, sizeof (OsThumbPrivate));
}
static void
os_thumb_init (OsThumb *thumb)
{
OsThumbPrivate *priv;
thumb->priv = G_TYPE_INSTANCE_GET_PRIVATE (thumb,
OS_TYPE_THUMB,
OsThumbPrivate);
priv = thumb->priv;
priv->animation = os_animation_new (RATE_ANIMATION, DURATION_FADE_OUT,
fade_out_cb, NULL, thumb);
gtk_window_set_skip_pager_hint (GTK_WINDOW (thumb), TRUE);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (thumb), TRUE);
gtk_window_set_decorated (GTK_WINDOW (thumb), FALSE);
gtk_window_set_focus_on_map (GTK_WINDOW (thumb), FALSE);
gtk_window_set_accept_focus (GTK_WINDOW (thumb), FALSE);
gtk_widget_set_app_paintable (GTK_WIDGET (thumb), TRUE);
gtk_widget_add_events (GTK_WIDGET (thumb), GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK |
GDK_SCROLL_MASK);
os_thumb_screen_changed (GTK_WIDGET (thumb), NULL);
os_thumb_composited_changed (GTK_WIDGET (thumb));
}
static gboolean
os_thumb_button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
if (priv->source_id != 0)
{
g_source_remove (priv->source_id);
priv->source_id = 0;
}
/* Stop the animation on user interaction,
* the button_press_event. */
os_animation_stop (priv->animation, fade_out_stop_cb);
if (event->type == GDK_BUTTON_PRESS)
{
if (event->button == 1 ||
event->button == 2)
{
gtk_grab_add (widget);
priv->pointer.x = event->x;
priv->pointer.y = event->y;
priv->pointer_root.x = event->x_root;
priv->pointer_root.y = event->y_root;
priv->event |= OS_EVENT_BUTTON_PRESS;
priv->event &= ~(OS_EVENT_MOTION_NOTIFY);
priv->tolerance = TRUE;
gtk_widget_queue_draw (widget);
}
}
return FALSE;
}
static gboolean
os_thumb_button_release_event (GtkWidget *widget,
GdkEventButton *event)
{
GtkAllocation allocation;
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
gtk_widget_get_allocation (widget, &allocation);
if (event->type == GDK_BUTTON_RELEASE)
{
if (event->button == 1 ||
event->button == 2)
{
gtk_grab_remove (widget);
priv->event &= ~(OS_EVENT_BUTTON_PRESS | OS_EVENT_MOTION_NOTIFY);
gtk_widget_queue_draw (widget);
}
}
return FALSE;
}
static void
os_thumb_composited_changed (GtkWidget *widget)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
priv->rgba = FALSE;
if (gdk_screen_is_composited (gtk_widget_get_screen (widget)))
{
GdkVisual *visual;
guint32 red_mask;
guint32 green_mask;
guint32 blue_mask;
visual = gtk_widget_get_visual (widget);
gdk_visual_get_red_pixel_details (visual, &red_mask, NULL, NULL);
gdk_visual_get_green_pixel_details (visual, &green_mask, NULL, NULL);
gdk_visual_get_blue_pixel_details (visual, &blue_mask, NULL, NULL);
if (gdk_visual_get_depth (visual) == 32 && (red_mask == 0xff0000 &&
green_mask == 0x00ff00 &&
blue_mask == 0x0000ff))
priv->rgba = TRUE;
}
gtk_widget_queue_draw (widget);
}
/* Simplified wrapper of cairo_pattern_add_color_stop_rgba. */
static void
pattern_add_gdk_rgba_stop (cairo_pattern_t *pat,
gdouble stop,
const GdkRGBA *color,
gdouble alpha)
{
cairo_pattern_add_color_stop_rgba (pat, stop, color->red, color->green, color->blue, alpha);
}
/* Simplified wrapper of cairo_set_source_rgba. */
static void
set_source_gdk_rgba (cairo_t *cr,
const GdkRGBA *color,
gdouble alpha)
{
cairo_set_source_rgba (cr, color->red, color->green, color->blue, alpha);
}
/* Draw an arrow using cairo. */
static void
draw_arrow (cairo_t *cr,
const GdkRGBA *color,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
cairo_save (cr);
cairo_translate (cr, x, y);
cairo_move_to (cr, -width / 2, -height / 2);
cairo_line_to (cr, 0, height / 2);
cairo_line_to (cr, width / 2, -height / 2);
cairo_close_path (cr);
set_source_gdk_rgba (cr, color, 0.75);
cairo_fill_preserve (cr);
set_source_gdk_rgba (cr, color, 1.0);
cairo_stroke (cr);
cairo_restore (cr);
}
/* Draw a grip using cairo. */
static void
draw_grip (cairo_t *cr,
gdouble x,
gdouble y,
gint nx,
gint ny)
{
gint lx, ly;
for (ly = 0; ly < ny; ly++)
{
for (lx = 0; lx < nx; lx++)
{
gint sx = lx * 3;
gint sy = ly * 3;
cairo_rectangle (cr, x + sx, y + sy, 1, 1);
}
}
}
/* Draw a rounded rectangle using cairo. */
static void
draw_round_rect (cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
gdouble radius)
{
radius = MIN (radius, MIN (width / 2.0, height / 2.0));
if (radius < 1)
{
cairo_rectangle (cr, x, y, width, height);
return;
}
cairo_move_to (cr, x + radius, y);
cairo_arc (cr, x + width - radius, y + radius, radius, G_PI * 1.5, G_PI * 2);
cairo_arc (cr, x + width - radius, y + height - radius, radius, 0, G_PI * 0.5);
cairo_arc (cr, x + radius, y + height - radius, radius, G_PI * 0.5, G_PI);
cairo_arc (cr, x + radius, y + radius, radius, G_PI, G_PI * 1.5);
}
/* Convert RGB to HLS. */
static void
rgb_to_hls (gdouble *r,
gdouble *g,
gdouble *b)
{
gdouble min;
gdouble max;
gdouble red;
gdouble green;
gdouble blue;
gdouble h, l, s;
gdouble delta;
h = 0;
red = *r;
green = *g;
blue = *b;
if (red > green)
{
if (red > blue)
max = red;
else
max = blue;
if (green < blue)
min = green;
else
min = blue;
}
else
{
if (green > blue)
max = green;
else
max = blue;
if (red < blue)
min = red;
else
min = blue;
}
l = (max + min) / 2;
if (fabs (max - min) < 0.0001)
{
h = 0;
s = 0;
}
else
{
if (l <= 0.5)
s = (max - min) / (max + min);
else
s = (max - min) / (2 - max - min);
delta = max - min;
if (red == max)
h = (green - blue) / delta;
else if (green == max)
h = 2 + (blue - red) / delta;
else if (blue == max)
h = 4 + (red - green) / delta;
h *= 60;
if (h < 0.0)
h += 360;
}
*r = h;
*g = l;
*b = s;
}
/* Convert HLS to RGB. */
static void
hls_to_rgb (gdouble *h,
gdouble *l,
gdouble *s)
{
gdouble hue;
gdouble lightness;
gdouble saturation;
gdouble m1, m2;
gdouble r, g, b;
lightness = *l;
saturation = *s;
if (lightness <= 0.5)
m2 = lightness * (1 + saturation);
else
m2 = lightness + saturation - lightness * saturation;
m1 = 2 * lightness - m2;
if (saturation == 0)
{
*h = lightness;
*l = lightness;
*s = lightness;
}
else
{
hue = *h + 120;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
r = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
r = m2;
else if (hue < 240)
r = m1 + (m2 - m1) * (240 - hue) / 60;
else
r = m1;
hue = *h;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
g = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
g = m2;
else if (hue < 240)
g = m1 + (m2 - m1) * (240 - hue) / 60;
else
g = m1;
hue = *h-120;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
b = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
b = m2;
else if (hue < 240)
b = m1 + (m2 - m1) * (240 - hue) / 60;
else
b = m1;
*h = r;
*l = g;
*s = b;
}
}
/* Shade a GdkRGBA color. */
static void
shade_gdk_rgba (const GdkRGBA *a,
gfloat k,
GdkRGBA *b)
{
gdouble red;
gdouble green;
gdouble blue;
red = a->red;
green = a->green;
blue = a->blue;
if (k == 1.0)
{
b->red = red;
b->green = green;
b->blue = blue;
return;
}
rgb_to_hls (&red, &green, &blue);
green *= k;
if (green > 1.0)
green = 1.0;
else if (green < 0.0)
green = 0.0;
blue *= k;
if (blue > 1.0)
blue = 1.0;
else if (blue < 0.0)
blue = 0.0;
hls_to_rgb (&red, &green, &blue);
b->red = red;
b->green = green;
b->blue = blue;
b->alpha = a->alpha;
}
/* Convert a GdkColor to GdkRGBA. */
static void
convert_gdk_color_to_gdk_rgba (GdkColor *color,
GdkRGBA *rgba)
{
rgba->red = (gdouble) color->red / (gdouble) 65535;
rgba->green = (gdouble) color->green / (gdouble) 65535;
rgba->blue = (gdouble) color->blue / (gdouble) 65535;
rgba->alpha = 1.0;
}
enum {
ACTION_NORMAL,
ACTION_DRAG,
ACTION_PAGE_UP,
ACTION_PAGE_DOWN
};
static gboolean
os_thumb_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkAllocation allocation;
cairo_t *cr;
GtkStyle *style;
GdkRGBA bg, bg_active, bg_selected;
GdkRGBA bg_arrow_up, bg_arrow_down;
GdkRGBA bg_shadow, bg_dark_line, bg_bright_line;
GdkRGBA arrow_color;
OsThumb *thumb;
OsThumbPrivate *priv;
cairo_pattern_t *pat;
gint width, height;
gint radius;
gint action;
thumb = OS_THUMB (widget);
priv = thumb->priv;
radius = priv->rgba ? THUMB_RADIUS : 0;
gtk_widget_get_allocation (widget, &allocation);
width = allocation.width;
height = allocation.height;
style = gtk_widget_get_style (widget);
convert_gdk_color_to_gdk_rgba (&style->bg[gtk_widget_get_state (widget)], &bg);
convert_gdk_color_to_gdk_rgba (&style->bg[GTK_STATE_ACTIVE], &bg_active);
convert_gdk_color_to_gdk_rgba (&style->bg[GTK_STATE_SELECTED], &bg_selected);
convert_gdk_color_to_gdk_rgba (&style->fg[gtk_widget_get_state (widget)], &arrow_color);
cr = gdk_cairo_create (gtk_widget_get_window (widget));
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_save (cr);
cairo_translate (cr, 0.5, 0.5);
width--;
height--;
cairo_set_line_width (cr, 1.0);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
/* Type of action. */
action = ACTION_NORMAL;
if (priv->event & OS_EVENT_BUTTON_PRESS)
{
if (priv->event & OS_EVENT_MOTION_NOTIFY)
action = ACTION_DRAG;
else if ((priv->orientation == GTK_ORIENTATION_VERTICAL && (priv->pointer.y < height / 2)) ||
(priv->orientation == GTK_ORIENTATION_HORIZONTAL && (priv->pointer.x < width / 2)))
action = ACTION_PAGE_UP;
else
action = ACTION_PAGE_DOWN;
}
/* Background. */
draw_round_rect (cr, 0, 0, width, height, radius);
set_source_gdk_rgba (cr, &bg, 1.0);
cairo_fill_preserve (cr);
/* Background pattern from top to bottom. */
shade_gdk_rgba (&bg, 0.86, &bg_arrow_up);
shade_gdk_rgba (&bg, 1.1, &bg_arrow_down);
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
pat = cairo_pattern_create_linear (0, 0, 0, height);
else
pat = cairo_pattern_create_linear (0, 0, width, 0);
pattern_add_gdk_rgba_stop (pat, 0.0, &bg_arrow_up, 0.8);
pattern_add_gdk_rgba_stop (pat, 1.0, &bg_arrow_down, 0.8);
cairo_set_source (cr, pat);
cairo_pattern_destroy (pat);
if (action == ACTION_DRAG)
{
cairo_fill_preserve (cr);
set_source_gdk_rgba (cr, &bg, 0.8);
cairo_fill (cr);
}
else
cairo_fill (cr);
/* Page up or down pressed buttons. */
if (action == ACTION_PAGE_UP ||
action == ACTION_PAGE_DOWN)
{
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
if (action == ACTION_PAGE_UP)
cairo_rectangle (cr, 0, 0, width, height / 2);
else
cairo_rectangle (cr, 0, height / 2, width, height / 2);
}
else
{
if (action == ACTION_PAGE_UP)
cairo_rectangle (cr, 0, 0, width / 2, height);
else
cairo_rectangle (cr, width / 2, 0, width / 2, height);
}
set_source_gdk_rgba (cr, &bg, 0.8);
cairo_fill (cr);
}
/* 2px fat border around the thumb. */
cairo_save (cr);
cairo_set_line_width (cr, 2.0);
draw_round_rect (cr, 0.5, 0.5, width - 1, height - 1, radius - 1);
if (!priv->detached)
set_source_gdk_rgba (cr, &bg_selected, 1.0);
else
set_source_gdk_rgba (cr, &bg_active, 1.0);
cairo_stroke (cr);
cairo_restore (cr);
/* 1px subtle shadow around the background. */
shade_gdk_rgba (&bg, 0.2, &bg_shadow);
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
pat = cairo_pattern_create_linear (0, 0, 0, height);
else
pat = cairo_pattern_create_linear (0, 0, width, 0);
pattern_add_gdk_rgba_stop (pat, 0.5, &bg_shadow, 0.06);
switch (action)
{
case ACTION_NORMAL:
pattern_add_gdk_rgba_stop (pat, 0.0, &bg_shadow, 0.22);
pattern_add_gdk_rgba_stop (pat, 1.0, &bg_shadow, 0.22);
break;
case ACTION_DRAG:
pattern_add_gdk_rgba_stop (pat, 0.0, &bg_shadow, 0.2);
pattern_add_gdk_rgba_stop (pat, 1.0, &bg_shadow, 0.2);
break;
case ACTION_PAGE_UP:
pattern_add_gdk_rgba_stop (pat, 0.0, &bg_shadow, 0.1);
pattern_add_gdk_rgba_stop (pat, 1.0, &bg_shadow, 0.22);
break;
case ACTION_PAGE_DOWN:
pattern_add_gdk_rgba_stop (pat, 0.0, &bg_shadow, 0.22);
pattern_add_gdk_rgba_stop (pat, 1.0, &bg_shadow, 0.1);
break;
}
cairo_set_source (cr, pat);
cairo_pattern_destroy (pat);
draw_round_rect (cr, 1, 1, width - 2, height - 2, radius);
cairo_stroke (cr);
/* 1px frame around the background. */
shade_gdk_rgba (&bg, 0.6, &bg_dark_line);
shade_gdk_rgba (&bg, 1.2, &bg_bright_line);
draw_round_rect (cr, 2, 2, width - 4, height - 4, radius - 1);
set_source_gdk_rgba (cr, &bg_bright_line, 0.6);
cairo_stroke (cr);
/* Only draw the grip when the thumb is at full height. */
if ((priv->orientation == GTK_ORIENTATION_VERTICAL && height == THUMB_HEIGHT - 1) ||
(priv->orientation == GTK_ORIENTATION_HORIZONTAL && width == THUMB_HEIGHT - 1) )
{
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
pat = cairo_pattern_create_linear (0, 0, 0, height);
else
pat = cairo_pattern_create_linear (0, 0, width, 0);
pattern_add_gdk_rgba_stop (pat, 0.0, &bg_dark_line, 0.0);
pattern_add_gdk_rgba_stop (pat, 0.49, &bg_dark_line, 0.36);
pattern_add_gdk_rgba_stop (pat, 0.49, &bg_dark_line, 0.36);
pattern_add_gdk_rgba_stop (pat, 1.0, &bg_dark_line, 0.0);
cairo_set_source (cr, pat);
cairo_pattern_destroy (pat);
/* Grip. */
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
/* Page UP. */
draw_grip (cr, width / 2 - 6.5, 13.5, 5, 6);
/* Page DOWN. */
draw_grip (cr, width / 2 - 6.5, height / 2 + 3.5, 5, 6);
}
else
{
/* Page UP. */
draw_grip (cr, 16.5, height / 2 - 6.5, 5, 6);
/* Page DOWN. */
draw_grip (cr, width / 2 + 3.5, height / 2 - 6.5, 5, 6);
}
cairo_fill (cr);
}
/* Separators between the two steppers. */
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
cairo_move_to (cr, 1.5, height / 2);
cairo_line_to (cr, width - 1.5, height / 2);
set_source_gdk_rgba (cr, &bg_dark_line, 0.36);
cairo_stroke (cr);
cairo_move_to (cr, 1.5, 1 + height / 2);
cairo_line_to (cr, width - 1.5, 1 + height / 2);
set_source_gdk_rgba (cr, &bg_bright_line, 0.5);
cairo_stroke (cr);
}
else
{
cairo_move_to (cr, width / 2, 1.5);
cairo_line_to (cr, width / 2, height - 1.5);
set_source_gdk_rgba (cr, &bg_dark_line, 0.36);
cairo_stroke (cr);
cairo_move_to (cr, 1 + width / 2, 1.5);
cairo_line_to (cr, 1 + width / 2, height - 1.5);
set_source_gdk_rgba (cr, &bg_bright_line, 0.5);
cairo_stroke (cr);
}
/* Arrows. */
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
/* Direction UP. */
cairo_save (cr);
cairo_translate (cr, width / 2 + 0.5, 8.5);
cairo_rotate (cr, G_PI);
draw_arrow (cr, &arrow_color, 0.5, 0, 5, 3);
cairo_restore (cr);
/* Direction DOWN. */
cairo_save (cr);
cairo_translate (cr, width / 2 + 0.5, height - 8.5);
cairo_rotate (cr, 0);
draw_arrow (cr, &arrow_color, -0.5, 0, 5, 3);
cairo_restore (cr);
}
else
{
/* Direction LEFT. */
cairo_save (cr);
cairo_translate (cr, 8.5, height / 2 + 0.5);
cairo_rotate (cr, G_PI * 0.5);
draw_arrow (cr, &arrow_color, -0.5, 0, 5, 3);
cairo_restore (cr);
/* Direction RIGHT. */
cairo_save (cr);
cairo_translate (cr, width - 8.5, height / 2 + 0.5);
cairo_rotate (cr, G_PI * 1.5);
draw_arrow (cr, &arrow_color, 0.5, 0, 5, 3);
cairo_restore (cr);
}
cairo_restore (cr);
cairo_restore (cr);
cairo_destroy (cr);
return FALSE;
}
static gboolean
os_thumb_leave_notify_event (GtkWidget *widget,
GdkEventCrossing *event)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
/* This event is called more times with Gtk+ 3 and XI2 devices,
* let's accept that because the overhead is still minimal. */
/* If we exit the thumb when a button is pressed,
* there's no need to stop the animation, it should
* already be stopped.
* Stop it only if OS_EVENT_BUTTON_PRESS is not set. */
if (!(priv->event & OS_EVENT_BUTTON_PRESS))
{
if (priv->source_id != 0)
{
g_source_remove (priv->source_id);
priv->source_id = 0;
}
os_animation_stop (priv->animation, NULL);
}
priv->tolerance = FALSE;
return FALSE;
}
static void
os_thumb_map (GtkWidget *widget)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
gtk_window_set_opacity (GTK_WINDOW (widget), 1.0f);
if (priv->grabbed_widget != NULL)
g_object_unref (priv->grabbed_widget);
priv->grabbed_widget = gtk_grab_get_current ();
if (priv->grabbed_widget != NULL)
{
g_object_ref_sink (priv->grabbed_widget);
gtk_grab_remove (priv->grabbed_widget);
}
GTK_WIDGET_CLASS (os_thumb_parent_class)->map (widget);
}
static gboolean
os_thumb_motion_notify_event (GtkWidget *widget,
GdkEventMotion *event)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
if (priv->source_id != 0)
{
g_source_remove (priv->source_id);
priv->source_id = 0;
}
/* On motion, stop the fade-out. */
os_animation_stop (priv->animation, fade_out_stop_cb);
/* If you're not dragging, and you're outside
* the tolerance pixels, enable the fade-out.
* OS_EVENT_MOTION_NOTIFY is set only on dragging,
* see code few lines below. */
if (!(priv->event & OS_EVENT_MOTION_NOTIFY))
{
if (!priv->tolerance ||
(abs (priv->pointer.x - event->x) > TOLERANCE_FADE ||
abs (priv->pointer.y - event->y) > TOLERANCE_FADE))
{
priv->tolerance = FALSE;
priv->source_id = g_timeout_add (TIMEOUT_FADE_OUT,
timeout_fade_out_cb,
thumb);
}
}
if (priv->event & OS_EVENT_BUTTON_PRESS &&
!(priv->event & OS_EVENT_MOTION_NOTIFY))
{
if (abs (priv->pointer_root.x - event->x_root) <= TOLERANCE_MOTION &&
abs (priv->pointer_root.y - event->y_root) <= TOLERANCE_MOTION)
return FALSE;
priv->event |= OS_EVENT_MOTION_NOTIFY;
gtk_widget_queue_draw (widget);
}
return FALSE;
}
static void
os_thumb_screen_changed (GtkWidget *widget,
GdkScreen *old_screen)
{
GdkScreen *screen;
GdkColormap *colormap;
screen = gtk_widget_get_screen (widget);
colormap = gdk_screen_get_rgba_colormap (screen);
if (colormap)
gtk_widget_set_colormap (widget, colormap);
}
static gboolean
os_thumb_scroll_event (GtkWidget *widget,
GdkEventScroll *event)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
if (priv->source_id != 0)
{
g_source_remove (priv->source_id);
priv->source_id = 0;
}
/* If started, stop the fade-out. */
os_animation_stop (priv->animation, fade_out_stop_cb);
if (priv->event & OS_EVENT_MOTION_NOTIFY)
{
priv->event &= ~(OS_EVENT_MOTION_NOTIFY);
gtk_widget_queue_draw (widget);
}
return FALSE;
}
static void
os_thumb_unmap (GtkWidget *widget)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (widget);
priv = thumb->priv;
priv->event = OS_EVENT_NONE;
priv->tolerance = FALSE;
gtk_grab_remove (widget);
if (priv->grabbed_widget != NULL && gtk_widget_get_mapped (priv->grabbed_widget))
gtk_grab_add (priv->grabbed_widget);
GTK_WIDGET_CLASS (os_thumb_parent_class)->unmap (widget);
}
static GObject*
os_thumb_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *object;
object = G_OBJECT_CLASS (os_thumb_parent_class)->constructor
(type, n_construct_properties, construct_properties);
g_object_set (object, "type", GTK_WINDOW_POPUP, NULL);
return object;
}
static void
os_thumb_dispose (GObject *object)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (object);
priv = thumb->priv;
if (priv->source_id != 0)
{
g_source_remove (priv->source_id);
priv->source_id = 0;
}
if (priv->animation != NULL)
{
g_object_unref (priv->animation);
priv->animation = NULL;
}
if (priv->grabbed_widget != NULL)
{
g_object_unref (priv->grabbed_widget);
priv->grabbed_widget = NULL;
}
G_OBJECT_CLASS (os_thumb_parent_class)->dispose (object);
}
static void
os_thumb_finalize (GObject *object)
{
G_OBJECT_CLASS (os_thumb_parent_class)->finalize (object);
}
static void
os_thumb_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (object);
priv = thumb->priv;
switch (prop_id)
{
case PROP_ORIENTATION:
g_value_set_enum (value, priv->orientation);
break;
default:
break;
}
}
static void
os_thumb_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
OsThumb *thumb;
OsThumbPrivate *priv;
thumb = OS_THUMB (object);
priv = thumb->priv;
switch (prop_id)
{
case PROP_ORIENTATION:
{
priv->orientation = g_value_get_enum (value);
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
gtk_window_resize (GTK_WINDOW (object), THUMB_WIDTH, THUMB_HEIGHT);
else
gtk_window_resize (GTK_WINDOW (object), THUMB_HEIGHT, THUMB_WIDTH);
break;
}
default:
break;
}
}
/* Public functions. */
/**
* os_thumb_new:
*
* Creates a new OsThumb instance.
*
* Returns: a new OsThumb instance.
**/
GtkWidget*
os_thumb_new (GtkOrientation orientation)
{
return g_object_new (OS_TYPE_THUMB, "orientation", orientation, NULL);
}
/**
* os_thumb_resize:
* @thumb: a #OsThumb
* @width: width in pixels
* @height: height in pixels
*
* Resize the thumb.
**/
void
os_thumb_resize (OsThumb *thumb,
gint width,
gint height)
{
g_return_if_fail (OS_IS_THUMB (thumb));
gtk_window_resize (GTK_WINDOW (thumb), width, height);
}
/**
* os_thumb_set_detached:
* @thumb: a #OsThumb
* @detached: a gboolean
*
* Sets the thumb to be detached.
**/
void
os_thumb_set_detached (OsThumb *thumb,
gboolean detached)
{
OsThumbPrivate *priv;
g_return_if_fail (OS_IS_THUMB (thumb));
priv = thumb->priv;
if (priv->detached != detached)
{
priv->detached = detached;
gtk_widget_queue_draw (GTK_WIDGET (thumb));
}
}
overlay-scrollbar-0.2.17.1+16.04.20151117/os/Makefile.am 0000644 0000153 0000161 00000001007 12622657062 022420 0 ustar pbuser pbgroup 0000000 0000000 VER=
source_h = \
$(srcdir)/os-private.h \
$(srcdir)/os-scrollbar.h
source_c = \
$(srcdir)/os-animation.c \
$(srcdir)/os-bar.c \
$(srcdir)/os-log.c \
$(srcdir)/os-scrollbar.c \
$(srcdir)/os-thumb.c
liboverlay_scrollbar_LTLIBRARIES = liboverlay-scrollbar.la
liboverlay_scrollbardir = $(GTK_MODULES_DIR)
liboverlay_scrollbar_la_SOURCES = $(source_h) $(source_c)
liboverlay_scrollbar_la_CFLAGS = $(OS_CFLAGS)
liboverlay_scrollbar_la_LIBADD = $(OS_LIBADD) -lm
liboverlay_scrollbar_la_LDFLAGS = $(OS_LDFLAGS)
overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-scrollbar.h 0000644 0000153 0000161 00000002113 12622657062 023136 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
*/
#ifndef OVERLAY_SCROLLBAR_H
#define OVERLAY_SCROLLBAR_H
#include
G_BEGIN_DECLS
typedef enum
{
SCROLLBAR_MODE_NORMAL,
SCROLLBAR_MODE_OVERLAY_AUTO,
SCROLLBAR_MODE_OVERLAY_POINTER,
SCROLLBAR_MODE_OVERLAY_TOUCH
} ScrollbarMode;
G_END_DECLS
#endif /* OVERLAY_SCROLLBAR_H */
overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-scrollbar.c 0000644 0000153 0000161 00000361424 12622657062 023146 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
*/
#ifndef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "os-scrollbar.h"
#include "os-private.h"
#include
#include
#include
#include
#include
#include
/* Size of the bar in pixels. */
#define BAR_SIZE 3
/* Size of the proximity effect in pixels. */
#define PROXIMITY_SIZE 34
/* Max duration of the scrolling. */
#define MAX_DURATION_SCROLLING 1000
/* Min duration of the scrolling. */
#define MIN_DURATION_SCROLLING 250
/* Modifier key used to slow down actions. */
#define MODIFIER_KEY GDK_CONTROL_MASK
/* Timeout assumed for PropertyNotify _NET_ACTIVE_WINDOW event. */
#define TIMEOUT_PRESENT_WINDOW 400
/* Timeout before hiding in ms, after leaving the proximity area. */
#define TIMEOUT_PROXIMITY_HIDE 200
/* Timeout before hiding in ms, after leaving the thumb. */
#define TIMEOUT_THUMB_HIDE 200
/* Timeout before showing in ms, after entering the proximity. */
#define TIMEOUT_THUMB_SHOW 100
/* Timeout before hiding in ms, after leaving the toplevel. */
#define TIMEOUT_TOPLEVEL_HIDE 200
typedef enum {
OS_SCROLL_PAGE,
OS_SCROLL_STEP
} OsScrollType;
typedef enum
{
OS_SIDE_TOP, /* Scrollbar is at top. */
OS_SIDE_BOTTOM, /* Scrollbar is at bottom. */
OS_SIDE_LEFT, /* Scrollbar is at left. */
OS_SIDE_RIGHT /* Scrollbar is at right. */
} OsSide;
typedef enum
{
OS_STATE_NONE = 0, /* No state. */
OS_STATE_CONNECTED = 1, /* Thumb and bar move connected, like a native scrollbar. */
OS_STATE_DETACHED = 2, /* The thumb is visually detached from the bar, and you can see the tail. */
OS_STATE_FINE_SCROLL = 4, /* A fine scroll is currently running, the modifier key must be pressed. */
OS_STATE_FULLSIZE = 8, /* The scrollbar is fullsize, so we hide it. */
OS_STATE_INTERNAL = 16, /* The thumb is touching a strut or a screen edge, it's internal. */
OS_STATE_LOCKED = 32, /* Thumb is locked in its position when moving in the proximity area. */
OS_STATE_RECONNECTING = 64 /* The thumb is reconnecting with the bar, there's likely an animation in progress. */
} OsStateFlags;
typedef enum
{
OS_STRUT_SIDE_NONE = 0, /* No strut. */
OS_STRUT_SIDE_TOP = 1, /* Strut at top. */
OS_STRUT_SIDE_BOTTOM = 2, /* Strut at bottom. */
OS_STRUT_SIDE_LEFT = 4, /* Strut at left. */
OS_STRUT_SIDE_RIGHT = 8 /* Strut at right. */
} OsStrutSideFlags;
typedef struct
{
gboolean proximity;
gboolean running;
} OsWindowFilter;
typedef struct
{
GdkRectangle overlay;
GdkRectangle slider;
GdkRectangle trough;
GtkAllocation bar_all;
GtkAllocation thumb_all;
GtkAdjustment *adjustment;
GtkOrientation orientation;
GtkWidget *thumb;
GtkWindowGroup *window_group;
OsAnimation *animation;
OsBar *bar;
OsCoordinate pointer;
OsCoordinate thumb_win;
OsEventFlags event;
OsStateFlags state;
OsSide side;
OsWindowFilter filter;
gboolean active_window;
gboolean allow_resize;
gboolean allow_resize_paned;
gboolean resizing_paned;
gboolean hidable_thumb;
gboolean window_button_press; /* FIXME(Cimi) to replace with X11 input events. */
gdouble value;
gfloat fine_scroll_multiplier;
gfloat slide_initial_slider_position;
gfloat slide_initial_coordinate;
gint64 present_time;
guint32 source_deactivate_bar_id;
guint32 source_hide_thumb_id;
guint32 source_show_thumb_id;
guint32 source_unlock_thumb_id;
} OsScrollbarPrivate;
static Atom net_active_window_atom = None;
static Atom unity_net_workarea_region_atom = None;
static GSList *os_root_list = NULL;
static GSList *scrollbar_list = NULL;
static GQuark os_quark_placement = 0;
static GQuark os_quark_qdata = 0;
static ScrollbarMode scrollbar_mode = SCROLLBAR_MODE_NORMAL;
static cairo_region_t *os_workarea = NULL;
static void adjustment_changed_cb (GtkAdjustment *adjustment, gpointer user_data);
static void adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data);
static OsScrollbarPrivate* get_private (GtkWidget *widget);
static void notify_adjustment_cb (GObject *object, gpointer user_data);
static void notify_orientation_cb (GObject *object, gpointer user_data);
static GdkFilterReturn root_filter_func (GdkXEvent *gdkxevent, GdkEvent *event, gpointer user_data);
static void scrolling_cb (gfloat weight, gpointer user_data);
static void scrolling_end_cb (gpointer user_data);
static void swap_adjustment (GtkScrollbar *scrollbar, GtkAdjustment *adjustment);
static void swap_thumb (GtkScrollbar *scrollbar, GtkWidget *thumb);
static gboolean thumb_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data);
static gboolean thumb_button_release_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data);
static gboolean thumb_enter_notify_event_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data);
static gboolean thumb_leave_notify_event_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data);
static void thumb_map_cb (GtkWidget *widget, gpointer user_data);
static gboolean thumb_motion_notify_event_cb (GtkWidget *widget, GdkEventMotion *event, gpointer user_data);
static gboolean thumb_scroll_event_cb (GtkWidget *widget, GdkEventScroll *event, gpointer user_data);
static void thumb_unmap_cb (GtkWidget *widget, gpointer user_data);
/* GtkScrollbar vfunc pointers. */
static gboolean (* pre_hijacked_scrollbar_expose_event) (GtkWidget *widget, GdkEventExpose *event);
static void (* pre_hijacked_scrollbar_size_request) (GtkWidget *widget, GtkRequisition *requisition);
static void (* pre_hijacked_scrollbar_state_changed) (GtkWidget *widget, GtkStateType state);
static void (* pre_hijacked_scrollbar_grab_notify) (GtkWidget *widget, gboolean was_grabbed);
static void (* pre_hijacked_scrollbar_hide) (GtkWidget *widget);
static void (* pre_hijacked_scrollbar_map) (GtkWidget *widget);
static void (* pre_hijacked_scrollbar_realize) (GtkWidget *widget);
static void (* pre_hijacked_scrollbar_show) (GtkWidget *widget);
static void (* pre_hijacked_scrollbar_size_allocate) (GtkWidget *widget, GdkRectangle *allocation);
static void (* pre_hijacked_scrollbar_unmap) (GtkWidget *widget);
static void (* pre_hijacked_scrollbar_unrealize) (GtkWidget *widget);
static void (* pre_hijacked_scrollbar_dispose) (GObject *object);
/* Hijacked GtkScrollbar vfunc pointers. */
static gboolean hijacked_scrollbar_expose_event (GtkWidget *widget, GdkEventExpose *event);
static void hijacked_scrollbar_size_request (GtkWidget *widget, GtkRequisition *requisition);
static void hijacked_scrollbar_state_changed (GtkWidget *widget, GtkStateType state);
static void hijacked_scrollbar_grab_notify (GtkWidget *widget, gboolean was_grabbed);
static void hijacked_scrollbar_hide (GtkWidget *widget);
static void hijacked_scrollbar_map (GtkWidget *widget);
static void hijacked_scrollbar_realize (GtkWidget *widget);
static void hijacked_scrollbar_show (GtkWidget *widget);
static void hijacked_scrollbar_size_allocate (GtkWidget *widget, GdkRectangle *allocation);
static void hijacked_scrollbar_unmap (GtkWidget *widget);
static void hijacked_scrollbar_unrealize (GtkWidget *widget);
/* GtkWidget vfunc pointers. */
static void (* widget_class_hide) (GtkWidget *widget);
static void (* widget_class_map) (GtkWidget *widget);
static void (* widget_class_realize) (GtkWidget *widget);
static void (* widget_class_show) (GtkWidget *widget);
static void (* widget_class_unmap) (GtkWidget *widget);
static void (* widget_class_unrealize) (GtkWidget *widget);
/* Calculate bar layout info. */
static void
calc_layout_bar (GtkScrollbar *scrollbar,
gdouble adjustment_value)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
gint y, trough_length, height;
y = 0;
trough_length = priv->trough.height;
if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
height = (trough_length * (gtk_adjustment_get_page_size (priv->adjustment) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment))));
else
height = gtk_range_get_min_slider_size (GTK_RANGE (scrollbar));
height = MAX (height, gtk_range_get_min_slider_size (GTK_RANGE (scrollbar)));
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
y = (trough_length - height) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
y = CLAMP (y, 0, trough_length);
priv->overlay.y = y;
priv->overlay.height = height;
}
else
{
gint x, trough_length, width;
x = 0;
trough_length = priv->trough.width;
if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
width = (trough_length * (gtk_adjustment_get_page_size (priv->adjustment) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment))));
else
width = gtk_range_get_min_slider_size (GTK_RANGE (scrollbar));
width = MAX (width, gtk_range_get_min_slider_size (GTK_RANGE (scrollbar)));
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
x = (trough_length - width) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
x = CLAMP (x, 0, trough_length);
priv->overlay.x = x;
priv->overlay.width = width;
}
}
/* Calculate slider (thumb) layout info. */
static void
calc_layout_slider (GtkScrollbar *scrollbar,
gdouble adjustment_value)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
gint y, trough_length, height;
y = 0;
trough_length = priv->trough.height;
height = priv->slider.height;
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
y = (trough_length - height) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
y = CLAMP (y, 0, trough_length);
priv->slider.y = y;
}
else
{
gint x, trough_length, width;
x = 0;
trough_length = priv->trough.width;
width = priv->slider.width;
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
x = (trough_length - width) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
x = CLAMP (x, 0, trough_length);
priv->slider.x = x;
}
}
/* Calculate slide_initial_slider_position with more precision. */
static void
calc_precise_slide_values (GtkScrollbar *scrollbar,
gfloat x_coordinate,
gfloat y_coordinate)
{
OsScrollbarPrivate *priv;
gdouble adjustment_value;
priv = get_private (GTK_WIDGET (scrollbar));
adjustment_value = gtk_adjustment_get_value (priv->adjustment);
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
gdouble y1, y2, trough_length, height;
y1 = 0;
trough_length = priv->trough.height;
if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
height = (trough_length * (gtk_adjustment_get_page_size (priv->adjustment) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment))));
else
height = gtk_range_get_min_slider_size (GTK_RANGE (scrollbar));
height = MAX (height, gtk_range_get_min_slider_size (GTK_RANGE (scrollbar)));
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
y1 = (trough_length - height) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
y2 = 0;
height = priv->slider.height;
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
y2 = (trough_length - height) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
priv->slide_initial_slider_position = CLAMP (MIN (y1, y2), 0, trough_length);
priv->slide_initial_coordinate = y_coordinate;
}
else
{
gdouble x1, x2, trough_length, width;
x1 = 0;
trough_length = priv->trough.width;
if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
width = (trough_length * (gtk_adjustment_get_page_size (priv->adjustment) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment))));
else
width = gtk_range_get_min_slider_size (GTK_RANGE (scrollbar));
width = MAX (width, gtk_range_get_min_slider_size (GTK_RANGE (scrollbar)));
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
x1 = (trough_length - width) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
x2 = 0;
width = priv->slider.width;
if (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment) != 0)
x2 = (trough_length - width) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)));
priv->slide_initial_slider_position = CLAMP (MIN (x1, x2), 0, trough_length);
priv->slide_initial_coordinate = x_coordinate;
}
}
/* Calculate the workarea using _UNITY_NET_WORKAREA_REGION. */
static void
calc_workarea (Display *display,
Window root)
{
Atom type;
gint result, fmt;
gulong nitems, nleft;
guchar *property_data;
gulong *long_data;
result = XGetWindowProperty (display, root,
unity_net_workarea_region_atom,
0L, 4096L, FALSE, XA_CARDINAL,
&type, &fmt, &nitems, &nleft, &property_data);
/* Clear the os_workarea region,
* before the union with the new rectangles.
* Maybe it'd be better to place this call
* inside the if statement below. */
cairo_region_subtract (os_workarea, os_workarea);
if (result == Success && property_data)
{
long_data = (gulong*) property_data;
if (fmt == 32 && type == XA_CARDINAL && nitems % 4 == 0)
{
guint count;
guint i;
count = nitems / 4;
for (i = 0; i < count; i++)
{
cairo_rectangle_int_t rect;
rect.x = long_data[i * 4 + 0];
rect.y = long_data[i * 4 + 1];
rect.width = long_data[i * 4 + 2];
rect.height = long_data[i * 4 + 3];
cairo_region_union_rectangle (os_workarea, &rect);
}
}
}
}
/* Check whether the thumb movement can be considered connected or not. */
static void
check_connection (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
gint x_pos, y_pos;
priv = get_private (GTK_WIDGET (scrollbar));
/* This seems to be required to get proper values. */
calc_layout_bar (scrollbar, gtk_adjustment_get_value (priv->adjustment));
calc_layout_slider (scrollbar, gtk_adjustment_get_value (priv->adjustment));
gdk_window_get_origin (gtk_widget_get_window (priv->thumb), &x_pos, &y_pos);
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
if (priv->overlay.height > priv->slider.height)
{
if (y_pos >= priv->thumb_win.y + priv->overlay.y &&
y_pos + priv->slider.height <= priv->thumb_win.y + priv->overlay.y + priv->overlay.height)
priv->state |= OS_STATE_CONNECTED;
else
priv->state &= ~(OS_STATE_CONNECTED);
}
else
{
if (y_pos == priv->thumb_win.y + priv->slider.y)
priv->state |= OS_STATE_CONNECTED;
else
priv->state &= ~(OS_STATE_CONNECTED);
}
}
else
{
if (priv->overlay.width > priv->slider.width)
{
if (x_pos >= priv->thumb_win.x + priv->overlay.x &&
x_pos + priv->slider.width <= priv->thumb_win.x + priv->overlay.x + priv->overlay.width)
priv->state |= OS_STATE_CONNECTED;
else
priv->state &= ~(OS_STATE_CONNECTED);
}
else
{
if (x_pos == priv->thumb_win.x + priv->slider.x)
priv->state |= OS_STATE_CONNECTED;
else
priv->state &= ~(OS_STATE_CONNECTED);
}
}
}
/* Convert coordinates into GtkRange values. */
static inline gdouble
coord_to_value (GtkScrollbar *scrollbar,
gfloat coord)
{
OsScrollbarPrivate *priv;
gdouble frac;
gdouble value;
gint trough_length;
gint slider_length;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
trough_length = priv->trough.height;
slider_length = MAX (priv->slider.height, priv->overlay.height);
}
else
{
trough_length = priv->trough.width;
slider_length = MAX (priv->slider.width, priv->overlay.width);
}
if (trough_length == slider_length)
frac = 1.0;
else
frac = (MAX (0, coord) / (gdouble) (trough_length - slider_length));
value = gtk_adjustment_get_lower (priv->adjustment) + frac * (gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment));
value = CLAMP (value,
gtk_adjustment_get_lower (priv->adjustment),
gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment));
return value;
}
/* destroy the private struct */
static void
destroy_private (gpointer priv)
{
g_slice_free (OsScrollbarPrivate, priv);
}
/* Get the private struct. If there isn't one, return NULL */
static OsScrollbarPrivate*
lookup_private (GtkWidget *widget)
{
return g_object_get_qdata (G_OBJECT (widget), os_quark_qdata);
}
/* Get the private struct. If there isn't one, create it */
static OsScrollbarPrivate*
get_private (GtkWidget *widget)
{
OsScrollbarPrivate *priv;
/* Fetch the private qdata struct. */
priv = lookup_private (widget);
if (!priv)
{
/* The widget doesn't have an associated qdata,
* initialize and store it. */
OsScrollbarPrivate *qdata;
/* Describe the widget for theming. */
gtk_widget_set_name (widget, "OsScrollbar");
if (os_root_list == NULL)
{
GdkScreen *screen;
GdkWindow *root;
/* Create the static linked list prepending the object. */
os_root_list = g_slist_prepend (os_root_list, widget);
if (os_workarea == NULL)
os_workarea = cairo_region_create ();
/* Apply the root_filter_func. */
screen = gtk_widget_get_screen (widget);
root = gdk_screen_get_root_window (screen);
gdk_window_set_events (root, gdk_window_get_events (root) |
GDK_PROPERTY_CHANGE_MASK);
gdk_window_add_filter (root, root_filter_func, NULL);
}
else
{
/* Prepend the object to the static linked list. */
os_root_list = g_slist_prepend (os_root_list, widget);
}
/* Initialize memory. */
qdata = g_slice_new0 (OsScrollbarPrivate);
/* Initialize struct variables. */
qdata->side = OS_SIDE_RIGHT;
qdata->hidable_thumb = TRUE;
qdata->fine_scroll_multiplier = 1.0;
qdata->bar = os_bar_new ();
qdata->window_group = gtk_window_group_new ();
qdata->animation = os_animation_new (RATE_ANIMATION, MAX_DURATION_SCROLLING,
scrolling_cb, scrolling_end_cb, widget);
/* Store qdata. */
g_object_set_qdata_full (G_OBJECT (widget), os_quark_qdata, qdata, destroy_private);
priv = qdata;
/* Create adjustment and thumb. */
if (gtk_range_get_adjustment (GTK_RANGE (widget)))
swap_adjustment (GTK_SCROLLBAR (widget), gtk_range_get_adjustment (GTK_RANGE (widget)));
priv->orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
swap_thumb (GTK_SCROLLBAR (widget), os_thumb_new (priv->orientation));
priv->resizing_paned = FALSE;
/* Connect to adjustment and orientation signals. */
g_signal_connect (G_OBJECT (widget), "notify::adjustment",
G_CALLBACK (notify_adjustment_cb), NULL);
g_signal_connect (G_OBJECT (widget), "notify::orientation",
G_CALLBACK (notify_orientation_cb), NULL);
}
return priv;
}
/* Hide the thumb if it's the case. */
static void
hide_thumb (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->hidable_thumb)
gtk_widget_hide (priv->thumb);
}
/* Timeout before hiding the thumb. */
static gboolean
hide_thumb_cb (gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
hide_thumb (scrollbar);
priv->source_hide_thumb_id = 0;
return FALSE;
}
/* Return TRUE if the widget is insensitive. */
static gboolean
is_insensitive (GtkScrollbar *scrollbar)
{
return gtk_widget_get_state (GTK_WIDGET (scrollbar)) == GTK_STATE_INSENSITIVE;
}
/* Move the bar. */
static void
move_bar (GtkScrollbar *scrollbar)
{
GdkRectangle mask;
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
mask.x = 0;
mask.y = priv->overlay.y;
mask.width = priv->bar_all.width;
mask.height = priv->overlay.height;
}
else
{
mask.x = priv->overlay.x;
mask.y = 0;
mask.width = priv->overlay.width;
mask.height = priv->bar_all.height;
}
os_bar_move_resize (priv->bar, mask);
}
/* Callback called by the scrolling animation. */
static void
scrolling_cb (gfloat weight,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
gdouble new_value;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
if (weight < 1.0f)
{
gdouble current_value;
gdouble diff;
current_value = gtk_adjustment_get_value (priv->adjustment);
diff = priv->value - current_value;
new_value = current_value + diff * weight;
}
else
new_value = priv->value;
gtk_adjustment_set_value (priv->adjustment, new_value);
}
/* End function called by the scrolling animation. */
static void
scrolling_end_cb (gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
/* Only update the slide values at the end of a reconnection,
* with the button pressed, otherwise it's not needed. */
if ((priv->state & OS_STATE_RECONNECTING) &&
(priv->event & OS_EVENT_BUTTON_PRESS))
{
gint x_pos, y_pos;
gdk_window_get_origin (gtk_widget_get_window (priv->thumb), &x_pos, &y_pos);
calc_precise_slide_values (scrollbar, x_pos + priv->pointer.x, y_pos + priv->pointer.y);
}
/* Check if the thumb can be considered connected after the animation. */
check_connection (scrollbar);
/* Unset OS_STATE_RECONNECTING since the animation ended. */
priv->state &= ~(OS_STATE_RECONNECTING);
}
/* Sanitize x coordinate of thumb window. */
static gint
sanitize_x (GtkScrollbar *scrollbar,
gint x,
gint y)
{
GdkRectangle gdk_rect;
GdkScreen *screen;
OsScrollbarPrivate *priv;
cairo_rectangle_int_t rect;
gint screen_x, screen_width, n_monitor, monitor_x;
priv = get_private (GTK_WIDGET (scrollbar));
/* The x - 1 coordinate shift is done
* to calculate monitor boundaries. */
monitor_x = priv->side == OS_SIDE_LEFT ? x : x - 1;
screen = gtk_widget_get_screen (GTK_WIDGET (scrollbar));
n_monitor = gdk_screen_get_monitor_at_point (screen, monitor_x, y);
gdk_screen_get_monitor_geometry (screen, n_monitor, &gdk_rect);
rect.x = gdk_rect.x;
rect.y = gdk_rect.y;
rect.width = gdk_rect.width;
rect.height = gdk_rect.height;
screen_x = rect.x;
screen_width = rect.x + rect.width;
if (!cairo_region_is_empty (os_workarea))
{
cairo_region_t *monitor_workarea;
cairo_region_t *struts_region;
cairo_rectangle_int_t tmp_rect;
gint i, reg_x, reg_width;
reg_x = rect.x;
reg_width = rect.x + rect.width;
/* Full monitor region. */
monitor_workarea = cairo_region_create_rectangle (&rect);
struts_region = cairo_region_copy (monitor_workarea);
/* Workarea region for current monitor. */
cairo_region_intersect (monitor_workarea, os_workarea);
/* Struts region for current monitor. */
cairo_region_subtract (struts_region, monitor_workarea);
for (i = 0; i < cairo_region_num_rectangles (struts_region); i++)
{
OsStrutSideFlags strut_side;
gint count;
cairo_region_get_rectangle (struts_region, i, &tmp_rect);
strut_side = OS_STRUT_SIDE_NONE;
count = 0;
/* Determine which side the strut is on. */
if (tmp_rect.y == rect.y)
{
strut_side |= OS_STRUT_SIDE_TOP;
count++;
}
if (tmp_rect.x == rect.x)
{
strut_side |= OS_STRUT_SIDE_LEFT;
count++;
}
if (tmp_rect.x + tmp_rect.width == rect.x + rect.width)
{
strut_side |= OS_STRUT_SIDE_RIGHT;
count++;
}
if (tmp_rect.y + tmp_rect.height == rect.y + rect.height)
{
strut_side |= OS_STRUT_SIDE_BOTTOM;
count++;
}
/* Handle multiple sides. */
if (count >= 2)
{
if (tmp_rect.width > tmp_rect.height)
strut_side &= ~(OS_STRUT_SIDE_LEFT | OS_STRUT_SIDE_RIGHT);
else if (tmp_rect.width < tmp_rect.height)
strut_side &= ~(OS_STRUT_SIDE_TOP | OS_STRUT_SIDE_BOTTOM);
}
/* Get the monitor boundaries using the strut. */
if (strut_side & OS_STRUT_SIDE_LEFT)
{
if (tmp_rect.x + tmp_rect.width > reg_x)
reg_x = tmp_rect.x + tmp_rect.width;
}
if (strut_side & OS_STRUT_SIDE_RIGHT)
{
if (tmp_rect.x < reg_width)
reg_width = tmp_rect.x;
}
}
screen_x = reg_x;
screen_width = reg_width;
cairo_region_destroy (monitor_workarea);
cairo_region_destroy (struts_region);
}
if (priv->side == OS_SIDE_RIGHT &&
(n_monitor != gdk_screen_get_monitor_at_point (screen, monitor_x + priv->thumb_all.width, y) ||
monitor_x + priv->thumb_all.width >= screen_width))
{
priv->state |= OS_STATE_INTERNAL;
return MAX (x - priv->thumb_all.width, screen_width - priv->thumb_all.width);
}
if (priv->side == OS_SIDE_LEFT &&
(n_monitor != gdk_screen_get_monitor_at_point (screen, monitor_x - priv->thumb_all.width, y) ||
monitor_x - priv->thumb_all.width <= screen_x))
{
priv->state |= OS_STATE_INTERNAL;
return MAX (x, screen_x);
}
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
priv->state &= ~(OS_STATE_INTERNAL);
return x;
}
/* Sanitize y coordinate of thumb window. */
static gint
sanitize_y (GtkScrollbar *scrollbar,
gint x,
gint y)
{
GdkRectangle gdk_rect;
GdkScreen *screen;
OsScrollbarPrivate *priv;
cairo_rectangle_int_t rect;
gint screen_y, screen_height, n_monitor, monitor_y;
priv = get_private (GTK_WIDGET (scrollbar));
/* The y - 1 coordinate shift is done
* to calculate monitor boundaries. */
monitor_y = priv->side == OS_SIDE_TOP ? y : y - 1;
screen = gtk_widget_get_screen (GTK_WIDGET (scrollbar));
n_monitor = gdk_screen_get_monitor_at_point (screen, x, monitor_y);
gdk_screen_get_monitor_geometry (screen, n_monitor, &gdk_rect);
rect.x = gdk_rect.x;
rect.y = gdk_rect.y;
rect.width = gdk_rect.width;
rect.height = gdk_rect.height;
screen_y = rect.y;
screen_height = rect.y + rect.height;
if (!cairo_region_is_empty (os_workarea))
{
cairo_region_t *monitor_workarea;
cairo_region_t *struts_region;
cairo_rectangle_int_t tmp_rect;
gint i, reg_y, reg_height;
reg_y = rect.y;
reg_height = rect.y + rect.height;
/* Full monitor region. */
monitor_workarea = cairo_region_create_rectangle (&rect);
struts_region = cairo_region_copy (monitor_workarea);
/* Workarea region for current monitor. */
cairo_region_intersect (monitor_workarea, os_workarea);
/* Struts region for current monitor. */
cairo_region_subtract (struts_region, monitor_workarea);
for (i = 0; i < cairo_region_num_rectangles (struts_region); i++)
{
OsStrutSideFlags strut_side;
gint count;
cairo_region_get_rectangle (struts_region, i, &tmp_rect);
strut_side = OS_STRUT_SIDE_NONE;
count = 0;
/* Determine which side the strut is on. */
if (tmp_rect.y == rect.y)
{
strut_side |= OS_STRUT_SIDE_TOP;
count++;
}
if (tmp_rect.x == rect.x)
{
strut_side |= OS_STRUT_SIDE_LEFT;
count++;
}
if (tmp_rect.x + tmp_rect.width == rect.x + rect.width)
{
strut_side |= OS_STRUT_SIDE_RIGHT;
count++;
}
if (tmp_rect.y + tmp_rect.height == rect.y + rect.height)
{
strut_side |= OS_STRUT_SIDE_BOTTOM;
count++;
}
/* Handle multiple sides. */
if (count >= 2)
{
if (tmp_rect.width > tmp_rect.height)
strut_side &= ~(OS_STRUT_SIDE_LEFT | OS_STRUT_SIDE_RIGHT);
else if (tmp_rect.width < tmp_rect.height)
strut_side &= ~(OS_STRUT_SIDE_TOP | OS_STRUT_SIDE_BOTTOM);
}
/* Get the monitor boundaries using the strut. */
if (strut_side & OS_STRUT_SIDE_TOP)
{
if (tmp_rect.y + tmp_rect.height > reg_y)
reg_y = tmp_rect.y + tmp_rect.height;
}
if (strut_side & OS_STRUT_SIDE_BOTTOM)
{
if (tmp_rect.y < reg_height)
reg_height = tmp_rect.y;
}
}
screen_y = reg_y;
screen_height = reg_height;
cairo_region_destroy (monitor_workarea);
cairo_region_destroy (struts_region);
}
if (priv->side == OS_SIDE_BOTTOM &&
(n_monitor != gdk_screen_get_monitor_at_point (screen, x, monitor_y + priv->thumb_all.height) ||
monitor_y + priv->thumb_all.height >= screen_height))
{
priv->state |= OS_STATE_INTERNAL;
return MAX (y - priv->thumb_all.height, screen_height - priv->thumb_all.height);
}
if (priv->side == OS_SIDE_TOP &&
(n_monitor != gdk_screen_get_monitor_at_point (screen, x, monitor_y - priv->thumb_all.height) ||
monitor_y - priv->thumb_all.height <= screen_y))
{
priv->state |= OS_STATE_INTERNAL;
return MAX (y, screen_y);
}
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
priv->state &= ~(OS_STATE_INTERNAL);
return y;
}
/* Move the thumb window. */
static void
move_thumb (GtkScrollbar *scrollbar,
gint x,
gint y)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
gtk_window_move (GTK_WINDOW (priv->thumb),
sanitize_x (scrollbar, x, y),
sanitize_y (scrollbar, x, y));
}
/* Callback called when the adjustment changes. */
static void
notify_adjustment_cb (GObject *object,
gpointer user_data)
{
GtkScrollbar *scrollbar;
scrollbar = GTK_SCROLLBAR (object);
swap_adjustment (scrollbar, gtk_range_get_adjustment (GTK_RANGE (object)));
}
/* Callback called when the orientation changes. */
static void
notify_orientation_cb (GObject *object,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (object);
priv = get_private (GTK_WIDGET (scrollbar));
priv->orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (object));
swap_thumb (scrollbar, os_thumb_new (priv->orientation));
}
/* Stop function called by the scrolling animation. */
static void
scrolling_stop_cb (gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
/* No slide values update here,
* handle them separately! */
/* Check if the thumb can be considered connected after the animation. */
check_connection (scrollbar);
/* Unset OS_STATE_RECONNECTING since the animation ended. */
priv->state &= ~(OS_STATE_RECONNECTING);
}
/* Swap adjustment pointer. */
static void
swap_adjustment (GtkScrollbar *scrollbar,
GtkAdjustment *adjustment)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->adjustment != NULL)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->adjustment),
G_CALLBACK (adjustment_changed_cb), scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->adjustment),
G_CALLBACK (adjustment_value_changed_cb), scrollbar);
g_object_unref (priv->adjustment);
}
priv->adjustment = adjustment;
if (priv->adjustment != NULL)
{
g_object_ref_sink (priv->adjustment);
g_signal_connect (G_OBJECT (priv->adjustment), "changed",
G_CALLBACK (adjustment_changed_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->adjustment), "value-changed",
G_CALLBACK (adjustment_value_changed_cb), scrollbar);
}
}
/* Swap thumb pointer. */
static void
swap_thumb (GtkScrollbar *scrollbar,
GtkWidget *thumb)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->thumb != NULL)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_button_press_event_cb, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_button_release_event_cb, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_enter_notify_event_cb, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_leave_notify_event_cb, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_map_cb, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_motion_notify_event_cb, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_scroll_event_cb, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->thumb),
thumb_unmap_cb, scrollbar);
gtk_widget_destroy (priv->thumb);
g_object_unref (priv->thumb);
}
priv->thumb = thumb;
if (priv->thumb != NULL)
{
g_object_ref_sink (priv->thumb);
g_signal_connect (G_OBJECT (priv->thumb), "button-press-event",
G_CALLBACK (thumb_button_press_event_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->thumb), "button-release-event",
G_CALLBACK (thumb_button_release_event_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->thumb), "enter-notify-event",
G_CALLBACK (thumb_enter_notify_event_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->thumb), "leave-notify-event",
G_CALLBACK (thumb_leave_notify_event_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->thumb), "map",
G_CALLBACK (thumb_map_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->thumb), "motion-notify-event",
G_CALLBACK (thumb_motion_notify_event_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->thumb), "scroll-event",
G_CALLBACK (thumb_scroll_event_cb), scrollbar);
g_signal_connect (G_OBJECT (priv->thumb), "unmap",
G_CALLBACK (thumb_unmap_cb), scrollbar);
}
}
/* Timeout before unlocking the thumb. */
static gboolean
unlock_thumb_cb (gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->hidable_thumb)
priv->state &= ~(OS_STATE_LOCKED);
priv->source_unlock_thumb_id = 0;
return FALSE;
}
/* Get the window at pointer. */
static GdkWindow*
window_at_pointer (GdkWindow *window,
gint *x,
gint *y)
{
return gdk_window_at_pointer (x, y);
}
/* Get the position of the pointer. */
static GdkWindow*
window_get_pointer (GdkWindow *window,
gint *x,
gint *y,
GdkModifierType *mask)
{
return gdk_window_get_pointer (window, x, y, mask);
}
/* Adjustment functions. */
/* Calculate fine_scroll_multiplier. */
static void
calc_fine_scroll_multiplier (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
/* FIXME(Cimi) Not sure about this calculation...
* However seems to work "enough" well. */
priv->fine_scroll_multiplier = MIN ((priv->orientation == GTK_ORIENTATION_VERTICAL ?
priv->trough.height : priv->trough.width) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment)),
1);
}
static void
adjustment_changed_cb (GtkAdjustment *adjustment,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
/* FIXME(Cimi) we should control each time os_bar_show ()/hide ()
* is called here and in map ()/unmap ().
* We are arbitrary calling that and I'm frightened we should show or keep
* hidden a bar that is meant to be hidden/shown.
* I don't want to see bars reappearing because
* of a change in the adjustment of an invisible bar or viceversa. */
if (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
gtk_adjustment_get_page_size (adjustment))
{
priv->state &= ~(OS_STATE_FULLSIZE);
if (priv->filter.proximity)
os_bar_show (priv->bar);
}
else
{
priv->state |= OS_STATE_FULLSIZE;
if (priv->filter.proximity)
{
os_bar_hide (priv->bar);
gtk_widget_hide (priv->thumb);
}
}
calc_layout_bar (scrollbar, gtk_adjustment_get_value (adjustment));
calc_layout_slider (scrollbar, gtk_adjustment_get_value (adjustment));
calc_fine_scroll_multiplier (scrollbar);
if (!(priv->event & OS_EVENT_ENTER_NOTIFY) &&
!(priv->event & OS_EVENT_MOTION_NOTIFY))
gtk_widget_hide (priv->thumb);
move_bar (scrollbar);
}
/* Update the tail (visual connection) between bar and thumb. */
static void
update_tail (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
gint x_pos, y_pos;
priv = get_private (GTK_WIDGET (scrollbar));
gdk_window_get_origin (gtk_widget_get_window (priv->thumb), &x_pos, &y_pos);
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
if (priv->thumb_win.y + priv->overlay.y >= y_pos + priv->slider.height)
{
GdkRectangle mask;
mask.x = 0;
mask.y = y_pos + priv->slider.height / 2 - priv->thumb_win.y;
mask.width = priv->bar_all.width;
mask.height = priv->overlay.y - mask.y;
os_bar_connect (priv->bar, mask);
priv->state |= OS_STATE_DETACHED;
os_bar_set_detached (priv->bar, TRUE, FALSE);
os_thumb_set_detached (OS_THUMB (priv->thumb), TRUE);
}
else if (priv->thumb_win.y + priv->overlay.y + priv->overlay.height <= y_pos)
{
GdkRectangle mask;
mask.x = 0;
mask.y = priv->overlay.y + priv->overlay.height;
mask.width = priv->bar_all.width;
mask.height = y_pos + priv->slider.height / 2 - priv->thumb_win.y - mask.y;
os_bar_connect (priv->bar, mask);
priv->state |= OS_STATE_DETACHED;
os_bar_set_detached (priv->bar, TRUE, FALSE);
os_thumb_set_detached (OS_THUMB (priv->thumb), TRUE);
}
else
{
priv->state &= ~(OS_STATE_DETACHED);
os_bar_set_detached (priv->bar, FALSE, FALSE);
os_thumb_set_detached (OS_THUMB (priv->thumb), FALSE);
}
}
else
{
if (priv->thumb_win.x + priv->overlay.x >= x_pos + priv->slider.width)
{
GdkRectangle mask;
mask.x = x_pos + priv->slider.width / 2 - priv->thumb_win.x;
mask.y = 0;
mask.width = priv->overlay.x - mask.x;
mask.height = priv->bar_all.height;
os_bar_connect (priv->bar, mask);
priv->state |= OS_STATE_DETACHED;
os_bar_set_detached (priv->bar, TRUE, FALSE);
os_thumb_set_detached (OS_THUMB (priv->thumb), TRUE);
}
else if (priv->thumb_win.x + priv->overlay.x + priv->overlay.width <= x_pos)
{
GdkRectangle mask;
mask.x = priv->overlay.x + priv->overlay.width;
mask.y = 0;
mask.width = x_pos + priv->slider.width / 2 - priv->thumb_win.x - mask.x;
mask.height = priv->bar_all.height;
os_bar_connect (priv->bar, mask);
priv->state |= OS_STATE_DETACHED;
os_bar_set_detached (priv->bar, TRUE, FALSE);
os_thumb_set_detached (OS_THUMB (priv->thumb), TRUE);
}
else
{
priv->state &= ~(OS_STATE_DETACHED);
os_bar_set_detached (priv->bar, FALSE, FALSE);
os_thumb_set_detached (OS_THUMB (priv->thumb), FALSE);
}
}
}
static void
adjustment_value_changed_cb (GtkAdjustment *adjustment,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
calc_layout_bar (scrollbar, gtk_adjustment_get_value (adjustment));
calc_layout_slider (scrollbar, gtk_adjustment_get_value (adjustment));
if (!(priv->event & OS_EVENT_ENTER_NOTIFY) &&
!(priv->event & OS_EVENT_MOTION_NOTIFY))
gtk_widget_hide (priv->thumb);
if (gtk_widget_get_mapped (priv->thumb) &&
!((priv->event & OS_EVENT_MOTION_NOTIFY) &&
(priv->state & OS_STATE_CONNECTED)))
update_tail (scrollbar);
move_bar (scrollbar);
}
/* Root window functions. */
/* Filter function applied to the root window. */
static GdkFilterReturn
root_filter_func (GdkXEvent *gdkxevent,
GdkEvent *event,
gpointer user_data)
{
XEvent* xev;
xev = gdkxevent;
if (xev->type == PropertyNotify)
{
if (xev->xproperty.atom == unity_net_workarea_region_atom)
{
calc_workarea (xev->xany.display, xev->xany.window);
}
}
return GDK_FILTER_CONTINUE;
}
/* Thumb functions. */
/* Present a X11 window. */
static void
present_window_with_timestamp (Screen *screen,
gint xid,
guint32 timestamp)
{
Display *display;
Window root;
XEvent xev;
if (timestamp == 0)
g_warning ("Received a timestamp of 0; window activation may not "
"function properly.\n");
display = DisplayOfScreen (screen);
root = RootWindowOfScreen (screen);
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = display;
xev.xclient.window = xid;
xev.xclient.message_type = gdk_x11_get_xatom_by_name ("_NET_ACTIVE_WINDOW");
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1;
xev.xclient.data.l[1] = timestamp;
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 0;
xev.xclient.data.l[4] = 0;
gdk_error_trap_push ();
XSendEvent (display, root, False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
gdk_flush ();
gdk_error_trap_pop ();
}
/* Present a Gdk window. */
static void
present_gdk_window_with_timestamp (GtkWidget *widget,
guint32 timestamp)
{
/* Check if the widget is realized, to protect against:
* https://bugs.launchpad.net/ayatana-scrollbar/+bug/846562 */
if (gtk_widget_get_realized (widget))
present_window_with_timestamp (GDK_SCREEN_XSCREEN (gtk_widget_get_screen (widget)),
GDK_WINDOW_XID (gtk_widget_get_window (widget)),
timestamp);
}
static gboolean
thumb_button_press_event_cb (GtkWidget *widget,
GdkEventButton *event,
gpointer user_data)
{
if (event->type == GDK_BUTTON_PRESS)
{
if (event->button == 1 ||
event->button == 2)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
gtk_window_set_transient_for (GTK_WINDOW (widget),
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (scrollbar))));
priv->present_time = g_get_monotonic_time ();
present_gdk_window_with_timestamp (GTK_WIDGET (scrollbar), event->time);
priv->event |= OS_EVENT_BUTTON_PRESS;
priv->event &= ~(OS_EVENT_MOTION_NOTIFY);
/* Middle-click or shift+left-click for "jump to" action. */
if (event->button == 2 ||
(event->button == 1 && (event->state & GDK_SHIFT_MASK)))
{
/* Reconnect the thumb with the bar. */
gdouble new_value;
gfloat c;
gint32 duration;
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
c = event->y_root - priv->thumb_win.y - event->y;
else
c = event->x_root - priv->thumb_win.x - event->x;
/* If a scrolling animation is running,
* stop it and add the new value. */
os_animation_stop (priv->animation, scrolling_stop_cb);
new_value = coord_to_value (scrollbar, c);
/* Only start the animation if needed. */
if (new_value != gtk_adjustment_get_value (priv->adjustment))
{
priv->state |= OS_STATE_RECONNECTING;
priv->value = new_value;
/* Calculate and set the duration. */
if (priv->value > gtk_adjustment_get_value (priv->adjustment))
duration = MIN_DURATION_SCROLLING + ((priv->value - gtk_adjustment_get_value (priv->adjustment)) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment))) *
(MAX_DURATION_SCROLLING - MIN_DURATION_SCROLLING);
else
duration = MIN_DURATION_SCROLLING + ((gtk_adjustment_get_value (priv->adjustment) - priv->value) /
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_lower (priv->adjustment))) *
(MAX_DURATION_SCROLLING - MIN_DURATION_SCROLLING);
os_animation_set_duration (priv->animation, duration);
/* Start the scrolling animation. */
os_animation_start (priv->animation);
}
}
calc_precise_slide_values (scrollbar, event->x_root, event->y_root);
priv->pointer.x = event->x;
priv->pointer.y = event->y;
}
}
return FALSE;
}
/* Scroll down, with animation. */
static void
scroll_down (GtkScrollbar *scrollbar,
OsScrollType scroll_type)
{
OsScrollbarPrivate *priv;
gdouble new_value, increment;
gint32 duration;
priv = get_private (GTK_WIDGET (scrollbar));
/* Either step down or page down. */
if (scroll_type == OS_SCROLL_STEP)
increment = gtk_adjustment_get_step_increment (priv->adjustment);
else
increment = gtk_adjustment_get_page_increment (priv->adjustment);
/* If a scrolling animation is running,
* stop it and add the new value. */
if (os_animation_is_running (priv->animation))
{
os_animation_stop (priv->animation, scrolling_stop_cb);
new_value = priv->value + increment;
}
else
new_value = gtk_adjustment_get_value (priv->adjustment) + increment;
priv->value = CLAMP (new_value,
gtk_adjustment_get_lower (priv->adjustment),
gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment));
/* There's no need to do start a new animation. */
if (priv->value == gtk_adjustment_get_value (priv->adjustment))
return;
/* Calculate and set the duration. */
if (scroll_type == OS_SCROLL_STEP)
duration = MIN_DURATION_SCROLLING;
else
duration = MIN_DURATION_SCROLLING + ((priv->value - gtk_adjustment_get_value (priv->adjustment)) / increment) *
(MAX_DURATION_SCROLLING - MIN_DURATION_SCROLLING);
os_animation_set_duration (priv->animation, duration);
/* Start the scrolling animation. */
os_animation_start (priv->animation);
}
/* Scroll up, with animation. */
static void
scroll_up (GtkScrollbar *scrollbar,
OsScrollType scroll_type)
{
OsScrollbarPrivate *priv;
gdouble new_value, increment;
gint32 duration;
priv = get_private (GTK_WIDGET (scrollbar));
/* Either step up or page up. */
if (scroll_type == OS_SCROLL_STEP)
increment = gtk_adjustment_get_step_increment (priv->adjustment);
else
increment = gtk_adjustment_get_page_increment (priv->adjustment);
/* If a scrolling animation is running,
* stop it and subtract the new value. */
if (os_animation_is_running (priv->animation))
{
os_animation_stop (priv->animation, scrolling_stop_cb);
new_value = priv->value - increment;
}
else
new_value = gtk_adjustment_get_value (priv->adjustment) - increment;
priv->value = CLAMP (new_value,
gtk_adjustment_get_lower (priv->adjustment),
gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment));
/* There's no need to do start a new animation. */
if (priv->value == gtk_adjustment_get_value (priv->adjustment))
return;
/* Calculate and set the duration. */
if (scroll_type == OS_SCROLL_STEP)
duration = MIN_DURATION_SCROLLING;
else
duration = MIN_DURATION_SCROLLING + ((gtk_adjustment_get_value (priv->adjustment) - priv->value) / increment) *
(MAX_DURATION_SCROLLING - MIN_DURATION_SCROLLING);
os_animation_set_duration (priv->animation, duration);
/* Start the scrolling animation. */
os_animation_start (priv->animation);
}
static gboolean
thumb_button_release_event_cb (GtkWidget *widget,
GdkEventButton *event,
gpointer user_data)
{
if (event->type == GDK_BUTTON_RELEASE)
{
if (event->button == 1 ||
event->button == 2)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
gtk_window_set_transient_for (GTK_WINDOW (widget), NULL);
/* Don't trigger actions on thumb dragging or jump-to scrolling. */
if (event->button == 1 &&
!(event->state & GDK_SHIFT_MASK) &&
!(priv->event & OS_EVENT_MOTION_NOTIFY))
{
OsScrollType scroll_type;
/* Type of the scroll to perform. */
if (event->state & MODIFIER_KEY)
scroll_type = OS_SCROLL_STEP;
else
scroll_type = OS_SCROLL_PAGE;
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
if (priv->pointer.y < priv->slider.height / 2)
scroll_up (scrollbar, scroll_type);
else
scroll_down (scrollbar, scroll_type);
}
else
{
if (priv->pointer.x < priv->slider.width / 2)
scroll_up (scrollbar, scroll_type);
else
scroll_down (scrollbar, scroll_type);
}
}
priv->event &= ~(OS_EVENT_BUTTON_PRESS | OS_EVENT_MOTION_NOTIFY);
}
}
return FALSE;
}
static void
enter_event (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
priv->event |= OS_EVENT_ENTER_NOTIFY;
priv->hidable_thumb = FALSE;
if (priv->state & OS_STATE_INTERNAL)
priv->state |= OS_STATE_LOCKED;
}
static gboolean
thumb_enter_notify_event_cb (GtkWidget *widget,
GdkEventCrossing *event,
gpointer user_data)
{
GtkScrollbar *scrollbar;
scrollbar = GTK_SCROLLBAR (user_data);
enter_event (scrollbar);
return FALSE;
}
static gboolean
thumb_leave_notify_event_cb (GtkWidget *widget,
GdkEventCrossing *event,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
/* When exiting the thumb horizontally (or vertically),
* in LOCKED state, remove the lock. */
if ((priv->state & OS_STATE_LOCKED) &&
((priv->side == OS_SIDE_RIGHT && event->x < 0) ||
(priv->side == OS_SIDE_BOTTOM && event->y < 0) ||
(priv->side == OS_SIDE_LEFT && event->x - priv->thumb_all.width >= 0) ||
(priv->side == OS_SIDE_TOP && event->y - priv->thumb_all.height >= 0)))
priv->state &= ~(OS_STATE_LOCKED);
/* Add the timeouts only if you are
* not interacting with the thumb. */
if (!(priv->event & OS_EVENT_BUTTON_PRESS))
{
priv->event &= ~(OS_EVENT_ENTER_NOTIFY);
priv->hidable_thumb = TRUE;
if (priv->source_hide_thumb_id != 0)
g_source_remove (priv->source_hide_thumb_id);
priv->source_hide_thumb_id = g_timeout_add (TIMEOUT_THUMB_HIDE,
hide_thumb_cb,
scrollbar);
}
return FALSE;
}
static void
thumb_map_cb (GtkWidget *widget,
gpointer user_data)
{
Display *display;
GtkScrollbar *scrollbar;
XWindowChanges changes;
guint32 xid, xid_parent;
unsigned int value_mask = CWSibling | CWStackMode;
int res;
scrollbar = GTK_SCROLLBAR (user_data);
xid = GDK_WINDOW_XID (gtk_widget_get_window (widget));
xid_parent = GDK_WINDOW_XID (gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (scrollbar))));
display = GDK_WINDOW_XDISPLAY (gtk_widget_get_window (GTK_WIDGET (scrollbar)));
changes.sibling = xid_parent;
changes.stack_mode = Above;
gdk_error_trap_push ();
XConfigureWindow (display, xid, value_mask, &changes);
gdk_flush ();
if ((res = gdk_error_trap_pop ()))
{
/* FIXME(Cimi) this code tries to restack the window using
* the _NET_RESTACK_WINDOW atom. See:
* http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html#id2506866
* Should work on window managers that supports this, like compiz.
* Unfortunately, metacity doesn't yet, so we might decide to implement
* this atom in metacity/mutter as well.
* We need to restack the window because the thumb window can be above
* every window, noticeable when you make the thumb of an unfocused window
* appear, and it could be above other windows (like the focused one).
* XConfigureWindow doesn't always work (well, should work only with
* compiz 0.8 or older), because it is not handling the reparenting done
* at the WM level (metacity and compiz >= 0.9 do reparenting). */
Window root = gdk_x11_get_default_root_xwindow ();
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.display = display;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = xid;
xev.xclient.message_type = gdk_x11_get_xatom_by_name ("_NET_RESTACK_WINDOW");
xev.xclient.format = 32;
xev.xclient.data.l[0] = 2;
xev.xclient.data.l[1] = xid_parent;
xev.xclient.data.l[2] = Above;
xev.xclient.data.l[3] = 0;
xev.xclient.data.l[4] = 0;
gdk_error_trap_push ();
XSendEvent (display, root, False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
gdk_flush ();
gdk_error_trap_pop ();
}
}
/* From pointer movement, set adjustment value. */
static void
capture_movement (GtkScrollbar *scrollbar,
gint mouse_x,
gint mouse_y)
{
OsScrollbarPrivate *priv;
gfloat c;
gint delta;
gdouble new_value;
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
delta = mouse_y - priv->slide_initial_coordinate;
else
delta = mouse_x - priv->slide_initial_coordinate;
/* With fine scroll, slow down the scroll. */
if (priv->state & OS_STATE_FINE_SCROLL)
c = priv->slide_initial_slider_position + delta * priv->fine_scroll_multiplier;
else
c = priv->slide_initial_slider_position + delta;
new_value = coord_to_value (scrollbar, c);
gtk_adjustment_set_value (priv->adjustment, new_value);
}
static GtkPaned*
find_gtk_paned_from_scrollbar(GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
GtkWidget* temp = GTK_WIDGET (scrollbar);
do
{
GtkPaned *paned = GTK_PANED (gtk_widget_get_ancestor (GTK_WIDGET (temp), GTK_TYPE_PANED));
if (!paned)
return FALSE;
if ((priv->side == OS_SIDE_RIGHT || priv->side == OS_SIDE_LEFT) &&
gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_HORIZONTAL)
{
temp = GTK_WIDGET (paned);
break;
}
else if ((priv->side == OS_SIDE_BOTTOM || priv->side == OS_SIDE_TOP) &&
gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_VERTICAL)
{
temp = GTK_WIDGET (paned);
break;
}
else
{
temp = gtk_widget_get_parent (GTK_WIDGET (paned));
}
} while(temp);
return GTK_PANED (temp);
}
static gboolean
thumb_motion_notify_event_cb (GtkWidget *widget,
GdkEventMotion *event,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
if (priv->event & OS_EVENT_BUTTON_PRESS)
{
gint x, y;
gint f_x, f_y;
f_x = abs (priv->pointer.x - event->x);
f_y = abs (priv->pointer.y - event->y);
/* Use tolerance at the first calls to this motion notify event. */
if (!(priv->event & OS_EVENT_MOTION_NOTIFY) &&
f_x <= TOLERANCE_MOTION &&
f_y <= TOLERANCE_MOTION)
return FALSE;
/* The scrollbar allows resize, and a scrolling is not started yet.
* Decide if the user will start a window resize or a normal scroll. */
if (priv->allow_resize &&
!(priv->event & OS_EVENT_MOTION_NOTIFY))
{
/* Trying to draw the area of interest,
* in the case of a vertical scrollbar.
* Everything is reflected for horizontal scrollbars.
*
* + +
* + SCROLLING +
* + +
* + + + +
* R + + + + + + + R
* E + + E
* S + no action + S
* I + taken + I
* Z + + Z
* E + + + + + + + E
* + + + +
* + +
* + SCROLLING +
* + +
*
* The diagonal lines are inclined differently, using 0.5 * f_y.
**/
if (((priv->side == OS_SIDE_RIGHT || priv->side == OS_SIDE_LEFT) && f_x > 0.5 * f_y) ||
((priv->side == OS_SIDE_BOTTOM || priv->side == OS_SIDE_TOP) && f_y > 0.5 * f_x))
{
/* We're either in the 'RESIZE' or in the 'no action taken' area. */
if (((priv->side == OS_SIDE_RIGHT || priv->side == OS_SIDE_LEFT) && f_x > TOLERANCE_DRAG) ||
((priv->side == OS_SIDE_BOTTOM || priv->side == OS_SIDE_TOP) && f_y > TOLERANCE_DRAG))
{
/* We're in the 'RESIZE' area.
* Set the right resize type and hide the thumb. */
GdkWindowEdge window_edge;
switch (priv->side)
{
case OS_SIDE_RIGHT:
window_edge = GDK_WINDOW_EDGE_EAST;
break;
case OS_SIDE_BOTTOM:
window_edge = GDK_WINDOW_EDGE_SOUTH;
break;
case OS_SIDE_LEFT:
window_edge = GDK_WINDOW_EDGE_WEST;
break;
case OS_SIDE_TOP:
window_edge = GDK_WINDOW_EDGE_NORTH;
break;
}
gdk_window_begin_resize_drag (gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (scrollbar))),
window_edge,
1,
event->x_root,
event->y_root,
event->time);
gtk_widget_hide (widget);
}
/* We're in the 'no action taken' area.
* Skip this event. */
if (!priv->allow_resize_paned)
return FALSE;
}
/* We're in the 'SCROLLING' area.
* Continue processing the event. */
}
if (priv->allow_resize_paned &&
!(priv->event & OS_EVENT_MOTION_NOTIFY))
{
if (((priv->side == OS_SIDE_RIGHT || priv->side == OS_SIDE_LEFT) && f_x > 0.5 * f_y) ||
((priv->side == OS_SIDE_BOTTOM || priv->side == OS_SIDE_TOP) && f_y > 0.5 * f_x))
{
/* We're either in the 'RESIZE' or in the 'no action taken' area. */
if (((priv->side == OS_SIDE_RIGHT || priv->side == OS_SIDE_LEFT) && f_x > TOLERANCE_DRAG) ||
((priv->side == OS_SIDE_BOTTOM || priv->side == OS_SIDE_TOP) && f_y > TOLERANCE_DRAG))
{
/* We're in the 'RESIZE' area. */
GtkPaned* paned = find_gtk_paned_from_scrollbar(scrollbar);
if (!paned)
return FALSE;
GdkWindow *handle_window = gtk_paned_get_handle_window (paned);
GdkEventButton *fwd_event = (GdkEventButton*) gdk_event_new (GDK_BUTTON_PRESS);
fwd_event->window = handle_window;
fwd_event->time = event->time;
fwd_event->x = 1;
fwd_event->y = 1;
fwd_event->state = event->state;
fwd_event->button = 1;
fwd_event->x_root = event->x_root;
fwd_event->y_root = event->y_root;
fwd_event->device = event->device;
gdk_event_put ((GdkEvent*) fwd_event);
priv->resizing_paned = TRUE;
gtk_widget_hide (widget);
}
/* We're in the 'no action taken' area.
* Skip this event. */
return FALSE;
}
}
if (!(priv->event & OS_EVENT_MOTION_NOTIFY))
{
/* Check if we can consider the thumb movement connected with the overlay. */
check_connection (scrollbar);
/* It is a scrolling event, set the flag. */
priv->event |= OS_EVENT_MOTION_NOTIFY;
}
/* Before stopping the animation,
* check if it's reconnecting.
* In this case we need to update the slide values
* with the current position. */
if (os_animation_is_running (priv->animation))
{
if (priv->state & OS_STATE_RECONNECTING)
{
/* It's a reconnecting animation. */
calc_precise_slide_values (scrollbar, event->x_root, event->y_root);
}
else
{
/* Stop the paging animation now. */
os_animation_stop (priv->animation, scrolling_stop_cb);
}
}
/* Check for modifier keys. */
if (event->state & MODIFIER_KEY)
{
/* You pressed the modifier key for the first time,
* let's deal with it. */
if (!(priv->state & OS_STATE_FINE_SCROLL))
{
calc_fine_scroll_multiplier (scrollbar);
calc_precise_slide_values (scrollbar, event->x_root, event->y_root);
priv->state |= OS_STATE_FINE_SCROLL;
}
priv->state &= ~(OS_STATE_CONNECTED);
}
else
{
/* You released the modifier key for the first time,
* let's deal with it. */
if (priv->state & OS_STATE_FINE_SCROLL)
{
/* Recalculate slider positions. */
calc_precise_slide_values (scrollbar, event->x_root, event->y_root);
priv->state &= ~(OS_STATE_FINE_SCROLL);
}
}
/* Behave differently when the thumb is connected or not. */
if (priv->state & OS_STATE_CONNECTED)
{
/* This is a connected scroll,
* the thumb movement is kept in sync with the overlay. */
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
if (priv->overlay.height > priv->slider.height)
{
/* Limit x and y within the overlay. */
x = priv->thumb_win.x;
y = CLAMP (event->y_root - priv->pointer.y,
priv->thumb_win.y + priv->overlay.y,
priv->thumb_win.y + priv->overlay.y + priv->overlay.height - priv->slider.height);
}
else
{
x = priv->thumb_win.x;
y = priv->thumb_win.y + priv->slider.y;
}
}
else
{
if (priv->overlay.width > priv->slider.width)
{
/* Limit x and y within the overlay. */
x = CLAMP (event->x_root - priv->pointer.x,
priv->thumb_win.x + priv->overlay.x,
priv->thumb_win.x + priv->overlay.x + priv->overlay.width - priv->slider.width);
y = priv->thumb_win.y;
}
else
{
x = priv->thumb_win.x + priv->slider.x;
y = priv->thumb_win.y;
}
}
/* There's no need to stop animations,
* since the reconnecting animation should not have
* state OS_STATE_CONNECTED, unless it's ended.
* Just capture the movement and change adjustment's value (scroll). */
capture_movement (scrollbar, event->x_root, event->y_root);
}
else
{
/* This is a disconnected scroll, works subtly different.
* It has to take care of reconnection,
* and scrolling is not allowed when hitting an edge. */
/* Limit x and y within the allocation. */
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
x = priv->thumb_win.x;
y = CLAMP (event->y_root - priv->pointer.y,
priv->thumb_win.y,
priv->thumb_win.y + priv->thumb_all.height - priv->slider.height);
}
else
{
x = CLAMP (event->x_root - priv->pointer.x,
priv->thumb_win.x,
priv->thumb_win.x + priv->thumb_all.width - priv->slider.width);
y = priv->thumb_win.y;
}
/* Disconnected scroll while detached,
* do not scroll when hitting an edge. */
if ((priv->orientation == GTK_ORIENTATION_VERTICAL &&
y > priv->thumb_win.y &&
y < priv->thumb_win.y + priv->thumb_all.height - priv->slider.height) ||
(priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
x > priv->thumb_win.x &&
x < priv->thumb_win.x + priv->thumb_all.width - priv->slider.width))
{
/* Stop the animation now.
* Only the reconnecting animation can be running now,
* because the paging animations were stop before. */
os_animation_stop (priv->animation, NULL);
/* Capture the movement and change adjustment's value (scroll). */
capture_movement (scrollbar, event->x_root, event->y_root);
}
else if (!os_animation_is_running (priv->animation) &&
!(priv->state & OS_STATE_DETACHED) &&
!(priv->state & OS_STATE_FINE_SCROLL))
{
/* Animate scrolling till reaches the edge. */
if ((priv->orientation == GTK_ORIENTATION_VERTICAL && y <= priv->thumb_win.y) ||
(priv->orientation == GTK_ORIENTATION_HORIZONTAL && x <= priv->thumb_win.x))
priv->value = gtk_adjustment_get_lower (priv->adjustment);
else
priv->value = gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment);
/* Proceed with the reconnection only if needed. */
if (priv->value != gtk_adjustment_get_value (priv->adjustment))
{
/* If the thumb is not detached, proceed with reconnection. */
priv->state |= OS_STATE_RECONNECTING;
os_animation_set_duration (priv->animation, MIN_DURATION_SCROLLING);
/* Start the scrolling animation. */
os_animation_start (priv->animation);
}
}
}
/* Move the thumb window. */
move_thumb (scrollbar, x, y);
/* Adjust slide values in some special situations,
* update the tail if the thumb is detached and
* check if the movement changed the thumb state to connected. */
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
if (gtk_adjustment_get_value (priv->adjustment) <= gtk_adjustment_get_lower (priv->adjustment))
{
if (priv->state & OS_STATE_DETACHED)
update_tail (scrollbar);
if (!(priv->state & OS_STATE_CONNECTED))
check_connection (scrollbar);
priv->slide_initial_slider_position = 0;
priv->slide_initial_coordinate = MAX (event->y_root, priv->thumb_win.y + priv->pointer.y);
}
else if (priv->overlay.y + priv->overlay.height >= priv->trough.height)
{
if (priv->state & OS_STATE_DETACHED)
update_tail (scrollbar);
if (!(priv->state & OS_STATE_CONNECTED))
check_connection (scrollbar);
priv->slide_initial_slider_position = priv->trough.height - MAX (priv->overlay.height, priv->slider.height);
priv->slide_initial_coordinate = MIN (event->y_root, (priv->thumb_win.y + priv->trough.height -
priv->slider.height + priv->pointer.y));
}
}
else
{
if (gtk_adjustment_get_value (priv->adjustment) <= gtk_adjustment_get_lower (priv->adjustment))
{
if (priv->state & OS_STATE_DETACHED)
update_tail (scrollbar);
if (!(priv->state & OS_STATE_CONNECTED))
check_connection (scrollbar);
priv->slide_initial_slider_position = 0;
priv->slide_initial_coordinate = MAX (event->x_root, priv->thumb_win.x + priv->pointer.x);
}
else if (priv->overlay.x + priv->overlay.width >= priv->trough.width)
{
if (priv->state & OS_STATE_DETACHED)
update_tail (scrollbar);
if (!(priv->state & OS_STATE_CONNECTED))
check_connection (scrollbar);
priv->slide_initial_slider_position = priv->trough.width - MAX (priv->overlay.width, priv->slider.width);
priv->slide_initial_coordinate = MIN (event->x_root, (priv->thumb_win.x + priv->trough.width -
priv->slider.width + priv->pointer.x));
}
}
}
return FALSE;
}
/* Mouse wheel delta. */
static gdouble
get_wheel_delta (GtkScrollbar *scrollbar,
GdkScrollDirection direction)
{
OsScrollbarPrivate *priv;
gdouble delta;
priv = get_private (GTK_WIDGET (scrollbar));
delta = pow (gtk_adjustment_get_page_size (priv->adjustment), 2.0 / 3.0);
if (direction == GDK_SCROLL_UP ||
direction == GDK_SCROLL_LEFT)
delta = - delta;
return delta;
}
static gboolean
thumb_scroll_event_cb (GtkWidget *widget,
GdkEventScroll *event,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
gdouble delta;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
/* Stop the scrolling animation if it's running. */
os_animation_stop (priv->animation, NULL);
/* Slow down scroll wheel with the modifier key pressed,
* by a 0.2 factor. */
if (event->state & MODIFIER_KEY)
delta = get_wheel_delta (scrollbar, event->direction) * 0.2;
else
delta = get_wheel_delta (scrollbar, event->direction);
gtk_adjustment_set_value (priv->adjustment,
CLAMP (gtk_adjustment_get_value (priv->adjustment) + delta,
gtk_adjustment_get_lower (priv->adjustment),
(gtk_adjustment_get_upper (priv->adjustment) -
gtk_adjustment_get_page_size (priv->adjustment))));
/* Deal with simultaneous events. */
if (priv->event & OS_EVENT_BUTTON_PRESS)
{
priv->event &= ~(OS_EVENT_MOTION_NOTIFY);
/* we need to update the slide values
* with the current position. */
calc_precise_slide_values (scrollbar, event->x_root, event->y_root);
}
return FALSE;
}
static void
thumb_unmap_cb (GtkWidget *widget,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
priv->event = OS_EVENT_NONE;
priv->hidable_thumb = TRUE;
priv->state &= OS_STATE_FULLSIZE;
/* Remove running hide timeout, if there is one. */
if (priv->source_hide_thumb_id != 0)
{
g_source_remove (priv->source_hide_thumb_id);
priv->source_hide_thumb_id = 0;
}
/* This could hardly still be running,
* but it is not impossible. */
if (priv->source_show_thumb_id != 0)
{
g_source_remove (priv->source_show_thumb_id);
priv->source_show_thumb_id = 0;
}
os_bar_set_detached (priv->bar, FALSE, TRUE);
}
/* Toplevel functions. */
static gboolean
toplevel_configure_event_cb (GtkWidget *widget,
GdkEventConfigure *event,
gpointer user_data)
{
GtkScrollbar *scrollbar = GTK_SCROLLBAR (user_data);
OsScrollbarPrivate *priv = get_private (GTK_WIDGET (scrollbar));
const gint64 current_time = g_get_monotonic_time ();
const gint64 end_time = priv->present_time + TIMEOUT_PRESENT_WINDOW * 1000;
if (current_time > end_time)
gtk_widget_hide (priv->thumb);
priv->state &= ~(OS_STATE_LOCKED);
calc_layout_bar (scrollbar, gtk_adjustment_get_value (priv->adjustment));
calc_layout_slider (scrollbar, gtk_adjustment_get_value (priv->adjustment));
return FALSE;
}
/* widget's window functions. */
/* Move the thumb in the proximity area. */
static void
adjust_thumb_position (GtkScrollbar *scrollbar,
gdouble event_x,
gdouble event_y)
{
OsScrollbarPrivate *priv;
gint x, y, x_pos, y_pos;
priv = get_private (GTK_WIDGET (scrollbar));
gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (scrollbar)), &x_pos, &y_pos);
if (priv->state & OS_STATE_LOCKED)
{
gint x_pos_t, y_pos_t;
gdk_window_get_origin (gtk_widget_get_window (priv->thumb), &x_pos_t, &y_pos_t);
/* If the pointer is moving in the area of the proximity
* at the left of the thumb (so, not vertically intercepting the thumb),
* unlock it. Viceversa for the horizontal orientation.
* The flag OS_STATE_LOCKED is set only for a mapped thumb,
* so we can freely ask for its position and do our calculations. */
if ((priv->side == OS_SIDE_RIGHT && x_pos + event_x <= x_pos_t) ||
(priv->side == OS_SIDE_BOTTOM && y_pos + event_y <= y_pos_t) ||
(priv->side == OS_SIDE_LEFT && x_pos + event_x >= x_pos_t + priv->thumb_all.width) ||
(priv->side == OS_SIDE_TOP && y_pos + event_y >= y_pos_t + priv->thumb_all.height))
priv->state &= ~(OS_STATE_LOCKED);
else
return;
}
/* Calculate priv->thumb_win.x and priv->thumb_win.y
* (thumb window allocation in root coordinates).
* I guess it's faster to store these values,
* instead calling everytime gdk_window_get_origin (),
* because it requires less calls than Gdk, which, in fact,
* loops through multiple functions to return them. */
priv->thumb_win.x = x_pos + priv->thumb_all.x;
priv->thumb_win.y = y_pos + priv->thumb_all.y;
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
x = priv->thumb_all.x;
if (priv->overlay.height > priv->slider.height)
{
/* Overlay (bar) is longer than the slider (thumb).
* The thumb is locked within the overlay,
* until the mouse is on the middle of page up or page down buttons. */
if (event_y < priv->thumb_all.y + priv->overlay.y + priv->slider.height / 4)
{
/* Align to page up. */
y = event_y - priv->slider.height / 4;
}
else if (event_y > priv->thumb_all.y + priv->overlay.y + priv->overlay.height - priv->slider.height / 4)
{
/* Align to page down. */
y = event_y - priv->slider.height * 3 / 4;
}
else
{
/* Lock within the overlay. */
y = CLAMP (event_y - priv->slider.height / 2,
priv->thumb_all.y + priv->overlay.y,
priv->thumb_all.y + priv->overlay.y + priv->overlay.height - priv->slider.height);
}
}
else
{
/* Slider (thumb) is longer than (or equal) the overlay (bar).
* The thumb is locked to its natural position (priv->slider.y),
* until the mouse is on the middle of page up or page down buttons. */
if (event_y < priv->thumb_all.y + priv->slider.y + priv->slider.height / 4)
{
/* Align to page up. */
y = event_y - priv->slider.height / 4;
}
else if (event_y > priv->thumb_all.y + priv->slider.y + priv->slider.height - priv->slider.height / 4)
{
/* Align to page down. */
y = event_y - priv->slider.height * 3 / 4;
}
else
{
/* Lock to its natural position. */
y = priv->thumb_all.y + priv->slider.y;
}
}
/* Restrict y within the thumb allocation. */
y = CLAMP (y, priv->thumb_all.y, priv->thumb_all.y + priv->thumb_all.height - priv->slider.height);
}
else
{
y = priv->thumb_all.y;
if (priv->overlay.width > priv->slider.width)
{
/* Overlay (bar) is longer than the slider (thumb).
* The thumb is locked within the overlay,
* until the mouse is on the middle of page up or page down buttons. */
if (event_x < priv->thumb_all.x + priv->overlay.x + priv->slider.width / 4)
{
/* Align to page up. */
x = event_x - priv->slider.width / 4;
}
else if (event_x > priv->thumb_all.x + priv->overlay.x + priv->overlay.width - priv->slider.width / 4)
{
/* Align to page down. */
x = event_x - priv->slider.width * 3 / 4;
}
else
{
/* Lock within the overlay. */
x = CLAMP (event_x - priv->slider.width / 2,
priv->thumb_all.x + priv->overlay.x,
priv->thumb_all.x + priv->overlay.x + priv->overlay.width - priv->slider.width);
}
}
else
{
/* Slider (thumb) is longer than (or equal) the overlay (bar).
* The thumb is locked to its natural position (priv->slider.x),
* until the mouse is on the middle of page up or page down buttons. */
if (event_x < priv->thumb_all.x + priv->slider.x + priv->slider.width / 4)
{
/* Align to page up. */
x = event_x - priv->slider.width / 4;
}
else if (event_x > priv->thumb_all.x + priv->slider.x + priv->slider.width - priv->slider.width / 4)
{
/* Align to page down. */
x = event_x - priv->slider.width * 3 / 4;
}
else
{
/* Lock to its natural position. */
x = priv->thumb_all.x + priv->slider.x;
}
}
/* Restrict x within the thumb allocation. */
x = CLAMP (x, priv->thumb_all.x, priv->thumb_all.x + priv->thumb_all.width - priv->slider.width);
}
move_thumb (scrollbar, x_pos + x, y_pos + y);
}
/* Checks if the pointer is in the proximity area. */
static gboolean
check_proximity (GtkScrollbar *scrollbar,
gint x,
gint y)
{
OsScrollbarPrivate *priv;
gint proximity_size;
priv = get_private (GTK_WIDGET (scrollbar));
proximity_size = PROXIMITY_SIZE;
/* If the thumb is internal, enlarge the proximity area. */
if (priv->state & OS_STATE_INTERNAL)
{
gint x_pos, y_pos;
gdk_window_get_origin (gtk_widget_get_window (priv->thumb), &x_pos, &y_pos);
/* This absolute value to add is obtained subtracting
* the real position of the thumb from the theoretical position.
* This difference should consist exactly in the amount of pixels (of the thumb)
* falling in the proximity, that is the value we want to enlarge this area. */
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
proximity_size += abs (priv->thumb_win.x - x_pos);
else
proximity_size += abs (priv->thumb_win.y - y_pos);
}
switch (priv->side)
{
case OS_SIDE_RIGHT:
return (x >= priv->bar_all.x + priv->bar_all.width - proximity_size &&
x <= priv->bar_all.x + priv->bar_all.width) &&
(y >= priv->bar_all.y &&
y <= priv->bar_all.y + priv->bar_all.height);
break;
case OS_SIDE_BOTTOM:
return (y >= priv->bar_all.y + priv->bar_all.height - proximity_size &&
y <= priv->bar_all.y + priv->bar_all.height) &&
(x >= priv->bar_all.x &&
x <= priv->bar_all.x + priv->bar_all.width);
break;
case OS_SIDE_LEFT:
return (x <= priv->bar_all.x + priv->bar_all.width + proximity_size &&
x >= priv->bar_all.x) &&
(y >= priv->bar_all.y &&
y <= priv->bar_all.y + priv->bar_all.height);
break;
case OS_SIDE_TOP:
return (y <= priv->bar_all.y + priv->bar_all.height + proximity_size &&
y >= priv->bar_all.y) &&
(x >= priv->bar_all.x &&
x <= priv->bar_all.x + priv->bar_all.width);
break;
default:
break;
}
return FALSE;
}
/* Returns TRUE if touch mode is enabled. */
static gboolean
is_touch_mode (GtkWidget *widget,
gint device_id)
{
return scrollbar_mode == SCROLLBAR_MODE_OVERLAY_TOUCH;
}
/* Callback that shows the thumb if it's the case. */
static gboolean
show_thumb_cb (gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
if (!priv->hidable_thumb)
{
gtk_widget_show (priv->thumb);
update_tail (scrollbar);
}
priv->source_show_thumb_id = 0;
return FALSE;
}
/* Adds a timeout to reveal the thumb. */
static void
show_thumb (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
/* Just update the tail if the thumb is already mapped. */
if (gtk_widget_get_mapped (priv->thumb))
{
update_tail (scrollbar);
return;
}
if (priv->state & OS_STATE_INTERNAL)
{
/* If the scrollbar is close to one edge of the screen,
* show it immediately, ignoring the timeout,
* to preserve Fitts' law. */
if (priv->source_show_thumb_id != 0)
{
g_source_remove (priv->source_show_thumb_id);
priv->source_show_thumb_id = 0;
}
gtk_widget_show (priv->thumb);
update_tail (scrollbar);
}
else if (priv->source_show_thumb_id == 0)
priv->source_show_thumb_id = g_timeout_add (TIMEOUT_THUMB_SHOW,
show_thumb_cb,
scrollbar);
}
/* Filter function applied to the toplevel window. */
typedef enum
{
OS_XEVENT_NONE,
OS_XEVENT_BUTTON_PRESS,
OS_XEVENT_BUTTON_RELEASE,
OS_XEVENT_LEAVE,
OS_XEVENT_MOTION
} OsXEvent;
static GdkFilterReturn
window_filter_func (GdkXEvent *gdkxevent,
GdkEvent *event,
gpointer user_data)
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
XEvent *xev;
g_return_val_if_fail (GTK_IS_SCROLLBAR (user_data), GDK_FILTER_CONTINUE);
scrollbar = GTK_SCROLLBAR (user_data);
priv = get_private (GTK_WIDGET (scrollbar));
xev = gdkxevent;
g_return_val_if_fail (OS_IS_BAR (priv->bar), GDK_FILTER_CONTINUE);
g_return_val_if_fail (OS_IS_THUMB (priv->thumb), GDK_FILTER_CONTINUE);
if (!(priv->state & OS_STATE_FULLSIZE))
{
OsXEvent os_xevent;
gdouble event_x, event_y;
gint sourceid;
os_xevent = OS_XEVENT_NONE;
event_x = 0;
event_y = 0;
sourceid = 0;
/* Deal with X core events, when apps (like rhythmbox),
* are using gdk_disable_miltidevice (). */
if (xev->type == ButtonPress)
os_xevent = OS_XEVENT_BUTTON_PRESS;
if (xev->type == ButtonRelease)
{
os_xevent = OS_XEVENT_BUTTON_RELEASE;
event_x = xev->xbutton.x;
event_y = xev->xbutton.y;
}
if (xev->type == LeaveNotify)
os_xevent = OS_XEVENT_LEAVE;
if (xev->type == MotionNotify)
{
os_xevent = OS_XEVENT_MOTION;
event_x = xev->xmotion.x;
event_y = xev->xmotion.y;
}
if (os_xevent == OS_XEVENT_BUTTON_PRESS)
{
priv->window_button_press = TRUE;
if (priv->source_show_thumb_id != 0)
{
g_source_remove (priv->source_show_thumb_id);
priv->source_show_thumb_id = 0;
}
gtk_widget_hide (priv->thumb);
}
if (priv->window_button_press && os_xevent == OS_XEVENT_BUTTON_RELEASE)
{
priv->window_button_press = FALSE;
/* Proximity area. */
if (check_proximity (scrollbar, event_x, event_y))
{
priv->hidable_thumb = FALSE;
adjust_thumb_position (scrollbar, event_x, event_y);
if (priv->state & OS_STATE_LOCKED)
return GDK_FILTER_CONTINUE;
if (!is_touch_mode (GTK_WIDGET (scrollbar), sourceid) && !priv->resizing_paned)
show_thumb (scrollbar);
}
}
if (os_xevent == OS_XEVENT_LEAVE)
{
priv->window_button_press = FALSE;
if (gtk_widget_get_mapped (priv->thumb) &&
!(priv->event & OS_EVENT_BUTTON_PRESS))
{
priv->hidable_thumb = TRUE;
if (priv->source_hide_thumb_id != 0)
g_source_remove (priv->source_hide_thumb_id);
priv->source_hide_thumb_id = g_timeout_add (TIMEOUT_TOPLEVEL_HIDE,
hide_thumb_cb,
scrollbar);
}
if (priv->source_show_thumb_id != 0)
{
g_source_remove (priv->source_show_thumb_id);
priv->source_show_thumb_id = 0;
}
if (priv->source_unlock_thumb_id != 0)
g_source_remove (priv->source_unlock_thumb_id);
priv->source_unlock_thumb_id = g_timeout_add (TIMEOUT_TOPLEVEL_HIDE,
unlock_thumb_cb,
scrollbar);
}
/* Get the motion_notify_event trough XEvent. */
if (!priv->window_button_press && os_xevent == OS_XEVENT_MOTION)
{
/* Proximity area. */
if (check_proximity (scrollbar, event_x, event_y))
{
priv->hidable_thumb = FALSE;
if (priv->source_hide_thumb_id != 0)
{
g_source_remove (priv->source_hide_thumb_id);
priv->source_hide_thumb_id = 0;
}
adjust_thumb_position (scrollbar, event_x, event_y);
if (priv->state & OS_STATE_LOCKED)
return GDK_FILTER_CONTINUE;
if (!is_touch_mode (GTK_WIDGET (scrollbar), sourceid) && !priv->resizing_paned)
show_thumb (scrollbar);
}
else
{
priv->state &= ~(OS_STATE_LOCKED);
if (priv->source_show_thumb_id != 0)
{
g_source_remove (priv->source_show_thumb_id);
priv->source_show_thumb_id = 0;
}
if (gtk_widget_get_mapped (priv->thumb) &&
!(priv->event & OS_EVENT_BUTTON_PRESS))
{
priv->hidable_thumb = TRUE;
if (priv->source_hide_thumb_id == 0)
priv->source_hide_thumb_id = g_timeout_add (TIMEOUT_PROXIMITY_HIDE,
hide_thumb_cb,
scrollbar);
}
}
}
}
return GDK_FILTER_CONTINUE;
}
/* Add the window filter function. */
static void
add_window_filter (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
/* Don't add duplicated filters. */
if (!priv->filter.running &&
gtk_widget_get_realized (GTK_WIDGET (scrollbar)))
{
priv->filter.running = TRUE;
gdk_window_add_filter (gtk_widget_get_window (GTK_WIDGET (scrollbar)),
window_filter_func,
scrollbar);
}
}
/* Remove the window filter function. */
static void
remove_window_filter (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
/* Remove only if the filter is running. */
if (priv->filter.running &&
gtk_widget_get_realized (GTK_WIDGET (scrollbar)))
{
priv->filter.running = FALSE;
gdk_window_remove_filter (gtk_widget_get_window (GTK_WIDGET (scrollbar)),
window_filter_func,
scrollbar);
}
}
static gboolean
use_overlay_scrollbar (void)
{
return scrollbar_mode != SCROLLBAR_MODE_NORMAL && ubuntu_gtk_get_use_overlay_scrollbar ();
}
static void
hijacked_scrollbar_dispose (GObject *object)
{
if (use_overlay_scrollbar ())
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (object);
os_root_list = g_slist_remove (os_root_list, scrollbar);
if (os_root_list == NULL)
{
if (os_workarea != NULL)
{
cairo_region_destroy (os_workarea);
os_workarea = NULL;
}
gdk_window_remove_filter (gdk_get_default_root_window (),
root_filter_func, NULL);
}
priv = lookup_private (GTK_WIDGET(scrollbar));
if (priv != NULL)
{
if (priv->source_deactivate_bar_id != 0)
{
g_source_remove (priv->source_deactivate_bar_id);
priv->source_deactivate_bar_id = 0;
}
if (priv->source_hide_thumb_id != 0)
{
g_source_remove (priv->source_hide_thumb_id);
priv->source_hide_thumb_id = 0;
}
if (priv->source_show_thumb_id != 0)
{
g_source_remove (priv->source_show_thumb_id);
priv->source_show_thumb_id = 0;
}
if (priv->source_unlock_thumb_id != 0)
{
g_source_remove (priv->source_unlock_thumb_id);
priv->source_unlock_thumb_id = 0;
}
if (priv->animation != NULL)
{
g_object_unref (priv->animation);
priv->animation = NULL;
}
if (priv->bar != NULL)
{
g_object_unref (priv->bar);
priv->bar = NULL;
}
if (priv->window_group != NULL)
{
g_object_unref (priv->window_group);
priv->window_group = NULL;
}
swap_adjustment (scrollbar, NULL);
swap_thumb (scrollbar, NULL);
}
}
(* pre_hijacked_scrollbar_dispose) (object);
}
static gboolean
hijacked_scrollbar_expose_event (GtkWidget *widget,
GdkEventExpose *event)
{
if (use_overlay_scrollbar ())
return TRUE;
return (* pre_hijacked_scrollbar_expose_event) (widget, event);
}
static void
hijacked_scrollbar_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
if (use_overlay_scrollbar ())
return;
(* pre_hijacked_scrollbar_grab_notify) (widget, was_grabbed);
}
static void
hijacked_scrollbar_hide (GtkWidget *widget)
{
if (use_overlay_scrollbar ())
{
(* widget_class_hide) (widget);
return;
}
(* pre_hijacked_scrollbar_hide) (widget);
}
static void
hijacked_scrollbar_map (GtkWidget *widget)
{
if (use_overlay_scrollbar ())
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (widget);
priv = get_private (widget);
(* widget_class_map) (widget);
if (!(priv->state & OS_STATE_FULLSIZE))
os_bar_show (priv->bar);
if (!is_insensitive (scrollbar))
{
priv->filter.proximity = TRUE;
add_window_filter (scrollbar);
}
return;
}
(* pre_hijacked_scrollbar_map) (widget);
}
static void
hijacked_scrollbar_realize (GtkWidget *widget)
{
scrollbar_list = g_slist_prepend (scrollbar_list, widget);
if (use_overlay_scrollbar ())
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (widget);
priv = get_private (widget);
(* widget_class_realize) (widget);
gtk_window_group_add_window (priv->window_group, GTK_WINDOW (gtk_widget_get_toplevel (widget)));
gdk_window_set_events (gtk_widget_get_window (widget),
gdk_window_get_events (gtk_widget_get_window (widget)) |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK);
if (priv->filter.proximity)
add_window_filter (scrollbar);
g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)), "configure-event",
G_CALLBACK (toplevel_configure_event_cb), scrollbar);
calc_layout_bar (scrollbar, gtk_adjustment_get_value (priv->adjustment));
os_bar_set_parent (priv->bar, widget);
return;
}
(* pre_hijacked_scrollbar_realize) (widget);
}
static void
hijacked_scrollbar_show (GtkWidget *widget)
{
if (use_overlay_scrollbar ())
{
(* widget_class_show) (widget);
return;
}
(* pre_hijacked_scrollbar_show) (widget);
}
/* Retrieve the side of the scrollbar. */
static void
retrieve_side (GtkScrollbar *scrollbar)
{
GtkCornerType corner;
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
corner = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (scrollbar), os_quark_placement));
if (GTK_IS_SCROLLED_WINDOW (gtk_widget_get_parent (GTK_WIDGET (scrollbar))))
corner = gtk_scrolled_window_get_placement (GTK_SCROLLED_WINDOW (gtk_widget_get_parent (GTK_WIDGET (scrollbar))));
/* GtkCornerType to OsSide. */
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
if (corner == GTK_CORNER_TOP_LEFT ||
corner == GTK_CORNER_TOP_RIGHT)
priv->side = OS_SIDE_BOTTOM;
else
priv->side = OS_SIDE_TOP;
}
else
{
if (gtk_widget_get_direction (GTK_WIDGET (scrollbar)) == GTK_TEXT_DIR_LTR)
{
if (corner == GTK_CORNER_TOP_LEFT ||
corner == GTK_CORNER_BOTTOM_LEFT)
priv->side = OS_SIDE_RIGHT;
else
priv->side = OS_SIDE_LEFT;
}
else
{
if (corner == GTK_CORNER_TOP_RIGHT ||
corner == GTK_CORNER_BOTTOM_RIGHT)
priv->side = OS_SIDE_RIGHT;
else
priv->side = OS_SIDE_LEFT;
}
}
}
/* Retrieve if the thumb can resize its toplevel window. */
static void
retrieve_resizability (GtkScrollbar *scrollbar)
{
GdkWindow *scrollbar_window;
GdkWindow *toplevel_window;
OsScrollbarPrivate *priv;
gint x, y;
gint x_pos, y_pos;
priv = get_private (GTK_WIDGET (scrollbar));
/* By default, they don't allow resize. */
priv->allow_resize = FALSE;
scrollbar_window = gtk_widget_get_window (GTK_WIDGET (scrollbar));
if (!scrollbar_window)
return;
toplevel_window = gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (scrollbar)));
gdk_window_get_origin (toplevel_window, &x, &y);
gdk_window_get_root_coords (scrollbar_window,
priv->thumb_all.x, priv->thumb_all.y,
&x_pos, &y_pos);
/* Check if the thumb is next to a window edge,
* if that's the case, set the allow_resize gboolean. */
switch (priv->side)
{
case OS_SIDE_RIGHT:
if (x + gdk_window_get_width (toplevel_window) - x_pos <= THUMB_WIDTH)
priv->allow_resize = TRUE;
break;
case OS_SIDE_BOTTOM:
if (y + gdk_window_get_height (toplevel_window) - y_pos <= THUMB_WIDTH)
priv->allow_resize = TRUE;
break;
case OS_SIDE_LEFT:
if (x_pos - x <= THUMB_WIDTH)
priv->allow_resize = TRUE;
break;
case OS_SIDE_TOP:
if (y_pos - y <= THUMB_WIDTH)
priv->allow_resize = TRUE;
break;
default:
break;
}
if (priv->allow_resize)
return;
GtkPaned* paned = find_gtk_paned_from_scrollbar(scrollbar);
if (!paned)
return;
GdkWindow *handle_window = gtk_paned_get_handle_window (paned);
gdk_window_get_origin (handle_window, &x, &y);
priv->allow_resize_paned = FALSE;
priv->resizing_paned = FALSE;
/* Check if the thumb is next to a paned handle,
* if that's the case, set the allow_resize_paned gboolean. */
switch (priv->side)
{
case OS_SIDE_RIGHT:
if (x + gdk_window_get_width (handle_window) - x_pos <= THUMB_WIDTH)
priv->allow_resize_paned = TRUE;
break;
case OS_SIDE_LEFT:
if (x_pos - x <= THUMB_WIDTH)
priv->allow_resize_paned = TRUE;
break;
case OS_SIDE_BOTTOM:
if (y + gdk_window_get_height (handle_window) - y_pos <= THUMB_WIDTH)
priv->allow_resize_paned = TRUE;
break;
case OS_SIDE_TOP:
if (y_pos - y <= THUMB_WIDTH)
priv->allow_resize_paned = TRUE;
break;
default:
break;
}
}
static void
hijacked_scrollbar_size_allocate (GtkWidget *widget,
GdkRectangle *allocation)
{
if (use_overlay_scrollbar ())
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (widget);
priv = get_private (widget);
/* Get the side, then move thumb and bar accordingly. */
retrieve_side (scrollbar);
priv->trough.x = allocation->x;
priv->trough.y = allocation->y;
priv->trough.width = allocation->width;
priv->trough.height = allocation->height;
priv->bar_all = *allocation;
priv->thumb_all = *allocation;
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
{
priv->slider.width = THUMB_WIDTH;
if (priv->slider.height != MIN (THUMB_HEIGHT, allocation->height))
{
priv->slider.height = MIN (THUMB_HEIGHT, allocation->height);
os_thumb_resize (OS_THUMB (priv->thumb), priv->slider.width, priv->slider.height);
}
if (priv->side == OS_SIDE_RIGHT)
priv->bar_all.x = allocation->x - BAR_SIZE;
priv->bar_all.width = BAR_SIZE;
priv->thumb_all.width = THUMB_WIDTH;
if (priv->side == OS_SIDE_RIGHT)
priv->thumb_all.x = allocation->x - priv->bar_all.width;
else
priv->thumb_all.x = allocation->x + priv->bar_all.width - priv->thumb_all.width;
allocation->width = 0;
}
else
{
priv->slider.height = THUMB_WIDTH;
if (priv->slider.width != MIN (THUMB_HEIGHT, allocation->width))
{
priv->slider.width = MIN (THUMB_HEIGHT, allocation->width);
os_thumb_resize (OS_THUMB (priv->thumb), priv->slider.width, priv->slider.height);
}
if (priv->side == OS_SIDE_BOTTOM)
priv->bar_all.y = allocation->y - BAR_SIZE;
priv->bar_all.height = BAR_SIZE;
priv->thumb_all.height = THUMB_WIDTH;
if (priv->side == OS_SIDE_BOTTOM)
priv->thumb_all.y = allocation->y - priv->bar_all.height;
else
priv->thumb_all.y = allocation->y + priv->bar_all.height - priv->thumb_all.height;
allocation->height = 0;
}
if (priv->adjustment != NULL)
{
calc_layout_bar (scrollbar, gtk_adjustment_get_value (priv->adjustment));
calc_layout_slider (scrollbar, gtk_adjustment_get_value (priv->adjustment));
}
os_bar_size_allocate (priv->bar, priv->bar_all);
move_bar (scrollbar);
/* Set resizability. */
retrieve_resizability (scrollbar);
gtk_widget_set_allocation (widget, allocation);
return;
}
(* pre_hijacked_scrollbar_size_allocate) (widget, allocation);
}
/* Set the scrollbar to be insensitive. */
static void
set_insensitive (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
priv->filter.proximity = FALSE;
remove_window_filter (scrollbar);
gtk_widget_hide (priv->thumb);
}
/* Set the scrollbar to be sensitive. */
static void
set_sensitive (GtkScrollbar *scrollbar)
{
OsScrollbarPrivate *priv;
priv = get_private (GTK_WIDGET (scrollbar));
/* Only add the filter if the scrollbar is mapped.
* It makes sense to me, if we are missing proximity areas,
* it might worth having a look here. */
if (gtk_widget_get_mapped (GTK_WIDGET (scrollbar)))
{
priv->filter.proximity = TRUE;
add_window_filter (scrollbar);
}
}
static void
hijacked_scrollbar_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
if (use_overlay_scrollbar ())
{
OsScrollbarPrivate *priv;
priv = get_private (widget);
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
requisition->width = 0;
else
requisition->height = 0;
widget->requisition = *requisition;
return;
}
(* pre_hijacked_scrollbar_size_request) (widget, requisition);
}
static void
hijacked_scrollbar_state_changed (GtkWidget *widget,
GtkStateType state)
{
if (use_overlay_scrollbar ())
{
GtkScrollbar *scrollbar;
scrollbar = GTK_SCROLLBAR (widget);
if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
set_insensitive (scrollbar);
else
set_sensitive (scrollbar);
return;
}
(* pre_hijacked_scrollbar_state_changed) (widget, state);
}
static void
hijacked_scrollbar_unmap (GtkWidget *widget)
{
if (use_overlay_scrollbar ())
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (widget);
priv = get_private (widget);
(* widget_class_unmap) (widget);
os_bar_hide (priv->bar);
gtk_widget_hide (priv->thumb);
priv->filter.proximity = FALSE;
remove_window_filter (scrollbar);
return;
}
(* pre_hijacked_scrollbar_unmap) (widget);
}
static void
hijacked_scrollbar_unrealize (GtkWidget *widget)
{
scrollbar_list = g_slist_remove (scrollbar_list, widget);
if (use_overlay_scrollbar ())
{
GtkScrollbar *scrollbar;
OsScrollbarPrivate *priv;
scrollbar = GTK_SCROLLBAR (widget);
priv = get_private (widget);
os_bar_hide (priv->bar);
/* There could be a race where the window is unrealized while
* the pointer just reached the proximity area and started the timeout,
* protect against it. */
if (priv->source_show_thumb_id != 0)
{
g_source_remove (priv->source_show_thumb_id);
priv->source_show_thumb_id = 0;
}
gtk_widget_hide (priv->thumb);
priv->filter.running = FALSE;
gdk_window_remove_filter (gtk_widget_get_window (widget), window_filter_func, scrollbar);
g_signal_handlers_disconnect_by_func (G_OBJECT (gtk_widget_get_toplevel (widget)),
G_CALLBACK (toplevel_configure_event_cb), scrollbar);
os_bar_set_parent (priv->bar, NULL);
(* widget_class_unrealize) (widget);
return;
}
(* pre_hijacked_scrollbar_unrealize) (widget);
}
/* Check if the application is blacklisted. */
static gboolean
app_is_blacklisted (void)
{
/* Black-list of program names retrieved with g_get_prgname (). */
static const gchar *blacklist[] = {
"acroread", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/876218 */
"eclipse", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/769277 */
"gnome-system-monitor", /* https://bugs.launchpad.net/overlay-scrollbar/+bug/1415964 */
"gnucash", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/770304 */
"gvim", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/847943 */
"lnotes", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/890986 */
"soffice", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/847918 */
"synaptic", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/755238 */
"vinagre", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/847932 */
"vmplayer", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/770625 */
"vmware", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/770625 */
"gnome-boxes"
};
static const gchar *blacklist_prefix[] = {
"emacs", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/847940 */
"firefox", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/847922 */
"gimp", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/803163 */
"notes", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/890986 */
"thunderbird", /* https://bugs.launchpad.net/ayatana-scrollbar/+bug/847929 */
};
GModule *module;
gpointer func;
gint32 i;
const gint32 nr_programs = G_N_ELEMENTS (blacklist);
const gint32 nr_programs_prefix = G_N_ELEMENTS (blacklist_prefix);
const gchar *prgname;
const gchar *flag;
prgname = g_get_prgname ();
flag = g_getenv ("LIBOVERLAY_SCROLLBAR");
/* Check LIBOVERLAY_SCROLLBAR. */
if (flag != NULL)
{
/* Blacklist if is set to 0. */
if (*flag == '\0' || *flag == '0')
return TRUE;
/* Special mode to override the blacklist. */
if (g_strcmp0 (flag, "override-blacklist") == 0)
return FALSE;
}
/* Black-list of symbols. */
module = g_module_open (NULL, 0);
/* https://bugs.launchpad.net/ayatana-scrollbar/+bug/847966 */
if (g_module_symbol (module, "qt_startup_hook", &func))
{
g_module_close (module);
return TRUE;
}
g_module_close (module);
/* Black-list of program names. */
for (i = 0; i < nr_programs; i++)
if (g_strcmp0 (blacklist[i], prgname) == 0)
return TRUE;
for (i = 0; i < nr_programs_prefix; i++)
if (g_str_has_prefix (prgname, blacklist_prefix[i]))
return TRUE;
return FALSE;
}
/* Patch GtkScrollbar vtable. */
static void
patch_scrollbar_class_vtable (GType type)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
GType *children;
guint n, i;
/* Patch vtable, assigning new pointers to vtable functions. */
object_class = g_type_class_ref (type);
widget_class = g_type_class_ref (type);
if (object_class->dispose == pre_hijacked_scrollbar_dispose)
object_class->dispose = hijacked_scrollbar_dispose;
if (widget_class->expose_event == pre_hijacked_scrollbar_expose_event)
widget_class->expose_event = hijacked_scrollbar_expose_event;
if (widget_class->size_request == pre_hijacked_scrollbar_size_request)
widget_class->size_request = hijacked_scrollbar_size_request;
if (widget_class->state_changed == pre_hijacked_scrollbar_state_changed)
widget_class->state_changed = hijacked_scrollbar_state_changed;
if (pre_hijacked_scrollbar_grab_notify &&
widget_class->grab_notify == pre_hijacked_scrollbar_grab_notify)
widget_class->grab_notify = hijacked_scrollbar_grab_notify;
if (widget_class->hide == pre_hijacked_scrollbar_hide)
widget_class->hide = hijacked_scrollbar_hide;
if (widget_class->map == pre_hijacked_scrollbar_map)
widget_class->map = hijacked_scrollbar_map;
if (widget_class->realize == pre_hijacked_scrollbar_realize)
widget_class->realize = hijacked_scrollbar_realize;
if (widget_class->show == pre_hijacked_scrollbar_show)
widget_class->show = hijacked_scrollbar_show;
if (widget_class->size_allocate == pre_hijacked_scrollbar_size_allocate)
widget_class->size_allocate = hijacked_scrollbar_size_allocate;
if (widget_class->unmap == pre_hijacked_scrollbar_unmap)
widget_class->unmap = hijacked_scrollbar_unmap;
if (widget_class->unrealize == pre_hijacked_scrollbar_unrealize)
widget_class->unrealize = hijacked_scrollbar_unrealize;
/* Recurse GType children. */
children = g_type_children (type, &n);
for (i = 0; i < n; i++)
patch_scrollbar_class_vtable (children[i]);
g_free (children);
}
/* Load custom style for overlay scrollbar. */
static void
custom_style_load (void)
{
gtk_rc_parse_string ("style \"overlay-scrollbar\" {\n"
" GtkScrolledWindow::scrollbar-spacing = 0\n"
" GtkScrolledWindow::scrollbars-within-bevel = 1\n"
" }\n"
"\n"
"class \"GtkScrolledWindow\" style \"overlay-scrollbar\"");
}
/* Unload all scrollbars. */
static void
scrollbar_mode_changed_unload_gfunc (gpointer data,
gpointer user_data)
{
GtkWidget *widget;
GSList **mapped_list;
widget = GTK_WIDGET (data);
mapped_list = user_data;
/* The following unrealize will unmap the widget:
* create a list of mapped scrollbars to remap them afterwards. */
if (gtk_widget_get_mapped (widget))
{
*mapped_list = g_slist_prepend (*mapped_list, widget);
gtk_widget_hide (widget);
}
gtk_widget_unrealize (widget);
}
/* Load all scrollbars. */
static void
scrollbar_mode_changed_load_gfunc (gpointer data,
gpointer user_data)
{
gtk_widget_realize (GTK_WIDGET (data));
}
/* Complete load of all scrollbars. */
static void
scrollbar_mode_changed_load_end_gfunc (gpointer data,
gpointer user_data)
{
GtkWidget *widget;
widget = GTK_WIDGET (data);
/* Remap the list of scrollbars that were unmapped by the unload gfunc.
* Request a resize to update widget allocation. */
gtk_widget_show (widget);
gtk_widget_queue_resize (widget);
}
/* Callback called when scrollbar-mode changes. */
static void
scrollbar_mode_changed_cb (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
GSettings *settings;
GSList *tmp_list, *mapped_list;
settings = G_SETTINGS (object);
tmp_list = g_slist_copy (scrollbar_list);
/* Initialize the pointer by initializing its variable. */
mapped_list = NULL;
/* Unload all scrollbars, using previous scrollbar_mode. */
g_slist_foreach (tmp_list, scrollbar_mode_changed_unload_gfunc, &mapped_list);
/* Update the scrollbar_mode variable. */
scrollbar_mode = g_settings_get_enum (settings, "scrollbar-mode");
/* Gtk+ 2.0 doesn't support dynamic loading of styles.
* Please contact me in case I'm wrong,
* and I'll add the required bits here. */
/* Load all scrollbars, using new scrollbar_mode. */
g_slist_foreach (tmp_list, scrollbar_mode_changed_load_gfunc, NULL);
/* Map the scrollbars that were unmapped by unload. */
g_slist_foreach (mapped_list, scrollbar_mode_changed_load_end_gfunc, NULL);
g_slist_free (mapped_list);
g_slist_free (tmp_list);
}
/* Suppress the warning 'missing-declarations'. */
void gtk_module_init (void);
void
gtk_module_init (void)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
GSettings *settings;
/* Exit if the application is blacklisted. */
if (app_is_blacklisted ())
return;
/* Initialize static variables. */
net_active_window_atom = gdk_x11_get_xatom_by_name ("_NET_ACTIVE_WINDOW");
unity_net_workarea_region_atom = gdk_x11_get_xatom_by_name ("_UNITY_NET_WORKAREA_REGION");
os_quark_placement = g_quark_from_static_string ("os_quark_placement");
os_quark_qdata = g_quark_from_static_string ("os-scrollbar");
/* Store GtkScrollbar vfunc pointers. */
object_class = g_type_class_ref (GTK_TYPE_SCROLLBAR);
widget_class = g_type_class_ref (GTK_TYPE_SCROLLBAR);
pre_hijacked_scrollbar_dispose = object_class->dispose;
pre_hijacked_scrollbar_expose_event = widget_class->expose_event;
pre_hijacked_scrollbar_size_request = widget_class->size_request;
pre_hijacked_scrollbar_state_changed = widget_class->state_changed;
pre_hijacked_scrollbar_grab_notify = widget_class->grab_notify;
pre_hijacked_scrollbar_hide = widget_class->hide;
pre_hijacked_scrollbar_map = widget_class->map;
pre_hijacked_scrollbar_realize = widget_class->realize;
pre_hijacked_scrollbar_show = widget_class->show;
pre_hijacked_scrollbar_size_allocate = widget_class->size_allocate;
pre_hijacked_scrollbar_unmap = widget_class->unmap;
pre_hijacked_scrollbar_unrealize = widget_class->unrealize;
/* Store GtkWidget vfunc pointers. */
widget_class = g_type_class_ref (GTK_TYPE_WIDGET);
widget_class_hide = widget_class->hide;
widget_class_map = widget_class->map;
widget_class_realize = widget_class->realize;
widget_class_show = widget_class->show;
widget_class_unmap = widget_class->unmap;
widget_class_unrealize = widget_class->unrealize;
/* Patch GtkScrollbar vtable. */
patch_scrollbar_class_vtable (GTK_TYPE_SCROLLBAR);
/* Connect to gsettings. */
settings = g_settings_new ("com.canonical.desktop.interface");
g_signal_connect (settings, "changed::scrollbar-mode",
G_CALLBACK (scrollbar_mode_changed_cb), NULL);
scrollbar_mode = g_settings_get_enum (settings, "scrollbar-mode");
ubuntu_gtk_set_use_overlay_scrollbar (TRUE);
/* Load custom overlay scrollbar style. */
if (use_overlay_scrollbar ())
custom_style_load ();
}
overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-animation.c 0000644 0000153 0000161 00000013725 12622657062 023140 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
* Loïc Molinari
*/
#ifndef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "os-private.h"
struct _OsAnimationPrivate {
OsAnimationEndFunc end_func;
OsAnimationUpdateFunc update_func;
gint64 start_time;
gint64 duration;
gint32 rate;
gpointer user_data;
guint32 source_id;
};
static void os_animation_dispose (GObject *object);
static void os_animation_finalize (GObject *object);
G_DEFINE_TYPE (OsAnimation, os_animation, G_TYPE_OBJECT);
static void
os_animation_class_init (OsAnimationClass *class)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (class);
gobject_class->dispose = os_animation_dispose;
gobject_class->finalize = os_animation_finalize;
g_type_class_add_private (gobject_class, sizeof (OsAnimationPrivate));
}
static void
os_animation_init (OsAnimation *animation)
{
animation->priv = G_TYPE_INSTANCE_GET_PRIVATE (animation,
OS_TYPE_ANIMATION,
OsAnimationPrivate);
}
static void
os_animation_dispose (GObject *object)
{
OsAnimation *animation;
OsAnimationPrivate *priv;
animation = OS_ANIMATION (object);
priv = animation->priv;
if (priv->source_id != 0)
{
g_source_remove (priv->source_id);
priv->source_id = 0;
}
G_OBJECT_CLASS (os_animation_parent_class)->dispose (object);
}
static void
os_animation_finalize (GObject *object)
{
G_OBJECT_CLASS (os_animation_parent_class)->finalize (object);
}
/* Public functions. */
/**
* os_animation_new:
* @rate: rate of the update
* @duration: duration of the animation
* @update_func: function to call on update
* @end_func: function to call at the end
* @user_data: pointer to the user data
*
* Creates a new OsAnimation
*
* Returns: the pointer to the #OsAnimation
**/
OsAnimation*
os_animation_new (gint32 rate,
gint32 duration,
OsAnimationUpdateFunc update_func,
OsAnimationEndFunc end_func,
gpointer user_data)
{
OsAnimation *animation;
OsAnimationPrivate *priv;
g_return_val_if_fail (rate != 0, NULL);
g_return_val_if_fail (duration != 0, NULL);
g_return_val_if_fail (update_func != NULL, NULL);
animation = g_object_new (OS_TYPE_ANIMATION, NULL);
priv = animation->priv;
priv->update_func = update_func;
priv->end_func = end_func;
priv->user_data = user_data;
priv->duration = (gint64) duration * G_GINT64_CONSTANT (1000);
priv->rate = 1000 / rate;
return animation;
}
/**
* os_animation_is_running:
* @animation: a #OsAnimation
*
* Returns TRUE if the animation is running
**/
gboolean
os_animation_is_running (OsAnimation *animation)
{
OsAnimationPrivate *priv;
g_return_val_if_fail (animation != NULL, FALSE);
priv = animation->priv;
return priv->source_id != 0;
}
/**
* os_animation_set_duration:
* @animation: a #OsAnimation
* @duration: the new duration
*
* Sets the new duration of the animation
**/
void
os_animation_set_duration (OsAnimation *animation,
gint32 duration)
{
OsAnimationPrivate *priv;
g_return_if_fail (animation != NULL);
g_return_if_fail (duration != 0);
priv = animation->priv;
priv->duration = (gint64) duration * G_GINT64_CONSTANT (1000);
}
/* Callback called by the animation. */
static gboolean
update_cb (gpointer user_data)
{
OsAnimation *animation = OS_ANIMATION (user_data);
OsAnimationPrivate *priv = animation->priv;
const gint64 current_time = g_get_monotonic_time ();
const gint64 end_time = priv->start_time + priv->duration;
if (current_time < end_time)
{
/* On-going animation. */
const gfloat diff_time = current_time - priv->start_time;
const gfloat weight = diff_time / priv->duration;
priv->update_func (weight, priv->user_data);
return TRUE;
}
else
{
/* Animation ended. */
priv->update_func (1.0f, priv->user_data);
priv->source_id = 0;
if (priv->end_func != NULL)
priv->end_func (priv->user_data);
return FALSE;
}
}
/**
* os_animation_start:
* @animation: a #OsAnimation
*
* Starts the animation
**/
void
os_animation_start (OsAnimation *animation)
{
OsAnimationPrivate *priv;
g_return_if_fail (animation != NULL);
priv = animation->priv;
if (priv->source_id == 0)
{
priv->start_time = g_get_monotonic_time ();
priv->source_id = g_timeout_add (priv->rate, update_cb, animation);
}
}
/**
* os_animation_stop:
* @animation: a #OsAnimation
* @stop_func: function to call at the stop
*
* Stops the animation.
* Before stopping, calls stop_func (if not NULL),
* or end_func (if not NULL).
**/
void
os_animation_stop (OsAnimation *animation,
OsAnimationStopFunc stop_func)
{
OsAnimationPrivate *priv;
g_return_if_fail (animation != NULL);
priv = animation->priv;
if (priv->source_id != 0)
{
if (stop_func != NULL)
stop_func (priv->user_data);
else if (priv->end_func != NULL)
priv->end_func (priv->user_data);
g_source_remove (priv->source_id);
priv->source_id = 0;
}
}
overlay-scrollbar-0.2.17.1+16.04.20151117/tests/ 0000755 0000153 0000161 00000000000 12622657427 021114 5 ustar pbuser pbgroup 0000000 0000000 overlay-scrollbar-0.2.17.1+16.04.20151117/tests/test-os.c 0000644 0000153 0000161 00000027423 12622657062 022661 0 ustar pbuser pbgroup 0000000 0000000 /* overlay-scrollbar
*
* Copyright © 2011 Canonical Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* 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
*
* Authored by Andrea Cimitan
*/
#include
static char text0[] = "Ubuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!";
static char text1[] = "Ubuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!\n\
Ubuntu is gonna rock!\nUbuntu is gonna rock!\nUbuntu is gonna rock!";
typedef struct
{
const gboolean check;
const gchar *description;
}
List;
enum
{
COLUMN_CHECK,
COLUMN_DESCRIPTION,
COLUMN_ACTIVE,
NUM_COLUMNS
};
static List data[] =
{
{ FALSE, "Ubuntu really rocks." },
{ TRUE, "Ubuntu will rock soon!" },
{ FALSE, "Nokia and Microsoft are gonna rock." },
{ TRUE, "Cimi needs a vacation!" },
{ FALSE, "I prefer rain to sunshine." },
{ TRUE, "I wonna flight with Mark's jet." },
{ FALSE, "Gtk+ 2.0 and X11 rocks." },
{ FALSE, "Nokia won't kill Qt with this move." },
{ TRUE, "Can't wait for the sun to ride my bike." },
{ TRUE, "UDS in Florida was awesome!" },
{ FALSE, "I'm not bored at all of writing there." },
{ TRUE, "A developer should be tanned." },
{ FALSE, "Firefox is faster than Chromium." },
{ TRUE, "Please Cimi, stop writing!" },
};
static GtkTreeModel* model_create (void);
static void renderer_check_toggled_cb (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer user_data);
static void tree_view_add_columns (GtkTreeView *treeview);
static void window_destroy_cb (GtkWidget *widget,
gpointer user_data);
/**
* model_create:
* create GtkTreeModel
**/
static GtkTreeModel*
model_create (void)
{
gint i = 0;
gint length;
GtkListStore *store;
GtkTreeIter iter;
/* create list store */
store = gtk_list_store_new (NUM_COLUMNS,
G_TYPE_BOOLEAN,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
/* add data to the list store */
length = (gint) G_N_ELEMENTS (data);
for (i = 0; i < length; i++)
{
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_CHECK, data[i].check,
COLUMN_DESCRIPTION, data[i].description,
COLUMN_ACTIVE, FALSE,
-1);
}
return GTK_TREE_MODEL (store);
}
/**
* renderer_check_toggled_cb:
* callback for "toggled" signal
**/
static void
renderer_check_toggled_cb (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer user_data)
{
GtkTreeModel *model = (GtkTreeModel*)user_data;
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean check;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, COLUMN_CHECK, &check, -1);
/* do something with the value */
check ^= 1;
/* set new value */
gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_CHECK, check, -1);
/* clean up */
gtk_tree_path_free (path);
}
/**
* tree_view_add_columns:
* add columns to the GtkTreeView
**/
static void
tree_view_add_columns (GtkTreeView *treeview)
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
/* column for fixed toggles */
renderer = gtk_cell_renderer_toggle_new ();
g_signal_connect (renderer, "toggled",
G_CALLBACK (renderer_check_toggled_cb), model);
column = gtk_tree_view_column_new_with_attributes ("True?",
renderer,
"active", COLUMN_CHECK,
NULL);
/* set this column to a fixed sizing (of 50 pixels) */
gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
gtk_tree_view_append_column (treeview, column);
/* column for description */
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("Description",
renderer,
"text",
COLUMN_DESCRIPTION,
NULL);
gtk_tree_view_column_set_sort_column_id (column, COLUMN_DESCRIPTION);
gtk_tree_view_append_column (treeview, column);
}
/**
* window_destroy_cb:
* destroy callback for window
**/
static void
window_destroy_cb (GtkWidget *widget,
gpointer user_data)
{
gtk_main_quit ();
}
/**
* main:
* main routine
**/
int
main (int argc,
char *argv[])
{
GtkWidget *scrolled_window_text0, *scrolled_window_text1, *scrolled_window_tree_view;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *text_view0, *text_view1;
GtkWidget *tree_view;
GtkTreeModel *model;
GtkTextBuffer *text_buffer0, *text_buffer1;
GtkWidget *window;
gtk_init (&argc, &argv);
/* window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 500);
gtk_window_set_title (GTK_WINDOW (window), "Vertical \"Overlay Scrollbar\" test");
/* vbox */
vbox = gtk_vbox_new (TRUE, 2);
/* hbox */
hbox = gtk_hbox_new (TRUE, 2);
/* scrolled_window_text0 */
scrolled_window_text0 = gtk_scrolled_window_new (NULL, NULL);
/* text_view0 */
text_view0 = gtk_text_view_new ();
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window_text0), text_view0);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window_text0), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_placement (GTK_SCROLLED_WINDOW (scrolled_window_text0), GTK_CORNER_BOTTOM_RIGHT);
/* text_buffer0 */
text_buffer0 = gtk_text_view_get_buffer(GTK_TEXT_VIEW (text_view0));
gtk_text_buffer_set_text (text_buffer0, text0, -1);
/* scrolled_window_text1 */
scrolled_window_text1 = gtk_scrolled_window_new (NULL, NULL);
/* text_view1 */
text_view1 = gtk_text_view_new ();
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window_text1), text_view1);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window_text1), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
/* text_buffer1 */
text_buffer1 = gtk_text_view_get_buffer(GTK_TEXT_VIEW (text_view1));
gtk_text_buffer_set_text (text_buffer1, text1, -1);
/* model */
model = model_create ();
/* tree_view */
tree_view = gtk_tree_view_new_with_model (model);
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
gtk_tree_view_set_search_column (GTK_TREE_VIEW (tree_view),
COLUMN_DESCRIPTION);
tree_view_add_columns (GTK_TREE_VIEW (tree_view));
g_object_unref (model);
/* scrolled_window_tree_view */
scrolled_window_tree_view = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window_tree_view),
GTK_SHADOW_ETCHED_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window_tree_view),
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (scrolled_window_tree_view), tree_view);
/* containers */
gtk_container_set_border_width (GTK_CONTAINER (window), 2);
gtk_container_add (GTK_CONTAINER (hbox), scrolled_window_text0);
gtk_container_add (GTK_CONTAINER (hbox), scrolled_window_text1);
gtk_container_add (GTK_CONTAINER (vbox), hbox);
gtk_container_add (GTK_CONTAINER (vbox), scrolled_window_tree_view);
gtk_container_add (GTK_CONTAINER (window), vbox);
/* signals */
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (window_destroy_cb), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
overlay-scrollbar-0.2.17.1+16.04.20151117/tests/Makefile.am 0000644 0000153 0000161 00000000162 12622657062 023142 0 ustar pbuser pbgroup 0000000 0000000 VER=
noinst_PROGRAMS = \
test-os
test_os_CFLAGS = -I$(top_srcdir) $(OS_CFLAGS)
test_os_LDFLAGS = $(OS_LIBADD)
overlay-scrollbar-0.2.17.1+16.04.20151117/configure.ac 0000644 0000153 0000161 00000007765 12622657062 022252 0 ustar pbuser pbgroup 0000000 0000000 AC_PREREQ(2.63)
# Package
AC_INIT([overlay-scrollbar],[0.3.0],[https://bugs.launchpad.net/ayatana-scrollbar],[overlay-scrollbar])
AC_CONFIG_SRCDIR([os/os-scrollbar.h])
AC_CONFIG_MACRO_DIR([build])
AC_CONFIG_AUX_DIR([build])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.11 foreign dist-bzip2 ])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
NANO=`echo AC_PACKAGE_VERSION | cut -d'.' -f4`
if test x"$NANO" = x || test "x$NANO" = "x0" ; then
OS_TRUNK="no"
PACKAGE_VERSION_NANO="0"
else
OS_TRUNK="yes"
PACKAGE_VERSION_NANO="$NANO"
fi
AC_SUBST(OS_TRUNK)
AC_SUBST(PACKAGE_VERSION_NANO)
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC
AM_PROG_CC_C_O
LT_INIT
AC_C_CONST
AC_C_INLINE
# Build dependencies
AC_PATH_PROG([GLIB_MKENUMS], [glib-mkenums])
AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal])
PKG_PROG_PKG_CONFIG
GLIB_GSETTINGS
AC_SUBST(glib_req, 2.26.0)
AC_SUBST(gtk_req, 2.24.26)
AC_SUBST(cairo_req, 1.10)
PKG_CHECK_MODULES(DEPS, [glib-2.0 >= $glib_req gtk+-2.0 >= $gtk_req cairo >= $cairo_req gmodule-2.0 >= $glib_req x11],
[AC_SUBST(DEPS_CFLAGS)
AC_SUBST(DEPS_LIBS)])
GTK_MODULES_DIR="${libdir}/gtk-2.0/modules"
AC_SUBST(GTK_MODULES_DIR)
# Check if build tests
AC_ARG_ENABLE([tests],
[AC_HELP_STRING([--disable-tests], [Disable tests])],
[],
[enable_tests=yes])
AM_CONDITIONAL([ENABLE_TESTS], [test "x$enable_tests" != "xno"])
# Debug flags
if test x$OS_TRUNK = xyes; then
DEFAULT_DEBUG="yes"
else
DEFAULT_DEBUG="no"
fi
AC_SUBST(DEFAULT_DEBUG)
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug=@<:@no/yes/full@:>@],[Control debug level @<:@default=debug_default@:>@])],
[],
[enable_debug=$DEFAULT_DEBUG])
AS_CASE([$enable_debug],
[yes],
[
test "$cflags_set" = set || CFLAGS="$CFLAGS -g"
DEBUG_CFLAGS="-D_DEBUG"
],
[no],
[DEBUG_CFLAGS="-DNDEBUG"],
[AC_MSG_ERROR([Unknown argument for --enable-debug])]
)
AC_SUBST(DEBUG_CFLAGS)
# Maintainer flags
if test x$OS_TRUNK = xyes; then
DEFAULT_MAINTAINER_FLAGS="yes"
else
DEFAULT_MAINTAINER_FLAGS="no"
fi
AC_SUBST(DEFAULT_MAINTAINER_FLAGS)
AC_ARG_ENABLE([maintainer-flags],
[AS_HELP_STRING([--enable-maintainer-flags=@<:@no/yes@:>@], [Use strict compiler flags @<:@default=maintainer_flags_default@:>@])],
[],
[enable_maintainer_flags=$DEFAULT_MAINTAINER_FLAGS])
AS_IF([test "x$enable_maintainer_flags" = "xyes" && test "x$GCC" = "xyes"],
[
AS_COMPILER_FLAGS([MAINTAINER_CFLAGS],
["-fno-common -Wall -Wextra -Werror -Wempty-body
-Wno-missing-field-initializers -Wstrict-aliasing=2
-Wno-unused-parameter -Wdeclaration-after-statement
-Wpointer-arith -Wcast-align -Wwrite-strings
-Wformat-security -Wformat-nonliteral -Winit-self
-Wmissing-declarations -Wnested-externs -Wundef
-Wold-style-definition -Wswitch-default
-Wredundant-decls"])
]
)
AC_SUBST(MAINTAINER_CFLAGS)
# Variables
OS_LIBADD="\$(DEPS_LIBS)"
AC_SUBST(OS_LIBADD)
OS_CFLAGS="-I\$(top_srcdir) -DOS_COMPILATION \$(DEPS_CFLAGS) \$(DEBUG_CFLAGS) \$(MAINTAINER_CFLAGS)"
AC_SUBST(OS_CFLAGS)
OS_LDFLAGS="-shared -module -avoid-version"
AC_SUBST(OS_LDFLAGS)
# Files
AC_CONFIG_FILES([
Makefile
build/Makefile
data/Makefile
os/Makefile
tests/Makefile
])
AC_OUTPUT
# Summary
echo ""
echo " overlay-scrollbar $PACKAGE_VERSION"
echo " ======================="
echo ""
echo " Tests: ${enable_tests}"
echo " Debug: ${enable_debug}"
echo " Prefix: ${prefix}"
echo " Module: ${GTK_MODULES_DIR}"
echo ""
echo " Compiler flags: ${CPPFLAGS} ${DEBUG_CFLAGS} ${MAINTAINER_CFLAGS}"
echo ""
echo " NOTE:"
echo " Run 'make clean' if you change Gtk+ version."
echo " The binary created has the same name regardless of the version."
echo ""
overlay-scrollbar-0.2.17.1+16.04.20151117/autogen.sh 0000755 0000153 0000161 00000000123 12622657062 021742 0 ustar pbuser pbgroup 0000000 0000000 #!/bin/sh -e
mkdir -p m4
autoreconf -i
test -n "$NOCONFIGURE" || ./configure "$@"