overlay-scrollbar-0.2.17.1+16.04.20151117/0000755000015300001610000000000012622657427017752 5ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/data/0000755000015300001610000000000012622657427020663 5ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/data/com.canonical.desktop.interface.gschema.xml0000644000015300001610000000041012622657062031114 0ustar pbuserpbgroup00000000000000 'overlay-auto' overlay-scrollbar-0.2.17.1+16.04.20151117/data/81overlay-scrollbar0000644000015300001610000000030612622657062024413 0ustar pbuserpbgroup00000000000000# 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.am0000644000015300001610000000055012622657062022712 0ustar pbuserpbgroup00000000000000EXTRA_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/TODO0000644000015300001610000000000012622657062020423 0ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/NEWS0000644000015300001610000002411012622657062020442 0ustar pbuserpbgroup00000000000000overlay-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/AUTHORS0000644000015300001610000000013312622657062021012 0ustar pbuserpbgroup00000000000000Andrea Cimitan Loïc Molinari overlay-scrollbar-0.2.17.1+16.04.20151117/build/0000755000015300001610000000000012622657427021051 5ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/build/Makefile.am0000644000015300001610000000004112622657062023073 0ustar pbuserpbgroup00000000000000EXTRA_DIST = as-compiler-flag.m4 overlay-scrollbar-0.2.17.1+16.04.20151117/build/as-compiler-flag.m40000644000015300001610000000273612622657062024440 0ustar pbuserpbgroup00000000000000dnl 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.am0000644000015300001610000000101712622657062022000 0ustar pbuserpbgroup00000000000000SUBDIRS = \ 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/RELEASE0000644000015300001610000000000012622657062020736 0ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/COPYING0000644000015300001610000006364212622657062021013 0ustar pbuserpbgroup00000000000000 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/README0000644000015300001610000000000012622657062020613 0ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/os/0000755000015300001610000000000012622657427020373 5ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/os/os-log.c0000644000015300001610000000300412622657062021727 0ustar pbuserpbgroup00000000000000/* 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.c0000644000015300001610000004406612622657062021727 0ustar pbuserpbgroup00000000000000/* 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.h0000644000015300001610000002101212622657062022624 0ustar pbuserpbgroup00000000000000/* 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.c0000644000015300001610000007402112622657062022274 0ustar pbuserpbgroup00000000000000/* 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.am0000644000015300001610000000100712622657062022420 0ustar pbuserpbgroup00000000000000VER= 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.h0000644000015300001610000000211312622657062023136 0ustar pbuserpbgroup00000000000000/* 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.c0000644000015300001610000036142412622657062023146 0ustar pbuserpbgroup00000000000000/* 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.c0000644000015300001610000001372512622657062023140 0ustar pbuserpbgroup00000000000000/* 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/0000755000015300001610000000000012622657427021114 5ustar pbuserpbgroup00000000000000overlay-scrollbar-0.2.17.1+16.04.20151117/tests/test-os.c0000644000015300001610000002742312622657062022661 0ustar pbuserpbgroup00000000000000/* 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.am0000644000015300001610000000016212622657062023142 0ustar pbuserpbgroup00000000000000VER= 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.ac0000644000015300001610000000776512622657062022252 0ustar pbuserpbgroup00000000000000AC_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.sh0000755000015300001610000000012312622657062021742 0ustar pbuserpbgroup00000000000000#!/bin/sh -e mkdir -p m4 autoreconf -i test -n "$NOCONFIGURE" || ./configure "$@"