./0000755000004100000410000000000013437202764011253 5ustar www-datawww-data./CMakeLists.txt0000644000004100000410000002664313437202764014026 0ustar www-datawww-dataproject (unity) cmake_minimum_required(VERSION 2.8.9) include (cmake/Documentation.cmake) include (cmake/pch.cmake) include (GNUInstallDirs) # # Base bits # set (PROJECT_NAME "unity") set (UNITY_MAJOR 7) set (UNITY_MINOR 5) set (UNITY_MICRO 0) set (UNITY_VERSION "${UNITY_MAJOR}.${UNITY_MINOR}.${UNITY_MICRO}") set (UNITY_API_VERSION "6.0") set (UNITY_COMPONENTS_VERSION "6") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGNOME_DESKTOP_USE_UNSTABLE_API -std=c++11 -fno-permissive") set (CMAKE_CXX_FLAGS_DEBUG "-g3 -DUNITY_DEBUG_BUILD") set (CMAKE_CXX_FLAGS_RELEASE "") option( ENABLE_X_SUPPORT "Enable X.org support in unity" ON ) option( ENABLE_UNIT_TESTS "Enable Unity Unit Tests" ON ) # This is due to bug lp:668799 - qemu-arm segfaults executing msgmerge option( I18N_SUPPORT "Enable I18N, do the .po file thing." ON ) option( ENABLE_NETWORKAREAREGION_PLUGIN "Enable Unity Network Area region Plugin" OFF ) if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64") set (PIC_FLAGS "-fPIC") endif() if (ENABLE_X_SUPPORT) add_definitions(-DUSE_X11) message("Unity is configured with support for X.org") else () message("Unity is configured with without X.org") add_definitions(-DNO_X11) endif () if (BUILD_GLES) add_definitions(-DNUX_OPENGLES_20) add_definitions(-DUSE_GLES) set (UNITY_STANDALONE_LADD "unity-core-${UNITY_API_VERSION};m;pthread;dl") else () set (UNITY_STANDALONE_LADD "unity-core-${UNITY_API_VERSION};m;pthread;dl;GL;GLU") endif () if (CMAKE_BUILD_TYPE MATCHES coverage) set (COVERAGE_XML_FILE "${CMAKE_BINARY_DIR}/coverage.xml") # FIXME: Read below # set (COVERAGE_INFO_FILE "${CMAKE_BINARY_DIR}/coverage-html.info") # set (COVERAGE_HTML_DIR "${CMAKE_BINARY_DIR}/coverage-html") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage" ) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage" ) set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} --coverage" ) set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage" ) find_program(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin") if (NOT GCOVR_EXECUTABLE) message(FATAL_ERROR "Cannot enable coverage targets because gcovr was not found.") else () message (STATUS "Enabling XML coverage report") add_custom_target (coverage-xml COMMAND "${GCOVR_EXECUTABLE}" --exclude="tests.*" --exclude="obj-.*" -x -r "${CMAKE_SOURCE_DIR}" -o "${COVERAGE_XML_FILE}") endif() # FIXME: This got commented out, as it forces a strict dependency on lcov # find_program(LCOV_EXECUTABLE lcov HINTS ${LCOV_ROOT} "${LCOV_ROOT}/bin") # find_program(GENHTML_EXECUTABLE genhtml HINTS ${GENHTML_ROOT} "${GENHTML_ROOT}/bin") # if (NOT LCOV_EXECUTABLE) # message(FATAL_ERROR "Cannot enable coverage targets because gcovr was not found.") # elseif (NOT GENHTML_EXECUTABLE) # message(FATAL_ERROR "Cannot enable coverage targets because genhtml was not found.") # else () # message (STATUS "Enabling HTML coverage report") # add_custom_target (coverage-html # COMMAND "${LCOV_EXECUTABLE}" --directory "${CMAKE_BINARY_DIR}" --capture --output-file "${COVERAGE_INFO_FILE}" # COMMAND "${GENHTML_EXECUTABLE}" --output-directory "${COVERAGE_HTML_DIR}" "${COVERAGE_INFO_FILE}") # endif() endif (CMAKE_BUILD_TYPE MATCHES coverage) # # Niceties # set (ARCHIVE_NAME unity-${UNITY_VERSION}) add_custom_target (pre-distcheck COMMAND echo "" && echo "• Releasing Unity ${UNITY_VERSION}" && cd ${CMAKE_SOURCE_DIR} && echo "• Generating ChangeLog" && bzr log --gnu-changelog > ChangeLog && echo "• Generating AUTHORS" && bzr log --long --levels=0 | grep -e "^\\s*author:" | cut -d ":" -f 2 | sed "s/,/\n/g" | sed -r -f AUTHOR-glue | sort -u | uniq -i > AUTHORS && echo "• Running Distcheck" ) add_custom_target (dist COMMAND echo "• Creating Tarball" && bzr export --root=${ARCHIVE_NAME} ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2 && echo "• Signing Tarball" && gpg --armor --sign --detach-sig ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_dependencies(dist pre-distcheck) add_custom_target (distcheck COMMAND cd ${CMAKE_BINARY_DIR} && rm -rf ${ARCHIVE_NAME} && tar xf ${ARCHIVE_NAME}.tar.bz2 && mkdir ${ARCHIVE_NAME}/build && cd ${ARCHIVE_NAME}/build && cmake -DCMAKE_INSTALL_PREFIX=../install -DGSETTINGS_LOCALINSTALL=ON .. -DCMAKE_MODULE_PATH=/usr/share/cmake && make && make install && make check-headless ) add_dependencies(distcheck dist) add_custom_target (post-distcheck COMMAND echo "• Committing Release" && bzr commit -m\"Release ${UNITY_VERSION}\" --unchanged && echo "• Tagging Release" && bzr tag ${UNITY_VERSION} && echo "• Unity ${UNITY_VERSION} is ready for release." WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_dependencies(post-distcheck distcheck) add_custom_target (release) add_dependencies (release distcheck) # # config.h # set (VERSION "${UNITY_VERSION}") set (PREFIXPATH "${CMAKE_INSTALL_PREFIX}") set (UNITY_LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}/${PROJECT_NAME}") set (UNITY_DATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}") set (UNITY_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}") set (PKGDATADIR "${UNITY_DATADIR}/icons") set (SOURCEDATADIR "${CMAKE_CURRENT_SOURCE_DIR}/resources") set (BUILDDIR "${CMAKE_BINARY_DIR}") set (TESTDATADIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/data") set (LOCALE_DIR "${CMAKE_INSTALL_FULL_LOCALEDIR}") # specify the domain directly rather than refering to a variable # like ${PROJECT_NAME} to no confuse dh_translations set (GETTEXT_PACKAGE "unity") find_package (PkgConfig) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gtk+-3.0 --variable prefix OUTPUT_VARIABLE GTK_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} unity --variable lensesdir OUTPUT_VARIABLE LENSES_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable indicatordir OUTPUT_VARIABLE INDICATORDIR OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable iconsdir OUTPUT_VARIABLE INDICATORICONDIR OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable prefix OUTPUT_VARIABLE INDICATORPREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) set (INDICATOR_SERVICE_DIR "${INDICATORPREFIX}/share/${PROJECT_NAME}/indicators") configure_file (${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h) # # i18n # find_package (Gettext REQUIRED) set (COMPIZ_I18N_DIR ${CMAKE_SOURCE_DIR}/po) add_custom_command (OUTPUT ${CMAKE_SOURCE_DIR}/po/${PROJECT_NAME}.pot COMMAND xgettext -c --from-code=UTF-8 --files-from ${CMAKE_SOURCE_DIR}/po/POTFILES.in --keyword=_ -o ${CMAKE_SOURCE_DIR}/po/${PROJECT_NAME}.pot --copyright-holder="Canonical Ltd" --msgid-bugs-address="ayatana-dev@lists.launchpad.net" --no-wrap --no-location WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) if (I18N_SUPPORT) if (GETTEXT_FOUND) set (HAVE_GETTEXT true) file (GLOB _translations ${CMAKE_SOURCE_DIR}/po/*.po) GETTEXT_CREATE_TRANSLATIONS (${CMAKE_SOURCE_DIR}/po/${PROJECT_NAME}.pot ALL ${_translations}) endif (GETTEXT_FOUND) endif() # # Enable or disable boot logging # option (BOOT_LOGGER "Enable startup performance logging" OFF) if (BOOT_LOGGER) SET (BOOT_LOGGER_FLAG "-DENABLE_LOGGER") endif (BOOT_LOGGER) SET (MAINTAINER_CXXFLAGS "-Werror -Wall -Wcast-align -Wempty-body -Wformat-security -Winit-self -Warray-bounds -Wno-error=deprecated-declarations") option (DISABLE_ERROR_ON_LOCAL_TYPEDEFS_WARNINGS "Disable errors when local typedefs are unused" ON) if (DISABLE_ERROR_ON_LOCAL_TYPEDEFS_WARNINGS) SET (MAINTAINER_CXXFLAGS "${MAINTAINER_CXXFLAGS} -Wno-error=unused-local-typedefs") endif (DISABLE_ERROR_ON_LOCAL_TYPEDEFS_WARNINGS) option (DISABLE_MAINTAINER_CXXFLAGS "Disable maintainer CXXFlags" OFF) if (DISABLE_MAINTAINER_CXXFLAGS) SET (MAINTAINER_CXXFLAGS "") endif (DISABLE_MAINTAINER_CXXFLAGS) # Make sure these flags are used for every build. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MAINTAINER_CXXFLAGS}") # # Compiz Plugins # set(UNITY_PROTOCOL_PRIVATE_DEPS unity-protocol-private>=7.1.0) set(UNITY_PLUGIN_SHARED_DEPS ${UNITY_PROTOCOL_PRIVATE_DEPS} appstream-glib atk atk-bridge-2.0 cairo>=1.13.1 dbusmenu-glib-0.4 dee-1.0 gio-2.0>=2.30.0 gio-unix-2.0 gmodule-2.0 gthread-2.0 gtk+-3.0>=3.1 indicator3-0.4>=0.4.90 json-glib-1.0 libbamf3>=0.5.3 gnome-desktop-3.0 libnotify libstartup-notification-1.0 nux-4.0>=4.0.5 sigc++-2.0>=2.4.0 unity-misc>=0.4.0 xpathselect=1.4 zeitgeist-2.0 ) set(UNITY_PLUGIN_DEPS ${UNITY_PLUGIN_SHARED_DEPS}) if(ENABLE_X_SUPPORT) set(UNITY_PLUGIN_DEPS ${UNITY_PLUGIN_DEPS} compiz>=0.9.11 libgeis x11 xfixes xi>=1.6.99.1 xrender>=0.9 ) endif () pkg_check_modules (CACHED_UNITY_DEPS REQUIRED ${UNITY_PLUGIN_DEPS}) pkg_check_modules (CACHED_UNITY_PRIVATE_DEPS REQUIRED ${UNITY_PROTOCOL_PRIVATE_DEPS}) find_library (UNITY_PROTOCOL_PRIVATE_LIB unity-protocol-private ${CACHED_UNITY_PRIVATE_DEPS_LIBDIR} ${CACHED_UNITY_PRIVATE_DEPS_LIBRARY_DIRS}) set(UNITY_STANDALONE_LADD ${UNITY_STANDALONE_LADD} ${UNITY_PROTOCOL_PRIVATE_LIB}) add_subdirectory(a11y) add_subdirectory(unity-shared) add_subdirectory(dash) add_subdirectory(launcher) add_subdirectory(data) if (ENABLE_X_SUPPORT) add_subdirectory(hud) add_subdirectory(lockscreen) add_subdirectory(panel) add_subdirectory(decorations) add_subdirectory(plugins/unityshell) add_subdirectory(plugins/unity-mt-grab-handles) add_subdirectory(shortcuts) add_subdirectory(shutdown) add_subdirectory(unity-standalone) if (ENABLE_NETWORKAREAREGION_PLUGIN) add_subdirectory(plugins/networkarearegion) endif (ENABLE_NETWORKAREAREGION_PLUGIN) endif () add_subdirectory(doc) add_subdirectory(services) add_subdirectory(tools) add_subdirectory(UnityCore) add_subdirectory(guides) add_subdirectory(gnome) if (ENABLE_UNIT_TESTS) add_subdirectory(tests) else (ENABLE_UNIT_TESTS) set (MISSING_TESTS_MSG "-- Tests disabled, compile with -DENABLE_UNIT_TESTS=ON") add_custom_target (check COMMAND echo ${MISSING_TESTS_MSG}) add_custom_target (check-headless COMMAND echo ${MISSING_TESTS_MSG}) add_custom_target (gcheck COMMAND echo ${MISSING_TESTS_MSG}) endif (ENABLE_UNIT_TESTS) # Resources install (FILES resources/dash-widgets.json DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/themes) file (GLOB _datafiles "${CMAKE_CURRENT_SOURCE_DIR}/resources/*") install (FILES ${_datafiles} DESTINATION ${PKGDATADIR}) # # docs # # check if doxygen is even installed find_package(Doxygen) if (DOXYGEN_FOUND STREQUAL "NO") message("Doxygen not found. Documentation will not be built") endif (DOXYGEN_FOUND STREQUAL "NO") if (DOXYGEN_FOUND STREQUAL "YES") set(TOP_SRCDIR ${CMAKE_CURRENT_SOURCE_DIR}) # prepare doxygen configuration file configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) # add doxygen as target add_custom_target(doxygen ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) # cleanup $build/api-doc on "make clean" set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES api-doc) endif (DOXYGEN_FOUND STREQUAL "YES") ./unity-standalone/0000755000004100000410000000000013437202764014551 5ustar www-datawww-data./unity-standalone/CMakeLists.txt0000644000004100000410000000166213437202764017316 0ustar www-datawww-dataset(UNITY_SRC ../plugins/unityshell/src) set (CFLAGS ${CACHED_UNITY_DEPS_CFLAGS} ${CACHED_UNITY_DEPS_CFLAGS_OTHER} ${PIC_FLAGS} ) string (REPLACE ";" " " CFLAGS "${CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}") set (LIBS ${CACHED_UNITY_DEPS_LDFLAGS} ${UNITY_STANDALONE_LADD}) include_directories (.. ../services ../UnityCore ${UNITY_SRC} ${CMAKE_BINARY_DIR}) # # Headers & Sources # set (STANDALONE_SOURCES StandaloneUnity.cpp ) add_executable (unity-standalone StandaloneUnity.cpp) # This makes linker to include library dir in RUNPATH find_library (COMPIZ_LIB compiz_core ${COMPIZ_LIBDIR}) target_link_libraries (unity-standalone dash-lib launcher-lib panel-lib unity-shared unity-shared-bamf unity-shared-standalone ${COMPIZ_LIB}) ./unity-standalone/StandaloneUnity.cpp0000644000004100000410000001557213437202764020410 0ustar www-datawww-data/* * Copyright 2010 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Gordon Allott * Neil Jagdish Patel * */ #include #include #include #include #include #include #include #include "dash/DashController.h" #include "dash/DashView.h" #include "launcher/FavoriteStoreGSettings.h" #include "launcher/Launcher.h" #include "launcher/LauncherController.h" #include "panel/PanelController.h" #include "panel/PanelView.h" #include "unity-shared/BGHash.h" #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/DashStyle.h" #include "unity-shared/FontSettings.h" #include "unity-shared/KeyGrabber.h" #include "unity-shared/MenuManager.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UnitySettings.h" namespace { static int display_width = 1200; static int display_height = 720; static gboolean no_window_decorations = FALSE; static gboolean force_tv = FALSE; static GOptionEntry entries[] = { {"width", 'w', 0, G_OPTION_ARG_INT, &display_width, "Display width", NULL}, {"height", 'h', 0, G_OPTION_ARG_INT, &display_height, "Display height", NULL}, {"no-window-decorations", 'd', 0, G_OPTION_ARG_NONE, &no_window_decorations, "Disables the window decorations", NULL}, {"force-tv", 't', 0, G_OPTION_ARG_NONE, &force_tv, "Forces the TV interface", NULL}, {NULL} }; } using namespace unity; struct StandaloneDndManager : XdndManager { int Monitor() const { return 0; } }; struct StandaloneKeyGrabber : key::Grabber { CompAction::Vector& GetActions() override { return noActions(); } uint32_t AddAction(CompAction const&) override { return 0; }; bool RemoveAction(CompAction const&) override { return false; }; bool RemoveAction(uint32_t id) override { return false; }; }; class UnityStandalone { public: UnityStandalone (); ~UnityStandalone (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); launcher::Controller::Ptr launcher_controller; dash::Controller::Ptr dash_controller; panel::Controller::Ptr panel_controller; }; UnityStandalone::UnityStandalone () { } UnityStandalone::~UnityStandalone () { } void UnityStandalone::Init () { auto xdnd_manager = std::make_shared(); auto edge_barriers = std::make_shared(); auto indicators = std::make_shared(); auto key_grabber = std::make_shared(); auto menu_manager = std::make_shared(indicators, key_grabber); launcher_controller = std::make_shared(xdnd_manager, edge_barriers); panel_controller = std::make_shared(menu_manager, edge_barriers); dash_controller = std::make_shared(); } void UnityStandalone::InitWindowThread(nux::NThread* thread, void* InitData) { UnityStandalone *self = static_cast(InitData); self->Init(); } class UnityStandaloneTV { public: UnityStandaloneTV(); ~UnityStandaloneTV(); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init(); launcher::Controller::Ptr launcher_controller; dash::Controller::Ptr dash_controller; }; UnityStandaloneTV::UnityStandaloneTV() {}; UnityStandaloneTV::~UnityStandaloneTV() {}; void UnityStandaloneTV::Init() { auto xdnd_manager = std::make_shared(); auto edge_barriers = std::make_shared(); launcher_controller = std::make_shared(xdnd_manager, edge_barriers); dash_controller = std::make_shared(); UBusManager().SendMessage(UBUS_DASH_EXTERNAL_ACTIVATION, nullptr); } void UnityStandaloneTV::InitWindowThread(nux::NThread* thread, void* InitData) { UnityStandaloneTV *self = static_cast(InitData); self->Init(); } int main(int argc, char **argv) { nux::WindowThread* wt = NULL; GError *error = NULL; GOptionContext *context; nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); context = g_option_context_new("- Unity standalone"); g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE); g_option_context_add_group(context, gtk_get_option_group(TRUE)); g_option_context_parse(context, &argc, &argv, &error); if (error != NULL) { g_print("Option parsiong failed: %s\n", error->message); g_error_free(error); exit(1); } gtk_init(&argc, &argv); BGHash bghash; FontSettings font_settings; // The instances for the pseudo-singletons. Settings settings; settings.is_standalone = true; if (force_tv) Settings::Instance().form_factor(FormFactor::TV); dash::Style dash_style; panel::Style panel_style; unity::ThumbnailGenerator thumbnail_generator; internal::FavoriteStoreGSettings favorite_store; BackgroundEffectHelper::blur_type = BLUR_NONE; if (!force_tv) { UnityStandalone *standalone_runner = new UnityStandalone(); wt = nux::CreateNuxWindow("standalone-unity", display_width, display_height, (no_window_decorations) ? nux::WINDOWSTYLE_NOBORDER : nux::WINDOWSTYLE_NORMAL, 0, /* no parent */ false, &UnityStandalone::InitWindowThread, standalone_runner); } else { //TODO - we should be able to pass in a monitor so that we can make the window //the size of the monitor and position the window on the monitor correctly. UnityStandaloneTV *standalone_runner = new UnityStandaloneTV(); wt = nux::CreateNuxWindow("standalone-unity-tv", display_width, display_height, (no_window_decorations) ? nux::WINDOWSTYLE_NOBORDER : nux::WINDOWSTYLE_NORMAL, 0, /* no parent */ false, &UnityStandaloneTV::InitWindowThread, standalone_runner); } wt->Run(NULL); delete wt; return 0; } ./INSTALL0000644000004100000410000001175613437202764012316 0ustar www-datawww-data Install -------------------------------------------------------------------------------- • Notes - libunity is an independant library which has a client side API for talking to Unity. However it does not depend on the main Unity codebase and the main Unity codebase does not depend on it. - Unity and it's desktop environment modules are all modules of Compiz. We use a patched version of Compiz which uses the GLib main loop instead of the custom Compiz main loop. This allows us to use GNOME libraries easily inside the Unity plugins. We are currently working on getting this patch upstreamed, but until then you will need to build this special version of Compiz. - libunity is written in Vala and the rest of Unity in C++/C. - Unity depends on a library called Nux (lp:nux) which let's us do OpenGL layouts quickly and efficiently. • Dependencies These are in Debian package name form, but it should be easy enough to translate them to other systems: libglib2.0-dev libgdk-pixbuf2.0-dev libcairo2-dev libpng12-dev libglew1.5-dev libglewmx1.5-dev libxxf86vm-dev libgl1-mesa-dev libsigc++-2.0-dev libpango1.0-dev doxygen cmake pkg-config intltool libbamf-dev gsettings-desktop-schemas-dev libgconf2-dev libglib2.0-dev libdbusmenu-glib-dev libgtk2.0-dev libdee-dev libindicator-dev libboost-dev libboost-serialization-dev libmetacity-dev python-dev cython However, as with any project, it's probably best to just run autogen/cmake and figure out what you need/is missing. If your distro supports grabbing all the packages needed to build a package, then at least do that for Compiz, as I'm not going to detail everything it needs here. In case your distro isn't packaging all the Ayatana software, these links might come in handy: https://launchpad.net/dee https://launchpad.net/bamf https://launchpad.net/libindicator Also, although we don't hard depend on them, having a few indicators installed will make your experience better: https://launchpad.net/indicator-appmenu https://launchpad.net/indicator-application https://launchpad.net/indicator-network https://launchpad.net/indicator-sound https://launchpad.net/indicator-messages https://launchpad.net/indicator-datetime https://launchpad.net/indicator-me https://launchpad.net/indicator-session • Build Compiz GLib This is taken from http://wiki.ubuntu.com/Unity/InstallationGuideFromSource and was originally authored by Sam: core: git clone git://git.compiz.org/users/dbo/compiz-with-glib-mainloop cd compiz-with-glib-mainloop mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/opt/unity make sudo make findcompiz_install sudo make install exporting paths: export PKG_CONFIG_PATH=/opt/unity/lib/pkgconfig:${PKG_CONFIG_PATH} export LD_LIBRARY_PATH=/opt/unity/lib:${LD_LIBRARY_PATH} export LD_RUN_PATH=/opt/unity/lib:${LD_RUN_PATH} libcompizconfig: git clone git://git.compiz.org/compiz/compizconfig/libcompizconfig cd libcompizconfig mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/opt/unity make sudo make install compizconfig-python: git clone git://git.compiz.org/compiz/compizconfig/compizconfig-python cd compizconfig-python python setup.py install --prefix=/opt/unity ccsm: git clone git://git.compiz.org/compiz/compizconfig/ccsm cd ccsm python setup.py install --prefix=/opt/unity plugins-main: git clone git://git.compiz.org/compiz/plugins-main cd plugins-main git submodule init git pull origin master git submodule update mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/opt/unity make sudo make install plugins-extra: git clone git://git.compiz.org/compiz/plugins-extra cd plugins-extra git submodule init git pull origin master git submodule update mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/opt/unity make sudo make install • Build Nux bzr branch lp:nux cd nux ./autogen.sh --disable-documentation --prefix=/opt/unity make sudo make install • Build Unity bzr branch lp:unity cd unity mkdir build; cd build cmake .. -DCMAKE_BUILD_TYPE=Debug -DCOMPIZ_PLUGIN_INSTALL_TYPE=package -DCMAKE_INSTALL_PREFIX=/opt/unity make sudo make install • Cleanup unset PKG_CONFIG_PATH unset LD_LIBRARY_PATH unset LD_RUN_PATH • Testing add this to your /home/$USER/.bashrc function compiz-unity-setup-env { export PATH=/opt/unity/bin:${PATH} export PYTHONPATH=/opt/unity/lib/python2.6/site-packages } Logout, login, then in a terminal do $ compiz-unity-setup-env $ compiz --replace cpp & $ ccsm And then use the CompizConfig Settings Window to search for and enable the Unity plugin! • Bugs If you find bugs in this installation guide or in Unity itself, please report them at https://launchpad.net/unity/+filebug ./README0000644000004100000410000000132713437202764012136 0ustar www-datawww-data Unity -------------------------------------------------------------------------------- • Installation Please see INSTALL or http://wiki.ubuntu.com/Unity/InstallationGuideFromSource • Tests - You can run `make check` in the build directory to run all GTester tests - In the build directory, ./tests/test-panel will start the panel in standalone mode, which is great for testing • Environmental Variables PANEL_USE_LOCAL_SERVICE=${anything} Makes the panel run the unity-panel-service directly instead of through D-Bus activation. This is used for testing how the panel reacts when it starts before the service does. ./config.h.cmake0000644000004100000410000000152313437202764013751 0ustar www-datawww-data#ifndef CONFIG_H #define CONFIG_H #cmakedefine PREFIXPATH "@PREFIXPATH@" #cmakedefine UNITY_DATADIR "@UNITY_DATADIR@" #cmakedefine UNITY_LIBDIR "@UNITY_LIBDIR@" #cmakedefine UNITY_INSTALL_LIBDIR "@UNITY_INSTALL_LIBDIR@" #cmakedefine PKGDATADIR "@PKGDATADIR@" #cmakedefine LOCALE_DIR "@LOCALE_DIR@" #cmakedefine VERSION "@VERSION@" #cmakedefine BUILDDIR "@BUILDDIR@" #cmakedefine SOURCEDATADIR "@SOURCEDATADIR@" #cmakedefine TESTDATADIR "@TESTDIRDIR@" #cmakedefine GETTEXT_PACKAGE "@GETTEXT_PACKAGE@" #cmakedefine LENSES_DIR "@LENSES_DIR@" #cmakedefine GTK_PREFIX "@GTK_PREFIX@" #ifndef INDICATORDIR #cmakedefine INDICATORDIR "@INDICATORDIR@" #endif #ifndef INDICATORICONDIR #cmakedefine INDICATORICONDIR "@INDICATORICONDIR@" #endif #ifndef INDICATOR_SERVICE_DIR #cmakedefine INDICATOR_SERVICE_DIR "@INDICATOR_SERVICE_DIR@" #endif #endif // CONFIG_H ./dash/0000755000004100000410000000000013437202764012172 5ustar www-datawww-data./dash/PlacesGroup.cpp0000755000004100000410000005247113437202764015136 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include #include #include "PlacesGroup.h" #include #include "config.h" #include #include #include "unity-shared/StaticCairoText.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/GraphicsUtils.h" #include "ResultView.h" #include "ResultViewGrid.h" #include "ResultRendererTile.h" #include "ResultRendererHorizontalTile.h" #include "CoverflowResultView.h" #include "FilterBasicButton.h" DECLARE_LOGGER(logger, "unity.dash.placesgroup"); namespace unity { namespace dash { namespace { const nux::Color EXPAND_DEFAULT_TEXT_COLOR(1.0f, 1.0f, 1.0f, 0.5f); const float EXPAND_DEFAULT_ICON_OPACITY = 0.5f; // Category highlight const RawPixel HIGHLIGHT_RIGHT_PADDING = 10_em; const RawPixel HIGHLIGHT_HEIGHT = 24_em; const RawPixel HIGHLIGHT_LEFT_PADDING = 10_em; const RawPixel SPACE_BETWEEN_CHILDREN = 10_em; const RawPixel TEXT_INTERNAL_MARGIN = 15_em; const RawPixel EXPAND_INTERNAL_MARGIN = 8_em; const double DEFAULT_SCALE = 1.0; // Font const char* const NAME_LABEL_FONT = "Ubuntu 13"; // 17px = 13 const char* const EXPANDER_LABEL_FONT = "Ubuntu 10"; // 13px = 10 } class HeaderView : public nux::View { public: HeaderView(NUX_FILE_LINE_DECL) : nux::View(NUX_FILE_LINE_PARAM) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(true); } protected: void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { }; void DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { graphics_engine.PushClippingRectangle(GetGeometry()); nux::GetPainter().PushPaintLayerStack(); if (GetLayout()) { GetLayout()->ProcessDraw(graphics_engine, force_draw); } nux::GetPainter().PopPaintLayerStack(); graphics_engine.PopClippingRectangle(); } bool AcceptKeyNavFocus() { return true; } nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) { if (event_type != nux::EVENT_MOUSE_WHEEL && TestMousePointerInclusion(mouse_position, event_type)) return this; else return nullptr; } }; NUX_IMPLEMENT_OBJECT_TYPE(PlacesGroup); PlacesGroup::PlacesGroup(dash::StyleInterface& style) : nux::View(NUX_TRACKER_LOCATION), scale(DEFAULT_SCALE), _style(style), _child_layout(nullptr), _child_view(nullptr), _using_filters_background(false), _is_expanded(false), _is_expanded_pushed(false), _n_visible_items_in_unexpand_mode(0), _n_total_items(0), _coverflow_enabled(false), _disabled_header_count(false) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); scale.changed.connect(sigc::mem_fun(this, &PlacesGroup::UpdateScale)); nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; nux::TexCoordXForm texxform; _background_layer.reset(new nux::TextureLayer(_style.GetCategoryBackgroundNoFilters()->GetDeviceTexture(), texxform, nux::color::White, false, rop)); _group_layout = new nux::VLayout("", NUX_TRACKER_LOCATION); // Spacelayout size is updated in UpdatePlacesGroupSize _space_layout = new nux::SpaceLayout(0, 0, 0, 0); _group_layout->AddLayout(_space_layout, 0); _header_view = new HeaderView(NUX_TRACKER_LOCATION); _group_layout->AddView(_header_view, 0, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); _header_layout = new nux::HLayout(NUX_TRACKER_LOCATION); _header_layout->SetLeftAndRightPadding(_style.GetCategoryHeaderLeftPadding().CP(scale), 0); _header_view->SetLayout(_header_layout); _icon = new IconTexture("", _style.GetCategoryIconSize().CP(scale)); _header_layout->AddView(_icon, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX); _text_layout = new nux::HLayout(NUX_TRACKER_LOCATION); _header_layout->AddLayout(_text_layout, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); _name = new StaticCairoText("", NUX_TRACKER_LOCATION); _name->SetFont(NAME_LABEL_FONT); _name->SetLines(-1); _name->SetTextEllipsize(StaticCairoText::NUX_ELLIPSIZE_END); _name->SetTextAlignment(StaticCairoText::NUX_ALIGN_LEFT); _text_layout->AddView(_name, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); _expand_layout = new nux::HLayout(NUX_TRACKER_LOCATION); _text_layout->AddLayout(_expand_layout, 0, nux::MINOR_POSITION_END, nux::MINOR_SIZE_MATCHCONTENT); _expand_label_layout = new nux::HLayout(NUX_TRACKER_LOCATION); _expand_layout->AddLayout(_expand_label_layout, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); _expand_label = new StaticCairoText("", NUX_TRACKER_LOCATION); _expand_label->SetFont(EXPANDER_LABEL_FONT); _expand_label->SetLines(-1); _expand_label->SetTextEllipsize(StaticCairoText::NUX_ELLIPSIZE_END); _expand_label->SetTextAlignment(StaticCairoText::NUX_ALIGN_LEFT); _expand_label->SetTextColor(EXPAND_DEFAULT_TEXT_COLOR); _expand_label_layout->AddView(_expand_label, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX); _expand_icon = new IconTexture(_style.GetGroupExpandIcon()); _expand_icon->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); _expand_icon->SetOpacity(EXPAND_DEFAULT_ICON_OPACITY); _expand_icon->SetVisible(false); _expand_layout->AddView(_expand_icon, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); SetLayout(_group_layout); // don't need to disconnect these signals as they are disconnected when this object destroys the contents _header_view->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick)); _header_view->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged)); _header_view->key_nav_focus_activate.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelActivated)); _icon->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick)); _name->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick)); _expand_label->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick)); _expand_icon->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick)); key_nav_focus_change.connect([this](nux::Area* area, bool has_focus, nux::KeyNavDirection direction) { if (!has_focus) return; if(direction == nux::KEY_NAV_UP) nux::GetWindowCompositor().SetKeyFocusArea(_child_view, direction); else { if (IsExpandable()) nux::GetWindowCompositor().SetKeyFocusArea(GetHeaderFocusableView(), direction); else nux::GetWindowCompositor().SetKeyFocusArea(_child_view, direction); } }); UpdatePlacesGroupSize(); } void PlacesGroup::UpdatePlacesGroupSize() { int icon_size = _style.GetCategoryIconSize().CP(scale); int top_space = _style.GetPlacesGroupTopSpace().CP(scale); _space_layout->SetMinimumSize(top_space, top_space); _space_layout->SetMaximumSize(top_space, top_space); _header_layout->SetSpaceBetweenChildren(SPACE_BETWEEN_CHILDREN.CP(scale())); _header_layout->SetLeftAndRightPadding(_style.GetCategoryHeaderLeftPadding().CP(scale), 0); _icon->SetMinMaxSize(icon_size, icon_size); _text_layout->SetHorizontalInternalMargin(TEXT_INTERNAL_MARGIN.CP(scale())); _expand_layout->SetHorizontalInternalMargin(EXPAND_INTERNAL_MARGIN.CP(scale())); } void PlacesGroup::UpdateScale(double scale) { _name->SetMinimumSize(nux::AREA_MIN_WIDTH, nux::AREA_MIN_HEIGHT); _name->SetMaximumSize(nux::AREA_MAX_WIDTH, nux::AREA_MAX_HEIGHT); _name->SetScale(scale); _expand_label->SetScale(scale); _icon->SetSize(_style.GetCategoryIconSize().CP(scale)); _icon->ReLoadIcon(); auto const& arrow = _expand_icon->texture(); _expand_icon->SetMinMaxSize(RawPixel(arrow->GetWidth()).CP(scale), RawPixel(arrow->GetHeight()).CP(scale)); if (_child_view) _child_view->scale = scale; ComputeContentSize(); UpdatePlacesGroupSize(); UpdateResultViewPadding(); } void PlacesGroup::OnLabelActivated(nux::Area* label) { SetExpanded(!_is_expanded); } void PlacesGroup::OnLabelFocusChanged(nux::Area* label, bool has_focus, nux::KeyNavDirection direction) { if (HeaderHasKeyFocus()) { _ubus.SendMessage(UBUS_RESULT_VIEW_KEYNAV_CHANGED, g_variant_new("(iiii)", 0, 0, 0, 0)); } QueueDraw(); } void PlacesGroup::SetName(std::string const& name) { if (_cached_name != name) { _cached_name = name; _name->SetText(glib::String(g_markup_escape_text(name.c_str(), -1)).Str()); } } void PlacesGroup::SetHeaderCountVisible(bool disable) { _disabled_header_count = !disable; Relayout(); } StaticCairoText* PlacesGroup::GetLabel() { return _name; } StaticCairoText* PlacesGroup::GetExpandLabel() { return _expand_label; } void PlacesGroup::SetIcon(std::string const& path_to_emblem) { _icon->SetByIconName(path_to_emblem, _style.GetCategoryIconSize().CP(scale)); } void PlacesGroup::UpdateResultViewPadding() { if (_child_layout) { _child_layout->SetTopAndBottomPadding(_style.GetPlacesGroupResultTopPadding().CP(scale), 0); _child_layout->SetLeftAndRightPadding(_style.GetPlacesGroupResultLeftPadding().CP(scale), 0); } } void PlacesGroup::SetChildView(dash::ResultView* view) { if (_child_view) { RemoveChild(_child_view); } if (_child_layout != NULL) { _group_layout->RemoveChildObject(_child_layout); } AddChild(view); _child_view = view; _child_view->scale = scale(); _child_layout = new nux::VLayout(); _child_layout->AddView(_child_view, 0); UpdateResultViewPadding(); _group_layout->AddLayout(_child_layout, 1); UpdateVisibleItems(view->results_per_row()); view->results_per_row.changed.connect(sigc::mem_fun(this, &PlacesGroup::UpdateVisibleItems)); QueueDraw(); } void PlacesGroup::UpdateVisibleItems(int visible_items) { _n_visible_items_in_unexpand_mode = visible_items; RefreshLabel(); } dash::ResultView* PlacesGroup::GetChildView() { return _child_view; } void PlacesGroup::SetChildLayout(nux::Layout* layout) { _group_layout->AddLayout(layout, 1); QueueDraw(); } void PlacesGroup::RefreshLabel() { if (_disabled_header_count) { _expand_icon->SetVisible(false); _expand_label->SetVisible(false); return; } std::string result_string; if (_n_visible_items_in_unexpand_mode < _n_total_items) { if (_is_expanded) { result_string = _("See fewer results"); } else { LOG_TRACE(logger) << _n_total_items << " - " << _n_visible_items_in_unexpand_mode; result_string = glib::String(g_strdup_printf(g_dngettext(GETTEXT_PACKAGE, "See one more result", "See %d more results", _n_total_items - _n_visible_items_in_unexpand_mode), _n_total_items - _n_visible_items_in_unexpand_mode)).Str(); } } bool visible = !(_n_visible_items_in_unexpand_mode >= _n_total_items && _n_total_items != 0); _expand_icon->SetVisible(visible); SetName(_cached_name); _expand_label->SetText(result_string); _expand_label->SetVisible(visible); // See bug #748101 ("Dash - "See more..." line should be base-aligned with section header") // We're making two assumptions here: // [a] The font size _name is bigger than the font size of _expand_label // [b] The bottom sides have the same y coordinate int bottom_padding = _name->GetBaseHeight() - _name->GetBaseline() - (_expand_label->GetBaseHeight() - _expand_label->GetBaseline()); _expand_label_layout->SetTopAndBottomPadding(0, bottom_padding); QueueDraw(); } void PlacesGroup::Refresh() { RefreshLabel(); ComputeContentSize(); QueueDraw(); } void PlacesGroup::Relayout() { if (_relayout_idle) return; _relayout_idle.reset(new glib::Idle(glib::Source::Priority::HIGH)); _relayout_idle->Run(sigc::mem_fun(this, &PlacesGroup::OnIdleRelayout)); } bool PlacesGroup::OnIdleRelayout() { if (GetChildView()) { Refresh(); QueueDraw(); _group_layout->QueueDraw(); GetChildView()->QueueDraw(); ComputeContentSize(); _relayout_idle.reset(); } return false; } long PlacesGroup::ComputeContentSize() { long ret = nux::View::ComputeContentSize(); nux::Geometry const& geo = GetGeometry(); // only the width matters if (_cached_geometry.GetWidth() != geo.GetWidth()) { _focus_layer.reset(_style.FocusOverlay(geo.width - HIGHLIGHT_LEFT_PADDING.CP(scale()) - HIGHLIGHT_RIGHT_PADDING.CP(scale()), HIGHLIGHT_HEIGHT.CP(scale()))); _cached_geometry = geo; } return ret; } void PlacesGroup::Draw(nux::GraphicsEngine& graphics_engine, bool forceDraw) { nux::Geometry const& base(GetGeometry()); graphics_engine.PushClippingRectangle(base); if (RedirectedAncestor()) graphics::ClearGeometry(GetGeometry()); if (ShouldBeHighlighted() && _focus_layer) { nux::Geometry geo(_header_layout->GetGeometry()); geo.width = base.width - HIGHLIGHT_RIGHT_PADDING.CP(scale()) - HIGHLIGHT_LEFT_PADDING.CP(scale()); geo.x += HIGHLIGHT_LEFT_PADDING.CP(scale()); _focus_layer->SetGeometry(geo); _focus_layer->Renderlayer(graphics_engine); } if (_background_layer) { nux::Geometry bg_geo = base; int bg_width = _background_layer->GetDeviceTexture()->GetWidth(); bg_geo.x = std::max(bg_geo.width - bg_width, 0); // to render into a space left over by the scrollview (1 has NOT to be scaled) bg_geo.width = std::min(bg_width, bg_geo.GetWidth()) + 1; bg_geo.height = _background_layer->GetDeviceTexture()->GetHeight(); _background_layer->SetGeometry(bg_geo); _background_layer->Renderlayer(graphics_engine); } graphics_engine.PopClippingRectangle(); } void PlacesGroup::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); graphics_engine.PushClippingRectangle(base); int pushed_paint_layers = 0; if (!IsFullRedraw()) { if (RedirectedAncestor()) { // Bit tedious. Need to clear the area of the redirected window taken by views if (_icon->IsRedrawNeeded()) graphics::ClearGeometry(_icon->GetGeometry()); if (_name->IsRedrawNeeded()) graphics::ClearGeometry(_name->GetGeometry()); if (_expand_label->IsRedrawNeeded()) graphics::ClearGeometry(_expand_label->GetGeometry()); if (_expand_icon->IsRedrawNeeded()) graphics::ClearGeometry(_expand_icon->GetGeometry()); if (_child_view && _child_view->IsRedrawNeeded()) graphics::ClearGeometry(_child_view->GetGeometry()); } if (ShouldBeHighlighted() && _focus_layer) { ++pushed_paint_layers; nux::GetPainter().PushLayer(graphics_engine, _focus_layer->GetGeometry(), _focus_layer.get()); } if (_background_layer) { ++pushed_paint_layers; nux::GetPainter().PushLayer(graphics_engine, _background_layer->GetGeometry(), _background_layer.get()); } } else { nux::GetPainter().PushPaintLayerStack(); } _group_layout->ProcessDraw(graphics_engine, force_draw); if (IsFullRedraw()) { nux::GetPainter().PopPaintLayerStack(); } else if (pushed_paint_layers > 0) { nux::GetPainter().PopBackground(pushed_paint_layers); } graphics_engine.PopClippingRectangle(); } void PlacesGroup::SetCounts(unsigned n_total_items) { _n_total_items = n_total_items; Relayout(); } bool PlacesGroup::IsExpandable() const { return (_n_visible_items_in_unexpand_mode < _n_total_items); } bool PlacesGroup::GetExpanded() const { return _is_expanded; } void PlacesGroup::SetExpanded(bool is_expanded) { if (_is_expanded == is_expanded) return; if (is_expanded && _n_total_items <= _n_visible_items_in_unexpand_mode) return; _is_expanded = is_expanded; Refresh(); if (_is_expanded) _expand_icon->SetTexture(_style.GetGroupUnexpandIcon()); else _expand_icon->SetTexture(_style.GetGroupExpandIcon()); auto const& tex = _expand_icon->texture(); _expand_icon->SetMinMaxSize(RawPixel(tex->GetWidth()).CP(scale), RawPixel(tex->GetHeight()).CP(scale)); expanded.emit(this); } void PlacesGroup::PushExpanded() { _is_expanded_pushed = GetExpanded(); } void PlacesGroup::PopExpanded() { SetExpanded(_is_expanded_pushed); } void PlacesGroup::RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags) { SetExpanded(!_is_expanded); } void PlacesGroup::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) { QueueDraw(); } void PlacesGroup::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) { QueueDraw(); } int PlacesGroup::GetHeaderHeight() const { return _header_layout->GetGeometry().height; } bool PlacesGroup::HeaderHasKeyFocus() const { return (_header_view && _header_view->HasKeyFocus()); } bool PlacesGroup::HeaderIsFocusable() const { return (_header_view != nullptr); } nux::View* PlacesGroup::GetHeaderFocusableView() const { return _header_view; } bool PlacesGroup::ShouldBeHighlighted() const { return (HeaderHasKeyFocus() && IsExpandable()); } void PlacesGroup::SetResultsPreviewAnimationValue(float preview_animation) { if (_child_view) _child_view->desaturation_progress = preview_animation; } void PlacesGroup::SetFiltersExpanded(bool filters_expanded) { nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; nux::TexCoordXForm texxform; if (filters_expanded && !_using_filters_background) { _background_layer.reset(new nux::TextureLayer(_style.GetCategoryBackground()->GetDeviceTexture(), texxform, nux::color::White, false, rop)); } else if (!filters_expanded && _using_filters_background) { _background_layer.reset(new nux::TextureLayer(_style.GetCategoryBackgroundNoFilters()->GetDeviceTexture(), texxform, nux::color::White, false, rop)); } _using_filters_background = filters_expanded; QueueDraw(); } // // Key navigation // bool PlacesGroup::AcceptKeyNavFocus() { return true; } // // Introspection // std::string PlacesGroup::GetName() const { return "PlacesGroup"; } void PlacesGroup::AddProperties(debug::IntrospectionData& wrapper) { wrapper.add("header-x", _header_view->GetAbsoluteX()); wrapper.add("header-y", _header_view->GetAbsoluteY()); wrapper.add("header-width", _header_view->GetAbsoluteWidth()); wrapper.add("header-height", _header_view->GetAbsoluteHeight()); wrapper.add("header-geo", _header_view->GetAbsoluteGeometry()); wrapper.add("header-has-keyfocus", HeaderHasKeyFocus()); wrapper.add("header-is-highlighted", ShouldBeHighlighted()); wrapper.add("name", _name->GetText()); wrapper.add("is-visible", IsVisible()); wrapper.add("is-expanded", GetExpanded()); wrapper.add("expand-label-is-visible", _expand_label->IsVisible()); wrapper.add("expand-label-y", _expand_label->GetAbsoluteY()); wrapper.add("expand-label-geo", _expand_label->GetAbsoluteGeometry()); wrapper.add("expand-label-baseline", _expand_label->GetBaseline()); wrapper.add("name-label-y", _name->GetAbsoluteY()); wrapper.add("name-label-baseline", _name->GetBaseline()); wrapper.add("name-label-geo", _name->GetAbsoluteGeometry()); } glib::Variant PlacesGroup::GetCurrentFocus() const { if (_header_view && _header_view->HasKeyFocus()) { return glib::Variant("HeaderView"); } else if (_child_view && _child_view->HasKeyFocus()) { return g_variant_new("(si)", "ResultView", _child_view->GetSelectedIndex()); } return nullptr; } void PlacesGroup::SetCurrentFocus(glib::Variant const& variant) { if (g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) { std::string str = glib::gchar_to_string(g_variant_get_string(variant, NULL)); if (str == "HeaderView" && _header_view) nux::GetWindowCompositor().SetKeyFocusArea(_header_view); } else if (g_variant_is_of_type(variant, G_VARIANT_TYPE("(si)"))) { glib::String str; gint32 index; g_variant_get(variant, "(si)", &str, &index); if (str.Str() == "ResultView" && _child_view) { _child_view->SetSelectedIndex(index); nux::GetWindowCompositor().SetKeyFocusArea(_child_view); } } } } // namespace dash } // namespace unity ./dash/CMakeLists.txt0000644000004100000410000000266413437202764014742 0ustar www-datawww-dataset(UNITY_SRC ../plugins/unityshell/src) set (CFLAGS ${CACHED_UNITY_DEPS_CFLAGS} ${CACHED_UNITY_DEPS_CFLAGS_OTHER} ${PIC_FLAGS} ) string (REPLACE ";" " " CFLAGS "${CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}") set (LIBS ${CACHED_UNITY_DEPS_LDFLAGS} ${UNITY_STANDALONE_LADD}) include_directories (.. ../services ../UnityCore ${UNITY_SRC} ${CMAKE_BINARY_DIR}) add_subdirectory(previews) # # Headers & Sources # set (DASH_SOURCES ApplicationStarterImp.cpp DashController.cpp DashView.cpp DashViewPrivate.cpp FilterAllButton.cpp FilterBar.cpp FilterBasicButton.cpp FilterExpanderLabel.cpp FilterFactory.cpp FilterGenreButton.cpp FilterGenreWidget.cpp FilterMultiRangeButton.cpp FilterMultiRangeWidget.cpp FilterRatingsButton.cpp FilterRatingsWidget.cpp ScopeBar.cpp ScopeBarIcon.cpp ScopeView.cpp PlacesGroup.cpp PreviewStateMachine.cpp ResultRenderer.cpp ResultRendererHorizontalTile.cpp ResultRendererTile.cpp ResultView.cpp ResultViewGrid.cpp ) add_library (dash-lib STATIC ${DASH_SOURCES}) add_dependencies (dash-lib unity-core-${UNITY_API_VERSION} unity-shared) target_link_libraries (dash-lib previews-lib) add_pch(pch/dash_pch.hh dash-lib) # # Standalone variant # add_executable (dash StandaloneDash.cpp) target_link_libraries (dash dash-lib unity-shared unity-shared-standalone) ./dash/ResultView.cpp0000644000004100000410000002451613437202764015017 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "ResultView.h" #include #include #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/GraphicsUtils.h" #include "unity-shared/UnitySettings.h" namespace unity { namespace dash { namespace { double const DEFAULT_SCALE = 1.0; } NUX_IMPLEMENT_OBJECT_TYPE(ResultView); ResultView::ResultView(NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , expanded(true) , desaturation_progress(0.0) , enable_texture_render(false) , scale(DEFAULT_SCALE) , renderer_(NULL) , cached_result_(nullptr, nullptr, nullptr) , default_click_activation_(ActivateType::PREVIEW) { expanded.changed.connect([this](bool value) { QueueRelayout(); NeedRedraw(); }); desaturation_progress.changed.connect([this](float value) { NeedRedraw(); }); default_click_activation.SetGetterFunction([this] { if (Settings::Instance().double_click_activate()) return default_click_activation_; return ActivateType::DIRECT; }); default_click_activation.SetSetterFunction([this] (ActivateType at) { if (default_click_activation_ != at) { default_click_activation_ = at; return true; } return false; }); Settings::Instance().font_scaling.changed.connect(sigc::mem_fun(this, &ResultView::UpdateFontScale)); enable_texture_render.changed.connect(sigc::mem_fun(this, &ResultView::OnEnableRenderToTexture)); scale.changed.connect(sigc::mem_fun(this, &ResultView::UpdateScale)); } ResultView::~ResultView() { for( auto wrapper: introspectable_children_) { delete wrapper.second; } introspectable_children_.clear(); for (ResultIterator it(GetIteratorAtRow(0)); !it.IsLast(); ++it) { renderer_->Unload(*it); } renderer_->UnReference(); } void ResultView::UpdateScale(double scale) { if (renderer_) { renderer_->scale = scale; for (auto const& result : *result_model_) renderer_->ReloadResult(result); QueueDraw(); } } void ResultView::UpdateFontScale(double scale) { if (renderer_) { for (auto const& result : *result_model_) renderer_->ReloadResult(result); QueueDraw(); } } void ResultView::SetModelRenderer(ResultRenderer* renderer) { if (renderer_ != NULL) renderer_->UnReference(); renderer_ = renderer; renderer->NeedsRedraw.connect([this]() { NeedRedraw(); }); renderer_->SinkReference(); NeedRedraw(); } void ResultView::AddResult(Result const& result) { renderer_->Preload(result); NeedRedraw(); } void ResultView::RemoveResult(Result const& result) { renderer_->Unload(result); } void ResultView::SetResultsModel(Results::Ptr const& result_model) { // cleanup if (result_model_) { result_connections_.Clear(); for (ResultIterator it(GetIteratorAtRow(0)); !it.IsLast(); ++it) RemoveResult(*it); } result_model_ = result_model; if (result_model_) { result_connections_.Add(result_model_->result_added.connect(sigc::mem_fun(this, &ResultView::AddResult))); result_connections_.Add(result_model_->result_removed.connect(sigc::mem_fun(this, &ResultView::RemoveResult))); } } int ResultView::GetSelectedIndex() const { return -1; } void ResultView::SetSelectedIndex(int index) { } unsigned ResultView::GetNumResults() { if (result_model_) return result_model_->count(); return 0; } ResultIterator ResultView::GetIteratorAtRow(unsigned row) { DeeModelIter* iter = NULL; if (result_model_) { if (result_model_->model()) { iter = row > 0 ? dee_model_get_iter_at_row(result_model_->model(), row) : dee_model_get_first_iter(result_model_->model()); return ResultIterator(result_model_->model(), iter, result_model_->GetTag()); } } return ResultIterator(glib::Object()); } unsigned int ResultView::GetIndexForLocalResult(LocalResult const& local_result) { unsigned int index = 0; for (ResultIterator it(GetIteratorAtRow(0)); !it.IsLast(); ++it) { if ((*it).uri == local_result.uri) break; index++; } return index; } LocalResult ResultView::GetLocalResultForIndex(unsigned int index) { if (index >= GetNumResults()) return LocalResult(); return LocalResult(*GetIteratorAtRow(index)); } ResultView::ActivateType ResultView::GetLocalResultActivateType(LocalResult const& result) const { if (boost::starts_with(result.uri, "x-unity-no-preview")) return ActivateType::DIRECT; return ActivateType::PREVIEW; } void ResultView::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) {} void ResultView::DrawContent(nux::GraphicsEngine& GfxContent, bool force_draw) { nux::Geometry base = GetGeometry(); GfxContent.PushClippingRectangle(base); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(GfxContent, force_draw); GfxContent.PopClippingRectangle(); } void ResultView::OnEnableRenderToTexture(bool enable_render_to_texture) { if (!enable_render_to_texture) { result_textures_.clear(); } } std::vector const& ResultView::GetResultTextureContainers() { UpdateRenderTextures(); return result_textures_; } void ResultView::RenderResultTexture(ResultViewTexture::Ptr const& result_texture) { // Do we need to re-create the texture? if (!result_texture->texture.IsValid() || result_texture->texture->GetWidth() != GetWidth() || result_texture->texture->GetHeight() != GetHeight()) { result_texture->texture = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture(GetHeight(), GetWidth(), 1, nux::BITFMT_R8G8B8A8); if (!result_texture->texture.IsValid()) return; } nux::GetPainter().PushBackgroundStack(); graphics::PushOffscreenRenderTarget(result_texture->texture); // clear the texture. CHECKGL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); CHECKGL(glClear(GL_COLOR_BUFFER_BIT)); nux::GraphicsEngine& graphics_engine(nux::GetWindowThread()->GetGraphicsEngine()); nux::Geometry offset_rect = graphics_engine.ModelViewXFormRect(GetGeometry()); graphics_engine.PushModelViewMatrix(nux::Matrix4::TRANSLATE(-offset_rect.x, -offset_rect.y, 0)); ProcessDraw(graphics_engine, true); graphics_engine.PopModelViewMatrix(); graphics::PopOffscreenRenderTarget(); nux::GetPainter().PopBackgroundStack(); } void ResultView::UpdateRenderTextures() { if (!enable_texture_render) return; nux::Geometry root_geo(GetAbsoluteGeometry()); if (result_textures_.size() > 0) { ResultViewTexture::Ptr const& result_texture = result_textures_[0]; result_texture->abs_geo.x = root_geo.x; result_texture->abs_geo.y = root_geo.y; result_texture->abs_geo.width = GetWidth(); result_texture->abs_geo.height = GetHeight(); } else { ResultViewTexture::Ptr result_texture(new ResultViewTexture); result_texture->abs_geo = root_geo; result_texture->row_index = 0; result_textures_.push_back(result_texture); } } std::string ResultView::GetName() const { return "ResultView"; } void ResultView::GetResultDimensions(int& rows, int& columns) { columns = results_per_row; rows = result_model_ ? ceil(static_cast(result_model_->count()) / static_cast(std::max(1, columns))) : 0.0; } void ResultView::AddProperties(debug::IntrospectionData& introspection) { introspection .add("expanded", expanded); } debug::Introspectable::IntrospectableList ResultView::GetIntrospectableChildren() { // Because the children are in fact wrappers for the results, we can't just re-crate them every time the // GetIntrospectableChildren is called; otherwise result property introspection will not work correctly (objects change each time this is called). // Therefore, we need to be a bit more clever, and acculumate and cache the wrappers. // clear children (no delete). RemoveAllChildren(); std::set existing_results; // re-create list of children. int index = 0; if (result_model_) { for (ResultIterator iter(result_model_->model()); !iter.IsLast(); ++iter) { Result const& result = *iter; debug::ResultWrapper* result_wrapper = NULL; auto map_iter = introspectable_children_.find(result.uri); // Create new result. if (map_iter == introspectable_children_.end()) { result_wrapper = CreateResultWrapper(result, index); introspectable_children_[result.uri] = result_wrapper; } else { result_wrapper = map_iter->second; UpdateResultWrapper(result_wrapper, result, index); } AddChild(result_wrapper); existing_results.insert(result.uri); index++; } } // Delete old children. auto child_iter = introspectable_children_.begin(); for (; child_iter != introspectable_children_.end(); ) { if (existing_results.find(child_iter->first) == existing_results.end()) { // delete and remove the child from the map. delete child_iter->second; child_iter = introspectable_children_.erase(child_iter); } else { ++child_iter; } } return debug::Introspectable::GetIntrospectableChildren(); } debug::ResultWrapper* ResultView::CreateResultWrapper(Result const& result, int index) { return new debug::ResultWrapper(result); } void ResultView::UpdateResultWrapper(debug::ResultWrapper* wrapper, Result const& result, int index) { } } } ./dash/FilterRatingsWidget.cpp0000644000004100000410000000631213437202764016621 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include #include "config.h" #include #include "unity-shared/DashStyle.h" #include "unity-shared/GraphicsUtils.h" #include "FilterGenreWidget.h" #include "FilterGenreButton.h" #include "FilterBasicButton.h" #include "FilterRatingsButton.h" #include "FilterRatingsWidget.h" namespace unity { namespace dash { namespace { const RawPixel STAR_SIZE = 28_em; } NUX_IMPLEMENT_OBJECT_TYPE(FilterRatingsWidget); FilterRatingsWidget::FilterRatingsWidget(NUX_FILE_LINE_DECL) : FilterExpanderLabel(_("Rating"), NUX_FILE_LINE_PARAM) , all_button_(nullptr) { nux::VLayout* layout = new nux::VLayout(NUX_TRACKER_LOCATION); ratings_ = new FilterRatingsButton(NUX_TRACKER_LOCATION); layout->AddView(ratings_); UpdateSize(); SetContents(layout); scale.changed.connect([this] (double scale) { if (all_button_) all_button_->scale = scale; UpdateSize(); }); } void FilterRatingsWidget::UpdateSize() { dash::Style& style = dash::Style::Instance(); int top_padding = style.GetSpaceBetweenFilterWidgets().CP(scale) - style.GetFilterHighlightPadding().CP(scale) - (1_em).CP(scale); // -1 (PNGs have an 1px top padding) int bottom_padding = style.GetFilterHighlightPadding().CP(scale); static_cast(GetLayout())->SetTopAndBottomPadding(top_padding, bottom_padding); ratings_->scale = scale(); ratings_->SetMinimumHeight(STAR_SIZE.CP(scale)); ratings_->ApplyMinHeight(); } void FilterRatingsWidget::SetFilter(Filter::Ptr const& filter) { filter_ = std::static_pointer_cast(filter); // all button auto show_button_func = [this] (bool show_all_button) { all_button_ = show_all_button ? new FilterAllButton(NUX_TRACKER_LOCATION) : nullptr; SetRightHandView(all_button_); if (all_button_) { all_button_->scale = scale(); all_button_->SetFilter(filter_); } }; show_button_func(filter_->show_all_button); filter_->show_all_button.changed.connect(show_button_func); all_button_->SetFilter(filter_); expanded = !filter_->collapsed(); ratings_->SetFilter(filter_); SetLabel(filter_->name); NeedRedraw(); } std::string FilterRatingsWidget::GetFilterType() { return "FilterRatingsWidget"; } void FilterRatingsWidget::ClearRedirectedRenderChildArea() { if (ratings_->IsRedrawNeeded()) graphics::ClearGeometry(ratings_->GetGeometry()); } } // namespace dash } // namespace unity ./dash/FilterAllButton.cpp0000644000004100000410000000342613437202764015755 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "config.h" #include #include "FilterAllButton.h" namespace unity { namespace dash { NUX_IMPLEMENT_OBJECT_TYPE(FilterAllButton); FilterAllButton::FilterAllButton(NUX_FILE_LINE_DECL) : FilterBasicButton(_("All"), NUX_FILE_LINE_PARAM) { SetActive(true); SetInputEventSensitivity(false); state_change.connect(sigc::mem_fun(this, &FilterAllButton::OnStateChanged)); } void FilterAllButton::SetFilter(Filter::Ptr const& filter) { filter_ = filter; OnFilteringChanged(filter_->filtering); // Evil hack ;) filtering_connection_ = filter_->filtering.changed.connect(sigc::mem_fun(this, &FilterAllButton::OnFilteringChanged)); } void FilterAllButton::OnStateChanged(nux::View* view) { if (filter_ and Active()) filter_->Clear(); QueueDraw(); } void FilterAllButton::OnFilteringChanged(bool filtering) { SetActive(!filtering); SetInputEventSensitivity(filtering); } } // namespace dash } // namespace unity ./dash/FilterGenreWidget.h0000644000004100000410000000361313437202764015720 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERGENREWIDGET_H #define UNITYSHELL_FILTERGENREWIDGET_H #include #include #include #include #include #include "FilterAllButton.h" #include "FilterExpanderLabel.h" namespace unity { namespace dash { class FilterBasicButton; class FilterGenreButton; class FilterGenre : public FilterExpanderLabel { NUX_DECLARE_OBJECT_TYPE(FilterGenre, FilterExpanderLabel); public: FilterGenre(int columns, NUX_FILE_LINE_PROTO); void SetFilter(Filter::Ptr const& filter); std::string GetFilterType(); protected: void InitTheme(); void ClearRedirectedRenderChildArea(); private: void OnOptionAdded(FilterOption::Ptr const& new_filter); void OnOptionRemoved(FilterOption::Ptr const& removed_filter); void UpdateSize(int columns); nux::GridHLayout* genre_layout_; FilterAllButton* all_button_; std::vector buttons_; CheckOptionFilter::Ptr filter_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERGENRESWIDGET_H ./dash/CoverflowResultView.h0000755000004100000410000000302513437202764016346 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Jason Smith */ #ifndef UNITY_COVERFLOWRESULTVIEW_H #define UNITY_COVERFLOWRESULTVIEW_H #include "ResultView.h" namespace unity { namespace dash { class CoverflowResultView : public ResultView { NUX_DECLARE_OBJECT_TYPE(CoverflowResultView, ResultView); public: CoverflowResultView(NUX_FILE_LINE_DECL); ~CoverflowResultView(); virtual void SetModelRenderer(ResultRenderer* renderer); virtual void AddResult(Result& result); virtual void RemoveResult(Result& result); virtual void Activate(LocalResult const& local_result, int index, ActivateType type); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual long ComputeContentSize(); private: struct Impl; Impl* pimpl; }; } } #endif ./dash/DashController.h0000644000004100000410000000617113437202764015273 0ustar www-datawww-data/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_DASH_CONTROLLER_H_ #define UNITY_DASH_CONTROLLER_H_ #include #include #include #include #include #include #include #include #include "DashView.h" #include "unity-shared/Introspectable.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/ResizingBaseWindow.h" namespace unity { namespace dash { class Controller : public unity::debug::Introspectable, public sigc::trackable { public: typedef std::shared_ptr Ptr; typedef std::function WindowCreator; Controller(WindowCreator const& create_window = nullptr); nux::BaseWindow* window() const; bool CheckShortcutActivation(const char* key_string); std::vector GetAllShortcuts(); nux::Property use_primary; sigc::signal on_realize; void HideDash(); void QuicklyHideDash(); bool ShowDash(); void ReFocusKeyInput(); bool IsVisible() const; bool IsCommandLensOpen() const; nux::Geometry GetInputWindowGeometry(); nux::ObjectPtr const& Dash() const; int Monitor() const; protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: void EnsureDash(); void SetupWindow(); void SetupDashView(); void RegisterUBusInterests(); nux::Geometry GetIdealWindowGeometry(); int GetIdealMonitor(); void OnMonitorChanged(int primary, std::vector const&); void UpdateDashPosition(); void Relayout(); void OnMouseDownOutsideWindow(int x, int y, unsigned long bflags, unsigned long kflags); void OnExternalShowDash(GVariant* variant); void OnExternalHideDash(GVariant* variant); void OnActivateRequest(GVariant* variant); void FocusWindow(); void StartShowHideTimeline(); void OnViewShowHideFrame(double progress); static void OnWindowConfigure(int width, int height, nux::Geometry& geo, void* data); private: WindowCreator create_window_; nux::ObjectPtr window_; nux::ObjectPtr view_; int monitor_; bool visible_; connection::Wrapper screen_ungrabbed_slot_; connection::Wrapper form_factor_changed_; glib::DBusServer dbus_server_; glib::TimeoutSeconds ensure_timeout_; glib::Source::UniquePtr grab_wait_; nux::animation::AnimateValue timeline_animator_; UBusManager ubus_manager_; }; } } #endif ./dash/ResultRenderer.cpp0000644000004100000410000001034213437202764015643 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "ResultRenderer.h" #include "unity-shared/RawPixel.h" #include #include #include #include namespace unity { namespace dash { namespace { const std::string DEFAULT_GICON = ". GThemedIcon text-x-preview"; const RawPixel DEFAULT_ICON_SIZE = 64_em; GdkPixbuf* _icon_hint_get_drag_pixbuf(std::string icon_hint, int size) { GdkPixbuf *pbuf; GtkIconTheme *theme; glib::Object info; glib::Error error; glib::Object icon; if (icon_hint.empty()) icon_hint = DEFAULT_GICON; if (g_str_has_prefix(icon_hint.c_str(), "/")) { pbuf = gdk_pixbuf_new_from_file_at_scale (icon_hint.c_str(), size, size, TRUE, &error); if (error || !pbuf || !GDK_IS_PIXBUF (pbuf)) { icon_hint = "application-default-icon"; } else return pbuf; } theme = gtk_icon_theme_get_default(); icon = g_icon_new_for_string(icon_hint.c_str(), NULL); if (icon.IsType(G_TYPE_ICON)) { if (icon.IsType(UNITY_PROTOCOL_TYPE_ANNOTATED_ICON)) { auto anno = glib::object_cast(icon); GIcon *base_icon = unity_protocol_annotated_icon_get_icon(anno); info = gtk_icon_theme_lookup_by_gicon(theme, base_icon, size, GTK_ICON_LOOKUP_FORCE_SIZE); } else { info = gtk_icon_theme_lookup_by_gicon(theme, icon, size, GTK_ICON_LOOKUP_FORCE_SIZE); } } else { info = gtk_icon_theme_lookup_icon(theme, icon_hint.c_str(), size, GTK_ICON_LOOKUP_FORCE_SIZE); } if (!info) { info = gtk_icon_theme_lookup_icon(theme, "application-default-icon", size, GTK_ICON_LOOKUP_FORCE_SIZE); } if (!gtk_icon_info_get_filename(info)) { info = gtk_icon_theme_lookup_icon(theme, "application-default-icon", size, GTK_ICON_LOOKUP_FORCE_SIZE); } pbuf = gtk_icon_info_load_icon(info, &error); if (error) { pbuf = nullptr; } return pbuf; } double const DEFAULT_SCALE = 1.0; } NUX_IMPLEMENT_OBJECT_TYPE(ResultRenderer); ResultRenderer::ResultRenderer(NUX_FILE_LINE_DECL) : InitiallyUnownedObject(NUX_FILE_LINE_PARAM) , width(50) , height(50) , scale(DEFAULT_SCALE) {} void ResultRenderer::Render(nux::GraphicsEngine& GfxContext, Result& /*row*/, ResultRendererState /*state*/, nux::Geometry const& geometry, int /*x_offset*/, int /*y_offset*/, nux::Color const& color, float saturate) { nux::GetPainter().PushDrawSliceScaledTextureLayer(GfxContext, geometry, nux::eBUTTON_NORMAL, nux::color::White, nux::eAllCorners); } void ResultRenderer::Preload(Result const& row) { // pre-load the given row } void ResultRenderer::Unload(Result const& row) { // unload any resources } nux::NBitmapData* ResultRenderer::GetDndImage(Result const& row) const { nux::GdkGraphics graphics(_icon_hint_get_drag_pixbuf(row.icon_hint, DEFAULT_ICON_SIZE.CP(scale))); return graphics.GetBitmap(); } } } ./dash/FilterBar.cpp0000644000004100000410000000762113437202764014556 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include #include #include "unity-shared/DashStyle.h" #include "unity-shared/GraphicsUtils.h" #include "FilterBar.h" #include "FilterExpanderLabel.h" #include "FilterFactory.h" namespace unity { namespace dash { namespace { double const DEFAULT_SCALE = 1.0; } DECLARE_LOGGER(logger, "unity.dash.filterbar"); NUX_IMPLEMENT_OBJECT_TYPE(FilterBar); FilterBar::FilterBar(NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , scale(DEFAULT_SCALE) { SetLayout(new nux::VLayout(NUX_TRACKER_LOCATION)); scale.changed.connect(sigc::mem_fun(this, &FilterBar::UpdateScale)); UpdateScale(scale); } void FilterBar::UpdateScale(double scale) { for (auto& filters : filter_map_) filters.second->scale = scale; auto& style = dash::Style::Instance(); auto* layout = static_cast(GetLayout()); layout->SetTopAndBottomPadding(style.GetFilterBarTopPadding().CP(scale) - style.GetFilterHighlightPadding().CP(scale)); layout->SetSpaceBetweenChildren(style.GetSpaceBetweenFilterWidgets().CP(scale) - style.GetFilterHighlightPadding().CP(scale)); } void FilterBar::SetFilters(Filters::Ptr const& filters) { filters_ = filters; } void FilterBar::AddFilter(Filter::Ptr const& filter) { if (filter_map_.count(filter) > 0) { LOG_WARN(logger) << "Attempting to add a filter that has already been added"; return; } FilterExpanderLabel* filter_view = factory_.WidgetForFilter(filter); filter_view->scale = scale(); AddChild(filter_view); filter_map_[filter] = filter_view; GetLayout()->AddView(filter_view, 0, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); } void FilterBar::RemoveFilter(Filter::Ptr const& filter) { for (auto iter: filter_map_) { if (iter.first->id == filter->id) { FilterExpanderLabel* filter_view = iter.second; RemoveChild(filter_view); filter_map_.erase(filter_map_.find(iter.first)); GetLayout()->RemoveChildObject(filter_view); break; } } } void FilterBar::ClearFilters() { for (auto iter: filter_map_) { FilterExpanderLabel* filter_view = iter.second; RemoveChild(filter_view); GetLayout()->RemoveChildObject(filter_view); } filter_map_.clear(); } void FilterBar::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) {} void FilterBar::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { graphics_engine.PushClippingRectangle(GetGeometry()); if (!IsFullRedraw() && RedirectedAncestor()) { for (auto iter: filter_map_) { FilterExpanderLabel* filter_view = iter.second; if (filter_view && filter_view->IsVisible() && filter_view->IsRedrawNeeded()) graphics::ClearGeometry(filter_view->GetGeometry()); } } GetLayout()->ProcessDraw(graphics_engine, force_draw); graphics_engine.PopClippingRectangle(); } // // Key navigation // bool FilterBar::AcceptKeyNavFocus() { return false; } // // Introspection // std::string FilterBar::GetName() const { return "FilterBar"; } void FilterBar::AddProperties(debug::IntrospectionData& introspection) { introspection.add(GetAbsoluteGeometry()); } } // namespace dash } // namespace unity ./dash/ResultRenderer.h0000644000004100000410000000430013437202764015305 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef RESULTRENDERER_H #define RESULTRENDERER_H #include #include namespace unity { namespace dash { class ResultRenderer : public nux::InitiallyUnownedObject { public: NUX_DECLARE_OBJECT_TYPE(ResultRenderer, nux::InitiallyUnownedObject); enum ResultRendererState { RESULT_RENDERER_NORMAL, RESULT_RENDERER_ACTIVE, RESULT_RENDERER_PRELIGHT, RESULT_RENDERER_SELECTED, RESULT_RENDERER_INSENSITIVE }; ResultRenderer(NUX_FILE_LINE_PROTO); virtual ~ResultRenderer() {} virtual void Render(nux::GraphicsEngine& GfxContext, Result& row, ResultRendererState state, nux::Geometry const& geometry, int x_offset, int y_offset, nux::Color const& color, float saturate); // this is just to start preloading images and text that the renderer might // need - can be ignored virtual void Preload(Result const& row); // unload any previous grabbed images virtual void Unload(Result const& row); virtual void ReloadResult(Result const& row) {} // get a image to drag virtual nux::NBitmapData* GetDndImage(Result const& row) const; nux::Property width; nux::Property height; nux::Property scale; sigc::signal NeedsRedraw; }; } } #endif // RESULTRENDERER_H ./dash/DashView.h0000644000004100000410000001502713437202764014062 0ustar www-datawww-data/* * Copyright (C) 2010-2014 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_DASH_VIEW_H_ #define UNITY_DASH_VIEW_H_ #include #include #include #include #include #include #include #include #include "ScopeBar.h" #include "ScopeView.h" #include "ApplicationStarter.h" #include "previews/PreviewContainer.h" #include "PreviewStateMachine.h" #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/BGHash.h" #include "unity-shared/Introspectable.h" #include "unity-shared/OverlayRenderer.h" #include "unity-shared/SearchBar.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/OverlayWindowButtons.h" namespace na = nux::animation; namespace unity { namespace dash { class DashLayout; class DashView : public nux::View, public unity::debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(DashView, nux::View); typedef std::unordered_map> ScopeViews; public: DashView(Scopes::Ptr const& scopes, ApplicationStarter::Ptr const& application_starter); ~DashView(); nux::Property scale; void AboutToShow(); void AboutToHide(); void Relayout(); void DisableBlur(); void OnActivateRequest(GVariant* args); void SetMonitor(int monitor); void SetMonitorOffset(int x, int y); bool IsCommandLensOpen() const; std::string const GetIdForShortcutActivation(std::string const& shortcut) const; std::vector GetAllShortcuts(); nux::View* default_focus() const; nux::Geometry const& GetContentGeometry() const; protected: void ProcessDndEnter(); virtual Area* FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state); private: void SetupViews(); void SetupUBusConnections(); nux::Geometry GetBestFitGeometry(nux::Geometry const& for_geo); void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw); virtual long PostLayoutManagement (long LayoutResult); nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type); // Dash animations void DrawDashSplit(nux::GraphicsEngine& graphics_engine, nux::Geometry& split_clip); void DrawPreviewContainer(nux::GraphicsEngine& graphics_engine); void DrawPreviewResultTextures(nux::GraphicsEngine& gfx_context, bool force_draw); void DrawPreview(nux::GraphicsEngine& gfx_context, bool force_draw); void StartPreviewAnimation(); void EndPreviewAnimation(); void BuildPreview(Preview::Ptr model); void ClosePreview(); void OnPreviewAnimationFinished(); void OnBackgroundColorChanged(GVariant* args); void OnSearchChanged(std::string const& search_string); void OnLiveSearchReached(std::string const& search_string); void OnScopeAdded(Scope::Ptr const& scope, int position); void OnScopeBarActivated(std::string const& id); void OnScopeSearchFinished(std::string const& scope_id, std::string const& search_string, glib::Error const& err); void OnResultActivated(ResultView::ActivateType type, LocalResult const& local_result, GVariant* data, std::string const& unique_id); void OnResultActivatedReply(LocalResult const& local_result, ScopeHandledType type, glib::HintsMap const& hints); bool DoFallbackActivation(std::string const& uri); bool LaunchApp(std::string const& appname); void OnEntryActivated(); std::string AnalyseScopeURI(std::string const& uri); void UpdateScopeFilter(std::string scope_id, std::string filter, std::string value); void UpdateScopeFilterValue(Filter::Ptr filter, std::string value); bool AcceptKeyNavFocus(); bool InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character); std::string GetName() const; void AddProperties(debug::IntrospectionData&); nux::Geometry GetRenderAbsoluteGeometry() const; void UpdateDashViewSize(); void UpdateScale(double scale); void OnDPIChanged(); nux::Area* KeyNavIteration(nux::KeyNavDirection direction); nux::Area* SkipUnexpandableHeaderKeyNav(); UBusManager ubus_manager_; Scopes::Ptr scopes_; ScopeViews scope_views_; ApplicationStarter::Ptr application_starter_; // View related PreviewStateMachine preview_state_machine_; previews::PreviewContainer::Ptr preview_container_; bool preview_displaying_; std::string stored_activated_unique_id_; dash::previews::Navigation preview_navigation_mode_; nux::VLayout* layout_; DashLayout* content_layout_; nux::View* content_view_; nux::HLayout* search_bar_layout_; SearchBar* search_bar_; nux::VLayout* scopes_layout_; ScopeBar* scope_bar_; nux::SpaceLayout* top_space_; nux::ObjectPtr active_scope_view_; nux::ObjectPtr preview_scope_view_; connection::Wrapper scope_can_refine_connection_; connection::Wrapper key_nav_focus_change_connection_; // Drawing related nux::Geometry content_geo_; OverlayRenderer renderer_; LocalResult last_activated_result_; guint64 last_activated_timestamp_; bool activate_on_finish_; glib::Source::UniquePtr activate_delay_; bool visible_; bool neko_mode_; nux::ObjectPtr dash_view_copy_; nux::ObjectPtr search_view_copy_; nux::ObjectPtr filter_view_copy_; nux::ObjectPtr layout_copy_; int opening_column_x_; int opening_row_y_; int opening_column_width_; int opening_row_height_; std::unique_ptr> split_animation_; float animate_split_value_; std::unique_ptr> preview_container_animation_; float animate_preview_container_value_; std::unique_ptr> preview_animation_; float animate_preview_value_; nux::ObjectPtr overlay_window_buttons_; int monitor_; friend class TestDashView; }; } } #endif ./dash/ResultRendererTile.h0000644000004100000410000000574613437202764016142 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef RESULTRENDERERTILE_H #define RESULTRENDERERTILE_H #include "ResultRenderer.h" #include "unity-shared/IconLoader.h" namespace unity { namespace dash { struct TextureContainer { typedef nux::ObjectPtr BaseTexturePtr; BaseTexturePtr text; BaseTexturePtr icon; BaseTexturePtr prelight; glib::Object drag_icon; int slot_handle; TextureContainer() : slot_handle(0) {} ~TextureContainer() { if (slot_handle > 0) IconLoader::GetDefault().DisconnectHandle(slot_handle); } }; class ResultRendererTile : public ResultRenderer { public: NUX_DECLARE_OBJECT_TYPE(ResultRendererTile, ResultRenderer); ResultRendererTile(NUX_FILE_LINE_PROTO, bool neko_mode = false); virtual ~ResultRendererTile() {} virtual void Render(nux::GraphicsEngine& GfxContext, Result& row, ResultRendererState state, nux::Geometry const& geometry, int x_offset, int y_offset, nux::Color const& color, float saturate); virtual void Preload(Result const& row); virtual void Unload(Result const& row); virtual nux::NBitmapData* GetDndImage(Result const& row) const; void ReloadResult(Result const& row); int Padding() const; protected: virtual void LoadText(Result const& row); void LoadIcon(Result const& row); nux::ObjectPtr prelight_cache_; nux::ObjectPtr normal_cache_; private: //icon loading callbacks void IconLoaded(std::string const& texid, int max_width, int max_height, glib::Object const& pixbuf, std::string const& icon_name, Result const& row); nux::BaseTexture* CreateTextureCallback(std::string const& texid, int width, int height, glib::Object const& pixbuf); nux::BaseTexture* DrawHighlight(std::string const& texid, int width, int height); void UpdateWidthHeight(); bool neko_mode_; }; } } #endif // RESULTRENDERERTILE_H ./dash/FilterBasicButton.h0000644000004100000410000000416513437202764015734 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERBASICBUTTON_H #define UNITYSHELL_FILTERBASICBUTTON_H #include #include #include namespace unity { namespace dash { class FilterBasicButton : public nux::ToggleButton { NUX_DECLARE_OBJECT_TYPE(FilterBasicButton, nux::ToggleButton); public: FilterBasicButton(nux::TextureArea* image, NUX_FILE_LINE_PROTO); FilterBasicButton(std::string const& label, NUX_FILE_LINE_PROTO); FilterBasicButton(std::string const& label, nux::TextureArea* image, NUX_FILE_LINE_PROTO); FilterBasicButton(NUX_FILE_LINE_PROTO); nux::Property scale; std::string const& GetLabel() const; protected: virtual long ComputeContentSize(); virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); void InitTheme(); void SetClearBeforeDraw(bool clear_before_draw); void RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state); void RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr); typedef std::unique_ptr NuxCairoPtr; NuxCairoPtr prelight_; NuxCairoPtr active_; NuxCairoPtr normal_; NuxCairoPtr focus_; nux::Geometry cached_geometry_; private: void UpdateScale(double); std::string label_; bool clear_before_draw_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERBASICBUTTON_H ./dash/ResultViewGrid.cpp0000644000004100000410000010543613437202764015626 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include #include #include #include #include #include #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/Timer.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/GraphicsUtils.h" #include "unity-shared/RawPixel.h" #include "unity-shared/WindowManager.h" #include #include "ResultViewGrid.h" #include "math.h" DECLARE_LOGGER(logger, "unity.dash.results"); namespace unity { namespace dash { namespace { const float UNFOCUSED_GHOST_ICON_OPACITY_REF = 0.3f; const float UNFOCUSED_ICON_SATURATION_REF = 0.05f; const float FOCUSED_GHOST_ICON_OPACITY_REF = 0.7f; const float FOCUSED_ICON_SATURATION_REF = 0.5f; const int DOUBLE_CLICK_SPEED = 500; //500 ms (double-click speed hardcoded to 400 ms in nux) const RawPixel WIDTH_PADDING = 25_em; const RawPixel SCROLLBAR_WIDTH = 3_em; const std::string APPLICATION_URI_PREFIX = "application://"; } NUX_IMPLEMENT_OBJECT_TYPE(ResultViewGrid); ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL) : ResultView(NUX_FILE_LINE_PARAM) , horizontal_spacing(0) , vertical_spacing(0) , padding(0) , mouse_over_index_(-1) , active_index_(-1) , selected_index_(-1) , last_lazy_loaded_result_(0) , all_results_preloaded_(true) , last_mouse_down_x_(-1) , last_mouse_down_y_(-1) , drag_index_(~0) , recorded_dash_width_(-1) , recorded_dash_height_(-1) , mouse_last_x_(-1) , mouse_last_y_(-1) , extra_horizontal_spacing_(0) { EnableDoubleClick(true); SetAcceptKeyNavFocusOnMouseDown(false); auto queue_draw_cb = sigc::hide(sigc::mem_fun(this, &ResultViewGrid::QueueDraw)); horizontal_spacing.changed.connect(queue_draw_cb); vertical_spacing.changed.connect(queue_draw_cb); padding.changed.connect(queue_draw_cb); selected_index_.changed.connect(queue_draw_cb); expanded.changed.connect([this](bool value) { if (value) all_results_preloaded_ = false; }); results_per_row.changed.connect([this](int value) { if (value > 0) all_results_preloaded_ = false; }); scale.changed.connect(sigc::mem_fun(this, &ResultViewGrid::UpdateScale)); key_nav_focus_change.connect(sigc::mem_fun(this, &ResultViewGrid::OnKeyNavFocusChange)); key_nav_focus_activate.connect([this] (nux::Area *area) { Activate(focused_result_, selected_index_, ResultView::ActivateType::DIRECT); }); key_down.connect(sigc::mem_fun(this, &ResultViewGrid::OnKeyDown)); mouse_move.connect(sigc::mem_fun(this, &ResultViewGrid::MouseMove)); mouse_click.connect(sigc::mem_fun(this, &ResultViewGrid::MouseClick)); mouse_double_click.connect(sigc::mem_fun(this, &ResultViewGrid::MouseDoubleClick)); mouse_down.connect([this](int x, int y, unsigned long mouse_state, unsigned long button_state) { last_mouse_down_x_ = x; last_mouse_down_y_ = y; unsigned index = GetIndexAtPosition(x, y); mouse_over_index_ = index; }); mouse_leave.connect([this](int x, int y, unsigned long mouse_state, unsigned long button_state) { mouse_over_index_ = -1; mouse_last_x_ = -1; mouse_last_y_ = -1; NeedRedraw(); }); WindowManager::Default().average_color.changed.connect(queue_draw_cb); ubus_.RegisterInterest(UBUS_DASH_SIZE_CHANGED, [this] (GVariant* data) { // on dash size changed, we update our stored values, this sucks //FIXME in P - make dash size the size of our dash not the entire screen g_variant_get (data, "(ii)", &recorded_dash_width_, &recorded_dash_height_); }); ubus_.RegisterInterest(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, [this] (GVariant* data) { int nav_mode = 0; GVariant* local_result_variant = NULL; glib::String proposed_unique_id; g_variant_get(data, "(ivs)", &nav_mode, &local_result_variant, &proposed_unique_id); LocalResult local_result(LocalResult::FromVariant(local_result_variant)); g_variant_unref(local_result_variant); if (proposed_unique_id.Str() != unique_id()) return; unsigned num_results = GetNumResults(); if (local_result == activated_result_) { int current_index = GetIndexForLocalResult(activated_result_); if (nav_mode == -1) // left { current_index--; } else if (nav_mode == 1) // right { current_index++; } if (current_index < 0 || static_cast(current_index) >= num_results) { LOG_ERROR(logger) << "requested to activated a result that does not exist: " << current_index; return; } // closed if (nav_mode == 0) { activated_result_.clear(); } else { selected_index_ = active_index_ = current_index; activated_result_ = GetLocalResultForIndex(current_index); LOG_DEBUG(logger) << "activating preview for index: " << "(" << current_index << ")" << " " << activated_result_.uri; Activate(activated_result_, current_index, ActivateType::PREVIEW); } } }); SetDndEnabled(true, false); } void ResultViewGrid::Activate(LocalResult const& local_result, int index, ResultView::ActivateType type) { activate_timer_.reset(); unsigned num_results = GetNumResults(); int left_results = index; int right_results = num_results ? (num_results - index) - 1 : 0; //FIXME - just uses y right now, needs to use the absolute position of the bottom of the result // (jay) Here is the fix: Compute the y position of the row where the item is located. nux::Geometry abs_geo = GetAbsoluteGeometry(); int row_y = padding + abs_geo.y; int column_x = padding + abs_geo.x; int row_height = renderer_->height + vertical_spacing; int column_width = renderer_->width + horizontal_spacing; if (GetItemsPerRow()) { int num_results = GetNumResults(); int items_per_row = GetItemsPerRow(); int num_row = num_results / items_per_row; if (num_results % items_per_row) { ++num_row; } int column_index = index % items_per_row; int row_index = index / items_per_row; column_x += column_index * column_width; row_y += row_index * row_height; } if (type == ActivateType::PREVIEW) { if (GetLocalResultActivateType(local_result) != type) type = ActivateType::DIRECT; } active_index_ = index; guint64 timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; glib::Variant data(g_variant_new("(tiiiiii)", timestamp, column_x, row_y, column_width, row_height, left_results, right_results)); ResultActivated.emit(local_result, type, data); } void ResultViewGrid::QueueLazyLoad() { if (all_results_preloaded_ || GetNumResults() == 0) return; if (results_changed_idle_) return; if (!lazy_load_source_) { lazy_load_source_.reset(new glib::Idle(glib::Source::Priority::DEFAULT)); // dont need to reset the last start index as all the previous ones would have been preloaded already. lazy_load_source_->Run(sigc::mem_fun(this, &ResultViewGrid::DoLazyLoad)); } } void ResultViewGrid::QueueResultsChanged() { // even if we're not going to run the lazy load, we need to reset the start in case it's running already. last_lazy_loaded_result_ = 0; if (!results_changed_idle_) { // using glib::Source::Priority::HIGH because this needs to happen *before* next draw results_changed_idle_.reset(new glib::Idle(glib::Source::Priority::HIGH)); results_changed_idle_->Run([this] () { SizeReallocate(); results_changed_idle_.reset(); lazy_load_source_.reset(); // no point doing this one as well. if (!all_results_preloaded_) { last_lazy_loaded_result_ = 0; // reset the lazy load index in case we got an insert DoLazyLoad(); // also calls QueueDraw } return false; }); } } bool ResultViewGrid::DoLazyLoad() { util::Timer timer; bool queue_additional_load = false; // if this is set, we will return early and start loading more next frame // instead we will just pre-load all the items if expanded or just one row if not int index = 0; int items_per_row = GetItemsPerRow(); for (ResultIterator it(GetIteratorAtRow(last_lazy_loaded_result_)); !it.IsLast(); ++it) { if ((!expanded && index < items_per_row) || expanded) { renderer_->Preload(*it); } if (!expanded && index >= items_per_row) break; //early exit if (timer.ElapsedSeconds() > 0.008) { queue_additional_load = true; break; } last_lazy_loaded_result_++; index++; } if (!queue_additional_load) { all_results_preloaded_ = true; lazy_load_source_.reset(); } else if (!lazy_load_source_) { lazy_load_source_.reset(new glib::Idle(glib::Source::Priority::DEFAULT)); lazy_load_source_->Run(sigc::mem_fun(this, &ResultViewGrid::DoLazyLoad)); } QueueDraw(); return queue_additional_load; } int ResultViewGrid::GetItemsPerRow() { int items_per_row = (GetGeometry().width - (padding * 2) + horizontal_spacing) / (renderer_->width + horizontal_spacing); return (items_per_row) ? items_per_row : 1; // always at least one item per row } void ResultViewGrid::GetResultDimensions(int& rows, int& columns) { columns = GetItemsPerRow(); rows = result_model_ ? ceil(static_cast(result_model_->count()) / static_cast(std::max(1, columns))) : 0.0; } void ResultViewGrid::SetModelRenderer(ResultRenderer* renderer) { ResultView::SetModelRenderer(renderer); SizeReallocate(); } void ResultViewGrid::AddResult(Result const& result) { all_results_preloaded_ = false; QueueResultsChanged(); } void ResultViewGrid::RemoveResult(Result const& result) { ResultView::RemoveResult(result); // removing a result might make a non-preloaded one visible all_results_preloaded_ = false; QueueResultsChanged(); } void ResultViewGrid::SizeReallocate() { //FIXME - needs to use the geometry assigned to it, but only after a layout int items_per_row = GetItemsPerRow(); unsigned num_results = GetNumResults(); int total_rows = std::ceil(num_results / (double)items_per_row) ; int total_height = 0; if (expanded) { total_height = (total_rows * renderer_->height) + (total_rows * vertical_spacing); } else { total_height = renderer_->height + vertical_spacing; } int width = (items_per_row * renderer_->width) + (padding*2) + ((items_per_row - 1) * horizontal_spacing); int geo_width = GetBaseWidth(); int extra_width = geo_width - (width + WIDTH_PADDING.CP(scale()) - SCROLLBAR_WIDTH.CP(scale())); if (items_per_row != 1) extra_horizontal_spacing_ = extra_width / (items_per_row - 1); if (extra_horizontal_spacing_ < 0) extra_horizontal_spacing_ = 0; total_height += (padding * 2); // add padding SetMinimumHeight(total_height); SetMaximumHeight(total_height); mouse_over_index_ = GetIndexAtPosition(mouse_last_x_, mouse_last_y_); results_per_row = items_per_row; } bool ResultViewGrid::InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character) { nux::KeyNavDirection direction = nux::KEY_NAV_NONE; switch (keysym) { case NUX_VK_UP: direction = nux::KeyNavDirection::KEY_NAV_UP; break; case NUX_VK_DOWN: direction = nux::KeyNavDirection::KEY_NAV_DOWN; break; case NUX_VK_LEFT: direction = nux::KeyNavDirection::KEY_NAV_LEFT; break; case NUX_VK_RIGHT: direction = nux::KeyNavDirection::KEY_NAV_RIGHT; break; case NUX_VK_LEFT_TAB: direction = nux::KeyNavDirection::KEY_NAV_TAB_PREVIOUS; break; case NUX_VK_TAB: direction = nux::KeyNavDirection::KEY_NAV_TAB_NEXT; break; case NUX_VK_ENTER: case NUX_KP_ENTER: direction = nux::KeyNavDirection::KEY_NAV_ENTER; break; case XK_Menu: return true; default: direction = nux::KeyNavDirection::KEY_NAV_NONE; break; } if (direction == nux::KeyNavDirection::KEY_NAV_NONE || direction == nux::KeyNavDirection::KEY_NAV_TAB_NEXT || direction == nux::KeyNavDirection::KEY_NAV_TAB_PREVIOUS || direction == nux::KeyNavDirection::KEY_NAV_ENTER) { // we don't handle these cases return false; } int items_per_row = GetItemsPerRow(); unsigned num_results = GetNumResults(); int total_rows = std::ceil(num_results / static_cast(items_per_row)); // items per row is always at least 1 total_rows = (expanded) ? total_rows : 1; // restrict to one row if not expanded // check for edge cases where we want the keynav to bubble up if (direction == nux::KEY_NAV_LEFT && (selected_index_ % items_per_row == 0)) return false; // pressed left on the first item, no diiice else if (direction == nux::KEY_NAV_RIGHT && (selected_index_ == static_cast(num_results - 1))) return false; // pressed right on the last item, nope. nothing for you else if (direction == nux::KEY_NAV_RIGHT && (selected_index_ % items_per_row) == (items_per_row - 1)) return false; // pressed right on the last item in the first row in non expanded mode. nothing doing. else if (direction == nux::KEY_NAV_UP && selected_index_ < items_per_row) return false; // key nav up when already on top row else if (direction == nux::KEY_NAV_DOWN && selected_index_ >= (total_rows-1) * items_per_row) return false; // key nav down when on bottom row return true; } bool ResultViewGrid::AcceptKeyNavFocus() { return true; } void ResultViewGrid::OnKeyDown (unsigned long event_type, unsigned long event_keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count) { nux::KeyNavDirection direction = nux::KEY_NAV_NONE; switch (event_keysym) { case NUX_VK_UP: direction = nux::KeyNavDirection::KEY_NAV_UP; break; case NUX_VK_DOWN: direction = nux::KeyNavDirection::KEY_NAV_DOWN; break; case NUX_VK_LEFT: direction = nux::KeyNavDirection::KEY_NAV_LEFT; break; case NUX_VK_RIGHT: direction = nux::KeyNavDirection::KEY_NAV_RIGHT; break; case NUX_VK_LEFT_TAB: direction = nux::KeyNavDirection::KEY_NAV_TAB_PREVIOUS; break; case NUX_VK_TAB: direction = nux::KeyNavDirection::KEY_NAV_TAB_NEXT; break; case NUX_VK_ENTER: case NUX_KP_ENTER: direction = nux::KeyNavDirection::KEY_NAV_ENTER; break; default: direction = nux::KeyNavDirection::KEY_NAV_NONE; break; } // if we got this far, we definately got a keynav signal if (focused_result_.uri.empty()) focused_result_ = (*GetIteratorAtRow(0)); int items_per_row = GetItemsPerRow(); unsigned num_results = GetNumResults(); int total_rows = std::ceil(num_results / static_cast(items_per_row)); // items per row is always at least 1 total_rows = (expanded) ? total_rows : 1; // restrict to one row if not expanded if (direction == nux::KEY_NAV_LEFT && (selected_index_ == 0)) return; // pressed left on the first item, no diiice if (direction == nux::KEY_NAV_RIGHT && (selected_index_ == static_cast(num_results - 1))) return; // pressed right on the last item, nope. nothing for you if (direction == nux::KEY_NAV_RIGHT && !expanded && selected_index_ == items_per_row - 1) return; // pressed right on the last item in the first row in non expanded mode. nothing doing. switch (direction) { case (nux::KEY_NAV_LEFT): { selected_index_ = selected_index_ - 1; break; } case (nux::KEY_NAV_RIGHT): { selected_index_ = selected_index_ + 1; break; } case (nux::KEY_NAV_UP): { selected_index_ = selected_index_ - items_per_row; break; } case (nux::KEY_NAV_DOWN): { selected_index_ = selected_index_ + items_per_row; break; } default: break; } selected_index_ = std::max(0, selected_index_()); selected_index_ = std::min(static_cast(num_results - 1), selected_index_()); ResultIterator iter(GetIteratorAtRow(selected_index_)); focused_result_ = (*iter); std::tuple focused_coord = GetResultPosition(selected_index_); int focused_x = std::get<0>(focused_coord); int focused_y = std::get<1>(focused_coord); ubus_.SendMessage(UBUS_RESULT_VIEW_KEYNAV_CHANGED, g_variant_new("(iiii)", focused_x, focused_y, renderer_->width(), renderer_->height())); selection_change.emit(); if (event_type == nux::NUX_KEYDOWN && event_keysym == XK_Menu) { Activate(focused_result_, selected_index_, ActivateType::PREVIEW); } } nux::Area* ResultViewGrid::KeyNavIteration(nux::KeyNavDirection direction) { return this; } void ResultViewGrid::OnKeyNavFocusChange(nux::Area *area, bool has_focus, nux::KeyNavDirection direction) { if (HasKeyFocus()) { if (result_model_ && selected_index_ < 0 && GetNumResults()) { ResultIterator first_iter(result_model_->model()); focused_result_ = (*first_iter); selected_index_ = 0; } int items_per_row = GetItemsPerRow(); unsigned num_results = GetNumResults(); if (direction == nux::KEY_NAV_UP && expanded) { // This View just got focused through keyboard navigation and the // focus is comming from the bottom. We want to focus the // first item (on the left) of the last row in this grid. int total_rows = std::ceil(num_results / (double)items_per_row); selected_index_ = items_per_row * (total_rows-1); } if (direction != nux::KEY_NAV_NONE) { std::tuple focused_coord = GetResultPosition(selected_index_); int focused_x = std::get<0>(focused_coord); int focused_y = std::get<1>(focused_coord); ubus_.SendMessage(UBUS_RESULT_VIEW_KEYNAV_CHANGED, g_variant_new("(iiii)", focused_x, focused_y, renderer_->width(), renderer_->height())); } selection_change.emit(); } else { selected_index_ = -1; focused_result_.clear(); selection_change.emit(); } } long ResultViewGrid::ComputeContentSize() { SizeReallocate(); QueueLazyLoad(); return ResultView::ComputeContentSize(); } typedef std::tuple ResultListBounds; ResultListBounds ResultViewGrid::GetVisableResults() { int items_per_row = GetItemsPerRow(); unsigned num_results = GetNumResults(); int start, end; if (!expanded) { // we are not expanded, so the bounds are known start = 0; end = items_per_row - 1; } else { //find the row we start at int absolute_y = GetAbsoluteY() - GetToplevel()->GetAbsoluteY(); unsigned row_size = renderer_->height + vertical_spacing; if (absolute_y < 0) { // we are scrolled up a little, int row_index = abs(absolute_y) / row_size; start = row_index * items_per_row; } else { start = 0; } if (absolute_y + GetAbsoluteHeight() > GetToplevel()->GetAbsoluteHeight()) { // our elements overflow the visable viewport int visible_height = (GetToplevel()->GetAbsoluteHeight() - std::max(absolute_y, 0)); visible_height = std::min(visible_height, absolute_y + GetAbsoluteHeight()); int visible_rows = std::ceil(visible_height / static_cast(row_size)); end = start + (visible_rows * items_per_row) + items_per_row; } else { end = num_results - 1; } } start = std::max(start, 0); end = std::min(end, static_cast(num_results - 1)); return ResultListBounds(start, end); } void ResultViewGrid::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { int items_per_row = GetItemsPerRow(); unsigned num_results = GetNumResults(); int total_rows = (!expanded) ? 0 : (num_results / items_per_row) + 1; int row_size = renderer_->height + vertical_spacing; int y_position = padding + GetGeometry().y; ResultListBounds visible_bounds = GetVisableResults(); nux::Geometry absolute_geometry(GetAbsoluteGeometry()); for (int row_index = 0; row_index <= total_rows; row_index++) { DrawRow(GfxContext, visible_bounds, row_index, y_position, absolute_geometry); y_position += row_size; } } void ResultViewGrid::DrawRow(nux::GraphicsEngine& GfxContext, ResultListBounds const& visible_bounds, int row_index, int y_position, nux::Geometry const& absolute_position) { unsigned int current_alpha_blend; unsigned int current_src_blend_factor; unsigned int current_dest_blend_factor; GfxContext.GetRenderStates().GetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); int items_per_row = GetItemsPerRow(); int row_lower_bound = row_index * items_per_row; if (row_lower_bound >= std::get<0>(visible_bounds) && row_lower_bound <= std::get<1>(visible_bounds)) { float saturation_progress = 1.0 - desaturation_progress(); float saturation = 1.0; float opacity = 1.0; int x_position = padding + GetGeometry().x; for (int column_index = 0; column_index < items_per_row; column_index++) { int index = (row_index * items_per_row) + column_index; if (index < 0 || index >= (int)GetNumResults()) break; ResultRenderer::ResultRendererState state = ResultRenderer::RESULT_RENDERER_NORMAL; if (enable_texture_render() == false) { if (index == selected_index_) { state = ResultRenderer::RESULT_RENDERER_SELECTED; } } else if (index == active_index_) { state = ResultRenderer::RESULT_RENDERER_SELECTED; } int half_width = recorded_dash_width_ / 2; int half_height = recorded_dash_height_ / 2; int offset_x, offset_y; /* Guard against divide-by-zero. SIGFPEs are not mythological * contrary to popular belief */ if (half_width >= 10) offset_x = std::max(std::min((x_position - half_width) / (half_width / 10), 5), -5); else offset_x = 0; if (half_height >= 10) offset_y = std::max(std::min(((y_position + absolute_position.y) - half_height) / (half_height / 10), 5), -5); else offset_y = 0; if (recorded_dash_width_ < 1 || recorded_dash_height_ < 1) { offset_x = 0; offset_y = 0; } // Color and saturation if (state == ResultRenderer::RESULT_RENDERER_SELECTED) { saturation = saturation_progress + (1.0-saturation_progress) * FOCUSED_ICON_SATURATION_REF; opacity = saturation_progress + (1.0-saturation_progress) * FOCUSED_GHOST_ICON_OPACITY_REF; } else { saturation = saturation_progress + (1.0-saturation_progress) * UNFOCUSED_ICON_SATURATION_REF; opacity = saturation_progress + (1.0-saturation_progress) * UNFOCUSED_GHOST_ICON_OPACITY_REF; } auto const& bg_color = WindowManager::Default().average_color(); nux::Color tint(opacity + (1.0f-opacity) * bg_color.red, opacity + (1.0f-opacity) * bg_color.green, opacity + (1.0f-opacity) * bg_color.blue, opacity); nux::Geometry render_geo(x_position, y_position, renderer_->width, renderer_->height); Result result(*GetIteratorAtRow(index)); renderer_->Render(GfxContext, result, state, render_geo, offset_x, offset_y, tint, saturation); x_position += renderer_->width + horizontal_spacing + extra_horizontal_spacing_; } } GfxContext.GetRenderStates().SetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); } void ResultViewGrid::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry base = GetGeometry(); GfxContext.PushClippingRectangle(base); if (GetCompositionLayout()) { GetCompositionLayout()->ProcessDraw(GfxContext, force_draw); } GfxContext.PopClippingRectangle(); } void ResultViewGrid::MouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { unsigned index = GetIndexAtPosition(x, y); if (mouse_over_index_ != index) { selected_index_ = mouse_over_index_ = index; nux::GetWindowCompositor().SetKeyFocusArea(this); } mouse_last_x_ = x; mouse_last_y_ = y; } void ResultViewGrid::MouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags) { unsigned num_results = GetNumResults(); unsigned index = GetIndexAtPosition(x, y); mouse_over_index_ = index; if (index < num_results) { // we got a click on a button so activate it ResultIterator it(GetIteratorAtRow(index)); Result result = *it; selected_index_ = index; focused_result_ = result; activated_result_ = result; if (nux::GetEventButton(button_flags) == nux::NUX_MOUSE_BUTTON1) { if (default_click_activation() == ActivateType::PREVIEW && GetLocalResultActivateType(activated_result_) == ActivateType::PREVIEW) { // delay activate for single left click. (for double click check) activate_timer_.reset(new glib::Timeout(DOUBLE_CLICK_SPEED, [this, index] { Activate(activated_result_, index, ActivateType::PREVIEW); return false; })); } else { Activate(activated_result_, index, ActivateType::DIRECT); } } else { Activate(activated_result_, index, ActivateType::PREVIEW); } } } void ResultViewGrid::MouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags) { if (default_click_activation() == ActivateType::DIRECT) return; unsigned num_results = GetNumResults(); unsigned index = GetIndexAtPosition(x, y); mouse_over_index_ = index; if (index < num_results && nux::GetEventButton(button_flags) == nux::NUX_MOUSE_BUTTON1) { // we got a click on a button so activate it ResultIterator it(GetIteratorAtRow(index)); Result result = *it; selected_index_ = index; focused_result_ = result; activated_result_ = result; Activate(activated_result_, index, ActivateType::DIRECT); } } unsigned ResultViewGrid::GetIndexAtPosition(int x, int y) { if (x < 0 || y < 0) return -1; unsigned items_per_row = GetItemsPerRow(); unsigned column_size = renderer_->width + horizontal_spacing + extra_horizontal_spacing_; unsigned row_size = renderer_->height + vertical_spacing; int x_bound = items_per_row * column_size + padding; if ((x < padding) || (x >= x_bound)) return -1; if (y < padding) return -1; unsigned row_number = std::max((y - padding), 0) / row_size ; unsigned column_number = std::max((x - padding), 0) / column_size; return (row_number * items_per_row) + column_number; } std::tuple ResultViewGrid::GetResultPosition(LocalResult const& local_result) { unsigned int index = GetIndexForLocalResult(local_result); return GetResultPosition(index); } std::tuple ResultViewGrid::GetResultPosition(const unsigned int& index) { if (G_UNLIKELY(index >= static_cast(GetNumResults()) || index < 0)) { LOG_ERROR(logger) << "index (" << index << ") does not exist in this category"; return std::tuple(0,0); } // out of bounds. int items_per_row = GetItemsPerRow(); int column_size = renderer_->width + horizontal_spacing + extra_horizontal_spacing_; int row_size = renderer_->height + vertical_spacing; int y = row_size * (index / items_per_row) + padding; int x = column_size * (index % items_per_row) + padding; return std::tuple(x, y); } /* -------- DND code -------- */ bool ResultViewGrid::DndSourceDragBegin() { #ifdef USE_X11 drag_index_ = GetIndexAtPosition(last_mouse_down_x_, last_mouse_down_y_); if (drag_index_ >= GetNumResults()) return false; Reference(); ResultIterator iter(GetIteratorAtRow(drag_index_)); current_drag_result_ = *iter; if (current_drag_result_.empty()) current_drag_result_.uri = current_drag_result_.uri.substr(current_drag_result_.uri.find(":") + 1); if (boost::starts_with(current_drag_result_.uri, APPLICATION_URI_PREFIX)) { auto desktop_id = current_drag_result_.uri.substr(APPLICATION_URI_PREFIX.size()); auto desktop_path = DesktopUtilities::GetDesktopPathById(desktop_id); if (!desktop_path.empty()) current_drag_result_.uri = "file://" + desktop_path; } LOG_DEBUG (logger) << "Dnd begin at " << last_mouse_down_x_ << ", " << last_mouse_down_y_ << " - using; " << current_drag_result_.uri; return true; #else return false; #endif } nux::NBitmapData* ResultViewGrid::DndSourceGetDragImage() { if (drag_index_ >= GetNumResults()) return nullptr; Result result(*GetIteratorAtRow(drag_index_)); return renderer_->GetDndImage(result); } std::list ResultViewGrid::DndSourceGetDragTypes() { std::list result; result.push_back("text/uri-list"); return result; } const char* ResultViewGrid::DndSourceGetDataForType(const char* type, int* size, int* format) { *format = 8; if (!current_drag_result_.empty()) { *size = strlen(current_drag_result_.uri.c_str()); return current_drag_result_.uri.c_str(); } else { *size = 0; return 0; } } void ResultViewGrid::DndSourceDragFinished(nux::DndAction result) { #ifdef USE_X11 UnReference(); last_mouse_down_x_ = -1; last_mouse_down_y_ = -1; current_drag_result_.clear(); drag_index_ = ~0; // We need this because the drag can start in a ResultViewGrid and can // end in another ResultViewGrid EmitMouseLeaveSignal(0, 0, 0, 0); // We need an extra mouse motion to highlight the icon under the mouse // as soon as dnd finish Display* display = nux::GetGraphicsDisplay()->GetX11Display(); if (display) { XWarpPointer(display, None, None, 0, 0, 0, 0, 0, 0); XSync(display, 0); } #endif } int ResultViewGrid::GetSelectedIndex() const { return selected_index_; } void ResultViewGrid::SetSelectedIndex(int index) { unsigned num_results = GetNumResults(); if (num_results == 0) { focused_result_ = LocalResult(); index = -1; } else { if (index >= 0 && (unsigned)index >= num_results) index = num_results-1; ResultIterator iter(GetIteratorAtRow(index)); focused_result_ = (*iter); } selected_index_ = index; } void ResultViewGrid::UpdateScale(double scale) { UpdateRenderTextures(); } void ResultViewGrid::UpdateRenderTextures() { nux::Geometry root_geo(GetAbsoluteGeometry()); int items_per_row = GetItemsPerRow(); unsigned num_results = GetNumResults(); unsigned int total_rows = (!expanded) ? 1 : std::ceil(num_results / (double)items_per_row); int row_height = renderer_->height + vertical_spacing; int cumulative_height = 0; unsigned int row_index = 0; for (; row_index < total_rows; row_index++) { // only one texture for non-expanded. if (!expanded && row_index > 0) break; if (row_index >= result_textures_.size()) { ResultViewTexture::Ptr result_texture(new ResultViewTexture); result_texture->abs_geo.x = root_geo.x; result_texture->abs_geo.y = root_geo.y + cumulative_height; result_texture->abs_geo.width = GetWidth(); result_texture->abs_geo.height = row_height; result_texture->row_index = row_index; result_textures_.push_back(result_texture); } else { ResultViewTexture::Ptr const& result_texture(result_textures_[row_index]); result_texture->abs_geo.x = root_geo.x; result_texture->abs_geo.y = root_geo.y + cumulative_height; result_texture->abs_geo.width = GetWidth(); result_texture->abs_geo.height = row_height; result_texture->row_index = row_index; } cumulative_height += row_height; } // get rid of old textures. for (; row_index < result_textures_.size(); row_index++) { result_textures_.pop_back(); } } void ResultViewGrid::RenderResultTexture(ResultViewTexture::Ptr const& result_texture) { int row_height = renderer_->height + vertical_spacing; // Do we need to re-create the texture? if (!result_texture->texture.IsValid() || result_texture->texture->GetWidth() != GetWidth() || result_texture->texture->GetHeight() != row_height) { result_texture->texture = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture(GetWidth(), row_height, 1, nux::BITFMT_R8G8B8A8); if (!result_texture->texture.IsValid()) return; } ResultListBounds visible_bounds(0, GetNumResults()-1); graphics::PushOffscreenRenderTarget(result_texture->texture); // clear the texture. CHECKGL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); CHECKGL(glClear(GL_COLOR_BUFFER_BIT)); nux::GraphicsEngine& graphics_engine(nux::GetWindowThread()->GetGraphicsEngine()); nux::Geometry offset_rect = graphics_engine.ModelViewXFormRect(GetGeometry()); graphics_engine.PushModelViewMatrix(nux::Matrix4::TRANSLATE(-offset_rect.x, 0, 0)); DrawRow(graphics_engine, visible_bounds, result_texture->row_index, 0, GetAbsoluteGeometry()); graphics_engine.PopModelViewMatrix(); graphics::PopOffscreenRenderTarget(); } debug::ResultWrapper* ResultViewGrid::CreateResultWrapper(Result const& result, int index) { int x_offset = GetAbsoluteX(); int y_offset = GetAbsoluteY(); std::tuple result_coord = GetResultPosition(index); nux::Geometry geo(std::get<0>(result_coord) + x_offset, std::get<1>(result_coord) + y_offset, renderer_->width, renderer_->height); return new debug::ResultWrapper(result, geo); } void ResultViewGrid::UpdateResultWrapper(debug::ResultWrapper* wrapper, Result const& result, int index) { if (!wrapper) return; int x_offset = GetAbsoluteX(); int y_offset = GetAbsoluteY(); std::tuple result_coord = GetResultPosition(index); nux::Geometry geo(std::get<0>(result_coord) + x_offset, std::get<1>(result_coord) + y_offset, renderer_->width, renderer_->height); wrapper->UpdateGeometry(geo); } } } ./dash/StandaloneDash.cpp0000644000004100000410000001105513437202764015570 0ustar www-datawww-data/* * Copyright 2010 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Gordon Allott * Neil Jagdish Patel * */ #include #include "Nux/Nux.h" #include "Nux/NuxTimerTickSource.h" #include "Nux/VLayout.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include #include #include "ApplicationStarterImp.h" #include "unity-shared/BGHash.h" #include "unity-shared/FontSettings.h" #include "DashView.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UBusWrapper.h" #include #include const unity::RawPixel WIDTH(1024); const unity::RawPixel HEIGHT(768); using namespace unity::dash; class TestRunner { public: TestRunner(std::string const& scope, double scale) : scope_(scope.empty() ? "home.scope" : scope) , scale_(scale) {} static void InitWindowThread(nux::NThread* thread, void* InitData); void Init(); std::string scope_; double scale_; nux::Layout *layout; }; void TestRunner::Init () { layout = new nux::HLayout(NUX_TRACKER_LOCATION); DashView* view = new DashView(std::make_shared(), std::make_shared()); view->scale = scale_; view->DisableBlur(); view->SetMinMaxSize(WIDTH.CP(scale_), HEIGHT.CP(scale_)); layout->AddView (view, 1, nux::MINOR_POSITION_CENTER); layout->SetMinMaxSize(WIDTH.CP(scale_), HEIGHT.CP(scale_)); view->AboutToShow(); nux::GetWindowThread()->SetLayout (layout); nux::GetWindowCompositor().SetKeyFocusArea(view->default_focus()); unity::UBusManager::SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", scope_.c_str(), GOTO_DASH_URI, "")); } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init(); } int main(int argc, char **argv) { gtk_init (&argc, &argv); unity::Settings settings; unity::BGHash bghash; unity::FontSettings font_settings; nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. unity::ThumbnailGenerator thumb_generator; unity::dash::Style dash_style; unity::panel::Style panel_style; double scale = 1.0; unity::glib::String scope; unity::glib::Error err; GOptionEntry args_parsed[] = { { "scope", 's', 0, G_OPTION_ARG_STRING, &scope, "The default scope ", "S" }, { "scaling-factor", 'f', 0, G_OPTION_ARG_DOUBLE, &scale, "The dash scaling factor", "F" }, { NULL } }; std::shared_ptr ctx(g_option_context_new("Standalone Dash"), g_option_context_free); g_option_context_add_main_entries(ctx.get(), args_parsed, NULL); if (!g_option_context_parse(ctx.get(), &argc, &argv, &err)) std::cerr << "Got error when parsing arguments: " << err << std::endl; TestRunner *test_runner = new TestRunner(scope.Str(), scale); std::unique_ptr wt(nux::CreateGUIThread(TEXT("Unity Dash"), WIDTH.CP(scale), HEIGHT.CP(scale), 0, &TestRunner::InitWindowThread, test_runner)); nux::ObjectPtr background_tex; background_tex.Adopt(nux::CreateTextureFromFile("/usr/share/backgrounds/warty-final-ubuntu.png")); nux::TexCoordXForm texxform; auto tex_layer = std::make_shared(background_tex->GetDeviceTexture(), texxform, nux::color::White); wt->SetWindowBackgroundPaintLayer(tex_layer.get()); nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); wt->Run(nullptr); return EXIT_SUCCESS; } ./dash/ScopeView.h0000644000004100000410000001441413437202764014253 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2014 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_SCOPE_VIEW_H_ #define UNITY_SCOPE_VIEW_H_ #include #include #include #include #include #include #include #include #include #include "FilterBar.h" #include "unity-shared/Introspectable.h" #include "PlacesGroup.h" #include "ResultViewGrid.h" #include "unity-shared/NuxObjectPtrHash.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/PlacesOverlayVScrollBar.h" namespace unity { namespace dash { class ScopeScrollView; class ScopeView : public nux::View, public unity::debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(ScopeView, nux::View); typedef std::vector CategoryGroups; typedef std::unordered_map ResultCounts; public: ScopeView(Scope::Ptr const& scope, nux::Area* show_filters); CategoryGroups GetOrderedCategoryViews() const; FilterBar* filter_bar() const { return filter_bar_; } Scope::Ptr scope() const; nux::Area* fscroll_view() const; int GetNumRows(); void AboutToShow(); void JumpToTop(); void PerformPageNavigation(ScrollDir dir); virtual void ActivateFirst(); nux::ROProperty search_string; nux::Property filters_expanded; nux::Property view_type; nux::Property can_refine_search; nux::Property scale; nux::Property neko_mode; sigc::signal result_activated; typedef std::function SearchCallback; bool PerformSearch(std::string const& search_query, SearchCallback const& callback); void ForceCategoryExpansion(std::string const& view_id, bool expand); void PushFilterExpansion(bool expand); void PopFilterExpansion(); bool GetPushedFilterExpansion() const; void SetResultsPreviewAnimationValue(float preview_animation); void EnableResultTextures(bool enable_result_textures); std::vector GetResultTextureContainers(); void RenderResultTexture(ResultViewTexture::Ptr const& result_texture); private: void SetupViews(nux::Area* show_filters); void SetupCategories(Categories::Ptr const& categories); void SetupResults(Results::Ptr const& results); void SetupFilters(Filters::Ptr const& filters); void OnCategoryAdded(Category const& category); void OnCategoryChanged(Category const& category); void OnCategoryRemoved(Category const& category); void OnResultAdded(Result const& result); void OnResultRemoved(Result const& result); void OnSearchComplete(std::string const& search_string, glib::HintsMap const& hints, glib::Error const& err); void OnGroupExpanded(PlacesGroup* group); void CheckScrollBarState(); void OnColumnsChanged(); void OnFilterAdded(Filter::Ptr filter); void OnFilterRemoved(Filter::Ptr filter); void OnViewTypeChanged(ScopeViewType view_type); void OnScopeFilterExpanded(bool expanded); void QueueReinitializeFilterCategoryModels(unsigned int index); bool ReinitializeCategoryResultModels(); void ClearCategories(); void OnCategoryOrderChanged(std::vector const& order); void QueueCategoryCountsCheck(); void CheckCategoryCounts(); void CheckNoResults(glib::HintsMap const& hints); void HideResultsMessage(); void PushResultFocus(const char* reason); void PopResultFocus(const char* reason); ResultView* GetResultViewForCategory(unsigned int category_index); virtual PlacesGroup::Ptr CreatePlacesGroup(Category const& category); void BuildPreview(std::string const& uri, Preview::Ptr model); void UpdateScopeViewSize(); void UpdateScale(double scale); virtual void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw); virtual bool AcceptKeyNavFocus(); virtual std::string GetName() const; virtual void AddProperties(debug::IntrospectionData&); void OnCompositorKeyNavFocusChanged(nux::Area*, bool, nux::KeyNavDirection); CategoryGroups category_views_; Scope::Ptr scope_; glib::Cancellable cancellable_; glib::Cancellable search_cancellable_; std::vector category_order_; ResultCounts counts_; bool no_results_active_; std::string search_string_; PlacesGroup::Ptr last_expanded_group_; nux::HLayout* layout_; ScopeScrollView* scroll_view_; nux::VLayout* scroll_layout_; ScopeScrollView* fscroll_view_; nux::VLayout* fscroll_layout_; FilterBar* filter_bar_; StaticCairoText* no_results_; UBusManager ubus_manager_; glib::Source::UniquePtr model_updated_timeout_; int last_good_filter_model_; glib::Source::UniquePtr fix_filter_models_idle_; glib::Source::UniquePtr hide_message_delay_; bool filter_expansion_pushed_; connection::handle result_added_connection_; connection::handle result_removed_connection_; connection::handle category_added_connection_; connection::handle category_changed_connection_; connection::handle category_removed_connection_; connection::handle filter_added_connection_; connection::handle filter_removed_connection_; connection::handle key_nav_focus_connection_; connection::Manager conn_manager_; bool scope_connected_; bool search_on_next_connect_; int current_focus_category_position_; glib::Variant current_focus_variant_; friend class TestScopeView; }; } // namespace dash } // namespace unity #endif // UNITY_SCOPE_VIEW_H_ ./dash/FilterExpanderLabel.h0000644000004100000410000000565413437202764016231 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTEREXPANDERLABEL_H #define UNITYSHELL_FILTEREXPANDERLABEL_H #include #include #include #include #include #include #include #include "unity-shared/IconTexture.h" #include "unity-shared/Introspectable.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/ExpanderView.h" namespace nux { class AbstractPaintLayer; } namespace unity { class HSeparator; namespace dash { class FilterExpanderLabel : public nux::View, public debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(FilterExpanderLabel, nux::View); public: FilterExpanderLabel(std::string const& label, NUX_FILE_LINE_PROTO); void SetRightHandView(nux::View* view); void SetLabel(std::string const& label); void SetContents(nux::Layout* layout); virtual void SetFilter(Filter::Ptr const& filter) = 0; virtual std::string GetFilterType() = 0; ExpanderView* expander_view() const { return expander_view_; } nux::Property scale; nux::Property expanded; protected: nux::Area* FindAreaUnderMouse(const nux::Point&, nux::NuxEventType) override; virtual bool AcceptKeyNavFocus(); virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void ClearRedirectedRenderChildArea() = 0; // Introspection virtual std::string GetName() const; virtual void AddProperties(debug::IntrospectionData&); private: void BuildLayout(); void UpdateLayoutSizes(); void DoExpandChange(bool change); bool ShouldBeHighlighted(); void UpdateScale(double scale); nux::VLayout* layout_; nux::LinearLayout* top_bar_layout_; ExpanderView* expander_view_; nux::LinearLayout* expander_layout_; nux::View* right_hand_contents_; StaticCairoText* cairo_label_; nux::VLayout* arrow_layout_; IconTexture* expand_icon_; nux::ObjectPtr contents_; std::unique_ptr focus_layer_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTEREXPANDERLABEL_H ./dash/CoverflowResultView.cpp0000755000004100000410000001753313437202764016712 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Jason Smith */ #include "CoverflowResultView.h" #include "unity-shared/IconLoader.h" #include "unity-shared/IconTexture.h" #include "unity-shared/DashStyle.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/GraphicsUtils.h" #include #include #include #include #include #include namespace unity { namespace dash { NUX_IMPLEMENT_OBJECT_TYPE(CoverflowResultView); class CoverflowResultItem : public nux::CoverflowItem { public: CoverflowResultItem(Result& result, CoverflowResultView *parent, nux::CoverflowModel::Ptr model); virtual ~CoverflowResultItem(); nux::ObjectPtr GetTexture() const; virtual void Activate(int button); int Index(); std::string Uri(); Result result_; CoverflowResultView* parent_; nux::CoverflowModel::Ptr model_; IconTexture *icon_texture_; UBusManager ubus_; }; class CoverflowResultView::Impl : public sigc::trackable { public: Impl(CoverflowResultView *parent); ~Impl(); void ComputeFlatIcons(); CoverflowResultView *parent_; nux::Coverflow *coverflow_; nux::HLayout* layout_; UBusManager ubus_; }; CoverflowResultItem::CoverflowResultItem(Result& result, CoverflowResultView *parent, nux::CoverflowModel::Ptr model) : CoverflowItem(result.name()) , result_(result) , parent_(parent) , model_(model) { Style& style = Style::Instance(); std::string const& icon_hint = result.icon_hint; std::string icon_name = !icon_hint.empty() ? icon_hint : ". GThemedIcon text-x-preview"; static const int element_size = style.GetTileHeight(); icon_texture_ = new IconTexture(icon_name.c_str(), element_size, true); icon_texture_->SinkReference(); icon_texture_->LoadIcon(); icon_texture_->texture_updated.connect([this] (nux::ObjectPtr const&) { if (parent_) parent_->NeedRedraw(); }); } CoverflowResultItem::~CoverflowResultItem() { icon_texture_->UnReference(); } std::string CoverflowResultItem::Uri() { return result_.uri(); } int CoverflowResultItem::Index() { int i = 0; for (auto item : model_->Items()) { if (this == item.GetPointer()) return i; i++; } return -1; } nux::ObjectPtr CoverflowResultItem::GetTexture() const { return icon_texture_->texture(); } void CoverflowResultItem::Activate(int button) { int index = Index(); int size = model_->Items().size(); glib::Variant data(g_variant_new("(iiii)", 0, 0, index, size - index)); //Left and right click take you to previews. if (button == 1 || button == 3) parent_->Activate(LocalResult(result_), index, ResultView::ActivateType::PREVIEW); //Scroll click opens up music player. else if (button == 2) parent_->Activate(LocalResult(result_), index, ResultView::ActivateType::DIRECT); } CoverflowResultView::Impl::Impl(CoverflowResultView *parent) : parent_(parent) , coverflow_(new nux::Coverflow) , layout_(new nux::HLayout()) { layout_->AddView(coverflow_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); parent_->SetLayout(layout_); coverflow_->SetCameraDistance(1.44); coverflow_->fov = 60; coverflow_->folding_angle = 45; coverflow_->true_perspective = false; coverflow_->camera_motion_drift_enabled = false; coverflow_->show_reflection = true; coverflow_->folding_depth = .25; coverflow_->reflection_strength = .2f; //coverflow_->folding_rate = 3.5; coverflow_->kinetic_scroll_rate = 0.05f; coverflow_->mouse_drag_rate = 1.5f; coverflow_->pinching = 0.2f; coverflow_->y_offset = 0.15f; coverflow_->reflection_size = .5f; ubus_.RegisterInterest(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, [this] (GVariant* data) { int nav_mode = 0; GVariant* local_result_variant = nullptr; glib::String proposed_unique_id; g_variant_get(data, "(ivs)", &nav_mode, &local_result_variant, &proposed_unique_id); LocalResult local_result(LocalResult::FromVariant(local_result_variant)); g_variant_unref(local_result_variant); if (proposed_unique_id.Str() != parent_->unique_id()) return; unsigned num_results = coverflow_->model()->Items().size(); int current_index = parent->GetIndexForLocalResult(local_result); if (nav_mode == -1) // left { current_index--; } else if (nav_mode == 1) // right { current_index++; } if (current_index < 0 || static_cast(current_index) >= num_results) { return; } if (nav_mode) { parent_->Activate(parent_->GetLocalResultForIndex(current_index), current_index, ActivateType::PREVIEW); } }); } CoverflowResultView::Impl::~Impl() { } CoverflowResultView::CoverflowResultView(NUX_FILE_LINE_DECL) : ResultView(NUX_FILE_LINE_PARAM) , pimpl(new CoverflowResultView::Impl(this)) { Style& style = Style::Instance(); SetMinimumHeight(style.GetTileHeight()); } CoverflowResultView::~CoverflowResultView() { } void CoverflowResultView::SetModelRenderer(ResultRenderer* renderer) { return; // abstracting breaks down here. Needs to be reworked. } void CoverflowResultView::AddResult(Result& result) { nux::CoverflowItem::Ptr result_item(new CoverflowResultItem(result, this, pimpl->coverflow_->model())); pimpl->coverflow_->model()->AddItem(result_item); } void CoverflowResultView::RemoveResult(Result& result) { // Ineffecient, API needs to be improved in Coverflow? for (nux::CoverflowItem::Ptr item : pimpl->coverflow_->model()->Items()) { CoverflowResultItem *result_item = static_cast(item.GetPointer()); if (result_item->result_.uri == result.uri) // maybe? { pimpl->coverflow_->model()->RemoveItem(item); break; } } } void CoverflowResultView::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { // do nothing here } void CoverflowResultView::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry base = GetGeometry(); GfxContext.PushClippingRectangle(base); if (RedirectedAncestor()) graphics::ClearGeometry(base); if (GetCompositionLayout()) { GetCompositionLayout()->ProcessDraw(GfxContext, force_draw); } GfxContext.PopClippingRectangle(); } void CoverflowResultView::Impl::ComputeFlatIcons() { float width = coverflow_->ViewportWidthAtDepth(0); width /= coverflow_->space_between_icons(); int flat_icons = static_cast(std::floor((width - 2.0f) / 2.0f)); coverflow_->flat_icons = flat_icons; } long CoverflowResultView::ComputeContentSize() { pimpl->ComputeFlatIcons(); long ret = ResultView::ComputeContentSize(); return ret; } void CoverflowResultView::Activate(LocalResult const& local_result, int index, ResultView::ActivateType type) { unsigned num_results = pimpl->coverflow_->model()->Items().size(); int left_results = index; int right_results = num_results ? (num_results - index) - 1 : 0; int row_y = GetAbsoluteY(); int column_x = -1; int row_height = renderer_->height; int column_width = GetWidth(); glib::Variant data(g_variant_new("(iiii)", column_x, row_y, column_width, row_height, left_results, right_results)); ResultActivated.emit(local_result, type, data); } } } ./dash/DashController.cpp0000644000004100000410000003545013437202764015630 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #include "DashController.h" #include #include #include #include "UnityCore/GSettingsScopes.h" #include "ApplicationStarterImp.h" #include "unity-shared/AnimationUtils.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/UScreen.h" #include "unity-shared/WindowManager.h" namespace unity { namespace dash { DECLARE_LOGGER(logger, "unity.dash.controller"); const char* window_title = "unity-dash"; namespace { const unsigned PRELOAD_TIMEOUT_LENGTH = 40; const unsigned FADE_DURATION = 90; namespace dbus { const std::string BUS_NAME = "com.canonical.Unity"; const std::string PATH = "/com/canonical/Unity/Dash"; const std::string INTROSPECTION =\ "" " " "" " " " " "" " " ""; } } Controller::Controller(Controller::WindowCreator const& create_window) : use_primary(false) , create_window_(create_window) , monitor_(0) , visible_(false) , dbus_server_(dbus::BUS_NAME) , ensure_timeout_(PRELOAD_TIMEOUT_LENGTH) , timeline_animator_(Settings::Instance().low_gfx() ? 0 : FADE_DURATION) { RegisterUBusInterests(); ensure_timeout_.Run([this]() { EnsureDash(); return false; }); timeline_animator_.updated.connect(sigc::mem_fun(this, &Controller::OnViewShowHideFrame)); // As a default. the create_window_ function should just create a base window. if (create_window_ == nullptr) { create_window_ = [this]() { return new ResizingBaseWindow(dash::window_title, [this](nux::Geometry const& geo) { if (view_) return GetInputWindowGeometry(); return geo; }); }; } SetupWindow(); UScreen::GetDefault()->changed.connect(sigc::mem_fun(this, &Controller::OnMonitorChanged)); Settings::Instance().launcher_position.changed.connect(sigc::hide(sigc::mem_fun(this, &Controller::Relayout))); Settings::Instance().low_gfx.changed.connect(sigc::track_obj([this] (bool low_gfx) { timeline_animator_.SetDuration(low_gfx ? 0 : FADE_DURATION); }, *this)); form_factor_changed_ = Settings::Instance().form_factor.changed.connect([this] (FormFactor) { if (window_ && view_ && visible_) { // Relayout here so the input window size updates. Relayout(); window_->PushToFront(); window_->SetInputFocus(); nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus()); } }); auto& wm = WindowManager::Default(); wm.initiate_spread.connect(sigc::mem_fun(this, &Controller::HideDash)); wm.screen_viewport_switch_started.connect(sigc::mem_fun(this, &Controller::HideDash)); dbus_server_.AddObjects(dbus::INTROSPECTION, dbus::PATH); dbus_server_.GetObjects().front()->SetMethodsCallsHandler([this] (std::string const& method, GVariant*) { if (method == "HideDash") HideDash(); return static_cast(nullptr); }); } void Controller::SetupWindow() { window_ = create_window_(); window_->SetBackgroundColor(nux::Color(0.0f, 0.0f, 0.0f, 0.0f)); window_->SetConfigureNotifyCallback(&Controller::OnWindowConfigure, this); window_->ShowWindow(false); window_->SetOpacity(0.0f); window_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow)); if (nux::GetWindowThread()->IsEmbeddedWindow()) { /* FIXME - first time we load our windows there is a race that causes the input * window not to actually get input, this side steps that by causing an input window * show and hide before we really need it. */ WindowManager& wm = WindowManager::Default(); wm.SaveInputFocus(); window_->EnableInputWindow(true, dash::window_title, true, false); window_->EnableInputWindow(false, dash::window_title, true, false); wm.RestoreInputFocus(); } } void Controller::SetupDashView() { view_ = new DashView(std::make_shared(), std::make_shared()); AddChild(view_.GetPointer()); nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION); layout->AddView(view_.GetPointer(), 1); layout->SetContentDistribution(nux::MAJOR_POSITION_START); layout->SetVerticalExternalMargin(0); layout->SetHorizontalExternalMargin(0); window_->SetLayout(layout); window_->UpdateInputWindowGeometry(); } void Controller::RegisterUBusInterests() { ubus_manager_.RegisterInterest(UBUS_DASH_EXTERNAL_ACTIVATION, sigc::mem_fun(this, &Controller::OnExternalShowDash)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_CLOSE_REQUEST, sigc::mem_fun(this, &Controller::OnExternalHideDash)); ubus_manager_.RegisterInterest(UBUS_DASH_ABOUT_TO_SHOW, [this] (GVariant*) { EnsureDash(); }); ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, [this] (GVariant *data) { unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; int width = 0; int height = 0; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); // hide if something else is coming up if (overlay_identity.Str() != "dash") { HideDash(); } }); } void Controller::EnsureDash() { LOG_DEBUG(logger) << "Initializing Dash"; if (!window_) SetupWindow(); if (!view_) { SetupDashView(); Relayout(); ensure_timeout_.Remove(); on_realize.emit(); } } int Controller::Monitor() const { return monitor_; } nux::BaseWindow* Controller::window() const { return window_.GetPointer(); } // We update the @geo that's sent in with our desired width and height void Controller::OnWindowConfigure(int window_width, int window_height, nux::Geometry& geo, void* data) { Controller* self = static_cast(data); geo = self->GetIdealWindowGeometry(); } int Controller::GetIdealMonitor() { UScreen *uscreen = UScreen::GetDefault(); int primary_monitor; if (window_->IsVisible()) primary_monitor = monitor_; else if (use_primary) primary_monitor = uscreen->GetPrimaryMonitor(); else primary_monitor = uscreen->GetMonitorWithMouse(); return primary_monitor; } nux::Geometry Controller::GetIdealWindowGeometry() { UScreen *uscreen = UScreen::GetDefault(); auto monitor_geo = uscreen->GetMonitorGeometry(GetIdealMonitor()); int launcher_size = unity::Settings::Instance().LauncherSize(monitor_); // We want to cover as much of the screen as possible to grab any mouse events outside // of our window if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { monitor_geo.x += launcher_size; monitor_geo.width -= launcher_size; } else { monitor_geo.height -= launcher_size; } return monitor_geo; } void Controller::OnMonitorChanged(int primary, std::vector const& monitors) { if (!visible_ || !window_ || !view_) return; monitor_ = std::min(GetIdealMonitor(), monitors.size()-1); view_->SetMonitor(monitor_); Relayout(); } void Controller::Relayout() { EnsureDash(); view_->Relayout(); window_->SetGeometry(GetIdealWindowGeometry()); UpdateDashPosition(); } void Controller::UpdateDashPosition() { auto launcher_position = Settings::Instance().launcher_position(); int left_offset = 0; int top_offset = panel::Style::Instance().PanelHeight(monitor_); int launcher_size = unity::Settings::Instance().LauncherSize(monitor_); if (launcher_position == LauncherPosition::LEFT) { left_offset = launcher_size; } else if (launcher_position == LauncherPosition::BOTTOM && Settings::Instance().form_factor() == FormFactor::DESKTOP) { auto const& monitor_geo = UScreen::GetDefault()->GetMonitorGeometry(monitor_); top_offset = monitor_geo.height - view_->GetContentGeometry().height - launcher_size; } view_->SetMonitorOffset(left_offset, top_offset); } void Controller::OnMouseDownOutsideWindow(int x, int y, unsigned long bflags, unsigned long kflags) { HideDash(); } void Controller::OnExternalShowDash(GVariant* variant) { EnsureDash(); if (!visible_) ShowDash(); else HideDash(); } void Controller::OnExternalHideDash(GVariant* variant) { HideDash(); } bool Controller::ShowDash() { // Don't want to show at the wrong time if (visible_) return false; WindowManager& wm = WindowManager::Default(); if (wm.IsExpoActive()) wm.TerminateExpo(); // We often need to wait for the mouse/keyboard to be available while a plugin // is finishing it's animations/cleaning up. In these cases, we patiently wait // for the screen to be available again before honouring the request. if (wm.IsScreenGrabbed()) { screen_ungrabbed_slot_ = wm.screen_ungrabbed.connect([this] { grab_wait_.reset(); ShowDash(); }); // Let's wait ungrab event for maximum a couple of seconds... grab_wait_.reset(new glib::TimeoutSeconds(2, [this] { screen_ungrabbed_slot_->disconnect(); return false; })); return false; } screen_ungrabbed_slot_->disconnect(); wm.SaveInputFocus(); EnsureDash(); monitor_ = GetIdealMonitor(); view_->SetMonitor(monitor_); view_->AboutToShow(); UpdateDashPosition(); FocusWindow(); visible_ = true; StartShowHideTimeline(); nux::Geometry const& view_content_geo = view_->GetContentGeometry(); GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, monitor_, view_content_geo.width, view_content_geo.height); ubus_manager_.SendMessage(UBUS_OVERLAY_SHOWN, info); return true; } void Controller::FocusWindow() { window_->ShowWindow(true); window_->PushToFront(); if (nux::GetWindowThread()->IsEmbeddedWindow()) { // in standalone (i.e. not embedded) mode, we do not need an input window. we are one. window_->EnableInputWindow(true, dash::window_title, true, false); // update the input window geometry. This causes the input window to match the actual size of the dash. window_->UpdateInputWindowGeometry(); } window_->SetInputFocus(); window_->QueueDraw(); nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus()); } void Controller::QuicklyHideDash() { HideDash(); timeline_animator_.Stop(); window_->ShowWindow(false); } void Controller::HideDash() { if (!visible_) return; EnsureDash(); view_->AboutToHide(); window_->CaptureMouseDownAnyWhereElse(false); window_->EnableInputWindow(false, dash::window_title, true, false); visible_ = false; auto& wc = nux::GetWindowCompositor(); auto *key_focus_area = wc.GetKeyFocusArea(); if (key_focus_area && key_focus_area->IsChildOf(view_.GetPointer())) wc.SetKeyFocusArea(nullptr, nux::KEY_NAV_NONE); WindowManager::Default().RestoreInputFocus(); StartShowHideTimeline(); nux::Geometry const& view_content_geo = view_->GetContentGeometry(); GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, monitor_, view_content_geo.width, view_content_geo.height); ubus_manager_.SendMessage(UBUS_OVERLAY_HIDDEN, info); } void Controller::StartShowHideTimeline() { EnsureDash(); animation::StartOrReverseIf(timeline_animator_, visible_); } void Controller::OnViewShowHideFrame(double opacity) { window_->SetOpacity(opacity); if (opacity == 0.0f && !visible_) { window_->ShowWindow(false); } } void Controller::OnActivateRequest(GVariant* variant) { EnsureDash(); view_->OnActivateRequest(variant); } bool Controller::CheckShortcutActivation(const char* key_string) { if (!key_string) return false; EnsureDash(); std::string scope_id = view_->GetIdForShortcutActivation(key_string); if (!scope_id.empty()) { WindowManager& wm = WindowManager::Default(); if (wm.IsScaleActive()) wm.TerminateScale(); GVariant* args = g_variant_new("(sus)", scope_id.c_str(), dash::GOTO_DASH_URI, ""); OnActivateRequest(args); g_variant_unref(args); return true; } return false; } std::vector Controller::GetAllShortcuts() { EnsureDash(); return view_->GetAllShortcuts(); } // Introspectable std::string Controller::GetName() const { return "DashController"; } void Controller::AddProperties(debug::IntrospectionData& introspection) { introspection.add("visible", visible_) .add("ideal_monitor", GetIdealMonitor()) .add("monitor", monitor_); } void Controller::ReFocusKeyInput() { if (visible_) { window_->PushToFront(); window_->SetInputFocus(); } } bool Controller::IsVisible() const { return visible_; } bool Controller::IsCommandLensOpen() const { return visible_ && view_->IsCommandLensOpen(); } nux::Geometry Controller::GetInputWindowGeometry() { EnsureDash(); int launcher_size = Settings::Instance().LauncherSize(monitor_); auto const& monitor_geo = UScreen::GetDefault()->GetMonitorGeometry(monitor_); dash::Style& style = dash::Style::Instance(); nux::Geometry const& window_geo(window_->GetGeometry()); nux::Geometry const& view_content_geo(view_->GetContentGeometry()); nux::Geometry geo(window_geo.x, window_geo.y, view_content_geo.width, view_content_geo.height); if (Settings::Instance().form_factor() == FormFactor::DESKTOP) { geo.width += style.GetDashVerticalBorderWidth().CP(view_->scale()); geo.height += style.GetDashHorizontalBorderHeight().CP(view_->scale()); if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) geo.y = monitor_geo.height - view_content_geo.height - launcher_size - style.GetDashHorizontalBorderHeight().CP(view_->scale()); } else if (Settings::Instance().form_factor() == FormFactor::NETBOOK) { geo.height = monitor_geo.height; if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) geo.height -= launcher_size; } return geo; } nux::ObjectPtr const& Controller::Dash() const { return view_; } } } ./dash/ResultView.h0000644000004100000410000000766013437202764014465 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef RESULTVIEW_H #define RESULTVIEW_H #include #include #include #include #include #include #include "unity-shared/Introspectable.h" #include "ResultRenderer.h" namespace unity { namespace debug { class ResultWrapper; } namespace dash { struct ResultViewTexture { typedef std::shared_ptr Ptr; unsigned int category_index; nux::Geometry abs_geo; int row_index; nux::ObjectPtr texture; }; class ResultView : public nux::View, public debug::Introspectable { public: enum class ActivateType { DIRECT, PREVIEW }; NUX_DECLARE_OBJECT_TYPE(ResultView, nux::View); ResultView(NUX_FILE_LINE_DECL); virtual ~ResultView(); void SetModelRenderer(ResultRenderer* renderer); void SetResultsModel(Results::Ptr const& results); unsigned int GetIndexForLocalResult(LocalResult const&); LocalResult GetLocalResultForIndex(unsigned int); ActivateType GetLocalResultActivateType(LocalResult const&) const; nux::Property expanded; nux::Property results_per_row; nux::Property unique_id; nux::Property desaturation_progress; nux::Property enable_texture_render; nux::Property scale; nux::RWProperty default_click_activation; sigc::signal ResultActivated; std::string GetName() const; ResultIterator GetIteratorAtRow(unsigned row); void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); virtual int GetSelectedIndex() const; virtual void SetSelectedIndex(int index); virtual void Activate(LocalResult const& local_result, int index, ActivateType type) = 0; std::vector const& GetResultTextureContainers(); virtual void RenderResultTexture(ResultViewTexture::Ptr const& result_texture); virtual void GetResultDimensions(int& rows, int& columns); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void UpdateRenderTextures(); virtual void AddResult(Result const& result); virtual void RemoveResult(Result const& result); unsigned GetNumResults(); virtual debug::ResultWrapper* CreateResultWrapper(Result const& result, int index); virtual void UpdateResultWrapper(debug::ResultWrapper* wrapper, Result const& result, int index); void OnEnableRenderToTexture(bool enable_render_to_texture); // properties ResultRenderer* renderer_; Results::Ptr result_model_; std::map introspectable_children_; std::vector result_textures_; private: void OnRowAdded(DeeModel* model, DeeModelIter* iter); void OnRowRemoved(DeeModel* model, DeeModelIter* iter); void UpdateScale(double scale); void UpdateFontScale(double scale); Result cached_result_; ActivateType default_click_activation_; connection::Manager result_connections_; }; } } #endif //RESULTVIEW_H ./dash/pch/0000755000004100000410000000000013437202764012744 5ustar www-datawww-data./dash/pch/dash_pch.hh0000644000004100000410000000215413437202764015040 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Jussi Pakkanen */ /* * These are the precompiled header includes for Dash. * Only system header files can be listed here. */ #include #include #include #include #include #include #include #include #include #include ./dash/FilterExpanderLabel.cpp0000644000004100000410000002243313437202764016556 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "unity-shared/DashStyle.h" #include "unity-shared/GraphicsUtils.h" #include "FilterExpanderLabel.h" namespace unity { namespace dash { namespace { const double DEFAULT_SCALE = 1.0; const float EXPAND_DEFAULT_ICON_OPACITY = 1.0f; const RawPixel EXPANDER_LAYOUT_SPACE_BETWEEN_CHILDREN = 8_em; const RawPixel ARROW_HORIZONTAL_PADDING = 2_em; const RawPixel ARROW_TOP_PADDING = 11_em; const RawPixel ARROW_BOTTOM_PADDING = 9_em; // font const char* const FONT_EXPANDER_LABEL = "Ubuntu 13"; // 17px = 13 } NUX_IMPLEMENT_OBJECT_TYPE(FilterExpanderLabel); FilterExpanderLabel::FilterExpanderLabel(std::string const& label, NUX_FILE_LINE_DECL) : nux::View(NUX_FILE_LINE_PARAM) , scale(DEFAULT_SCALE) , expanded(true) , layout_(nullptr) , top_bar_layout_(nullptr) , expander_view_(nullptr) , expander_layout_(nullptr) , right_hand_contents_(nullptr) , cairo_label_(nullptr) { scale.changed.connect(sigc::mem_fun(this, &FilterExpanderLabel::UpdateScale)); expanded.changed.connect(sigc::mem_fun(this, &FilterExpanderLabel::DoExpandChange)); BuildLayout(); } void FilterExpanderLabel::SetLabel(std::string const& label) { cairo_label_->SetText(label); expander_view_->label = label; } void FilterExpanderLabel::UpdateScale(double scale) { cairo_label_->SetScale(scale); UpdateLayoutSizes(); } void FilterExpanderLabel::SetRightHandView(nux::View* view) { dash::Style& style = dash::Style::Instance(); if (right_hand_contents_) { top_bar_layout_->RemoveChildObject(right_hand_contents_); right_hand_contents_ = nullptr; } if (view) { right_hand_contents_ = view; right_hand_contents_->SetMinimumHeight(style.GetAllButtonHeight()); right_hand_contents_->SetMaximumHeight(style.GetAllButtonHeight()); top_bar_layout_->AddView(right_hand_contents_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX); } } void FilterExpanderLabel::SetContents(nux::Layout* contents) { // Since the contents is initially unowned, we don't want to Adopt it, just assign. contents_ = contents; layout_->AddLayout(contents_.GetPointer(), 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); QueueDraw(); } void FilterExpanderLabel::BuildLayout() { layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); top_bar_layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); expander_layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); expander_view_ = new ExpanderView(NUX_TRACKER_LOCATION); expander_view_->expanded = expanded(); expanded.changed.connect([this] (bool expanded) { expander_view_->expanded = expanded; }); expander_view_->SetLayout(expander_layout_); top_bar_layout_->AddView(expander_view_, 1); cairo_label_ = new StaticCairoText("", NUX_TRACKER_LOCATION); cairo_label_->SetFont(FONT_EXPANDER_LABEL); cairo_label_->SetScale(scale); cairo_label_->SetTextColor(nux::color::White); cairo_label_->SetAcceptKeyboardEvent(false); expand_icon_ = new IconTexture(Style::Instance().GetGroupUnexpandIcon()); expand_icon_->SetOpacity(EXPAND_DEFAULT_ICON_OPACITY); expand_icon_->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); expand_icon_->SetVisible(true); arrow_layout_ = new nux::VLayout(); arrow_layout_->AddView(expand_icon_, 0, nux::MINOR_POSITION_CENTER); expander_layout_->AddView(cairo_label_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); expander_layout_->AddView(arrow_layout_, 0, nux::MINOR_POSITION_CENTER); layout_->AddLayout(top_bar_layout_, 0, nux::MINOR_POSITION_START); layout_->SetVerticalInternalMargin(0); SetLayout(layout_); // Lambda functions auto mouse_expand = [this](int x, int y, unsigned long b, unsigned long k) { expanded = !expanded; }; auto key_redraw = [this](nux::Area*, bool, nux::KeyNavDirection) { QueueDraw(); }; auto key_expand = [this](nux::Area*) { expanded = !expanded; }; // Signals expander_view_->mouse_click.connect(mouse_expand); expander_view_->key_nav_focus_change.connect(key_redraw); expander_view_->key_nav_focus_activate.connect(key_expand); cairo_label_->mouse_click.connect(mouse_expand); expand_icon_->mouse_click.connect(mouse_expand); UpdateLayoutSizes(); } void FilterExpanderLabel::UpdateLayoutSizes() { auto& style = dash::Style::Instance(); layout_->SetLeftAndRightPadding(style.GetFilterBarLeftPadding().CP(scale), style.GetFilterBarRightPadding().CP(scale)); top_bar_layout_->SetTopAndBottomPadding(style.GetFilterHighlightPadding().CP(scale)); expander_layout_->SetSpaceBetweenChildren(EXPANDER_LAYOUT_SPACE_BETWEEN_CHILDREN.CP(scale)); auto const& tex = expand_icon_->texture(); expand_icon_->SetMinMaxSize(RawPixel(tex->GetWidth()).CP(scale), RawPixel(tex->GetHeight()).CP(scale)); arrow_layout_->SetLeftAndRightPadding(ARROW_HORIZONTAL_PADDING.CP(scale)); arrow_layout_->SetTopAndBottomPadding(ARROW_TOP_PADDING.CP(scale), ARROW_BOTTOM_PADDING.CP(scale)); QueueRelayout(); QueueDraw(); } void FilterExpanderLabel::DoExpandChange(bool change) { dash::Style& style = dash::Style::Instance(); if (expanded) expand_icon_->SetTexture(style.GetGroupUnexpandIcon()); else expand_icon_->SetTexture(style.GetGroupExpandIcon()); auto const& tex = expand_icon_->texture(); expand_icon_->SetMinMaxSize(RawPixel(tex->GetWidth()).CP(scale), RawPixel(tex->GetHeight()).CP(scale)); if (change and contents_ and !contents_->IsChildOf(layout_)) { layout_->AddLayout(contents_.GetPointer(), 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL, 100.0f, nux::LayoutPosition(1)); } else if (!change and contents_ and contents_->IsChildOf(layout_)) { layout_->RemoveChildObject(contents_.GetPointer()); } layout_->ComputeContentSize(); QueueDraw(); } bool FilterExpanderLabel::ShouldBeHighlighted() { return ((expander_view_ && expander_view_->HasKeyFocus())); } void FilterExpanderLabel::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); graphics_engine.PushClippingRectangle(base); if (ShouldBeHighlighted()) { nux::Geometry geo(top_bar_layout_->GetGeometry()); geo.x = base.x; geo.width = base.width; if (!focus_layer_) focus_layer_.reset(dash::Style::Instance().FocusOverlay(geo.width, geo.height)); focus_layer_->SetGeometry(geo); focus_layer_->Renderlayer(graphics_engine); } graphics_engine.PopClippingRectangle(); } void FilterExpanderLabel::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { graphics_engine.PushClippingRectangle(GetGeometry()); int pushed_paint_layers = 0; if (!IsFullRedraw()) { if (RedirectedAncestor()) { if (cairo_label_->IsRedrawNeeded()) graphics::ClearGeometry(cairo_label_->GetGeometry()); if (expand_icon_->IsRedrawNeeded()) graphics::ClearGeometry(expand_icon_->GetGeometry()); if (right_hand_contents_ && right_hand_contents_->IsRedrawNeeded()) graphics::ClearGeometry(right_hand_contents_->GetGeometry()); if (expanded()) ClearRedirectedRenderChildArea(); } if (focus_layer_ && ShouldBeHighlighted()) { ++pushed_paint_layers; nux::GetPainter().PushLayer(graphics_engine, focus_layer_->GetGeometry(), focus_layer_.get()); } } else { nux::GetPainter().PushPaintLayerStack(); } GetLayout()->ProcessDraw(graphics_engine, force_draw); if (IsFullRedraw()) { nux::GetPainter().PopPaintLayerStack(); } else if (pushed_paint_layers > 0) { nux::GetPainter().PopBackground(pushed_paint_layers); } graphics_engine.PopClippingRectangle(); } nux::Area* FilterExpanderLabel::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) { if (event_type == nux::EVENT_MOUSE_WHEEL) return nullptr; else return nux::View::FindAreaUnderMouse(mouse_position, event_type); } // // Key navigation // bool FilterExpanderLabel::AcceptKeyNavFocus() { return false; } // // Introspection // std::string FilterExpanderLabel::GetName() const { return "FilterExpanderLabel"; } void FilterExpanderLabel::AddProperties(debug::IntrospectionData& introspection) { bool content_has_focus = false; auto focus_area = nux::GetWindowCompositor().GetKeyFocusArea(); if (focus_area && contents_) content_has_focus = focus_area->IsChildOf(contents_.GetPointer()); introspection.add("expander-has-focus", (expander_view_ && expander_view_->HasKeyFocus())) .add("expanded", expanded()) .add(GetAbsoluteGeometry()) .add("content-has-focus", content_has_focus); } } // namespace dash } // namespace unity ./dash/ScopeBarIcon.cpp0000644000004100000410000001000613437202764015202 0ustar www-datawww-data/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #include "unity-shared/DashStyle.h" #include "ScopeBarIcon.h" #include "config.h" namespace unity { namespace dash { namespace { RawPixel const FOCUS_OVERLAY_HEIGHT = 44_em; RawPixel const FOCUS_OVERLAY_WIDTH = 60_em; RawPixel const TEXTURE_SIZE = 24_em; double const DEFAULT_SCALE = 1.0; } NUX_IMPLEMENT_OBJECT_TYPE(ScopeBarIcon); ScopeBarIcon::ScopeBarIcon(std::string id_, std::string icon_hint, std::string name_) : IconTexture(icon_hint, TEXTURE_SIZE) , id(id_) , name(name_) , active(false) , scale(DEFAULT_SCALE) , inactive_opacity_(0.4f) { SetMinMaxSize(FOCUS_OVERLAY_WIDTH.CP(scale()), FOCUS_OVERLAY_HEIGHT.CP(scale())); focus_layer_.reset(Style::Instance().FocusOverlay(FOCUS_OVERLAY_WIDTH.CP(scale()), FOCUS_OVERLAY_HEIGHT.CP(scale()))); SetOpacity(inactive_opacity_); SetAcceptKeyNavFocus(true); SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(true); active.changed.connect(sigc::mem_fun(this, &ScopeBarIcon::OnActiveChanged)); scale.changed.connect(sigc::mem_fun(this, &ScopeBarIcon::UpdateScale)); key_nav_focus_change.connect([this](nux::Area*, bool, nux::KeyNavDirection){ QueueDraw(); }); } void ScopeBarIcon::UpdateScale(double scale) { int overlay_width = FOCUS_OVERLAY_WIDTH.CP(scale); int overlay_height = FOCUS_OVERLAY_HEIGHT.CP(scale); SetMinMaxSize(overlay_width, overlay_height); focus_layer_.reset(Style::Instance().FocusOverlay(overlay_width, overlay_height)); SetSize(TEXTURE_SIZE.CP(scale)); ReLoadIcon(); QueueDraw(); } void ScopeBarIcon::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& geo = GetGeometry(); graphics_engine.PushClippingRectangle(geo); if (HasKeyFocus() && focus_layer_) { nux::Geometry geo(GetGeometry()); nux::AbstractPaintLayer* layer = focus_layer_.get(); layer->SetGeometry(geo); layer->Renderlayer(graphics_engine); } if (texture()) { unsigned int current_alpha_blend; unsigned int current_src_blend_factor; unsigned int current_dest_blend_factor; graphics_engine.GetRenderStates().GetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); graphics_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); float opacity = active ? 1.0f : inactive_opacity_; int width = 0, height = 0; GetTextureSize(&width, &height); nux::TexCoordXForm texxform; texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); texxform.SetWrap(nux::TEXWRAP_CLAMP_TO_BORDER, nux::TEXWRAP_CLAMP_TO_BORDER); graphics_engine.QRP_1Tex(geo.x + ((geo.width - width) / 2), geo.y + ((geo.height - height) / 2), width, height, texture()->GetDeviceTexture(), texxform, nux::color::White * opacity); graphics_engine.GetRenderStates().SetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); } graphics_engine.PopClippingRectangle(); } void ScopeBarIcon::OnActiveChanged(bool is_active) { QueueDraw(); } // Introspectable std::string ScopeBarIcon::GetName() const { return "ScopeBarIcon"; } void ScopeBarIcon::AddProperties(debug::IntrospectionData& wrapper) { wrapper.add(GetAbsoluteGeometry()); wrapper.add("name", id); } } } ./dash/FilterMultiRangeWidget.cpp0000644000004100000410000002707313437202764017270 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include "unity-shared/DashStyle.h" #include "unity-shared/GraphicsUtils.h" #include "FilterMultiRangeWidget.h" #include "FilterMultiRangeButton.h" #include "FilterBasicButton.h" #include #include "config.h" #include namespace unity { namespace dash { NUX_IMPLEMENT_OBJECT_TYPE(FilterMultiRangeWidget); FilterMultiRangeWidget::FilterMultiRangeWidget(NUX_FILE_LINE_DECL) : FilterExpanderLabel(_("Multi-range"), NUX_FILE_LINE_PARAM) , all_button_(nullptr) , dragging_(false) { InitTheme(); dash::Style& style = dash::Style::Instance(); const int left_padding = 0; const int right_padding = 0; const int top_padding = style.GetSpaceBetweenFilterWidgets() - style.GetFilterHighlightPadding() - 2; const int bottom_padding = style.GetFilterHighlightPadding() - 1; layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); layout_->SetLeftAndRightPadding(left_padding, right_padding); layout_->SetTopAndBottomPadding(top_padding, bottom_padding); SetContents(layout_); OnActiveChanged(false); mouse_move.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseMove)); mouse_down.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseDown)); mouse_up.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseUp)); mouse_drag.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseDrag)); scale.changed.connect([this] (double scale) { if (all_button_) all_button_->scale = scale; for (auto const& button : buttons_) button->scale = scale; }); } void FilterMultiRangeWidget::SetFilter(Filter::Ptr const& filter) { // Reset filter. layout_->Clear(); buttons_.clear(); mouse_down_button_.Release(); mouse_down_left_active_button_.Release(); mouse_down_right_active_button_.Release(); filter_ = std::static_pointer_cast(filter); // all button auto show_button_func = [this] (bool show_all_button) { all_button_ = show_all_button ? new FilterAllButton(NUX_TRACKER_LOCATION) : nullptr; SetRightHandView(all_button_); if (all_button_) { all_button_->scale = scale(); all_button_->SetFilter(filter_); } }; show_button_func(filter_->show_all_button); filter_->show_all_button.changed.connect(show_button_func); expanded = !filter_->collapsed(); filter_->option_added.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::OnOptionAdded)); filter_->option_removed.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::OnOptionRemoved)); // finally - make sure we are up-todate with our filter list for (auto it : filter_->options()) OnOptionAdded(it); SetLabel(filter_->name); } void FilterMultiRangeWidget::OnActiveChanged(bool value) { // go through all the buttons, and set the state :( int start = 2000; int end = 0; int index = 0; for (auto button : buttons_) { FilterOption::Ptr filter = button->GetFilter(); if (filter != nullptr) { bool tmp_active = filter->active; button->SetActive(tmp_active); if (filter->active) { if (index < start) start = index; if (index > end) end = index; } } index++; } index = 0; for (auto button : buttons_) { if (index == start && index == end) button->SetHasArrow(MultiRangeArrow::BOTH); else if (index == start) button->SetHasArrow(MultiRangeArrow::LEFT); else if (index == end && index != 0) button->SetHasArrow(MultiRangeArrow::RIGHT); else button->SetHasArrow(MultiRangeArrow::NONE); if (index == 0) button->SetVisualSide(MultiRangeSide::LEFT); else if (index == (int)buttons_.size() - 1) button->SetVisualSide(MultiRangeSide::RIGHT); else button->SetVisualSide(MultiRangeSide::CENTER); index++; } } void FilterMultiRangeWidget::OnOptionAdded(FilterOption::Ptr const& new_filter) { FilterMultiRangeButtonPtr button(new FilterMultiRangeButton(NUX_TRACKER_LOCATION)); button->scale = scale(); button->SetFilter(new_filter); layout_->AddView(button.GetPointer()); buttons_.push_back(button); new_filter->active.changed.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::OnActiveChanged)); OnActiveChanged(false); QueueRelayout(); } void FilterMultiRangeWidget::OnOptionRemoved(FilterOption::Ptr const& removed_filter) { for (auto it=buttons_.begin() ; it != buttons_.end(); it++) { if ((*it)->GetFilter() == removed_filter) { layout_->RemoveChildObject(it->GetPointer()); buttons_.erase(it); break; } } OnActiveChanged(false); QueueRelayout(); } std::string FilterMultiRangeWidget::GetFilterType() { return "FilterMultiRangeWidget"; } void FilterMultiRangeWidget::InitTheme() { //FIXME - build theme here - store images, cache them, fun fun fun } nux::Area* FilterMultiRangeWidget::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) { bool mouse_inside = TestMousePointerInclusionFilterMouseWheel(mouse_position, event_type); if (!mouse_inside) return nullptr; nux::Area* area = View::FindAreaUnderMouse(mouse_position, nux::NUX_MOUSE_MOVE); if (area && area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) { return this; } return area; } void FilterMultiRangeWidget::RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { nux::Geometry geo = GetAbsoluteGeometry(); nux::Point abs_cursor(geo.x + x, geo.y + y); UpdateMouseFocus(abs_cursor); } void FilterMultiRangeWidget::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) { mouse_down_button_.Release(); mouse_down_left_active_button_.Release(); mouse_down_right_active_button_.Release(); dragging_ = false; nux::Geometry geo = GetAbsoluteGeometry(); nux::Point abs_cursor(geo.x + x, geo.y + y); nux::Area* area = View::FindAreaUnderMouse(nux::Point(abs_cursor.x, abs_cursor.y), nux::NUX_MOUSE_PRESSED); if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) return; mouse_down_button_ = static_cast(area); // Cache the left/right selected buttons. FilterMultiRangeButtonPtr last_selected_button; for (FilterMultiRangeButtonPtr button : buttons_) { if (button->Active()) { if (!mouse_down_left_active_button_.IsValid()) mouse_down_left_active_button_ = button; last_selected_button = button; } } mouse_down_right_active_button_ = last_selected_button; } void FilterMultiRangeWidget::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) { FilterMultiRangeButtonPtr mouse_down_button(mouse_down_button_); mouse_down_button_.Release(); if (dragging_) { dragging_ = false; return; } nux::Geometry geo = GetAbsoluteGeometry(); nux::Area* area = View::FindAreaUnderMouse(nux::Point(geo.x + x, geo.y + y), nux::NUX_MOUSE_RELEASED); if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) return; FilterMultiRangeButtonPtr mouse_up_button; mouse_up_button = static_cast(area); if (mouse_up_button == mouse_down_button) Click(mouse_up_button); } void FilterMultiRangeWidget::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { nux::Geometry geo = GetAbsoluteGeometry(); nux::Point abs_cursor(geo.x + x, geo.y + y); UpdateMouseFocus(abs_cursor); if (!CheckDrag()) return; nux::Area* area = View::FindAreaUnderMouse(nux::Point(abs_cursor.x, abs_cursor.y), nux::NUX_MOUSE_MOVE); if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) return; FilterMultiRangeButtonPtr drag_over_button; drag_over_button = static_cast(area); if (!drag_over_button.IsValid()) return; dragging_ = true; auto end = buttons_.end(); int found_buttons = 0; for (auto iter = buttons_.begin(); iter != end; ++iter) { FilterMultiRangeButtonPtr button = *iter; bool activate = false; // if we've dragged the left button, we want to activate everything between the "drag over button" and the "right button" if (mouse_down_button_ == mouse_down_left_active_button_ && button == mouse_down_right_active_button_) { found_buttons++; activate = true; } // if we've dragged the right button, we want to activate everything between the "left button" and the "drag over button" else if (mouse_down_button_ == mouse_down_right_active_button_ && button == mouse_down_left_active_button_) { found_buttons++; activate = true; } if (button == drag_over_button) { found_buttons++; activate = true; } if (activate || (found_buttons > 0 && found_buttons < 2)) { button->Activate(); } else { button->Deactivate(); } } } bool FilterMultiRangeWidget::CheckDrag() { if (!mouse_down_button_) return false; auto end = buttons_.end(); bool between = false; bool active_found = false; for (auto iter = buttons_.begin(); iter != end; ++iter) { FilterMultiRangeButtonPtr button = *iter; if (button->Active()) { active_found = true; if (button == mouse_down_button_) { between = true; } } else if (active_found) { active_found = false; break; } } if (mouse_down_button_ != mouse_down_left_active_button_ && mouse_down_button_ != mouse_down_right_active_button_) { if (between) return false; mouse_down_left_active_button_ = mouse_down_button_; mouse_down_right_active_button_ = mouse_down_button_; } return true; } void FilterMultiRangeWidget::UpdateMouseFocus(nux::Point const& abs_cursor_position) { nux::Area* area = View::FindAreaUnderMouse(nux::Point(abs_cursor_position.x, abs_cursor_position.y), nux::NUX_MOUSE_MOVE); if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) return; nux::GetWindowCompositor().SetKeyFocusArea(static_cast(area), nux::KEY_NAV_NONE); } void FilterMultiRangeWidget::Click(FilterMultiRangeButtonPtr const& activated_button) { bool current_activated = activated_button->Active(); bool any_others_active = false; for (FilterMultiRangeButtonPtr button : buttons_) { if (button != activated_button) { if (button->Active()) any_others_active = true; button->Deactivate(); } } if (!any_others_active && current_activated) activated_button->Deactivate(); else activated_button->Activate(); } void FilterMultiRangeWidget::ClearRedirectedRenderChildArea() { for (auto button : buttons_) { if (button->IsRedrawNeeded()) graphics::ClearGeometry(button->GetGeometry()); } } } // namespace dash } // namespace unity ./dash/ApplicationStarterImp.cpp0000644000004100000410000000373113437202764017160 0ustar www-datawww-data/* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andrea Azzarone */ #include "ApplicationStarterImp.h" #include #include #include #include namespace unity { DECLARE_LOGGER(logger, "unity.applicationstarterimp"); bool ApplicationStarterImp::Launch(std::string const& application_name, Time timestamp) { std::string id = application_name; LOG_DEBUG(logger) << "Launching " << id; GdkDisplay* display = gdk_display_get_default(); glib::Object app_launch_context(gdk_display_get_app_launch_context(display)); if (timestamp > 0) gdk_app_launch_context_set_timestamp(app_launch_context, timestamp); while (true) { glib::Object info(g_desktop_app_info_new(id.c_str())); if (info) { glib::Error error; g_app_info_launch(glib::object_cast(info), nullptr, glib::object_cast(app_launch_context), &error); if (error) LOG_WARNING(logger) << "Unable to launch " << id << ":" << error; else return true; break; } // Try to replace the next - with a / and do the lookup again. auto pos = id.find_first_of('-'); if (pos != std::string::npos) id.replace(pos, 1, "/"); else break; } return false; } } ./dash/ResultRendererHorizontalTile.cpp0000644000004100000410000003320313437202764020534 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "ResultRendererHorizontalTile.h" #include #include #include #include "unity-shared/CairoTexture.h" #include "unity-shared/TextureCache.h" #include "unity-shared/RawPixel.h" #include "unity-shared/UnitySettings.h" #include namespace unity { namespace { const RawPixel CARD_VIEW_PADDING = 4_em; const RawPixel CARD_VIEW_ICON_SIZE = 64_em; const RawPixel CARD_VIEW_ICON_TEXT_GAP = 10_em; const RawPixel CARD_VIEW_WIDTH = 277_em; const RawPixel CARD_VIEW_HEIGHT = 74_em; const RawPixel CARD_VIEW_ICON_OUTLINE_WIDTH = 1_em; const int CARD_VIEW_HIGHLIGHT_CORNER_RADIUS = 2; const int CARD_VIEW_TEXT_LINE_SPACING = 0; void RenderTexture(nux::GraphicsEngine& GfxContext, int x, int y, int width, int height, nux::ObjectPtr const& texture, nux::TexCoordXForm &texxform, const nux::Color &color, float saturate ) { if (saturate == 1.0) { GfxContext.QRP_1Tex(x, y, width, height, texture, texxform, color); } else { GfxContext.QRP_TexDesaturate(x, y, width, height, texture, texxform, color, saturate); } } } namespace dash { NUX_IMPLEMENT_OBJECT_TYPE(ResultRendererHorizontalTile); ResultRendererHorizontalTile::ResultRendererHorizontalTile(NUX_FILE_LINE_DECL) : ResultRendererTile(NUX_FILE_LINE_PARAM) { ReloadTextures(); scale.changed.connect([this] (double) { ReloadTextures(); }); } void ResultRendererHorizontalTile::ReloadTextures() { width = CARD_VIEW_WIDTH.CP(scale()); height = CARD_VIEW_HEIGHT.CP(scale()); // pre-load the highlight texture // try and get a texture from the texture cache TextureCache& cache = TextureCache::GetDefault(); prelight_cache_ = cache.FindTexture("ResultRendererHorizontalTile.PreLightTexture", width, height, sigc::mem_fun(this, &ResultRendererHorizontalTile::DrawHighlight)); normal_cache_ = cache.FindTexture("ResultRendererHorizontalTile.NormalTexture", width, height, sigc::mem_fun(this, &ResultRendererHorizontalTile::DrawNormal)); } void ResultRendererHorizontalTile::Render(nux::GraphicsEngine& GfxContext, Result& row, ResultRendererState state, nux::Geometry const& geometry, int x_offset, int y_offset, nux::Color const& color, float saturate) { TextureContainer* container = row.renderer(); if (container == nullptr) return; // set up our texture mode nux::TexCoordXForm texxform; int icon_left_hand_side = geometry.x + Padding(); int icon_top_side = geometry.y + ((geometry.height - CARD_VIEW_ICON_SIZE.CP(scale())) / 2); // render overall tile background "rectangle" if (state == ResultRendererState::RESULT_RENDERER_NORMAL) { int x = icon_left_hand_side; int y = icon_top_side; int w = CARD_VIEW_WIDTH.CP(scale()); int h = CARD_VIEW_HEIGHT.CP(scale()); unsigned int alpha = 0; unsigned int src = 0; unsigned int dest = 0; GfxContext.GetRenderStates().GetBlend(alpha, src, dest); GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); RenderTexture(GfxContext, x, y, w, h, normal_cache_->GetDeviceTexture(), texxform, color, saturate); GfxContext.GetRenderStates().SetBlend(alpha, src, dest); } // render highlight if its needed if (state != ResultRendererState::RESULT_RENDERER_NORMAL) { int x = icon_left_hand_side; int y = icon_top_side; int w = CARD_VIEW_WIDTH.CP(scale()); int h = CARD_VIEW_HEIGHT.CP(scale()); RenderTexture(GfxContext, x, y, w, h, prelight_cache_->GetDeviceTexture(), texxform, color, saturate); } // draw the icon if (container->icon) { int x = icon_left_hand_side + CARD_VIEW_PADDING.CP(scale()) + CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()); int y = icon_top_side + CARD_VIEW_PADDING.CP(scale()) + CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()); int w = CARD_VIEW_ICON_SIZE.CP(scale()); int h = CARD_VIEW_ICON_SIZE.CP(scale()); gPainter.Paint2DQuadColor(GfxContext, x - CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()), y - CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()), w + 2 * CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()), h + 2 * CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()), nux::color::Black); RenderTexture(GfxContext, x, y, w, h, container->icon->GetDeviceTexture(), texxform, color, saturate); } if (container->text) { int x = icon_left_hand_side + CARD_VIEW_PADDING.CP(scale()) + 2 * CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()) + CARD_VIEW_ICON_SIZE.CP(scale()) + CARD_VIEW_ICON_TEXT_GAP.CP(scale()); int y = icon_top_side + CARD_VIEW_PADDING.CP(scale()); int w = container->text->GetWidth(); int h = container->text->GetHeight(); RenderTexture(GfxContext, x, y, w, h, container->text->GetDeviceTexture(), texxform, color, saturate); } } nux::BaseTexture* ResultRendererHorizontalTile::DrawHighlight(std::string const& texid, int width, int height) { nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, width, height); cairo_surface_set_device_scale(cairo_graphics.GetSurface(), scale(), scale()); cairo_t* cr = cairo_graphics.GetInternalContext(); cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.0); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); // draw the highlight cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 0.2f); cairo_graphics.DrawRoundedRectangle(cr, 1.0f, 0.0f, 0.0f, CARD_VIEW_HIGHLIGHT_CORNER_RADIUS, width/scale(), height/scale(), false); cairo_fill(cr); return texture_from_cairo_graphics(cairo_graphics); } nux::BaseTexture* ResultRendererHorizontalTile::DrawNormal(std::string const& texid, int width, int height) { nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, width, height); cairo_surface_set_device_scale(cairo_graphics.GetSurface(), scale(), scale()); cairo_t* cr = cairo_graphics.GetInternalContext(); cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.0); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); // draw the normal bg cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.075f); cairo_graphics.DrawRoundedRectangle(cr, 1.0f, 0.0f, 0.0f, CARD_VIEW_HIGHLIGHT_CORNER_RADIUS, width/scale(), height/scale(), false); cairo_fill(cr); return texture_from_cairo_graphics(cairo_graphics); } void ResultRendererHorizontalTile::LoadText(Result const& row) { std::stringstream final_text; char *name = g_markup_escape_text(row.name().c_str() , -1); char *comment = g_markup_escape_text(row.comment().c_str() , -1); if(row.comment().empty()) final_text << "" << name << ""; else final_text << "" << name << "" << "\n" << comment; g_free(name); g_free(comment); nux::CairoGraphics _cairoGraphics(CAIRO_FORMAT_ARGB32, CARD_VIEW_WIDTH.CP(scale()) - CARD_VIEW_ICON_SIZE.CP(scale()) - 2 * CARD_VIEW_ICON_OUTLINE_WIDTH.CP(scale()) - 2 * CARD_VIEW_PADDING.CP(scale()) - CARD_VIEW_ICON_TEXT_GAP.CP(scale()), CARD_VIEW_HEIGHT.CP(scale()) - 2 * CARD_VIEW_PADDING.CP(scale())); cairo_surface_set_device_scale(_cairoGraphics.GetSurface(), scale(), scale()); cairo_t* cr = _cairoGraphics.GetInternalContext(); PangoLayout* layout = NULL; PangoFontDescription* desc = NULL; PangoContext* pango_context = NULL; GdkScreen* screen = gdk_screen_get_default(); // not ref'ed cairo_set_font_options(cr, gdk_screen_get_font_options(screen)); layout = pango_cairo_create_layout(cr); desc = pango_font_description_from_string("Ubuntu 10"); pango_layout_set_font_description(layout, desc); pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); pango_layout_set_spacing(layout, CARD_VIEW_TEXT_LINE_SPACING * PANGO_SCALE); pango_layout_set_width(layout, (CARD_VIEW_WIDTH - CARD_VIEW_ICON_SIZE - 2 * CARD_VIEW_ICON_OUTLINE_WIDTH - 2 * CARD_VIEW_PADDING - CARD_VIEW_ICON_TEXT_GAP) * PANGO_SCALE); pango_layout_set_height(layout, -4); pango_layout_set_markup(layout, final_text.str().c_str(), -1); pango_context = pango_layout_get_context(layout); // is not ref'ed pango_cairo_context_set_font_options(pango_context, gdk_screen_get_font_options(screen)); pango_cairo_context_set_resolution(pango_context, 96.0 * Settings::Instance().font_scaling()); pango_layout_context_changed(layout); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); double offset = 0.0; PangoRectangle logRect = {0, 0, 0, 0}; pango_layout_get_extents(layout, NULL, &logRect); if (pango_layout_get_line_count(layout) < 4) offset = ((CARD_VIEW_HEIGHT - 2 * CARD_VIEW_PADDING) - (logRect.height / PANGO_SCALE)) / 2.0; cairo_move_to(cr, 0.0f, offset); pango_cairo_show_layout(cr, layout); // clean up pango_font_description_free(desc); g_object_unref(layout); TextureContainer *container = row.renderer(); if (container) container->text = texture_ptr_from_cairo_graphics(_cairoGraphics); } nux::NBitmapData* ResultRendererHorizontalTile::GetDndImage(Result const& row) const { TextureContainer* container = row.renderer(); nux::NBitmapData* bitmap = nullptr; if (container && container->drag_icon && container->drag_icon.IsType(GDK_TYPE_PIXBUF)) { int width = gdk_pixbuf_get_width(container->drag_icon); int height = gdk_pixbuf_get_height(container->drag_icon); if (width != CARD_VIEW_ICON_SIZE.CP(scale()) || height != CARD_VIEW_ICON_SIZE.CP(scale())) { nux::GdkGraphics graphics(gdk_pixbuf_scale_simple(container->drag_icon, CARD_VIEW_ICON_SIZE.CP(scale()), CARD_VIEW_ICON_SIZE.CP(scale()), GDK_INTERP_BILINEAR)); bitmap = graphics.GetBitmap(); } } return bitmap ? bitmap : ResultRendererTile::GetDndImage(row); } } } ./dash/FilterGenreWidget.cpp0000644000004100000410000001136213437202764016253 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include "config.h" #include #include #include "unity-shared/DashStyle.h" #include "unity-shared/GraphicsUtils.h" #include "FilterGenreWidget.h" #include "FilterGenreButton.h" #include "FilterBasicButton.h" namespace unity { namespace dash { namespace { const RawPixel CHILDREN_SPACE = 12_em; const RawPixel CHILDREN_SPACE_SMALLER = 10_em; } NUX_IMPLEMENT_OBJECT_TYPE(FilterGenre); FilterGenre::FilterGenre(int columns, NUX_FILE_LINE_DECL) : FilterExpanderLabel(_("Categories"), NUX_FILE_LINE_PARAM) , all_button_(nullptr) { InitTheme(); genre_layout_ = new nux::GridHLayout(NUX_TRACKER_LOCATION); genre_layout_->ForceChildrenSize(true); genre_layout_->MatchContentSize(true); genre_layout_->EnablePartialVisibility(false); UpdateSize(columns); SetContents(genre_layout_); scale.changed.connect([this, columns] (double scale) { if (all_button_) all_button_->scale = scale; for (auto* button : buttons_) button->scale = scale; UpdateSize(columns); }); } void FilterGenre::UpdateSize(int columns) { auto& style = dash::Style::Instance(); genre_layout_->SetTopAndBottomPadding(style.GetSpaceBetweenFilterWidgets().CP(scale) - style.GetFilterHighlightPadding().CP(scale), style.GetFilterHighlightPadding().CP(scale)); if (columns == 3) { genre_layout_->SetChildrenSize((style.GetFilterBarWidth().CP(scale) - CHILDREN_SPACE.CP(scale) * 2) / 3, style.GetFilterButtonHeight().CP(scale)); genre_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale), CHILDREN_SPACE.CP(scale)); } else { genre_layout_->SetChildrenSize((style.GetFilterBarWidth().CP(scale) - CHILDREN_SPACE_SMALLER.CP(scale)) / 2, style.GetFilterButtonHeight().CP(scale)); genre_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE_SMALLER.CP(scale), CHILDREN_SPACE.CP(scale)); } } void FilterGenre::SetFilter(Filter::Ptr const& filter) { filter_ = std::static_pointer_cast(filter); // all button auto show_button_func = [this] (bool show_all_button) { all_button_ = show_all_button ? new FilterAllButton(NUX_TRACKER_LOCATION) : nullptr; SetRightHandView(all_button_); if (all_button_) { all_button_->scale = scale(); all_button_->SetFilter(filter_); } }; show_button_func(filter_->show_all_button); filter_->show_all_button.changed.connect(show_button_func); expanded = !filter_->collapsed(); filter_->option_added.connect(sigc::mem_fun(this, &FilterGenre::OnOptionAdded)); filter_->option_removed.connect(sigc::mem_fun(this, &FilterGenre::OnOptionRemoved)); // finally - make sure we are up-todate with our filter list for (auto it : filter_->options()) OnOptionAdded(it); SetLabel(filter_->name); } void FilterGenre::OnOptionAdded(FilterOption::Ptr const& new_filter) { std::string tmp_label(new_filter->name); glib::String escape(g_markup_escape_text(tmp_label.c_str(), -1)); std::string label(escape.Value()); FilterGenreButton* button = new FilterGenreButton(label, NUX_TRACKER_LOCATION); button->scale = scale(); button->SetFilter(new_filter); genre_layout_->AddView(button, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); buttons_.push_back(button); QueueRelayout(); } void FilterGenre::OnOptionRemoved(FilterOption::Ptr const& removed_filter) { for (auto it=buttons_.begin() ; it != buttons_.end(); ++it) { if ((*it)->GetFilter() == removed_filter) { genre_layout_->RemoveChildObject(*it); buttons_.erase(it); QueueRelayout(); break; } } } std::string FilterGenre::GetFilterType() { return "FilterBasicButton"; } void FilterGenre::InitTheme() { //FIXME - build theme here - store images, cache them, fun fun fun } void FilterGenre::ClearRedirectedRenderChildArea() { for (auto button : buttons_) { if (button->IsRedrawNeeded()) graphics::ClearGeometry(button->GetGeometry()); } } } // namespace dash } // namespace unity ./dash/FilterAllButton.h0000644000004100000410000000300413437202764015412 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #ifndef UNITYSHELL_FILTERALLBUTTON_H #define UNITYSHELL_FILTERALLBUTTON_H #include #include #include #include "FilterBasicButton.h" namespace unity { namespace dash { class FilterAllButton : public FilterBasicButton { NUX_DECLARE_OBJECT_TYPE(FilterAllButton, FilterBasicButton); public: FilterAllButton(NUX_FILE_LINE_PROTO); void SetFilter(Filter::Ptr const& filter); private: void OnFilteringChanged(bool filtering); void OnStateChanged(nux::View* view); Filter::Ptr filter_; connection::Wrapper filtering_connection_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERALLBUTTON_H ./dash/FilterMultiRangeWidget.h0000644000004100000410000000555313437202764016734 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERMULTIRANGE_H #define UNITYSHELL_FILTERMULTIRANGE_H #include #include #include #include #include "FilterAllButton.h" #include "FilterBasicButton.h" #include "FilterExpanderLabel.h" namespace unity { namespace dash { class FilterMultiRangeButton; class FilterMultiRangeWidget : public FilterExpanderLabel { NUX_DECLARE_OBJECT_TYPE(FilterMultiRangeWidget, FilterExpanderLabel); typedef nux::ObjectPtr FilterMultiRangeButtonPtr; public: FilterMultiRangeWidget(NUX_FILE_LINE_PROTO); void SetFilter(Filter::Ptr const& filter); std::string GetFilterType(); protected: void InitTheme(); nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type); void RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void ClearRedirectedRenderChildArea(); private: void OnAllActivated(nux::View* view); void OnOptionAdded(dash::FilterOption::Ptr const& new_filter); void OnOptionRemoved(dash::FilterOption::Ptr const& removed_filter); void OnActiveChanged(bool value); void UpdateMouseFocus(nux::Point const& abs_cursor_position); virtual void Click(FilterMultiRangeButtonPtr const& button); bool CheckDrag(); nux::HLayout* layout_; FilterAllButton* all_button_; std::vector buttons_; MultiRangeFilter::Ptr filter_; FilterMultiRangeButtonPtr mouse_down_button_; FilterMultiRangeButtonPtr mouse_down_left_active_button_; FilterMultiRangeButtonPtr mouse_down_right_active_button_; bool dragging_; friend class TestFilterMultiRangeWidget; }; } // unityshell dash } // unityshell unity #endif // UNITYSHELL_FILTERMULTIRANGE_H ./dash/FilterGenreButton.h0000644000004100000410000000265013437202764015750 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERGENREBUTTON_H #define UNITYSHELL_FILTERGENREBUTTON_H #include #include #include #include "FilterBasicButton.h" namespace unity { namespace dash { class FilterGenreButton : public FilterBasicButton { NUX_DECLARE_OBJECT_TYPE(FilterGenreButton, FilterBasicButton); public: FilterGenreButton(std::string const& label, NUX_FILE_LINE_PROTO); FilterGenreButton(NUX_FILE_LINE_PROTO); void SetFilter(FilterOption::Ptr const& filter); FilterOption::Ptr GetFilter(); private: FilterOption::Ptr filter_; }; } // namespace dash } // namespace unity #endif // FILTERGENREBUTTON_H ./dash/previews/0000755000004100000410000000000013437202764014036 5ustar www-datawww-data./dash/previews/Track.h0000644000004100000410000000564413437202764015264 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef TRACK_H #define TRACK_H #include #include #include #include #include "unity-shared/Introspectable.h" #include "PreviewPlayer.h" namespace nux { class LayeredLayout; } namespace unity { class StaticCairoText; class IconTexture; namespace dash { namespace previews { class Track : public nux::View, public debug::Introspectable { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(Track, nux::View); Track(NUX_FILE_LINE_PROTO); void Update(dash::Track const& track_row); nux::Property scale; protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void PreLayoutManagement(); virtual nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); virtual bool AcceptKeyNavFocus() { return false; } void SetupBackground(); void SetupViews(); bool HasStatusFocus() const; void OnTrackControlMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags); void OnTrackControlMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags); void UpdateTrackState(); protected: std::string uri_; PlayerState play_state_; float progress_; PreviewPlayer player_; unity::StaticCairoText* track_number_; unity::StaticCairoText* title_; unity::StaticCairoText* duration_; typedef std::unique_ptr LayerPtr; LayerPtr focus_layer_; LayerPtr progress_layer_; nux::Layout* title_layout_; nux::Layout* duration_layout_; nux::View* status_play_layout_; nux::View* status_pause_layout_; nux::View* track_number_layout_; nux::LayeredLayout* track_status_layout_; IconTexture* status_play_; IconTexture* status_pause_; bool mouse_over_; connection::Wrapper player_connection_; private: void UpdateScale(double scale); }; } } } #endif // TRACK_H ./dash/previews/CMakeLists.txt0000644000004100000410000000446413437202764016606 0ustar www-datawww-dataset(UNITY_SRC ../plugins/unityshell/src) set (CFLAGS ${CACHED_UNITY_DEPS_CFLAGS} ${CACHED_UNITY_DEPS_CFLAGS_OTHER} ${PIC_FLAGS} ) string (REPLACE ";" " " CFLAGS "${CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}") set (LIBS ${CACHED_UNITY_DEPS_LDFLAGS} ${UNITY_STANDALONE_LADD}) include_directories (.. ../services ../UnityCore ${UNITY_SRC} ${CMAKE_BINARY_DIR}) # # Headers & Sources # set (PREVIEWS_SOURCES ActionButton.cpp ActionLink.cpp ApplicationPreview.cpp GenericPreview.cpp MusicPreview.cpp MoviePreview.cpp PaymentPreview.cpp Preview.cpp PreviewContainer.cpp PreviewInfoHintWidget.cpp PreviewNavigator.cpp PreviewRatingsWidget.cpp SocialPreview.cpp SocialPreviewContent.cpp SocialPreviewComments.cpp TabIterator.cpp Track.cpp Tracks.cpp MusicPaymentPreview.cpp ErrorPreview.cpp ) add_library (previews-lib STATIC ${PREVIEWS_SOURCES}) add_dependencies (previews-lib unity-core-${UNITY_API_VERSION} unity-shared) target_link_libraries (previews-lib unity-shared) add_pch(pch/previews_pch.hh previews-lib) # # Application Standalone variant # add_executable (app_previews StandaloneApplicationPreview.cpp) target_link_libraries (app_previews previews-lib unity-shared unity-shared-standalone) # # Music Standalone variant # add_executable (music_previews StandaloneMusicPreview.cpp) target_link_libraries (music_previews previews-lib unity-shared unity-shared-standalone) # # Social Standalone variant # add_executable (social_previews StandaloneSocialPreview.cpp) target_link_libraries (social_previews previews-lib unity-shared unity-shared-standalone) # # Movie Standalone variant # add_executable (movie_previews StandaloneMoviePreview.cpp) target_link_libraries (movie_previews previews-lib unity-shared unity-shared-standalone) # # Payment Standalone variant # add_executable (payment_previews StandaloneMusicPaymentPreview.cpp) add_dependencies (payment_previews previews-lib) target_link_libraries (payment_previews previews-lib unity-shared unity-shared-standalone) # # Error Standalone variant # add_executable (error_previews StandaloneErrorPreview.cpp) add_dependencies (error_previews previews-lib) target_link_libraries (error_previews previews-lib unity-shared unity-shared-standalone) ./dash/previews/ApplicationPreview.h0000644000004100000410000000437313437202764020023 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef APPLICATIONPREVIEW_H #define APPLICATIONPREVIEW_H #include "Preview.h" #include "unity-shared/OverlayScrollView.h" namespace unity { class IconTexture; namespace dash { namespace previews { class PreviewRatingsWidget; class ApplicationPreview : public Preview { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(ApplicationPreview, Preview); ApplicationPreview(dash::Preview::Ptr preview_model); ~ApplicationPreview(); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void PreLayoutManagement(); virtual void SetupViews(); void UpdateScale(double scale) override; protected: nux::VLayout* title_subtitle_layout_; nux::HLayout* image_data_layout_; nux::HLayout* main_app_info_; nux::VLayout* icon_layout_; nux::VLayout* app_data_layout_; nux::VLayout* app_updated_copywrite_layout_; nux::VLayout* app_info_layout_; ScrollView* app_info_scroll_; nux::Layout* actions_layout_; nux::ObjectPtr app_icon_; nux::ObjectPtr app_rating_; nux::ObjectPtr license_; nux::ObjectPtr last_update_; nux::ObjectPtr copywrite_; }; } } } #endif //APPLICATIONPREVIEW_H ./dash/previews/ErrorPreview.cpp0000644000004100000410000002365513437202764017210 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Diego Sarmentero * Manuel de la Pena * */ #include "TabIterator.h" #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/CoverArt.h" #include "unity-shared/StaticCairoText.h" #include #include #include #include #include #include "ErrorPreview.h" #include "PreviewInfoHintWidget.h" #include "stdio.h" #include "config.h" #include namespace unity { namespace dash { namespace previews { namespace { nux::logging::Logger logger("unity.dash.previews.ErrorPreview"); const RawPixel TITLE_DATA_MAX_SIZE = 76_em; const RawPixel TITLE_DATA_CHILDREN_SPACE = 10_em; const RawPixel LINE_SPACING = 10_em; const RawPixel TITLE_MAX_WIDTH = 480_em; const RawPixel CHILDREN_SPACE = 5_em; const RawPixel BUTTONS_DATA_SPACE = 20_em; const RawPixel INTRO_SPACE = 110_em; } const std::string ErrorPreview::CANCEL_ACTION = "cancel"; const std::string ErrorPreview::GO_TO_U1_ACTION = "open_u1_link"; NUX_IMPLEMENT_OBJECT_TYPE(ErrorPreview) ErrorPreview::ErrorPreview(dash::Preview::Ptr preview_model) : PaymentPreview(preview_model) { PaymentPreview::SetupBackground(); SetupViews(); UpdateScale(scale); scale.changed.connect(sigc::mem_fun(this, &ErrorPreview::UpdateScale)); } ErrorPreview::~ErrorPreview() { } nux::Area* ErrorPreview::FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) { return Preview::FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); } std::string ErrorPreview::GetName() const { return "ErrorPreview"; } void ErrorPreview::AddProperties(debug::IntrospectionData& introspection) { PaymentPreview::AddProperties(introspection); } void ErrorPreview::OnActionActivated(ActionButton* button, std::string const& id) { Preview::OnActionActivated(button, id); } void ErrorPreview::OnActionLinkActivated(ActionLink *link, std::string const& id) { if (preview_model_) preview_model_->PerformAction(id); } void ErrorPreview::LoadActions() { // Loop over the buttons and add them to the correct var // this is not efficient but is the only way we have atm for (dash::Preview::ActionPtr action : preview_model_->GetActions()) { nux::ObjectPtr button = this->CreateButton(action); button->scale = scale(); button->activate.connect(sigc::mem_fun(this, &ErrorPreview::OnActionActivated)); buttons_map_.insert(std::make_pair(action->id, button)); } } nux::Layout* ErrorPreview::GetTitle() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout* title_data_layout = new nux::VLayout(); title_data_layout->SetMaximumHeight(TITLE_DATA_MAX_SIZE.CP(scale)); title_data_layout->SetSpaceBetweenChildren(TITLE_DATA_CHILDREN_SPACE.CP(scale)); title_ = new StaticCairoText( preview_model_->title.Get(), true, NUX_TRACKER_LOCATION); title_->SetFont(style.payment_title_font()); title_->SetLines(-1); title_->SetFont(style.title_font()); title_->SetMaximumWidth(TITLE_MAX_WIDTH.CP(scale)); title_->SetTextEllipsize(StaticCairoText::EllipsizeState::NUX_ELLIPSIZE_END); title_data_layout->AddView(title_.GetPointer(), 1); subtitle_ = new StaticCairoText( preview_model_->subtitle.Get(), true, NUX_TRACKER_LOCATION); subtitle_->SetLines(-1); subtitle_->SetFont(style.payment_subtitle_font()); title_data_layout->AddView(subtitle_.GetPointer(), 1); title_data_layout->AddSpace(1, 1); return title_data_layout; } nux::Layout* ErrorPreview::GetPrice() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout *prize_data_layout = new nux::VLayout(); prize_data_layout->SetMaximumHeight(TITLE_DATA_MAX_SIZE.CP(scale)); prize_data_layout->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); prize_data_layout->SetPadding(0, TITLE_DATA_CHILDREN_SPACE.CP(scale), 0, 0); purchase_prize_ = new StaticCairoText( error_preview_model_->purchase_prize.Get(), true, NUX_TRACKER_LOCATION); purchase_prize_->SetLines(-1); purchase_prize_->SetFont(style.payment_prize_title_font()); prize_data_layout->AddView(purchase_prize_.GetPointer(), 1, nux::MINOR_POSITION_END); purchase_hint_ = new StaticCairoText( _("Ubuntu One best offer"), true, NUX_TRACKER_LOCATION); purchase_hint_->SetLines(-1); purchase_hint_->SetFont(style.payment_prize_subtitle_font()); prize_data_layout->AddView(purchase_hint_.GetPointer(), 1, nux::MINOR_POSITION_END); purchase_type_ = new StaticCairoText( error_preview_model_->purchase_type.Get(), true, NUX_TRACKER_LOCATION); purchase_type_->SetLines(-1); purchase_type_->SetFont(style.payment_prize_subtitle_font()); prize_data_layout->AddView(purchase_type_.GetPointer(), 1, nux::MINOR_POSITION_END); return prize_data_layout; } nux::Layout* ErrorPreview::GetBody() { previews::Style& style = dash::previews::Style::Instance(); nux::HLayout *body_layout = new nux::HLayout(); nux::HLayout *intro_layout = new nux::HLayout(); nux::VLayout *icon_layout = new nux::VLayout(); icon_layout->SetPadding((78_em).CP(scale), (10_em).CP(scale), (90_em).CP(scale), (43_em).CP(scale)); intro_layout->SetPadding((75_em).CP(scale), (20_em).CP(scale), 0, 0); intro_layout->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); intro_ = new StaticCairoText( error_preview_model_->header.Get(), true, NUX_TRACKER_LOCATION); intro_->SetFont(style.payment_intro_font().c_str()); intro_->SetScale(scale); intro_->SetLines(-3); intro_->SetLineSpacing(LINE_SPACING.CP(scale)); intro_->SetTextEllipsize(StaticCairoText::EllipsizeState::NUX_ELLIPSIZE_END); intro_layout->AddView(intro_.GetPointer());//, 0, nux::MINOR_POSITION_CENTER); warning_texture_ = new IconTexture(style.GetWarningIcon()); icon_layout->AddView(warning_texture_.GetPointer(), 0, nux::MINOR_POSITION_END); body_layout->AddLayout(icon_layout, 0); body_layout->AddLayout(intro_layout, 0); body_layout->AddSpace(1, 1); return body_layout; } nux::Layout* ErrorPreview::GetFooter() { previews::Style& style = dash::previews::Style::Instance(); nux::HLayout* actions_buffer_h = new nux::HLayout(); actions_buffer_h->AddSpace(0, 1); nux::HLayout* buttons_data_layout = new TabIteratorHLayout(tab_iterator_); buttons_data_layout->SetSpaceBetweenChildren(style.GetSpaceBetweenActions().CP(scale)); buttons_data_layout->AddSpace(BUTTONS_DATA_SPACE.CP(scale), 1); if(buttons_map_[ErrorPreview::CANCEL_ACTION].GetPointer()){ ActionButton* button = (ActionButton*)buttons_map_[ErrorPreview::CANCEL_ACTION].GetPointer(); buttons_data_layout->AddView(buttons_map_[ErrorPreview::CANCEL_ACTION].GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_END); AddChild(button); tab_iterator_->Append(button); } if(buttons_map_[ErrorPreview::GO_TO_U1_ACTION].GetPointer()){ ActionButton* button = (ActionButton*)buttons_map_[ErrorPreview::GO_TO_U1_ACTION].GetPointer(); buttons_data_layout->AddView(buttons_map_[ErrorPreview::GO_TO_U1_ACTION].GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_END); AddChild(button); tab_iterator_->Prepend(button); } return buttons_data_layout; } void ErrorPreview::PreLayoutManagement() { nux::Geometry geo = GetGeometry(); GetLayout()->SetGeometry(geo); previews::Style& style = dash::previews::Style::Instance(); int width = std::max(0, geo.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale)); if(full_data_layout_) { full_data_layout_->SetMaximumWidth(width); } if(header_layout_) { header_layout_->SetMaximumWidth(width); } if(intro_) { intro_->SetMaximumWidth(width - INTRO_SPACE.CP(scale)); } if(footer_layout_) { footer_layout_->SetMaximumWidth(width); } Preview::PreLayoutManagement(); } void ErrorPreview::SetupViews() { error_preview_model_ = dynamic_cast(preview_model_.get()); if (!error_preview_model_) { LOG_ERROR(logger) << "Could not derive preview model from given parameter."; return; } // load the buttons so that they can be accessed in order LoadActions(); PaymentPreview::SetupViews(); } void ErrorPreview::UpdateScale(double scale) { if (intro_) intro_->SetScale(scale); if (purchase_hint_) purchase_hint_->SetScale(scale); if (purchase_prize_) purchase_prize_->SetScale(scale); if (purchase_type_) purchase_type_->SetScale(scale); if (warning_texture_) { previews::Style& style = dash::previews::Style::Instance(); RawPixel width(style.GetWarningIcon()->GetWidth()); RawPixel height(style.GetWarningIcon()->GetHeight()); warning_texture_->SetSize(std::max(width, height).CP(scale)); warning_texture_->ReLoadIcon(); } if (title_) title_->SetMaximumWidth(TITLE_MAX_WIDTH.CP(scale)); Preview::UpdateScale(scale); } } } } ./dash/previews/PreviewNavigator.cpp0000644000004100000410000001272613437202764020046 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "PreviewNavigator.h" #include #include #include #include namespace unity { namespace dash { namespace previews { NUX_IMPLEMENT_OBJECT_TYPE(PreviewNavigator); PreviewNavigator::PreviewNavigator(Orientation direction, NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , scale(1.0) , direction_(direction) , texture_(nullptr) , visual_state_(VisualState::NORMAL) { SetupViews(); UpdateTexture(); UpdateScale(scale); scale.changed.connect(sigc::mem_fun(this, &PreviewNavigator::UpdateScale)); } void PreviewNavigator::SetEnabled(bool enabled) { if (enabled != texture_->IsVisible()) { texture_->SetVisible(enabled); QueueRelayout(); } } std::string PreviewNavigator::GetName() const { return "PreviewNavigator"; } void PreviewNavigator::AddProperties(debug::IntrospectionData& introspection) { introspection .add("button-x", texture_->GetAbsoluteX()) .add("button-y", texture_->GetAbsoluteY()) .add("button-width", texture_->GetGeometry().width) .add("button-height", texture_->GetGeometry().height) .add("button-geo", texture_->GetAbsoluteGeometry()) .add("direction", static_cast(direction_)); } void PreviewNavigator::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } void PreviewNavigator::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry base = GetGeometry(); gfx_engine.PushClippingRectangle(base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } void PreviewNavigator::SetupViews() { previews::Style& style = dash::previews::Style::Instance(); if (direction_ == Orientation::LEFT || direction_ == Orientation::RIGHT) { nux::VLayout* vlayout = new nux::VLayout(); nux::HLayout* hlayout = new nux::HLayout(); vlayout->SetSpaceBetweenChildren(0); hlayout->SetSpaceBetweenChildren(0); layout_ = hlayout; if (direction_ == Orientation::LEFT) texture_ = new IconTexture(Style::Instance().GetNavLeftIcon(), style.GetNavigatorIconSize().CP(scale), style.GetNavigatorIconSize().CP(scale)); else texture_ = new IconTexture(Style::Instance().GetNavRightIcon(), style.GetNavigatorIconSize().CP(scale), style.GetNavigatorIconSize().CP(scale)); texture_->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); vlayout->AddSpace(0,1); vlayout->AddLayout(hlayout); vlayout->AddSpace(0,1); SetLayout(vlayout); } else if (direction_ == Orientation::UP || direction_ == Orientation::DOWN) { // No support for this Yet g_assert(false); nux::HLayout* hlayout = new nux::HLayout(); nux::VLayout* vlayout = new nux::VLayout(); hlayout->SetSpaceBetweenChildren(0); vlayout->SetSpaceBetweenChildren(0); layout_ = vlayout; hlayout->AddSpace(0,1); hlayout->AddLayout(vlayout); hlayout->AddSpace(0,1); SetLayout(hlayout); } layout_->AddSpace(0, 1); if (texture_) { AddChild(texture_); layout_->AddView(texture_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); texture_->mouse_click.connect([this](int, int, unsigned long, unsigned long) { activated.emit(); }); texture_->mouse_enter.connect(sigc::mem_fun(this, &PreviewNavigator::TexRecvMouseEnter)); texture_->mouse_leave.connect(sigc::mem_fun(this, &PreviewNavigator::TexRecvMouseLeave)); } layout_->AddSpace(0, 1); } void PreviewNavigator::TexRecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) { visual_state_ = VisualState::ACTIVE; UpdateTexture(); QueueDraw(); } void PreviewNavigator::TexRecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) { visual_state_ = VisualState::NORMAL; UpdateTexture(); QueueDraw(); } void PreviewNavigator::UpdateTexture() { if (!texture_) return; switch (visual_state_) { case VisualState::ACTIVE: texture_->SetOpacity(1.0); break; case VisualState::NORMAL: default: texture_->SetOpacity(0.2); break; } } void PreviewNavigator::UpdateScale(double scale) { previews::Style& style = dash::previews::Style::Instance(); if (texture_) { int icon_size = style.GetNavigatorIconSize().CP(scale); texture_->SetMinMaxSize(icon_size, icon_size); } QueueRelayout(); QueueDraw(); } } // namespace previews } // namespace dash } // namespace unity ./dash/previews/MusicPaymentPreview.cpp0000644000004100000410000004400113437202764020521 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/CoverArt.h" #include "unity-shared/StaticCairoText.h" #include "config.h" #include #include #include #include #include #include "MusicPaymentPreview.h" #include "PreviewInfoHintWidget.h" #include "stdio.h" namespace unity { namespace dash { namespace previews { namespace { nux::logging::Logger logger("unity.dash.previews.payment.preview.music"); const RawPixel DATA_MAX_HEIGHT = 76_em; const RawPixel TITLE_CHILDREN_SPACE = 10_em; const RawPixel PRIZE_CHILDREN_SPACE = 5_em; const RawPixel TITLE_MAX_WIDTH = 480_em; const RawPixel INTRO_MIN_HEIGHT = 50_em; const RawPixel FORM_MIN_HEIGHT = 107_em; const RawPixel FORM_PADDING = 20_em; const RawPixel LABELS_CHILDREN_SPACE = 18_em; const RawPixel PASSWORD_MIN_HEIGHT = 40_em; const RawPixel PASSWORD_MIN_WIDTH = 240_em; const RawPixel ACTIONS_CHILDREN_SPACE_MAX = 16_em; const RawPixel ACTIONS_CHILDREN_SPACE_MIN = 8_em; const RawPixel BUTTONS_SPACE = 20_em; const RawPixel HEADER_CHILDREN_SPACE = 10_em; const RawPixel HEADER_MAX_SIZE = 76_em; const RawPixel BODY_CHILDREN_SPACE = 20_em; } // static string definitions const std::string MusicPaymentPreview::DATA_INFOHINT_ID = "album_purchase_preview"; const std::string MusicPaymentPreview::DATA_PASSWORD_KEY = "password"; const std::string MusicPaymentPreview::CHANGE_PAYMENT_ACTION = "change_payment_method"; const std::string MusicPaymentPreview::FORGOT_PASSWORD_ACTION = "forgot_password"; const std::string MusicPaymentPreview::CANCEL_PURCHASE_ACTION = "cancel_purchase"; const std::string MusicPaymentPreview::PURCHASE_ALBUM_ACTION = "purchase_album"; NUX_IMPLEMENT_OBJECT_TYPE(MusicPaymentPreview) MusicPaymentPreview::MusicPaymentPreview(dash::Preview::Ptr preview_model) : PaymentPreview(preview_model) { SetupViews(); PaymentPreview::SetupBackground(); UpdateScale(scale); } std::string MusicPaymentPreview::GetName() const { return "MusicPaymentPreview"; } void MusicPaymentPreview::AddProperties(debug::IntrospectionData& introspection) { PaymentPreview::AddProperties(introspection); } void MusicPaymentPreview::OnActionActivated(ActionButton* button, std::string const& id) { // Check the action id and send the password only when we // purchasing a song if(id == MusicPaymentPreview::PURCHASE_ALBUM_ACTION && preview_model_ && password_entry_) { // HACK: We need to think a better way to do this auto const& password = password_entry_->text_entry()->GetText(); glib::HintsMap hints { {MusicPaymentPreview::DATA_PASSWORD_KEY, glib::Variant(password)} }; preview_model_->PerformAction(id, hints); // show the overlay ShowOverlay(); return; } Preview::OnActionActivated(button, id); } void MusicPaymentPreview::OnActionLinkActivated(ActionLink *link, std::string const& id) { if (preview_model_) preview_model_->PerformAction(id); } void MusicPaymentPreview::LoadActions() { // Loop over the buttons and add them to the correct var // this is not efficient but is the only way we have atm for (dash::Preview::ActionPtr action : preview_model_->GetActions()) { const char *action_id = action->id.c_str(); if(MusicPaymentPreview::CHANGE_PAYMENT_ACTION == action_id || MusicPaymentPreview::FORGOT_PASSWORD_ACTION == action_id) { nux::ObjectPtr link = this->CreateLink(action); link->scale = scale(); link->activate.connect(sigc::mem_fun(this, &MusicPaymentPreview::OnActionLinkActivated)); buttons_map_.insert(std::make_pair(action->id, link)); } else { nux::ObjectPtr button = this->CreateButton(action); button->activate.connect(sigc::mem_fun(this, &MusicPaymentPreview::OnActionActivated)); button->scale = scale(); buttons_map_.insert(std::make_pair(action->id, button)); } LOG_DEBUG(logger) << "added button for action with id '" << action->id << "'"; } } nux::Layout* MusicPaymentPreview::GetTitle() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout* title_data_layout = new nux::VLayout(); title_data_layout->SetMaximumHeight(DATA_MAX_HEIGHT.CP(scale)); title_data_layout->SetSpaceBetweenChildren(TITLE_CHILDREN_SPACE.CP(scale)); title_ = new StaticCairoText( preview_model_->title.Get(), true, NUX_TRACKER_LOCATION); title_->SetFont(style.payment_title_font()); title_->SetLines(-1); title_->SetScale(scale); title_->SetFont(style.title_font()); title_->SetMaximumWidth(TITLE_MAX_WIDTH.CP(scale)); title_->SetTextEllipsize(StaticCairoText::EllipsizeState::NUX_ELLIPSIZE_END); title_data_layout->AddView(title_.GetPointer(), 1); subtitle_ = new StaticCairoText( preview_model_->subtitle.Get(), true, NUX_TRACKER_LOCATION); subtitle_->SetLines(-1); subtitle_->SetScale(scale); subtitle_->SetFont(style.payment_subtitle_font()); title_data_layout->AddView(subtitle_.GetPointer(), 1); title_data_layout->AddSpace(1, 1); return title_data_layout; } nux::Layout* MusicPaymentPreview::GetPrice() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout *prize_data_layout = new nux::VLayout(); prize_data_layout->SetMaximumHeight(DATA_MAX_HEIGHT.CP(scale)); prize_data_layout->SetSpaceBetweenChildren(PRIZE_CHILDREN_SPACE.CP(scale)); prize_data_layout->SetPadding(0, TITLE_CHILDREN_SPACE.CP(scale), 0, 0); purchase_prize_ = new StaticCairoText( payment_preview_model_->purchase_prize.Get(), true, NUX_TRACKER_LOCATION); purchase_prize_->SetLines(-1); purchase_prize_->SetScale(scale); purchase_prize_->SetFont(style.payment_prize_title_font()); prize_data_layout->AddView(purchase_prize_.GetPointer(), 1, nux::MINOR_POSITION_END); purchase_hint_ = new StaticCairoText( _("Ubuntu One best offer"), true, NUX_TRACKER_LOCATION); purchase_hint_->SetLines(-1); purchase_hint_->SetScale(scale); purchase_hint_->SetFont(style.payment_prize_subtitle_font()); prize_data_layout->AddView(purchase_hint_.GetPointer(), 1, nux::MINOR_POSITION_END); purchase_type_ = new StaticCairoText( payment_preview_model_->purchase_type.Get(), true, NUX_TRACKER_LOCATION); purchase_type_->SetLines(-1); purchase_type_->SetScale(scale); purchase_type_->SetFont(style.payment_prize_subtitle_font()); prize_data_layout->AddView(purchase_type_.GetPointer(), 1, nux::MINOR_POSITION_END); return prize_data_layout; } nux::Layout* MusicPaymentPreview::GetBody() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout *body_layout = new nux::VLayout(); body_layout->SetSpaceBetweenChildren(BODY_CHILDREN_SPACE.CP(scale)); intro_ = new StaticCairoText( payment_preview_model_->header.Get(), true, NUX_TRACKER_LOCATION); intro_->SetFont(style.payment_intro_font()); intro_->SetScale(scale); intro_->SetLineSpacing(TITLE_CHILDREN_SPACE.CP(scale)); intro_->SetLines(-style.GetDescriptionLineCount()); intro_->SetMinimumHeight(INTRO_MIN_HEIGHT.CP(scale)); form_layout_ = new nux::HLayout(); form_layout_->SetSpaceBetweenChildren(TITLE_CHILDREN_SPACE.CP(scale)); form_layout_->SetMinimumHeight(FORM_MIN_HEIGHT.CP(scale)); form_layout_->SetLeftAndRightPadding(FORM_PADDING.CP(scale)); form_layout_->SetTopAndBottomPadding(TITLE_CHILDREN_SPACE.CP(scale)); form_layout_->AddLayout(GetFormLabels(), 1, nux::MINOR_POSITION_END); form_layout_->AddLayout(GetFormFields(), 1, nux::MINOR_POSITION_END); form_layout_->AddLayout(GetFormActions(), 1, nux::MINOR_POSITION_END); body_layout->AddView(intro_.GetPointer(), 1); body_layout->AddLayout(form_layout_.GetPointer(), 1); return body_layout; } nux::Layout* MusicPaymentPreview::GetFormLabels() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout *labels_layout = new nux::VLayout(); if (error_message_.empty()) { labels_layout->SetSpaceBetweenChildren(LABELS_CHILDREN_SPACE.CP(scale)); } else { labels_layout->SetSpaceBetweenChildren(TITLE_CHILDREN_SPACE.CP(scale)); } email_label_ = new StaticCairoText( _("Ubuntu One email:"), true, NUX_TRACKER_LOCATION); email_label_->SetLines(-1); email_label_->SetScale(scale); email_label_->SetFont(style.payment_form_labels_font()); labels_layout->AddView(email_label_.GetPointer(), 0, nux::MINOR_POSITION_END); payment_label_ = new StaticCairoText( _("Payment method:"), true, NUX_TRACKER_LOCATION); payment_label_->SetLines(-1); payment_label_->SetScale(scale); payment_label_->SetFont(style.payment_form_labels_font()); labels_layout->AddView(payment_label_.GetPointer(), 0, nux::MINOR_POSITION_END); password_label_ = new StaticCairoText( _("Ubuntu One password:"), true, NUX_TRACKER_LOCATION); password_label_->SetLines(-1); password_label_->SetScale(scale); password_label_->SetFont(style.payment_form_labels_font()); password_label_->SetMinimumHeight(PASSWORD_MIN_HEIGHT.CP(scale)); labels_layout->AddView(password_label_.GetPointer(), 0, nux::MINOR_POSITION_END); return labels_layout; } nux::Layout* MusicPaymentPreview::GetFormFields() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout *fields_layout = new nux::VLayout(); if (error_message_.empty()) { fields_layout->SetSpaceBetweenChildren(LABELS_CHILDREN_SPACE.CP(scale)); } else { fields_layout->SetSpaceBetweenChildren(TITLE_CHILDREN_SPACE.CP(scale)); } email_ = new StaticCairoText( payment_preview_model_->email.Get(), true, NUX_TRACKER_LOCATION); email_->SetLines(-1); email_->SetScale(scale); email_->SetFont(style.payment_form_data_font()); fields_layout->AddView(email_.GetPointer(), 1, nux::MINOR_POSITION_START); payment_ = new StaticCairoText( payment_preview_model_->payment_method.Get(), true, NUX_TRACKER_LOCATION); payment_->SetLines(-1); payment_->SetScale(scale); payment_->SetFont(style.payment_form_data_font()); fields_layout->AddView(payment_.GetPointer(), 1, nux::MINOR_POSITION_START); password_entry_ = new TextInput(); password_entry_->SetMinimumHeight(PASSWORD_MIN_HEIGHT.CP(scale)); password_entry_->SetMinimumWidth(PASSWORD_MIN_WIDTH.CP(scale)); password_entry_->input_hint = _("Password"); fields_layout->AddView(password_entry_.GetPointer(), 1, nux::MINOR_POSITION_START); password_entry_->text_entry()->SetPasswordMode(true); const char password_char = '*'; password_entry_->text_entry()->SetPasswordChar(&password_char); if (!error_message_.empty()) { StaticCairoText* error = new StaticCairoText( _("Wrong password"), true, NUX_TRACKER_LOCATION); error->SetLines(-1); error->SetScale(scale); error->SetFont(style.payment_form_data_font()); // ensure it is an error using red error->SetTextColor(style.payment_error_color()); fields_layout->AddView(error, 0, nux::MINOR_POSITION_START); } return fields_layout; } nux::Layout* MusicPaymentPreview::GetFormActions() { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout *actions_layout = new nux::VLayout(); if (error_message_.empty()) { actions_layout->SetSpaceBetweenChildren(ACTIONS_CHILDREN_SPACE_MAX.CP(scale)); } else { actions_layout->SetSpaceBetweenChildren(ACTIONS_CHILDREN_SPACE_MIN.CP(scale)); } nux::ObjectPtr empty_; empty_ = new StaticCairoText( "", true, NUX_TRACKER_LOCATION); empty_->SetLines(-1); empty_->SetScale(scale); empty_->SetFont(style.payment_form_labels_font()); actions_layout->AddView(empty_.GetPointer(), 1, nux::MINOR_POSITION_START); if(buttons_map_[MusicPaymentPreview::CHANGE_PAYMENT_ACTION].GetPointer()) actions_layout->AddView( buttons_map_[MusicPaymentPreview::CHANGE_PAYMENT_ACTION].GetPointer(), 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_END); if(buttons_map_[MusicPaymentPreview::FORGOT_PASSWORD_ACTION].GetPointer()) actions_layout->AddView( buttons_map_[MusicPaymentPreview::FORGOT_PASSWORD_ACTION].GetPointer(), 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_END); return actions_layout; } nux::Layout* MusicPaymentPreview::GetFooter() { previews::Style& style = dash::previews::Style::Instance(); nux::HLayout* actions_buffer_h = new nux::HLayout(); actions_buffer_h->AddSpace(0, 1); nux::HLayout* buttons_data_layout = new nux::HLayout(); buttons_data_layout->SetSpaceBetweenChildren(style.GetSpaceBetweenActions().CP(scale)); lock_texture_ = new IconTexture(style.GetLockIcon(), style.GetPaymentLockWidth().CP(scale), style.GetPaymentLockHeight().CP(scale)); buttons_data_layout->AddView(lock_texture_.GetPointer(), 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_BEGIN); buttons_data_layout->AddSpace(BUTTONS_SPACE.CP(scale), 1); if(buttons_map_[MusicPaymentPreview::CANCEL_PURCHASE_ACTION].GetPointer()) buttons_data_layout->AddView(buttons_map_[MusicPaymentPreview::CANCEL_PURCHASE_ACTION].GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_END); if(buttons_map_[MusicPaymentPreview::PURCHASE_ALBUM_ACTION].GetPointer()) buttons_data_layout->AddView(buttons_map_[MusicPaymentPreview::PURCHASE_ALBUM_ACTION].GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_END); return buttons_data_layout; } const std::string MusicPaymentPreview::GetErrorMessage(GVariant *dict) { glib::Variant data(g_variant_lookup_value(dict, "error_message", G_VARIANT_TYPE_ANY)); if (!data) return ""; return data.GetString(); } void MusicPaymentPreview::PreLayoutManagement() { nux::Geometry const& geo = GetGeometry(); GetLayout()->SetGeometry(geo); previews::Style& style = dash::previews::Style::Instance(); int content_width = geo.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale); int width = std::max(0, content_width); if(full_data_layout_) { full_data_layout_->SetMaximumWidth(width); } if(header_layout_) { header_layout_->SetMaximumWidth(width); } if(intro_) { intro_->SetMaximumWidth(width); } if(form_layout_) { form_layout_->SetMaximumWidth(width); } if(footer_layout_) { footer_layout_->SetMaximumWidth(width); } // set the tab ordering SetFirstInTabOrder(password_entry_->text_entry()); SetLastInTabOrder(buttons_map_[MusicPaymentPreview::CANCEL_PURCHASE_ACTION].GetPointer()); SetLastInTabOrder(buttons_map_[MusicPaymentPreview::PURCHASE_ALBUM_ACTION].GetPointer()); SetLastInTabOrder(buttons_map_[MusicPaymentPreview::CHANGE_PAYMENT_ACTION].GetPointer()); SetLastInTabOrder(buttons_map_[MusicPaymentPreview::FORGOT_PASSWORD_ACTION].GetPointer()); Preview::PreLayoutManagement(); } void MusicPaymentPreview::SetupViews() { payment_preview_model_ = dynamic_cast(preview_model_.get()); if (!payment_preview_model_) { LOG_ERROR(logger) << "Could not derive preview model from given parameter."; return; } dash::Preview::InfoHintPtrList hints = preview_model_->GetInfoHints(); GVariant *preview_data = NULL; for (dash::Preview::InfoHintPtr info_hint : hints) { if (info_hint->id == MusicPaymentPreview::DATA_INFOHINT_ID) { preview_data = info_hint->value; if (preview_data != NULL) { error_message_ = GetErrorMessage(preview_data); } break; } } // load the buttons so that they can be accessed in order LoadActions(); PaymentPreview::SetupViews(); } void MusicPaymentPreview::UpdateScale(double scale) { PaymentPreview::UpdateScale(scale); if (intro_) intro_->SetScale(scale); if (email_label_) email_label_->SetScale(scale); if (payment_label_) payment_label_->SetScale(scale); if (payment_) payment_->SetScale(scale); if (password_label_) password_label_->SetScale(scale); if (purchase_hint_) purchase_hint_->SetScale(scale); if (purchase_prize_) purchase_prize_->SetScale(scale); if (purchase_type_) purchase_type_->SetScale(scale); if (change_payment_) change_payment_->SetScale(scale); if (error_label_) error_label_->SetScale(scale); previews::Style& style = dash::previews::Style::Instance(); if (lock_texture_) lock_texture_->SetSize(std::max(style.GetPaymentLockWidth().CP(scale), style.GetPaymentLockHeight().CP(scale))); if (password_entry_) { password_entry_->SetMinimumHeight(PASSWORD_MIN_HEIGHT.CP(scale)); password_entry_->SetMinimumWidth(PASSWORD_MIN_WIDTH.CP(scale)); } if (form_layout_) { form_layout_->SetSpaceBetweenChildren(TITLE_CHILDREN_SPACE.CP(scale)); form_layout_->SetMinimumHeight(FORM_MIN_HEIGHT.CP(scale)); form_layout_->SetLeftAndRightPadding(FORM_PADDING.CP(scale)); form_layout_->SetTopAndBottomPadding(TITLE_CHILDREN_SPACE.CP(scale)); } } } } } ./dash/previews/SocialPreviewComments.h0000644000004100000410000000434513437202764020477 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Ken VanDine * */ #ifndef SOCIAL_PREVIEW_COMMENTS_H #define SOCIAL_PREVIEW_COMMENTS_H #include #include #include #include "unity-shared/StaticCairoText.h" #include "unity-shared/Introspectable.h" #include #include "PreviewContainer.h" namespace unity { namespace dash { namespace previews { class SocialPreviewComments : public nux::View, public unity::debug::Introspectable { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(SocialPreviewComments, nux::View); SocialPreviewComments(dash::Preview::Ptr preview_model, NUX_FILE_LINE_PROTO); nux::Property scale; sigc::signal request_close() const { return preview_container_.request_close; } protected: typedef nux::ObjectPtr StaticCairoTextPtr; typedef std::pair Comment; std::list comments_; dash::Preview::Ptr preview_model_; virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void PreLayoutManagement(); virtual bool AcceptKeyNavFocus() { return false; } void SetupViews(); virtual std::string GetName() const; virtual void AddProperties(debug::IntrospectionData&); private: PreviewContainer preview_container_; }; } } } #endif // SOCIAL_PREVIEW_COMMENTS_H ./dash/previews/ActionButton.h0000644000004100000410000000576213437202764016632 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef ACTIONBUTTON_H #define ACTIONBUTTON_H #include #include #include #include "unity-shared/Introspectable.h" namespace unity { class StaticCairoText; } namespace unity { class IconTexture; namespace dash { class ActionButton : public nux::AbstractButton, public debug::Introspectable { public: ActionButton(std::string const& action_hint, std::string const& label, std::string const& icon_hint, NUX_FILE_LINE_PROTO); ~ActionButton(); sigc::signal activate; void SetFont(std::string const& font_hint); void SetExtraHint(std::string const& extra_hint, std::string const& font_hint); void Activate() {} void Deactivate() {} virtual bool AcceptKeyNavFocus() { return true; } std::string GetLabel() const; std::string GetExtraText() const; nux::Property scale; protected: virtual long ComputeContentSize(); virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) {} virtual void RecvClick(int x, int y, unsigned long button_flags, unsigned long key_flags); void Init(); void InitTheme(); void RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state); void RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr); void BuildLayout(std::string const& label, std::string const& icon_hint, std::string const& extra_hint); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: typedef std::unique_ptr NuxCairoPtr; NuxCairoPtr cr_prelight_; NuxCairoPtr cr_active_; NuxCairoPtr cr_normal_; NuxCairoPtr cr_focus_; nux::Geometry cached_geometry_; std::string action_hint_; std::string icon_hint_; std::string font_hint_; std::string extra_hint_; std::string extra_font_hint_; nux::ObjectPtr image_; nux::ObjectPtr static_text_; nux::ObjectPtr extra_text_; void UpdateScale(double scale); }; } // namespace dash } // namespace unity #endif // ACTIONBUTTON_H ./dash/previews/Tracks.cpp0000644000004100000410000001021313437202764015766 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "Tracks.h" #include #include #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include namespace unity { namespace dash { namespace previews { namespace { DECLARE_LOGGER(logger, "unity.dash.preview.music.tracks"); const RawPixel CHILDREN_SPACE = 1_em; } NUX_IMPLEMENT_OBJECT_TYPE(Tracks); Tracks::Tracks(dash::Tracks::Ptr tracks, NUX_FILE_LINE_DECL) : ScrollView(NUX_FILE_LINE_PARAM) , tracks_(tracks) { SetupViews(); if (tracks_) { sig_conn_.Add(tracks_->track_added.connect(sigc::mem_fun(this, &Tracks::OnTrackAdded))); sig_conn_.Add(tracks_->track_changed.connect(sigc::mem_fun(this, &Tracks::OnTrackUpdated))); sig_conn_.Add(tracks_->track_removed.connect(sigc::mem_fun(this, &Tracks::OnTrackRemoved))); // Add what we've got. for (std::size_t i = 0; i < tracks_->count.Get(); ++i) OnTrackAdded(tracks_->RowAtIndex(i)); } UpdateScale(scale); scale.changed.connect(sigc::mem_fun(this, &Tracks::UpdateScale)); } std::string Tracks::GetName() const { return "Tracks"; } void Tracks::AddProperties(debug::IntrospectionData& introspection) { introspection .add("track-count", m_tracks.size()); } void Tracks::SetupViews() { EnableHorizontalScrollBar(false); layout_ = new nux::VLayout(); layout_->SetPadding(0, previews::Style::Instance().GetDetailsRightMargin().CP(scale), 0, 0); layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); SetLayout(layout_); } void Tracks::OnTrackUpdated(dash::Track const& track_row) { auto pos = m_tracks.find(track_row.uri.Get()); if (pos == m_tracks.end()) return; pos->second->Update(track_row); pos->second->scale = scale(); } void Tracks::OnTrackAdded(dash::Track const& track_row) { LOG_TRACE(logger) << "OnTrackAdded for " << track_row.title.Get(); std::string track_uri = track_row.uri.Get(); if (m_tracks.find(track_uri) != m_tracks.end()) return; previews::Style& style = dash::previews::Style::Instance(); previews::Track::Ptr track_view(new previews::Track(NUX_TRACKER_LOCATION)); AddChild(track_view.GetPointer()); track_view->Update(track_row); track_view->SetMinimumHeight(style.GetTrackHeight().CP(scale)); track_view->SetMaximumHeight(style.GetTrackHeight().CP(scale)); track_view->scale = scale(); layout_->AddView(track_view.GetPointer(), 0); m_tracks[track_uri] = track_view; ComputeContentSize(); } void Tracks::OnTrackRemoved(dash::Track const& track_row) { LOG_TRACE(logger) << "OnTrackRemoved for " << track_row.title.Get(); auto pos = m_tracks.find(track_row.uri.Get()); if (pos == m_tracks.end()) return; RemoveChild(pos->second.GetPointer()); layout_->RemoveChildObject(pos->second.GetPointer()); ComputeContentSize(); } void Tracks::UpdateScale(double scale) { int track_height = Style::Instance().GetTrackHeight().CP(scale); for (auto const& track : m_tracks) { track.second->SetMinimumHeight(track_height); track.second->SetMaximumHeight(track_height); track.second->scale = scale; } if (layout_) { layout_->SetPadding(0, Style::Instance().GetDetailsRightMargin().CP(scale), 0, 0); layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); } QueueRelayout(); QueueDraw(); } } // namespace previews } // namespace dash } // namespace unity ./dash/previews/Preview.cpp0000644000004100000410000002174013437202764016167 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "Preview.h" #include "TabIterator.h" #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/CoverArt.h" #include #include #include #include #include "ActionButton.h" #include "GenericPreview.h" #include "ApplicationPreview.h" #include "ErrorPreview.h" #include "MusicPreview.h" #include "MoviePreview.h" #include "MusicPaymentPreview.h" #include "SocialPreview.h" #include "PreviewInfoHintWidget.h" #include "ActionButton.h" namespace unity { namespace dash { namespace previews { DECLARE_LOGGER(logger, "unity.dash.preview.view"); previews::Preview::Ptr Preview::PreviewForModel(dash::Preview::Ptr model) { if (!model) { LOG_WARN(logger) << "Unable to create Preview object"; return previews::Preview::Ptr(); } if (model->renderer_name == "preview-generic") { return Preview::Ptr(new GenericPreview(model)); } else if (model->renderer_name == "preview-payment") { dash::PaymentPreview* payment_preview_model = dynamic_cast( model.get()); if (payment_preview_model->preview_type.Get() == dash::PaymentPreview::MUSIC) { return Preview::Ptr(new MusicPaymentPreview(model)); } else { return Preview::Ptr(new ErrorPreview(model)); } } else if (model->renderer_name == "preview-application") { return Preview::Ptr(new ApplicationPreview(model)); } else if (model->renderer_name == "preview-music") { return Preview::Ptr(new MusicPreview(model)); } else if (model->renderer_name == "preview-movie") { return Preview::Ptr(new MoviePreview(model)); } else if (model->renderer_name == "preview-social") { return Preview::Ptr(new SocialPreview(model)); } else { LOG_WARN(logger) << "Unable to create Preview for renderer: " << model->renderer_name.Get() << "; using generic"; } return Preview::Ptr(new GenericPreview(model)); } NUX_IMPLEMENT_OBJECT_TYPE(Preview); Preview::Preview(dash::Preview::Ptr preview_model) : View(NUX_TRACKER_LOCATION) , scale(1.0f) , preview_model_(preview_model) , tab_iterator_(new TabIterator()) , full_data_layout_(nullptr) , image_(nullptr) , title_(nullptr) , subtitle_(nullptr) , preview_container_(new PreviewContainer) { scale.changed.connect(sigc::mem_fun(this, &Preview::UpdateScale)); } Preview::~Preview() { delete tab_iterator_; } std::string Preview::GetName() const { return "Preview"; } void Preview::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("uri", preview_model_->preview_result.uri); } void Preview::OnActionActivated(ActionButton* button, std::string const& id) { if (preview_model_) preview_model_->PerformAction(id); } nux::Layout* Preview::BuildGridActionsLayout(dash::Preview::ActionPtrList actions, std::list& buttons) { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout* actions_layout_v = new nux::VLayout(); actions_layout_v->SetSpaceBetweenChildren(style.GetSpaceBetweenActions().CP(scale)); uint rows = actions.size() / 2 + ((actions.size() % 2 > 0) ? 1 : 0); uint action_iter = 0; for (uint i = 0; i < rows; i++) { nux::HLayout* actions_layout_h = new TabIteratorHLayout(tab_iterator_); actions_layout_h->SetSpaceBetweenChildren(style.GetSpaceBetweenActions().CP(scale)); for (uint j = 0; j < 2 && action_iter < actions.size(); j++, action_iter++) { dash::Preview::ActionPtr action = actions[action_iter]; ActionButton* button = new ActionButton(action->id, action->display_name, action->icon_hint, NUX_TRACKER_LOCATION); tab_iterator_->Append(button); AddChild(button); button->SetFont(style.action_font()); button->SetExtraHint(action->extra_text, style.action_extra_font()); button->activate.connect(sigc::mem_fun(this, &Preview::OnActionActivated)); buttons.push_back(button); actions_layout_h->AddView(button, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_BEGIN); } actions_layout_v->AddLayout(actions_layout_h, 0, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_BEGIN); } return actions_layout_v; } nux::Layout* Preview::BuildVerticalActionsLayout(dash::Preview::ActionPtrList actions, std::list& buttons) { previews::Style& style = dash::previews::Style::Instance(); nux::VLayout* actions_layout_v = new TabIteratorVLayout(tab_iterator_); actions_layout_v->SetSpaceBetweenChildren(style.GetSpaceBetweenActions().CP(scale)); uint action_iter = 0; for (uint i = 0; i < actions.size(); i++) { dash::Preview::ActionPtr action = actions[action_iter++]; ActionButton* button = new ActionButton(action->id, action->display_name, action->icon_hint, NUX_TRACKER_LOCATION); tab_iterator_->Append(button); AddChild(button); button->SetFont(style.action_font()); button->SetExtraHint(action->extra_text, style.action_extra_font()); button->activate.connect(sigc::mem_fun(this, &Preview::OnActionActivated)); buttons.push_back(button); actions_layout_v->AddView(button, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_BEGIN); } return actions_layout_v; } void Preview::UpdateCoverArtImage(CoverArt* cover_art) { if (!preview_model_) return; previews::Style& style = dash::previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_->OnMouseDown(x, y, button_flags, key_flags); }; std::string image_hint; if (preview_model_->image.Get()) { glib::String tmp_icon(g_icon_to_string(preview_model_->image.Get())); image_hint = tmp_icon.Str(); } if (!image_hint.empty()) cover_art->SetImage(image_hint); else if (!preview_model_->image_source_uri.Get().empty()) cover_art->GenerateImage(preview_model_->image_source_uri); else cover_art->SetNoImageAvailable(); cover_art->SetFont(style.no_preview_image_font()); cover_art->mouse_click.connect(on_mouse_down); } nux::Area* Preview::FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) { nux::Area* tab_area = tab_iterator_->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); if (tab_area) { return tab_area; } return nullptr; } nux::Area* Preview::KeyNavIteration(nux::KeyNavDirection direction) { return tab_iterator_->KeyNavIteration(direction); } void Preview::SetFirstInTabOrder(nux::InputArea* area) { tab_iterator_->Prepend(area); } void Preview::SetLastInTabOrder(nux::InputArea* area) { tab_iterator_->Append(area); } void Preview::SetTabOrder(nux::InputArea* area, int index) { tab_iterator_->Insert(area, index); } void Preview::SetTabOrderBefore(nux::InputArea* area, nux::InputArea* after) { tab_iterator_->InsertBefore(area, after); } void Preview::SetTabOrderAfter(nux::InputArea* area, nux::InputArea* before) { tab_iterator_->InsertAfter(area, before); } void Preview::RemoveFromTabOrder(nux::InputArea* area) { tab_iterator_->Remove(area); } void Preview::OnNavigateIn() { nux::InputArea* default_focus = tab_iterator_->DefaultFocus(); if (default_focus) nux::GetWindowCompositor().SetKeyFocusArea(default_focus); } sigc::signal Preview::request_close() const { return preview_container_->request_close; } void Preview::UpdateScale(double scale) { if (image_) image_->scale = scale; if (title_) title_->SetScale(scale); if (subtitle_) subtitle_->SetScale(scale); if (description_) description_->SetScale(scale); if (preview_container_) preview_container_->scale = scale; if (preview_info_hints_) preview_info_hints_->scale = scale; for (nux::AbstractButton* button : action_buttons_) { if (ActionButton* bn = dynamic_cast(button)) bn->scale = scale; if (ActionLink* link = dynamic_cast(button)) link->scale = scale; } QueueRelayout(); QueueDraw(); } } // preview } // dash } // unity ./dash/previews/PreviewContainer.h0000644000004100000410000000673613437202764017507 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef PREVIEWCONTAINER_H #define PREVIEWCONTAINER_H #include #include #include #include #include #include "Preview.h" #include "unity-shared/Introspectable.h" #include namespace unity { namespace dash { namespace previews { class PreviewNavigator; class PreviewContent; enum class Navigation : unsigned int { NONE = 0, LEFT = (1<<0), RIGHT = (1<<1), BOTH = LEFT|RIGHT }; class PreviewContainer : public nux::View, public debug::Introspectable { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(PreviewContainer, nux::View); PreviewContainer(NUX_FILE_LINE_PROTO); void Preview(dash::Preview::Ptr preview_model, Navigation direction); // calling this should disable the nav buttons to the left or the right of the preview void DisableNavButton(Navigation button); bool IsNavigationDisabled(Navigation button) const; // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); // For the nav buttons to the left/right of the previews, call when they are activated sigc::signal navigate_left; sigc::signal navigate_right; sigc::signal request_close; nux::Property scale; bool AcceptKeyNavFocus(); nux::Area* KeyNavIteration(nux::KeyNavDirection direction); nux::Area* FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state); nux::Geometry GetLayoutGeometry() const; void OnMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); protected: void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw); void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw); bool InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character); void OnKeyDown(unsigned long event_type, unsigned long event_keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count); private: void SetupViews(); void QueueAnimation(double progress); double GetSwipeAnimationProgress(struct timespec const& current) const; private: void UpdateScale(double scale); // View related nux::HLayout* layout_content_; PreviewNavigator* nav_left_; PreviewNavigator* nav_right_; PreviewContent* preview_layout_; Navigation nav_disabled_; // Animation nux::animation::AnimateValue animation_; friend class PreviewContent; }; } // namespace previews } // namespace dash } // namespace unity #endif //PREVIEWCONTAINER_H ./dash/previews/GenericPreview.cpp0000644000004100000410000002236213437202764017465 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/CoverArt.h" #include #include #include #include #include #include #include "GenericPreview.h" #include "PreviewInfoHintWidget.h" namespace unity { namespace dash { namespace previews { namespace { const RawPixel CHILDREN_SPACE = 12_em; const RawPixel FULL_CHILDREN_SPACE = 16_em; } DECLARE_LOGGER(logger, "unity.dash.preview.generic"); NUX_IMPLEMENT_OBJECT_TYPE(GenericPreview); GenericPreview::GenericPreview(dash::Preview::Ptr preview_model) : Preview(preview_model) , image_data_layout_(nullptr) , preview_info_layout_(nullptr) , preview_info_scroll_(nullptr) , preview_data_layout_(nullptr) , actions_layout_(nullptr) { SetupViews(); UpdateScale(scale); scale.changed.connect(sigc::mem_fun(this, &GenericPreview::UpdateScale)); } GenericPreview::~GenericPreview() { } void GenericPreview::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); gfx_engine.PopClippingRectangle(); } void GenericPreview::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } std::string GenericPreview::GetName() const { return "GenericPreview"; } void GenericPreview::AddProperties(debug::IntrospectionData& introspection) { Preview::AddProperties(introspection); } void GenericPreview::SetupViews() { if (!preview_model_) { LOG_ERROR(logger) << "Could not derive preview model from given parameter."; return; } previews::Style& style = dash::previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_->OnMouseDown(x, y, button_flags, key_flags); }; image_data_layout_ = new nux::HLayout(); image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); ///////////////////// // Image image_ = new CoverArt(); AddChild(image_.GetPointer()); UpdateCoverArtImage(image_.GetPointer()); ///////////////////// ///////////////////// // Data Panel full_data_layout_ = new nux::VLayout(); full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(FULL_CHILDREN_SPACE.CP(scale)); ///////////////////// // Data preview_data_layout_ = new nux::VLayout(); preview_data_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); title_ = new StaticCairoText(preview_model_->title, true, NUX_TRACKER_LOCATION); AddChild(title_.GetPointer()); title_->SetLines(-1); title_->SetFont(style.title_font().c_str()); title_->mouse_click.connect(on_mouse_down); preview_data_layout_->AddView(title_.GetPointer(), 1); if (!preview_model_->subtitle.Get().empty()) { subtitle_ = new StaticCairoText(preview_model_->subtitle, true, NUX_TRACKER_LOCATION); AddChild(subtitle_.GetPointer()); subtitle_->SetLines(-1); subtitle_->SetFont(style.subtitle_size_font().c_str()); subtitle_->mouse_click.connect(on_mouse_down); preview_data_layout_->AddView(subtitle_.GetPointer(), 1); } ///////////////////// ///////////////////// // Description auto* preview_info = new ScrollView(NUX_TRACKER_LOCATION); preview_info_scroll_ = preview_info; preview_info->scale = scale(); preview_info->EnableHorizontalScrollBar(false); preview_info->mouse_click.connect(on_mouse_down); preview_info_layout_ = new nux::VLayout(); preview_info_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); preview_info->SetLayout(preview_info_layout_); if (!preview_model_->description.Get().empty()) { description_ = new StaticCairoText(preview_model_->description, false, NUX_TRACKER_LOCATION); // not escaped! AddChild(description_.GetPointer()); description_->SetFont(style.description_font().c_str()); description_->SetTextAlignment(StaticCairoText::NUX_ALIGN_TOP); description_->SetLines(-style.GetDescriptionLineCount()); description_->SetLineSpacing(style.GetDescriptionLineSpacing()); description_->mouse_click.connect(on_mouse_down); preview_info_layout_->AddView(description_.GetPointer()); } if (!preview_model_->GetInfoHints().empty()) { preview_info_hints_ = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth().CP(scale)); AddChild(preview_info_hints_.GetPointer()); preview_info_hints_->request_close().connect([this]() { preview_container_->request_close.emit(); }); preview_info_layout_->AddView(preview_info_hints_.GetPointer()); } ///////////////////// ///////////////////// // Actions action_buttons_.clear(); actions_layout_ = BuildGridActionsLayout(preview_model_->GetActions(), action_buttons_); actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); /////////////////// full_data_layout_->AddLayout(preview_data_layout_, 0); full_data_layout_->AddView(preview_info, 1); full_data_layout_->AddView(actions_layout_, 0); ///////////////////// image_data_layout_->AddView(image_.GetPointer(), 0); image_data_layout_->AddLayout(full_data_layout_, 1); mouse_click.connect(on_mouse_down); SetLayout(image_data_layout_); } void GenericPreview::PreLayoutManagement() { nux::Geometry geo = GetGeometry(); previews::Style& style = dash::previews::Style::Instance(); nux::Geometry geo_art(geo.x, geo.y, style.GetAppImageAspectRatio() * geo.height, geo.height); int content_width = geo.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale); if (content_width - geo_art.width < style.GetDetailsPanelMinimumWidth().CP(scale)) geo_art.width = std::max(0, content_width - style.GetDetailsPanelMinimumWidth().CP(scale)); image_->SetMinMaxSize(geo_art.width, geo_art.height); int details_width = std::max(0, content_width - geo_art.width); if (title_) { title_->SetMaximumWidth(details_width); } if (subtitle_) { subtitle_->SetMaximumWidth(details_width); } if (description_) { description_->SetMaximumWidth(details_width); } int button_w = CLAMP((details_width - style.GetSpaceBetweenActions().CP(scale)) / 2, 0, style.GetActionButtonMaximumWidth().CP(scale)); int button_h = style.GetActionButtonHeight().CP(scale); for (nux::AbstractButton* button : action_buttons_) button->SetMinMaxSize(button_w, button_h); Preview::PreLayoutManagement(); } void GenericPreview::UpdateScale(double scale) { if (image_) image_->scale = scale; if (preview_info_scroll_) preview_info_scroll_->scale = scale; if (preview_info_hints_) preview_info_hints_->scale = scale; previews::Style& style = dash::previews::Style::Instance(); if (full_data_layout_) { full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(FULL_CHILDREN_SPACE.CP(scale)); } if (image_data_layout_) image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); if (preview_info_layout_) preview_info_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); if (preview_data_layout_) preview_data_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); if (actions_layout_) actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); Preview::UpdateScale(scale); } } } } ./dash/previews/ActionLink.cpp0000644000004100000410000001517313437202764016604 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #include "ActionLink.h" #include #include #include "unity-shared/DashStyle.h" #include "unity-shared/IconTexture.h" #include "unity-shared/StaticCairoText.h" namespace { nux::logging::Logger logger("unity.dash.actionlink"); const double LINK_NORMAL_ALPHA_VALUE = 4; const double LINK_HIGHLIGHTED_ALPHA_VALUE = 1; } namespace unity { namespace dash { ActionLink::ActionLink(std::string const& action_hint, std::string const& label, NUX_FILE_LINE_DECL) : nux::AbstractButton(NUX_FILE_LINE_PARAM) , scale(1.0) , action_hint_(action_hint) , aligment_(StaticCairoText::NUX_ALIGN_CENTRE) , underline_(StaticCairoText::NUX_UNDERLINE_SINGLE) { Init(); BuildLayout(label); UpdateScale(scale); scale.changed.connect(sigc::mem_fun(this, &ActionLink::UpdateScale)); } std::string ActionLink::GetName() const { return "ActionLink"; } void ActionLink::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("action", action_hint_) .add("label", label_) .add("font-hint", font_hint) .add("active", active_) .add("text-aligment", text_aligment) .add("underline-state", underline_state); } void ActionLink::Init() { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(true); // set properties to ensure that we do redraw when one of them changes text_aligment.SetSetterFunction(sigc::mem_fun(this, &ActionLink::set_aligment)); text_aligment.SetGetterFunction(sigc::mem_fun(this, &ActionLink::get_aligment)); underline_state.SetSetterFunction(sigc::mem_fun(this, &ActionLink::set_underline)); underline_state.SetGetterFunction(sigc::mem_fun(this, &ActionLink::get_underline)); font_hint.SetSetterFunction(sigc::mem_fun(this, &ActionLink::set_font_hint)); font_hint.SetGetterFunction(sigc::mem_fun(this, &ActionLink::get_font_hint)); key_nav_focus_change.connect([this] (nux::Area*, bool, nux::KeyNavDirection) { QueueDraw(); }); key_nav_focus_activate.connect([this](nux::Area*) { if (GetInputEventSensitivity()) activate.emit(this, action_hint_); }); } void ActionLink::BuildLayout(std::string const& label) { if (label != label_) { label_ = label; if (static_text_) { static_text_.Release(); static_text_ = NULL; } if (!label_.empty()) { static_text_ = new StaticCairoText(label_, true, NUX_TRACKER_LOCATION); if (!font_hint_.empty()) static_text_->SetFont(font_hint_); static_text_->SetScale(scale); static_text_->SetInputEventSensitivity(false); static_text_->SetTextAlignment(aligment_); static_text_->SetUnderline(underline_); } } RemoveLayout(); nux::VLayout* layout = new nux::VLayout(); if (static_text_) { layout->AddView(static_text_.GetPointer(), 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_END); } SetLayout(layout); ComputeContentSize(); QueueDraw(); } int ActionLink::GetLinkAlpha(nux::ButtonVisualState state, bool keyboardFocus) { if (keyboardFocus || state == nux::ButtonVisualState::VISUAL_STATE_PRELIGHT) return LINK_HIGHLIGHTED_ALPHA_VALUE; else return LINK_NORMAL_ALPHA_VALUE; } void ActionLink::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry const& geo = GetGeometry(); gPainter.PaintBackground(GfxContext, geo); // set up our texture mode nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); // clear what is behind us unsigned int alpha = 0, src = 0, dest = 0; // set the alpha of the text according to its state static_text_->SetTextAlpha(GetLinkAlpha(GetVisualState(), HasKeyboardFocus())); GfxContext.GetRenderStates().GetBlend(alpha, src, dest); GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::Color col = nux::color::Black; col.alpha = 0; GfxContext.QRP_Color(geo.x, geo.y, geo.width, geo.height, col); GfxContext.GetRenderStates().SetBlend(alpha, src, dest); if (GetCompositionLayout()) { gPainter.PushPaintLayerStack(); { GfxContext.PushClippingRectangle(geo); gPainter.PushPaintLayerStack(); GetCompositionLayout()->ProcessDraw(GfxContext, force_draw); gPainter.PopPaintLayerStack(); GfxContext.PopClippingRectangle(); } gPainter.PopPaintLayerStack(); } } void ActionLink::RecvClick(int x, int y, unsigned long button_flags, unsigned long key_flags) { activate.emit(this, action_hint_); } bool ActionLink::set_aligment(StaticCairoText::AlignState aligment) { if(static_text_ && aligment_ != aligment) { static_text_->SetTextAlignment(aligment_); aligment_ = aligment; ComputeContentSize(); QueueDraw(); } return true; } StaticCairoText::AlignState ActionLink::get_aligment() { return aligment_; } bool ActionLink::set_underline(StaticCairoText::UnderlineState underline) { if(static_text_ && underline_ != underline) { static_text_->SetUnderline(underline_); underline_ = underline; ComputeContentSize(); QueueDraw(); } return true; } StaticCairoText::UnderlineState ActionLink::get_underline() { return underline_; } bool ActionLink::set_font_hint(std::string font_hint) { if(static_text_ && font_hint_ != font_hint) { static_text_->SetFont(font_hint_); font_hint_ = font_hint; ComputeContentSize(); QueueDraw(); } return true; } std::string ActionLink::get_font_hint() { return font_hint_; } std::string ActionLink::GetLabel() const { return label_; } void ActionLink::UpdateScale(double scale) { if (static_text_) static_text_->SetScale(scale); QueueRelayout(); QueueDraw(); } } // namespace dash } // namespace unity ./dash/previews/MusicPreview.h0000644000004100000410000000403713437202764016635 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef MUSICPREVIEW_H #define MUSICPREVIEW_H #include "Preview.h" #include "unity-shared/OverlayScrollView.h" #include "unity-shared/IconTexture.h" namespace unity { namespace dash { namespace previews { class Tracks; class MusicPreview : public Preview { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(MusicPreview, Preview); MusicPreview(dash::Preview::Ptr preview_model); ~MusicPreview(); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void PreLayoutManagement(); virtual void SetupViews(); virtual void UpdateScale(double scale); virtual void OnNavigateOut(); bool HasUbuntuOneCredentials(); protected: nux::ObjectPtr tracks_; nux::ObjectPtr warning_msg_; nux::ObjectPtr warning_texture_; std::string no_credentials_message_; nux::Layout* actions_layout_; nux::HLayout* image_data_layout_; nux::VLayout* icon_layout_; nux::VLayout* album_data_layout_; }; } } } #endif // MUSICPREVIEW_H ./dash/previews/SocialPreviewContent.h0000644000004100000410000000530313437202764020317 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * Ken VanDine * */ #ifndef SOCIAL_PREVIEW_CONTENT_H #define SOCIAL_PREVIEW_CONTENT_H #include #include #include #include #include "unity-shared/StaticCairoText.h" #include "unity-shared/Introspectable.h" #include "PreviewContainer.h" namespace unity { namespace dash { namespace previews { class SocialPreviewContent : public nux::View, public unity::debug::Introspectable { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(SocialPreviewContent, nux::View); SocialPreviewContent(std::string const& text, NUX_FILE_LINE_PROTO); nux::Property scale; void SetText(std::string const& text); sigc::signal request_close() const { return preview_container_.request_close; } protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void PreLayoutManagement(); virtual bool AcceptKeyNavFocus() { return false; } void SetupViews(); void UpdateBaloonTexture(); void RedrawBubble(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state); void DrawBubble(cairo_t* cr, double line_width, double radius, double x, double y, double width, double height, double tailPosition, double tailWidth); virtual std::string GetName() const; virtual void AddProperties(debug::IntrospectionData&); private: void UpdateScale(double scale); nux::ObjectPtr text_; typedef std::unique_ptr NuxCairoPtr; NuxCairoPtr cr_bubble_; PreviewContainer preview_container_; }; } } } #endif // SOCIAL_PREVIEW_CONTENT_H ./dash/previews/TabIterator.h0000644000004100000410000000471413437202764016435 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * Manuel de la Pena * */ #include #include #include #include #include namespace unity { namespace dash { namespace previews { class TabIterator { public: TabIterator() {} void Prepend(nux::InputArea* area); void Append(nux::InputArea* area); void Insert(nux::InputArea* area, unsigned index); void InsertBefore(nux::InputArea* area, nux::InputArea* after); void InsertAfter(nux::InputArea* area, nux::InputArea* before); void Remove(nux::InputArea* area); std::list const& GetTabAreas() const; nux::InputArea* DefaultFocus() const; nux::InputArea* FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state); nux::Area* KeyNavIteration(nux::KeyNavDirection direction); private: friend class TestTabIterator; std::list areas_; }; class TabIteratorHLayout : public nux::HLayout { public: TabIteratorHLayout(TabIterator* iterator) :tab_iterator_(iterator) { } nux::Area* KeyNavIteration(nux::KeyNavDirection direction) { return tab_iterator_->KeyNavIteration(direction); } private: TabIterator* tab_iterator_; }; class TabIteratorVLayout : public nux::VLayout { public: TabIteratorVLayout(TabIterator* iterator) :tab_iterator_(iterator) { } nux::Area* KeyNavIteration(nux::KeyNavDirection direction) { return tab_iterator_->KeyNavIteration(direction); } private: TabIterator* tab_iterator_; }; } // previews } // dash } // unity ./dash/previews/ApplicationPreview.cpp0000644000004100000410000003565313437202764020363 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/CoverArt.h" #include "unity-shared/IconTexture.h" #include #include #include #include #include #include #include "config.h" #include #include "ApplicationPreview.h" #include "ActionButton.h" #include "PreviewInfoHintWidget.h" #include "PreviewRatingsWidget.h" namespace unity { namespace dash { namespace previews { namespace { const RawPixel ICON_SPACE_CHILDREN = 3_em; const RawPixel DATA_SPACE_CHILDREN = 16_em; const RawPixel INFO_SPACE_CHILDREN = 12_em; const RawPixel COPYRIGHT_SPACE_CHILDREN = 8_em; const RawPixel ICON_SIZE = 72_em; } DECLARE_LOGGER(logger, "unity.dash.preview.application"); NUX_IMPLEMENT_OBJECT_TYPE(ApplicationPreview); ApplicationPreview::ApplicationPreview(dash::Preview::Ptr preview_model) : Preview(preview_model) , title_subtitle_layout_(nullptr) , image_data_layout_(nullptr) , main_app_info_(nullptr) , icon_layout_(nullptr) , app_data_layout_(nullptr) , app_updated_copywrite_layout_(nullptr) , app_info_layout_(nullptr) , app_info_scroll_(nullptr) , actions_layout_(nullptr) { SetupViews(); } ApplicationPreview::~ApplicationPreview() { } void ApplicationPreview::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); gfx_engine.PopClippingRectangle(); } void ApplicationPreview::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } std::string ApplicationPreview::GetName() const { return "ApplicationPreview"; } void ApplicationPreview::AddProperties(debug::IntrospectionData& introspection) { Preview::AddProperties(introspection); } void ApplicationPreview::SetupViews() { dash::ApplicationPreview* app_preview_model = dynamic_cast(preview_model_.get()); if (!app_preview_model) { LOG_ERROR(logger) << "Could not derive application preview model from given parameter."; return; } previews::Style& style = dash::previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_->OnMouseDown(x, y, button_flags, key_flags); }; image_data_layout_ = new nux::HLayout(); image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); ///////////////////// // Image image_ = new CoverArt(); image_->scale = scale(); AddChild(image_.GetPointer()); UpdateCoverArtImage(image_.GetPointer()); ///////////////////// ///////////////////// // App Data Panel full_data_layout_ = new nux::VLayout(); full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(DATA_SPACE_CHILDREN.CP(scale)); ///////////////////// // Main App Info main_app_info_ = new nux::HLayout(); main_app_info_->SetSpaceBetweenChildren(style.GetSpaceBetweenIconAndDetails().CP(scale)); ///////////////////// // Icon Layout icon_layout_ = new nux::VLayout(); icon_layout_->SetSpaceBetweenChildren(ICON_SPACE_CHILDREN.CP(scale)); app_icon_ = new IconTexture(app_preview_model->app_icon.Get().RawPtr() ? g_icon_to_string(app_preview_model->app_icon.Get().RawPtr()) : "", ICON_SIZE.CP(scale)); AddChild(app_icon_.GetPointer()); app_icon_->SetMinimumSize(style.GetAppIconAreaWidth().CP(scale), style.GetAppIconAreaWidth().CP(scale)); app_icon_->SetMaximumSize(style.GetAppIconAreaWidth().CP(scale), style.GetAppIconAreaWidth().CP(scale)); app_icon_->mouse_click.connect(on_mouse_down); icon_layout_->AddView(app_icon_.GetPointer(), 0); if (app_preview_model->rating >= 0) { app_rating_ = new PreviewRatingsWidget(); AddChild(app_rating_.GetPointer()); app_rating_->scale = scale(); app_rating_->SetMaximumHeight(style.GetRatingWidgetHeight().CP(scale)); app_rating_->SetMinimumHeight(style.GetRatingWidgetHeight().CP(scale)); app_rating_->SetRating(app_preview_model->rating); app_rating_->SetReviews(app_preview_model->num_ratings); app_rating_->request_close().connect([this]() { preview_container_->request_close.emit(); }); icon_layout_->AddView(app_rating_.GetPointer(), 0); } ///////////////////// ///////////////////// // Data app_data_layout_ = new nux::VLayout(); app_data_layout_->SetSpaceBetweenChildren(DATA_SPACE_CHILDREN.CP(scale)); title_subtitle_layout_ = new nux::VLayout(); title_subtitle_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); title_ = new StaticCairoText(preview_model_->title, true, NUX_TRACKER_LOCATION); AddChild(title_.GetPointer()); title_->SetLines(-1); title_->SetScale(scale); title_->SetFont(style.title_font().c_str()); title_->mouse_click.connect(on_mouse_down); title_subtitle_layout_->AddView(title_.GetPointer(), 1); if (!preview_model_->subtitle.Get().empty()) { subtitle_ = new StaticCairoText(preview_model_->subtitle, true, NUX_TRACKER_LOCATION); AddChild(subtitle_.GetPointer()); subtitle_->SetFont(style.subtitle_size_font().c_str()); subtitle_->SetLines(-1); subtitle_->SetScale(scale); subtitle_->mouse_click.connect(on_mouse_down); title_subtitle_layout_->AddView(subtitle_.GetPointer(), 1); } app_updated_copywrite_layout_ = new nux::VLayout(); app_updated_copywrite_layout_->SetSpaceBetweenChildren(COPYRIGHT_SPACE_CHILDREN.CP(scale)); if (!app_preview_model->license.Get().empty()) { license_ = new StaticCairoText(app_preview_model->license, true, NUX_TRACKER_LOCATION); AddChild(license_.GetPointer()); license_->SetFont(style.app_license_font().c_str()); license_->SetLines(-1); license_->SetScale(scale); license_->mouse_click.connect(on_mouse_down); app_updated_copywrite_layout_->AddView(license_.GetPointer(), 1); } if (!app_preview_model->last_update.Get().empty()) { std::stringstream last_update; last_update << _("Last Updated") << " " << app_preview_model->last_update.Get(); last_update_ = new StaticCairoText(last_update.str(), true, NUX_TRACKER_LOCATION); AddChild(last_update_.GetPointer()); last_update_->SetFont(style.app_last_update_font().c_str()); last_update_->SetScale(scale); last_update_->mouse_click.connect(on_mouse_down); app_updated_copywrite_layout_->AddView(last_update_.GetPointer(), 1); } if (!app_preview_model->copyright.Get().empty()) { copywrite_ = new StaticCairoText(app_preview_model->copyright, true, NUX_TRACKER_LOCATION); AddChild(copywrite_.GetPointer()); copywrite_->SetFont(style.app_copywrite_font().c_str()); copywrite_->SetLines(-1); copywrite_->SetScale(scale); copywrite_->mouse_click.connect(on_mouse_down); app_updated_copywrite_layout_->AddView(copywrite_.GetPointer(), 1); } app_data_layout_->AddLayout(title_subtitle_layout_); app_data_layout_->AddLayout(app_updated_copywrite_layout_); // buffer space ///////////////////// main_app_info_->AddLayout(icon_layout_, 0); main_app_info_->AddLayout(app_data_layout_, 1); ///////////////////// ///////////////////// // Description auto* app_info = new ScrollView(NUX_TRACKER_LOCATION); app_info_scroll_ = app_info; app_info->scale = scale(); app_info->EnableHorizontalScrollBar(false); app_info->mouse_click.connect(on_mouse_down); app_info_layout_ = new nux::VLayout(); app_info_layout_->SetSpaceBetweenChildren(INFO_SPACE_CHILDREN.CP(scale)); app_info->SetLayout(app_info_layout_); if (!preview_model_->description.Get().empty()) { description_ = new StaticCairoText(preview_model_->description, false, NUX_TRACKER_LOCATION); // not escaped! AddChild(description_.GetPointer()); description_->SetFont(style.description_font().c_str()); description_->SetTextAlignment(StaticCairoText::NUX_ALIGN_TOP); description_->SetLines(-style.GetDescriptionLineCount()); description_->SetLineSpacing(style.GetDescriptionLineSpacing()); description_->mouse_click.connect(on_mouse_down); app_info_layout_->AddView(description_.GetPointer()); } if (!preview_model_->GetInfoHints().empty()) { preview_info_hints_ = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth().CP(scale)); AddChild(preview_info_hints_.GetPointer()); preview_info_hints_->request_close().connect([this]() { preview_container_->request_close.emit(); }); app_info_layout_->AddView(preview_info_hints_.GetPointer()); } ///////////////////// ///////////////////// // Actions action_buttons_.clear(); actions_layout_ = BuildGridActionsLayout(preview_model_->GetActions(), action_buttons_); actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); /////////////////// full_data_layout_->AddLayout(main_app_info_, 0); full_data_layout_->AddView(app_info, 1); full_data_layout_->AddLayout(actions_layout_, 0); ///////////////////// image_data_layout_->AddView(image_.GetPointer(), 0); image_data_layout_->AddLayout(full_data_layout_, 1); mouse_click.connect(on_mouse_down); SetLayout(image_data_layout_); } void ApplicationPreview::PreLayoutManagement() { nux::Geometry geo = GetGeometry(); previews::Style& style = dash::previews::Style::Instance(); nux::Geometry geo_art(geo.x, geo.y, style.GetAppImageAspectRatio() * geo.height, geo.height); int content_width = geo.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale); if (content_width - geo_art.width < style.GetDetailsPanelMinimumWidth().CP(scale)) geo_art.width = std::max(0, content_width - style.GetDetailsPanelMinimumWidth().CP(scale)); image_->SetMinMaxSize(geo_art.width, geo_art.height); int details_width = std::max(0, content_width - geo_art.width); int top_app_info_max_width = std::max(0, details_width - style.GetAppIconAreaWidth().CP(scale) - style.GetSpaceBetweenIconAndDetails().CP(scale)); if (title_) { title_->SetMaximumWidth(top_app_info_max_width); } if (subtitle_) { subtitle_->SetMaximumWidth(top_app_info_max_width); } if (license_) { license_->SetMaximumWidth(top_app_info_max_width); } if (last_update_) { last_update_->SetMaximumWidth(top_app_info_max_width); } if (copywrite_) { copywrite_->SetMaximumWidth(top_app_info_max_width); } if (description_) { description_->SetMaximumWidth(details_width); } int button_w = CLAMP((details_width - style.GetSpaceBetweenActions().CP(scale)) / 2, 0, style.GetActionButtonMaximumWidth().CP(scale)); int button_h = style.GetActionButtonHeight().CP(scale); for (nux::AbstractButton* button : action_buttons_) button->SetMinMaxSize(button_w, button_h); Preview::PreLayoutManagement(); } void ApplicationPreview::UpdateScale(double scale) { Preview::UpdateScale(scale); previews::Style& style = dash::previews::Style::Instance(); if (app_icon_) { app_icon_->SetSize(ICON_SIZE.CP(scale)); app_icon_->SetMinimumSize(style.GetAppIconAreaWidth().CP(scale), style.GetAppIconAreaWidth().CP(scale)); app_icon_->SetMaximumSize(style.GetAppIconAreaWidth().CP(scale), style.GetAppIconAreaWidth().CP(scale)); app_icon_->ReLoadIcon(); } if (app_info_scroll_) app_info_scroll_->scale = scale; if (license_) license_->SetScale(scale); if (last_update_) last_update_->SetScale(scale); if (copywrite_) copywrite_->SetScale(scale); if (app_rating_) { app_rating_->SetMaximumHeight(style.GetRatingWidgetHeight().CP(scale)); app_rating_->SetMinimumHeight(style.GetRatingWidgetHeight().CP(scale)); app_rating_->scale = scale; } if (image_data_layout_) image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); if (full_data_layout_) { full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(DATA_SPACE_CHILDREN.CP(scale)); } if (main_app_info_) main_app_info_->SetSpaceBetweenChildren(style.GetSpaceBetweenIconAndDetails().CP(scale)); if (icon_layout_) icon_layout_->SetSpaceBetweenChildren(ICON_SPACE_CHILDREN.CP(scale)); if (app_data_layout_) app_data_layout_->SetSpaceBetweenChildren(DATA_SPACE_CHILDREN.CP(scale)); if (title_subtitle_layout_) title_subtitle_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); if (app_info_layout_) app_info_layout_->SetSpaceBetweenChildren(INFO_SPACE_CHILDREN.CP(scale)); if (actions_layout_) actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); if (app_updated_copywrite_layout_) app_updated_copywrite_layout_->SetSpaceBetweenChildren(COPYRIGHT_SPACE_CHILDREN.CP(scale)); } } // namespace previews } // namespace dash } // namepsace unity ./dash/previews/PreviewInfoHintWidget.h0000644000004100000410000000475613437202764020447 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef PREVIEWINFOHINTWIDGET_H #define PREVIEWINFOHINTWIDGET_H #include #include #include #include "unity-shared/Introspectable.h" #include "PreviewContainer.h" namespace unity { class StaticCairoText; } namespace unity { namespace dash { namespace previews { class PreviewInfoHintWidget : public nux::View, public debug::Introspectable { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(PreviewInfoHintWidget, nux::View); PreviewInfoHintWidget(dash::Preview::Ptr preview_model, int icon_size); nux::Property scale; // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); void PreLayoutManagement(); sigc::signal request_close() const { return preview_container_.request_close; } protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual bool AcceptKeyNavFocus() { return false; } void SetupBackground(); void SetupViews(); void IconLoaded(std::string const& texid, unsigned size, glib::Object const& pixbuf, std::string icon_name); protected: int icon_size_; nux::HLayout* layout_; nux::VLayout* info_names_layout_; nux::VLayout* info_values_layout_; dash::Preview::Ptr preview_model_; private: PreviewContainer preview_container_; void UpdateScale(double scale); }; } // namespace previews } // namespace dash } // namespace unity #endif //PREVIEWINFOHINTWIDGET_H ./dash/previews/Track.cpp0000644000004100000410000003652513437202764015621 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "Track.h" #include "unity-shared/IntrospectableWrappers.h" #include #include #include #include #include #include namespace unity { namespace dash { namespace previews { namespace { const RawPixel LAYOUT_SPACING = 6_em; const RawPixel TITLE_PADDING = 3_em; } class TmpView : public nux::View { public: TmpView(NUX_FILE_LINE_PROTO): View(NUX_FILE_LINE_PARAM) {} virtual ~TmpView() {} virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) {} virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); } virtual bool AcceptKeyNavFocus() { return false; } }; NUX_IMPLEMENT_OBJECT_TYPE(Track); class TrackProgressLayer : public nux::AbstractPaintLayer { public: TrackProgressLayer(nux::Color const& left_color, nux::Color const& right_color, nux::Color const& progress_color, bool write_alpha = false, nux::ROPConfig const& ROP = nux::ROPConfig::Default) : left_color_(left_color) , right_color_(right_color) , progress_color_(progress_color) , write_alpha_(write_alpha) , rop_(ROP) {} virtual ~TrackProgressLayer() {} virtual void Renderlayer(nux::GraphicsEngine& graphics_engine) { unsigned int current_red_mask; unsigned int current_green_mask; unsigned int current_blue_mask; unsigned int current_alpha_mask; unsigned int current_alpha_blend; unsigned int current_src_blend_factor; unsigned int current_dest_blend_factor; // Get the current color mask and blend states. They will be restored later. graphics_engine.GetRenderStates().GetColorMask(current_red_mask, current_green_mask, current_blue_mask, current_alpha_mask); // Get the current blend states. They will be restored later. graphics_engine.GetRenderStates().GetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); graphics_engine.GetRenderStates().SetColorMask(GL_TRUE, GL_TRUE, GL_TRUE, write_alpha_ ? GL_TRUE : GL_FALSE); graphics_engine.GetRenderStates().SetBlend(rop_.Blend, rop_.SrcBlend, rop_.DstBlend); // Gradient to current pos. graphics_engine.QRP_Color(geometry_.x, geometry_.y, geometry_.width, geometry_.height, left_color_, left_color_, right_color_, right_color_); int current_progress_pos = geometry_.width > 2 ? (geometry_.x + geometry_.width) - 2 : geometry_.x; // current pos outline. graphics_engine.QRP_Color(current_progress_pos, geometry_.y, MIN(2, geometry_.width), geometry_.height, progress_color_); // Restore Color mask and blend states. graphics_engine.GetRenderStates().SetColorMask(current_red_mask, current_green_mask, current_blue_mask, current_alpha_mask); // Restore the blend state graphics_engine.GetRenderStates().SetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); } virtual nux::AbstractPaintLayer* Clone() const { return new TrackProgressLayer(*this); } private: nux::Color left_color_; nux::Color right_color_; nux::Color progress_color_; bool write_alpha_; nux::ROPConfig rop_; }; Track::Track(NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , scale(1.0) , play_state_(PlayerState::STOPPED) , progress_(0.0) , mouse_over_(false) { SetupBackground(); SetupViews(); scale.changed.connect(sigc::mem_fun(this, &Track::UpdateScale)); } std::string Track::GetName() const { return "Track"; } void Track::AddProperties(debug::IntrospectionData& introspection) { introspection .add("uri", uri_) .add("play-state", (int)play_state_) .add("progress", progress_) .add("playpause-x", track_status_layout_->GetAbsoluteX()) .add("playpause-y", track_status_layout_->GetAbsoluteX()) .add("playpause-width", track_status_layout_->GetGeometry().width) .add("playpause-height", track_status_layout_->GetGeometry().height) .add("playpause-geo", track_status_layout_->GetAbsoluteGeometry()); } void Track::Update(dash::Track const& track) { uri_ = track.uri; title_->SetText(track.title, true); std::stringstream ss_track_number; ss_track_number << track.track_number; track_number_->SetText(ss_track_number.str()); glib::String duration(g_strdup_printf("%d:%.2d", track.length/60, (track.length) % 60)); duration_->SetText(duration); player_connection_ = player_.updated.connect([this](std::string const& uri, PlayerState player_state, double progress) { if (uri != uri_) { // If we're received an update for another track, we're obviously not playing this track anymore. if (progress_ != 0.0 || play_state_ != PlayerState::STOPPED) { progress_ = 0.0; play_state_ = PlayerState::STOPPED; UpdateTrackState(); } return; } progress_ = progress; play_state_ = player_state; UpdateTrackState(); }); QueueDraw(); } void Track::SetupBackground() { nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; focus_layer_.reset(new nux::ColorLayer(nux::Color(0.15f, 0.15f, 0.15f, 0.15f), true, rop)); progress_layer_.reset(new TrackProgressLayer(nux::Color(0.25f, 0.25f, 0.25f, 0.15f), nux::Color(0.05f, 0.05f, 0.05f, 0.15f), nux::Color(0.60f, 0.60f, 0.60f, 0.15f), true, rop)); } void Track::SetupViews() { previews::Style& style = previews::Style::Instance(); nux::HLayout* layout = new nux::HLayout(); layout->SetLeftAndRightPadding(0,0); nux::BaseTexture* tex_play = style.GetPlayIcon(); status_play_ = new IconTexture(tex_play, style.GetStatusIconSize().CP(scale), style.GetStatusIconSize().CP(scale)); status_play_->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); nux::BaseTexture* tex_pause = style.GetPauseIcon(); status_pause_ = new IconTexture(tex_pause, style.GetStatusIconSize().CP(scale), style.GetStatusIconSize().CP(scale)); status_pause_->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); track_number_ = new StaticCairoText("", NUX_TRACKER_LOCATION); track_number_->SetTextAlignment(StaticCairoText::NUX_ALIGN_CENTRE); track_number_->SetTextVerticalAlignment(StaticCairoText::NUX_ALIGN_CENTRE); track_number_->SetLines(-1); track_number_->SetScale(scale); track_number_->SetFont(style.track_font()); title_ = new StaticCairoText("", NUX_TRACKER_LOCATION); title_->SetTextAlignment(StaticCairoText::NUX_ALIGN_LEFT); title_->SetTextVerticalAlignment(StaticCairoText::NUX_ALIGN_CENTRE); title_->SetLines(-1); title_->SetScale(scale); title_->SetFont(style.track_font()); duration_ = new StaticCairoText("", NUX_TRACKER_LOCATION); duration_->SetTextEllipsize(StaticCairoText::NUX_ELLIPSIZE_NONE); duration_->SetTextAlignment(StaticCairoText::NUX_ALIGN_RIGHT); duration_->SetTextVerticalAlignment(StaticCairoText::NUX_ALIGN_CENTRE); duration_->SetLines(-1); duration_->SetMinimumWidth(style.GetMusicDurationWidth().CP(scale)); duration_->SetMaximumWidth(style.GetMusicDurationWidth().CP(scale)); duration_->SetScale(scale); duration_->SetFont(style.track_font()); // Layouts // stick text fields in a layout so they don't alter thier geometry. status_play_layout_ = new TmpView(); status_play_layout_->SetLayout(new nux::HLayout()); status_play_layout_->GetLayout()->AddSpace(0, 1); status_play_layout_->GetLayout()->AddView(status_play_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); status_play_layout_->GetLayout()->AddSpace(0, 1); status_pause_layout_ = new TmpView(); status_pause_layout_->SetLayout(new nux::HLayout()); status_pause_layout_->GetLayout()->AddSpace(0, 1); status_pause_layout_->GetLayout()->AddView(status_pause_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); status_pause_layout_->GetLayout()->AddSpace(0, 1); track_number_layout_ = new TmpView(); track_number_layout_->SetLayout(new nux::HLayout()); track_number_layout_->GetLayout()->AddSpace(0, 1); track_number_layout_->GetLayout()->AddView(track_number_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); track_number_layout_->GetLayout()->AddSpace(0, 1); track_status_layout_ = new nux::LayeredLayout(); track_status_layout_->AddLayer(status_play_layout_, true); track_status_layout_->AddLayer(status_pause_layout_, true); track_status_layout_->AddLayer(track_number_layout_, true); track_status_layout_->SetActiveLayer(track_number_layout_); title_layout_ = new nux::HLayout(); title_layout_->SetLeftAndRightPadding(TITLE_PADDING.CP(scale)); title_layout_->AddView(title_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); title_layout_->AddSpace(0, 0); duration_layout_ = new nux::HLayout(); duration_layout_->AddSpace(0, 1); duration_layout_->AddView(duration_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); layout->AddLayout(track_status_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); layout->AddLayout(title_layout_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); layout->AddLayout(duration_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); SetLayout(layout); mouse_enter.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseEnter)); mouse_leave.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseLeave)); mouse_click.connect([this](int, int, unsigned long, unsigned long) { switch (play_state_) { case PlayerState::PLAYING: player_.Pause(); break; case PlayerState::PAUSED: player_.Resume(); break; case PlayerState::STOPPED: default: player_.Play(uri_); break; } }); } void Track::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true); if (HasStatusFocus()) { focus_layer_->SetGeometry(track_status_layout_->GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, focus_layer_->GetGeometry(), focus_layer_.get()); } int progress_width = progress_ * (duration_layout_->GetGeometry().x + duration_layout_->GetGeometry().width - title_layout_->GetGeometry().x); if (progress_width > 0.0) { nux::Geometry geo_progress(title_layout_->GetGeometry().x, base.y, progress_width, base.height); progress_layer_->SetGeometry(geo_progress); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, progress_layer_->GetGeometry(), progress_layer_.get()); } gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); } void Track::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); int pop_layers = 0; if (!IsFullRedraw()) { if (HasStatusFocus()) { nux::GetPainter().PushLayer(gfx_engine, focus_layer_->GetGeometry(), focus_layer_.get()); pop_layers++; } int progress_width = progress_ * (duration_layout_->GetGeometry().x + duration_layout_->GetGeometry().width - title_layout_->GetGeometry().x); if (progress_width > 0.0) { nux::GetPainter().PushLayer(gfx_engine, progress_layer_->GetGeometry(), progress_layer_.get()); pop_layers++; } } else nux::GetPainter().PushPaintLayerStack(); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); if (IsFullRedraw()) nux::GetPainter().PopPaintLayerStack(); else if (pop_layers > 0) nux::GetPainter().PopBackground(pop_layers); gfx_engine.PopClippingRectangle(); } nux::Area* Track::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) { bool mouse_inside = TestMousePointerInclusion(mouse_position, event_type); if (mouse_inside == false) return NULL; return this; } bool Track::HasStatusFocus() const { return mouse_over_ || play_state_ == PlayerState::PLAYING || play_state_ == PlayerState::PAUSED; } void Track::OnTrackControlMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) { mouse_over_ = true; UpdateTrackState(); QueueDraw(); } void Track::OnTrackControlMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) { mouse_over_ = false; UpdateTrackState(); QueueDraw(); } void Track::UpdateTrackState() { if (mouse_over_) { switch (play_state_) { case PlayerState::PLAYING: track_status_layout_->SetActiveLayer(status_pause_layout_); break; case PlayerState::PAUSED: case PlayerState::STOPPED: default: track_status_layout_->SetActiveLayer(status_play_layout_); break; } } else { switch (play_state_) { case PlayerState::PLAYING: track_status_layout_->SetActiveLayer(status_play_layout_); break; case PlayerState::PAUSED: track_status_layout_->SetActiveLayer(status_pause_layout_); break; case PlayerState::STOPPED: default: track_status_layout_->SetActiveLayer(track_number_layout_); break; } } QueueDraw(); } void Track::PreLayoutManagement() { previews::Style& style = previews::Style::Instance(); nux::Geometry const& geo = GetGeometry(); track_status_layout_->SetMinimumWidth(geo.height); track_status_layout_->SetMaximumWidth(geo.height); const int max_width = std::max(GetGeometry().width - geo.height - style.GetMusicDurationWidth().CP(scale) - LAYOUT_SPACING.CP(scale)*2, 0); title_->SetMaximumWidth(max_width); View::PreLayoutManagement(); } void Track::UpdateScale(double scale) { auto& style = Style::Instance(); int icon_size = style.GetStatusIconSize().CP(scale); track_number_->SetScale(scale); title_->SetScale(scale); duration_->SetMaximumWidth(style.GetMusicDurationWidth().CP(scale)); duration_->SetMinimumWidth(style.GetMusicDurationWidth().CP(scale)); duration_->SetScale(scale); title_layout_->SetLeftAndRightPadding(TITLE_PADDING.CP(scale)); status_play_->SetMinMaxSize(icon_size, icon_size); status_pause_->SetMinMaxSize(icon_size, icon_size); QueueRelayout(); QueueDraw(); } } // namespace previews } // namespace dash } // namesapce unity ./dash/previews/ActionLink.h0000644000004100000410000000562313437202764016250 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #ifndef ACTIONLINK_H #define ACTIONLINK_H #include #include #include #include "unity-shared/Introspectable.h" #include "unity-shared/StaticCairoText.h" namespace unity { class IconTexture; namespace dash { class ActionLink : public nux::AbstractButton, public debug::Introspectable { public: ActionLink(std::string const& action_hint, std::string const& label, NUX_FILE_LINE_PROTO); sigc::signal activate; nux::RWProperty text_aligment; nux::RWProperty underline_state; nux::RWProperty font_hint; nux::Property scale; void Activate() {} void Deactivate() {} virtual bool AcceptKeyNavFocus() const { return true; } std::string GetLabel() const; std::string GetExtraText() const; protected: nux::ObjectPtr static_text_; int GetLinkAlpha(nux::ButtonVisualState state, bool keyboardFocus); void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) {} void RecvClick(int x, int y, unsigned long button_flags, unsigned long key_flags); void Init(); void BuildLayout(std::string const& label); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); // this methods/vars could be private but are protected to make testing // easier bool set_aligment(StaticCairoText::AlignState aligment); StaticCairoText::AlignState get_aligment(); bool set_underline(StaticCairoText::UnderlineState underline); StaticCairoText::UnderlineState get_underline(); bool set_font_hint(std::string font_hint); std::string get_font_hint(); std::string action_hint_; std::string font_hint_; StaticCairoText::AlignState aligment_; StaticCairoText::UnderlineState underline_; private: typedef std::unique_ptr NuxCairoPtr; void UpdateScale(double scale); }; } // namespace dash } // namespace unity #endif // ACTIONLINK_H ./dash/previews/Tracks.h0000644000004100000410000000372313437202764015443 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef TRACKS_H #define TRACKS_H #include #include #include #include #include "unity-shared/OverlayScrollView.h" #include "unity-shared/Introspectable.h" #include "Track.h" namespace nux { class VLayout; } namespace unity { namespace dash { class Track; namespace previews { class Tracks : public debug::Introspectable, public dash::ScrollView { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(Tracks, nux::View); Tracks(dash::Tracks::Ptr tracks, NUX_FILE_LINE_PROTO); protected: virtual bool AcceptKeyNavFocus() { return false; } void SetupViews(); void OnTrackUpdated(dash::Track const& track); void OnTrackAdded(dash::Track const& track); void OnTrackRemoved(dash::Track const&track); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); protected: dash::Tracks::Ptr tracks_; nux::VLayout* layout_; std::map m_tracks; connection::Manager sig_conn_; private: void UpdateScale(double scale); }; } } } #endif // TRACKS_H ./dash/previews/PreviewRatingsWidget.h0000644000004100000410000000412513437202764020326 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef UNITYSHELL_PREVIEWRATINGSWIDGET_H #define UNITYSHELL_PREVIEWRATINGSWIDGET_H #include #include #include "PreviewContainer.h" namespace nux { class StaticCairoText; } namespace unity { class RatingsButton; namespace dash { namespace previews { class PreviewRatingsWidget : public debug::Introspectable, public nux::View { NUX_DECLARE_OBJECT_TYPE(PreviewRatingsWidget, nux::View); public: PreviewRatingsWidget(NUX_FILE_LINE_PROTO); void SetRating(float rating); float GetRating() const; void SetReviews(int count); nux::Property scale; sigc::signal request_close() const { return preview_container_.request_close; } protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual bool AcceptKeyNavFocus() { return false; } // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: void UpdateScale(double scale); RatingsButton* ratings_; StaticCairoText* reviews_; nux::VLayout* layout_; PreviewContainer preview_container_; }; } // namespace previews } // namespace dash } // namespace unity #endif // UNITYSHELL_PREVIEWRATINGSWIDGET_H ./dash/previews/TabIterator.cpp0000644000004100000410000000756613437202764017000 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * Manuel de la Pena * */ #include "TabIterator.h" namespace unity { namespace dash { namespace previews { void TabIterator::Remove(nux::InputArea* area) { areas_.remove(area); } void TabIterator::Prepend(nux::InputArea* area) { Remove(area); areas_.push_front(area); } void TabIterator::Append(nux::InputArea* area) { Remove(area); areas_.push_back(area); } void TabIterator::Insert(nux::InputArea* area, unsigned index) { Remove(area); std::list::iterator it = areas_.begin(); if (index < areas_.size()) { std::advance(it, index); areas_.insert(it, area); } else areas_.push_back(area); } void TabIterator::InsertBefore(nux::InputArea* area, nux::InputArea* after) { Remove(area); std::list::iterator it = std::find(areas_.begin(), areas_.end(), after); areas_.insert(it, area); } void TabIterator::InsertAfter(nux::InputArea* area, nux::InputArea* before) { Remove(area); std::list::iterator it = std::find(areas_.begin(), areas_.end(), before); if (it != areas_.end()) ++it; areas_.insert(it, area); } std::list const& TabIterator::GetTabAreas() const { return areas_; } nux::InputArea* TabIterator::DefaultFocus() const { if (areas_.empty()) return nullptr; return *areas_.begin(); } nux::InputArea* TabIterator::FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) { if (areas_.empty()) return nullptr; nux::InputArea* current_focus_area = nux::GetWindowCompositor().GetKeyFocusArea(); auto it = std::find(areas_.begin(), areas_.end(), current_focus_area); if (it != areas_.end()) return current_focus_area; return *areas_.begin(); } nux::Area* TabIterator::KeyNavIteration(nux::KeyNavDirection direction) { if (areas_.empty()) return nullptr; if (direction != nux::KEY_NAV_TAB_PREVIOUS && direction != nux::KEY_NAV_TAB_NEXT) { return nullptr; } nux::InputArea* current_focus_area = nux::GetWindowCompositor().GetKeyFocusArea(); if (current_focus_area) { auto it = std::find(areas_.begin(), areas_.end(), current_focus_area); if (direction == nux::KEY_NAV_TAB_PREVIOUS) { if (it == areas_.begin()) return *areas_.rbegin(); else { it--; if (it == areas_.begin()) return *areas_.rbegin(); return *it; } } else if (direction == nux::KEY_NAV_TAB_NEXT) { if (it == areas_.end()) { return *areas_.begin(); } else { it++; if (it == areas_.end()) { return *areas_.begin(); } return *it; } } } else { if (direction == nux::KEY_NAV_TAB_PREVIOUS) { return *areas_.rbegin(); } else if (direction == nux::KEY_NAV_TAB_NEXT) { return *areas_.begin(); } } return nullptr; } } // previews } // dash } // unity ./dash/previews/pch/0000755000004100000410000000000013437202764014610 5ustar www-datawww-data./dash/previews/pch/previews_pch.hh0000644000004100000410000000172413437202764017633 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Jussi Pakkanen */ /* * These are the precompiled header includes for this module. * Only system header files can be listed here. */ #include #include #include #include ./dash/previews/MusicPaymentPreview.h0000644000004100000410000000741413437202764020175 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #ifndef MUSIC_PAYMENT_PREVIEW_H #define MUSIC_PAYMENT_PREVIEW_H #include #include #include #include "ActionButton.h" #include "ActionLink.h" #include "PaymentPreview.h" #include "unity-shared/IconTexture.h" #include "unity-shared/TextInput.h" namespace nux { class AbstractPaintLayer; class StaticCairoText; class VLayout; } namespace unity { namespace dash { namespace previews { class CoverArt; class PreviewInfoHintWidget; class MusicPaymentPreview : public PaymentPreview { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(MusicPaymentPreview, Preview); MusicPaymentPreview(dash::Preview::Ptr preview_model); private: void LoadActions(); protected: // key used to find the correct info hint static const std::string DATA_INFOHINT_ID; // keys of the data preview static const std::string DATA_PASSWORD_KEY; // ations ids static const std::string CHANGE_PAYMENT_ACTION; static const std::string FORGOT_PASSWORD_ACTION; static const std::string CANCEL_PURCHASE_ACTION; static const std::string PURCHASE_ALBUM_ACTION; // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); nux::Layout* GetTitle(); nux::Layout* GetPrice(); nux::Layout* GetBody(); nux::Layout* GetFormLabels(); nux::Layout* GetFormFields(); nux::Layout* GetFormActions(); nux::Layout* GetFooter(); const std::string GetErrorMessage(GVariant *dict); void OnActionActivated(ActionButton* button, std::string const& id); void OnActionLinkActivated(ActionLink* link, std::string const& id); virtual void SetupViews(); virtual void UpdateScale(double scale) override; void PreLayoutManagement(); protected: // content elements nux::ObjectPtr image_; nux::ObjectPtr intro_; nux::ObjectPtr title_; nux::ObjectPtr subtitle_; nux::ObjectPtr email_label_; nux::ObjectPtr email_; nux::ObjectPtr payment_label_; nux::ObjectPtr payment_; nux::ObjectPtr password_label_; nux::ObjectPtr password_entry_; nux::ObjectPtr purchase_hint_; nux::ObjectPtr purchase_prize_; nux::ObjectPtr purchase_type_; nux::ObjectPtr change_payment_; nux::ObjectPtr forgotten_password_; nux::ObjectPtr error_label_; nux::ObjectPtr form_layout_; dash::PaymentPreview* payment_preview_model_; // do we want to type? std::string error_message_; // actions std::map> buttons_map_; // lock texture nux::ObjectPtr lock_texture_; typedef std::unique_ptr LayerPtr; LayerPtr details_bg_layer_; }; } } } #endif // MUSIC_PAYMENT_PREVIEW_H ./dash/previews/ActionButton.cpp0000644000004100000410000002251513437202764017160 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "unity-shared/DashStyle.h" #include "ActionButton.h" #include #include "unity-shared/IconTexture.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/UnitySettings.h" namespace unity { namespace { const RawPixel MIN_BUTTON_HEIGHT = 34_em; const RawPixel MIN_BUTTON_WIDTH = 48_em; const RawPixel icon_size = 24_em; } namespace dash { ActionButton::ActionButton(std::string const& action_hint, std::string const& label, std::string const& icon_hint, NUX_FILE_LINE_DECL) : nux::AbstractButton(NUX_FILE_LINE_PARAM) , scale(1.0) , action_hint_(action_hint) , image_(nullptr) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(true); Init(); BuildLayout(label, icon_hint, ""); scale.changed.connect(sigc::mem_fun(this, &ActionButton::UpdateScale)); Settings::Instance().font_scaling.changed.connect(sigc::hide(sigc::mem_fun(this, &ActionButton::InitTheme))); } ActionButton::~ActionButton() { } std::string ActionButton::GetName() const { return "ActionButton"; } void ActionButton::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("action", action_hint_) .add("label", label_) .add("icon-hint", icon_hint_) .add("font-hint", font_hint_) .add("active", active_); } void ActionButton::Init() { InitTheme(); key_nav_focus_activate.connect([this](nux::Area*) { if (GetInputEventSensitivity()) activate.emit(this, action_hint_); }); } void ActionButton::InitTheme() { nux::Geometry const& geo = GetGeometry(); cr_prelight_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &ActionButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRELIGHT))); cr_active_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &ActionButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRESSED))); cr_normal_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &ActionButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_NORMAL))); cr_focus_.reset(new nux::CairoWrapper(geo, sigc::mem_fun(this, &ActionButton::RedrawFocusOverlay))); double font_scaling = Settings::Instance().font_scaling() * scale; SetMinimumHeight(MIN_BUTTON_HEIGHT.CP(font_scaling)); SetMinimumWidth(MIN_BUTTON_WIDTH.CP(font_scaling)); } void ActionButton::SetExtraHint(std::string const& extra_hint, std::string const& font_hint) { extra_font_hint_= font_hint; if (extra_text_) { extra_text_->SetFont(extra_font_hint_); ComputeContentSize(); QueueDraw(); } BuildLayout(label_, icon_hint_, extra_hint); } void ActionButton::BuildLayout(std::string const& label, std::string const& icon_hint, std::string const& extra_hint) { if (icon_hint != icon_hint_) { icon_hint_ = icon_hint; if (image_) { image_.Release(); image_ = NULL; } if (!icon_hint_.empty()) { image_ = new IconTexture(icon_hint, icon_size.CP(scale)); image_->texture_updated.connect([this](nux::ObjectPtr const&) { BuildLayout(label_, icon_hint_, extra_hint_); }); image_->SetInputEventSensitivity(false); image_->SetMinMaxSize(icon_size.CP(scale), icon_size.CP(scale)); } } if (label != label_) { label_ = label; if (static_text_) { static_text_.Release(); static_text_ = NULL; } if (!label_.empty()) { static_text_ = new StaticCairoText(label_, true, NUX_TRACKER_LOCATION); if (!font_hint_.empty()) static_text_->SetFont(font_hint_); static_text_->SetInputEventSensitivity(false); static_text_->SetTextAlignment(StaticCairoText::NUX_ALIGN_CENTRE); } } if (extra_hint != extra_hint_) { extra_hint_ = extra_hint; if (extra_text_) { extra_text_.Release(); extra_text_ = NULL; } if (!extra_hint_.empty()) { extra_text_ = new StaticCairoText(extra_hint_, true, NUX_TRACKER_LOCATION); if (!extra_font_hint_.empty()) extra_text_->SetFont(extra_font_hint_); extra_text_->SetInputEventSensitivity(false); extra_text_->SetTextAlignment(StaticCairoText::NUX_ALIGN_CENTRE); } } RemoveLayout(); nux::HLayout* layout = new nux::HLayout(); layout->SetHorizontalInternalMargin(6); layout->SetPadding(2, 0, 2, 0); layout->AddSpace(0,1); if (image_) layout->AddView(image_.GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); if (static_text_) layout->AddView(static_text_.GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); if (extra_text_) layout->AddView(extra_text_.GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); layout->AddSpace(0,1); SetLayout(layout); ComputeContentSize(); QueueDraw(); } void ActionButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); Style::Instance().Button(cr, faked_state, "", -1, Alignment::CENTER, true); } void ActionButton::RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr) { cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); Style::Instance().ButtonFocusOverlay(cr, 0.20f); } long ActionButton::ComputeContentSize() { long ret = nux::View::ComputeContentSize(); nux::Geometry const& geo = GetGeometry(); if (cached_geometry_ != geo && geo.width > 0 && geo.height > 0) { if (cr_prelight_) cr_prelight_->Invalidate(geo); if (cr_active_) cr_active_->Invalidate(geo); if (cr_normal_) cr_normal_->Invalidate(geo); if (cr_focus_) cr_focus_->Invalidate(geo); cached_geometry_ = geo; } return ret; } void ActionButton::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry const& geo = GetGeometry(); gPainter.PaintBackground(GfxContext, geo); // set up our texture mode nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); // clear what is behind us unsigned int alpha = 0, src = 0, dest = 0; GfxContext.GetRenderStates().GetBlend(alpha, src, dest); GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::Color col = nux::color::Black; col.alpha = 0; GfxContext.QRP_Color(geo.x, geo.y, geo.width, geo.height, col); nux::BaseTexture* texture = cr_normal_->GetTexture(); if (GetVisualState() == nux::ButtonVisualState::VISUAL_STATE_PRELIGHT) texture = cr_prelight_->GetTexture(); else if (GetVisualState() == nux::ButtonVisualState::VISUAL_STATE_PRESSED) texture = cr_active_->GetTexture(); GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height, texture->GetDeviceTexture(), texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); if (HasKeyboardFocus()) { GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height, cr_focus_->GetTexture()->GetDeviceTexture(), texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); } GfxContext.GetRenderStates().SetBlend(alpha, src, dest); if (GetCompositionLayout()) { gPainter.PushPaintLayerStack(); { nux::Geometry clip_geo = geo; GfxContext.PushClippingRectangle(clip_geo); gPainter.PushPaintLayerStack(); GetCompositionLayout()->ProcessDraw(GfxContext, force_draw); gPainter.PopPaintLayerStack(); GfxContext.PopClippingRectangle(); } gPainter.PopPaintLayerStack(); } } void ActionButton::RecvClick(int x, int y, unsigned long button_flags, unsigned long key_flags) { activate.emit(this, action_hint_); } void ActionButton::SetFont(std::string const& font_hint) { if (static_text_) { static_text_->SetFont(font_hint); ComputeContentSize(); QueueDraw(); } } std::string ActionButton::GetLabel() const { return label_; } std::string ActionButton::GetExtraText() const { return extra_hint_; } void ActionButton::UpdateScale(double scale) { InitTheme(); if (image_) { image_->SetSize(icon_size.CP(scale)); image_->SetMinMaxSize(icon_size.CP(scale), icon_size.CP(scale)); image_->ReLoadIcon(); } if (static_text_) static_text_->SetScale(scale); if (extra_text_) extra_text_->SetScale(scale); QueueRelayout(); QueueDraw(); } } // namespace dash } // namespace unity ./dash/previews/StandaloneMoviePreview.cpp0000644000004100000410000002245713437202764021206 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include #include "Nux/Nux.h" #include "Nux/NuxTimerTickSource.h" #include "Nux/VLayout.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include #include #include #include #include #include #include "unity-shared/FontSettings.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" const unity::RawPixel WIDTH(1000); const unity::RawPixel HEIGHT(600); using namespace unity; using namespace unity::dash; static double scale = 1.0; class DummyView : public nux::View { public: DummyView(nux::View* view) : View(NUX_TRACKER_LOCATION) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bg_layer_.reset(new nux::ColorLayer(nux::Color(81, 26, 48), true, rop)); nux::Layout* layout = new nux::VLayout(); layout->SetPadding(16); layout->AddView(view, 1, nux::MINOR_POSITION_CENTER); SetLayout(layout); } virtual ~DummyView() {} // Keyboard navigation bool AcceptKeyNavFocus() { return false; } protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); bg_layer_->SetGeometry(GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, GetGeometry(), bg_layer_.get()); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (!IsFullRedraw()) nux::GetPainter().PushLayer(gfx_engine, GetGeometry(), bg_layer_.get()); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); if (!IsFullRedraw()) nux::GetPainter().PopBackground(); gfx_engine.PopClippingRectangle(); } typedef std::unique_ptr LayerPtr; LayerPtr bg_layer_; }; class TestRunner { public: TestRunner (); ~TestRunner (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); void NavRight(); void NavLeft(); previews::PreviewContainer::Ptr container_; nux::Layout *layout_; int nav_iter; }; TestRunner::TestRunner () { nav_iter = 0; } TestRunner::~TestRunner () { } void TestRunner::Init () { container_ = new previews::PreviewContainer(NUX_TRACKER_LOCATION); container_->navigate_right.connect(sigc::mem_fun(this, &TestRunner::NavRight)); container_->navigate_left.connect(sigc::mem_fun(this, &TestRunner::NavLeft)); container_->request_close.connect([this]() { exit(0); }); container_->scale = scale; DummyView* dummyView = new DummyView(container_.GetPointer()); layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); layout_->AddView(dummyView, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); nux::GetWindowThread()->SetLayout (layout_); NavRight(); } void TestRunner::NavRight() { std::stringstream title; title << "Up " << ++nav_iter; const char* subtitle = "2009"; const char* description = "By tying thousands of balloons to his home, 78-year-old Carl sets out to fulfill his lifelong dream to see the wilds of South America. Russell, a wilderness explorer 70 years younger, inadvertently becomes a stowaway."; glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); unity_protocol_movie_preview_set_rating(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 0.5); unity_protocol_movie_preview_set_num_ratings(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 17); unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTMwODg0NDY1Nl5BMl5BanBnXkFtZTcwMjkwNTgyMg@@._V1._SY317_.jpg"); unity_protocol_preview_set_title(proto_obj, title.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); unity_protocol_preview_add_action(proto_obj, "play", "Play", NULL, 0); unity_protocol_preview_add_action(proto_obj, "upgradHD", "Upgrade to HD", NULL, 0); unity_protocol_preview_add_info_hint(proto_obj, "director", "Director", NULL, g_variant_new("s", "Steve Martino, Mike Thurmeier")); unity_protocol_preview_add_info_hint(proto_obj, "cast", "Cast", NULL, g_variant_new("s", "Ray Romano, Denis Leary and John Leguizamo")); unity_protocol_preview_add_info_hint(proto_obj, "genre", "Genre", NULL, g_variant_new("s", "Animation")); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::RIGHT); } void TestRunner::NavLeft() { std::stringstream title; title << "Ice Age, Continental Drift" << --nav_iter; const char* subtitle = "2012, 88 min"; const char* description = "Manny, Diego, and Sid embark upon another adventure after their continent is set adrift. Using an iceberg as a ship, they encounter sea creatures and battle pirates as they explore a new world."; glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); unity_protocol_movie_preview_set_rating(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 0.5); unity_protocol_movie_preview_set_num_ratings(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 17); unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); unity_protocol_preview_set_title(proto_obj, title.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); unity_protocol_preview_add_action(proto_obj, "play", "Play", NULL, 0); unity_protocol_preview_add_action(proto_obj, "upgradHD", "Upgrade to HD", NULL, 0); unity_protocol_preview_add_info_hint(proto_obj, "director", "Director", NULL, g_variant_new("s", "Steve Martino, Mike Thurmeier")); unity_protocol_preview_add_info_hint(proto_obj, "cast", "Cast", NULL, g_variant_new("s", "Ray Romano, Denis Leary and John Leguizamo")); unity_protocol_preview_add_info_hint(proto_obj, "genre", "Genre", NULL, g_variant_new("s", "Animation")); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::LEFT); } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init (); } int main(int argc, char **argv) { nux::WindowThread* wt = NULL; gtk_init (&argc, &argv); nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; unity::ThumbnailGenerator thumbnail_generator; unity::glib::Error err; GOptionEntry args_parsed[] = { { "scaling-factor", 's', 0, G_OPTION_ARG_DOUBLE, &scale, "The dash scaling factor", "F" }, { NULL } }; std::shared_ptr ctx(g_option_context_new("Unity Preview"), g_option_context_free); g_option_context_add_main_entries(ctx.get(), args_parsed, NULL); if (!g_option_context_parse(ctx.get(), &argc, &argv, &err)) std::cerr << "Got error when parsing arguments: " << err << std::endl; TestRunner *test_runner = new TestRunner (); wt = nux::CreateGUIThread(TEXT("Unity Preview"), WIDTH.CP(scale), HEIGHT.CP(scale), 0, &TestRunner::InitWindowThread, test_runner); nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); wt->Run (NULL); delete wt; return 0; } ./dash/previews/StandaloneMusicPreview.cpp0000644000004100000410000001741013437202764021200 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include #include "Nux/Nux.h" #include "Nux/VLayout.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include #include #include #include #include #include "unity-shared/FontSettings.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" #include "LensDBusTestRunner.h" #define WIDTH 972 #define HEIGHT 452 using namespace unity; using namespace unity::dash; DECLARE_LOGGER(logger, "unity.dash.preview.standalone"); class DummyView : public nux::View { public: DummyView(nux::View* view) : View(NUX_TRACKER_LOCATION) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bg_layer_.reset(new nux::ColorLayer(nux::Color(81, 26, 48), true, rop)); nux::Layout* layout = new nux::VLayout(); layout->SetPadding(16); layout->AddView(view, 1, nux::MINOR_POSITION_CENTER); SetLayout(layout); } virtual ~DummyView() {} // Keyboard navigation bool AcceptKeyNavFocus() { return false; } protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); bg_layer_->SetGeometry(GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, GetGeometry(), bg_layer_.get()); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (!IsFullRedraw()) nux::GetPainter().PushLayer(gfx_engine, GetGeometry(), bg_layer_.get()); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); if (!IsFullRedraw()) nux::GetPainter().PopBackground(); gfx_engine.PopClippingRectangle(); } typedef std::unique_ptr LayerPtr; LayerPtr bg_layer_; }; class TestRunner : public previews::ScopeDBusTestRunner { public: TestRunner(std::string const& search_string); ~TestRunner (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); void NavRight(); void NavLeft(); previews::PreviewContainer::Ptr container_; nux::Layout *layout_; unsigned int nav_iter; previews::Navigation nav_direction_; std::string search_string_; bool first_; }; TestRunner::TestRunner (std::string const& search_string) : ScopeDBusTestRunner("com.canonical.Unity.Scope.Music","/com/canonical/unity/scope/music", "com.canonical.Unity.Scope") , search_string_(search_string) , first_(true) { nav_iter = 0; nav_direction_ = previews::Navigation::RIGHT; connected.connect([this](bool connected) { if (connected) { Search(search_string_); } }); results_->result_added.connect([this](Result const& result) { previews::Navigation navDisabled = previews::Navigation::BOTH; if (nav_iter < results_->count.Get() - 1) navDisabled = previews::Navigation( static_cast(navDisabled) & ~static_cast(previews::Navigation::RIGHT)); if (results_->count.Get() > 0 && nav_iter > 0) navDisabled = previews::Navigation( static_cast(navDisabled) & ~static_cast(previews::Navigation::LEFT)); if (first_) { first_ = false; Preview(result.uri); } container_->DisableNavButton(navDisabled); }); } TestRunner::~TestRunner () { } void TestRunner::Init () { container_ = new previews::PreviewContainer(NUX_TRACKER_LOCATION); container_->navigate_right.connect(sigc::mem_fun(this, &TestRunner::NavRight)); container_->navigate_left.connect(sigc::mem_fun(this, &TestRunner::NavLeft)); container_->request_close.connect([this]() { exit(0); }); DummyView* dummyView = new DummyView(container_.GetPointer()); layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); layout_->AddView(dummyView, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); nux::GetWindowThread()->SetLayout (layout_); container_->DisableNavButton(previews::Navigation::BOTH); preview_ready.connect([this](std::string const& uri, dash::Preview::Ptr preview_model) { container_->Preview(preview_model, nav_direction_); }); } void TestRunner::NavRight() { nav_direction_ = previews::Navigation::RIGHT; Result result = results_->RowAtIndex(++nav_iter); LOG_DEBUG(logger) << "Preview: " << result.uri.Get(); Preview(result.uri); previews::Navigation navDisabled = previews::Navigation::BOTH; if (nav_iter < results_->count.Get() - 1) navDisabled = previews::Navigation( static_cast(navDisabled) & ~static_cast(previews::Navigation::RIGHT)); if (results_->count.Get() > 0 && nav_iter > 0) navDisabled = previews::Navigation( static_cast(navDisabled) & ~static_cast(previews::Navigation::LEFT)); container_->DisableNavButton(navDisabled); } void TestRunner::NavLeft() { nav_direction_ = previews::Navigation::LEFT; Result result = results_->RowAtIndex(--nav_iter); LOG_DEBUG(logger) << "Preview: " << result.uri.Get(); Preview(result.uri); previews::Navigation navDisabled = previews::Navigation::BOTH; if (nav_iter < results_->count.Get() - 1) navDisabled = previews::Navigation( static_cast(navDisabled) & ~static_cast(previews::Navigation::RIGHT)); if (results_->count.Get() > 0 && nav_iter > 0) navDisabled = previews::Navigation( static_cast(navDisabled) & ~static_cast(previews::Navigation::LEFT)); container_->DisableNavButton(navDisabled); } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init (); } int main(int argc, char **argv) { nux::WindowThread* wt = NULL; gtk_init (&argc, &argv); if (argc < 2) { printf("Usage: music_previews SEARCH_STRING\n"); return 1; } nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; unity::ThumbnailGenerator thumbnail_generator; TestRunner *test_runner = new TestRunner (argv[1]); wt = nux::CreateGUIThread(TEXT("Unity Preview"), WIDTH, HEIGHT, 0, &TestRunner::InitWindowThread, test_runner); wt->Run (NULL); delete wt; return 0; } ./dash/previews/SocialPreviewComments.cpp0000644000004100000410000001344213437202764021030 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Ken VanDine * */ #include "unity-shared/DashStyle.h" #include "unity-shared/PreviewStyle.h" #include #include #include #include #include "SocialPreviewComments.h" namespace unity { namespace dash { namespace previews { namespace { const RawPixel LAYOUT_SPACING = 12_em; const RawPixel CHILDREN_SPACE = 6_em; } NUX_IMPLEMENT_OBJECT_TYPE(SocialPreviewComments); SocialPreviewComments::SocialPreviewComments(dash::Preview::Ptr preview_model, NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , scale(1.0) , preview_model_(preview_model) { SetupViews(); scale.changed.connect(sigc::hide(sigc::mem_fun(this, &SocialPreviewComments::SetupViews))); } void SocialPreviewComments::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } void SocialPreviewComments::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (GetCompositionLayout()) { unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); } gfx_engine.PopClippingRectangle(); } void SocialPreviewComments::PreLayoutManagement() { previews::Style& style = previews::Style::Instance(); nux::Geometry const& geo = GetGeometry(); int comment_width = 0; int minimum_detail_width = style.GetDetailsPanelMinimumWidth().CP(scale); for (Comment const& comment : comments_) { int width = minimum_detail_width; if (comment.first) width = std::max(minimum_detail_width, comment.first->GetTextExtents().width); if (comment_width < width) comment_width = width; } int comment_value_width = MAX(0, geo.width - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale)); for (Comment const& comment : comments_) { if (comment.first) { comment.first->SetMaximumWidth(comment_value_width); } if (comment.second) { comment.second->SetMaximumWidth(comment_value_width); } } View::PreLayoutManagement(); } void SocialPreviewComments::SetupViews() { dash::SocialPreview* social_preview_model = dynamic_cast(preview_model_.get()); RemoveLayout(); comments_.clear(); previews::Style& style = previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_.OnMouseDown(x, y, button_flags, key_flags); }; nux::VLayout* layout = new nux::VLayout(); layout->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); for (dash::SocialPreview::CommentPtr comment : social_preview_model->GetComments()) { nux::HLayout* name_layout = new nux::HLayout(); name_layout->SetSpaceBetweenChildren(LAYOUT_SPACING.CP(scale)); StaticCairoTextPtr comment_name; if (!comment->display_name.empty()) { comment_name = new StaticCairoText(comment->display_name, true, NUX_TRACKER_LOCATION); comment_name->SetFont(style.info_hint_bold_font()); comment_name->SetLines(-1); comment_name->SetScale(scale); comment_name->SetTextAlignment(StaticCairoText::NUX_ALIGN_LEFT); comment_name->mouse_click.connect(on_mouse_down); name_layout->AddView(comment_name.GetPointer(), 0, nux::MINOR_POSITION_START); } StaticCairoTextPtr comment_time; if (!comment->time.empty()) { comment_time = new StaticCairoText(comment->time, true, NUX_TRACKER_LOCATION); comment_time->SetFont(style.info_hint_font()); comment_time->SetLines(-1); comment_time->SetScale(scale); comment_time->SetTextAlignment(StaticCairoText::NUX_ALIGN_RIGHT); comment_time->mouse_click.connect(on_mouse_down); name_layout->AddView(comment_time.GetPointer(), 0, nux::MINOR_POSITION_START); } nux::HLayout* comment_layout = new nux::HLayout(); comment_layout->SetSpaceBetweenChildren(LAYOUT_SPACING.CP(scale)); StaticCairoTextPtr comment_value(new StaticCairoText(comment->content, false, NUX_TRACKER_LOCATION)); comment_value->SetFont(style.info_hint_font()); comment_value->SetLines(-7); comment_value->SetScale(scale); comment_value->SetTextAlignment(StaticCairoText::NUX_ALIGN_LEFT); comment_value->mouse_click.connect(on_mouse_down); comment_layout->AddView(comment_value.GetPointer(), 1, nux::MINOR_POSITION_START); Comment comment_views(comment_name, comment_value); comments_.push_back(comment_views); layout->AddLayout(name_layout, 0); layout->AddLayout(comment_layout, 1); } mouse_click.connect(on_mouse_down); SetLayout(layout); } std::string SocialPreviewComments::GetName() const { return "SocialPreviewComments"; } void SocialPreviewComments::AddProperties(debug::IntrospectionData& introspection) { introspection.add(GetAbsoluteGeometry()); } } } } ./dash/previews/PreviewInfoHintWidget.cpp0000644000004100000410000001610213437202764020766 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * Marco Trevisan * */ #include "config.h" #include #include "PreviewInfoHintWidget.h" #include "unity-shared/IntrospectableWrappers.h" #include #include #include #include #include namespace unity { namespace dash { namespace previews { namespace { const RawPixel LAYOUT_SPACING = 12_em; const RawPixel HINTS_SPACING = 6_em; } NUX_IMPLEMENT_OBJECT_TYPE(PreviewInfoHintWidget); PreviewInfoHintWidget::PreviewInfoHintWidget(dash::Preview::Ptr preview_model, int icon_size) : View(NUX_TRACKER_LOCATION) , scale(1.0) , icon_size_(icon_size) , layout_(nullptr) , info_names_layout_(nullptr) , info_values_layout_(nullptr) , preview_model_(preview_model) { SetupViews(); scale.changed.connect(sigc::mem_fun(this, &PreviewInfoHintWidget::UpdateScale)); } void PreviewInfoHintWidget::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } void PreviewInfoHintWidget::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (GetCompositionLayout()) { unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); } gfx_engine.PopClippingRectangle(); } std::string PreviewInfoHintWidget::GetName() const { return "PreviewInfoHintWidget"; } void PreviewInfoHintWidget::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()); } std::string StringFromVariant(GVariant* variant) { std::stringstream ss; const GVariantType* info_hint_type = g_variant_get_type(variant); if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_BOOLEAN)) { ss << g_variant_get_int16(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_INT16)) { ss << g_variant_get_int16(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_UINT16)) { ss << g_variant_get_uint16(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_INT32)) { ss << g_variant_get_int32(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_UINT32)) { ss << g_variant_get_uint32(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_INT64)) { ss << g_variant_get_int64(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_UINT64)) { ss << g_variant_get_uint64(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_DOUBLE)) { ss << g_variant_get_double(variant); } else if (g_variant_type_equal(info_hint_type, G_VARIANT_TYPE_STRING)) { std::string str = g_variant_get_string(variant, NULL); ss << str; } else { ss << "unknown value"; } return ss.str(); } void PreviewInfoHintWidget::SetupViews() { RemoveLayout(); auto& style = previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_.OnMouseDown(x, y, button_flags, key_flags); }; layout_ = new nux::HLayout(); layout_->SetSpaceBetweenChildren(LAYOUT_SPACING.CP(scale)); auto *hint_vlayout = new nux::VLayout(); hint_vlayout->SetSpaceBetweenChildren(HINTS_SPACING.CP(scale)); layout_->AddLayout(hint_vlayout); info_names_layout_ = hint_vlayout; hint_vlayout = new nux::VLayout(); hint_vlayout->SetSpaceBetweenChildren(HINTS_SPACING.CP(scale)); layout_->AddLayout(hint_vlayout); info_values_layout_ = hint_vlayout; for (dash::Preview::InfoHintPtr const& info_hint : preview_model_->GetInfoHints()) { // The "%s" is used in the dash preview to display the ": " infos auto const& name = glib::String(g_strdup_printf (_("%s:"), info_hint->display_name.c_str())).Str(); auto* info_name = new StaticCairoText(name == ":" ? "" : name, true, NUX_TRACKER_LOCATION); info_name->SetFont(style.info_hint_bold_font()); info_name->SetLines(-1); info_name->SetScale(scale); info_name->SetTextAlignment(StaticCairoText::NUX_ALIGN_RIGHT); info_name->SetMinimumWidth(style.GetInfoHintNameMinimumWidth().CP(scale)); info_name->SetMaximumWidth(style.GetInfoHintNameMaximumWidth().CP(scale)); info_name->mouse_click.connect(on_mouse_down); info_names_layout_->AddView(info_name, 1, nux::MINOR_POSITION_RIGHT); auto* info_value = new StaticCairoText(StringFromVariant(info_hint->value), true, NUX_TRACKER_LOCATION); info_value->SetFont(style.info_hint_font()); info_value->SetLines(-1); info_value->SetScale(scale); info_value->mouse_click.connect(on_mouse_down); info_values_layout_->AddView(info_value, 1, nux::MINOR_POSITION_LEFT); } mouse_click.connect(on_mouse_down); SetLayout(layout_); } void PreviewInfoHintWidget::PreLayoutManagement() { if (info_names_layout_ && info_values_layout_) { nux::Geometry const& geo = GetGeometry(); info_names_layout_->SetMaximumWidth(info_names_layout_->GetContentWidth()); int max_width = std::max(0, geo.width - info_names_layout_->GetWidth() - LAYOUT_SPACING.CP(scale) -1); for (auto value : info_values_layout_->GetChildren()) value->SetMaximumWidth(max_width); } View::PreLayoutManagement(); } void PreviewInfoHintWidget::UpdateScale(double scale) { if (layout_) layout_->SetSpaceBetweenChildren(LAYOUT_SPACING.CP(scale)); if (info_names_layout_) { info_names_layout_->SetSpaceBetweenChildren(HINTS_SPACING.CP(scale)); for (auto* area : info_names_layout_->GetChildren()) static_cast(area)->SetScale(scale); } if (info_values_layout_) { info_values_layout_->SetSpaceBetweenChildren(HINTS_SPACING.CP(scale)); for (auto* area : info_values_layout_->GetChildren()) static_cast(area)->SetScale(scale); } QueueRelayout(); QueueDraw(); } } // namespace previews } // namespace dash } // namespace unity ./dash/previews/SocialPreviewContent.cpp0000644000004100000410000002324513437202764020657 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * Ken VanDine * */ #include "unity-shared/DashStyle.h" #include "unity-shared/PreviewStyle.h" #include #include "SocialPreviewContent.h" namespace unity { namespace dash { namespace previews { namespace { const RawPixel BUBBLE_WIDTH = 300_em; const RawPixel BUBBLE_HEIGHT = 250_em; const RawPixel TAIL_HEIGHT = 50_em; const int TAIL_POS_FROM_RIGHT = 60; const RawPixel TEXT_LINE_SPACING = 5_em; } inline nux::Geometry GetBubbleGeometry(nux::Geometry const& geo, double scale) { int width = std::min(BUBBLE_WIDTH.CP(scale), geo.width); int height = std::min(BUBBLE_HEIGHT.CP(scale) + TAIL_HEIGHT.CP(scale), geo.height); return nux::Geometry(geo.x + (geo.width - width)/2, geo.y + (geo.height - height)/2, width, height); } NUX_IMPLEMENT_OBJECT_TYPE(SocialPreviewContent); SocialPreviewContent::SocialPreviewContent(std::string const& text, NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , scale(1.0) { SetupViews(); if (text.length() > 0) SetText(text); UpdateScale(scale); scale.changed.connect(sigc::mem_fun(this, &SocialPreviewContent::UpdateScale)); } void SocialPreviewContent::SetText(std::string const& text) { std::stringstream ss; ss << " "; ss << text; ss << " "; text_->SetText(ss.str()); UpdateBaloonTexture(); } void SocialPreviewContent::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& geo = GetGeometry(); gPainter.PaintBackground(gfx_engine, geo); // set up our texture mode nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); // clear what is behind us unsigned int alpha = 0, src = 0, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::ObjectPtr tex = cr_bubble_->GetTexture()->GetDeviceTexture(); nux::Geometry geo_bubble(GetBubbleGeometry(geo, scale)); gfx_engine.QRP_1Tex(geo_bubble.x, geo_bubble.y, tex->GetWidth(), tex->GetHeight(), tex, texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); if (GetCompositionLayout()) { gPainter.PushPaintLayerStack(); { nux::Geometry clip_geo = geo; gfx_engine.PushClippingRectangle(clip_geo); gPainter.PushPaintLayerStack(); GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gPainter.PopPaintLayerStack(); gfx_engine.PopClippingRectangle(); } gPainter.PopPaintLayerStack(); } } void SocialPreviewContent::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { } void SocialPreviewContent::SetupViews() { dash::previews::Style const& style = dash::previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_.OnMouseDown(x, y, button_flags, key_flags); }; text_ = new StaticCairoText("", false, NUX_TRACKER_LOCATION); text_->SetLines(-8); text_->SetScale(scale); text_->SetFont(style.content_font()); text_->SetLineSpacing(TEXT_LINE_SPACING.CP(scale)); text_->SetTextEllipsize(StaticCairoText::NUX_ELLIPSIZE_MIDDLE); text_->mouse_click.connect(on_mouse_down); nux::Layout* layout = new nux::Layout(); layout->AddView(text_.GetPointer(), 1); mouse_click.connect(on_mouse_down); SetLayout(layout); nux::Geometry bubble_geo = GetGeometry(); cr_bubble_.reset(new nux::CairoWrapper(bubble_geo, sigc::bind(sigc::mem_fun(this, &SocialPreviewContent::RedrawBubble), nux::ButtonVisualState::VISUAL_STATE_PRELIGHT))); } void SocialPreviewContent::UpdateBaloonTexture() { nux::Geometry const& geo = GetGeometry(); nux::Geometry geo_cr(GetBubbleGeometry(geo, scale)); int max_width = std::max(0, (int)(geo_cr.width - 2*(geo_cr.width*0.1))); int max_height = std::max(0, (int)((geo_cr.height - TAIL_HEIGHT.CP(scale)) - 2*((geo_cr.height - TAIL_HEIGHT.CP(scale))*0.1))); // this will update the texture with the actual size of the text. text_->SetMaximumHeight(max_height); text_->SetMaximumWidth(max_width); nux::Geometry const& geo_text = text_->GetGeometry(); // center text text_->SetBaseX(geo_cr.x + geo_cr.width/2 - geo_text.width/2); text_->SetBaseY(geo_cr.y + geo_cr.height/2 - geo_text.height/2 - TAIL_HEIGHT.CP(scale)/2); if (geo_cr.width > 0 && geo_cr.height > 0) { cr_bubble_->Invalidate(geo_cr); } } void SocialPreviewContent::RedrawBubble(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { auto* surface = cairo_get_target(cr); double width = std::max(0.0, cairo_image_surface_get_width(surface) / scale()); double height = std::max(0.0, cairo_image_surface_get_height(surface) / scale() - TAIL_HEIGHT); double tailPosition = width - TAIL_POS_FROM_RIGHT - TAIL_HEIGHT; if (width > 0 && height > 0) { double line_width = 6.0; double radius = 28.0; double x = 0.0; double y = 0.0; DrawBubble(cr, line_width, radius, x, y, width, height, tailPosition, TAIL_HEIGHT); } } inline double _align(double val, bool odd=true) { double fract = val - (int) val; if (odd) { // for strokes with an odd line-width if (fract != 0.5f) return (double) ((int) val + 0.5f); else return val; } else { // for strokes with an even line-width if (fract != 0.0f) return (double) ((int) val); else return val; } } void SocialPreviewContent::DrawBubble(cairo_t* cr, double line_width, double radius, double x, double y, double width, double height, double tailPosition, double tailWidth) { auto* surface = cairo_get_target(cr); // sanity check if (cairo_status(cr) != CAIRO_STATUS_SUCCESS && cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) return; cairo_surface_set_device_scale(surface, scale(), scale()); cairo_set_line_width(cr, line_width); bool odd = line_width != double((int)line_width); // top-left, right of the corner cairo_move_to(cr, _align (x + radius, odd), _align (y, odd)); // top-right, left of the corner cairo_line_to(cr, _align(x + width - radius, odd), _align(y, odd)); // top-right, below the corner cairo_arc(cr, _align(x + width - radius, odd), _align(y + radius, odd), radius, -90.0f * G_PI / 180.0f, 0.0f * G_PI / 180.0f); // bottom-right, above the corner cairo_line_to(cr, _align(x + width, odd), _align(y + height - radius, odd)); // bottom-right, left of the corner cairo_arc(cr, _align(x + width - radius, odd), _align(y + height - radius, odd), radius, 0.0f * G_PI / 180.0f, 90.0f * G_PI / 180.0f); if (tailWidth > 0.0 && tailPosition > 0 && tailPosition <= (x + width - tailWidth - radius)) { // tail-right, tail top cairo_line_to(cr, _align(tailPosition + tailWidth, odd), _align(y + height, odd)); // tail-right, tail bottom cairo_line_to(cr, _align(tailPosition + tailWidth, odd), _align(y + height + tailWidth, odd)); // tail-right, tail bottom cairo_line_to(cr, _align(tailPosition, odd), _align(y + height, odd)); } // bottom-left, right of the corner cairo_line_to(cr, _align(x + radius, odd), _align(y + height, odd)); // bottom-left, above the corner cairo_arc(cr, _align(x + radius, odd), _align(y + height - radius, odd), radius, 90.0f * G_PI / 180.0f, 180.0f * G_PI / 180.0f); // top-left, right of the corner cairo_arc(cr, _align(x + radius, odd), _align(y + radius, odd), radius, 180.0f * G_PI / 180.0f, 270.0f * G_PI / 180.0f); nux::Color color_fill(1.0, 1.0, 1.0, 0.2); cairo_set_source_rgba(cr, color_fill.red, color_fill.green, color_fill.blue, color_fill.alpha); cairo_fill_preserve(cr); cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OUT); nux::Color color_stroke(1.0, 1.0, 1.0, 0.5); cairo_set_source_rgba(cr, color_stroke.red, color_stroke.green, color_stroke.blue, color_stroke.alpha); cairo_stroke(cr); } void SocialPreviewContent::PreLayoutManagement() { UpdateBaloonTexture(); View::PreLayoutManagement(); } std::string SocialPreviewContent::GetName() const { return "SocialPreviewContent"; } void SocialPreviewContent::AddProperties(debug::IntrospectionData& introspection) { introspection.add(GetAbsoluteGeometry()); } void SocialPreviewContent::UpdateScale(double scale) { if (text_) text_->SetScale(scale); UpdateBaloonTexture(); } } } } ./dash/previews/MoviePreview.h0000644000004100000410000000363113437202764016633 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef MOVIEPREVIEW_H #define MOVIEPREVIEW_H #include "Preview.h" #include "unity-shared/OverlayScrollView.h" namespace unity { namespace dash { namespace previews { class PreviewRatingsWidget; class MoviePreview : public Preview { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(MoviePreview, Preview); MoviePreview(dash::Preview::Ptr preview_model); ~MoviePreview(); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void PreLayoutManagement(); virtual void OnNavigateOut(); virtual void OnNavigateInComplete(); virtual void SetupViews(); virtual void UpdateScale(double scale); protected: nux::ObjectPtr rating_; nux::HLayout* image_data_layout_; nux::VLayout* preview_info_layout_; ScrollView* preview_info_scroll_; nux::Layout* actions_layout_; }; } } } #endif // MOVIEPREVIEW_H ./dash/previews/PreviewContainer.cpp0000644000004100000410000005043513437202764020035 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "PreviewContainer.h" #include #include "unity-shared/AnimationUtils.h" #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/TimeUtil.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "unity-shared/GraphicsUtils.h" #include "unity-shared/UnitySettings.h" #include "PreviewNavigator.h" #include #include "config.h" namespace unity { namespace dash { namespace previews { Navigation operator&(const Navigation lhs, const Navigation rhs) { return Navigation(int(lhs) & int(rhs)); } namespace { const int ANIM_DURATION_LONG = 500; const int PREVIEW_SPINNER_WAIT = 2000; const RawPixel CHILDREN_SPACE = 6_em; } class PreviewContent : public nux::Layout, public debug::Introspectable { public: PreviewContent(PreviewContainer*const parent) : scale(1.0) , parent_(parent) , progress_(0.0) , curve_progress_(0.0) , animating_(false) , waiting_preview_(false) , rotation_(0.0) , preview_initiate_count_(0) , nav_complete_(0) , relative_nav_index_(0) { geometry_changed.connect([this](nux::Area*, nux::Geometry& geo) { // Need to update the preview geometries when updating the container geo. UpdateAnimationProgress(progress_, curve_progress_); }); spin_ = dash::Style::Instance().GetSearchSpinIcon(scale); scale.changed.connect(sigc::mem_fun(this, &PreviewContent::UpdateScale)); } void UpdateScale(double scale) { spin_ = dash::Style::Instance().GetSearchSpinIcon(scale); for (auto* area : GetChildren()) static_cast(area)->scale = scale; QueueDraw(); } // From debug::Introspectable std::string GetName() const { return "PreviewContent"; } void AddProperties(debug::IntrospectionData& introspection) { introspection .add("animating", animating_) .add("animation_progress", progress_) .add("waiting_preview", waiting_preview_) .add("preview-initiate-count", preview_initiate_count_) .add("navigation-complete-count", nav_complete_) .add("relative-nav-index", relative_nav_index_); } void PushPreview(previews::Preview::Ptr preview, Navigation direction) { if (preview) { preview_initiate_count_++; StopPreviewWait(); // the parents layout will not change based on the previews. preview->SetReconfigureParentLayoutOnGeometryChange(false); AddChild(preview.GetPointer()); AddView(preview.GetPointer()); preview->SetVisible(false); preview->scale = scale(); } else { // if we push a null preview, then start waiting. StartPreviewWait(); } PreviewSwipe swipe; swipe.direction = direction; swipe.preview = preview; push_preview_.push(swipe); if (!animating_) { UpdateAnimationProgress(0.0, 0.0); } start_navigation.emit(); } bool IsAnimating() { return animating_; } int SwipeQueueSize() const { return push_preview_.size(); } float GetAnimationProgress() const { return progress_; } void UpdateAnimationProgress(float progress, float curve_progress) { progress_ = progress; curve_progress_ = curve_progress; if (!animating_) { if (!push_preview_.empty()) { animating_= true; swipe_ = push_preview_.front(); push_preview_.pop(); if (current_preview_) current_preview_->OnNavigateOut(); if (swipe_.preview) swipe_.preview->OnNavigateIn(); } } nux::Geometry const& geometry = GetGeometry(); if (animating_) { // swipe out. if (current_preview_) { current_preview_->SetVisible(true); nux::Geometry swipeOut = geometry; if (swipe_.direction == Navigation::RIGHT) swipeOut.OffsetPosition(-(curve_progress * (parent_->GetGeometry().width - geometry.x)), 0); else if (swipe_.direction == Navigation::LEFT) swipeOut.OffsetPosition(curve_progress* (parent_->GetGeometry().width - geometry.x), 0); current_preview_->SetGeometry(swipeOut); } // swipe in if (swipe_.preview) { swipe_.preview->SetVisible(true); nux::Geometry swipeIn = geometry; if (swipe_.direction == Navigation::RIGHT) swipeIn.OffsetPosition(float(parent_->GetGeometry().width - geometry.x) - (curve_progress * (parent_->GetGeometry().width - geometry.x)), 0); else if (swipe_.direction == Navigation::LEFT) swipeIn.OffsetPosition(-((1.0-curve_progress)*(parent_->GetGeometry().width - geometry.x)), 0); swipe_.preview->SetGeometry(swipeIn); } } if (progress >= 1.0) { // if we were animating, we need to remove the old preview, and replace it with the new. if (animating_) { animating_ = false; if (current_preview_) { RemoveChild(current_preview_.GetPointer()); RemoveChildObject(current_preview_.GetPointer()); current_preview_.Release(); } if (swipe_.preview) { if (swipe_.direction == Navigation::RIGHT) relative_nav_index_++; else if (swipe_.direction == Navigation::LEFT) relative_nav_index_--; current_preview_ = swipe_.preview; swipe_.preview.Release(); if (current_preview_) current_preview_->OnNavigateInComplete(); } // another swipe? if (!push_preview_.empty()) { progress_ = 0; continue_navigation.emit(); } else { end_navigation.emit(); } } // set the geometry to the whole layout. if (current_preview_) { current_preview_->SetGeometry(geometry); } nav_complete_++; } } void StartPreviewWait() { preview_wait_timer_.reset(new glib::Timeout(PREVIEW_SPINNER_WAIT, [this] () { if (waiting_preview_) return false; waiting_preview_ = true; rotate_matrix_.Rotate_z(0.0f); rotation_ = 0.0f; parent_->QueueDraw(); return false; })); } void StopPreviewWait() { preview_wait_timer_.reset(); waiting_preview_ = false; parent_->QueueDraw(); } bool OnFrameTimeout() { frame_timeout_.reset(); rotation_ += 0.1f; if (rotation_ >= 360.0f) rotation_ = 0.0f; rotate_matrix_.Rotate_z(rotation_); parent_->QueueDraw(); return false; } // Dont draw in process draw. this is so we can control the z order. void ProcessDraw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } void ProcessDraw2(nux::GraphicsEngine& gfx_engine, bool force_draw) { if (swipe_.preview && swipe_.preview->IsVisible()) { swipe_.preview->ProcessDraw(gfx_engine, force_draw); } if (current_preview_ && current_preview_->IsVisible()) { current_preview_->ProcessDraw(gfx_engine, force_draw); } if (waiting_preview_) { nux::Geometry const& base = GetGeometry(); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::TexCoordXForm texxform; texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.min_filter = nux::TEXFILTER_LINEAR; texxform.mag_filter = nux::TEXFILTER_LINEAR; nux::Geometry spin_geo(base.x + ((base.width - spin_->GetWidth()) / 2), base.y + ((base.height - spin_->GetHeight()) / 2), spin_->GetWidth(), spin_->GetHeight()); // Geometry (== Rect) uses integers which were rounded above, // hence an extra 0.5 offset for odd sizes is needed // because pure floating point is not being used. int spin_offset_w = !(base.width % 2) ? 0 : 1; int spin_offset_h = !(base.height % 2) ? 0 : 1; // we need to apply the rotation transformation first. nux::Matrix4 matrix_texture; matrix_texture = nux::Matrix4::TRANSLATE(-spin_geo.x - (spin_geo.width + spin_offset_w) / 2.0f, -spin_geo.y - (spin_geo.height + spin_offset_h) / 2.0f, 0) * matrix_texture; matrix_texture = rotate_matrix_ * matrix_texture; matrix_texture = nux::Matrix4::TRANSLATE(spin_geo.x + (spin_geo.width + spin_offset_w) / 2.0f, spin_geo.y + (spin_geo.height + spin_offset_h) / 2.0f, 0) * matrix_texture; gfx_engine.SetModelViewMatrix(gfx_engine.GetModelViewMatrix() * matrix_texture); gfx_engine.QRP_1Tex(spin_geo.x, spin_geo.y, spin_geo.width, spin_geo.height, spin_->GetDeviceTexture(), texxform, nux::color::White); // revert to model view matrix stack gfx_engine.ApplyModelViewMatrix(); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); if (!frame_timeout_) { frame_timeout_.reset(new glib::Timeout(22, sigc::mem_fun(this, &PreviewContent::OnFrameTimeout))); } } draw_cmd_queued_ = false; } nux::Area* KeyNavIteration(nux::KeyNavDirection direction) { if (swipe_.preview) return swipe_.preview->KeyNavIteration(direction); else if (current_preview_) return current_preview_->KeyNavIteration(direction); return NULL; } nux::Area* FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) { if (swipe_.preview) return swipe_.preview->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); else if (current_preview_) return current_preview_->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); return nullptr; } sigc::signal start_navigation; sigc::signal continue_navigation; sigc::signal end_navigation; nux::Property scale; private: PreviewContainer*const parent_; // Swipe animation struct PreviewSwipe { Navigation direction; previews::Preview::Ptr preview; void reset() { preview.Release(); } }; previews::Preview::Ptr current_preview_; std::queue push_preview_; PreviewSwipe swipe_; float progress_; float curve_progress_; bool animating_; // wait animation glib::Source::UniquePtr preview_wait_timer_; glib::Source::UniquePtr _frame_timeout; bool waiting_preview_; nux::ObjectPtr spin_; glib::Source::UniquePtr frame_timeout_; nux::Matrix4 rotate_matrix_; float rotation_; // intropection data. int preview_initiate_count_; int nav_complete_; int relative_nav_index_; }; NUX_IMPLEMENT_OBJECT_TYPE(PreviewContainer); PreviewContainer::PreviewContainer(NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , scale(1.0) , preview_layout_(nullptr) , nav_disabled_(Navigation::NONE) , animation_(Settings::Instance().low_gfx() ? 0 : ANIM_DURATION_LONG) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); SetupViews(); key_down.connect(sigc::mem_fun(this, &PreviewContainer::OnKeyDown)); mouse_click.connect(sigc::mem_fun(this, &PreviewContainer::OnMouseDown)); scale.changed.connect(sigc::mem_fun(this, &PreviewContainer::UpdateScale)); animation_.updated.connect(sigc::mem_fun(this, &PreviewContainer::QueueAnimation)); Settings::Instance().low_gfx.changed.connect(sigc::track_obj([this] (bool low_gfx) { animation_.SetDuration(low_gfx ? 0 : ANIM_DURATION_LONG); }, *this)); } void PreviewContainer::Preview(dash::Preview::Ptr preview_model, Navigation direction) { previews::Preview::Ptr preview_view = preview_model ? previews::Preview::PreviewForModel(preview_model) : previews::Preview::Ptr(); if (preview_view) { preview_view->request_close().connect([this]() { request_close.emit(); }); preview_layout_->PushPreview(preview_view, direction); } } void PreviewContainer::DisableNavButton(Navigation button) { nav_disabled_ = button; nav_right_->SetEnabled(IsNavigationDisabled(Navigation::RIGHT) == false); nav_left_->SetEnabled(IsNavigationDisabled(Navigation::LEFT) == false); QueueDraw(); } bool PreviewContainer::IsNavigationDisabled(Navigation button) const { return (nav_disabled_ & button) != Navigation::NONE; } std::string PreviewContainer::GetName() const { return "PreviewContainer"; } void PreviewContainer::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("navigate-left-enabled", !IsNavigationDisabled(Navigation::LEFT)) .add("navigate-right-enabled", !IsNavigationDisabled(Navigation::RIGHT)); } void PreviewContainer::SetupViews() { previews::Style& style = previews::Style::Instance(); nux::VLayout* layout = new nux::VLayout(); SetLayout(layout); layout->SetTopAndBottomPadding(style.GetPreviewTopPadding().CP(scale), 0); layout_content_ = new nux::HLayout(); layout_content_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); layout->AddLayout(layout_content_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); layout_content_->AddSpace(0, 1); nav_left_ = new PreviewNavigator(Orientation::LEFT, NUX_TRACKER_LOCATION); AddChild(nav_left_); nav_left_->scale = scale(); nav_left_->SetMinimumWidth(style.GetNavigatorWidth().CP(scale)); nav_left_->SetMaximumWidth(style.GetNavigatorWidth().CP(scale)); nav_left_->activated.connect([this]() { navigate_left.emit(); }); layout_content_->AddView(nav_left_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); preview_layout_ = new PreviewContent(this); preview_layout_->SetMinMaxSize(style.GetPreviewWidth().CP(scale), style.GetPreviewHeight().CP(scale)); AddChild(preview_layout_); layout_content_->AddLayout(preview_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); nav_right_ = new PreviewNavigator(Orientation::RIGHT, NUX_TRACKER_LOCATION); AddChild(nav_right_); nav_right_->scale = scale(); nav_right_->SetMinimumWidth(style.GetNavigatorWidth().CP(scale)); nav_right_->SetMaximumWidth(style.GetNavigatorWidth().CP(scale)); nav_right_->activated.connect([this]() { navigate_right.emit(); }); layout_content_->AddView(nav_right_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); layout_content_->AddSpace(0, 1); layout->AddSpace(0, 1); preview_layout_->start_navigation.connect([this] { if (animation_.CurrentState() == na::Animation::State::Running) preview_layout_->UpdateAnimationProgress(1, 1); animation::Start(animation_, animation::Direction::FORWARD); }); preview_layout_->continue_navigation.connect([this] { QueueAnimation(animation_.GetCurrentValue()); }); preview_layout_->end_navigation.connect([this] { animation_.Stop(); }); navigate_right.connect( [this]() { preview_layout_->StartPreviewWait(); } ); navigate_left.connect( [this]() { preview_layout_->StartPreviewWait(); } ); } void PreviewContainer::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } void PreviewContainer::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); bool redirect_to_texture = RedirectRenderingToTexture(); // This is necessary when doing redirected rendering. Clean the area below this view. if (redirect_to_texture) { // This is necessary when doing redirected rendering. // Clean the area below this view before drawing anything. gfx_engine.GetRenderStates().SetBlend(false); gfx_engine.QRP_Color(GetX(), GetY(), GetWidth(), GetHeight(), nux::Color(0.0f, 0.0f, 0.0f, 0.0f)); } // Paint using ProcessDraw2. ProcessDraw is overrided by empty impl so we can control z order. if (preview_layout_) { preview_layout_->ProcessDraw2(gfx_engine, force_draw || redirect_to_texture); } if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw || RedirectRenderingToTexture()); gfx_engine.PopClippingRectangle(); } namespace { double easeInOutQuart(double t) { t = CLAMP(t, 0.0, 1.0); t*=2.0f; if (t < 1.0) return 0.5f*pow(t, 4); else { t -= 2.0f; return -0.5f * (pow(t, 4)- 2); } } } double PreviewContainer::GetSwipeAnimationProgress(struct timespec const& current) const { return preview_layout_ ? preview_layout_->GetAnimationProgress() : 0 + animation_.GetCurrentValue(); } void PreviewContainer::QueueAnimation(double progress) { if (preview_layout_) preview_layout_->UpdateAnimationProgress(progress, easeInOutQuart(progress)); // ease in/out. QueueDraw(); } bool PreviewContainer::AcceptKeyNavFocus() { return true; } bool PreviewContainer::InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character) { switch (keysym) { case NUX_VK_ESCAPE: return true; default: break; } return false; } void PreviewContainer::OnKeyDown(unsigned long event_type, unsigned long event_keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count) { if (event_type == nux::NUX_KEYDOWN) { switch (event_keysym) { case NUX_VK_ESCAPE: request_close.emit(); break; default: return; } } } nux::Area* PreviewContainer::FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) { nux::Area* area = preview_layout_->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); if (area) return area; return this; } nux::Area* PreviewContainer::KeyNavIteration(nux::KeyNavDirection direction) { using namespace nux; nux::Area* area = preview_layout_->KeyNavIteration(direction); if (area) return area; switch(direction) { case KEY_NAV_LEFT: if (!IsNavigationDisabled(Navigation::LEFT)) navigate_left.emit(); break; case KEY_NAV_RIGHT: if (!IsNavigationDisabled(Navigation::RIGHT)) navigate_right.emit(); break; default: break; } return this; } void PreviewContainer::OnMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) { int button = nux::GetEventButton(button_flags); if (button == nux::MOUSE_BUTTON1 || button == nux::MOUSE_BUTTON2 || button == nux::MOUSE_BUTTON3) { request_close.emit(); } } nux::Geometry PreviewContainer::GetLayoutGeometry() const { return layout_content_->GetAbsoluteGeometry(); } void PreviewContainer::UpdateScale(double scale) { previews::Style& style = previews::Style::Instance(); GetLayout()->SetTopAndBottomPadding(style.GetPreviewTopPadding().CP(scale), 0); preview_layout_->SetMinMaxSize(style.GetPreviewWidth().CP(scale), style.GetPreviewHeight().CP(scale)); preview_layout_->scale = scale; layout_content_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); nav_left_->SetMinimumWidth(style.GetNavigatorWidth().CP(scale)); nav_left_->SetMaximumWidth(style.GetNavigatorWidth().CP(scale)); nav_left_->scale = scale; nav_right_->SetMinimumWidth(style.GetNavigatorWidth().CP(scale)); nav_right_->SetMaximumWidth(style.GetNavigatorWidth().CP(scale)); nav_right_->scale = scale; QueueRelayout(); QueueDraw(); } } // namespace previews } // namespace dash } // namespace unity ./dash/previews/SocialPreview.cpp0000644000004100000410000003146713437202764017331 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Ken VanDine * */ #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/CoverArt.h" #include "unity-shared/IconTexture.h" #include #include #include #include #include #include #include "config.h" #include #include "SocialPreview.h" #include "SocialPreviewContent.h" #include "SocialPreviewComments.h" #include "ActionButton.h" #include "PreviewInfoHintWidget.h" namespace unity { namespace dash { namespace previews { DECLARE_LOGGER(logger, "unity.dash.preview.social"); namespace { const RawPixel CHILDREN_SPACE = 16_em; const RawPixel ICON_CHILDREN_SPACE = 3_em; const RawPixel SOCIAL_INFO_CHILDREN_SPACE = 12_em; } NUX_IMPLEMENT_OBJECT_TYPE(SocialPreview); SocialPreview::SocialPreview(dash::Preview::Ptr preview_model) : Preview(preview_model) , image_data_layout_(nullptr) , main_social_info_(nullptr) , comments_layout_(nullptr) , social_content_layout_(nullptr) , social_data_layout_(nullptr) , social_info_layout_(nullptr) , social_info_scroll_(nullptr) , icon_layout_(nullptr) , actions_layout_(nullptr) { SetupViews(); UpdateScale(scale); } SocialPreview::~SocialPreview() { } void SocialPreview::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); gfx_engine.PopClippingRectangle(); } void SocialPreview::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } std::string SocialPreview::GetName() const { return "SocialPreview"; } void SocialPreview::AddProperties(debug::IntrospectionData& introspection) { Preview::AddProperties(introspection); } void SocialPreview::SetupViews() { dash::SocialPreview* social_preview_model = dynamic_cast(preview_model_.get()); if (!social_preview_model) { LOG_ERROR(logger) << "Could not derive social preview model from given parameter."; return; } previews::Style& style = dash::previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_->OnMouseDown(x, y, button_flags, key_flags); }; image_data_layout_ = new nux::HLayout(); image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); nux::VLayout* social_content_layout_ = new nux::VLayout(); social_content_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); if (social_preview_model->description.Get().length() > 0) { content_ = new SocialPreviewContent(social_preview_model->description, NUX_TRACKER_LOCATION); content_->request_close().connect([this]() { preview_container_->request_close.emit(); }); social_content_layout_->AddView(content_.GetPointer(), 1); } else { image_ = new CoverArt(); AddChild(image_.GetPointer()); UpdateCoverArtImage(image_.GetPointer()); social_content_layout_->AddView(image_.GetPointer(), 1); } ///////////////////// ///////////////////// // Social Data Panel full_data_layout_ = new nux::VLayout(); full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); ///////////////////// // Main Social Info main_social_info_ = new nux::HLayout(); main_social_info_->SetSpaceBetweenChildren(style.GetSpaceBetweenIconAndDetails().CP(scale)); ///////////////////// // Icon Layout icon_layout_ = new nux::VLayout(); icon_layout_->SetSpaceBetweenChildren(ICON_CHILDREN_SPACE.CP(scale)); avatar_ = new IconTexture(social_preview_model->avatar() ? g_icon_to_string(social_preview_model->avatar()) : "", MIN(style.GetAvatarAreaWidth().CP(scale), style.GetAvatarAreaHeight().CP(scale))); AddChild(avatar_.GetPointer()); avatar_->SetMinMaxSize(style.GetAvatarAreaWidth().CP(scale), style.GetAvatarAreaHeight().CP(scale)); avatar_->mouse_click.connect(on_mouse_down); icon_layout_->AddView(avatar_.GetPointer(), 0); ///////////////////// ///////////////////// // Data social_data_layout_ = new nux::VLayout(); social_data_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); title_ = new StaticCairoText(preview_model_->title, true, NUX_TRACKER_LOCATION); AddChild(title_.GetPointer()); title_->SetLines(-1); title_->SetScale(scale); title_->SetFont(style.title_font().c_str()); title_->mouse_click.connect(on_mouse_down); subtitle_ = new StaticCairoText(preview_model_->subtitle, true, NUX_TRACKER_LOCATION); AddChild(subtitle_.GetPointer()); subtitle_->SetFont(style.content_font().c_str()); subtitle_->SetLines(-1); subtitle_->SetScale(scale); subtitle_->mouse_click.connect(on_mouse_down); social_data_layout_->AddView(title_.GetPointer(), 0); social_data_layout_->AddView(subtitle_.GetPointer(), 0); social_data_layout_->AddSpace(0, 1); // buffer space ///////////////////// main_social_info_->AddLayout(icon_layout_, 0); main_social_info_->AddLayout(social_data_layout_, 1); ///////////////////// ///////////////////// // Details auto* social_info = new ScrollView(NUX_TRACKER_LOCATION); social_info_scroll_ = social_info; social_info->scale = scale(); social_info->EnableHorizontalScrollBar(false); social_info->mouse_click.connect(on_mouse_down); social_info_layout_ = new nux::VLayout(); social_info_layout_->SetSpaceBetweenChildren(SOCIAL_INFO_CHILDREN_SPACE.CP(scale)); social_info->SetLayout(social_info_layout_); if (!preview_model_->GetInfoHints().empty()) { preview_info_hints_ = new PreviewInfoHintWidget(preview_model_, style.GetAvatarAreaWidth()); AddChild(preview_info_hints_.GetPointer()); preview_info_hints_->request_close().connect([this]() { preview_container_->request_close.emit(); }); social_info_layout_->AddView(preview_info_hints_.GetPointer(), 0); } ///////////////////// // Comments/Replies if (!social_preview_model->GetComments().empty()) { comments_layout_ = new nux::HLayout(); comments_layout_->SetSpaceBetweenChildren(SOCIAL_INFO_CHILDREN_SPACE.CP(scale)); std::string tmp_comments_hint = _("Comments"); tmp_comments_hint += ":"; comments_hint_ = new StaticCairoText(tmp_comments_hint, true, NUX_TRACKER_LOCATION); AddChild(comments_hint_.GetPointer()); comments_hint_->SetLines(-1); comments_hint_->SetScale(scale); comments_hint_->SetFont(style.info_hint_bold_font().c_str()); comments_hint_->SetTextAlignment(StaticCairoText::NUX_ALIGN_RIGHT); comments_hint_->mouse_click.connect(on_mouse_down); comments_layout_->AddView(comments_hint_.GetPointer(), 0, nux::MINOR_POSITION_START); comments_ = new SocialPreviewComments(preview_model_, NUX_TRACKER_LOCATION); AddChild(comments_.GetPointer()); comments_->request_close().connect([this]() { preview_container_->request_close.emit(); }); comments_layout_->AddView(comments_.GetPointer()); social_info_layout_->AddView(comments_layout_, 0); } ///////////////////// // Actions action_buttons_.clear(); actions_layout_ = BuildGridActionsLayout(preview_model_->GetActions(), action_buttons_); actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); /////////////////// full_data_layout_->AddLayout(main_social_info_, 0, nux::MINOR_POSITION_START); full_data_layout_->AddView(social_info, 1, nux::MINOR_POSITION_START); //full_data_layout_->AddView(comments_.GetPointer(), 1, nux::MINOR_POSITION_START); full_data_layout_->AddLayout(actions_layout_, 0); ///////////////////// image_data_layout_->AddView(social_content_layout_, 0); image_data_layout_->AddLayout(full_data_layout_, 1); mouse_click.connect(on_mouse_down); SetLayout(image_data_layout_); } void SocialPreview::PreLayoutManagement() { nux::Geometry geo = GetGeometry(); previews::Style& style = dash::previews::Style::Instance(); nux::Geometry geo_content(geo.x, geo.y, style.GetAppImageAspectRatio() * geo.height, geo.height); int content_width = geo.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale); if (content_width - geo_content.width < style.GetDetailsPanelMinimumWidth().CP(scale)) geo_content.width = std::max(0, content_width - style.GetDetailsPanelMinimumWidth().CP(scale)); if (content_) { content_->SetMinMaxSize(geo_content.width, geo_content.height); } if (image_) { image_->SetMinMaxSize(geo_content.width, geo_content.height); } int details_width = std::max(0, content_width - geo_content.width); int top_social_info_max_width = std::max(0, details_width - style.GetAppIconAreaWidth().CP(scale) - style.GetSpaceBetweenIconAndDetails().CP(scale)); if (title_) { title_->SetMaximumWidth(top_social_info_max_width); } if (subtitle_) { subtitle_->SetMaximumWidth(top_social_info_max_width); } if (comments_) { comments_->SetMaximumWidth(top_social_info_max_width); } if (comments_hint_) { comments_hint_->SetMinimumWidth(style.GetInfoHintNameMinimumWidth().CP(scale)); } int button_w = CLAMP((details_width - style.GetSpaceBetweenActions().CP(scale)) / 2, 0, style.GetActionButtonMaximumWidth().CP(scale)); int button_h = style.GetActionButtonHeight().CP(scale); for (nux::AbstractButton* button : action_buttons_) button->SetMinMaxSize(button_w, button_h); Preview::PreLayoutManagement(); } void SocialPreview::UpdateScale(double scale) { Preview::UpdateScale(scale); if (preview_info_hints_) preview_info_hints_->scale = scale; previews::Style& style = dash::previews::Style::Instance(); if (avatar_) { avatar_->SetMinMaxSize(style.GetAvatarAreaWidth().CP(scale), style.GetAvatarAreaHeight().CP(scale)); avatar_->SetSize(MIN(style.GetAvatarAreaWidth().CP(scale), style.GetAvatarAreaHeight().CP(scale))); avatar_->ReLoadIcon(); } if (image_data_layout_) image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); if (social_content_layout_) social_content_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); if (main_social_info_) main_social_info_->SetSpaceBetweenChildren(style.GetSpaceBetweenIconAndDetails().CP(scale)); if (icon_layout_) icon_layout_->SetSpaceBetweenChildren(ICON_CHILDREN_SPACE.CP(scale)); if (social_data_layout_) social_data_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); if (social_info_layout_) social_info_layout_->SetSpaceBetweenChildren(SOCIAL_INFO_CHILDREN_SPACE.CP(scale)); if (social_info_scroll_) social_info_scroll_->scale = scale; if (actions_layout_) actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); if (content_) content_->scale = scale; if (comments_) comments_->scale = scale; if (comments_layout_) comments_layout_->SetSpaceBetweenChildren(SOCIAL_INFO_CHILDREN_SPACE.CP(scale)); if (comments_hint_) comments_hint_->SetScale(scale); } } // namespace previews } // namespace dash } // namepsace unity ./dash/previews/SocialPreview.h0000644000004100000410000000445213437202764016770 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Ken VanDine * */ #ifndef SOCIALPREVIEW_H #define SOCIALPREVIEW_H #include "Preview.h" #include "unity-shared/OverlayScrollView.h" namespace unity { class IconTexture; namespace dash { namespace previews { class PreviewLikesWidget; class SocialPreviewContent; class SocialPreviewComments; class SocialPreview : public Preview { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(SocialPreview, Preview); SocialPreview(dash::Preview::Ptr social_preview_model); ~SocialPreview(); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void PreLayoutManagement(); virtual void SetupViews(); virtual void UpdateScale(double scale) override; protected: nux::VLayout* sender_layout_; nux::VLayout* title_layout_; nux::HLayout* image_data_layout_; nux::HLayout* main_social_info_; nux::HLayout* comments_layout_; nux::VLayout* social_content_layout_; nux::VLayout* social_data_layout_; nux::VLayout* social_info_layout_; ScrollView* social_info_scroll_; nux::VLayout* icon_layout_; nux::Layout* actions_layout_; nux::ObjectPtr avatar_; nux::ObjectPtr content_; nux::ObjectPtr comments_; nux::ObjectPtr comments_hint_; }; } } } #endif //SOCIALPREVIEW_H ./dash/previews/PaymentPreview.cpp0000644000004100000410000002725413437202764017533 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Diego Sarmentero * Manuel de la Pena * */ #include "PaymentPreview.h" #include #include #include "unity-shared/CoverArt.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PreviewStyle.h" namespace unity { namespace dash { namespace previews { namespace { nux::logging::Logger logger("unity.dash.previews.payment.preview"); const RawPixel CONTENT_DATA_CHILDREN_SPACE = 5_em; const RawPixel CONTENT_DATA_PADDING = 10_em; const RawPixel OVERLAY_LAYOUT_SPACE = 20_em; const RawPixel HEADER_CHILDREN_SPACE = 10_em; const RawPixel HEADER_MAX_SIZE = 76_em; const RawPixel IMAGE_MIN_MAX_SIZE = 64_em; const RawPixel HEADER_SPACE = 10_em; const RawPixel LINK_MIN_WIDTH = 178_em; const RawPixel LINK_MAX_HEIGHT = 34_em; } class OverlaySpinner : public unity::debug::Introspectable, public nux::View { NUX_DECLARE_OBJECT_TYPE(OverlaySpinner, nux::View); public: OverlaySpinner(); nux::Property scale; void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); protected: // Introspectable methods std::string GetName() const; void AddProperties(debug::IntrospectionData&); // Key navigation virtual bool AcceptKeyNavFocus(); private: bool OnFrameTimeout(); nux::ObjectPtr spin_; glib::Source::UniquePtr frame_timeout_; nux::Matrix4 rotate_; float rotation_; }; NUX_IMPLEMENT_OBJECT_TYPE(OverlaySpinner); OverlaySpinner::OverlaySpinner() : nux::View(NUX_TRACKER_LOCATION) , scale(1.0) , rotation_(0.0f) { spin_ = dash::Style::Instance().GetSearchSpinIcon(scale); rotate_.Identity(); rotate_.Rotate_z(0.0); scale.changed.connect([this] (double scale) { spin_ = dash::Style::Instance().GetSearchSpinIcon(scale); QueueDraw(); }); } void OverlaySpinner::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry const& geo = GetGeometry(); nux::TexCoordXForm texxform; GfxContext.PushClippingRectangle(geo); nux::GetPainter().PaintBackground(GfxContext, geo); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.min_filter = nux::TEXFILTER_LINEAR; texxform.mag_filter = nux::TEXFILTER_LINEAR; unsigned int current_alpha_blend; unsigned int current_src_blend_factor; unsigned int current_dest_blend_factor; GfxContext.GetRenderStates().GetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::Geometry spin_geo(geo.x + ((geo.width - spin_->GetWidth()) / 2), geo.y + ((geo.height - spin_->GetHeight()) / 2), spin_->GetWidth(), spin_->GetHeight()); // Geometry (== Rect) uses integers which were rounded above, // hence an extra 0.5 offset for odd sizes is needed // because pure floating point is not being used. int spin_offset_w = !(geo.width % 2) ? 0 : 1; int spin_offset_h = !(geo.height % 2) ? 0 : 1; nux::Matrix4 matrix_texture; matrix_texture = nux::Matrix4::TRANSLATE(-spin_geo.x - (spin_geo.width + spin_offset_w) / 2.0f, -spin_geo.y - (spin_geo.height + spin_offset_h) / 2.0f, 0) * matrix_texture; matrix_texture = rotate_ * matrix_texture; matrix_texture = nux::Matrix4::TRANSLATE(spin_geo.x + (spin_geo.width + spin_offset_w) / 2.0f, spin_geo.y + (spin_geo.height + spin_offset_h) / 2.0f, 0) * matrix_texture; GfxContext.SetModelViewMatrix(GfxContext.GetModelViewMatrix() * matrix_texture); GfxContext.QRP_1Tex(spin_geo.x, spin_geo.y, spin_geo.width, spin_geo.height, spin_->GetDeviceTexture(), texxform, nux::color::White); // revert to model view matrix stack GfxContext.ApplyModelViewMatrix(); GfxContext.PopClippingRectangle(); GfxContext.GetRenderStates().SetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor); if (!frame_timeout_) { frame_timeout_.reset(new glib::Timeout(22, sigc::mem_fun(this, &OverlaySpinner::OnFrameTimeout))); } } void OverlaySpinner::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) { } bool OverlaySpinner::OnFrameTimeout() { rotation_ += 0.1f; if (rotation_ >= 360.0f) rotation_ = 0.0f; rotate_.Rotate_z(rotation_); QueueDraw(); frame_timeout_.reset(); return false; } std::string OverlaySpinner::GetName() const { return "OverlaySpinner"; } void OverlaySpinner::AddProperties(debug::IntrospectionData& introspection) { introspection.add(GetAbsoluteGeometry()); } bool OverlaySpinner::AcceptKeyNavFocus() { return false; } PaymentPreview::PaymentPreview(dash::Preview::Ptr preview_model) : Preview(preview_model) , data_(nullptr) , full_data_layout_(nullptr) , content_data_layout_(nullptr) , overlay_layout_(nullptr) , header_layout_(nullptr) , body_layout_(nullptr) , footer_layout_(nullptr) {} std::string PaymentPreview::GetName() const { return "PaymentPreview"; } void PaymentPreview::AddProperties(debug::IntrospectionData& introspection) { Preview::AddProperties(introspection); } nux::Layout* PaymentPreview::GetHeader() { nux::HLayout* header_data_layout = new nux::HLayout(); header_data_layout->SetSpaceBetweenChildren(HEADER_CHILDREN_SPACE.CP(scale)); header_data_layout->SetMaximumHeight(HEADER_MAX_SIZE.CP(scale)); header_data_layout->SetMinimumHeight(HEADER_MAX_SIZE.CP(scale)); image_ = new CoverArt(); image_->SetMinMaxSize(IMAGE_MIN_MAX_SIZE.CP(scale), IMAGE_MIN_MAX_SIZE.CP(scale)); AddChild(image_.GetPointer()); UpdateCoverArtImage(image_.GetPointer()); header_data_layout->AddView(image_.GetPointer(), 0); header_data_layout->AddLayout(GetTitle(), 0); header_data_layout->AddSpace(HEADER_SPACE.CP(scale), 1); header_data_layout->AddLayout(GetPrice(), 0); return header_data_layout; } nux::ObjectPtr PaymentPreview::CreateLink(dash::Preview::ActionPtr action) { previews::Style& style = dash::previews::Style::Instance(); nux::ObjectPtr link; link = new ActionLink(action->id, action->display_name, NUX_TRACKER_LOCATION); link->font_hint.Set(style.payment_form_labels_font().c_str()); link->SetMinimumWidth(LINK_MIN_WIDTH.CP(scale)); link->SetMaximumHeight(LINK_MAX_HEIGHT.CP(scale)); return link; } nux::ObjectPtr PaymentPreview::CreateButton(dash::Preview::ActionPtr action) { previews::Style& style = dash::previews::Style::Instance(); nux::ObjectPtr button; button = new ActionButton(action->id, action->display_name, action->icon_hint, NUX_TRACKER_LOCATION); button->SetFont(style.action_font()); button->SetExtraHint(action->extra_text, style.action_extra_font()); button->SetMinimumWidth(LINK_MIN_WIDTH.CP(scale)); button->SetMaximumHeight(LINK_MAX_HEIGHT.CP(scale)); return button; } void PaymentPreview::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); if (full_data_layout_) { unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); details_bg_layer_->SetGeometry(full_data_layout_->GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, full_data_layout_->GetGeometry(), details_bg_layer_.get()); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); } gfx_engine.PopClippingRectangle(); } void PaymentPreview::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (!IsFullRedraw()) nux::GetPainter().PushLayer(gfx_engine, details_bg_layer_->GetGeometry(), details_bg_layer_.get()); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); if (!IsFullRedraw()) nux::GetPainter().PopBackground(); gfx_engine.PopClippingRectangle(); } void PaymentPreview::ShowOverlay(bool isShown) { if (!full_data_layout_) return; if (isShown) { full_data_layout_->SetActiveLayerN(1); } else { full_data_layout_->SetActiveLayerN(0); } QueueDraw(); } void PaymentPreview::ShowOverlay() { ShowOverlay(true); } void PaymentPreview::HideOverlay() { ShowOverlay(false); } void PaymentPreview::SetupBackground() { details_bg_layer_.reset(dash::previews::Style::Instance().GetBackgroundLayer()); } void PaymentPreview::SetupViews() { full_data_layout_ = new nux::LayeredLayout(); // layout to be used to show the info content_data_layout_ = new nux::VLayout(); content_data_layout_->SetSpaceBetweenChildren(CONTENT_DATA_CHILDREN_SPACE.CP(scale)); content_data_layout_->SetPadding(CONTENT_DATA_PADDING.CP(scale), CONTENT_DATA_PADDING.CP(scale), 0, CONTENT_DATA_PADDING.CP(scale)); header_layout_ = GetHeader(); content_data_layout_->AddLayout(header_layout_.GetPointer(), 1); body_layout_ = GetBody(); content_data_layout_->AddLayout(body_layout_.GetPointer(), 1); footer_layout_ = GetFooter(); content_data_layout_->AddLayout(footer_layout_.GetPointer(), 1); full_data_layout_->AddLayout(content_data_layout_.GetPointer()); // layout to draw an overlay overlay_layout_ = new nux::VLayout(); calculating_ = new StaticCairoText( "Performing purchase", true, NUX_TRACKER_LOCATION); OverlaySpinner* spinner_ = new OverlaySpinner(); overlay_layout_->AddSpace(OVERLAY_LAYOUT_SPACE.CP(scale), 1); overlay_layout_->AddView(calculating_, 0, nux::MINOR_POSITION_CENTER); overlay_layout_->AddView(spinner_, 1, nux::MINOR_POSITION_CENTER); overlay_layout_->AddSpace(OVERLAY_LAYOUT_SPACE.CP(scale), 1); scale.changed.connect([this, spinner_] (double scale) { spinner_->scale = scale; }); full_data_layout_->AddLayout(overlay_layout_.GetPointer()); UpdateScale(scale); SetLayout(full_data_layout_.GetPointer()); } void PaymentPreview::UpdateScale(double scale) { Preview::UpdateScale(scale); if (calculating_) calculating_->SetScale(scale); if (content_data_layout_) { content_data_layout_->SetSpaceBetweenChildren(CONTENT_DATA_CHILDREN_SPACE.CP(scale)); content_data_layout_->SetPadding(CONTENT_DATA_PADDING.CP(scale), CONTENT_DATA_PADDING.CP(scale), 0, CONTENT_DATA_PADDING.CP(scale)); } } } } } ./dash/previews/StandaloneSocialPreview.cpp0000644000004100000410000003135313437202764021334 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "config.h" #include "Nux/Nux.h" #include "Nux/NuxTimerTickSource.h" #include "Nux/VLayout.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include #include #include #include #include #include #include "unity-shared/FontSettings.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" const unity::RawPixel WIDTH(1000); const unity::RawPixel HEIGHT(600); static double scale = 1.0; using namespace unity; using namespace unity::dash; class DummyView : public nux::View { public: DummyView(nux::View* view) : View(NUX_TRACKER_LOCATION) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bg_layer_.reset(new nux::ColorLayer(nux::Color(81, 26, 48), true, rop)); nux::Layout* layout = new nux::VLayout(); layout->SetPadding(16); layout->AddView(view, 1, nux::MINOR_POSITION_CENTER); SetLayout(layout); } virtual ~DummyView() {} // Keyboard navigation bool AcceptKeyNavFocus() { return false; } protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); bg_layer_->SetGeometry(GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, GetGeometry(), bg_layer_.get()); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (!IsFullRedraw()) nux::GetPainter().PushLayer(gfx_engine, GetGeometry(), bg_layer_.get()); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); if (!IsFullRedraw()) nux::GetPainter().PopBackground(); gfx_engine.PopClippingRectangle(); } typedef std::unique_ptr LayerPtr; LayerPtr bg_layer_; }; class TestRunner { public: TestRunner (); ~TestRunner (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); void NavRight(); void NavLeft(); previews::PreviewContainer::Ptr container_; nux::Layout *layout_; int nav_iter; glib::Source::UniquePtr preview_wait_timer_; }; TestRunner::TestRunner () { nav_iter = 0; } TestRunner::~TestRunner () { } void TestRunner::Init () { container_ = new previews::PreviewContainer(NUX_TRACKER_LOCATION); container_->navigate_right.connect(sigc::mem_fun(this, &TestRunner::NavRight)); container_->navigate_left.connect(sigc::mem_fun(this, &TestRunner::NavLeft)); container_->request_close.connect([this]() { exit(0); }); container_->scale = scale; DummyView* dummyView = new DummyView(container_.GetPointer()); layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); layout_->AddView(dummyView, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); nux::GetWindowThread()->SetLayout (layout_); const char* title = "Candace Flynn"; const char* subtitle = "@candacejeremyxo, 10 Aug 2012 05:01"; const char* description = "Lorem ipsum dolor sit amet, id eruditi referrentur cum, et est enim persequeris. Munere docendi intellegebat pro id, nam no delenit facilisis similique, ut usu eros aliquando. Electram postulant accusamus ut ius, cum ad impedit facilis mediocrem. At cum tamquam."; glib::Object iconHint1(g_icon_new_for_string("/usr/share/pixmaps/faces/sunflower.jpg", NULL)); glib::Object iconHint2(g_icon_new_for_string(PKGDATADIR"/lens-nav-home.svg", NULL)); glib::Object iconHint3(g_icon_new_for_string("/usr/share/icons/unity-icon-theme/places/svg/service-twitter.svg", NULL)); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_social_preview_new())); unity_protocol_social_preview_set_avatar(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), iconHint1); unity_protocol_social_preview_set_content(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), description); unity_protocol_preview_set_title(proto_obj, title); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); unity_protocol_preview_add_action(proto_obj, "view", "View", iconHint2, 0); unity_protocol_preview_add_action(proto_obj, "retweet", "Retweet", iconHint3, 0); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Stacy", "Lorem ipsum dolor sit amet, id eruditi referrentur cum, et est enim persequeris. Munere docendi intellegebat pro id, nam no delenit facilisis similique, ut usu eros aliquando. Electram postulant accusamus ut ius, cum ad impedit facilis mediocrem. At cum tamquam.", "13 minutes ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Jeremy", "This is a comment", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Stacy", "This is a comment", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Isabella", "This is a comment", "4 hours ago"); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::RIGHT); } void TestRunner::NavRight() { const char* title = "Phineas Flynn"; const char* subtitle = "@phineasflynn12, 10 Aug 2012 05:01"; const char* description = "I know what we are going to do today!"; // creates a generic preview object glib::Object iconHint1(g_icon_new_for_string("/usr/share/pixmaps/faces/astronaut.jpg", NULL)); glib::Object iconHint2(g_icon_new_for_string(PKGDATADIR"/lens-nav-home.svg", NULL)); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_social_preview_new())); unity_protocol_social_preview_set_avatar(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), iconHint1); unity_protocol_preview_set_title(proto_obj, title); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); unity_protocol_preview_add_action(proto_obj, "view", "View", iconHint2, 0); unity_protocol_preview_add_action(proto_obj, "retweet", "Retweet", nullptr, 0); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Isabella", "This is a comment", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Ferb", "This is a comment, yes it really is, i think so anyway... maybe it isn't? hard to really tell. This is a comment, yes it really is, i think so anyway... maybe it isn't? hard to really tell. This is a comment, yes it really is, i think so anyway... maybe it isn't? hard to really tell.", "4 hours ago..."); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Baljeet", "This is a comment", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Buford", "This is a comment", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Isabella", "Where's Perry?", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Pery the Platypus", "Over here", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Buford", "This is a comment", "4 hours ago"); unity_protocol_preview_add_info_hint(proto_obj, "likes", "Favorites", nullptr, g_variant_new("i", 1210)); unity_protocol_preview_add_info_hint(proto_obj, "retweets", "Retweets", nullptr, g_variant_new("i", 21)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::RIGHT); } void TestRunner::NavLeft() { const char* title = "Ferb Fletcher"; const char* subtitle = "@ferbfletcher123, 10 Aug 2012 05:01"; const char* description = "Profile pictures are what people want them to think they look like. Tagged pictures are what they really look like."; glib::Object iconHint1(g_icon_new_for_string("/usr/share/pixmaps/faces/soccerball.png", NULL)); glib::Object iconHint2(g_icon_new_for_string(PKGDATADIR"/lens-nav-home.svg", NULL)); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_social_preview_new())); unity_protocol_social_preview_set_avatar(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), iconHint1); unity_protocol_preview_set_title(proto_obj, title); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); unity_protocol_preview_add_action(proto_obj, "view", "View", iconHint2, 0); unity_protocol_preview_add_action(proto_obj, "retweet", "Retweet", nullptr, 0); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Baljeet", "This is a comment", "4 hours ago"); unity_protocol_social_preview_add_comment(UNITY_PROTOCOL_SOCIAL_PREVIEW(proto_obj.RawPtr()), "comment", "Major Monogram", "I'm a comment", "4 hours ago"); unity_protocol_preview_add_info_hint(proto_obj, "likes", "Favorites", nullptr, g_variant_new("i", 123)); unity_protocol_preview_add_info_hint(proto_obj, "retweets", "Retweets", nullptr, g_variant_new("i", 12)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::LEFT); } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init (); } int main(int argc, char **argv) { nux::WindowThread* wt = NULL; gtk_init (&argc, &argv); nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; unity::ThumbnailGenerator thumbnail_generator; unity::glib::Error err; GOptionEntry args_parsed[] = { { "scaling-factor", 's', 0, G_OPTION_ARG_DOUBLE, &scale, "The dash scaling factor", "F" }, { NULL } }; std::shared_ptr ctx(g_option_context_new("Unity Preview"), g_option_context_free); g_option_context_add_main_entries(ctx.get(), args_parsed, NULL); if (!g_option_context_parse(ctx.get(), &argc, &argv, &err)) std::cerr << "Got error when parsing arguments: " << err << std::endl; TestRunner *test_runner = new TestRunner (); wt = nux::CreateGUIThread(TEXT("Unity Preview"), WIDTH.CP(scale), HEIGHT.CP(scale), 0, &TestRunner::InitWindowThread, test_runner); nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); wt->Run (NULL); delete wt; return 0; } ./dash/previews/Skype.png0000755000004100000410000034376613437202764015665 0ustar www-datawww-dataPNG  IHDRhsRGBbKGD pHYs  tIME3 ?G`tEXtCommentCreated with GIMPW IDATxY%yNj  H 5.HbD(H9,"qcdRG7KE?@$&!vXPz)R8?UZ]CqE+V[5b@uSܗ1j9KAE|Ͳeq&ZVpkY=_P^ ὫS_<=թV3ն0Oyw D{cˈ1XAϒg~)la5/lj@ k~j/b?Ozo/g'6,D1QٯޙldVc*/>־{zqo4bLKW&r﬷4{*yxjc:=bչuVw/ jp2i}cl+lV4U3砹WgN*[hVmܤ!IpŭFk?烫QݗFn@eTVJaGW-TObp0wQ)ӹ~@C} q贺-I<0{u c! γrbNg 8,۪8vEjqImGm 1{@yy_cΤuQ9{u*V1n*wFetN`Q?, t-OWUz~q"S h%7U KmIBcQ;8-28Ip55:Y'o]ApdpC&[*s9"zK=d'ޫ &:;HQH~zA8ƣ Z{]k\INJs- T"Z. )]?h9Ix Աޚ̈́͞H}~ijAqEHZ ,LEМ5gglʧ*b>ėc44t4x y7 5y(C˨W==H.Te)A*{6%UeU#;,Pq7\Ui|Ԗxj͌z`\DTaOIcQR,3eFlh8LdaWfŇvso%T8 롕a(]0CC\2e)[قu0X{|>F!u]ztv-f\U%3tG5uHC687cgԍpˏU%.p0R>hR] >ZQKw/9Z"{@N>jQ-4wnD9>_HRB)r PNԃ6Bg@AjP uqrE$b,jVCZf48;P-՝_9,['̹0[G=+%ҡ[+.r*-T Z3\&#;^dK1XvիԀuiFSi}q";:d-SUA*0SщEzklՃIVuD-U|[Ds"~]Z:ƦGRBIEZ߷A`ntDrTD(! z +T:ƭPT.jhPdL4H?jmh *a*c~}=s;Łdbsaϛf@>% k E yXڌ:\TwnJU$,AqcB`Wa!p?ᮊ0N+HSY+{1"u@0at*jٻ 5e#g qGLep lcz5ڄ?فB(*bN%ճPGEmN2my"0 w61#3f-zѺ:MܵĹ^L"┵o xp`6uZW **YgTbJYTIV7%Ĵ.J=ժO˳XDhHlPM'CVQ"R m+42*0wqWmHu(vڜp{-; 6_M7 ;'{s xfgЈx'̖\013H=FM4HمU !ehUB:4'5ǖemKȊԲ=w=,RKX4ǒv &LbBtG6BX,72}F**;f.B";X 8s-t _{CEvv;eJhf&RjRKC8Ѧ":}-ƧkP)ΩjqMά8bTYqM#9e"ȫ11-^.bmv|6}*S".,Z8H0GzI= n6| xDtwAP+ s|6H4DMEUo|ﳑ:HM04SaZ=OWw^s*B1 "-u(eb pp :lr$#D20^BWuVXpzsFl̕h#gL|\֑PChSeo+";Ut|_s`4$LKJ$J-Sw@41UHB[87B9Fv#\V eoJK}5E0VfK6:Yo`҅ Xgy;/oMI;XxAODٲ;+tެ9P.r{0f;!L>~ *b4Kw }x*;Fl NQu#gzGʟ K5t?&Az^{ BJ.(xm̳I)!+Ⱥ,C]cP{]( ެVPЍS{ŒB\q NjAM=59L]U0T[-bc&舒rw3ɤ#ü+s|͓صaS-HC0Z2$iYL SyhAuU"͍HŚ*Z `ENB@\ƏS"iCNuUrEF=&/SL!U[X4GBMN W y6936CKǑ;n R܂md@enq4XQIc-Z!**=d {dM)) ,=ޱ~߽=?s/E X@ӈn*5 G&[v+tYZS453!ص4*x%?R4 @siO25bL%uԜ7$-k^Tk#^u;]i#NK-˖t3!`jpL 5#% ׭ 2ISqyZًo,KKN`#fF N(+~fG!^ZFp{eD̾xNBu$>Hp^|?kC 샫SVydFhj650T=˰aiLM=nQQC7VA("94]Ek ~JT$$ S U^'—.~^c,/i| ޖ1Ot w p;qMjΛħ^p %Y@sA1-JfEFbR,]LdG#~6or˲*E8,-*iXVX4e۹ʈQXɅlk-i~fjthcCH\Pp8(ndŞFT&:m zU)ROXDR *Aύ5=H4\7ʹV!Ð8b34 Ha ڌ].c/5RCPSsJۖDBXRHdPnvn'~9_CӁί7x/^SQ1Pژ:WTU|=E*g&߼Ja`cvY1`ӄ-.muF6IKZꆨ*IjY> n4MdmmLD~gsGQty.I0 qt:z.n,¥XGMk~;;|Ӊ\=a^#e]EC -\vzK"Z-iI]g`Gj2亊ڢMkCs񶋲3\?zHL S@_,Bŧʣ5"?dRZDsB\= T+r)V+pu.^6 t:_;Tib1y/ʤ4#f9PsQ\v'ZT\RU84Ut]ZN`l:Nv3a$啌cFvv@ $aks`bNTQjsNyFs6cƆh#)!jp{2zKVc4Jz@ 2ddjᐍ-dL7e_6VcX"<<ͭM-Qfzmzӳ`ZKMe?`k:ۉ Qs5$K_*? \|}T2A^4q[ MqkKB*}Z0bRc=̅ϲz`Ԁħ>=wpkyյ $j)PBQi4h[1HrH%@>_=?܆4 #bmH OU Rsfb/! LIWTuI _(]{Ol',͡;ONϙ:&sRSъ{kl r ɐʕcdZb}}he{ $M[Xs{~#::~%f方#wп~&Q\4cL WƥXGш;ラzun\J+2<9tVvo0NǨ8y/W9s 8Š`S9#wLyWyG?dkswPrmX'>G4rϽH1o= ?n/3X>Bbnwz[XS4j;_ZM~wt~yl2F#!Q}\z ^$f1Ģټ;:k5NZK! ѣY[_3?PCvݾ((Cgr_-j`{zq{ K{4:MC˛afVѸ79W<"V\&/7yѺ*8_LSsu:wFc9T2R6ӜAȮ?$:s I,xUKj!d|o? =>g?-6GC4j&~x{d N=q({9nn~_?F[h<(amk fAZZ=n룬\>_=MV`lD0ՎfX~ /q9y?K*-L+;o_ X__c4H1)[>&D(ss3DqLㅅ7Օ.MG(XF"V@іi Q6%&2Aߍ@LD,I2 )沒COqr;}f4BH|K']c IQLR/:Ik)ZR bd< wzh{A: |̦(ӧJ;~_Ʀ鞲e=ƫv:=n,9:_“lmq-Gx'/reZo|泟avzW>+B7fđ[%>/7Y_`4N: οTǕ+ysՋX|vc1)֎y95n$O|9~8EE_Oon9O{lױ`8➇k?%hzq,}z.]?Nȸg_"GW_⹧kL.25;/=Go;GcxQNϳv:sHۆE>|ԝs.Jop}royШÑc9)oos$E)I2>+("!Id]AG$%C2=屲kwg6SKp s4k+UYLt"tj&:ZbӲ.S*jx'W3 P$cZqg240vXT93Sz!2n؎kH2dW_+Xaz(pD8mK6Ea4 Klc QvLJۣj3NLMM33;S>𑉈$IǤ2?79=8DH͍-bS?W^Y%, qw~^;w[{q޽vw.\e~.:Q˃e9^o6Sw/Gl]}6~ ~hy("2vř7_gjz6< IDATiM͐\[) 6FRy7cHI22LR<ll$Œ"bN^~m J'"͍MFV8t6կ`⏸㾇tO28׹tck#Դ:v;$6z2ˋ8El RRRʋ/ꏞә_.g-3~ǎcs}-@DY8`2:v$vqTYCfPm l\м2RsƓV`F@!s&G 1K\\4e#s E2cte3ky٨ύKj`ת (T:CoEUNvyU/hk >HL",-`l;\E1Mic5wt'z=N< bm5O,M &2zhx)*cFvHR 0פdy`0{@˅GXIy6MFqDxb-,`ۛa^ێt5~ry>O|sxOo? IG}oԣq䖣y .}d4Aeuu/pct4?t .$MzSYDy`S7rr@P^\ FT5X _~n|OW@WFxWx;yxWi ~oZiRREE^O)V#w>O/o?ǁe>w V-#,.df~2/ 6!nZNZԦ^JZ22Re>LOxk]R1$ ᓟ%>SeE(ӛ&ɽz* y,/ͧc8˯/u4dLdfX&R ũSo~~q+fK{z3^L&QiB>uBUfQI;hMΕZ<>dE& 48:pG,"H)ג%۠=:?T`$4ƣ:+8D$rq٦;.zsHP%7]wac(QJȹ-f{[70?Jwv(dkIG 5m1, d+Q痗"X[[#MLfsk8ʁFΞٳN#µk K;~ Vn}wхi.h,H O2x`e};W/?e.^(Ubvs|.>CWϱzcO S.6Moel N8 V%^x\p8fw-qݷހ6{g|Nf8R"bXbgZν}=0v;{LD3Ewj^{O=~.ES'>ϷV&(9v=or`K/6d&^+][?]04&\x[ů&ﺕu=XY[Ú7AD8))S7mk,?۔ʯIFCo~$zOԶFdd8'yO^$Q^ nsɓh 8Ƨ>)ѯ*1\vwcMnwɋ?<ˇrn\/ |tĥ+?Lwjo// OolkLieןHLjd]N o1(u 3(X%b(Q[5[QD+I0Reu$t>GTK ob0ܑDFh"C!M,i@fS"C2DZ i2vƬc2^AE7ïW0;O=Ƶk$TxLXZ`MkmVJSDVl>Chfs&caHmJ5Crbiw%怶襆 q/D&)3l53"%߿VMdJldУ 2|cjƭA@Gk*, wpxiXH;S BH,,83MU.n5ʬp'/1ąΡ1FtG'US= Z{|}fʩ)Jfb"VY`5M*,,d3Veks73[R趼z*ѐns0t29diD^q/c()B9G _'Y__wp6j# kI nh1Q>3JĝNJC+۽+K7:|LŬ*Dݨ*ȷ;Rԡ(GO~G=S@)g8kjL,Q;ߔbA(vFD hLa'>r'n_z5DDD8 EH1Xm|l%8LLjGơz_c}ވau2N(khZDMս I\@T0L8ŭVV /Ef[&~ž*ԣQmnWV:k,HQnj/8ju:U*)\%1hΡv3VbF.vQ[isƥL5҄K.G?H#[E-ffElomgRS F6233M+$IҾD(+h4f4ak#a լ2LwH?P"~_4ss) jRꢻ}r% fٜ#^]u;7X1Sjh DƔBG& des24Q-1efJTط HM4ҲQ7.@;a6Vv]k"kYX%lmJ#T>d'df}pM=H$"L1LևvcN2̢(bwtHM Bj-kkkj9Hs݅ \?ш%$u7iN;ӎ82Bn^]-tp &ZXτlclD4ђ#=V]ktvD}|q:̤;ZR$ T%n֣{tz`?ݛrmRã*//R b:̧b5 r= B* j ~7[ш@"5ad1$i!2pNF]Ұ]X¿~`gx釻n&]flmyNAþq5rGRM3IL9뮖JA1fx`m[q"$(n ,1PetƷAϊšDbLS~#5R4(ц0RraefB{L{,m1`T@baĠT07|N1\wz`݂[xЂ^  + لƩ949~YҰ\âwn"zl"7ԄZd|F#~kr1f+p\pPV+E3u|;f/~?#2S%q#b Ip%58~4exK '5ٝuJF F4ca$wg5-vUϻopLJ\l!߀9 丄tioPB PE=‹v]#3rl?a6 4R';i,uۅ:DMtIr%%HJ⍻tsgSwH,UAL͝fb}\BK9TwZbɞvxm)3 ' o_/B(os#jI3F¬«I 蛩uBE H#)KYLl Uq<`Sc߀˾.=$Gj`e}"z5 GI7&OU/C*E<8@S@tdҲ;+X_jIȾfyf?1kN*[!/uPվXA1L&JRU1(B rvȂ0YU4lQ9'ㅛ"Dٸmlq9oA*4( ¶;s(;9rvTi#Vkq'aooqCh];cAZD;?({J_׿g/?SA.K=ЪO>$odZi}w5XsuTM _lʾ&dPP1U]S RJ֢R2fKT/bvL8A5R^dkB]'5+sa a[|1U2*RmUBAVv4euJr}v#GڂPRdKc_8j։"p}񬘈\@1̦.JZECxB/<]m*zl%T|]_xΥ5ԥ̠RRAJ;B#)z ra' uA;k9~I6.SKs,I~_'*ՁJ BFǘ\%<#* ^B($AICd%B5pnV!x1K$ȈW)gi[ ^b]ODCNvwDue7;j, Mc-Tlu5ĥl:"S90%sţH6} #`(.ԅBt>(̽ls&9Hdlk6L3y u(tsB\tJe/LY%9h@~f{__\]9O#Wy-D1X8`fX̺rt}8@A} D2c0DA^*mҖqțߌΜW6ވL)mbJ@sNl}O=!FGO{Jdji8?\A; u 2^VD3J$Վ*1~s`mvj(iy Ayu IDAT&83SΙO]T2ڧ >\y0~ 2֌=|ٟHۆHSB\,{77?Ϙ$s\41:˻KMt &`eNv:.Trnai^CC6!&9*jE*ғ]}roM=똮EnwIjժr~zO1`mQZ kZ$VP5D"cCL;f.cL rebW/ u'M~gATvQJH, 皬R+TERA  bu '| q5Mx8P[1oRdxi=!U.zλ.ϴPQ&Ps :)s**k%\+1W&l Gn6!Im6y/d#c#C+2tN3;ei~# s,tZ,ȤGp)\#7o_c#-3 ~c\`X==r3s'#q ogH9IQhUT Q,vzB'Up2-u\uq XOi=0Yz8Z3wq36ǜNqyp}JL<"+T12ۊDx`8l0ӻBmsyS[`G)T' R%Őp-S#llVpbi[(gt;չ~\E_gB6qdQ#YBC~?ck1ᅫ*u*[_o(YNɘ Uf84p1)Ǜ?p%¸>/Ww ^,$P >CdNj SӪYοiyY?Գu+<ٻ2SW1Y:Ce67A.$TJNe4gTOnq o`|GO%{t[`kZ[j\i&bnn82ܺ|W֥K\2-SqRC+8s XHi0Ip=1D6vLwYp`bR7a@axp1|/yx zSSu(z*'drC愞4JH].^K\2,CmR|-nslٛkAvp  i7^j= e:PP1%" _p1"6/ycew6b8y >ȱ^D$ZKg-uSi{IJ84u ǗXKnEKZF *ټŪ$ Ze00L,sckui37civCSm9kqb͉)͡h/Bf̋ e *TYT`ߚ]iu巍 .}VB)k< zݮ|S]0"7+P=UaVR͇v"QcgO;Co"p<%GwYT5Sh ?W&j.~L Ȯ{m̡[#ZwP\"$r}(kM;n˵Vd癷.󓭔-V?~^V2&M,VX/(iiDYXD,T6{xժs^,D3q|K2kÔK+umWyku9qvs{zC=q~$*%7;jo zOň-_A+4ox9LLVF;JdCgϔ6a:Ž\M?!M-0JGf*.U;E|2xkCy*/ xcOy[-Lb#iZuf-<D';&I{24cJ&i܍X[pc}s.p껜zK\inq.YFQ0_M+WXň? ٙ\ y}T|Nc-V`LPMryub9{ʄEvC/KHvদ#4K4&)Rl\'n융xB2n6BnM?;ō̏׆(~;;H[t4&ՌZcr:uTgA[J)&VZ@NlB)ƦD0r½\ ΍U.s"xgcC8$Y1{?kr LZ{ɌtmޜWBn[sgjCښϛv*l!tmKiP('3q`Ry`5=LȀLmW U_$ Cﹳ 5W0]ow(^h58 HAKn•U~|}1n?=!I0gE>=9v(j”EP{z ȧf,4\%3*t euq8wxk`2w; Ih<>LN-6"#9Qv1P Y8!DpA,ڹ8s]ZgD?\ݜv9AE$Z4Ӳ(uj&Ms̵&ùx1xR`eyQku\p*uX l3ˋSDm$d7tǭ*,ĺ ʅ?B{'I,wLYZ鞞Y v.0iF3Y| p_H [ΊY#uY*W{dFVU/v*͢*UDx{w>Sm/gK৷($%x*햧Dj:uOyjI#` 7-֒X@$aƛƇ_\˯p);>*0{q`jj :C(\ç;2䫡-ybL:P=̾ ޓ<::gVFFe-/ F{Q4ht %vjJ+ a*UI MiU.Wiq x:8)&37("MS>~ΣLc8<8 v"bkc\BPbD3jrk& iapipuVVVL#OzժH VIc-{U>.~쿲o.eN > sR]T, kNm6v|%)qTZbMTlQpu# L9|vW_KBQ;~3 `]Z*t>|?Cė8uśYx<;;Ru,X \wߺ>I<`I y7b ~g}H2:xw_cn8=vq` o_x)c~` WƙscS^^T꿣M-hٍu{mRb>e' {q 3i5&o5<2uhBلcݸήO/;Jܸyȉ?_|gkwˏ>;~ˏwX^} if!(30hQTIFc6o'`AXmɴ;(8"`KQ,St|/Q$(*ܔd}a.wdmX,6%z&0 9PM.ׇ$Pe쥻O ݚO-V2'\ 9?s8h=y6E̯~7ͅ0$IJWeZE'3mn⧟~Ν W_Xye^p7-\8{)]LF0Ru8 ]RfryLn̋”;,y?mSw׌1ss;5g/yg*ƜdxMwSƭ9 sn*QJ?)#;>߇N0z.-1wFtޓ9Lm"@s1W?C~*kTƹsRpOkd^i :\;Gz )}_b!-V.x'ׯvtUA9^ujmm ChGfqY^䠴%ESf]}ٜɅp4) ">TSXm@&~]QrPg"ED.(m6hu֖-Xo=2{(ҋd p? P"~̈rXYEzS]bY۷旟W_~lYTIT㼪a෯mqWΟ|[ 6P)+5$)W/fN{a/bki!Xɫs ^WOKv~d5u QdҰ4#JB@SָD5c,޼HCFW am> [v#.tC[5+ ="ulPEV*Oڀx#S1Kg=fq\qEa{q/xWoez #g&YHƀsջqvys~w`i%clNXt:|2)>$wV_]0x ;7iv 3,mTlPVTْ˓)iˮƼ3-Em?4AL^pĐ|zlBGVjNmF:g|1>5yLh1X5ʦomm -qY{`D A@j8G:!'6=CGs"xk 6/ GJE%jC/?Rx_L\[+B L奫Q_ΡڣgTns@#Sk >䙃\(@}mf6' V+D/4:"#<|M' 1+~n)3Q1<s+#oΥ-ݷ|hyͳXT]MgObK7 !/ww _,f̀ IDATy55=%=Lxt .}UR df*8VL#Ic<= *itԍ's(:B~_Ta^zdjuErvZU[_ϻ*Y(0y+5xs>)c=݋gxR 9")ZFbSYtzM-t~qNpk|xdi ^-z(i$:77 ?FOxZEh -5E,Z@CvIE~xznį8Ds0Kǭ4:69EeQII ۲쭐3'P|pq8ɭI?I8JRƩGO? UEvpH?ϲ^>0'c *oS= PWh|QdNXK+=7BjiU9<=nm̯I>aq7?1CC-{у(y~ IXetp ۊkPN<(;`NՍe熪>sJfo&?ϓ[zhf'T-p#DA@ Bzp~c]YK~e~ <#`Љ"k1jRgg{/NxjB>|A^7(L mZA\@\mB;2mJ} Z䘢E՞#*ϔMKC5*~*A*ťǕ]M?d0pay_=Qe<4.01}\TRJ=ήvو5Gފ{|`q 9:犳A'3* ]vk2ѭҨx:7\bD$/UU޽gr'!cvSn-_›KU-!ԁ~ş ^ݠDt u5GQv:Jĕ&3˩E6jhi ④G+Cx.Ob]L 8,P~vUla2f-hzZǦEl Y >RgvƌKlr̍s h'2(q7deud1~v"KZbM)c'V)ɮ V9T'pty^~x6;],:\Xq p&P] iuVpZuاᥢh.y^)fn$-\Mu* B֕] 33pT!my0[71)uLEP 5Ȼz,lI1+.N6VQ]f&bdSb19f6}mɸY amM޿qs Q]va ڛ?50IǐJ*GCu;dNјш;w%64Ӂ{G`JxfUqN4;Mբ:A1Ny4rkĵ(:Z#GDž1M5^PmӀ1Wv'bi5F2\T*-?#g7-f hd-QNzV9UsYT}lI\=WOX=鐣2eF|E~}",n)aWPhnSa$#8 ƇQF3)Q*60XJ αh%kÈ9MԄXLɯل֑ܬj8vV$3>fO!%Mf*$ Y;{QbyW؊o;i8rdh몊8>;\2O^ʍ_pO:?~z|X5/b:Z$>ga{uU5^ C$zC|TNR ނ%LM.`Gu(s0e'L2Ð2^,nٚ Xt0a$M؛̸;,B%9UciL2TDpO9Z FWp7GZ,A,GI\96xct.@%N7^d*\U:U [P1chs?E]}>ḙhDNB*Fq ) a}cJ*jGW9wɍ{|h{Lt:% 1aDF`u"-F^i ]qQ4E iUNV9 ۛ CxkhKUFes۩h&Z"9W=:*.Jdխel-G|v.v;ψLnu+gWJ]+bs{R^gxSr6]c",3I$ ktZ|8N-J@P=Or=q ըBqNrߘ6'@NGHH2<ŇDi9r3̔cǪKqHp}p!QbtU 4jLfĐ)uA*%qL?9%? s^Mw*}fkܼvprwogs`>=b v  Q Y둏pkUwl8I֩2+A̔DaZ43"p1?\s? Ԑ.dT:9]':E4`ۥxAA 5qRt/)',C*#N \; A YkIqIpǩ׉2{̝5z `nvǞ9~/5w}0d$.>;s` ̢.?$N7etR10/>6Ȇr&0@`2JRMaڭQ}5Ughfdzi-2Nm IX^迪_k-fxF@kJu-@46hqfxih.FPk"*@Ce/G_iI!AD.xtiL?0$izx\1FXl׷/mf^G-2/l[;ւ׺ =R/p|GӘDl{/vy0hN,t+^,q*Z@F PcHQp kBjE_]+9KZakL jWx.9'4REVO]8T|X MGA %G\瓭'f|mfMC5GrzsU3 ZMR F^ OI{Q(; DXR0U·Cc e9 \8Sʁ[uati&(,].ц-{<}0|#ﱽmE}teFg0$ Bo@+gmu)Q(_95.^ݴJLl`LdZW6+WO`bxr]C-Wzk:K9P)5x0OP~) 3zF#٤SsZ1#DT iKU4/&qJRS*}r"h K+|͟__KVs)|h8S(Հ,d0{<: {Vx_=||:ZEq3j?ٚH)84fBu*5g'jGA*UsȲUjT/OӥvfqJīR͚/3ZXZ4z'\ 7 -ucdf¥5_HU[] U8u `O)UOfQJZl23p_Z:b]Vmv\ ,bj[Q901Vo!0ds%d#̷/]`{4|twwl#$DYp}:VITW Σ$N~+6\G19}dFiʖZv&517y7b|3:3X ݹ hd;xv΢jD#?02]Ё$sf@#6Wm{8tY't0c.49%ޡ[:Yb U-*.bGB:D}O?MbG3}*8 @kɇɏTr&0^ycx\;Oo>G/\ VHTH-O]hUŨZ%Ui)9-@vhGJ6!5/"UZ})_"5 jqCmH8逎3x,'18wwbtDR fMk8<1$`uԓCB8 6MO/B֥bOU[acdzÈ0bk}m'䫛b.L4\&1Qu=i߲|@1ݟS>JbB%+**TT޸mSςlJ h8o{r2 QɌ6%ysws[dbx&#/[7O݆_6Z)ߋ5YO)(ڼ0W5v&1Lov+Ʀ$ M55Z#%@ƙ%.,ūg6.\%XȲ#3I}ȤT/D(;]^ʉ֣x:xGSJWNU+,lS;9xXb9aԱFQ iH@M){VLlـ]LՑ)blK]`Z)0ʣkCtZ˭G_ {i7Q,Dmm~ÝE+ ~t=5?{F_2 ̂bJSzy[ŞbQ\fccrYk|8P?XV*ybAY%s$8u/I4gy>b0텣m{0tN(Nz [K=)ipdnSÅs(affɊ$f)v'yu}ls![yFQDpE1.EY3c_"mK2*V z#Sd}4q92l%dT}^N{siih+tPJ-mH^} Z<ki)Ggě~Ƙf+n++gF_ĮK6$F*xقϣTA4w̧VKKȯ>$±lG5]'z^[ yvpTG86ߡp-jC $ՌoFa!610M4dD?3 nND'ׇFÀ ?_n- o'0;jn-| ί/]wG=S:bv~Α}姃>-G-nDCTtEiH_micp3{#ea9tQO:g6Ww'qQ`X_YLRMIƄ`L+||_ ̬ͨ-4&B߻` IDAT~' v ݠCom:;㳏ob?1εsgxT/U$V7C߹tzDRQf6HBrVyHH,w/,ꑮuHȠB4.΢px4U3/ ŁAN]J<' >x2K/ R'"%D6M>weYD*q!LDiL0i04v  2DѥUN*g~IS6!//ڙu^?ɲB9e [z .㥵!;ɔ0JL8'4Ћ"zBNqgiߤRh R?] Ueإ;i4>+yp򌳘9kYfO$vO(UY!LƘ SaZ:go  _[h.LS06ڟE抃SEeVO!6zxfٙ Ȥ:;[^XR`c>yo} 6&K؇ IhM` Wy SlB*4,vBD=Ŗjt G)2l4 \Klq=N3QG{l29C B VIk.'{޺q{P;0k'cz"i3Dh1x3JVUH6ٜ__EHkf$5A+;ɄPS0 ~`3\" 2˼W'˔x_Z_SC`6g[<+ &`-"J'XfY|qzr of$ސBPwc^u5{8U ˂ϴϔV{\>8i<5tsNm?=h邘V.iJ iB{ǣ;\bi}I@lraBN ^laXkh`b !`A=MU:AH/1LYuXY _޾ſ'opgXD:AW zS#tCO/E,%Y9S_TH3+A՜\gqCQ¢1E=DZgdkC}qh@YHᏺJ[p\ `a)4$&Mb4 % {_spkmˬ4[ \q(99脆0,h|WN<ϸ?tQr8b= RGɑ Vt<,J\Ť55VꠂˮvA#rg 99h8q!OCi[wǏUCtpsW[8Oh#X[k|}t҈փBcwygk72 *ҍ\7/LDn]0XO@eш&GVЦ)"Ap[S_luBTQHS%6F]&Ɍ{|=[ !Y t&54(6\=¶լ8s yɜzcSiѠ,0yG; 9IYUۧؿ Cx:t0 $Ɛ"b:݌.B|rf>w߼&{_av1KgтQ;FIŊ04ao7b G{" )@`4km-Y%Qt !0ɝԩm}2TXky[{Q'l3-b it+mH &mVOFoǴ ɖ?/>$E7.;[C#x&rRk_u.2^;g3½ (+W'7ަ7ڣ)c}ܝ`GZY22i9˿zGNmUˎ{cdRgԃFզPՈW}ܚc'5)\kR\52vQ`K\:8O 1zd(Zk%\Q(.o\ʥ!g¬@]uJւpo$Ȳ< )h½Ä2dHs;CI27ojC3sQ9ԇݲT }uHyV#h+OX#^qnfܸΑYu_!3W\v+IL2Š@nhxvCũk˰|W\`w}ˤ>I zO.h<#*^:0kqUGVcw;&Z\?f"?T1:ge(؜p;Lâubֲ %_'ET 4a&\݁iE{VrtT(E#Ж-ifp0ςCS}6.;X=۹[.'S&i O 1lC޹ɭOn2zSnCQMFӬD0Kj! !ۯhCMĬ'BR WKVÕ-X0G򭍬>(d?qZD]qI%\7wufi^CpEŨ?ph5琲ҞP}(_K㬹U{i;_j5Dɔ !}g( m J\v[a|i>/bz:'b5+Wca 5STwj\Ywzߧ_'h*Ꞌ+VsZ4gu܂\N5{+_rҭ9;0~fa4> ZZˍx3vfduQDg0;:s=Qi՘@ `ygW6HbP-(U!M rG09R~ۯ_e5~شfutizqwHtNҜO8Hˋn.OJyXe4?2!e9cFSܠb`ˊdfN ,b3&[@V9 d:6PLVo+G- gqrh6Zj]LKA}9@9ivXuT[kmngR\ 92!&]Z.7pF]R ۅ$IΦH<#Jg[ۗW/ۗ؃$ƐG심\X`˛<؋QlbSETe,tCMo w!Ue2Mc✛ɦz_6Jcd"c( b:CkR+'"L,fuccX(Ƀ%kc.YA.{"T@B@ն. ="lwJU~zӔY[威p8vP?S8]uPa)"n;|P8"jS5=v0N.Z׫{df Icl -"C/U6)Sf9a1tӘ^x>{g{h@&@WT =kg7x9vYN ӥtt!]t7yeɯ)p}hVI,ӔYIM.:xQ!&*ZfbUdTxzBdND"dƠ&sZ8 e1RE"?Ar0L;H/,Ru^y}&<`CML(H)7e3j-F %RRcMfb뤰(iU39n;(H͛{<^9N"OfĐbzJʍwR)UE3BbԒ&$I+}DZՑf}N^C ͪ4a-KafEAM!G-e1Z YWuNzZ.Sc QEnDޅO=& }c#@D FEVs,SKKUJ= M*Pu'] z:~dEa,:Nk,nKg=ܝtt`o 3ѵqe&x6sQF%2`w88fa0 'AT˄D-QˀtYezp.cOFH{C.m8bA3DDqs@ZʌYdocRJU" r9^6Y 3 -|:7Z1&u8f>r)MA ߽LJ&9q䀎&D9Ԟ gAqÓ=e&fA ehg?3lfʃCXKVCiM1'KZ(`ag^gd辸RgHw<~- _} ")SQFa|iNMr}(K16|>H6W&:.FP B^C6 ,wճPJ3::?5dÓ(;CV|R*wn{t8?wd1ZgjV Xh#P3C:e3^$x #~-<Ϯ32 ,έL=?3c'(5.|Jj`׿=:>=c?!+\)O)+X[/|\6ǧ%B+:(X8~;xr->*֥-z4YggqƷߣRZ''? ǹgɏg|쥰3JѬL?÷y"Pt%_Y S9'jޣm,aѬV=}ܹqZ 8%c X6F BƘPF!%j1vjoI^Mu д!Ѹ,AX֖dhhB&gplQ/a|{8YaWݫyXoIK3Lvi# w.b|c4k˨ziydY6R7ziB|&ƍ!B8rq#ڬ_+`~|cGdbbԵL?Fu_wȿ0oU\eyzv#:| |i&0qA> ?{e|XĠ`xh#G/O?S;:K/XKc pgaa!0<{_yW<^ϯrq2isgq<4Pޣ l;'>9ӯo?`dtGwȵ32>>а 7Wx[oǷΜOOp-HKkOLNdy},/em&`;.K O+v?яi.-dR"(´ "޴@Pz$ZA3͘bSeS\9t+ RV=BV@o||#fa*Cl<^d>%)fzT {x^h32P'f.>gxx Jh41BZh x48x0o9)&X^G EP)'1sMOQTYZ٠00m>פ\ZZ3Y~K4ju=ZRo+b;n١T3a96\cSɟ9?wp# 9 ͦ??feee)ߧQQ1k<^Zf!,>jOT$f[/x8s'hf_P&j*u=^Z BHf͢|Eޠl`Ge A&s3,/=Nz7m,Y\b G,*k!UiFJhUcu?fxhNst62' /Bk@D@23T{'p~۳X`:̙7oQD*t U6Y[+Qk8Ǐq>  &Ȑʘ[daz /mOA^',6jGr%|4(tiY2^m#l!!$CL?iԨx>&KY5x{|SԐʣDa,ƒ85 sܛ 2< 4@0ġqlXYYcjj g@P-qe2s2&!N!(75I10zW hDmˆs01e5rBQ݊[,zfZ H1Ͱ3mHaHgԬwKZpce K#,ZJO;{muF SPP 쇿3g? ) KK _|ƕjN]ꭻ+Ҷ0(*l1e ~5jJX[[GSG ;s_k?OX_Y_۷+4:JiUHs7 ~ ƫc*&"7Ow.G4.s}fRfy5CSyf n*CG9TTJ|᯹rs*#xoWB񛟿ُ"EJ(KFi;k,ܼAٱ2v嗏- n\ǿu Rܸ͛s= 6*UX {9t$)[Ҩ9Y>(eԡ0H =@IȰv/m74C aLJ*}jM_h: )` O!:[yfn}>XhYB)&a'FeEf!ehX"$ UFlwa,) } pm _A p<0KM4e2ĖxA¦PQ#l J0aaprÒu4(݀o G`\(G~YaFj/\V BH+HAQUmXT Ģ|_cxg0iʋ- Gk6F8! E1R"4 a`Y!,A&Z{aaMKɤ|OڲI6bIǶq,۲d8BY{'mщ씸~+UlD=+!'1dM;|A#;Zed|ɍ2}lY xqFãX(n766X] ㄈ*:@5+Q:6ЛUh6}/y>XR R*)cH 0lMTZAhDH'^t8Q~P^ aIV`( AfhU/rlAI)2'}V@^07ifBJhBXHtd e!J':%AW1CAB (YD xtB}- 55b%3N$oUrIF|D 2E-:)gyvCw\/Zm %j+&La'Mʒh(}7,6$U@MeD&ZatmuZB.q0cT*uZ:c GgvD۶+jwycѡ %ý_wp);!^A'CgEN⥖CZؖ1\SiuIZ|MH[ ʶVo3铴hդc -Ԩ.UGiUz x&Ue:ȩtM-2AqC0dǴym*4B>*10G4B*bԇ-]Ȫ=6ڰB8Udq.:)FD=! tټ0+"efӭ$^&Bp,D:^XHx8-F!DBvät볭KMhLw\Aϣ& f HuƎO{td>h緌0-kl_mR-hSL):h}E$B||{%[%gaYxE[]t)4ڸE DGmF/3&Vn)i+^ S0-ȫFN 2ݬv{C[+-hWKֻ ؾM/:7.YE$DKMׁEڶOz\m6*=N3ZxR=گ &iKFa8ִ>d []nǧi{ۡXw%,KO)j-r^GL#QϰD6m2T)u4%_MeQ =@"@v/yY.-bG21DWDŽm &0ĴŻ{)ٿm)$ ^7DI<_m z[vwzb m@5vD(Jt xR<1;&&&PJ'+əmx_q/\ՂhX`dUwUXV;0!#&_"OjuGh?o$aZU;(`ۿ΅k-pMѱ?XUbp۴~wXg[]ٴrߋ{F%J)ff;H$dKGFly=vT'δ2^v̙ͅn/]H"I$k (knٴzc컰L/ 0HN+uT uK"n86t IH}:8-gz,n:&o\:FI$"z 1 fMoc6+!߭HD"$DTp1xl>JA8Z?/x'wI?mM˧A%YXl+)Dc IDAT|.n8fs)7X(bYVw tS3l0:1(5$6OB}T]׿rŴmv2w BiJզ{Ѩc4:sa:ƴiѬ 80x:ofpdl fg޳HP`ڬz̽{wdq-= 2>0B^+hD0^~4l)F5\pPZ`mR4{_"c㓸R38Ѿǥ?X ?k;X`Ũz{*!t^) I^X6sɌW{mfJ^!{cVÇ0רo?4U>z 2Eia9Rr3ΰ…2y80e>odȍ+hm4>O(cPZ#FE^g }W\kzI&'/`IP^-@4$A(  aY`YX=WeHj}-m 0JaA# i#- K1HdkZ{=aneS`|f}!Nqk6Wdl~WX]Z&cA6Y_$vX]^dy}1K\ R)j*Z5ܸu[wHiZayi;buO?crlmV|3#  p*T*PAh]MEիJ%,ʕ+Ԥl?AtUu v'uA->]\Ʃy@x[p5R+Il`41`KJ3F71ƾhCcI&*z ǯ:|1@k UHjPGPo~ Q/q_1?w0X _lCG^īoQ\W<^|̱@>g|l/׮$o星Gl{Z eor5?8~  ր0wwܾs4L!˃G;O]\Zp}[o)ϱV^ &qש.ognJc9μ Qo{ܹ*So0"1T*šC:J^},j~JmVmحZѣ:< *4AB;+ &jH4W-_YefCSJܭCi|NH^^ u`,!E6/h) (_T5un޼ƣz bccZ~pRia4Jk`sG81cQrڶ"nʡVK˶C&C ٨RU iqT(00@L5#Kb!?8/$2PT5 Q$u"Ìm- q"w)ޠ  MLMV. 3%: CnF "S,XYzB&| LfvqIj1uPF*J++(7|릨ך G}Pڀ8MFadY)Qo`v&Ѩ !ɓ'5j|r2Ry ӆhhU(CPʧhy AZZA^#VQ DMyBm6HVVV:hhTv_"%iRatƛ[%ٝF!&GRIVCn\%+H;)nݾeIqȽ9 u= # 1ABXܔm,ї@t8%򮤿/ܼhro$P"t đCp 2'+hԦm_i en ƣUHafo^浫^ '+_QY_*:W-02f Zt%H[ V䀮m~7oxtU1JÎ$s <#"D'L#lב3W+(lVE i6jk-#ako2',7X|Hg<-cx𡋛 d>r-BHedsy&&y.W.Mkx<}8vSS}pt@XDA6G>F+e/pY#>xnrE/ҷg#3f0;;2"\'Nĵ+Wzq+ 1ߏ6C_>OS|7b``׶оԁ'cOĖR*~ئđwsAb w|DjjCKW4*JaؠxBURY~DSYQPu{&2eˮQx4?>y3L4!+PB32>E~p#O)N;eI\# |_|# RձB L,M?00ZB#,o}>)'+zXL`tNБ#((|.˧NjqY-W 8dyhi@aR=xgaK+!a``an߾e3RZ-nb^Ė.Q+"8\FGRvVle mg%'R"Cv]#6- lhqQlC=~2tQT`ڒqAtv fڨpX_<~>8Qxp"/>P(9q$tG%+Hk,Mk󺈛" 9m6Awm<\K)%RJ1\6G\}Cam<$5v QMςX|۠>co~-YCЊ|Ǐ1T4+Qq]qqzMeMv+pQshuuS/&1ԧ7<"[ ͕'744DTbuuRԖT isy{u%{.Fk4):5kkke!=ulᡈb+Mn'"HdGxM*o@HA@?Q-,R.$MXdttt:a]!n~69 ZM^ kx[rxk=V.SWT*iw_d7׿d'\  0<dii |R$;m"|#R31900Tw|imgSP٨0{v8~8)7%-$+k rJNxL@#z vH a=}p&ԿViw4#%s~n9 + 4*5L)ZZ<ύǴhckѵ+#zű {D&7=O*Dkno[`_5$J"|qd(O{~ nc+[]@?f5 YቧoJdך$rx7#vP\bcokjŖV`!v?atR&D$$I74A״lOGd"I G<m0t4}OMM ]lT0Ob`l"b F+ʥ2=.&pNYz/m|X]^Z,/-QջX5r/- E,_iTKe6J%ʥuJ j Pdvlb.Џ=:v{ QhJuϨUk7;*9uFz}v(g1!zsγm'*߱Iw{=6vjhy&ݚwH;eQ#ӊɌn߼ݙjZ'Neٷ/e~*ʐH6t'|0U#'r} R^_abb0LG8r0ksif{{f8CQhWX\dpxC[ħ,H284¡GwL^br2W18<NQ.mr(J`$)FBIC!tDj{E t A"-m`nB[ gj $ 6+&.L3O? oTr::;9c\`SQc=z\8 C{1H5J|',W8y9: yNdChSD8il|/"5<;RG} W3g$ڃafyyK9t({Q@2a,מD x63P'Zmd4NT40W?U7Ԩ(- aE܎P&DP0L::;xlVj3M&FV-%] P-oPX游JRqX\X\u``eF`\6IVZ fE!TxH@)?|ZLwX5CA2 )*i2x#Zk|PHh)7:= ]hB魥B Z5vy>V׼V r%N͒Ne\/GPh_[NM]*vgi J1<8[oMRQ+lnnﰶN|G|^ab&ɤF u(nH:u?Ks\[.!m-CҐuz*J'6ְdHgÈ'rZYm}AC{.k VΞ~s'LH$1ۍ#LFH$CC|ݽ3PCI#b 4$ll6YeRa8t) R4' LdH&;vb&{S2;3*̄4 $3٠oBC] AWO|XZ\° 4&a,..4%WȆwA2Y;4R004c{ T,Ra`hD"ɾΑLB9Q7FZ aK jK~xd2aϾcת\rյ52##qҤo'GAã  -aT6ރ5ԓ:50<BB!|W'$xethҦ6<'w7thR tHE8L?@(3:VlNm2i#%)R# N[4gʢѿߠ[%EK v^bEƐw_סJchmc$&*"WϤ5NƩzMDOѺIA^1SoG%[HRZJ2Bm RK-T7<6ݍ?P+bMpq׺;#whT0 i-s|vrۇfM Lsm TW; cfA<.^9fWWKKSՐREPګZJA2D+} )0qHA(E>ZhCn(PjQw u@}"3*(K/Mgi@J\.Goo/ak)AKрnQ!vB n<㢔 ګ ´,ҩajR94w*"d3E_&n%@7-MhHә V{=BŁ;e w6Iېۛ.Ǎz|N凛D|R­_?{dN;ޯt7D&sw  t lC _v򜄾 צ?gINPLGjS2]옂į7xh<^{ 2F7V]tto nn馧ݗϷMwm6-|o[kwwYz*Ŷ cMEzK%Ԝuܷo)}BQ c@fJ;pK~xs\7}va)i\a{@2_&kAi+}' vl;,zDk8'd6)5o8DUI8Ch1:60RnVC t0@mkf};O8t %,tLC,׷;Ή[w6qeѨKfZ/bt0D yX`&) @)4^붓/8 bхւVUgB4cq%XEEH{k'M`L"6(H+覴4Qk5`O(Yc);BP3  ,ҼmM_@6p0hoMLAHX|n^ޝ]ZMj_(30jRHA>uÙf~A PЗu:^#?}J+SLb7u-WL,9A1e1oS&$ }f"t JYXaEHN)0]z_<ޟ[]P"e1D-6r uklǦVq=7V<>+e&4M4 -VqlR`er shր*pfKWI%xt0C.#fd]]TFO'L|+O/DP]e4'-E{afwU^{}D"Ow Ku௑Eԍ ft*\Xav5|''@x B|3Knt ёӠ,B/t,;\N]ۚDÁ77/ɣtZ!Ectְ{}2!:T 8"gѦ)VW?,> pIhF܀Jk8=X7[ CJ֚ry &xw 7LBJV!n|K~ڻ>J )%J{ԛnVWtې tF:ٮrmf_\>&mi Ѭ^,eN_}'0ܘ!"NF jv$bÿ{r7oe?IgBj2A v bK AAuaP Z])p &T6̺0Sq)YidjU!kZI$L.sJa$P+C <5㫃x70`eW19G+IT=!="Bg<âVE\)2iYCڡX nSTdR]}Gi%"B=(>=>'I:p@!tP6fcTHDG}HǙsMq!#aV[0g{zHE}fy"I! (Kspu׮5:̢EgiBlD4tZI~?t`?`|NA[ōCL"Ec]pK@_6IrJƒNQLJ>^Zai_seX?'᝕MDC2W&7:yfoN! kf7*|VeUK2'Pᷠy޺_JIZc5M[U$*0J!#tu䩖8ŵ"ѭuAC)n!Z Jgr,|i޵\:Fa%Y-V}ʧN262`Ʉ]132e[K ]B d`edd@pV:HDHGًӬkeBF#XXZajnm\ЊBb|=}&՚W)0G \3-۾ADguǒJ"זxveۋzc:h7j87|WR@O&I4Led]+)f/p:  dfY1xVk3kd}I(>e4Pt4@(3$0):(j!<.Y|ѣJ2`ݷp`-gnDK۳h\JbŊKU(d?|6+5TY-UQ)>u?, \:A.J$ysTU2-EadA4r 4L\%)W=5Etεja`ecuH'LϱCXL"I _\B7L7AQΣB"vX| T]M̓RAi%zrSk -p36z7&ЛI1N2YqZ2) p}Yg88'J t /Pذ2Ք\7V=j a=bN[zOs}.v[\+`#'%Y5 `b䕅eerh![jKgN d _}SG/7-qlIh8i4rH00:_O=ZblqKMaP:aF`vIl*JyK o~pf8yr/I+Q(F|P> 0"&7hdk@H#:F@A6S*5]J*Wָ\Y:6-G0aP9Ԅ rG+}t{!JEط@" tP*%Ҵl,CL&vpտ{f60S}tsyR ȧ\lH &\M$KO J" K}!AoB8S %4Y)ЎFKB"E5m2l0T6!#e;w?`qv^\]J5d"e5 2! lEq4Fq*j~|n3CB* R@Y\^gb㡵3'yRtuR$0G沴Zx:$bTF 3pd"&B_ \[ !جTQJNOL\) \iP9,X/Vފt:E_G@Q+$!G"l\*HS]W %:V?J8 otݷ=g,- "m^K!/e2 \C+ |ͧ^[g}SqQxVk|^as!TƇ{Vشy\a:F.`o>xÈxJ7u^J)ף\ui0rI'x8@R󗘞cRCcsa~c.yxƭlAvy卷ygZ -/󓟾@MhihR 1}GIKw#58 EfO5= Z/!į.Jaۓ<1#Q&m@8ɷj.SUc=ih6%LGyjk,WP)t謬MJӇez1ɰ--6ʋGw_昞۽Ǜ?U%+ҊJaF@/ QsH)CI> *WY[_H\wm>̋?{7J|>&a;{pɫd&%*JL̮M:.<{: q fRҐ̐:RTo~nIiI³džxpB,`;uR#h2^K9Z.i2Oն% ;ͺ?vi15UD`ACյfFaVH&#$)(Ay,t=,E }[PAV82Vh;uЀ+lqtd&;]}]N[^յ&LtG6eogl|? ++2?x8vS.~@јphNeU9Z0`HAOg!|k L_HC wg.\X<;|RƢƅGxKgϠɁa6<w._JR1D{vPbe IDAT\9_)[kK65H&꒾y 4SW9oO $|x|)+=w?\JH&?_f>)dLi#LĪ%`i9[Zkl@/;oSZ]'y?K_)jRF &`e 3pFeȶHݔj`dsj{5,Oe^|! L3% dlM2&0 LXT!Qz!%R\2?Er Gr W+շ8sqGIBD#Ԝ&If; 4&vs<_fv\yru*tC4۪,ߩsz٨H˴Qz sk?ʕ +ލ`Уx6jHGNTT\pe4:lk#5s p[N*gBif9~ ] &pq*5%t03B@lrАM}'@6GI][{}em?1-R/gc4 -߹h'dB{h":" mʁ S' :IXg &|rc-LBH i`%SXth,E#("vɊT;؛!4F e.&ܕk> #Jv(de[v*zeo8fYu.4d4#~XD. jut\MBNjS8nI XF"40"j4/&Jy$ -qN59aX;zbV77QԍҊ)#m61δRXA_O`sF*{L$1=%~S)RҢV;;E)Й) t;gatvvЙOnHBA@"$LDso?ENi!`f T v![=8P:d3h}C ]_C#|k1ڛGvY)=FzӃaޚCrXh nr.vM6๯=J6?ejnחMb Ԣ?!%0CH# f N0M^~4Ssm_]ฃзm!=]y~ vcɘ۪G)HXcg-^zcNOLQ) V(޾t@E6p(I{&j+N/!n$[ @lzg8uȂԛuu9ٷ@cJ.'QWӲdP4Rh>Uz54O<|Wج8A veH: y Ց :i40j ld6+;ۿ@BB>ald.VʼnvPm*]6\ Y. pcP,.(-B[,/-S) %&/` „cH|LCi!L,`VdGqpp΃>w,JSh  Col{BL0ٮX`x^T= $Ak]cs0 Zfiqa,r;_X "4ϙ*؎F0 )CyFFQ/uM蠧tuieP_N鹮GlN؄[\3#б>M [Z՗^o׊VL3y.33\fi麨E0 BDGݡͦΘzЙَ˟ٟ{`$ atdK?ӿ4!Z _> 7;`D+TICEfL@_Ԁle24_Vqr(8`l&hN}N/T{Kb{k1ϢlSK;mt@-Sl6UkW(,b<zBOI9|2K r!0LӴBU]f*9PtD<b@%x8le]~Df~]Y[Ƴrӏ? Cw;/ރ)>o8~mwx_WQ20s4Wؿsgca<@{?f.ZtW<57xw<~ W/ryj#OMY҅aN޿/޻ 2[OGq6'}5^$aK:  r_#/VB200ѣ'˛|[/Iy~jedRR_P< P)ʕYZݠ(|!LAƊf(8p%]tғK! 42>Nwm%) OIıa>R]26KoW4 * ˫EfWX/ŞqX)dӌv3I:a0xfڭΠ?O֟)A׼[lyozCsr"4L\ ?{w?Ì8}}ԕk:z?rT&P.lG|.1u,+35ukssȞqHe xj鮸6\|旋9{k UcAlR77)%L}CZo{Z޲/VgG XV9wϿ.JJ(@=R J묬qFm ktٺ6:@0bZi>:{,w MyL 񴱩5B&E۷$O}exo3<)Sa]:r+$[Z9L5aN:Ig^`e = ]?q|" w1C~߰Ag\Cc{&Ky"}#{ɦ:vt Q܈{x㱸A̓RqB7jH02ñC74SX٨ B7N99fHoQ9mLWAو)x:U͊&$sT1ymb5ȑDpDSE#٘M ZF0q`cH\:Z}^*R,oq;sh >)XY+bƺT,͋hE"Z e+jfTaT t 4-掞 g}-$3 NIeo~[=躪E.Mcjv!{0óuX<ѽ|}toH 27(q]q\ŶOۮa6GO>ġ<.]˱߽"К&5\'(ևhɧ\el2(EPF^Ջ%1#Iꌱ( hi;J Fh XYZ/Wyx'IՃIib  A;VnYfį|ߏ+h]Wp*ERt2gK,WV0X5/dH36f;4׷[*"suAllVPVacfAci& |i62T3`DKED7"1/_gYи7ƃY=֘§@oO˹ 2 Q ᤼RInzF P~T,RFUlUq=;  h2lB.Ã?ƱeGA@C*С9p #mragSl}mK#`RŒRJ |G\)lD6PJ*$i%c{!}Lj™<ϋ J!m717 _/5ǥTlx0L]=t ]b%T",&aAns1 +\Z{^@a6+_6՝C(qgVݜI9| @Ji$ZXcXYsx뽏6ڔ8D OwgcG1 kktt4'0I,=sM[Yޣ ===mp8r \m쮨Zv%E(e 1 QE2p83 {_i9G̛YU@1BUe5yy }(;AR SR~ۘBkRkM3@c}? XWi&8*wF|WsG"<"&۹Fw.YHh* \y3D1Z8.LSB${Dg3:BE [.w:xeIqmD$3_իݤjqEV2yȻ084JX-v&f0>2Hm*,!naue ;[HaPΓ FW%(q !v\.e|\A7%-05KwW7# ##5JxJ=2imjrQȥS IDAT8uw0z&I"80J9nSd?E5TPJrnQs||tmp 9a6h~O/t13rFˤikok Y5 PNJ.+g~zEW;]]NΑǨ=N|E0% :l^}a玳)051 DjHD ZYYcvnv "Hǎ7fƇncB|/Do ԻPʯ۶PL/G5%ׯ]'Y7gjYD d2 ȣ1fݤ[ʕ?pn#^c-N4 v3&Nu=lChFjrskF<eph1Wylcrz#ZrL-=lGq|>D-1f۶.~ڛ So|yEH |sxD8鳬Ah~E >c'Q: Ō鱙ҏs'n*@_Qjc5 YBlF@2҇wmZC# gZ̆Қm?_+ZtJi\HQ~qKYZ_s3sKxQEMR I SO{Kc~l6CS]ܺ7JGW?]%MՅkX.B-( Y]yOy&CW SHWg}x?ɾPKw{7:rsDG <-etr?{ֆZ 3swt#-LLLJ\"<+w=6Aalk?Kw. Ui|*DU.PQKy P;< XJ=)@ikO"T&ӡA4QTZ*zDM];HM]M/8L?\;wS'a"skgY(rmnJ҈a 2 l0xUt[k?׾4@3H kڞ_pmەM̉gOquFFFx!FFpϜLҺ\.Jgw<ss .Ґ ZUvHmMi]tn&+2s4:!M\Oc۞_/YZ4 FY][cz~ "Z[wGq\0buܛZadC=,ײ52yOXh(F[&dM9jb*ᑪgY{i(z!$PȔXr(ZT[wR n.E+M&%I[+Z0eQ #/U]U)Uji<uq]S8؎8lǶc~~vR>YXT9鐸fz:iKXJK*h'Ȭ$+kƽqL.\S儮hCFkM2jϝD6qK0==ͩg5alqѱI_ó]ʇWp<*R˕[ACCIDAhVVq!9l)% *yUSӌOh#d{u68mx"I~SVsw~jq\&UIz-ZaR,WB`@#]X0M@2<6VRJ,+ ¨Z J+|/=pAPA0YL,b{v"Ouio&gR-Ѯ)UǔR[vb8.86`MPU O+$6ȔRdHsl%afW-=uiF_(4+tw" SZ$,߽>3%wn-o!MM yQdxSey X²:phaɻwck+G]\21a\rH7I]0\<e7RWP$(/DlA!{eQ/waЭ,+ؽwe6Gr_U$/Uwԏ RO 0bPu˙mm(y$QQyb`QLʐsW*0D ӊy_-lCȢ`g(~渢*ڇl$PH+zұB d|zuL)璳)srxzr2%JՖUUB*Z2 _NG9{:ki%k> IXRLnbX9\(i vVA(bPEqMV_9<K@ףfQXmOQ|T. }po2jh#pXW jf >|}H&][лs'f {jBl $8~ENcdYauaS*!'aGkcҳL738@?.MJmTqlU*6l0w IKC-蟑%u4ԧ|K7#@SRҐPEjv1Pw]ʐ 4u51SF"dRUG9'B_ehbռ 彨B1hԺZ&1h VH)ZjSxRQ@40),I uƩ)kU,U >6uEr%QtQ**`EU} Bj҄WU*`*!fP3M%Qic8K._`-&7M!o1M #]|rbOJW%d9$z;y^"rb͟.Erlkk.5`bgBW_ymfi#,}D dh#LML"e2a:^9uܼ?BZdE:'8?E? \%c۱=%-*T2?Z(K?IOTT,gaQUKBv }ě1ַ~>|M.?CS~PB04xOh$Ɖy#x2B.[?4vv+_oمyN\v>ѿsw2.s˴o+_:=[?DgNoasCX$?zۃČG֍ˌK0:5L4PԈAJ$/p2hkJ|FbqӧpXZL)1P[wHAcCgݸJo }`4$ j ByCJO%9;=ipIDRbx"b\>S'0w% )]!dywygK]G/H 6  o(p\eaJo{p/P87MWc&k7oOX:zzzx3޳m|{ŵK00 .sKSK Ǐfu%c-gss/[yzmvɘOt}Vd¦5AJ~hugt:Ÿ)e )J眈Jjb/>.2?` 1K5#@\`U&%b+ÛEZ9X1lT:@ue4 n_!!6>`C|8S`IͳϞㇹ=8MpE|ߥ9nޏ{E$Yo~{ӷ}ޗ_!\8;Uwhni'70,9>x-ǟW?ß6]ikIo|Z97 :gf^30oJ|;?nÏÍ>ۤôfhjvh˅_`X[ԺJ*hDi#pw).2tExGI>V] Ѫɶ-=Fo#>jEY[$Isd`@.TQX[?gzX,훃`c_O! [P1558MDc1(H 6Reyr3v"1 uF]&,R(MUrrQƻRSW˲F$ ,uE[spBoa)A]@#>!P=SBִgLjH?)G ?`s)yx_%{`';ˑ'֕sXI8p+wJ$df!k&f??#L~ywx _+<w/~eݾ϶y\>acǎ2=>Dvm{KN׾Y?ryE;{CdO\,Q)/0R"Xm%O ,vgA2"1*U7\y^OnQHZ :+]J?]OBW?;zO!*Q>  +i>ys0 2RQxz0P_8ʼni`IOC0PY[Np8$&|(S{w(x;$K(]$!ѽlZciņLONp N;+/6%5h7׃9S {T]C0'bW:&ṴO_Qv!XY^ wux&L&O`ɟ wF߿#fD"Q"?1-L²,,4M恑hXDD;+$K`̣XVއ2HH"I2,;i`O /{rlV~;ľ1 ZOSgIK$q|oC`m%較iHiFBCbS9wٹ0{cZd,3LlKCaȴ I{j9_{{q-^#8B)'X\IO")N?I$#}>k/glbWa2Ji\D=`U;n!;!Œ)ZN_ʾh:?Bφd[A"â2Us#uC̿!p]˲ٰIᅇ %"T$hCdGr>uφIyt_eS)&bơ* bc5} IDATCdseF4^HSJb*0Hiɓλܟc%hmܴ kf~9k @5ֱmuDMHfhl+PQJ "aZv [ ϲQ$Tc~q:M̯fptqzX>~ncίUN2R*=8<q EWk=m|JaEH\Wi%LvB3!sl1Bm"[q+ٺ*0>xltl,$" ŰX^]c%e`"45~mX&& ,-`Z,S_W|T-u5d+t55 `EV]RT_Nx ůUxVX*xξč\H~ M!ť">(E,a_,DHP~T俌K!R=.lWÛYjE4"Bha2&=rg359Gv#{{۷qp.,bu-s xCU7s^ZjZ:ݱ9ҹO;uVv=q񓫜îF_fyymۚ' o#Mf=t1EORt4ط\ s|H](e7G}Hh]+|_e޽*7xtΦx,Jcccܾ6g"s|ֻ|1hlɦs.K+i$bR1CA!ajvȸ6K\xrv I.'͐e.>u$~½-O3&+⦅B!1m z\vOZd 60Q:syy}"ݘA*.*B٤ZؾCΙsM7/Çq${g/qhvǑ} [0>>9q|'"&~R @D-0"(L .l3."1A}]IlFJgΜwdr6Qz/<Ͻ;7x,5L-?o Å(AB!h+ ^ %P bY]u-Ӎh:u7y*15T=ͻ&\W.k?{C+valp}z{{hom6edxV^ɏٿw-\zaE&RhR(P R:.8(a5@Dadfy%3Ikc-ܝ; l y8sd #|Ey!K7rm6,(Ij.;9Af;A>x-nߤ[C\3rŌh x6 GwvpQ<+Ҭs(mho囿2I&ne_n:sy]5f3a#׻!T6G^q`S;~S ZBz ܸC0" 6WFyu͈H)s >{ϟ:AKS;rarqah+kw0u8DZM(Š=ž)T"V8NLہ)xd6?8-\uS#g_g14g^b527)]ťOnֱ`PKDY|±mv VY"8Vso\uǵK]D#R݃))/6拗rN:|_dzjqƦi l7" d[4ŢQqg Hi"`ɐ7 Vtҳh1MMS>9eTp{h)Xf|&fF(s+x,h3 Jn,#KzpԃuZqIV8Ei2sK9T P^ۙQhjh bŃ@k΀@O$W,,Xqz0?!6'Y/Q>2Ѕ$ sLAI i԰0kJq(.WްGɂ}ZlNٱpep4V7u1)O&[@X"N<`rfI"I4G 3 6ED6|eb"~C,z<۷ul:fW (#^n0+-I8Fq'ZExH0IY_i)Md[w7m /r-fsd@!*zMߵQJS O9 h<(hZ>hȮUԅ]xQTyRUt4SPa,-aVP5n~5~ж("nrdCGnp]`9&g_a~0<x8mxƃ!BoوahWS6 .Gpaܽu>}$PH )6&ʇ}_o뤀%A - `Qw6`hx;wd4"V*;>?# z(fqKDVkZpQB̕{G=8+ǰ8G  uhMD-"AeIWLUGAZW-MX<"4ߑdVC9ϙ]>~wǷ/Bng"R'9j-;wXȹ3? f$`12Jv'{ƕn\c{^zarj=G[pܾ>őMkcL*s_:0]~۽qs!%u" i6X9Uݢ.zL/n6H@[k5 $w.1z `dr}`,mMsGZu Ƥ{ji4_Om n^bvb.2&>gz&s21ea~kLNHl;~xx ߹a&³/b[G~/yGXI%xyKwcY&Gǘ*`P/(t}~R"ݹш3\CM#a̞r0I>UkT uku@tE`] BJj1u6ci8ëxɊ ѻOv5 >SRAοW`I/rcti8xT[-@<{v3g;ˤiT ;wv I8tC2?"OFZݎ釺6Wvs8,.}v];Pp4 .)zvk6foؼD~oY"ED(_WYZ`48qp)ZLةQ٧s"d+A:B8:Ÿe=dn5ײJv}= 6k(k0$Ud )ui{]MM,b'{xF T%zys!hii. ==6?{ody}Gkc_(ɔdImKmdRIUJLM2JIURST$˱eY-$%q).Ab}9s.IH}5G/ 1gA[`Q0Ρ4W^6I!J( B ,=˖w{+'.Zۡ{IaIzz ]M<$twmC++6o~%d'= uH涪HXEGG;^ T*77zl?\&}qx!ʴfF [0NH}.t5iy˲艩 S6 ݣeejMIh_lm)>sVlt%BMGZ`*M33 `VStPQE{O:|\>m\ѡM]9lu'qtbu_'Q[xT_1T=drfiH{'TtPjHNYXKS~n}"^ `=0M(GOsV^]A Fqt|T)4^m΋-(P-eWt uFFs`\3&x "O IDAT'7M 9AӶP)z(  mpּNbCAUQ(CL~*h6Q^Bw/18%%X񆭄RwZxS˲Od{ن0/z( E{/X@Ӈ ' c&4gذK7f+[)mثҳVxc`r\q{8ÚƘx*%II|jm(jMaBFe(8k4WX,UyDP g yGQTehgﳥ%F$Q)HQNvQy}ق&J5\uDAk}NtzM?<)xfaq/]`S{0 e3EG!lmw`"і/|! T3.]Y%@:^S6Wn$vqx0Fmdu%N^ OqWgńXA6B}:Q5"_Cks{v0e3X+ AcLn}hC/$٫&`,]U"50`7ZUᓍҔ鹯86 RH xƛ4LNAYGKԡw\͹ _#wkk?ɨ/}%^L,O MHcBAܛ~QAJ>IyC{wJD%) 3sg/OK#I+6\U6$$,,g`Nn:Z];H&5ƣv7C]B2WZ2f+DEj79 ZkVxh+A4"M;HUMURK'|i%ǭnAr FxշH%DEy;LWNs nMGgeat:L.CU:&wVPSUm(3:6O_y{w Z`1P(w2޿Ln{zE-o8N eokõռ(fSWMrԻDM1o4V5Us5TM8J(03B G![-ިNuZf l2CXhlX׭VBJ 3x=)W΀k(-PʠM >D Y<cEAXicY6mm #= #hm8ioE&;h Gyh?ha{4"Ư|I(~B4O_ =- Qڗ BJ aYaKزm'fyd\S~kV) Oe(QWڊL?ޥiS~(O 2B }2Bz<hwNrl4N?_HD46.=e# yWYNg lѠdڐӨ_ίAT08ePxo79}x[w.q:w 9ԠGAZQe{{xǮ )l!%(S`@ؖ0 zQh Hh!,P.nBxo y򅢃:X<nhe$FXFREb0ހ(#Xp\均*0;\1la׎Hwt`0jI kF,g XeD%ڠ$[p_Π u/ؾ} 8\Klݹ} u2:>|,tZE9[/?͂(хH6h,\] c|h$0F+0$砦N|ޝx m6YZ/_/jF!Qڬ~Xuٮ{L(04*P0enAMu'BW??|sn^sI//!m/e~gx{.mm<#{!F; ?Ƨp8ۆO}w?ƃ8eBkM+\ Vc-EEe%GCKF2h \LtŜ@)drHGId[-Qn$VMurPUC?,(J.R\YIQ Qm[xClݔ".]]);QzM58+z7UASKQ9EćKT>Qd4ſˮCGؽ M ),/r_qm"sd4%oc9H1_]]ˆ9`qfo615s2=C3{ee_Α;v]ÁJƑ8O}idi>;|/q3|Gp~AF&N*)] ^Q7E.)hni7EG3tݹ !ax`[h +ͤ[)L,PtV5LA9ʯy8D&`u2|OV< _tNL>KN69lePBsܾ'<>ha,B2TE; Jd9vN  );E* f"6DCs<ٌk cd(PtF1dz#Wް^皲FkRMqwIof;(۷SLϏ~ Etf27Fpч—ı%3;/yKں7_#!N_W7}L21v# 9&:zefQkˆ2a߁$IiAvy[Z:飫TAƪ}@xX+4)MhqC94@K Gۂ{ﻓ3i$l?Fk;БUQ6X^+ THƙ V-NWUixý>L׎}4Gm1b l;oh}EVfw ?Cl5O}G?inNOp +t#p챇h!i;w 8-[8ri9r7)" _;xY{ġCхE#(e}!1zw3-M$cH5h%BfA6%A+!j[ p!Z0;[ ݆Ty&gI4w\rkv:”L[I4諍FkB-M*e/:."nIT>KDjl 9Wy;B$L0>LZS6B@k@x↛#T5yc2Z++\b%As8י@k\7B p=6PN#>\v8̿wcVE7`!{I=`Ǽ<-l8siǖ)%`/g@ad$-`@ t4+~wxB&׉Hc<ʱB;o{ev"ɿ7ZD Wm_4JJڬd|ٟH 68D8~$}x"BM>c@7PU Uyu<1=w~;0Ls "iAX&[a>My(ۗe]!.۔R_WWw=6ehNkgc.qx`]H K4F{?VXl(vE_"jZ"BRfuB7a]ZKWF.q: (C<1bP22 LA%O21;g%&sM]MVH+n|zٕ>@Fe\o[v ZJ ol챆CZӬW~5 a2KmxO%Piض-O.a\aaEHU)u.@eY8)d5VRDX\ror"$H`GsG 7Y 2cǮk|jjwd4ѫ,'R\8xSK6#Hfn) F&|: 7fkޫAMm0kq2:n4v=ѥL<952 ]gTɒC@YGrEE&W;&,SeQXRӚzFhmI y d&M%lZBv,Bxگ*fӫ^] 9Is"%\-ˢ%)fSEP9b0vTkgD]'RLu %% ;vD т1S,.a#7U3t٬ | |&~!wg$x1R1E$>T#[OuSmnP'nD`Y@;ח~_aLPNwhW)lKppn0(K]FJ'V_Z '+nFQ.=շvR5i[ spx &ȕ_+]uiI ﷩׺pxqriP/eYTsWm:9 j 4/Qz>j?T SǨRUBzWKc~.*_o?E95%~.O҉wɸ U@Ha(WMJꙚ+q)Ih:}w%ȵڽc+w 2Tb 7YPp'(Q!UÛy;io`riFN>zY.O.`Ehi c>={~VH e[;O>t7C}HF-"6 1/Ziu Dq>"?]2"0D)7JwBhۓ -6U-!ZhI!iM%yiE9q"cSZI݆j,ˀh$e}w}Ԟ$"߭\bGAg6rѠ]ڇ; 0Z,%ݻx#lNLjopSBq{~gGX*P%*ȵ$=AmXs_Ȯ(42Th4ۇRZj88j( IDATES,` \Wa,`;y]rQl5CK@o'ؾAc0fu]=ռ)C#2"j DQzؽ8Y΃M{k?tAF'YҞbmk7D,Ig[ vBD@h]YchmNp} r~iˊMYV5^xCRx-M lb^bAhQ{XIԻ8bŬPa|SpЀV8 \/:[iGpk=MC+qW)W(xDͮYF)-o9 >~^- pH OU.%*4m˭>DnVE5Q 3*CG<.-/2s4d L)g_i]~()s(N(:.bG 6Es4}ߎ4C` BU&bC}~ j>x47:)ѝwjoS*R f_gBe}G kuQ wm͵/U&SEr6޻ _yVv>̡F\墵q.SP,NXt  EpFGYpOT։poϋP?hk~VIƈ0KI[Gi}:f[2֪>3uV>]9eT7|MK@]/"V]:hsaenҵ{7$R,q#q;n.=VLuZuq常F e9(aRD+Үs7r¬r#Iޯ) kCJ ǚyecJ (pAX^/5Z o,"(l7<ӿ۷P*pbqi1fgfq\dޞzzzFJKZXM,ö-Ct2 Y&͒Zc6xfho#|4a*t~L`BS7j@H>sށX[;v"seC{=G>ȑ6Ae Ҡ5rq|=W8.pP RtoJwrD 4KЮZL\ I?eذVn&وUCQW/]޵Xui,ϝ'_ȓL$I&( \t+H)qh4J9EO}L}{<%" m1TԵEZMȑ m!Z+5Us^ A5Vcm<3iVVV%T@OJ)l )w:RJXXX̙3ض۱m,YٹY|-"(}}}ضRT*EWW]J$$Mtwu395ٳgtuu!*@WA~ ^P#*)ArX93l6[R}}}tuuD( ^A{qׯfI&r9.\@kk+=]UnRJ{ٲu+J)\ץAR㰴m`iIZ[XZZի*jahZOT0_ a/R+ZFB BQ4נ(c$,XEXh +$:pR:\73ɹ(o`+}tuwii~ Itˎr~MM"2kS!%#ڠ !H4m㢕"ڀ1W_!K%O8yQl˲0ў@xG%Vt8-D4֭ 6 >J1??O2 Cww7w尿t:ͥKgΝl߾6Ν;Dihmk!1::JWw71r9h@%CtwuS;1@NRCdY(lB7%k ~ Jj٘s>\N:o28{ XnwSSZ:^,+X[,i E.Eǥ;vH$qOsYV݁By\ab|߲޶&G69C;i$_(0riWy.wl-҅s]B4"Ht}FF"b)fS_7(>+G•hKJIGG?>Oغu+,//sYZ3>>NGG6m ٥ĵk 5^{FFc[|>0Թ\u,wZ"l"L&,^q#s?h$ZF9q\:MfeaY)+PDؖ%="M] Ρ;B.--M 1X^0" Ҏj롻;Nz##\ Yv leX6RMI2׮03BN.3,.,ϦCO.C)C:@]ID#Q.cpq6oW\!ϯ{_4fJm5Y \)qm0GE3͹%5v@jPkqbn_^WMui%rIcc. vk6Ls0 $D 1hێbKp YE5!@yd0K)zadАGrp²#Dc1~‹JYMPh[8,.-cǒHi!MH3757i+ONz  N'(aD]4KkWu)B. Rez 9R*L tSDw?p]uLNNNt:M.X,z) c mZuԅͺA*|'߽ƊI>xѩ fXm23:NN#xF(Lj*TɄ6H u6F[,0oDӹ>~>n?{a Iݻ9q6LLMpV|ϐλts9"sY@-ƟvD#$qff&::Bg_;t-JL."""[,zeb>#?I|RZXR0GfeD2ASsZ֒XFq<۶2uoxO1B `h L$._͛ٽ{7---<޽Z[[ի G" ƺ^/|R#+~~~QOoo/sssR)p]7^JN(H$*r^"i8siJ hmU~Y{3|<&m)?>]OކVgN_>|;WAܳҺ#Gk'x9Z [\gٳwen:CI$L{T{7]t4S_x&~_wR 6oO.=]$mg?purwOM m<$ݻi\^ ͝ m5h &hn)C=~c70eKzzy12[:޽[nRm3κӧٶm{Iz-FFFXXX vbd2[޲RJmwOcWwQ$/^ѣttt` uxbCijj7XRD(0`)jV;WRrC\:ʾ{$zysƫ^%Jyh#fr"o| u]o?yݓ }k޾Ē \dŠT&́>_OڠKWǝУDRH({ZYnFҊ;(&\s>צgёws]-o\I453ݟ D,bׁ;%m ev8ĮPiw{xdrOUڃ**Fƺ1)LuVƹv: r SSS<i.\4X` k,/-׷%-[C?d.sUyS*G[[WŲ DL'ظI!;?vX^=DXɓBA5 Yёy\i{6cٳYHl+Rbb 'ʍxX@!p]ޙHeww}KknUY{UwWu={fl3$ IDAT1X˒% '$#dl,|0BBdhLO{z5*ȌȈ\>TUהqSYYE{/9 Deq ' o{JZ v|`phvЁD3"Hs^`˧DS/8yp(q(O1%( ĬbiFrii-F(W![A R?G!ٓz۲y嗸w^5)@J+- z7U!,L\B70(zN]?ݷv{k C|(Z{vTX TNaaT(ڟgꌾosEa}v"{/KEu66YO/?AiٯĒU °s[ _:xzS?|ÍJ"́LOļ4ɜgIC$rGI,/D[d0z-nK"fuZlrb՛/uz*A'o $(!Cwy۶|q=ƲaR9J>E^{5_DZXU(J C kp,,GM2(&!a??TrWx˰;uxS|~=+2W?o>g_~o޻/\> .wXX8|_Ek!/Ϳs?yX*N\!BD^oo>'l:[J^zFoU~"W^ifJOԅҥKay Sm6*m^}UΟ?Tt\kS9E&ޗWW~B'%:C+W1Ű!CHp7ҫL[ nJ?`A#Cm\@܄_*PF+SZ J~?7ׇJm-8f>갳 N|=CFk mmcP}=n~1wV?o/~uR%zZt+sM}j &SOWNO5ئoq>o}-nݺȴJ)  up׮]0U(֚__7yJҡ\,ƶ-l¶l,ia) KI,BIDPmd0h ,=BX_-qE*Z#aFLS Xo;?ĕJP.̸RJ\nu 3W>٣r<5h >f@28x ewz ɍǙq5[Ozε׸r Aڲ,>>?XNT⥗_ףlZCJ)VNty!߸Ç}84-9wN;Ώ$ o3̡C6Y./"[R6$Io}ܓ"X1 QZfg_0)XP2>&jJ*= ɫ j$dv= =BjBhP 4!H3pzRVW>/TD25R# tI ˄fQOJiw>R"lNaG EFxB1iTh Ji@e@xa 2@e0G/@LQ)f|5[BJ)JjUB<ěe"-+$89!& |kq"nfk>ˆts8Fd6pl2Yi؟!aCGL3!dFڹsմOc^xsC>H!H4!IHVT%$M0Q*$90¤ юV֟_Ö^Xl=IO}SO^M+.Kۜt6SLC[L򲱣 0>NOV< ۉKRHFwfQSÁE# _ٔ?36lkVJ%fHd4(ɒFIIHQ{ B@P$H"oo q8% @fץ<{|vI#GD᎜ܻsA&N1K\cֹ~ewdXPJb)뙼R'E ~:Z__0 2JEof>}~&TF6VL0 iK BxCdZ e!J) ȍ41bhӦO 3ooYQCB %&22 s63xIKX6LnE.D&2>N)'͐~Eˑ(RfWi"j QF̢F'yy<@Oh4188n>nbuu360 M(a;7 C,I(dhBzE2;8F~RiϳIRFtaV(9"q -_M"RMrEI ]t] J,BSx[(¸ȉ _HwTKBŴCbFFudr &HQN& 5ʾS2YC9=<*B8ܠOƠ^^q>d{HuA T \D9V (e6'"W-.xS$c^b̵֬[}]ar`7#vCu==Z% gMP /C1cף MZG 2 Ԍv7a(E># #vv}J岋r\w7"epp vCzNT)(AOTBII{),- ?f=+}#-J`4CR*Y ;ۛ0/kvQz?:@#GI|cHe>kVFkxX5?[h )GbOhɄ$r(8$"ok:7QvWy9h}' gYK9ƘY naXiIEi4! !ǢQd!3JId9׎4I-H&quEWJVGoϑy#Oܧ^a)y1hM޿kwn0^wc#i\7h Xֈo} *q3۬E4\ݷT4NstPeqZ.cs^(HosWqƇxD(+t-T84nʩsk.m!kQR>HAwim֔wo!Db9ei[[eiʵ&ˋàU5;X86!nϳ0`}}} ieWlqw6(vC:g Y%-b#Wh~%d ',DjYh B 4iF׈&dN0u۠Tt'8@d~2(|n!L2H IaI1 $RFU;di O]#x9S+~pwxV02fw!7>x@U罏<%6W}۫l?ηr%Bo W\7*?̾qmk9n}LQn1:c97nߤ?booCUŭmoafܻ{N:˔/]o޻gyyyl[qmg9w,-ؤjX9}>ߡ^+NR:)v7*שU+zTZwi>Rm7h6l!r5(OA#̈0=(e"CiG#d%o%K"U")?$2QCM1|Qa֚~/htxwz4Vx?ͻJ3pS㿲K@%^~tx{sw>`m}>NJ l[a[+\Rosowq n Vλm޺ŝ{8Zlnox*VP^V]~p?}D3Ңl pZ?huxJ{w-EqcrK,.KnPr 2 NjA*reYQ %JG&)27ur'SrfSBT&6O{̨$ qU.ʌ?P,(NGbj~A @EpP\S\R* Q$ɨ5y:yR.xIJlUrZlFcT[ wwܿs*P0CG h2 R1y#T vwJ`E\B=@a qNzpnυK+zC,RQihPBSTX>}0Y_c#:ʣӃPSov+\twosY K<|FZ鰻#8T&f`:6NťE*]}ȃU|o:~3`scndT\-a(Lvz% [*Sۣ7uN]YlaY6K4 vY9urL-X9}%%yJ%WpHQo2ı]PUjv)k4Bh4t!kUV0`mwkZr<+gp=.\Lz(񍛿sh }!*?wTh))U*u\[1Cʥ2zn΃vwTtvuP!Ffp\떨:Μ>.{[P+JFCR&_uT:r؊HW1\m&&"L8H8u$t2r\i"E :~DXO1I -YA&!$R h9ޠ4iTY[)dt8IjOp%T$\d=xXcqHA95K5:#шQMVgi^xyjTKs8__jSm@^u,>QJR.RsDp:?0@( /iTp[)spn#2DZFY^|K*t-V*Jm7)jstT.".]ZXT贛81nG|ʹ UlيZ ^hRU-Enz>vBZ`k v5n޸RP: 4[M.j!((dbQڃm+<,-qͷ/ʱj$ xu7%%j>NF5}j)-RJM_l:Kk4DU]\ʱ!֪h-Vɍe/t ;jrpRGhdɡV IE\} PSQ"iD]D3_]Z2$ 4N(M>l=zu]bȺs+dlYGo$1!Qcގ›sE!ќ4O1.jM1$~1c6{/7т4([!hP\eRZdV/h!-ϣb{{d[\ROv4纼p|J9jsM,_YO2 ˲A·H~}'c弲k^)(~˸O=Ow4Z۱ci)\׍uCq_o|\Yal(R7W #yS?2k`S)HDV"IQ\ r]c҈F"dDS{ ^))&qtϗБ}ƠbfyIDAThYT_rJ؟ǩ@QV[y j377Cێ"&lS8+jj?VҤ9>SQ QJh-%+EiQN.Su*kuqǴ& P'wĄ]Qj@T7ibF?D:!&DL(dryѿyʮIyL(eM&d̺8b:B|ǦGEfd9ڬ%ʦ|ȃ2Aܯn"#(n䥾8ΦmQ34O|bCo&&S4fdr> @$ì cđ lOqr, E*ĥsIjRš$9@I0i-*N/)]2kf1n$) s{y 16/Lzҷ ԗJdN*QP%Hj+0e)" 8$L u׳eL\dfw@rԊAG%ۘ$(}TS䙼i@XDisLGiHjRJ0Di"!*iɫH9;q;ɋODH`BSm/ƩîԈ>.WPf>qE7r?")OP Drֳ$\'' #s= -EJi${B *71 h?+˶mLd6b74` 1ޒ#4WAK  Ib߂4b/r O~7^CN a(M.dҹ!CyLD%sܬ6z,-f>.OP/AvT:L,iFэ[1($#onst\jSaD4'?Ï i,zC8yţTʐ Ƅ*!ѤRT|$Eu Yb(ӼnzI<֓4J`ʹ#|RĞT!-"CiPL$Z)lc‰(G"Hj cSxГZsҴf:DgFET!x5Q~1ԋJr 0 B19)@'$YKd$;\O Dɂ\˓t= hql0^'ULf?A-EfD6yLzfxDQ^Yj!u2eJ K15bwH! ` 8ĤxJ4ߖcv~L1BmvvXF,s(40zi2c썦^k A.C Ų?b)i^sChM36ɑBSZ>2LO0OW<'|B?EGEzjD􏔄Q*+5%5:P: 2|󍄧2IjLZfLI ev,E:{H~Їb§11&]Jݯ sjocU裫DGOuL{?y0.?m$:!s' jܷcLU)xޫIENDB`./dash/previews/Preview.h0000644000004100000410000000755713437202764015646 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef PREVIEW_H #define PREVIEW_H #include #include #include #include "unity-shared/Introspectable.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/StaticCairoText.h" namespace nux { class AbstractButton; class AbstractPaintLayer; class Layout; class VLayout; class StaticCairoText; } namespace unity { namespace dash { class ActionButton; namespace previews { class CoverArt; class TabIterator; class PreviewInfoHintWidget; class PreviewContainer; class Preview : public nux::View, public debug::Introspectable { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(Preview, nux::View); Preview(dash::Preview::Ptr preview_model); virtual ~Preview(); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); static previews::Preview::Ptr PreviewForModel(dash::Preview::Ptr model); sigc::signal request_close() const; virtual nux::Area* FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state); virtual nux::Area* KeyNavIteration(nux::KeyNavDirection direction); nux::Property scale; protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw) {} virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) {} virtual void OnActionActivated(ActionButton* button, std::string const& id); virtual void OnNavigateIn(); virtual void OnNavigateInComplete() {} virtual void OnNavigateOut() {} virtual bool AcceptKeyNavFocus() { return false; } virtual void SetupViews() = 0; virtual void UpdateScale(double scale); nux::Layout* BuildGridActionsLayout(dash::Preview::ActionPtrList actions, std::list& buttons); nux::Layout* BuildVerticalActionsLayout(dash::Preview::ActionPtrList actions, std::list& buttons); void UpdateCoverArtImage(CoverArt* cover_art); void SetFirstInTabOrder(nux::InputArea* area); void SetLastInTabOrder(nux::InputArea* area); void SetTabOrder(nux::InputArea* area, int index); void SetTabOrderBefore(nux::InputArea* area, nux::InputArea* after); void SetTabOrderAfter(nux::InputArea* area, nux::InputArea* before); void RemoveFromTabOrder(nux::InputArea* area); protected: dash::Preview::Ptr preview_model_; std::list action_buttons_; TabIterator* tab_iterator_; nux::VLayout* full_data_layout_; nux::ObjectPtr image_; nux::ObjectPtr title_; nux::ObjectPtr subtitle_; nux::ObjectPtr description_; nux::ObjectPtr preview_info_hints_; typedef std::unique_ptr LayerPtr; friend class PreviewContent; // Need to declare this as a pointer to avoid a circular header // dependency issue between Preview.h and PreviewContainer.h nux::ObjectPtr preview_container_; }; } } } #endif // PREVIEW_H ./dash/previews/PreviewRatingsWidget.cpp0000644000004100000410000000707313437202764020666 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include #include #include #include "config.h" #include #include "unity-shared/RatingsButton.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "PreviewRatingsWidget.h" namespace unity { namespace dash { namespace previews { namespace { const RawPixel CHILDREN_SPACE = 3_em; const int RATINGS_SIZE = 18; const int RATINGS_GAP = 2; } NUX_IMPLEMENT_OBJECT_TYPE(PreviewRatingsWidget); PreviewRatingsWidget::PreviewRatingsWidget(NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , scale(1.0) { layout_ = new nux::VLayout(); layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); previews::Style& style = previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_.OnMouseDown(x, y, button_flags, key_flags); }; ratings_ = new RatingsButton(RATINGS_SIZE, RATINGS_GAP); ratings_->SetEditable(false); ratings_->mouse_click.connect(on_mouse_down); ratings_->scale = scale(); layout_->AddView(ratings_); reviews_ = new StaticCairoText("", NUX_TRACKER_LOCATION); reviews_->SetFont(style.user_rating_font()); reviews_->SetScale(scale); reviews_->mouse_click.connect(on_mouse_down); layout_->AddView(reviews_); mouse_click.connect(on_mouse_down); SetLayout(layout_); UpdateScale(scale); scale.changed.connect(sigc::mem_fun(this, &PreviewRatingsWidget::UpdateScale)); } void PreviewRatingsWidget::SetRating(float rating) { ratings_->SetRating(rating); } float PreviewRatingsWidget::GetRating() const { return ratings_->GetRating(); } void PreviewRatingsWidget::SetReviews(int count) { std::stringstream out; out << count; out << " reviews"; reviews_->SetText(out.str()); } void PreviewRatingsWidget::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } void PreviewRatingsWidget::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.PopClippingRectangle(); } std::string PreviewRatingsWidget::GetName() const { return "PreviewRatingsWidget"; } void PreviewRatingsWidget::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()); } void PreviewRatingsWidget::UpdateScale(double scale) { reviews_->SetScale(scale); ratings_->scale = scale; preview_container_.scale = scale; layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); QueueRelayout(); QueueDraw(); } } // namespace previews } // namespace dash } // namespace unity ./dash/previews/GenericPreview.h0000644000004100000410000000352513437202764017132 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef GENERICPREVIEW_H #define GENERICPREVIEW_H #include #include "Preview.h" #include "unity-shared/OverlayScrollView.h" namespace unity { namespace dash { namespace previews { class GenericPreview : public Preview { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(GenericPreview, Preview); GenericPreview(dash::Preview::Ptr preview_model); ~GenericPreview(); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void PreLayoutManagement(); virtual void SetupViews(); virtual void UpdateScale(double scale); nux::HLayout* image_data_layout_; nux::VLayout* preview_info_layout_; ScrollView* preview_info_scroll_; nux::VLayout* preview_data_layout_; nux::Layout* actions_layout_; }; } } } #endif // GENERICPREVIEW_H ./dash/previews/LensDBusTestRunner.h0000644000004100000410000001642613437202764017731 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef LENSDBUSTESTRUNNER_H #define LENSDBUSTESTRUNNER_H #include "DBusTestRunner.h" #include "UnityCore/ScopeProxyInterface.h" namespace unity { namespace dash { namespace previews { class ScopeDBusTestRunner : public DBusTestRunner { public: typedef std::map Hints; ScopeDBusTestRunner(std::string const& dbus_name, std::string const& dbus_path, std::string const& interface_name) : DBusTestRunner(dbus_name, dbus_path, interface_name) , results_(new Results(ModelType::REMOTE)) , results_variant_(NULL) { proxy_->Connect("Changed", sigc::mem_fun(this, &ScopeDBusTestRunner::OnChanged)); results_->end_transaction.connect(sigc::mem_fun(this, &ScopeDBusTestRunner::ResultsModelUpdated)); } virtual ~ScopeDBusTestRunner() {} void OnProxyConnectionChanged() { DBusTestRunner::OnProxyConnectionChanged(); if (proxy_->IsConnected()) { proxy_->Call("InfoRequest"); proxy_->Call("SetViewType", g_variant_new("(u)", ScopeViewType::SCOPE_VIEW)); } } void OnChanged(GVariant* parameters) { glib::String dbus_path; gboolean search_in_global = FALSE; gboolean visible = FALSE; glib::String search_hint; glib::String private_connection_name; glib::String results_model_name; glib::String global_results_model_name; glib::String categories_model_name; glib::String filters_model_name; GVariantIter* hints_iter = NULL; g_variant_get(parameters, "((sbbssssssa{sv}))", &dbus_path, &search_in_global, &visible, &search_hint, &private_connection_name, &results_model_name, &global_results_model_name, &categories_model_name, &filters_model_name, &hints_iter); LOG_DEBUG(logger) << "Scope info changed for " << dbus_name_ << "\n" << " Path: " << dbus_path << "\n" << " SearchInGlobal: " << search_in_global << "\n" << " Visible: " << visible << "\n" << " PrivateConnName: " << private_connection_name << "\n" << " Results: " << results_model_name << "\n" << " GlobalModel: " << global_results_model_name << "\n" << " Categories: " << categories_model_name << "\n" << " Filters: " << filters_model_name << "\n"; if (dbus_path.Str() == dbus_path_) { results_->swarm_name = results_model_name; LOG_DEBUG(logger) << "Received changes for " << dbus_path; } else { LOG_WARNING(logger) << "Paths do not match " << dbus_path_ << " != " << dbus_path; } connected_ = true; connected.emit(connected_); g_variant_iter_free(hints_iter); } void Search(std::string const& search_string) { LOG_DEBUG(logger) << "Searching '" << dbus_name_ << "' for '" << search_string << "'"; if (proxy_ == NULL) { LOG_DEBUG(logger) << "Skipping search. Proxy not initialized. ('" << dbus_name_ << "')"; return; } GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); search_cancellable_.Renew(); proxy_->Call("Search", g_variant_new("(sa{sv})", search_string.c_str(), &b), sigc::mem_fun(this, &ScopeDBusTestRunner::OnSearchFinished), search_cancellable_); g_variant_builder_clear(&b); } void OnSearchFinished(GVariant* parameters) { Hints hints; uint64_t reply_seqnum; reply_seqnum = ExtractModelSeqnum (parameters); if (results_->seqnum < reply_seqnum) { // wait for the end-transaction signal if (results_variant_) g_variant_unref (results_variant_); results_variant_ = g_variant_ref (parameters); return; } glib::Variant dict (parameters); dict.ASVToHints(hints); search_finished.emit(hints); } void ResultsModelUpdated(uint64_t begin_seqnum, uint64_t end_seqnum) { if (results_variant_ != NULL && end_seqnum >= ExtractModelSeqnum (results_variant_)) { glib::Variant dict(results_variant_, glib::StealRef()); Hints hints; dict.ASVToHints(hints); search_finished.emit(hints); results_variant_ = NULL; } } void Preview(std::string const& uri) { LOG_DEBUG(logger) << "Previewing '" << uri << "' on '" << dbus_name_ << "'"; if (!proxy_->IsConnected()) { LOG_DEBUG(logger) << "Skipping preview. Proxy not connected. ('" << dbus_name_ << "')"; return; } preview_cancellable_.Renew(); proxy_->Call("Activate", g_variant_new("(su)", uri.c_str(), UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_RESULT), sigc::mem_fun(this, &ScopeDBusTestRunner::ActivationReply), preview_cancellable_); } void ActivationReply(GVariant* parameters) { glib::String uri; guint32 handled; GVariant* hints_variant; Hints hints; g_variant_get(parameters, "((su@a{sv}))", &uri, &handled, &hints_variant); glib::Variant dict (hints_variant, glib::StealRef()); dict.ASVToHints(hints); LOG_WARNING(logger) << "ActivationReply type: " << handled; if (handled == UNITY_PROTOCOL_HANDLED_TYPE_SHOW_PREVIEW) { auto iter = hints.find("preview"); if (iter != hints.end()) { dash::Preview::Ptr preview(dash::Preview::PreviewForVariant(iter->second)); // would be nice to make parent_scope a shared_ptr, // but that's not really doable from here preview_ready.emit(uri.Str(), preview); return; } LOG_WARNING(logger) << "Unable to deserialize Preview"; } else { } } sigc::signal preview_ready; sigc::signal search_finished; protected: uint64_t ExtractModelSeqnum(GVariant *parameters) { GVariant *dict; guint64 seqnum64; uint64_t seqnum = 0; dict = g_variant_get_child_value (parameters, 0); if (dict) { if (g_variant_lookup (dict, "model-seqnum", "t", &seqnum64)) { seqnum = static_cast (seqnum64); } g_variant_unref (dict); } return seqnum; } glib::Cancellable preview_cancellable_; glib::Cancellable search_cancellable_; Results::Ptr results_; GVariant *results_variant_; }; } // namespace previews } // namespace dash } // namespace unity #endif // LENSDBUSTESTRUNNER_H ./dash/previews/PreviewNavigator.h0000644000004100000410000000447313437202764017513 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef PREVIEWNAVIGATOR_H #define PREVIEWNAVIGATOR_H #include #include #include "unity-shared/PreviewStyle.h" #include "unity-shared/Introspectable.h" namespace unity { class IconTexture; namespace dash { namespace previews { class PreviewNavigator : public debug::Introspectable, public nux::View { NUX_DECLARE_OBJECT_TYPE(PreviewNavigator, nux::View); public: typedef nux::ObjectPtr Ptr; PreviewNavigator(Orientation direction, NUX_FILE_LINE_PROTO); void SetEnabled(bool enabled); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); sigc::signal activated; virtual bool AcceptKeyNavFocus() { return false; } nux::Property scale; private: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw); virtual void TexRecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags); virtual void TexRecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags); void UpdateTexture(); void SetupViews(); void UpdateScale(double scale); private: const Orientation direction_; nux::Layout* layout_; IconTexture* texture_; enum class VisualState { NORMAL, ACTIVE }; VisualState visual_state_; }; } // namespace previews } // namespace dash } // namespace unity #endif // PREVIEWNAVIGATOR_H ./dash/previews/StandaloneErrorPreview.cpp0000644000004100000410000001607713437202764021221 0ustar www-datawww-data/* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #include #include "Nux/Nux.h" #include "Nux/VLayout.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include "Nux/NuxTimerTickSource.h" #include #include #include #include #include #include #include "unity-shared/FontSettings.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" const unity::RawPixel WIDTH(1100); const unity::RawPixel HEIGHT(600); using namespace unity; using namespace unity::dash; static double scale = 1.0; namespace { nux::logging::Logger logger("unity.dash.StandaloneMusicPreview"); } class DummyView : public nux::View { public: DummyView(nux::View* view) : View(NUX_TRACKER_LOCATION) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bg_layer_.reset(new nux::ColorLayer(nux::Color(81, 26, 48), true, rop)); nux::Layout* layout = new nux::VLayout(); layout->SetPadding(10); layout->AddView(view, 1, nux::MINOR_POSITION_CENTER); SetLayout(layout); } virtual ~DummyView() {} // Keyboard navigation bool AcceptKeyNavFocus() { return false; } protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); bg_layer_->SetGeometry(GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, GetGeometry(), bg_layer_.get()); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (!IsFullRedraw()) nux::GetPainter().PushLayer(gfx_engine, GetGeometry(), bg_layer_.get()); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); if (!IsFullRedraw()) nux::GetPainter().PopBackground(); gfx_engine.PopClippingRectangle(); } typedef std::unique_ptr LayerPtr; LayerPtr bg_layer_; }; class TestRunner { public: TestRunner (); ~TestRunner (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); previews::PreviewContainer::Ptr container_; nux::Layout *layout_; unsigned int nav_iter; previews::Navigation nav_direction_; std::string search_string_; bool first_; }; TestRunner::TestRunner () { } TestRunner::~TestRunner () { } void TestRunner::Init () { container_ = new previews::PreviewContainer(NUX_TRACKER_LOCATION); container_->request_close.connect([this]() { exit(0); }); container_->DisableNavButton(previews::Navigation::BOTH); container_->scale = scale; DummyView* dummyView = new DummyView(container_.GetPointer()); layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); layout_->AddView(dummyView, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); nux::GetWindowThread()->SetLayout (layout_); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW( unity_protocol_payment_preview_new())); unity_protocol_preview_set_title( proto_obj, "This Modern Glitch"); unity_protocol_preview_set_subtitle( proto_obj, "The Wombats"); unity_protocol_payment_preview_set_header( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "It seems that you haven't set your preferred metod for Ubuntu One. To add a pereferred method visist Ubuntu One."); unity_protocol_payment_preview_set_purchase_prize( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "10 eur"); unity_protocol_payment_preview_set_purchase_type( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "Digital CD"); unity_protocol_payment_preview_set_preview_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), UNITY_PROTOCOL_PREVIEW_PAYMENT_TYPE_ERROR); // set the diff actions unity_protocol_preview_add_action(proto_obj, "open_u1_link", "Go to Ubuntu One", NULL, 0); unity_protocol_preview_add_action(proto_obj, "cancel", "Cancel", NULL, 0); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::LEFT); } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init (); } int main(int argc, char **argv) { nux::WindowThread* wt = NULL; gtk_init (&argc, &argv); nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; unity::ThumbnailGenerator thumbnail_generator; unity::glib::Error err; GOptionEntry args_parsed[] = { { "scaling-factor", 's', 0, G_OPTION_ARG_DOUBLE, &scale, "The dash scaling factor", "F" }, { NULL } }; std::shared_ptr ctx(g_option_context_new("Unity Preview"), g_option_context_free); g_option_context_add_main_entries(ctx.get(), args_parsed, NULL); if (!g_option_context_parse(ctx.get(), &argc, &argv, &err)) std::cerr << "Got error when parsing arguments: " << err << std::endl; TestRunner *test_runner = new TestRunner (); wt = nux::CreateGUIThread(TEXT("Unity Preview"), WIDTH.CP(scale), HEIGHT.CP(scale), 0, &TestRunner::InitWindowThread, test_runner); nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); wt->Run (NULL); delete wt; return 0; } ./dash/previews/MusicPreview.cpp0000644000004100000410000002766313437202764017202 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/CoverArt.h" #include "unity-shared/IconTexture.h" #include #include #include #include #include #include "MusicPreview.h" #include "ActionButton.h" #include "Tracks.h" #include "PreviewInfoHintWidget.h" #include "PreviewPlayer.h" namespace unity { namespace dash { namespace previews { namespace { const RawPixel CHILDREN_SPACE = 16_em; const RawPixel ICON_LEFT_RIGHT_PADDING = 10_em; const RawPixel WARNING_MIN_HEIGHT = 50_em; const RawPixel WARNING_MAX_WIDTH = 300_em; } DECLARE_LOGGER(logger, "unity.dash.preview.music"); NUX_IMPLEMENT_OBJECT_TYPE(MusicPreview); MusicPreview::MusicPreview(dash::Preview::Ptr preview_model) : Preview(preview_model) , actions_layout_(nullptr) , image_data_layout_(nullptr) , icon_layout_(nullptr) { SetupViews(); UpdateScale(scale); } MusicPreview::~MusicPreview() { } void MusicPreview::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); gfx_engine.PopClippingRectangle(); } void MusicPreview::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } std::string MusicPreview::GetName() const { return "MusicPreview"; } void MusicPreview::AddProperties(debug::IntrospectionData& introspection) { Preview::AddProperties(introspection); } bool MusicPreview::HasUbuntuOneCredentials() { dash::Preview::InfoHintPtrList hints = preview_model_->GetInfoHints(); GVariant *preview_data = NULL; for (dash::Preview::InfoHintPtr const& info_hint : hints) { if (info_hint->id == "music_preview") { preview_data = info_hint->value; if (preview_data != NULL) { glib::Variant data(g_variant_lookup_value(preview_data, "no_credentials_label", G_VARIANT_TYPE_STRING)); if (!data) no_credentials_message_ = ""; else no_credentials_message_ = data.GetString(); } break; } } return no_credentials_message_.empty(); } void MusicPreview::SetupViews() { dash::MusicPreview* music_preview_model = dynamic_cast(preview_model_.get()); if (!music_preview_model) { LOG_ERROR(logger) << "Could not derive music preview model from given parameter."; return; } previews::Style& style = dash::previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_->OnMouseDown(x, y, button_flags, key_flags); }; image_data_layout_ = new nux::HLayout(); image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); ///////////////////// // Image image_ = new CoverArt(); image_->scale = scale(); AddChild(image_.GetPointer()); UpdateCoverArtImage(image_.GetPointer()); ///////////////////// ///////////////////// // App Data Panel full_data_layout_ = new nux::VLayout(); full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); ///////////////////// // Music Info album_data_layout_ = new nux::VLayout(); album_data_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); title_ = new StaticCairoText(preview_model_->title, true, NUX_TRACKER_LOCATION); AddChild(title_.GetPointer()); title_->SetFont(style.title_font().c_str()); title_->SetLines(-1); title_->SetScale(scale); title_->mouse_click.connect(on_mouse_down); album_data_layout_->AddView(title_.GetPointer(), 1); if (!preview_model_->subtitle.Get().empty()) { subtitle_ = new StaticCairoText(preview_model_->subtitle, true, NUX_TRACKER_LOCATION); AddChild(subtitle_.GetPointer()); subtitle_->SetFont(style.subtitle_size_font().c_str()); subtitle_->SetLines(-1); subtitle_->SetScale(scale); subtitle_->mouse_click.connect(on_mouse_down); album_data_layout_->AddView(subtitle_.GetPointer(), 1); } ///////////////////// ///////////////////// // Music Tracks dash::Tracks::Ptr tracks_model = music_preview_model->GetTracksModel(); if (tracks_model) { tracks_ = new previews::Tracks(tracks_model, NUX_TRACKER_LOCATION); tracks_->scale = scale(); AddChild(tracks_.GetPointer()); tracks_->mouse_click.connect(on_mouse_down); } ///////////////////// nux::HLayout* hint_actions_layout = new nux::HLayout(); ///////////////////// // Hints && Actions nux::VLayout* hints_layout = NULL; actions_layout_ = NULL; bool has_u1_creds = HasUbuntuOneCredentials(); if (has_u1_creds) { if (!preview_model_->GetInfoHints().empty()) { hints_layout = new nux::VLayout(); hints_layout->SetSpaceBetweenChildren(0); hints_layout->AddSpace(0, 1); preview_info_hints_ = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth().CP(scale)); preview_info_hints_->scale = scale(); AddChild(preview_info_hints_.GetPointer()); preview_info_hints_->request_close().connect([this]() { preview_container_->request_close.emit(); }); hints_layout->AddView(preview_info_hints_.GetPointer(), 0); // If there are actions, we use a vertical layout action_buttons_.clear(); actions_layout_ = BuildVerticalActionsLayout(preview_model_->GetActions(), action_buttons_); actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); } else // otherwise we add a grid layout. { action_buttons_.clear(); actions_layout_ = BuildGridActionsLayout(preview_model_->GetActions(), action_buttons_); if (action_buttons_.size() < 2) hint_actions_layout->AddSpace(0, 1); actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); } } else { // let the user know he needs to connect previews::Style& style = dash::previews::Style::Instance(); nux::HLayout* actions_layout = new nux::HLayout(); icon_layout_ = new nux::VLayout(); icon_layout_->SetLeftAndRightPadding(ICON_LEFT_RIGHT_PADDING.CP(scale)); warning_texture_ = new IconTexture(style.GetWarningIcon()); icon_layout_->AddView(warning_texture_.GetPointer(), 0, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL, 100.0f, nux::NUX_LAYOUT_BEGIN); actions_layout->AddLayout(icon_layout_, 0, nux::MINOR_POSITION_CENTER); warning_msg_ = new StaticCairoText( no_credentials_message_, true, NUX_TRACKER_LOCATION); AddChild(warning_msg_.GetPointer()); warning_msg_->SetFont(style.u1_warning_font().c_str()); warning_msg_->SetLines(-2); warning_msg_->SetScale(scale); warning_msg_->SetMinimumHeight(WARNING_MIN_HEIGHT.CP(scale)); warning_msg_->SetMaximumWidth(WARNING_MAX_WIDTH.CP(scale)); actions_layout->AddView(warning_msg_.GetPointer(), 0, nux::MINOR_POSITION_CENTER); } ///////////////////// if (hints_layout) hint_actions_layout->AddView(hints_layout, 1); hint_actions_layout->AddView(actions_layout_, 0); full_data_layout_->AddLayout(album_data_layout_, 0); if (tracks_) { full_data_layout_->AddView(tracks_.GetPointer(), 1); } full_data_layout_->AddLayout(hint_actions_layout, 0); ///////////////////// image_data_layout_->AddView(image_.GetPointer(), 0); image_data_layout_->AddLayout(full_data_layout_, 1); mouse_click.connect(on_mouse_down); SetLayout(image_data_layout_); } void MusicPreview::PreLayoutManagement() { nux::Geometry geo = GetGeometry(); GetLayout()->SetGeometry(geo); previews::Style& style = dash::previews::Style::Instance(); nux::Geometry geo_art(geo.x, geo.y, style.GetAppImageAspectRatio() * geo.height, geo.height); int content_width = geo.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale); if (content_width - geo_art.width < style.GetDetailsPanelMinimumWidth().CP(scale)) geo_art.width = MAX(0, content_width - style.GetDetailsPanelMinimumWidth().CP(scale)); image_->SetMinMaxSize(geo_art.width, geo_art.height); int details_width = MAX(0, content_width - geo_art.width); if (title_) { title_->SetMaximumWidth(details_width); } if (subtitle_) { subtitle_->SetMaximumWidth(details_width); } for (nux::AbstractButton* button : action_buttons_) { int action_width = CLAMP((details_width - style.GetSpaceBetweenActions().CP(scale)) / 2, 0, style.GetActionButtonMaximumWidth().CP(scale)); // do not use SetMinMax because width has to be able to grow button->SetMinimumWidth(action_width); button->SetMinimumHeight(style.GetActionButtonHeight().CP(scale)); button->SetMaximumHeight(style.GetActionButtonHeight().CP(scale)); } Preview::PreLayoutManagement(); } void MusicPreview::OnNavigateOut() { PreviewPlayer player; player.Stop(); } void MusicPreview::UpdateScale(double scale) { Preview::UpdateScale(scale); if (tracks_) tracks_->scale = scale; if (preview_info_hints_) preview_info_hints_->scale = scale; if (icon_layout_) icon_layout_->SetLeftAndRightPadding(ICON_LEFT_RIGHT_PADDING.CP(scale)); if (warning_msg_) { warning_msg_->SetScale(scale); warning_msg_->SetMinimumHeight(WARNING_MIN_HEIGHT.CP(scale)); warning_msg_->SetMaximumWidth(WARNING_MAX_WIDTH.CP(scale)); } previews::Style& style = dash::previews::Style::Instance(); if (image_data_layout_) image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); if (full_data_layout_) { full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); } if (album_data_layout_) album_data_layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle().CP(scale)); if (actions_layout_) actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); if (icon_layout_) icon_layout_->SetLeftAndRightPadding(ICON_LEFT_RIGHT_PADDING.CP(scale)); } } // namespace previews } // namespace dash } // namespace unity ./dash/previews/StandaloneMusicPaymentPreview.cpp0000644000004100000410000001670213437202764022541 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #include #include "Nux/Nux.h" #include "Nux/VLayout.h" #include "Nux/NuxTimerTickSource.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include #include #include #include #include #include #include "unity-shared/FontSettings.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" const unity::RawPixel WIDTH(1100); const unity::RawPixel HEIGHT(600); using namespace unity; using namespace unity::dash; namespace { static double scale = 1.0; nux::logging::Logger logger("unity.dash.StandaloneMusicPreview"); } class DummyView : public nux::View { public: DummyView(nux::View* view) : View(NUX_TRACKER_LOCATION) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bg_layer_.reset(new nux::ColorLayer(nux::Color(81, 26, 48), true, rop)); nux::Layout* layout = new nux::VLayout(); layout->SetPadding(10); layout->AddView(view, 1, nux::MINOR_POSITION_CENTER); SetLayout(layout); } virtual ~DummyView() {} // Keyboard navigation bool AcceptKeyNavFocus() { return false; } protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); bg_layer_->SetGeometry(GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, GetGeometry(), bg_layer_.get()); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (!IsFullRedraw()) nux::GetPainter().PushLayer(gfx_engine, GetGeometry(), bg_layer_.get()); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); if (!IsFullRedraw()) nux::GetPainter().PopBackground(); gfx_engine.PopClippingRectangle(); } typedef std::unique_ptr LayerPtr; LayerPtr bg_layer_; }; class TestRunner { public: TestRunner (); ~TestRunner (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); previews::PreviewContainer::Ptr container_; nux::Layout *layout_; unsigned int nav_iter; previews::Navigation nav_direction_; std::string search_string_; bool first_; }; TestRunner::TestRunner () { } TestRunner::~TestRunner () { } void TestRunner::Init () { container_ = new previews::PreviewContainer(NUX_TRACKER_LOCATION); container_->request_close.connect([this]() { exit(0); }); container_->DisableNavButton(previews::Navigation::BOTH); DummyView* dummyView = new DummyView(container_.GetPointer()); layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); layout_->AddView(dummyView, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); nux::GetWindowThread()->SetLayout (layout_); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW( unity_protocol_payment_preview_new())); unity_protocol_preview_set_title( proto_obj.RawPtr(), "This Modern Glitch"); unity_protocol_preview_set_subtitle( proto_obj.RawPtr(), "The Wombats"); unity_protocol_payment_preview_set_header( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "Hi mandel, you purchased in the past from Ubuntu One, would you like to use the same payment details? Please review your order:"); unity_protocol_payment_preview_set_email( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "manuel@canonical.com"); unity_protocol_payment_preview_set_payment_method( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "***** *** **** 246"); unity_protocol_payment_preview_set_purchase_prize( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "10 eur"); unity_protocol_payment_preview_set_purchase_type( UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), "Digital CD"); // set the diff actions unity_protocol_preview_add_action(proto_obj, "purchase_album", "Buy Now", NULL, 0); unity_protocol_preview_add_action(proto_obj, "cancel_purchase", "Cancel", NULL, 0); unity_protocol_preview_add_action(proto_obj, "forgot_password", "forgotten your Ubuntu One password?", NULL, 0); unity_protocol_preview_add_action(proto_obj, "change_payment_method", "change payment method", NULL, 0); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::LEFT); container_->scale = scale; } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init (); } int main(int argc, char **argv) { nux::WindowThread* wt = NULL; gtk_init (&argc, &argv); nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; unity::ThumbnailGenerator thumbnail_generator; unity::glib::Error err; GOptionEntry args_parsed[] = { { "scaling-factor", 's', 0, G_OPTION_ARG_DOUBLE, &scale, "The dash scaling factor", "F" }, { NULL } }; std::shared_ptr ctx(g_option_context_new("Unity Preview"), g_option_context_free); g_option_context_add_main_entries(ctx.get(), args_parsed, NULL); if (!g_option_context_parse(ctx.get(), &argc, &argv, &err)) std::cerr << "Got error when parsing arguments: " << err << std::endl; TestRunner *test_runner = new TestRunner (); wt = nux::CreateGUIThread(TEXT("Unity Preview"), WIDTH.CP(scale), HEIGHT.CP(scale), 0, &TestRunner::InitWindowThread, test_runner); nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); wt->Run (NULL); delete wt; return 0; } ./dash/previews/StandaloneApplicationPreview.cpp0000644000004100000410000003634313437202764022371 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "config.h" #include "Nux/Nux.h" #include "Nux/NuxTimerTickSource.h" #include "Nux/VLayout.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include #include #include #include #include #include #include "unity-shared/FontSettings.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" #include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" const unity::RawPixel WIDTH(1000); const unity::RawPixel HEIGHT(600); using namespace unity; using namespace unity::dash; static double scale = 1.0; class DummyView : public nux::View { public: DummyView(nux::View* view) : View(NUX_TRACKER_LOCATION) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bg_layer_.reset(new nux::ColorLayer(nux::Color(81, 26, 48), true, rop)); nux::Layout* layout = new nux::VLayout(); layout->SetPadding(16); layout->AddView(view, 1, nux::MINOR_POSITION_CENTER); SetLayout(layout); } virtual ~DummyView() {} // Keyboard navigation bool AcceptKeyNavFocus() { return false; } protected: virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); bg_layer_->SetGeometry(GetGeometry()); nux::GetPainter().RenderSinglePaintLayer(gfx_engine, GetGeometry(), bg_layer_.get()); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); if (!IsFullRedraw()) nux::GetPainter().PushLayer(gfx_engine, GetGeometry(), bg_layer_.get()); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); if (!IsFullRedraw()) nux::GetPainter().PopBackground(); gfx_engine.PopClippingRectangle(); } typedef std::unique_ptr LayerPtr; LayerPtr bg_layer_; }; class TestRunner { public: TestRunner (); ~TestRunner (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); void NavRight(); void NavLeft(); previews::PreviewContainer::Ptr container_; nux::Layout *layout_; int nav_iter; glib::Source::UniquePtr preview_wait_timer_; }; TestRunner::TestRunner () { nav_iter = 0; } TestRunner::~TestRunner () { } void TestRunner::Init () { container_ = new previews::PreviewContainer(NUX_TRACKER_LOCATION); container_->navigate_right.connect(sigc::mem_fun(this, &TestRunner::NavRight)); container_->navigate_left.connect(sigc::mem_fun(this, &TestRunner::NavLeft)); container_->request_close.connect([this]() { exit(0); }); container_->scale = scale; DummyView* dummyView = new DummyView(container_.GetPointer()); layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); layout_->AddView(dummyView, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); nux::GetWindowThread()->SetLayout (layout_); std::stringstream app_name; app_name << "Skype"; const char* subtitle = "Version 3.2, Size 32 MB"; std::stringstream description; for (int i = 0; i < 700; i++) description << "Application description " << i << std::endl; // creates a generic preview object glib::Object iconHint1(g_icon_new_for_string(PKGDATADIR"/lens-nav-music.svg", NULL)); glib::Object iconHint2(g_icon_new_for_string(PKGDATADIR"/lens-nav-home.svg", NULL)); glib::Object iconHint3(g_icon_new_for_string(PKGDATADIR"/lens-nav-people.svg", NULL)); GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£30.99")); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string(PKGDATADIR "/launcher_bfb.png", NULL)); unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Proprietary"); unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "(c) Skype 2012"); unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); unity_protocol_application_preview_set_rating(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 0.5); unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 17); unity_protocol_preview_set_image_source_uri(proto_obj, "file://" PKGDATADIR "/launcher_bfb.png"); unity_protocol_preview_set_title(proto_obj, app_name.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description.str().c_str()); unity_protocol_preview_add_action(proto_obj, "uninstall", "Uninstall", iconHint1, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "launch", "Download", iconHint2, 0, action_hints1); unity_protocol_preview_add_info_hint(proto_obj, "time", "Total time", iconHint1, g_variant_new("s", "16 h 34miin 45sec")); unity_protocol_preview_add_info_hint(proto_obj, "energy", "Energy", iconHint2, g_variant_new("s", "58.07 mWh")); unity_protocol_preview_add_info_hint(proto_obj, "load", "CPU Load", iconHint3, g_variant_new("i", 12)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::RIGHT); g_hash_table_unref(action_hints1); } void TestRunner::NavRight() { std::stringstream app_name; app_name << "Title " << ++nav_iter; const char* subtitle = "Version 3.2, Size 32 MB"; const char* description = "Skype is a proprietary voice-over-Internet Protocol service and software application originally created by Niklas Zennström and Janus Friis in 2003, and owned by Microsoft since 2011. \ The service allows users to communicate with peers by voice, video, and instant messaging over the Internet. Phone calls may be placed to recipients on the traditional telephone networks. Calls to other users within the Skype service are free of charge, while calls to landline telephones and mobile phones are charged via a debit-based user account system."; // creates a generic preview object glib::Object iconHint1(g_icon_new_for_string(PKGDATADIR"/lens-nav-music.svg", NULL)); glib::Object iconHint2(g_icon_new_for_string(PKGDATADIR"/lens-nav-home.svg", NULL)); glib::Object iconHint3(g_icon_new_for_string(PKGDATADIR"/lens-nav-people.svg", NULL)); glib::Object iconHint4(g_icon_new_for_string(PKGDATADIR"/lens-nav-people.svg", NULL)); GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£30.99")); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string(PKGDATADIR "/launcher_bfb.png", NULL)); unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Proprietary"); unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "(c) Skype 2012"); unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); unity_protocol_application_preview_set_rating(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 0.25); unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 5); unity_protocol_preview_set_image_source_uri(proto_obj, "file://" PKGDATADIR "/launcher_bfb.png"); unity_protocol_preview_set_title(proto_obj, app_name.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); unity_protocol_preview_add_action(proto_obj, "uninstall", "Uninstall", iconHint1, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "launch", "Download", iconHint2, 0, action_hints1); unity_protocol_preview_add_info_hint(proto_obj, "time", "Total time", iconHint1, g_variant_new("s", "16 h 34miin 45sec")); unity_protocol_preview_add_info_hint(proto_obj, "energy", "Energy", iconHint2, g_variant_new("s", "58.07 mWh")); unity_protocol_preview_add_info_hint(proto_obj, "load", "CPU Load", iconHint3, g_variant_new("d", 12.1)); if (nav_iter % 2 == 0) unity_protocol_preview_add_info_hint(proto_obj, "desc", "Very long long description", iconHint4, g_variant_new("s", "So looong description that does not fit into")); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::RIGHT); g_hash_table_unref(action_hints1); } void TestRunner::NavLeft() { preview_wait_timer_.reset(new glib::Timeout(2000, [this] () { std::stringstream app_name; app_name << "Title " << --nav_iter; const char* subtitle = "Version 3.2, Size 32 MB"; const char* description = "Skype is a proprietary voice-over-Internet Protocol service and software application originally created by Niklas Zennström and Janus Friis in 2003, and owned by Microsoft since 2011. \ The service allows users to communicate with peers by voice, video, and instant messaging over the Internet. Phone calls may be placed to recipients on the traditional telephone networks. Calls to other users within the Skype service are free of charge, while calls to landline telephones and mobile phones are charged via a debit-based user account system."; // creates a generic preview object glib::Object iconHint1(g_icon_new_for_string(PKGDATADIR"/lens-nav-music.svg", NULL)); glib::Object iconHint2(g_icon_new_for_string(PKGDATADIR"/lens-nav-home.svg", NULL)); glib::Object iconHint3(g_icon_new_for_string(PKGDATADIR"/lens-nav-people.svg", NULL)); GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£30.99")); glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string(PKGDATADIR"/launcher_bfb.png", NULL)); unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Proprietary"); unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "(c) Skype 2012"); unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); unity_protocol_application_preview_set_rating(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 0.8); unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 1223); unity_protocol_preview_set_image_source_uri(proto_obj, "file://" PKGDATADIR "/launcher_bfb.png"); unity_protocol_preview_set_title(proto_obj, app_name.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); unity_protocol_preview_add_action(proto_obj, "uninstall", "Uninstall", iconHint1, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "launch", "Download", iconHint2, 0, action_hints1); unity_protocol_preview_add_info_hint(proto_obj, "time", "Total time", iconHint1, g_variant_new("s", "16 h 34miin 45sec")); unity_protocol_preview_add_info_hint(proto_obj, "energy", "Energy", iconHint2, g_variant_new("s", "58.07 mWh")); unity_protocol_preview_add_info_hint(proto_obj, "load", "CPU Load", iconHint3, g_variant_new("i", 22)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); container_->Preview(preview_model, previews::Navigation::LEFT); g_hash_table_unref(action_hints1); return false; })); } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init (); } int main(int argc, char **argv) { nux::WindowThread* wt = NULL; gtk_init (&argc, &argv); nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; unity::ThumbnailGenerator thumbnail_generator; unity::glib::Error err; GOptionEntry args_parsed[] = { { "scaling-factor", 's', 0, G_OPTION_ARG_DOUBLE, &scale, "The dash scaling factor", "F" }, { NULL } }; std::shared_ptr ctx(g_option_context_new("Unity Preview"), g_option_context_free); g_option_context_add_main_entries(ctx.get(), args_parsed, NULL); if (!g_option_context_parse(ctx.get(), &argc, &argv, &err)) std::cerr << "Got error when parsing arguments: " << err << std::endl; TestRunner *test_runner = new TestRunner (); wt = nux::CreateGUIThread(TEXT("Unity Preview"), WIDTH.CP(scale), HEIGHT.CP(scale), 0, &TestRunner::InitWindowThread, test_runner); nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); wt->Run (NULL); delete wt; return 0; } ./dash/previews/DBusTestRunner.h0000644000004100000410000000407513437202764017104 0ustar www-datawww-data/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef DBUSTESTRUNNER_H #define DBUSTESTRUNNER_H #include #include #include #include namespace unity { namespace dash { namespace previews { DECLARE_LOGGER(logger, "unity.dash.dbus.testrunner"); class DBusTestRunner { public: typedef std::map Hints; DBusTestRunner(std::string const& dbus_name, std::string const& dbus_path, std::string const& interface_name) : proxy_(nullptr) , connected_(false) , dbus_name_(dbus_name) , dbus_path_(dbus_path) { proxy_ = new glib::DBusProxy(dbus_name, dbus_path, interface_name); proxy_->connected.connect(sigc::mem_fun(this, &DBusTestRunner::OnProxyConnectionChanged)); proxy_->disconnected.connect(sigc::mem_fun(this, &DBusTestRunner::OnProxyDisconnected)); } virtual ~DBusTestRunner() {} virtual void OnProxyConnectionChanged() { LOG_DEBUG(logger) << "Dbus connection changed. connected=" << (proxy_->IsConnected() ? "true" : "false"); } virtual void OnProxyDisconnected() { LOG_DEBUG(logger) << "Dbus disconnected"; } sigc::signal connected; glib::DBusProxy* proxy_; bool connected_; std::string dbus_name_; std::string dbus_path_; }; } // namespace previews } // namespace dash } // namespace unity #endif // DBUSTESTRUNNER_H ./dash/previews/ErrorPreview.h0000644000004100000410000000640613437202764016650 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Diego Sarmentero * Manuel de la Pena * */ #ifndef ERROR_PREVIEW_H #define ERROR_PREVIEW_H // key used to find the correct info hint #define ERROR_INFOHINT_ID "error_preview" // keys of the data preview // Necessary?? #define DATA_MESSAGE_KEY "message" // ations ids #define OPEN_U1_LINK_ACTION "open_u1_link" #include #include #include "ActionButton.h" #include "ActionLink.h" #include "PaymentPreview.h" #include "unity-shared/IconTexture.h" #include "unity-shared/TextInput.h" namespace nux { class AbstractPaintLayer; class StaticCairoText; class VLayout; } namespace unity { namespace dash { namespace previews { class CoverArt; class PreviewInfoHintWidget; class ErrorPreview : public PaymentPreview { public: typedef nux::ObjectPtr Ptr; NUX_DECLARE_OBJECT_TYPE(ErrorPreview, Preview) ErrorPreview(dash::Preview::Ptr preview_model); ~ErrorPreview(); nux::Area* FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); nux::Layout* GetTitle(); nux::Layout* GetPrice(); nux::Layout* GetBody(); nux::Layout* GetFooter(); private: void LoadActions(); protected: // ids for the diff actions to have static const std::string CANCEL_ACTION; static const std::string GO_TO_U1_ACTION; void OnActionActivated(ActionButton* button, std::string const& id); void OnActionLinkActivated(ActionLink* link, std::string const& id); void PreLayoutManagement(); virtual void SetupViews(); virtual void UpdateScale(double scale); // content elements nux::ObjectPtr image_; nux::ObjectPtr intro_; nux::ObjectPtr title_; nux::ObjectPtr subtitle_; nux::ObjectPtr purchase_hint_; nux::ObjectPtr purchase_prize_; nux::ObjectPtr purchase_type_; dash::PaymentPreview* error_preview_model_; // do we want to type? bool entry_selected_; // actions std::map> buttons_map_; // warning texture nux::ObjectPtr warning_texture_; typedef std::unique_ptr LayerPtr; LayerPtr details_bg_layer_; }; } } } #endif // ERROR_PREVIEW_H ./dash/previews/MoviePreview.cpp0000644000004100000410000002417713437202764017176 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include "unity-shared/IntrospectableWrappers.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/CoverArt.h" #include #include #include #include #include #include #include "MoviePreview.h" #include "PreviewInfoHintWidget.h" #include "PreviewRatingsWidget.h" namespace unity { namespace dash { namespace previews { namespace { const RawPixel CHILDREN_SPACE = 16_em; const RawPixel PREVIEW_INFO_CHILDREN_SPACE = 12_em; } DECLARE_LOGGER(logger, "unity.dash.preview.movie"); NUX_IMPLEMENT_OBJECT_TYPE(MoviePreview); MoviePreview::MoviePreview(dash::Preview::Ptr preview_model) : Preview(preview_model) , image_data_layout_(nullptr) , preview_info_layout_(nullptr) , preview_info_scroll_(nullptr) , actions_layout_(nullptr) { SetupViews(); } MoviePreview::~MoviePreview() { } std::string MoviePreview::GetName() const { return "MoviePreview"; } void MoviePreview::AddProperties(debug::IntrospectionData& introspection) { Preview::AddProperties(introspection); } void MoviePreview::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); nux::GetPainter().PaintBackground(gfx_engine, base); gfx_engine.PopClippingRectangle(); } void MoviePreview::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); gfx_engine.PushClippingRectangle(base); unsigned int alpha, src, dest = 0; gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); if (GetCompositionLayout()) GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } void MoviePreview::OnNavigateInComplete() { } void MoviePreview::OnNavigateOut() { } void MoviePreview::SetupViews() { dash::MoviePreview* movie_preview_model = dynamic_cast(preview_model_.get()); if (!movie_preview_model) { LOG_ERROR(logger) << "Could not derive movie preview model from given parameter."; return; } previews::Style& style = dash::previews::Style::Instance(); auto on_mouse_down = [this](int x, int y, unsigned long button_flags, unsigned long key_flags) { this->preview_container_->OnMouseDown(x, y, button_flags, key_flags); }; image_data_layout_ = new nux::HLayout(); image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); ///////////////////// // Image image_ = new CoverArt(); AddChild(image_.GetPointer()); UpdateCoverArtImage(image_.GetPointer()); ///////////////////// ///////////////////// // Data Panel full_data_layout_ = new nux::VLayout(); full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); ///////////////////// // Data nux::VLayout* app_data_layout = new nux::VLayout(); app_data_layout->SetSpaceBetweenChildren(style.GetSpaceBetweenTitleAndSubtitle()); title_ = new StaticCairoText(preview_model_->title, true, NUX_TRACKER_LOCATION); AddChild(title_.GetPointer()); title_->SetLines(-1); title_->SetScale(scale); title_->SetFont(style.title_font().c_str()); title_->mouse_click.connect(on_mouse_down); app_data_layout->AddView(title_.GetPointer(), 1); if (!preview_model_->subtitle.Get().empty()) { subtitle_ = new StaticCairoText(preview_model_->subtitle, true, NUX_TRACKER_LOCATION); AddChild(subtitle_.GetPointer()); subtitle_->SetLines(-1); subtitle_->SetScale(scale); subtitle_->SetFont(style.subtitle_size_font().c_str()); subtitle_->mouse_click.connect(on_mouse_down); app_data_layout->AddView(subtitle_.GetPointer(), 1); } ///////////////////// if (movie_preview_model->rating >= 0) { rating_ = new PreviewRatingsWidget(); AddChild(rating_.GetPointer()); rating_->SetMaximumHeight(style.GetRatingWidgetHeight().CP(scale)); rating_->SetMinimumHeight(style.GetRatingWidgetHeight().CP(scale)); rating_->SetRating(movie_preview_model->rating); rating_->SetReviews(movie_preview_model->num_ratings); rating_->request_close().connect([this]() { preview_container_->request_close.emit(); }); } ///////////////////// // Description auto* preview_info = new ScrollView(NUX_TRACKER_LOCATION); preview_info_scroll_ = preview_info; preview_info->scale = scale(); preview_info->EnableHorizontalScrollBar(false); preview_info->mouse_click.connect(on_mouse_down); preview_info_layout_ = new nux::VLayout(); preview_info_layout_->SetSpaceBetweenChildren(PREVIEW_INFO_CHILDREN_SPACE.CP(scale)); preview_info->SetLayout(preview_info_layout_); if (!preview_model_->GetInfoHints().empty()) { preview_info_hints_ = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth()); preview_info_hints_->scale = scale(); AddChild(preview_info_hints_.GetPointer()); preview_info_hints_->request_close().connect([this]() { preview_container_->request_close.emit(); }); preview_info_layout_->AddView(preview_info_hints_.GetPointer(), 0); } if (!preview_model_->description.Get().empty()) { description_ = new StaticCairoText(preview_model_->description, false, NUX_TRACKER_LOCATION); // not escaped! AddChild(description_.GetPointer()); description_->SetFont(style.description_font().c_str()); description_->SetScale(scale); description_->SetTextAlignment(StaticCairoText::NUX_ALIGN_TOP); description_->SetLines(-style.GetDescriptionLineCount()); description_->SetLineSpacing(style.GetDescriptionLineSpacing()); description_->mouse_click.connect(on_mouse_down); preview_info_layout_->AddView(description_.GetPointer()); } ///////////////////// ///////////////////// // Actions action_buttons_.clear(); actions_layout_ = BuildGridActionsLayout(preview_model_->GetActions(), action_buttons_); actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); /////////////////// full_data_layout_->AddLayout(app_data_layout, 0); if (rating_ != NULL) full_data_layout_->AddView(rating_.GetPointer(), 0); full_data_layout_->AddView(preview_info, 1); full_data_layout_->AddView(actions_layout_, 0); ///////////////////// image_data_layout_->AddView(image_.GetPointer(), 0); image_data_layout_->AddLayout(full_data_layout_, 1); mouse_click.connect(on_mouse_down); SetLayout(image_data_layout_); } void MoviePreview::PreLayoutManagement() { nux::Geometry geo = GetGeometry(); previews::Style& style = dash::previews::Style::Instance(); nux::Geometry geo_art(geo.x, geo.y, style.GetVideoImageAspectRatio() * geo.height, geo.height); int content_width = geo.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale); if (content_width - geo_art.width < style.GetDetailsPanelMinimumWidth().CP(scale)) geo_art.width = std::max(0, content_width - style.GetDetailsPanelMinimumWidth().CP(scale)); image_->SetMinMaxSize(geo_art.width, geo_art.height); int details_width = std::max(0, geo.width - geo_art.width - style.GetPanelSplitWidth().CP(scale) - style.GetDetailsLeftMargin().CP(scale) - style.GetDetailsRightMargin().CP(scale)); if (title_) { title_->SetMaximumWidth(details_width); } if (subtitle_) { subtitle_->SetMaximumWidth(details_width); } if (description_) { description_->SetMaximumWidth(details_width); } for (nux::AbstractButton* button : action_buttons_) { button->SetMinMaxSize(CLAMP((details_width - style.GetSpaceBetweenActions().CP(scale)) / 2, 0, style.GetActionButtonMaximumWidth().CP(scale)), style.GetActionButtonHeight().CP(scale)); } Preview::PreLayoutManagement(); } void MoviePreview::UpdateScale(double scale) { Preview::UpdateScale(scale); if (image_) image_->scale = scale; if (preview_info_hints_) preview_info_hints_->scale = scale; previews::Style& style = dash::previews::Style::Instance(); if (full_data_layout_) { full_data_layout_->SetPadding(style.GetDetailsTopMargin().CP(scale), 0, style.GetDetailsBottomMargin().CP(scale), style.GetDetailsLeftMargin().CP(scale)); full_data_layout_->SetSpaceBetweenChildren(CHILDREN_SPACE.CP(scale)); } if (image_data_layout_) image_data_layout_->SetSpaceBetweenChildren(style.GetPanelSplitWidth().CP(scale)); if (rating_) { rating_->SetMaximumHeight(style.GetRatingWidgetHeight().CP(scale)); rating_->SetMinimumHeight(style.GetRatingWidgetHeight().CP(scale)); } if (preview_info_scroll_) preview_info_scroll_->scale = scale; if (preview_info_layout_) preview_info_layout_->SetSpaceBetweenChildren(PREVIEW_INFO_CHILDREN_SPACE); if (actions_layout_) actions_layout_->SetLeftAndRightPadding(0, style.GetDetailsRightMargin().CP(scale)); } } // namespace previews } // namespace dash } // namespace unity ./dash/previews/PaymentPreview.h0000644000004100000410000000743613437202764017200 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Diego Sarmentero * Manuel de la Pena * */ #ifndef PAYMENT_PREVIEW_H #define PAYMENT_PREVIEW_H #include #include #include #include #include #include "ActionButton.h" #include "ActionLink.h" #include "Preview.h" #include "unity-shared/IconTexture.h" #include "unity-shared/TextInput.h" namespace nux { class AbstractPaintLayer; class StaticCairoText; class VLayout; } namespace unity { namespace dash { namespace previews { class CoverArt; class PreviewInfoHintWidget; class PaymentPreview : public Preview { public: typedef nux::ObjectPtr Ptr; PaymentPreview(dash::Preview::Ptr preview_model); // From debug::Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); // Create and connect an action link to OnActionLinkActivated nux::ObjectPtr CreateLink(dash::Preview::ActionPtr action); // Create and connect an action button OnActioButtonActivated nux::ObjectPtr CreateButton(dash::Preview::ActionPtr action); void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); void ShowOverlay(bool isShown); void ShowOverlay(); void HideOverlay(); private: protected: GVariant *data_; // build the header to be shown in the preview nux::Layout* GetHeader(); // Return the title layout (including layout data) to be added to the header // NULL is a possible return value. virtual nux::Layout* GetTitle() = 0; // Return the pize layout (including data) to be added to the header // NULL is a possible return value. virtual nux::Layout* GetPrice() = 0; // Return layout with the content to show. NULL is a possible return value. virtual nux::Layout* GetBody() = 0; // Return layout with the content to show. NULL is a possible return value. virtual nux::Layout* GetFooter() = 0; // Executed when a link is clicked. virtual void OnActionActivated(ActionButton* button, std::string const& id) = 0; // Executed when a button is clicked. virtual void OnActionLinkActivated(ActionLink* link, std::string const& id) = 0; virtual void PreLayoutManagement() = 0; virtual void LoadActions() = 0; virtual void SetupViews(); virtual void UpdateScale(double scale) override; virtual void SetupBackground(); nux::ObjectPtr full_data_layout_; nux::ObjectPtr content_data_layout_; nux::ObjectPtr overlay_layout_; nux::ObjectPtr header_layout_; nux::ObjectPtr body_layout_; nux::ObjectPtr footer_layout_; StaticCairoText* calculating_; // content elements nux::ObjectPtr image_; // do we want to type? bool entry_selected_; typedef std::unique_ptr LayerPtr; LayerPtr details_bg_layer_; }; } } } #endif // PAYMENT_PREVIEW_H ./dash/ScopeView.cpp0000755000004100000410000011072613437202764014614 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2014 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #include "ScopeView.h" #include #include #include #include "unity-shared/DashStyle.h" #include "ResultRendererTile.h" #include "ResultRendererHorizontalTile.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/OverlayScrollView.h" #include "unity-shared/GraphicsUtils.h" #include "config.h" #include namespace unity { namespace dash { DECLARE_LOGGER(logger, "unity.dash.scopeview"); DECLARE_LOGGER(focus_logger, "unity.dash.scopeview.focus"); namespace { const RawPixel CARD_VIEW_GAP_VERT = 24_em; // pixels const RawPixel CARD_VIEW_GAP_HORIZ = 25_em; // pixels const RawPixel FOCUSED_OFFSET = 30_em; const double DEFAULT_SCALE = 1.0; } // This is so we can access some protected members in scrollview. class ScopeScrollView: public dash::ScrollView { public: ScopeScrollView(NUX_FILE_LINE_DECL) : ScrollView(NUX_FILE_LINE_PARAM) , right_area_(nullptr) , up_area_(nullptr) { OnVisibleChanged.connect([this] (nux::Area* /*area*/, bool visible) { if (m_horizontal_scrollbar_enable) _hscrollbar->SetVisible(visible); if (m_vertical_scrollbar_enable) _vscrollbar->SetVisible(visible); }); } void ScrollToPosition(nux::Geometry const& position) { // much of this code is copied from Nux/ScrollView.cpp nux::Geometry const& geo = GetGeometry(); int child_y = position.y - geo.y; int child_y_diff = child_y - abs (_delta_y); if (child_y_diff + position.height < geo.height && child_y_diff >= 0) { return; } if (child_y_diff < 0) { ScrollUp (1, abs (child_y_diff)); } else { int size = child_y_diff - geo.height; // always keeps the top of a view on the screen size += position.height; ScrollDown (1, size); } } void SetRightArea(nux::Area* area) { right_area_ = area; } void SetUpArea(nux::Area* area) { up_area_ = area; } void EnableScrolling(bool enable_scrolling) { _vscrollbar->SetInputEventSensitivity(enable_scrolling); } nux::VScrollBar* GetScrollbar() const { return _vscrollbar; } protected: // This is so we can break the natural key navigation path. nux::Area* KeyNavIteration(nux::KeyNavDirection direction) { nux::Area* focus_area = nux::GetWindowCompositor().GetKeyFocusArea(); if (direction == nux::KEY_NAV_RIGHT && focus_area && focus_area->IsChildOf(this)) return right_area_; else if (direction == nux::KEY_NAV_UP && focus_area && focus_area->IsChildOf(this)) return up_area_; else return nux::ScrollView::KeyNavIteration(direction); } private: nux::Area* right_area_; nux::Area* up_area_; }; NUX_IMPLEMENT_OBJECT_TYPE(ScopeView); ScopeView::ScopeView(Scope::Ptr const& scope, nux::Area* show_filters) : nux::View(NUX_TRACKER_LOCATION) , filters_expanded(false) , can_refine_search(false) , scale(DEFAULT_SCALE) , neko_mode(false) , scope_(scope) , no_results_active_(false) , last_good_filter_model_(-1) , filter_expansion_pushed_(false) , scope_connected_(scope ? scope->connected : false) , search_on_next_connect_(false) , current_focus_category_position_(-1) { SetupViews(show_filters); search_string.SetGetterFunction([this] { return search_string_; }); filters_expanded.changed.connect(sigc::mem_fun(this, &ScopeView::OnScopeFilterExpanded)); view_type.changed.connect(sigc::mem_fun(this, &ScopeView::OnViewTypeChanged)); scale.changed.connect(sigc::mem_fun(this, &ScopeView::UpdateScale)); auto conn = nux::GetWindowCompositor().key_nav_focus_change.connect(sigc::mem_fun(this, &ScopeView::OnCompositorKeyNavFocusChanged)); key_nav_focus_connection_ = conn_manager_.Add(conn); if (scope_) { conn_manager_.Add(scope_->categories.changed.connect(sigc::mem_fun(this, &ScopeView::SetupCategories))); SetupCategories(scope->categories); conn_manager_.Add(scope_->results.changed.connect(sigc::mem_fun(this, &ScopeView::SetupResults))); SetupResults(scope->results); conn_manager_.Add(scope_->filters.changed.connect(sigc::mem_fun(this, &ScopeView::SetupFilters))); SetupFilters(scope->filters); neko_mode.changed.connect([this] (bool) { SetupCategories(scope_->categories); }); scope_->connected.changed.connect([this](bool is_connected) { // We need to search again if we were reconnected after being connected before. if (scope_connected_ && !is_connected) search_on_next_connect_ = true; else if (is_connected && search_on_next_connect_) { search_on_next_connect_ = false; if (IsVisible()) PerformSearch(search_string_, nullptr); } scope_connected_ = is_connected; }); } ubus_manager_.RegisterInterest(UBUS_RESULT_VIEW_KEYNAV_CHANGED, [this] (GVariant* data) { // we get this signal when a result view keynav changes, // its a bad way of doing this but nux ABI needs to be broken // to do it properly nux::Geometry focused_pos; g_variant_get (data, "(iiii)", &focused_pos.x, &focused_pos.y, &focused_pos.width, &focused_pos.height); for (auto const& group : category_views_) { if (group->GetLayout() != nullptr) { auto expand_label = group->GetHeaderFocusableView(); auto child = group->GetChildView(); if ((child && child->HasKeyFocus()) || (expand_label && expand_label->HasKeyFocus())) { focused_pos.x += child->GetGeometry().x; focused_pos.y += child->GetGeometry().y - FOCUSED_OFFSET.CP(scale()); focused_pos.height += FOCUSED_OFFSET.CP(scale()); scroll_view_->ScrollToPosition(focused_pos); break; } } } }); OnVisibleChanged.connect([this] (nux::Area* area, bool visible) { scroll_view_->SetVisible(visible); if ((filters_expanded && visible) || !visible) fscroll_view_->SetVisible(visible); }); } void ScopeView::SetupViews(nux::Area* show_filters) { layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); scroll_view_ = new ScopeScrollView(NUX_TRACKER_LOCATION); scroll_view_->scale = scale(); scroll_view_->EnableVerticalScrollBar(true); scroll_view_->EnableHorizontalScrollBar(false); layout_->AddView(scroll_view_); scroll_layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); scroll_view_->SetLayout(scroll_layout_); scroll_view_->SetRightArea(show_filters); scroll_view_->GetScrollbar()->queue_draw.connect(sigc::hide(sigc::mem_fun(scroll_layout_, &nux::VLayout::QueueDraw))); no_results_ = new StaticCairoText("", NUX_TRACKER_LOCATION); no_results_->SetTextColor(nux::color::White); no_results_->SetVisible(false); no_results_->SetScale(scale); scroll_layout_->AddView(no_results_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); fscroll_view_ = new ScopeScrollView(NUX_TRACKER_LOCATION); fscroll_view_->scale = scale(); fscroll_view_->EnableVerticalScrollBar(true); fscroll_view_->EnableHorizontalScrollBar(false); fscroll_view_->SetVisible(false); fscroll_view_->SetUpArea(show_filters); layout_->AddView(fscroll_view_, 1); fscroll_layout_ = new nux::VLayout(); fscroll_view_->SetLayout(fscroll_layout_); fscroll_view_->GetScrollbar()->queue_draw.connect(sigc::hide(sigc::mem_fun(fscroll_layout_, &nux::VLayout::QueueDraw))); filter_bar_ = new FilterBar(); AddChild(filter_bar_); fscroll_layout_->AddView(filter_bar_, 0); SetLayout(layout_); UpdateScopeViewSize(); } void ScopeView::UpdateScopeViewSize() { dash::Style const& style = dash::Style::Instance(); int right_padding = style.GetFilterViewRightPadding().CP(scale); int filter_width = style.GetFilterBarWidth().CP(scale) + style.GetFilterBarLeftPadding().CP(scale) + style.GetFilterBarRightPadding().CP(scale); layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenScopeAndFilters().CP(scale)); fscroll_view_->SetMinimumWidth(filter_width + right_padding); fscroll_view_->SetMaximumWidth(filter_width + right_padding); filter_bar_->SetMinimumWidth(filter_width); filter_bar_->SetMaximumWidth(filter_width); } void ScopeView::UpdateScale(double scale) { UpdateScopeViewSize(); for (auto& group : category_views_) group->scale = scale; scroll_view_->scale = scale; fscroll_view_->scale = scale; filter_bar_->scale = scale; no_results_->SetScale(scale); } void ScopeView::SetupCategories(Categories::Ptr const& categories) { conn_manager_.RemoveAndClear(&category_added_connection_); conn_manager_.RemoveAndClear(&category_changed_connection_); conn_manager_.RemoveAndClear(&category_removed_connection_); if (!categories) return; QueueCategoryCountsCheck(); auto conn = categories->category_added.connect(sigc::mem_fun(this, &ScopeView::OnCategoryAdded)); category_added_connection_ = conn_manager_.Add(conn); conn = categories->category_changed.connect(sigc::mem_fun(this, &ScopeView::OnCategoryChanged)); category_changed_connection_ = conn_manager_.Add(conn); conn = categories->category_removed.connect(sigc::mem_fun(this, &ScopeView::OnCategoryRemoved)); category_removed_connection_ = conn_manager_.Add(conn); categories->model.changed.connect(sigc::hide([this]() { ClearCategories(); })); ClearCategories(); for (unsigned int i = 0; i < categories->count(); ++i) OnCategoryAdded(categories->RowAtIndex(i)); scope_->category_order.changed.connect(sigc::mem_fun(this, &ScopeView::OnCategoryOrderChanged)); } void ScopeView::OnCategoryOrderChanged(std::vector const& order) { LOG_DEBUG(logger) << "Reordering categories for " << scope_->name(); ////////////////////////////////////////////////// // Find the current focus category position && result index // This is to keep the focus in the same place if categories are being added/removed/reordered PushResultFocus("reorder"); auto key_nav_focus_conn = conn_manager_.Get(key_nav_focus_connection_); key_nav_focus_conn.block(true); ////////////////////////////////////////////////// category_order_ = order; for (auto const& group : category_views_) scroll_layout_->RemoveChildObject(group.GetPointer()); if (scope_) { // there should be ~10 categories, so this shouldn't be too big of a deal for (unsigned i = 0; i < category_order_.size(); i++) { unsigned int desired_category_index = category_order_[i]; if (category_views_.size() <= desired_category_index) continue; scroll_layout_->AddView(category_views_[desired_category_index].GetPointer(), 0); } } ////////////////////////////////////////////////// // Update current focus category position && result index // This is to keep the focus in the same place if categories are being added/removed/reordered PopResultFocus("reorder"); key_nav_focus_conn.block(false); ////////////////////////////////////////////////// QueueRelayout(); } void ScopeView::SetupResults(Results::Ptr const& results) { conn_manager_.RemoveAndClear(&result_added_connection_); conn_manager_.RemoveAndClear(&result_removed_connection_); if (!results) return; auto conn = results->result_added.connect(sigc::mem_fun(this, &ScopeView::OnResultAdded)); result_added_connection_ = conn_manager_.Add(conn); conn = results->result_removed.connect(sigc::mem_fun(this, &ScopeView::OnResultRemoved)); result_removed_connection_ = conn_manager_.Add(conn); results->model.changed.connect([this] (glib::Object model) { for (unsigned int i = 0; i < category_views_.size(); ++i) { ResultView* result_view = GetResultViewForCategory(i); if (result_view) { result_view->SetResultsModel(scope_->GetResultsForCategory(i)); } } }); for (unsigned int i = 0; i < results->count(); ++i) OnResultAdded(results->RowAtIndex(i)); } void ScopeView::SetupFilters(Filters::Ptr const& filters) { conn_manager_.RemoveAndClear(&filter_added_connection_); conn_manager_.RemoveAndClear(&filter_removed_connection_); if (!filters) return; auto conn = filters->filter_added.connect(sigc::mem_fun(this, &ScopeView::OnFilterAdded)); filter_added_connection_ = conn_manager_.Add(conn); conn = filters->filter_removed.connect(sigc::mem_fun(this, &ScopeView::OnFilterRemoved)); filter_removed_connection_ = conn_manager_.Add(conn); auto clear_filters = [this] () { auto conn = conn_manager_.Get(filter_removed_connection_); bool blocked = conn.block(true); filter_bar_->ClearFilters(); conn.block(blocked); }; filters->model.changed.connect(sigc::hide(clear_filters)); clear_filters(); for (unsigned int i = 0; i < filters->count(); ++i) OnFilterAdded(filters->FilterAtIndex(i)); } void ScopeView::OnCategoryAdded(Category const& category) { if (category.index == static_cast(-1)) return; std::string const& name = category.name; std::string const& icon_hint = category.icon_hint; std::string const& renderer_name = category.renderer_name; unsigned index = category.index; bool reset_filter_models = index < category_views_.size(); LOG_DEBUG(logger) << "Category added '" << (scope_ ? scope_->name() : "unknown") << "': " << name << "[" << category.id() << "] " << "(" << icon_hint << ", " << renderer_name << ", " << index << ")"; ////////////////////////////////////////////////// // Find the current focus category position && result index // This is to keep the focus in the same place if categories are being added/removed/reordered PushResultFocus("add"); auto key_nav_focus_conn = conn_manager_.Get(key_nav_focus_connection_); key_nav_focus_conn.block(true); ////////////////////////////////////////////////// PlacesGroup::Ptr group(CreatePlacesGroup(category)); AddChild(group.GetPointer()); group->SetName(name); group->SetIcon(icon_hint); group->SetExpanded(false); group->SetVisible(false); group->scale = scale(); int view_index = category_order_.size(); auto find_view_index = std::find(category_order_.begin(), category_order_.end(), index); if (find_view_index == category_order_.end()) { category_order_.push_back(index); } else { view_index = find_view_index - category_order_.begin(); } category_views_.insert(category_views_.begin() + index, group); group->expanded.connect(sigc::mem_fun(this, &ScopeView::OnGroupExpanded)); /* Reset result count */ counts_[group] = 0; auto* results_view = new ResultViewGrid(NUX_TRACKER_LOCATION); if (category.GetContentType() == "social" && category.renderer_name == "default") { results_view->SetModelRenderer(new ResultRendererHorizontalTile(NUX_TRACKER_LOCATION)); results_view->horizontal_spacing = CARD_VIEW_GAP_HORIZ.CP(scale()); results_view->vertical_spacing = CARD_VIEW_GAP_VERT.CP(scale()); } else { results_view->SetModelRenderer(new ResultRendererTile(NUX_TRACKER_LOCATION, neko_mode())); } if (scope_) { results_view->unique_id = name + scope_->name(); results_view->expanded = false; if (scope_->id() == "applications.scope" || (scope_->id() == "home.scope" && category.id() == "applications.scope")) { results_view->default_click_activation = ResultView::ActivateType::DIRECT; } results_view->ResultActivated.connect([this, results_view] (LocalResult const& local_result, ResultView::ActivateType type, GVariant* data) { result_activated.emit(type, local_result, data, results_view->unique_id()); switch (type) { case ResultView::ActivateType::DIRECT: { scope_->Activate(local_result, nullptr, cancellable_); } break; case ResultView::ActivateType::PREVIEW: { scope_->Preview(local_result, nullptr, cancellable_); } break; default: break; }; }); /* Set up filter model for this category */ Results::Ptr results_model = scope_->GetResultsForCategory(index); counts_[group] = results_model ? results_model->count() : 0; results_view->SetResultsModel(results_model); } group->SetChildView(results_view); /* We need the full range of method args so we can specify the offset * of the group into the layout */ scroll_layout_->AddView(group.GetPointer(), 0, nux::MinorDimensionPosition::MINOR_POSITION_START, nux::MinorDimensionSize::MINOR_SIZE_FULL, 100.0f, (nux::LayoutPosition)view_index); ////////////////////////////////////////////////// // Update current focus category position && result index // This is to keep the focus in the same place if categories are being added/removed/reordered PopResultFocus("add"); key_nav_focus_conn.block(false); ////////////////////////////////////////////////// if (reset_filter_models) { QueueReinitializeFilterCategoryModels(index); } QueueCategoryCountsCheck(); } void ScopeView::OnCategoryChanged(Category const& category) { if (category_views_.size() <= category.index) return; PlacesGroup::Ptr const& group = category_views_[category.index]; group->SetName(category.name); group->SetIcon(category.icon_hint); QueueCategoryCountsCheck(); } void ScopeView::OnCategoryRemoved(Category const& category) { unsigned index = category.index; if (index == unsigned(-1) || category_views_.size() <= index) return; std::string const& name = category.name; std::string const& icon_hint = category.icon_hint; std::string const&renderer_name = category.renderer_name; bool reset_filter_models = index < category_views_.size()-1; LOG_DEBUG(logger) << "Category removed '" << (scope_ ? scope_->name() : "unknown") << "': " << name << "(" << icon_hint << ", " << renderer_name << ", " << index << ")"; auto category_pos = category_views_.begin() + index; PlacesGroup::Ptr group = *category_pos; if (last_expanded_group_ == group) last_expanded_group_.Release(); ////////////////////////////////////////////////// // Find the current focus category position && result index // This is to keep the focus in the same place if categories are being added/removed/reordered PushResultFocus("remove"); auto key_nav_focus_conn = conn_manager_.Get(key_nav_focus_connection_); key_nav_focus_conn.block(true); ////////////////////////////////////////////////// counts_.erase(group); category_views_.erase(category_pos); // remove from order auto order_pos = std::find(category_order_.begin(), category_order_.end(), index); if (order_pos != category_order_.end()) category_order_.erase(order_pos); scroll_layout_->RemoveChildObject(group.GetPointer()); RemoveChild(group.GetPointer()); ////////////////////////////////////////////////// // Update current focus category position && result index // This is to keep the focus in the same place if categories are being added/removed/reordered PopResultFocus("remove"); key_nav_focus_conn.block(false); ////////////////////////////////////////////////// QueueRelayout(); if (reset_filter_models) { QueueReinitializeFilterCategoryModels(index); } } void ScopeView::ClearCategories() { for (auto const& group : category_views_) { RemoveChild(group.GetPointer()); scroll_layout_->RemoveChildObject(group.GetPointer()); } counts_.clear(); category_views_.clear(); last_expanded_group_.Release(); QueueRelayout(); } void ScopeView::QueueReinitializeFilterCategoryModels(unsigned int start_category_index) { if (!scope_) return; Categories::Ptr category_model = scope_->categories(); unsigned int category_count = 0; if (!category_model || (category_count=category_model->count()) <= start_category_index) return; if (category_views_.size() <= (start_category_index + 1)) return; /* Scope is reodering the categories, and since their category index is based * on the row position in the model, we need to re-initialize the category result * models if we got insert and not an append */ for (auto iter = category_views_.begin() + start_category_index +1, end = category_views_.end(); iter != end; ++iter) { ResultView* result_view = (*iter)->GetChildView(); if (result_view) result_view->SetResultsModel(Results::Ptr()); } if (last_good_filter_model_ == -1 || static_cast(start_category_index) < last_good_filter_model_) { last_good_filter_model_ = static_cast(start_category_index); } if (!fix_filter_models_idle_) { fix_filter_models_idle_.reset(new glib::Idle(sigc::mem_fun(this, &ScopeView::ReinitializeCategoryResultModels), glib::Source::Priority::HIGH)); } } bool ScopeView::ReinitializeCategoryResultModels() { if (!scope_) return false; if (last_good_filter_model_ < 0) return false; if (category_views_.size() > static_cast(last_good_filter_model_)+1) { unsigned int category_index = static_cast(last_good_filter_model_) +1; for (auto iter = category_views_.begin() + category_index, end = category_views_.end(); iter != end; ++iter, category_index++) { ResultView* result_view = (*iter)->GetChildView(); if (result_view) result_view->SetResultsModel(scope_->GetResultsForCategory(category_index)); } } last_good_filter_model_ = -1; fix_filter_models_idle_.reset(); return false; } ResultView* ScopeView::GetResultViewForCategory(unsigned int category_index) { if (category_views_.size() <= category_index) return nullptr; auto category_pos = category_views_.begin() + category_index; PlacesGroup::Ptr group = *category_pos; return static_cast(group->GetChildView()); } void ScopeView::OnResultAdded(Result const& result) { // category not added yet. if (category_views_.size() <= result.category_index) return; std::string uri = result.uri; LOG_TRACE(logger) << "Result added '" << (scope_ ? scope_->name() : "unknown") << "': " << uri; counts_[category_views_[result.category_index]]++; // make sure we don't display the no-results-hint if we do have results CheckNoResults(glib::HintsMap()); QueueCategoryCountsCheck(); } void ScopeView::OnResultRemoved(Result const& result) { // category not added yet. if (category_views_.size() <= result.category_index) return; std::string uri = result.uri; LOG_TRACE(logger) << "Result removed '" << (scope_ ? scope_->name() : "unknown") << "': " << uri; counts_[category_views_[result.category_index]]--; // make sure we don't display the no-results-hint if we do have results CheckNoResults(glib::HintsMap()); QueueCategoryCountsCheck(); } void ScopeView::CheckNoResults(glib::HintsMap const& hints) { gint count = scope_->results() ? scope_->results()->count() : 0; if (count == 0) { std::stringstream markup; glib::HintsMap::const_iterator it; it = hints.find("no-results-hint"); markup << ""; if (it != hints.end()) markup << it->second.GetString(); else markup << _("Sorry, there is nothing that matches your search."); markup << ""; LOG_DEBUG(logger) << "The no-result-hint is: " << markup.str(); scroll_layout_->SetContentDistribution(nux::MAJOR_POSITION_CENTER); no_results_active_ = true; no_results_->SetText(markup.str()); no_results_->SetVisible(true); } else if (count && no_results_active_) { scroll_layout_->SetContentDistribution(nux::MAJOR_POSITION_START); no_results_active_ = false; no_results_->SetText(""); no_results_->SetVisible(false); } } void ScopeView::QueueCategoryCountsCheck() { if (!model_updated_timeout_) { model_updated_timeout_.reset(new glib::Idle([this] () { // Check if all results so far are from one category // If so, then expand that category. CheckCategoryCounts(); model_updated_timeout_.reset(); return false; }, glib::Source::Priority::HIGH)); } } void ScopeView::CheckCategoryCounts() { int number_of_displayed_categories = 0; PlacesGroup::Ptr new_expanded_group; PushResultFocus("count check"); for (auto category_index : category_order_) { if (category_views_.size() <= category_index) continue; PlacesGroup::Ptr const& group = category_views_[category_index]; group->SetCounts(counts_[group]); group->SetVisible(counts_[group] > 0); if (counts_[group] > 0) { ++number_of_displayed_categories; new_expanded_group = group; } } if (last_expanded_group_ and last_expanded_group_ != new_expanded_group) { last_expanded_group_->PopExpanded(); last_expanded_group_ = nullptr; } if (new_expanded_group and number_of_displayed_categories <= 2) { new_expanded_group->PushExpanded(); new_expanded_group->SetExpanded(true); last_expanded_group_ = new_expanded_group; } PopResultFocus("count check"); } void ScopeView::HideResultsMessage() { if (no_results_active_) { scroll_layout_->SetContentDistribution(nux::MAJOR_POSITION_START); no_results_active_ = false; no_results_->SetText(""); no_results_->SetVisible(false); } } bool ScopeView::PerformSearch(std::string const& search_query, SearchCallback const& callback) { if (search_string_ != search_query) { for (auto const& group : category_views_) group->SetExpanded(false); } search_string_ = search_query; if (scope_) { // 150ms to hide the no reults message if its take a while to return results hide_message_delay_.reset(new glib::Timeout(150, [this] () { HideResultsMessage(); return false; })); // cancel old search. search_cancellable_.Renew(); scope_->Search(search_query, [this, callback] (std::string const& search_string, glib::HintsMap const& hints, glib::Error const& err) { if (err && !scope_connected_) { // if we've failed a search due to connection issue, we need to try again when we re-connect search_on_next_connect_ = true; } CheckNoResults(hints); hide_message_delay_.reset(); if (callback) callback(scope_->id(), search_string, err); }, search_cancellable_); return true; } return false; } void ScopeView::OnGroupExpanded(PlacesGroup* group) { ResultViewGrid* grid = static_cast(group->GetChildView()); grid->expanded = group->GetExpanded(); QueueRelayout(); } void ScopeView::CheckScrollBarState() { if (scroll_layout_->GetHeight() > scroll_view_->GetHeight()) { scroll_view_->EnableVerticalScrollBar(true); } else { scroll_view_->EnableVerticalScrollBar(false); } } void ScopeView::OnFilterAdded(Filter::Ptr filter) { filter_bar_->AddFilter(filter); can_refine_search = true; } void ScopeView::OnFilterRemoved(Filter::Ptr filter) { filter_bar_->RemoveFilter(filter); } void ScopeView::OnViewTypeChanged(ScopeViewType view_type) { if (!scope_) return; scope_->view_type = view_type; } void ScopeView::OnScopeFilterExpanded(bool expanded) { if (fscroll_view_->IsVisible() != expanded) { fscroll_view_->SetVisible(expanded); QueueRelayout(); } for (auto const& category_view : category_views_) category_view->SetFiltersExpanded(expanded); } void ScopeView::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { if (RedirectedAncestor()) graphics::ClearGeometry(GetGeometry()); } void ScopeView::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& geo = GetGeometry(); graphics_engine.PushClippingRectangle(geo); CheckScrollBarState(); if (!IsFullRedraw() && RedirectedAncestor()) { if (filter_bar_ && filter_bar_->IsVisible() && filter_bar_->IsRedrawNeeded()) graphics::ClearGeometry(filter_bar_->GetGeometry()); else if (no_results_ && no_results_->IsVisible() && no_results_->IsRedrawNeeded()) graphics::ClearGeometry(no_results_->GetGeometry()); } layout_->ProcessDraw(graphics_engine, force_draw); graphics_engine.PopClippingRectangle(); } Scope::Ptr ScopeView::scope() const { return scope_; } nux::Area* ScopeView::fscroll_view() const { return fscroll_view_; } int ScopeView::GetNumRows() { int num_rows = 0; for (PlacesGroup::Ptr const& group : category_views_) { if (group->IsVisible() && group->GetChildView()) { num_rows += 1; // The category header if (group->GetExpanded()) { int result_rows = 0, result_columns = 0; group->GetChildView()->GetResultDimensions(result_rows, result_columns); num_rows += result_rows; } else num_rows += 1; } } return num_rows; } void ScopeView::AboutToShow() { JumpToTop(); OnScopeFilterExpanded(filters_expanded); } void ScopeView::JumpToTop() { scroll_view_->ScrollToPosition(nux::Geometry(0, 0, 0, 0)); } void ScopeView::PerformPageNavigation(ScrollDir dir) { scroll_view_->page_direction.emit(dir); } void ScopeView::ActivateFirst() { if (!scope_) return; Results::Ptr results = scope_->results; if (results->count()) { // the first displayed category might not be category_views_[0] for (auto iter = category_order_.begin(); iter != category_order_.end(); ++iter) { unsigned int category_index = *iter; if (category_views_.size() <= category_index) continue; PlacesGroup::Ptr group = category_views_[category_index]; ResultView* result_view = group->GetChildView(); if (result_view == nullptr) continue; auto it = result_view->GetIteratorAtRow(0); if (!it.IsLast()) { Result result(*it); result_view->Activate(result, result_view->GetIndexForLocalResult(result), ResultView::ActivateType::DIRECT); return; } } // Fallback Result result = results->RowAtIndex(0); if (result.uri != "") { result_activated.emit(ResultView::ActivateType::DIRECT, LocalResult(result), nullptr, ""); scope_->Activate(result); } } } // Keyboard navigation bool ScopeView::AcceptKeyNavFocus() { return false; } void ScopeView::ForceCategoryExpansion(std::string const& view_id, bool expand) { for (auto const& group : category_views_) { if (group->GetChildView()->unique_id == view_id) { if (expand) { group->PushExpanded(); group->SetExpanded(true); } else { group->PopExpanded(); } } } } void ScopeView::SetResultsPreviewAnimationValue(float preview_animation) { for (auto const& group : category_views_) group->SetResultsPreviewAnimationValue(preview_animation); } void ScopeView::EnableResultTextures(bool enable_result_textures) { scroll_view_->EnableScrolling(!enable_result_textures); for (auto const& group : category_views_) { ResultView* result_view = group->GetChildView(); if (result_view) { result_view->enable_texture_render = enable_result_textures; } } } std::vector ScopeView::GetResultTextureContainers() { // iterate in visual order std::vector textures; for (auto iter = category_order_.begin(); iter != category_order_.end(); ++iter) { unsigned int category_index = *iter; if (category_views_.size() <= category_index) continue; PlacesGroup::Ptr cateogry_view = category_views_[category_index]; if (!cateogry_view || !cateogry_view->IsVisible()) continue; ResultView* result_view = cateogry_view->GetChildView(); if (result_view) { // concatenate textures std::vector const& category_textures = result_view->GetResultTextureContainers(); for (auto iter2 = category_textures.begin(); iter2 != category_textures.end(); ++iter2) { ResultViewTexture::Ptr const& result_texture = *iter2; result_texture->category_index = category_index; textures.push_back(result_texture); } } } return textures; } void ScopeView::RenderResultTexture(ResultViewTexture::Ptr const& result_texture) { ResultView* result_view = GetResultViewForCategory(result_texture->category_index); if (result_view) result_view->RenderResultTexture(result_texture); } void ScopeView::PushFilterExpansion(bool expand) { filter_expansion_pushed_ = filters_expanded; filters_expanded = expand; } void ScopeView::PopFilterExpansion() { filters_expanded = GetPushedFilterExpansion(); } bool ScopeView::GetPushedFilterExpansion() const { return filter_expansion_pushed_; } PlacesGroup::Ptr ScopeView::CreatePlacesGroup(Category const& category) { return PlacesGroup::Ptr(new PlacesGroup(dash::Style::Instance())); } ScopeView::CategoryGroups ScopeView::GetOrderedCategoryViews() const { CategoryGroups category_view_ordered; for (auto const& category_index : category_order_) { if (category_views_.size() <= category_index) continue; PlacesGroup::Ptr const& group = category_views_[category_index]; category_view_ordered.push_back(group); } return category_view_ordered; } // Introspectable std::string ScopeView::GetName() const { return "ScopeView"; } void ScopeView::AddProperties(debug::IntrospectionData& introspection) { introspection .add("name", scope_->id) .add("scope-name", scope_->name) .add("visible", IsVisible()) .add("no-results-active", no_results_active_); } void ScopeView::OnCompositorKeyNavFocusChanged(nux::Area* area, bool has_focus, nux::KeyNavDirection) { if (!IsVisible()) return; LOG_DEBUG(focus_logger) << "Global focus changed to " << (area ? area->Type().name : "NULL"); if (area && has_focus) { // If we've change the focus to a places group child, then we need to update it's focus. bool found_group = false; while(area) { if (area->Type().IsDerivedFromType(PlacesGroup::StaticObjectType)) { found_group = true; break; } // opimise to break out if we reach this level as it will never be a group. else if (area == this) break; area = area->GetParentObject(); } if (!found_group && current_focus_category_position_ != -1) { LOG_DEBUG(focus_logger) << "Resetting focus for position " << current_focus_category_position_; current_focus_category_position_ = -1; current_focus_variant_ = nullptr; } } } void ScopeView::PushResultFocus(const char* reason) { int current_category_position = 0; for (auto iter = category_order_.begin(); iter != category_order_.end(); ++iter) { unsigned category_index = *iter; if (category_views_.size() <= category_index) continue; PlacesGroup::Ptr group = category_views_[category_index]; if (!group || !group->IsVisible()) continue; nux::Area* focus_area = nux::GetWindowCompositor().GetKeyFocusArea(); while(focus_area) { if (focus_area == group.GetPointer()) { current_focus_category_position_ = current_category_position; current_focus_variant_ = group->GetCurrentFocus(); LOG_DEBUG(focus_logger) << "Saving focus for position " << current_focus_category_position_ << " due to '" << reason << "'"; break; } // opimise to break out if we reach this level as it will never be a group. else if (focus_area == this) break; focus_area = focus_area->GetParentObject(); } current_category_position++; } } void ScopeView::PopResultFocus(const char* reason) { int current_category_position = 0; for (auto iter = category_order_.begin(); iter != category_order_.end(); ++iter) { unsigned category_index = *iter; if (category_views_.size() <= category_index) continue; PlacesGroup::Ptr group = category_views_[category_index]; if (!group || !group->IsVisible()) continue; if (current_category_position == current_focus_category_position_) { group->SetCurrentFocus(current_focus_variant_); LOG_DEBUG(focus_logger) << "Restoring focus for position " << current_focus_category_position_ << " due to '" << reason << "'"; break; } current_category_position++; } } } } ./dash/ScopeBar.h0000644000004100000410000000442413437202764014045 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #ifndef UNITYSHELL_SCOPE_BAR_H #define UNITYSHELL_SCOPE_BAR_H #include #include #include #include #include #include #include #include #include "unity-shared/IconTexture.h" #include "unity-shared/Introspectable.h" #include "unity-shared/UBusWrapper.h" #include "ScopeBarIcon.h" namespace nux { class AbstractPaintLayer; class HLayout; class LayeredLayout; } namespace unity { class IconTexture; class StaticCairoText; namespace dash { class ScopeBar : public nux::View, public unity::debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(ScopeBar, nux::View); typedef std::vector ScopeIcons; public: ScopeBar(); nux::Property scale; void AddScope(Scope::Ptr const& scope); void Activate(std::string id); void ActivateNext(); void ActivatePrevious(); std::string GetActiveScopeId() const; sigc::signal scope_activated; private: void SetupBackground(); void SetupLayout(); void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw); void SetActive(ScopeBarIcon* icon); void UpdateScale(double scale); bool AcceptKeyNavFocus(); std::string GetName() const; void AddProperties(debug::IntrospectionData&); typedef std::unique_ptr LayerPtr; ScopeIcons icons_; nux::HLayout* layout_; LayerPtr bg_layer_; friend class TestScopeBar; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_SCOPE_BAR_H ./dash/ApplicationStarterImp.h0000644000004100000410000000175013437202764016624 0ustar www-datawww-data/* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andrea Azzarone */ #ifndef UNITY_SHARED_APPLICATION_STARTER_IMP_H #define UNITY_SHARED_APPLICATION_STARTER_IMP_H #include "ApplicationStarter.h" namespace unity { class ApplicationStarterImp : public ApplicationStarter { public: bool Launch(std::string const& application_name, Time timestamp) override; }; } #endif ./dash/FilterRatingsButton.h0000644000004100000410000000310513437202764016313 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERRATINGSBUTTONWIDGET_H #define UNITYSHELL_FILTERRATINGSBUTTONWIDGET_H #include #include #include #include "unity-shared/RatingsButton.h" namespace unity { namespace dash { class FilterRatingsButton : public RatingsButton { NUX_DECLARE_OBJECT_TYPE(FilterRatingsButton, RatingsButton); public: FilterRatingsButton(NUX_FILE_LINE_PROTO); void SetFilter(Filter::Ptr const& filter); RatingsFilter::Ptr GetFilter() const; std::string GetFilterType(); protected: // Introspectable methods std::string GetName() const; void SetRating(float rating) override; float GetRating() const override; private: dash::RatingsFilter::Ptr filter_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERRATINGSBUTTONWIDGET_H ./dash/DashView.cpp0000644000004100000410000016263013437202764014420 0ustar www-datawww-data/* * Copyright (C) 2010-2014 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel * Nick Dedekind */ #include "DashView.h" #include "DashViewPrivate.h" #include "FilterExpanderLabel.h" #include "MultiMonitor.h" #include #include #include #include #include #include #include #include "unity-shared/DashStyle.h" #include "unity-shared/KeyboardUtil.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/WindowManager.h" namespace unity { namespace dash { DECLARE_LOGGER(logger, "unity.dash.view"); namespace { previews::Style preview_style; const int PREVIEW_ANIMATION_LENGTH = 250; const int DASH_TILE_HORIZONTAL_COUNT = 6; const int DASH_DEFAULT_CATEGORY_COUNT = 3; const RawPixel DASH_RESULT_RIGHT_PAD = 35_em; const RawPixel PREVIEW_ICON_SPLIT_OFFSCREEN_OFFSET = 10_em; const RawPixel PREVIEW_CONTAINER_TRIANGLE_WIDTH = 14_em; const RawPixel PREVIEW_CONTAINER_TRIANGLE_HEIGHT = 12_em; const int MAX_ENTRY_ACTIVATE_WAIT_TIMEOUT = 300; } // This is so we can access some protected members in nux::VLayout and // break the natural key navigation path. class DashLayout: public nux::VLayout { public: DashLayout(NUX_FILE_LINE_DECL) : nux::VLayout(NUX_FILE_LINE_PARAM) , area_(nullptr) {} void SetSpecialArea(nux::Area* area) { area_ = area; } protected: nux::Area* KeyNavIteration(nux::KeyNavDirection direction) { if (direction == nux::KEY_NAV_DOWN && area_ && area_->HasKeyFocus()) return nullptr; else return nux::VLayout::KeyNavIteration(direction); } private: nux::Area* area_; }; class DashContentView : public nux::View { public: DashContentView(NUX_FILE_LINE_DECL):View(NUX_FILE_LINE_PARAM) { SetRedirectRenderingToTexture(true); } void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) {} void DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { if (GetLayout()) GetLayout()->ProcessDraw(graphics_engine, force_draw); } }; NUX_IMPLEMENT_OBJECT_TYPE(DashView); DashView::DashView(Scopes::Ptr const& scopes, ApplicationStarter::Ptr const& application_starter) : nux::View(NUX_TRACKER_LOCATION) , scale(Settings::Instance().em()->DPIScale()) , scopes_(scopes) , application_starter_(application_starter) , preview_container_(nullptr) , preview_displaying_(false) , preview_navigation_mode_(previews::Navigation::NONE) , last_activated_timestamp_(0) , activate_on_finish_(false) , visible_(false) , neko_mode_(false) , opening_column_x_(-1) , opening_row_y_(-1) , opening_column_width_(0) , opening_row_height_(0) , animate_split_value_(0.0) , animate_preview_container_value_(0.0) , animate_preview_value_(0.0) , overlay_window_buttons_(new OverlayWindowButtons()) , monitor_(0) { renderer_.SetOwner(this); renderer_.owner_type = OverlayOwner::Dash; renderer_.need_redraw.connect([this] () { QueueDraw(); }); gsize tmp_sz; glib::String neko((gchar*)g_base64_decode("VU5JVFlfTkVLTw==", &tmp_sz)); neko_mode_ = g_getenv(neko) != nullptr; SetupViews(); SetupUBusConnections(); AddChild(overlay_window_buttons_.GetPointer()); preview_state_machine_.PreviewActivated.connect(sigc::mem_fun(this, &DashView::BuildPreview)); if (scopes_) { scopes_->scope_added.connect(sigc::mem_fun(this, &DashView::OnScopeAdded)); scopes_->LoadScopes(); } // If nux resets the focus, we need to set it back to the default focus item. key_nav_focus_change_connection_ = nux::GetWindowCompositor().key_nav_focus_change.connect([this](nux::Area *area, bool has_focus, nux::KeyNavDirection direction) { if (visible_ && !area) nux::GetWindowCompositor().SetKeyFocusArea(default_focus()); }); unity::Settings::Instance().dpi_changed.connect(sigc::mem_fun(this, &DashView::OnDPIChanged)); scale.changed.connect(sigc::mem_fun(this, &DashView::UpdateScale)); UpdateScale(scale()); } DashView::~DashView() { // Do this explicitely, otherwise dee will complain about invalid access // to the scope models RemoveLayout(); } void DashView::SetMonitor(int monitor) { if (monitor_== monitor) return; monitor_ = monitor; scale = Settings::Instance().em(monitor_)->DPIScale(); } void DashView::SetMonitorOffset(int x, int y) { renderer_.x_offset = x; renderer_.y_offset = y; top_space_->SetMinMaxSize(0, y); } bool DashView::IsCommandLensOpen() const { return (scope_bar_->GetActiveScopeId() == "commands.scope"); } void DashView::OnResultActivated(ResultView::ActivateType type, LocalResult const& local_result, GVariant* data, std::string const& unique_id) { last_activated_result_ = local_result; stored_activated_unique_id_ = unique_id; if (data) { // Update positioning information. int column_x = -1; int row_y = -1; int column_width = 0; int row_height = 0; int results_to_the_left = 0; int results_to_the_right = 0; g_variant_get(data, "(tiiiiii)", &last_activated_timestamp_, &column_x, &row_y, &column_width, &row_height, &results_to_the_left, &results_to_the_right); preview_state_machine_.SetSplitPosition(SplitPosition::CONTENT_AREA, row_y); preview_state_machine_.left_results = results_to_the_left; preview_state_machine_.right_results = results_to_the_right; opening_column_x_ = column_x; opening_row_y_ = row_y; opening_column_width_ = column_width; opening_row_height_ = row_height; } // we want immediate preview reaction on first opening. if (type == ResultView::ActivateType::PREVIEW && !preview_displaying_) { BuildPreview(Preview::Ptr(nullptr)); } } void DashView::BuildPreview(Preview::Ptr model) { if (!preview_displaying_) { StartPreviewAnimation(); content_view_->SetPresentRedirectedView(false); preview_scope_view_ = active_scope_view_; if (preview_scope_view_) { preview_scope_view_->ForceCategoryExpansion(stored_activated_unique_id_, true); preview_scope_view_->EnableResultTextures(true); preview_scope_view_->PushFilterExpansion(false); } if (!preview_container_) { preview_container_ = new previews::PreviewContainer(); preview_container_->SetRedirectRenderingToTexture(true); AddChild(preview_container_.GetPointer()); preview_container_->SetParentObject(this); } preview_container_->Preview(model, previews::Navigation::NONE); // no swipe left or right preview_container_->scale = scale(); preview_container_->SetGeometry(scopes_layout_->GetGeometry()); preview_displaying_ = true; // connect to nav left/right signals to request nav left/right movement. preview_container_->navigate_left.connect([this] () { preview_navigation_mode_ = previews::Navigation::LEFT; // sends a message to all result views, sending the the uri of the current preview result // and the unique id of the result view that should be handling the results ubus_manager_.SendMessage(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, g_variant_new("(ivs)", -1, g_variant_ref(last_activated_result_.Variant()), stored_activated_unique_id_.c_str())); }); preview_container_->navigate_right.connect([this] () { preview_navigation_mode_ = previews::Navigation::RIGHT; // sends a message to all result views, sending the the uri of the current preview result // and the unique id of the result view that should be handling the results ubus_manager_.SendMessage(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, g_variant_new("(ivs)", 1, g_variant_ref(last_activated_result_.Variant()), stored_activated_unique_id_.c_str())); }); preview_container_->request_close.connect([this] () { ClosePreview(); }); } else { // got a new preview whilst already displaying, we probably clicked a navigation button. preview_container_->Preview(model, preview_navigation_mode_); // TODO preview_container_->scale = scale(); } if (G_LIKELY(preview_state_machine_.left_results() > 0 && preview_state_machine_.right_results() > 0)) preview_container_->DisableNavButton(previews::Navigation::NONE); else if (preview_state_machine_.left_results() > 0) preview_container_->DisableNavButton(previews::Navigation::RIGHT); else if (preview_state_machine_.right_results() > 0) preview_container_->DisableNavButton(previews::Navigation::LEFT); else preview_container_->DisableNavButton(previews::Navigation::BOTH); QueueDraw(); } void DashView::ClosePreview() { if (preview_displaying_) { EndPreviewAnimation(); preview_displaying_ = false; } preview_navigation_mode_ = previews::Navigation::NONE; // re-focus dash view component. nux::GetWindowCompositor().SetKeyFocusArea(default_focus()); QueueDraw(); } void DashView::StartPreviewAnimation() { // We use linear animations so we can easily control when the next animation in the sequence starts. // The animation curve is caluclated separately. preview_animation_.reset(); preview_container_animation_.reset(); double anim_length = Settings::Instance().low_gfx() ? 0 : PREVIEW_ANIMATION_LENGTH; // Dash Split Open Animation split_animation_.reset(new na::AnimateValue()); split_animation_->SetDuration((1.0f - animate_split_value_) * anim_length); split_animation_->SetStartValue(animate_split_value_); split_animation_->SetFinishValue(1.0f); split_animation_->SetEasingCurve(na::EasingCurve(na::EasingCurve::Type::Linear)); split_animation_->updated.connect([this, anim_length](float const& linear_split_animate_value) { static na::EasingCurve split_animation_curve(na::EasingCurve::Type::InQuad); animate_split_value_ = split_animation_curve.ValueForProgress(linear_split_animate_value); QueueDraw(); // time to start the preview container animation? if (linear_split_animate_value >= 0.5f && !preview_container_animation_) { // Preview Container Close Animation preview_container_animation_.reset(new na::AnimateValue()); preview_container_animation_->SetDuration((1.0f - animate_preview_container_value_) * anim_length); preview_container_animation_->SetStartValue(animate_preview_container_value_); preview_container_animation_->SetFinishValue(1.0f); preview_container_animation_->SetEasingCurve(na::EasingCurve(na::EasingCurve::Type::Linear)); preview_container_animation_->updated.connect([this, anim_length](float const& linear_preview_container_animate_value) { static na::EasingCurve preview_container_animation_curve(na::EasingCurve::Type::InQuad); animate_preview_container_value_ = preview_container_animation_curve.ValueForProgress(linear_preview_container_animate_value); QueueDraw(); // time to start the preview animation? if (linear_preview_container_animate_value >= 0.9f && !preview_animation_) { // Preview Close Animation preview_animation_.reset(new na::AnimateValue()); preview_animation_->SetDuration((1.0f - animate_preview_value_) * anim_length); preview_animation_->SetStartValue(animate_preview_value_); preview_animation_->SetFinishValue(1.0f); preview_animation_->SetEasingCurve(na::EasingCurve(na::EasingCurve::Type::Linear)); preview_animation_->updated.connect([this](float const& linear_preview_animate_value) { animate_preview_value_ = linear_preview_animate_value; QueueDraw(); }); preview_animation_->finished.connect(sigc::mem_fun(this, &DashView::OnPreviewAnimationFinished)); preview_animation_->Start(); } }); preview_container_animation_->finished.connect(sigc::mem_fun(this, &DashView::OnPreviewAnimationFinished)); preview_container_animation_->Start(); } if (preview_scope_view_) preview_scope_view_->SetResultsPreviewAnimationValue(animate_split_value_); }); split_animation_->finished.connect(sigc::mem_fun(this, &DashView::OnPreviewAnimationFinished)); split_animation_->Start(); } void DashView::EndPreviewAnimation() { // We use linear animations so we can easily control when the next animation in the sequence starts. // The animation curve is caluclated separately. split_animation_.reset(); preview_container_animation_.reset(); double anim_length = Settings::Instance().low_gfx() ? 0 : PREVIEW_ANIMATION_LENGTH; // Preview Close Animation preview_animation_.reset(new na::AnimateValue()); preview_animation_->SetDuration(animate_preview_value_ * anim_length); preview_animation_->SetStartValue(1.0f - animate_preview_value_); preview_animation_->SetFinishValue(1.0f); preview_animation_->SetEasingCurve(na::EasingCurve(na::EasingCurve::Type::Linear)); preview_animation_->updated.connect([this, anim_length](float const& preview_value) { animate_preview_value_ = 1.0f - preview_value; QueueDraw(); // time to stop the split? if (preview_value >= 0.9f && !preview_container_animation_) { // Preview Container Close Animation preview_container_animation_.reset(new na::AnimateValue()); preview_container_animation_->SetDuration(animate_preview_container_value_ * anim_length); preview_container_animation_->SetStartValue(1.0f - animate_preview_container_value_); preview_container_animation_->SetFinishValue(1.0f); preview_container_animation_->SetEasingCurve(na::EasingCurve(na::EasingCurve::Type::Linear)); preview_container_animation_->updated.connect([this, anim_length](float const& linear_preview_container_animate_value) { static na::EasingCurve preview_container_animation_curve(na::EasingCurve::Type::InQuad); animate_preview_container_value_ = 1.0f - preview_container_animation_curve.ValueForProgress(linear_preview_container_animate_value); QueueDraw(); if (linear_preview_container_animate_value >= 0.5f && !split_animation_) { // Dash Split Close Animation split_animation_.reset(new na::AnimateValue()); split_animation_->SetDuration(animate_split_value_ * anim_length); split_animation_->SetStartValue(1.0f - animate_split_value_); split_animation_->SetFinishValue(1.0f); split_animation_->SetEasingCurve(na::EasingCurve(na::EasingCurve::Type::Linear)); split_animation_->updated.connect([this](float const& linear_split_animate_value) { static na::EasingCurve split_animation_curve(na::EasingCurve::Type::InQuad); animate_split_value_ = 1.0f - split_animation_curve.ValueForProgress(linear_split_animate_value); QueueDraw(); }); split_animation_->finished.connect(sigc::mem_fun(this, &DashView::OnPreviewAnimationFinished)); split_animation_->Start(); // if (preview_scope_view_) // preview_scope_view_->PopFilterExpansion(); } }); preview_container_animation_->finished.connect(sigc::mem_fun(this, &DashView::OnPreviewAnimationFinished)); preview_container_animation_->Start(); } }); preview_animation_->finished.connect(sigc::mem_fun(this, &DashView::OnPreviewAnimationFinished)); preview_animation_->Start(); } void DashView::OnPreviewAnimationFinished() { if (animate_preview_value_ != 0.0f || animate_preview_container_value_ != 0.0f || animate_split_value_ != 0.0f) return; // preview close finished. if (preview_container_) { RemoveChild(preview_container_.GetPointer()); preview_container_->UnParentObject(); preview_container_.Release(); // free resources preview_state_machine_.ClosePreview(); QueueDraw(); } // reset the saturation. if (preview_scope_view_.IsValid()) { preview_scope_view_->SetResultsPreviewAnimationValue(0.0); preview_scope_view_->ForceCategoryExpansion(stored_activated_unique_id_, false); preview_scope_view_->EnableResultTextures(false); preview_scope_view_->PopFilterExpansion(); } preview_scope_view_.Release(); content_view_->SetPresentRedirectedView(true); } void DashView::AboutToShow() { visible_ = true; search_bar_->text_entry()->SelectAll(); /* Give the scopes a chance to prep data before we map them */ if (active_scope_view_) { scope_bar_->Activate(active_scope_view_->scope()->id()); active_scope_view_->SetVisible(true); active_scope_view_->scope()->view_type = ScopeViewType::SCOPE_VIEW; // this will make sure the spinner animates if the search takes a while search_bar_->ForceLiveSearch(); search_bar_->search_hint = active_scope_view_->scope()->search_hint; } // if a preview is open, close it if (preview_displaying_) { ClosePreview(); } overlay_window_buttons_->Show(); renderer_.scale = scale(); renderer_.UpdateBlurBackgroundSize(content_geo_, GetRenderAbsoluteGeometry(), false); renderer_.AboutToShow(); } void DashView::AboutToHide() { if (BackgroundEffectHelper::blur_type == BLUR_STATIC) { content_geo_ = {0, 0, 0, 0}; renderer_.UpdateBlurBackgroundSize(content_geo_, GetRenderAbsoluteGeometry(), false); } visible_ = false; renderer_.AboutToHide(); if (scopes_) { for (auto scope : scopes_->GetScopes()) { scope->view_type = ScopeViewType::HIDDEN; LOG_DEBUG(logger) << "Setting ViewType " << ScopeViewType::HIDDEN << " on '" << scope->id() << "'"; } } if (active_scope_view_.IsValid()) active_scope_view_->SetVisible(false); // if a preview is open, close it if (preview_displaying_) { ClosePreview(); } overlay_window_buttons_->Hide(); } void DashView::SetupViews() { layout_ = new nux::VLayout(); SetLayout(layout_); top_space_ = new nux::SpaceLayout(0, 0, renderer_.y_offset(), renderer_.y_offset()); layout_->AddLayout(top_space_, 0); content_layout_ = new DashLayout(NUX_TRACKER_LOCATION); content_view_ = new DashContentView(NUX_TRACKER_LOCATION); content_view_->SetLayout(content_layout_); layout_->AddView(content_view_, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); search_bar_layout_ = new nux::HLayout(); content_layout_->AddLayout(search_bar_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); search_bar_ = new SearchBar(true); search_bar_->scale = scale(); AddChild(search_bar_); search_bar_->activated.connect(sigc::mem_fun(this, &DashView::OnEntryActivated)); search_bar_->search_changed.connect(sigc::mem_fun(this, &DashView::OnSearchChanged)); search_bar_->live_search_reached.connect(sigc::mem_fun(this, &DashView::OnLiveSearchReached)); search_bar_->showing_filters.changed.connect([this] (bool showing) { if (active_scope_view_) { active_scope_view_->filters_expanded = showing; QueueDraw(); } }); search_bar_layout_->AddView(search_bar_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); content_layout_->SetSpecialArea(search_bar_->show_filters()); scopes_layout_ = new nux::VLayout(); content_layout_->AddLayout(scopes_layout_, 1, nux::MINOR_POSITION_START); scope_bar_ = new ScopeBar(); AddChild(scope_bar_); scope_bar_->scope_activated.connect(sigc::mem_fun(this, &DashView::OnScopeBarActivated)); content_layout_->AddView(scope_bar_, 0, nux::MINOR_POSITION_CENTER); OnDPIChanged(); } void DashView::OnDPIChanged() { scale = Settings::Instance().em(monitor_)->DPIScale(); } void DashView::UpdateScale(double scale) { UpdateDashViewSize(); for (auto& scope : scope_views_) scope.second->scale = scale; search_bar_->scale = scale; scope_bar_->scale = scale; renderer_.scale = scale; if (preview_container_) preview_container_->scale = scale; Relayout(); } void DashView::UpdateDashViewSize() { dash::Style const& style = dash::Style::Instance(); layout_->SetLeftAndRightPadding(style.GetVSeparatorSize().CP(scale), 0); layout_->SetTopAndBottomPadding(style.GetHSeparatorSize().CP(scale), 0); content_layout_->SetTopAndBottomPadding(style.GetDashViewTopPadding().CP(scale), 0); search_bar_layout_->SetLeftAndRightPadding(style.GetSearchBarLeftPadding().CP(scale), 0); } void DashView::SetupUBusConnections() { ubus_manager_.RegisterInterest(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, sigc::mem_fun(this, &DashView::OnActivateRequest)); } long DashView::PostLayoutManagement(long LayoutResult) { Relayout(); return LayoutResult; } void DashView::Relayout() { nux::Geometry const& geo = GetGeometry(); content_geo_ = GetBestFitGeometry(geo); dash::Style& style = dash::Style::Instance(); int top_padding = style.GetDashViewTopPadding().CP(scale); // kinda hacky, but it makes sure the content isn't so big that it throws // the bottom of the dash off the screen // not hugely happy with this, so FIXME scopes_layout_->SetMaximumHeight (std::max(0, content_geo_.height - search_bar_->GetGeometry().height - scope_bar_->GetGeometry().height - top_padding)); scopes_layout_->SetMinimumHeight (std::max(0, content_geo_.height - search_bar_->GetGeometry().height - scope_bar_->GetGeometry().height - top_padding)); layout_->SetMinMaxSize(content_geo_.width, content_geo_.y + content_geo_.height); // Minus the padding that gets added to the left style.columns_number = floorf((content_geo_.width - (32_em).CP(scale)) / style.GetTileWidth().CP(scale)); ubus_manager_.SendMessage(UBUS_DASH_SIZE_CHANGED, g_variant_new("(ii)", content_geo_.width, content_geo_.height)); if (preview_displaying_) { if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) { auto preview_geo = content_geo_; int padding = style.GetDashHorizontalBorderHeight().CP(scale()); preview_geo.y += padding; preview_geo.height -= padding; preview_container_->SetGeometry(preview_geo); } else { preview_container_->SetGeometry(layout_->GetGeometry()); } } renderer_.UpdateBlurBackgroundSize(content_geo_, GetRenderAbsoluteGeometry(), false); QueueDraw(); } // Gives us the width and height of the contents that will give us the best "fit", // which means that the icons/views will not have unnecessary padding, everything will // look tight nux::Geometry DashView::GetBestFitGeometry(nux::Geometry const& for_geo) { dash::Style& style = dash::Style::Instance(); int vertical_offset = renderer_.y_offset; if (style.always_maximised) { return nux::Geometry(0, vertical_offset, for_geo.width, for_geo.height - vertical_offset); } int width = 0, height = 0; int tile_width = style.GetTileWidth().CP(scale); int category_height = (style.GetPlacesGroupTopSpace().CP(scale) + style.GetCategoryIconSize().CP(scale) + style.GetPlacesGroupResultTopPadding().CP(scale) + style.GetTileHeight().CP(scale)); int half = for_geo.width / 2; // if default dash size is bigger than half a screens worth of items, go for that. while ((width += tile_width) < half); width = std::max(width, tile_width * DASH_TILE_HORIZONTAL_COUNT); width += style.GetVSeparatorSize().CP(scale); width += style.GetPlacesGroupResultLeftPadding().CP(scale) + DASH_RESULT_RIGHT_PAD.CP(scale); height = style.GetHSeparatorSize().CP(scale); height += style.GetDashViewTopPadding().CP(scale); height += search_bar_->GetGeometry().height; height += category_height * DASH_DEFAULT_CATEGORY_COUNT; // adding three categories height += scope_bar_->GetGeometry().height; // width/height shouldn't be bigger than the geo available. width = std::min(width, for_geo.width); // launcher width is taken into account in for_geo. height = std::min(height, for_geo.height - vertical_offset); // panel height is not taken into account in for_geo. return nux::Geometry(0, vertical_offset, width, height); } void DashView::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& renderer_geo_abs(GetRenderAbsoluteGeometry()); nux::Geometry renderer_geo(GetGeometry()); renderer_geo.y += renderer_.y_offset; renderer_geo.height += renderer_.y_offset; renderer_.DrawFull(graphics_engine, content_geo_, renderer_geo_abs, renderer_geo, false); } void DashView::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { int renderer_y_offset = renderer_.y_offset(); nux::Geometry renderer_geo_abs(GetAbsoluteGeometry()); renderer_geo_abs.y += renderer_y_offset; renderer_geo_abs.height -= renderer_y_offset; nux::Geometry renderer_geo(GetGeometry()); renderer_geo.y += renderer_y_offset; renderer_geo.height += renderer_y_offset; renderer_.DrawInner(graphics_engine, content_geo_, renderer_geo_abs, renderer_geo); nux::Geometry const& geo_layout(layout_->GetGeometry()); // See lp bug: 1125346 (The sharp white line between dash and launcher is missing) nux::Geometry clip_geo = geo_layout; clip_geo.x += 1; if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) clip_geo.y += renderer_y_offset; graphics_engine.PushClippingRectangle(clip_geo); if (IsFullRedraw()) { nux::GetPainter().PushBackgroundStack(); } else { nux::GetPainter().PaintBackground(graphics_engine, geo_layout); } if (preview_container_.IsValid()) { nux::Geometry geo_split_clip; DrawDashSplit(graphics_engine, geo_split_clip); graphics_engine.PushClippingRectangle(geo_split_clip); if (preview_scope_view_.IsValid()) { DrawPreviewResultTextures(graphics_engine, force_draw); } DrawPreviewContainer(graphics_engine); // preview always on top. DrawPreview(graphics_engine, force_draw); graphics_engine.PopClippingRectangle(); } else { layout_->ProcessDraw(graphics_engine, force_draw); } if (IsFullRedraw()) { nux::GetPainter().PopBackgroundStack(); } graphics_engine.PopClippingRectangle(); renderer_.DrawInnerCleanup(graphics_engine, content_geo_, renderer_geo_abs, renderer_geo); } void DashView::DrawDashSplit(nux::GraphicsEngine& graphics_engine, nux::Geometry& split_clip) { nux::Geometry const& geo_layout(layout_->GetGeometry()); split_clip = geo_layout; if (animate_split_value_ == 1.0) return; // if we're not presenting, then we must be manually rendering it's backup texture. if (!content_view_->PresentRedirectedView() && content_view_->BackupTexture().IsValid()) { unsigned int alpha, src, dest = 0; graphics_engine.GetRenderStates().GetBlend(alpha, src, dest); graphics_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::TexCoordXForm texxform; texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); texxform.FlipVCoord(true); // Scope Bar texxform.uoffset = (scope_bar_->GetX() - content_view_->GetX())/(float)content_view_->GetWidth(); texxform.voffset = (scope_bar_->GetY() - content_view_->GetY())/(float)content_view_->GetHeight(); int start_y = scope_bar_->GetY(); int final_y = geo_layout.y + geo_layout.height + PREVIEW_ICON_SPLIT_OFFSCREEN_OFFSET.CP(scale); int scope_y = (1.0f - animate_split_value_) * start_y + (animate_split_value_ * final_y); graphics_engine.QRP_1Tex ( scope_bar_->GetX(), scope_y, scope_bar_->GetWidth(), scope_bar_->GetHeight(), content_view_->BackupTexture(), texxform, nux::Color((1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_)) ); split_clip.height = std::min(scope_y, geo_layout.height); if (active_scope_view_ && active_scope_view_->GetPushedFilterExpansion()) { // Search Bar texxform.uoffset = (search_bar_->GetX() - content_view_->GetX())/(float)content_view_->GetWidth(); texxform.voffset = (search_bar_->GetY() - content_view_->GetY())/(float)content_view_->GetHeight(); start_y = search_bar_->GetY(); final_y = geo_layout.y - search_bar_->GetHeight() - PREVIEW_ICON_SPLIT_OFFSCREEN_OFFSET.CP(scale); graphics_engine.QRP_1Tex ( search_bar_->GetX(), (1.0f - animate_split_value_) * start_y + (animate_split_value_ * final_y), search_bar_->GetWidth() - active_scope_view_->filter_bar()->GetWidth(), search_bar_->GetHeight(), content_view_->BackupTexture(), texxform, nux::Color((1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_)) ); // Filter Bar texxform.uoffset = (active_scope_view_->filter_bar()->GetX() -content_view_->GetX())/(float)content_view_->GetWidth(); texxform.voffset = (search_bar_->GetY() - content_view_->GetY())/(float)content_view_->GetHeight(); int start_x = active_scope_view_->filter_bar()->GetX(); int final_x = content_view_->GetX() + content_view_->GetWidth() + PREVIEW_ICON_SPLIT_OFFSCREEN_OFFSET.CP(scale); int filter_x = (1.0f - animate_split_value_) * start_x + (animate_split_value_ * final_x); graphics_engine.QRP_1Tex ( filter_x, search_bar_->GetY(), active_scope_view_->filter_bar()->GetWidth(), active_scope_view_->filter_bar()->GetY() + active_scope_view_->filter_bar()->GetHeight(), content_view_->BackupTexture(), texxform, nux::Color((1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_)) ); split_clip.width = filter_x; } else { // Search Bar texxform.uoffset = (search_bar_->GetX() - content_view_->GetX())/(float)content_view_->GetWidth(); texxform.voffset = (search_bar_->GetY() - content_view_->GetY())/(float)content_view_->GetHeight(); int start_y = search_bar_->GetY(); int final_y = geo_layout.y - search_bar_->GetHeight() - PREVIEW_ICON_SPLIT_OFFSCREEN_OFFSET.CP(scale); graphics_engine.QRP_1Tex ( search_bar_->GetX(), (1.0f - animate_split_value_) * start_y + (animate_split_value_ * final_y), search_bar_->GetWidth(), search_bar_->GetHeight(), content_view_->BackupTexture(), texxform, nux::Color((1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_), (1.0-animate_split_value_)) ); } graphics_engine.GetRenderStates().SetBlend(alpha, src, dest); } } void DashView::DrawPreviewContainer(nux::GraphicsEngine& graphics_engine) { if (animate_preview_container_value_ == 0.0f) return; nux::Geometry const& geo_content = content_view_->GetGeometry(); nux::Geometry geo_abs = GetAbsoluteGeometry(); nux::Geometry geo_abs_preview = preview_container_->GetLayoutGeometry(); unsigned int alpha, src, dest = 0; graphics_engine.GetRenderStates().GetBlend(alpha, src, dest); graphics_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Triangle pointed at preview item if (opening_column_x_ != -1) { int final_width = PREVIEW_CONTAINER_TRIANGLE_WIDTH.CP(scale); int final_height = PREVIEW_CONTAINER_TRIANGLE_HEIGHT.CP(scale); int x_center = geo_content.x + (opening_column_x_ - geo_abs.x) + opening_column_width_ / 2; int start_y = geo_abs_preview.y - geo_abs.y; int x1 = x_center - final_width/2; int final_y1 = start_y; int x2 = x_center + final_width/2; int final_y2 = start_y; int x3 = x_center; int final_y3 = start_y - final_height; graphics_engine.QRP_Triangle ( x1, (1.0f-animate_preview_container_value_) * start_y + (animate_preview_container_value_ * final_y1), x2, (1.0f-animate_preview_container_value_) * start_y + (animate_preview_container_value_ * final_y2), x3, (1.0f-animate_preview_container_value_) * start_y + (animate_preview_container_value_ * final_y3), nux::Color(0.0f, 0.0f, 0.0f, 0.10f) ); } // Preview Background int start_width = geo_content.width; int start_x = geo_content.x; int start_y = geo_abs_preview.y - geo_abs.y; int final_x = geo_content.x; int final_y = start_y; int final_width = geo_content.width; int final_height = geo_abs_preview.height; graphics_engine.QRP_Color ( (1.0f-animate_preview_container_value_) * start_x + (animate_preview_container_value_ * final_x), (1.0f-animate_preview_container_value_) * start_y + (animate_preview_container_value_ * final_y), (1.0f-animate_preview_container_value_) * start_width + (animate_preview_container_value_ * final_width), (animate_preview_container_value_ * final_height), nux::Color(0.0f, 0.0f, 0.0f, 0.10f) ); graphics_engine.GetRenderStates().SetBlend(alpha, src, dest); } void DashView::DrawPreviewResultTextures(nux::GraphicsEngine& graphics_engine, bool /*force_draw*/) { nux::Geometry const& geo_layout = layout_->GetGeometry(); nux::Geometry geo_abs = GetAbsoluteGeometry(); nux::Geometry geo_abs_preview = preview_container_->GetLayoutGeometry(); unsigned int alpha, src, dest = 0; graphics_engine.GetRenderStates().GetBlend(alpha, src, dest); graphics_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::TexCoordXForm texxform; texxform.FlipVCoord(true); texxform.uoffset = 0.0f; texxform.voffset = 0.0f; texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); std::vector result_textures = preview_scope_view_->GetResultTextureContainers(); std::vector top_textures; int height_concat_below = 0; // int i = 0; for (auto it = result_textures.begin(); it != result_textures.end(); ++it) { ResultViewTexture::Ptr const& result_texture = *it; if (!result_texture) continue; // split above. if (result_texture->abs_geo.y <= opening_row_y_) { top_textures.push_back(result_texture); continue; } // Bottom textures. int start_x = result_texture->abs_geo.x - geo_abs.x; int final_x = start_x; int start_y = result_texture->abs_geo.y - geo_abs.y; int final_y = geo_abs_preview.y + geo_abs_preview.height - geo_abs.y; final_y += std::min(60, ((geo_layout.y + geo_layout.height) - final_y)/4); final_y += height_concat_below; nux::Geometry geo_tex_top((1.0f-animate_split_value_) * start_x + (animate_split_value_ * final_x), (1.0f-animate_split_value_) * start_y + (animate_split_value_ * final_y), result_texture->abs_geo.width, result_texture->abs_geo.height); height_concat_below += geo_tex_top.height; // off the bottom if (geo_tex_top.y <= geo_layout.y + geo_layout.height) { preview_scope_view_->RenderResultTexture(result_texture); // If we haven't got it now, we're not going to get it if (!result_texture->texture.IsValid()) continue; graphics_engine.QRP_1Tex ( geo_tex_top.x, geo_tex_top.y, geo_tex_top.width, geo_tex_top.height, result_texture->texture, texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f) ); } } // Top Textures (in reverse) int height_concat_above = 0; for (auto it = top_textures.rbegin(); it != top_textures.rend(); ++it) { ResultViewTexture::Ptr const& result_texture = *it; // each texture starts at a higher position. height_concat_above += result_texture->abs_geo.height; int start_x = result_texture->abs_geo.x - geo_abs.x; int final_x = start_x; int start_y = result_texture->abs_geo.y - geo_abs.y; int final_y = -height_concat_above + (geo_abs_preview.y - geo_abs.y); nux::Geometry geo_tex_top((1.0f-animate_split_value_) * start_x + (animate_split_value_ * final_x), (1.0f-animate_split_value_) * start_y + (animate_split_value_ * final_y), result_texture->abs_geo.width, result_texture->abs_geo.height); // off the top if (geo_tex_top.y + geo_tex_top.height >= geo_layout.y) { preview_scope_view_->RenderResultTexture(result_texture); // If we haven't got it now, we're not going to get it if (!result_texture->texture.IsValid()) continue; graphics_engine.QRP_1Tex ( geo_tex_top.x, geo_tex_top.y, geo_tex_top.width, geo_tex_top.height, result_texture->texture, texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f) ); } } graphics_engine.GetRenderStates().SetBlend(alpha, src, dest); } void DashView::DrawPreview(nux::GraphicsEngine& graphics_engine, bool force_draw) { if (animate_preview_value_ > 0.0f) { bool animating = animate_split_value_ != 1.0f || animate_preview_value_ < 1.0f; bool preview_force_draw = force_draw || animating || IsFullRedraw(); if (preview_force_draw) nux::GetPainter().PushBackgroundStack(); if (animate_preview_value_ < 1.0f && preview_container_->RedirectRenderingToTexture()) { preview_container_->SetPresentRedirectedView(false); preview_container_->ProcessDraw(graphics_engine, preview_force_draw); unsigned int alpha, src, dest = 0; graphics_engine.GetRenderStates().GetBlend(alpha, src, dest); graphics_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::ObjectPtr preview_texture = preview_container_->BackupTexture(); if (preview_texture) { nux::TexCoordXForm texxform; texxform.FlipVCoord(true); texxform.uoffset = 0.0f; texxform.voffset = 0.0f; texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); nux::Geometry const& geo_preview = preview_container_->GetGeometry(); graphics_engine.QRP_1Tex ( geo_preview.x, geo_preview.y, geo_preview.width, geo_preview.height, preview_texture, texxform, nux::Color(animate_preview_value_, animate_preview_value_, animate_preview_value_, animate_preview_value_) ); } preview_container_->SetPresentRedirectedView(true); graphics_engine.GetRenderStates().SetBlend(alpha, src, dest); } else { preview_container_->ProcessDraw(graphics_engine, preview_force_draw); } if (preview_force_draw) nux::GetPainter().PopBackgroundStack(); } } void DashView::OnActivateRequest(GVariant* args) { glib::String uri; glib::String search_string; ScopeHandledType handled_type; g_variant_get(args, "(sus)", &uri, &handled_type, &search_string); std::string id(AnalyseScopeURI(uri.Str())); LOG_DEBUG(logger) << "External activation request: " << id << " (uri: "<< uri.Str() << ")"; // we got an activation request, we should probably close the preview if (preview_displaying_) { ClosePreview(); } if (visible_ && handled_type == ScopeHandledType::NOT_HANDLED) { ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST, NULL, glib::Source::Priority::HIGH); } else if (!visible_ || handled_type == ScopeHandledType::GOTO_DASH_URI) { if (!scopes_->GetScope(id)) { // should trigger the addition of the scope. scopes_->AppendScope(id); } scope_bar_->Activate(id); if (!visible_) ubus_manager_.SendMessage(UBUS_DASH_EXTERNAL_ACTIVATION); } } std::string DashView::AnalyseScopeURI(std::string const& uri) { impl::ScopeFilter filter = impl::parse_scope_uri(uri); if (!filter.filters.empty()) { if (scope_views_.find(filter.id) != std::end(scope_views_)) scope_views_[filter.id]->filters_expanded = true; // update the scope for each filter for (auto p : filter.filters) { UpdateScopeFilter(filter.id, p.first, p.second); } } return filter.id; } void DashView::UpdateScopeFilter(std::string scope_id, std::string filter_name, std::string value) { if (scopes_ && scopes_->GetScope(scope_id)) { Scope::Ptr scope = scopes_->GetScope(scope_id); Filters::Ptr filters = scope->filters; for (unsigned int i = 0; i < filters->count(); ++i) { Filter::Ptr filter = filters->FilterAtIndex(i); if (filter->id() == filter_name) { UpdateScopeFilterValue(filter, value); } } } } void DashView::UpdateScopeFilterValue(Filter::Ptr filter, std::string value) { if (filter->renderer_name == "filter-radiooption") { RadioOptionFilter::Ptr radio = std::static_pointer_cast(filter); for (auto option: radio->options()) { if (option->id == value) option->active = true; } } } void DashView::OnSearchChanged(std::string const& search_string) { activate_on_finish_ = false; } void DashView::OnLiveSearchReached(std::string const& search_string) { LOG_DEBUG(logger) << "Live search reached: " << search_string; if (!active_scope_view_.IsValid()) return; if (active_scope_view_->PerformSearch(search_string, sigc::mem_fun(this, &DashView::OnScopeSearchFinished))) { activate_delay_.reset(new glib::Timeout(MAX_ENTRY_ACTIVATE_WAIT_TIMEOUT, [this] { if (activate_on_finish_) { activate_on_finish_ = false; active_scope_view_->ActivateFirst(); } activate_delay_.reset(); return false; })); } } void DashView::OnScopeSearchFinished(std::string const& scope_id, std::string const& search_string, glib::Error const& err) { // match active scope? auto scope_pos = scope_views_.find(scope_id); if (scope_pos == scope_views_.end() || scope_pos->second != active_scope_view_) return; if (search_string == search_bar_->search_string) { if (err) LOG_WARNING(logger) << "Search failed '"<< search_string <<"'=> " << err; else LOG_DEBUG(logger) << "Search completed: " << search_string; search_bar_->SetSearchFinished(); if (activate_on_finish_) { activate_on_finish_ = false; activate_delay_.reset(); if (!err) active_scope_view_->ActivateFirst(); } } } void DashView::OnScopeAdded(Scope::Ptr const& scope, int position) { LOG_DEBUG(logger) << "Scope Added: " << scope->id(); scope_bar_->AddScope(scope); nux::ObjectPtr view(new ScopeView(scope, search_bar_->show_filters())); AddChild(view.GetPointer()); view->scale = scale(); view->neko_mode = neko_mode_; view->SetVisible(false); view->result_activated.connect(sigc::mem_fun(this, &DashView::OnResultActivated)); scopes_layout_->AddView(view.GetPointer(), 1); scope_views_[scope->id] = view; // set form factor used for the searches scope->form_factor = "desktop"; scope->activated.connect(sigc::mem_fun(this, &DashView::OnResultActivatedReply)); scope->connected.changed.connect([this] (bool value) { }); // Hook up to the new preview infrastructure scope->preview_ready.connect([this] (LocalResult const& result, Preview::Ptr model) { // HACK: Atm we don't support well the fact that a preview can be sent from // an ActionResponse and therefore transition does not work, this hack allows // to set the navigation mode to ensure that we have a nice transition if (dynamic_cast(model.get()) != NULL) { preview_state_machine_.left_results.Set(0); preview_state_machine_.right_results.Set(0); preview_navigation_mode_ = previews::Navigation::RIGHT; } preview_state_machine_.ActivatePreview(model); // this does not immediately display a preview - we now wait. }); } void DashView::OnScopeBarActivated(std::string const& id) { if (scope_views_.find(id) == scope_views_.end()) { LOG_WARN(logger) << "Unable to find Scope " << id; return; } if (IsCommandLensOpen() && scope_bar_->IsVisible()) { scope_bar_->SetVisible(false); } else if (!scope_bar_->IsVisible()) { scope_bar_->SetVisible(true); } if (active_scope_view_.IsValid()) active_scope_view_->SetVisible(false); nux::ObjectPtr view = active_scope_view_ = scope_views_[id]; view->SetVisible(true); view->AboutToShow(); for (auto it: scope_views_) { bool id_matches = it.first == id; ScopeViewType view_type = id_matches ? ScopeViewType::SCOPE_VIEW : ScopeViewType::HIDDEN; it.second->SetVisible(id_matches); it.second->view_type = view_type; LOG_DEBUG(logger) << "Setting ViewType " << view_type << " on '" << it.first << "'"; } search_bar_->SetVisible(true); QueueRelayout(); search_bar_->search_string = view->search_string; search_bar_->search_hint = view->scope()->search_hint; search_bar_->showing_filters = view->filters_expanded(); search_bar_->ForceLiveSearch(); search_bar_->text_entry()->SelectAll(); search_bar_->can_refine_search = view->can_refine_search(); scope_can_refine_connection_ = view->can_refine_search.changed.connect([this] (bool can_refine_search) { search_bar_->can_refine_search = can_refine_search; }); // Fix for a nux quirk. Unparented objects cannot have focus because there will be a break in the // parent tree and the compositor will not be able to walk the focus tree. This still needs to be here // for when scopes switch via the scope bar and the focus nees to move away from the scope bar to the search bar.. if (GetParentObject()) nux::GetWindowCompositor().SetKeyFocusArea(default_focus()); view->QueueDraw(); QueueDraw(); } void DashView::OnResultActivatedReply(LocalResult const& local_result, ScopeHandledType type, glib::HintsMap const& hints) { // We don't want to close the dash if there was another activation pending if (type == NOT_HANDLED) { if (!DoFallbackActivation(local_result.uri)) return; } else if (type == SHOW_DASH) { return; } else if (type == PERFORM_SEARCH) { auto it = hints.find("query"); if (it != hints.end()) { search_bar_->search_string = it->second.GetString(); return; } } ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); } bool DashView::DoFallbackActivation(std::string const& uri) { if (g_str_has_prefix(uri.c_str(), "application://")) { std::string const& appname = uri.substr(14); return application_starter_->Launch(appname, last_activated_timestamp_); } else if (g_str_has_prefix(uri.c_str(), "unity-runner://")) { std::string const& appname = uri.substr(15); return application_starter_->Launch(appname, last_activated_timestamp_); } else return gtk_show_uri(NULL, uri.c_str(), last_activated_timestamp_, NULL); return false; } void DashView::DisableBlur() { renderer_.DisableBlur(); } void DashView::OnEntryActivated() { if (active_scope_view_.IsValid()) { const std::array nekos = { "d2VsY29tZSBiYWNrIHVuaXR5=", "ZmFyZXdlbGwgdW5pdHk=", }; for (unsigned i = 0; i < nekos.size(); ++i) { gsize tmp_sz; glib::String neko((gchar*)g_base64_decode(nekos[i], &tmp_sz)); if (search_bar_->search_string() == neko.Str()) { for (auto const& view : scope_views_) view.second->neko_mode = (i != 0); search_bar_->search_string = ""; return; } } if (!activate_delay_ && !search_bar_->in_live_search()) active_scope_view_->ActivateFirst(); else activate_on_finish_ = true; } } // Keyboard navigation bool DashView::AcceptKeyNavFocus() { return false; } std::string const DashView::GetIdForShortcutActivation(std::string const& shortcut) const { Scope::Ptr scope = scopes_ ? scopes_->GetScopeForShortcut(shortcut) : Scope::Ptr(); if (scope) return scope->id; return ""; } std::vector DashView::GetAllShortcuts() { std::vector result; if (scopes_) { for (Scope::Ptr scope: scopes_->GetScopes()) { std::string const& shortcut = scope->shortcut; if (!shortcut.empty()) result.push_back(shortcut.at(0)); } } return result; } bool DashView::InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character) { if ((eventType == nux::NUX_KEYDOWN) && (key_sym == NUX_VK_ESCAPE)) { if (preview_displaying_) ClosePreview(); else if (!search_bar_->search_string().empty()) search_bar_->search_string = ""; else ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); return true; } return false; } nux::View* DashView::default_focus() const { return search_bar_->text_entry(); } // Introspectable std::string DashView::GetName() const { return "DashView"; } void DashView::AddProperties(debug::IntrospectionData& introspection) { dash::Style& style = dash::Style::Instance(); int num_rows = 1; // The search bar std::vector button_on_monitor; if (active_scope_view_.IsValid()) num_rows += active_scope_view_->GetNumRows(); std::string form_factor("unknown"); if (Settings::Instance().form_factor() == FormFactor::NETBOOK) form_factor = "netbook"; else if (Settings::Instance().form_factor() == FormFactor::DESKTOP) form_factor = "desktop"; else if (Settings::Instance().form_factor() == FormFactor::TV) form_factor = "tv"; for (unsigned i = 0; i < monitors::MAX; ++i) button_on_monitor.push_back(overlay_window_buttons_->IsVisibleOnMonitor(i)); introspection.add(nux::Geometry(GetAbsoluteX(), GetAbsoluteY(), content_geo_.width, content_geo_.height)) .add("num_rows", num_rows) .add("form_factor", form_factor) .add("vertical-border-width", style.GetDashVerticalBorderWidth().CP(scale)) .add("horizontal-border-height", style.GetDashHorizontalBorderHeight().CP(scale)) .add("preview_displaying", preview_displaying_) .add("preview_animation", animate_split_value_ * animate_preview_container_value_ * animate_preview_value_) .add("dash_maximized", style.always_maximised()) .add("overlay_window_buttons_shown", glib::Variant::FromVector(button_on_monitor)); } nux::Area* DashView::KeyNavIteration(nux::KeyNavDirection direction) { if (preview_displaying_) { return preview_container_->KeyNavIteration(direction); } else if (direction == nux::KEY_NAV_DOWN && search_bar_ && active_scope_view_.IsValid()) { auto show_filters = search_bar_->show_filters(); auto fscroll_view = active_scope_view_->fscroll_view(); if (show_filters && show_filters->HasKeyFocus()) { if (fscroll_view->IsVisible() && fscroll_view) return fscroll_view->KeyNavIteration(direction); else return active_scope_view_->KeyNavIteration(direction); } } return this; } void DashView::ProcessDndEnter() { auto const& event = nux::GetGraphicsDisplay()->GetCurrentEvent(); // Don't close the dash if the mouse is over the vertical line between the dash and the launcher. if (event.x != GetAbsoluteX()) ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); } nux::Area* DashView::SkipUnexpandableHeaderKeyNav() { PlacesGroup::Ptr prev_view; auto category_views = active_scope_view_->GetOrderedCategoryViews(); for (auto category : category_views) { if (category->GetLayout() != nullptr) { auto header = category->GetHeaderFocusableView(); if (header && header->HasKeyFocus() && !category->IsExpandable()) { if (prev_view) return prev_view->GetChildView(); else return search_bar_->text_entry(); } if (category->IsVisible()) prev_view = category; } } return nullptr; } nux::Area* DashView::FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) { // Only care about states of Alt, Ctrl, Super, Shift, not the lock keys special_keys_state &= (nux::NUX_STATE_ALT | nux::NUX_STATE_CTRL | nux::NUX_STATE_SUPER | nux::NUX_STATE_SHIFT); // Do what nux::View does, but if the event isn't a key navigation, // designate the text entry to process it. using namespace nux; nux::KeyNavDirection direction = KEY_NAV_NONE; bool ctrl = (special_keys_state & NUX_STATE_CTRL); switch (x11_key_code) { case NUX_VK_UP: direction = KEY_NAV_UP; break; case NUX_VK_DOWN: direction = KEY_NAV_DOWN; break; case NUX_VK_LEFT: direction = KEY_NAV_LEFT; break; case NUX_VK_RIGHT: direction = KEY_NAV_RIGHT; break; case NUX_VK_LEFT_TAB: direction = KEY_NAV_TAB_PREVIOUS; break; case NUX_VK_TAB: direction = KEY_NAV_TAB_NEXT; break; case NUX_VK_ENTER: case NUX_KP_ENTER: // Not sure if Enter should be a navigation key direction = KEY_NAV_ENTER; break; case NUX_VK_PAGE_UP: case NUX_VK_PAGE_DOWN: if (!preview_displaying_) { active_scope_view_->PerformPageNavigation((x11_key_code == NUX_VK_PAGE_UP) ? ScrollDir::UP : ScrollDir::DOWN); return nux::GetWindowCompositor().GetKeyFocusArea(); } break; default: auto const& close_key = WindowManager::Default().close_window_key(); if (close_key.first == special_keys_state && close_key.second == x11_key_code) { ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); return nullptr; } direction = KEY_NAV_NONE; } if (preview_displaying_) { return preview_container_->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); } // We should not do it here, but I really don't want to make DashView // focusable and I'm not able to know if ctrl is pressed in // DashView::KeyNavIteration. nux::InputArea* focus_area = nux::GetWindowCompositor().GetKeyFocusArea(); if (direction != KEY_NAV_NONE && key_symbol == nux::NUX_KEYDOWN && !search_bar_->im_preedit) { std::list tabs; if (active_scope_view_.IsValid()) { for (auto category : active_scope_view_->GetOrderedCategoryViews()) { if (category->IsVisible()) tabs.push_back(category.GetPointer()); } } if (search_bar_ && search_bar_->show_filters() && search_bar_->show_filters()->IsVisible()) { tabs.push_back(search_bar_->show_filters()); } if (active_scope_view_.IsValid() && active_scope_view_->filter_bar() && active_scope_view_->fscroll_view() && active_scope_view_->fscroll_view()->IsVisible()) { for (auto child : active_scope_view_->filter_bar()->GetLayout()->GetChildren()) { FilterExpanderLabel* filter = dynamic_cast(child); if (filter) tabs.push_back(filter->expander_view()); } } if (direction == KEY_NAV_TAB_PREVIOUS) { if (ctrl) { scope_bar_->ActivatePrevious(); } else { auto rbegin = tabs.rbegin(); auto rend = tabs.rend(); bool use_the_prev = false; for (auto tab = rbegin; tab != rend; ++tab) { const auto& tab_ptr = *tab; if (use_the_prev) return tab_ptr; if (focus_area) use_the_prev = focus_area->IsChildOf(tab_ptr); } for (auto tab = rbegin; tab != rend; ++tab) return *tab; } } else if (direction == KEY_NAV_TAB_NEXT) { if (ctrl) { scope_bar_->ActivateNext(); } else { bool use_the_next = false; for (auto tab : tabs) { if (use_the_next) return tab; if (focus_area) use_the_next = focus_area->IsChildOf(tab); } for (auto tab : tabs) return tab; } } } if (direction == KEY_NAV_UP) { if (auto skip_view = SkipUnexpandableHeaderKeyNav()) { return skip_view; } } bool search_key = false; if (direction == KEY_NAV_NONE) { if (keyboard::is_printable_key_symbol(x11_key_code) || keyboard::is_move_key_symbol(x11_key_code)) { search_key = true; } } if (!preview_displaying_ && (search_key || search_bar_->im_preedit)) { // then send the event to the search entry return search_bar_->text_entry(); } else if (next_object_to_key_focus_area_) { return next_object_to_key_focus_area_->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); } return nullptr; } nux::Area* DashView::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) { nux::Area* view = nullptr; if (overlay_window_buttons_->GetGeometry().IsInside(mouse_position)) { return overlay_window_buttons_->FindAreaUnderMouse(mouse_position, event_type); } else if (preview_displaying_) { nux::Point newpos = mouse_position; view = dynamic_cast(preview_container_.GetPointer())->FindAreaUnderMouse(newpos, event_type); } else { view = View::FindAreaUnderMouse(mouse_position, event_type); } return (view == nullptr) ? this : view; } nux::Geometry const& DashView::GetContentGeometry() const { return content_geo_; } nux::Geometry DashView::GetRenderAbsoluteGeometry() const { nux::Geometry renderer_geo_abs(GetAbsoluteGeometry()); renderer_geo_abs.y += renderer_.y_offset; renderer_geo_abs.height -= renderer_.y_offset; return renderer_geo_abs; } } } ./dash/ResultRendererTile.cpp0000644000004100000410000004204713437202764016470 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "ResultRendererTile.h" #include #include #include #include #include "unity-shared/CairoTexture.h" #include "unity-shared/DashStyle.h" #include "unity-shared/TextureCache.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/ThemeSettings.h" namespace unity { DECLARE_LOGGER(logger, "unity.dash.results"); namespace { const std::string DEFAULT_GICON = ". GThemedIcon text-x-preview"; const RawPixel PADDING = 6_em; const RawPixel SPACING = 10_em; const int FONT_SIZE = 10; const int FONT_MULTIPLIER = 1024; char const REPLACEMENT_CHAR = '?'; float const CORNER_HIGHTLIGHT_RADIUS = 2.0f; void RenderTexture(nux::GraphicsEngine& GfxContext, int x, int y, int width, int height, nux::ObjectPtr const& texture, nux::TexCoordXForm &texxform, const nux::Color &color, float saturate ) { if (saturate == 1.0) { GfxContext.QRP_1Tex(x, y, width, height, texture, texxform, color); } else { GfxContext.QRP_TexDesaturate(x, y, width, height, texture, texxform, color, saturate); } } } namespace dash { NUX_IMPLEMENT_OBJECT_TYPE(ResultRendererTile); ResultRendererTile::ResultRendererTile(NUX_FILE_LINE_DECL, bool neko_mode) : ResultRenderer(NUX_FILE_LINE_PARAM) , neko_mode_(neko_mode) { UpdateWidthHeight(); scale.changed.connect([this] (double) { UpdateWidthHeight(); }); } void ResultRendererTile::UpdateWidthHeight() { dash::Style const& style = dash::Style::Instance(); RawPixel tile_width = style.GetTileWidth(); RawPixel tile_height = style.GetTileHeight(); width = tile_width.CP(scale()); height = tile_height.CP(scale()); } void ResultRendererTile::Render(nux::GraphicsEngine& GfxContext, Result& row, ResultRendererState state, nux::Geometry const& geometry, int x_offset, int y_offset, nux::Color const& color, float saturate) { TextureContainer* container = row.renderer(); if (container == nullptr) return; dash::Style const& style = dash::Style::Instance(); int tile_icon_size = style.GetTileImageSize().CP(scale); // set up our texture mode nux::TexCoordXForm texxform; int icon_width, icon_height; if (container->icon == nullptr) { icon_width = icon_height = tile_icon_size; } else { icon_width = container->icon->GetWidth(); icon_height = container->icon->GetHeight(); } int icon_left_hand_side = geometry.x + (geometry.width - icon_width) / 2; int icon_top_side = geometry.y + PADDING.CP(scale()) + (tile_icon_size - icon_height) / 2; // render highlight if its needed if (container->prelight && state != ResultRendererState::RESULT_RENDERER_NORMAL) { int highlight_x = (geometry.x + geometry.width/2) - style.GetTileIconHightlightWidth().CP(scale)/2; int highlight_y = (geometry.y + PADDING.CP(scale) + tile_icon_size / 2) - style.GetTileIconHightlightHeight().CP(scale)/2; RenderTexture(GfxContext, highlight_x, highlight_y, container->prelight->GetWidth(), container->prelight->GetHeight(), container->prelight->GetDeviceTexture(), texxform, color, saturate); } // draw the icon if (container->icon) { RenderTexture(GfxContext, icon_left_hand_side, icon_top_side, container->icon->GetWidth(), container->icon->GetHeight(), container->icon->GetDeviceTexture(), texxform, color, saturate); } if (container->text) { RenderTexture(GfxContext, geometry.x + PADDING.CP(scale), geometry.y + tile_icon_size + SPACING.CP(scale), style.GetTileWidth().CP(scale) - (PADDING.CP(scale) * 2), style.GetTileHeight().CP(scale) - tile_icon_size - SPACING.CP(scale), container->text->GetDeviceTexture(), texxform, color, saturate); } } nux::BaseTexture* ResultRendererTile::DrawHighlight(std::string const& texid, int width, int height) { nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, width, height); cairo_surface_set_device_scale(cairo_graphics.GetSurface(), scale(), scale()); cairo_t* cr = cairo_graphics.GetInternalContext(); cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.0); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); // draw the highlight cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 0.2f); cairo_graphics.DrawRoundedRectangle(cr, 1.0f, 0.0f, 0.0f, CORNER_HIGHTLIGHT_RADIUS, width/scale(), height/scale(), false); cairo_fill(cr); return texture_from_cairo_graphics(cairo_graphics); } int ResultRendererTile::Padding() const { return PADDING.CP(scale()); } void ResultRendererTile::Preload(Result const& row) { if (row.renderer() == nullptr) { // Shouldn't really do this, but it's safe in this case and quicker than making a copy. const_cast(row).set_renderer(new TextureContainer()); LoadIcon(row); LoadText(row); } } void ResultRendererTile::ReloadResult(Result const& row) { Unload(row); if (row.renderer() == nullptr) const_cast(row).set_renderer(new TextureContainer()); LoadIcon(row); LoadText(row); } void ResultRendererTile::Unload(Result const& row) { TextureContainer *container = row.renderer(); if (container) { delete container; // Shouldn't really do this, but it's safe in this case and quicker than making a copy. const_cast(row).set_renderer(nullptr); } } nux::NBitmapData* ResultRendererTile::GetDndImage(Result const& row) const { TextureContainer* container = row.renderer(); nux::NBitmapData* bitmap = nullptr; if (container && container->drag_icon && container->drag_icon.IsType(GDK_TYPE_PIXBUF)) { // Need to ref the drag icon because GdkGraphics will unref it. nux::GdkGraphics graphics(GDK_PIXBUF(g_object_ref(container->drag_icon))); bitmap = graphics.GetBitmap(); } return bitmap ? bitmap : ResultRenderer::GetDndImage(row); } void ResultRendererTile::LoadIcon(Result const& row) { Style const& style = Style::Instance(); int tile_size = style.GetTileImageSize().CP(scale); int tile_gsize = style.GetTileGIconSize().CP(scale); std::string const& icon_hint = row.icon_hint; std::string icon_name = !icon_hint.empty() ? icon_hint : DEFAULT_GICON; if (neko_mode_) { const std::array pool = { "aHR0cDovL3BsYWNla2l0dGVuLmNvbS9nLyVpLyVpLw==", "aHR0cDovL3BsYWNla2l0dGVuLmNvbS8laS8laS8=", }; gsize tmp0; int tmp1 = tile_size - (rand() % RawPixel(16).CP(scale)); int tmp2 = tile_size - (rand() % RawPixel(16).CP(scale)); glib::String tmp3((gchar*)g_base64_decode(pool[rand() % pool.size()], &tmp0)); icon_name = glib::String(g_strdup_printf(tmp3, tmp1, tmp2)).Str(); } glib::Object icon(g_icon_new_for_string(icon_name.c_str(), nullptr)); TextureContainer* container = row.renderer(); if (container) { TextureCache& cache = TextureCache::GetDefault(); BaseTexturePtr texture_prelight(cache.FindTexture("resultview_prelight", style.GetTileIconHightlightWidth().CP(scale), style.GetTileIconHightlightHeight().CP(scale), sigc::mem_fun(this, &ResultRendererTile::DrawHighlight))); container->prelight = texture_prelight; } IconLoader::IconLoaderCallback slot = sigc::bind(sigc::mem_fun(this, &ResultRendererTile::IconLoaded), icon_hint, row); if (icon.IsType(G_TYPE_ICON)) { bool use_large_icon = icon.IsType(G_TYPE_FILE_ICON) || !icon.IsType(G_TYPE_THEMED_ICON); container->slot_handle = IconLoader::GetDefault().LoadFromGIconString(icon_name, tile_size, use_large_icon ? tile_size : tile_gsize, slot); } else { container->slot_handle = IconLoader::GetDefault().LoadFromIconName(icon_name, -1, tile_gsize, slot); } } nux::BaseTexture* ResultRendererTile::CreateTextureCallback(std::string const& texid, int width, int height, glib::Object const& pixbuf) { int pixbuf_width, pixbuf_height; pixbuf_width = gdk_pixbuf_get_width(pixbuf); pixbuf_height = gdk_pixbuf_get_height(pixbuf); if (G_UNLIKELY(!pixbuf_height || !pixbuf_width)) { LOG_ERROR(logger) << "Pixbuf: " << texid << " has a zero height/width: " << width << "," << height; pixbuf_width = (pixbuf_width) ? pixbuf_width : 1; // no zeros please pixbuf_height = (pixbuf_height) ? pixbuf_height: 1; // no zeros please } if (pixbuf_width == pixbuf_height) { // quick path for square icons return nux::CreateTexture2DFromPixbuf(pixbuf, true); } else { // slow path for non square icons that must be resized to fit in the square // texture double aspect = static_cast(pixbuf_height) / pixbuf_width; // already sanitized width/height so can not be 0.0 if (aspect < 1.0f) { pixbuf_width = Style::Instance().GetTileImageSize().CP(scale); pixbuf_height = pixbuf_width * aspect; if (pixbuf_height > height) { // scaled too big, scale down pixbuf_height = height; pixbuf_width = pixbuf_height / aspect; } } else { pixbuf_height = height; pixbuf_width = pixbuf_height / aspect; } if (gdk_pixbuf_get_height(pixbuf) == pixbuf_height) { // we changed our mind, fast path is good return nux::CreateTexture2DFromPixbuf(pixbuf, true); } nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, pixbuf_width, pixbuf_height); cairo_surface_set_device_scale(cairo_graphics.GetSurface(), scale(), scale()); cairo_t* cr = cairo_graphics.GetInternalContext(); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); double pixmap_scale = float(pixbuf_height) / gdk_pixbuf_get_height(pixbuf) / scale(); cairo_scale(cr, pixmap_scale, pixmap_scale); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); cairo_paint(cr); return texture_from_cairo_graphics(cairo_graphics); } } void ResultRendererTile::IconLoaded(std::string const& texid, int max_width, int max_height, glib::Object const& pixbuf, std::string const& icon_name, Result const& row) { TextureContainer *container = row.renderer(); if (pixbuf && container) { TextureCache& cache = TextureCache::GetDefault(); BaseTexturePtr texture(cache.FindTexture(icon_name, max_width, max_height, sigc::bind(sigc::mem_fun(this, &ResultRendererTile::CreateTextureCallback), pixbuf))); container->icon = texture; container->drag_icon = pixbuf; NeedsRedraw.emit(); if (container) container->slot_handle = 0; } else if (container) { // we need to load a missing icon IconLoader::IconLoaderCallback slot = sigc::bind(sigc::mem_fun(this, &ResultRendererTile::IconLoaded), icon_name, row); container->slot_handle = IconLoader::GetDefault().LoadFromGIconString(". GThemedIcon text-x-preview", max_width, max_height, slot); } } /* Blacklisted unicode ranges: * Burmese: U+1000 -> U+109F * Extended: U+AA60 -> U+AA7B */ bool IsBlacklistedChar(gunichar uni_c) { if ((uni_c >= 0x1000 && uni_c <= 0x109F) || (uni_c >= 0xAA60 && uni_c <= 0xAA7B)) { return true; } return false; } // FIXME Bug (lp.1239381) in the backend of pango that crashes // when using ellipsize with/height setting in pango std::string ReplaceBlacklistedChars(std::string const& str) { std::string new_string(""); if (!g_utf8_validate(str.c_str(), -1, NULL)) return new_string; gchar const* uni_s = str.c_str(); gunichar uni_c; gchar utf8_buff[6]; int size = g_utf8_strlen(uni_s, -1); for (int i = 0; i < size; ++i) { uni_c = g_utf8_get_char(uni_s); uni_s = g_utf8_next_char(uni_s); if (IsBlacklistedChar(uni_c)) { new_string += REPLACEMENT_CHAR; } else { int end = g_unichar_to_utf8(uni_c, utf8_buff); utf8_buff[end] = '\0'; new_string += utf8_buff; } } return new_string; } void ResultRendererTile::LoadText(Result const& row) { Style const& style = Style::Instance(); nux::CairoGraphics _cairoGraphics(CAIRO_FORMAT_ARGB32, style.GetTileWidth().CP(scale()) - (PADDING.CP(scale()) * 2), style.GetTileHeight().CP(scale()) - style.GetTileImageSize().CP(scale()) - SPACING.CP(scale())); cairo_surface_set_device_scale(_cairoGraphics.GetSurface(), scale(), scale()); cairo_t* cr = _cairoGraphics.GetInternalContext(); PangoLayout* layout = NULL; PangoFontDescription* desc = NULL; PangoContext* pango_context = NULL; GdkScreen* screen = gdk_screen_get_default(); // not ref'ed cairo_set_font_options(cr, gdk_screen_get_font_options(screen)); layout = pango_cairo_create_layout(cr); desc = pango_font_description_from_string(theme::Settings::Get()->font().c_str()); pango_font_description_set_size (desc, FONT_SIZE * FONT_MULTIPLIER); pango_layout_set_font_description(layout, desc); pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_START); pango_layout_set_width(layout, (style.GetTileWidth() - (PADDING * 2))* PANGO_SCALE); pango_layout_set_height(layout, -2); // FIXME bug #1239381 std::string name = ReplaceBlacklistedChars(row.name()); char *escaped_text = g_markup_escape_text(name.c_str(), -1); pango_layout_set_markup(layout, escaped_text, -1); g_free (escaped_text); pango_context = pango_layout_get_context(layout); // is not ref'ed pango_cairo_context_set_font_options(pango_context, gdk_screen_get_font_options(screen)); pango_cairo_context_set_resolution(pango_context, 96.0 * Settings::Instance().font_scaling()); pango_layout_context_changed(layout); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cairo_move_to(cr, 0.0f, 0.0f); pango_cairo_show_layout(cr, layout); // clean up pango_font_description_free(desc); g_object_unref(layout); TextureContainer *container = row.renderer(); if (container) container->text = texture_ptr_from_cairo_graphics(_cairoGraphics); } } } ./dash/ScopeBar.cpp0000644000004100000410000001547013437202764014403 0ustar www-datawww-data/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #include #include "ScopeBar.h" #include "config.h" #include #include #include "unity-shared/DashStyle.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/CairoTexture.h" #include "unity-shared/GraphicsUtils.h" #include "unity-shared/UBusMessages.h" namespace unity { namespace dash { namespace { // according to Q design the inner area of the scopebar should be 40px // (without any borders) RawPixel const SCOPEBAR_HEIGHT = 41_em; RawPixel const TRIANGLE_SIZE = 5_em; } NUX_IMPLEMENT_OBJECT_TYPE(ScopeBar); ScopeBar::ScopeBar() : nux::View(NUX_TRACKER_LOCATION) , scale(1.0) { scale.changed.connect(sigc::mem_fun(this, &ScopeBar::UpdateScale)); SetupBackground(); SetupLayout(); } void ScopeBar::SetupBackground() { nux::ROPConfig rop; rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bg_layer_.reset(new nux::ColorLayer(nux::Color(0.0f, 0.0f, 0.0f, 0.2f), true, rop)); } void ScopeBar::UpdateScale(double scale) { SetMinimumHeight(SCOPEBAR_HEIGHT.CP(scale)); SetMaximumHeight(SCOPEBAR_HEIGHT.CP(scale)); for (auto icon : icons_) icon->scale = scale; QueueDraw(); QueueRelayout(); } void ScopeBar::SetupLayout() { layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); layout_->SetContentDistribution(nux::MAJOR_POSITION_CENTER); SetLayout(layout_); SetMinimumHeight(SCOPEBAR_HEIGHT.CP(scale())); SetMaximumHeight(SCOPEBAR_HEIGHT.CP(scale())); } void ScopeBar::AddScope(Scope::Ptr const& scope) { ScopeBarIcon* icon = new ScopeBarIcon(scope->id, scope->icon_hint, scope->name); icon->SetVisible(scope->visible); icon->scale = scale(); scope->visible.changed.connect([icon](bool visible) { icon->SetVisible(visible); } ); icons_.push_back(icon); layout_->AddView(icon, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX); AddChild(icon); icon->mouse_click.connect([this, icon] (int x, int y, unsigned long button, unsigned long keyboard) { SetActive(icon); }); icon->key_nav_focus_activate.connect([this, icon](nux::Area*){ SetActive(icon); }); } void ScopeBar::Activate(std::string id) { for (auto icon: icons_) { if (icon->id == id) { SetActive(icon); break; } } } void ScopeBar::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); graphics_engine.PushClippingRectangle(base); if (RedirectedAncestor()) graphics::ClearGeometry(base); if (bg_layer_) { bg_layer_->SetGeometry(base); bg_layer_->Renderlayer(graphics_engine); } graphics_engine.PopClippingRectangle(); } void ScopeBar::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); graphics_engine.PushClippingRectangle(base); int pushed_paint_layers = 0; if (!IsFullRedraw()) { if (RedirectedAncestor()) { for (auto icon: icons_) { if (icon->IsVisible() && icon->IsRedrawNeeded()) graphics::ClearGeometry(icon->GetGeometry()); } } if (bg_layer_) { nux::GetPainter().PushLayer(graphics_engine, bg_layer_->GetGeometry(), bg_layer_.get()); pushed_paint_layers++; } } else { nux::GetPainter().PushPaintLayerStack(); } GetLayout()->ProcessDraw(graphics_engine, force_draw); if (IsFullRedraw()) { nux::GetPainter().PopPaintLayerStack(); } else if (pushed_paint_layers > 0) { nux::GetPainter().PopBackground(pushed_paint_layers); } for (auto icon: icons_) { if (icon->active && icon->IsVisible()) { nux::Geometry const& geo = icon->GetGeometry(); int middle = geo.x + geo.width/2; // Nux doesn't draw too well the small triangles, so let's draw a // bigger one and clip part of them using the "-1". int size = TRIANGLE_SIZE.CP(scale()); int y = base.y - 1; nux::GetPainter().Draw2DTriangleColor(graphics_engine, middle - size, y, middle, y + size, middle + size, y, nux::color::White); break; } } graphics_engine.PopClippingRectangle(); } void ScopeBar::SetActive(ScopeBarIcon* activated) { bool state_changed = false; for (auto icon: icons_) { bool state = icon == activated; if (icon->active != state) state_changed = true; icon->active = state; } if (state_changed) scope_activated.emit(activated->id); } void ScopeBar::ActivateNext() { bool activate_next = false; for (auto it = icons_.begin(); it < icons_.end(); it++) { ScopeBarIcon *icon = *it; if (activate_next && icon->IsVisible()) { SetActive(icon); return; } if (icon->active) activate_next = true; } // fallback. select first visible icon. for (auto it = icons_.begin(); it != icons_.end(); ++it) { ScopeBarIcon *icon = *it; if (icon->IsVisible()) { SetActive(icon); break; } } } void ScopeBar::ActivatePrevious() { bool activate_previous = false; for (auto rit = icons_.rbegin(); rit < icons_.rend(); ++rit) { ScopeBarIcon *icon = *rit; if (activate_previous && icon->IsVisible()) { SetActive(icon); return; } if (icon->active) activate_previous = true; } // fallback. select last visible icon. for (auto rit = icons_.rbegin(); rit != icons_.rend(); ++rit) { ScopeBarIcon *icon = *rit; if (icon->IsVisible()) { SetActive(icon); break; } } } // Keyboard navigation bool ScopeBar::AcceptKeyNavFocus() { return false; } // Introspectable std::string ScopeBar::GetName() const { return "ScopeBar"; } void ScopeBar::AddProperties(debug::IntrospectionData& wrapper) { for (auto icon : icons_) { if (icon->active) wrapper.add("active-scope", icon->id()); if (icon->HasKeyFocus()) wrapper.add("focused-scope-icon", icon->id()); } } std::string ScopeBar::GetActiveScopeId() const { for (auto* icon : icons_) { if (icon->active) return icon->id; } return ""; } } } ./dash/PreviewStateMachine.h0000644000004100000410000000351713437202764016260 0ustar www-datawww-data/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Gord Allott > */ #ifndef UNITY_PREVIEW_STATE_MACHINE_H_ #define UNITY_PREVIEW_STATE_MACHINE_H_ #include "previews/Preview.h" #include "previews/PreviewContainer.h" #include namespace unity { namespace dash { typedef enum { START, CONTENT_AREA, FILTER_BAR, SCOPE_BAR, SEARCH_BAR, END } SplitPosition; class PreviewStateMachine { public: PreviewStateMachine(); ~PreviewStateMachine(); void ActivatePreview(Preview::Ptr preview); // potentially async call. void Reset(); // resets the state but does not close the preview void ClosePreview(); void SetSplitPosition(SplitPosition position, int coord); int GetSplitPosition(SplitPosition position); nux::Property preview_active; nux::Property left_results; nux::Property right_results; sigc::signal PreviewActivated; private: void CheckPreviewRequirementsFulfilled(); // all stored co-ordinates are absolute geometry // to make dealing with the views inside the scrollview easier to understand std::unordered_map split_positions_; Preview::Ptr stored_preview_; bool requires_activation_; bool requires_new_position_; }; } } #endif ./dash/FilterBasicButton.cpp0000644000004100000410000001424213437202764016264 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "unity-shared/DashStyle.h" #include "unity-shared/UnitySettings.h" #include "FilterBasicButton.h" namespace unity { namespace dash { namespace { const RawPixel BUTTON_HEIGHT = 30_em; const RawPixel MIN_BUTTON_WIDTH = 48_em; const int FONT_SIZE_PX = 15; // 15px == 11pt } NUX_IMPLEMENT_OBJECT_TYPE(FilterBasicButton); FilterBasicButton::FilterBasicButton(nux::TextureArea* image, NUX_FILE_LINE_DECL) : FilterBasicButton(std::string(), image, NUX_FILE_LINE_PARAM) {} FilterBasicButton::FilterBasicButton(NUX_FILE_LINE_DECL) : FilterBasicButton(std::string(), NUX_FILE_LINE_PARAM) {} FilterBasicButton::FilterBasicButton(std::string const& label, NUX_FILE_LINE_DECL) : FilterBasicButton(label, nullptr, NUX_FILE_LINE_PARAM) {} FilterBasicButton::FilterBasicButton(std::string const& label, nux::TextureArea* image, NUX_FILE_LINE_DECL) : nux::ToggleButton(image, NUX_FILE_LINE_PARAM) , scale(1.0) , label_(label) { InitTheme(); SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(true); clear_before_draw_ = true; key_nav_focus_change.connect([this] (nux::Area*, bool, nux::KeyNavDirection) { QueueDraw(); }); key_nav_focus_activate.connect([this](nux::Area*) { if (GetInputEventSensitivity()) Active() ? Deactivate() : Activate(); }); scale.changed.connect(sigc::mem_fun(this, &FilterBasicButton::UpdateScale)); Settings::Instance().font_scaling.changed.connect(sigc::hide(sigc::mem_fun(this, &FilterBasicButton::InitTheme))); Style::Instance().changed.connect(sigc::mem_fun(this, &FilterBasicButton::InitTheme)); } void FilterBasicButton::InitTheme() { nux::Geometry const& geo = GetGeometry(); prelight_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &FilterBasicButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRELIGHT))); active_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &FilterBasicButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRESSED))); normal_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &FilterBasicButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_NORMAL))); focus_.reset(new nux::CairoWrapper(geo, sigc::mem_fun(this, &FilterBasicButton::RedrawFocusOverlay))); double font_scaling = Settings::Instance().font_scaling() * scale; SetMinimumWidth(MIN_BUTTON_WIDTH.CP(font_scaling)); ApplyMinWidth(); SetMinimumHeight(BUTTON_HEIGHT.CP(font_scaling)); SetMaximumHeight(BUTTON_HEIGHT.CP(font_scaling)); } void FilterBasicButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); Style::Instance().Button(cr, faked_state, label_, FONT_SIZE_PX, Alignment::CENTER, true); } void FilterBasicButton::RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr) { cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); Style::Instance().ButtonFocusOverlay(cr); } void FilterBasicButton::UpdateScale(double scale) { InitTheme(); QueueDraw(); } long FilterBasicButton::ComputeContentSize() { long ret = nux::Button::ComputeContentSize(); nux::Geometry const& geo = GetGeometry(); if (cached_geometry_ != geo) { prelight_->Invalidate(geo); active_->Invalidate(geo); normal_->Invalidate(geo); focus_->Invalidate(geo); cached_geometry_ = geo; } return ret; } void FilterBasicButton::SetClearBeforeDraw(bool clear_before_draw) { clear_before_draw_ = clear_before_draw; } void FilterBasicButton::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& geo = GetGeometry(); graphics_engine.PushClippingRectangle(geo); // set up our texture mode nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); // clear what is behind us unsigned int alpha = 0, src = 0, dest = 0; graphics_engine.GetRenderStates().GetBlend(alpha, src, dest); graphics_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::Color col = nux::color::Black; col.alpha = 0; graphics_engine.QRP_Color(geo.x, geo.y, geo.width, geo.height, col); nux::BaseTexture* texture = normal_->GetTexture(); if (Active()) texture = active_->GetTexture(); else if (GetVisualState() == nux::ButtonVisualState::VISUAL_STATE_PRELIGHT) texture = prelight_->GetTexture(); else if (GetVisualState() == nux::ButtonVisualState::VISUAL_STATE_PRESSED) texture = active_->GetTexture(); graphics_engine.QRP_1Tex(geo.x, geo.y, geo.width, geo.height, texture->GetDeviceTexture(), texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); if (HasKeyboardFocus()) { graphics_engine.QRP_1Tex(geo.x, geo.y, geo.width, geo.height, focus_->GetTexture()->GetDeviceTexture(), texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); } graphics_engine.GetRenderStates().SetBlend(alpha, src, dest); graphics_engine.PopClippingRectangle(); } std::string const& FilterBasicButton::GetLabel() const { return label_; } } // namespace dash } // namespace unity ./dash/FilterBar.h0000644000004100000410000000371313437202764014221 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERBAR_H #define UNITYSHELL_FILTERBAR_H #include #include #include "FilterFactory.h" #include "unity-shared/Introspectable.h" namespace unity { namespace dash { class FilterExpanderLabel; class FilterBar : public nux::View, public debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(FilterBar, nux::View); public: FilterBar(NUX_FILE_LINE_PROTO); nux::Property scale; void SetFilters(Filters::Ptr const& filters); void AddFilter(Filter::Ptr const& filter); void RemoveFilter(Filter::Ptr const& filter); void ClearFilters(); protected: virtual bool AcceptKeyNavFocus(); virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); // Introspection virtual std::string GetName() const; virtual void AddProperties(debug::IntrospectionData&); private: void UpdateScale(double scale); FilterFactory factory_; Filters::Ptr filters_; std::map filter_map_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERBAR_H ./dash/ResultRendererHorizontalTile.h0000644000004100000410000000376513437202764020213 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef RESULTRENDERERHORIZONTALTILE_H #define RESULTRENDERERHORIZONTALTILE_H #include "ResultRendererTile.h" namespace unity { namespace dash { // Initially unowned object class ResultRendererHorizontalTile : public ResultRendererTile { public: NUX_DECLARE_OBJECT_TYPE(ResultRendererHorizontalTile, ResultRendererTile); ResultRendererHorizontalTile(NUX_FILE_LINE_PROTO); virtual ~ResultRendererHorizontalTile() {} virtual void Render(nux::GraphicsEngine& GfxContext, Result& row, ResultRendererState state, nux::Geometry const& geometry, int x_offset, int y_offset, nux::Color const& color, float saturate); virtual nux::NBitmapData* GetDndImage(Result const& row) const; protected: virtual void LoadText(Result const& row); private: void ReloadTextures(); nux::BaseTexture* DrawHighlight(std::string const& texid, int width, int height); nux::BaseTexture* DrawNormal(std::string const& texid, int width, int height); }; } } #endif // RESULTRENDERERHORIZONTALTILE_H ./dash/FilterGenreButton.cpp0000644000004100000410000000327713437202764016311 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "FilterGenreButton.h" namespace unity { namespace dash { NUX_IMPLEMENT_OBJECT_TYPE(FilterGenreButton); FilterGenreButton::FilterGenreButton(std::string const& label, NUX_FILE_LINE_DECL) : FilterBasicButton(label, NUX_FILE_LINE_PARAM) { InitTheme(); state_change.connect([this](nux::Button* button) { if (filter_) filter_->active = Active(); }); } FilterGenreButton::FilterGenreButton(NUX_FILE_LINE_DECL) : FilterBasicButton(NUX_FILE_LINE_PARAM) { InitTheme(); state_change.connect([this](nux::Button* button) { if (filter_) filter_->active = Active(); }); } void FilterGenreButton::SetFilter(FilterOption::Ptr const& filter) { filter_ = filter; SetActive(filter_->active); filter_->active.changed.connect([this](bool is_active) { SetActive(is_active); }); } FilterOption::Ptr FilterGenreButton::GetFilter() { return filter_; } } // namespace dash } // namespace unity ./dash/PreviewStateMachine.cpp0000644000004100000410000000553413437202764016614 0ustar www-datawww-data/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Gord Allott > */ #include "PreviewStateMachine.h" #include namespace unity { namespace dash { DECLARE_LOGGER(logger, "unity.dash.preview.statemachine"); PreviewStateMachine::PreviewStateMachine() : preview_active(false) , left_results(-1) , right_results(-1) , stored_preview_(nullptr) , requires_activation_(true) , requires_new_position_(false) { for (int pos = SplitPosition::START; pos != SplitPosition::END; pos++) { split_positions_[pos] = -1; } left_results.changed.connect([this] (int value) { CheckPreviewRequirementsFulfilled();}); right_results.changed.connect([this] (int value) { CheckPreviewRequirementsFulfilled();}); } PreviewStateMachine::~PreviewStateMachine() { } void PreviewStateMachine::ActivatePreview(Preview::Ptr preview) { stored_preview_ = preview; requires_activation_ = true; CheckPreviewRequirementsFulfilled(); } void PreviewStateMachine::Reset() { left_results = -1; right_results = -1; stored_preview_ = nullptr; requires_activation_ = true; } void PreviewStateMachine::ClosePreview() { Reset(); SetSplitPosition(SplitPosition::CONTENT_AREA, -1); } void PreviewStateMachine::SetSplitPosition(SplitPosition position, int coord) { split_positions_[static_cast(position)] = coord; CheckPreviewRequirementsFulfilled(); } int PreviewStateMachine::GetSplitPosition(SplitPosition position) { return split_positions_[static_cast(position)]; } void PreviewStateMachine::CheckPreviewRequirementsFulfilled() { if (!requires_activation_) return; if (stored_preview_ == nullptr) { requires_activation_ = true; return; } /* right now this is disabled as long as we aren't doing the fancy splitting animation * as we don't care about positions * if (GetSplitPosition(CONTENT_AREA) < 0) return; if (GetSplitPosition(FILTER_BAR) < 0) return; if (GetSplitPosition(SCOPE_BAR) < 0) return; if (GetSplitPosition(SEARCH_BAR) < 0) return; */ if (left_results < 0 || right_results < 0) return; LOG_DEBUG(logger) << "activating preview: " << left_results << " - " << right_results; preview_active = true; PreviewActivated(stored_preview_); requires_activation_ = false; } } } ./dash/FilterMultiRangeButton.h0000644000004100000410000000550013437202764016754 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERMULTIRANGEBUTTON_H #define UNITYSHELL_FILTERMULTIRANGEBUTTON_H #include #include #include #include #include #include namespace unity { namespace dash { enum class MultiRangeSide : unsigned int { LEFT = 0, RIGHT, CENTER }; enum class MultiRangeArrow : unsigned int { LEFT = 0, RIGHT, BOTH, NONE }; class FilterMultiRangeButton : public nux::ToggleButton { NUX_DECLARE_OBJECT_TYPE(FilterMultiRangeButton, nux::ToggleButton); public: FilterMultiRangeButton(NUX_FILE_LINE_PROTO); nux::Property scale; void SetFilter(FilterOption::Ptr const& filter); FilterOption::Ptr GetFilter(); void SetVisualSide(MultiRangeSide side); //0 = left, 1 = center, 2 = right - sucky api i know :( void SetHasArrow(MultiRangeArrow arrow); //0 = left, 1 = both, 2 = right, -1 = none protected: virtual long ComputeContentSize(); virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); private: void InitTheme(); void RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state, MultiRangeArrow faked_arrow, MultiRangeSide faked_side); void RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr, MultiRangeArrow faked_arrow, MultiRangeSide faked_side); void OnActivated(nux::Area* area); void OnActiveChanged(bool value); FilterOption::Ptr filter_; typedef std::unique_ptr NuxCairoPtr; typedef std::pair MapKey; std::map active_; std::map focus_; std::map normal_; std::map prelight_; bool theme_init_; nux::Geometry cached_geometry_; MultiRangeArrow has_arrow_; MultiRangeSide side_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERMULTIRANGEBUTTON_H ./dash/PlacesGroup.h0000644000004100000410000001106513437202764014572 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Gordon Allott */ #ifndef UNITYSHELL_PLACES_GROUP_H #define UNITYSHELL_PLACES_GROUP_H #include #include #include #include #include #include #include "unity-shared/DashStyleInterface.h" #include "unity-shared/IconTexture.h" #include "unity-shared/Introspectable.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/UBusWrapper.h" #include #include "ResultView.h" namespace nux { class AbstractPaintLayer; class TextureLayer; } namespace unity { namespace dash { class HSeparator; class PlacesGroup : public nux::View, public debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(PlacesGroup, nux::View); public: typedef nux::ObjectPtr Ptr; PlacesGroup(dash::StyleInterface& style); nux::Property scale; void SetIcon(std::string const& icon); void SetName(std::string const& name); void SetHeaderCountVisible(bool disable); StaticCairoText* GetLabel(); StaticCairoText* GetExpandLabel(); void SetChildView(dash::ResultView* view); dash::ResultView* GetChildView(); void SetChildLayout(nux::Layout* layout); void Relayout(); void SetCounts(unsigned n_total_items); virtual bool IsExpandable() const; virtual void SetExpanded(bool is_expanded); virtual bool GetExpanded() const; void PushExpanded(); void PopExpanded(); void SetResultsPreviewAnimationValue(float preview_animation); int GetHeaderHeight() const; bool HeaderIsFocusable() const; nux::View* GetHeaderFocusableView() const; void SetFiltersExpanded(bool filters_expanded); sigc::signal expanded; glib::Variant GetCurrentFocus() const; void SetCurrentFocus(glib::Variant const& variant); protected: long ComputeContentSize(); void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw); void DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw); // Key navigation virtual bool AcceptKeyNavFocus(); // Introspection virtual std::string GetName() const; virtual void AddProperties(debug::IntrospectionData&); private: void Refresh(); bool HeaderHasKeyFocus() const; bool ShouldBeHighlighted() const; void DrawSeparatorChanged(bool draw); void RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags); void OnLabelActivated(nux::Area* label); void OnLabelFocusChanged(nux::Area* label, bool has_focus, nux::KeyNavDirection direction); bool OnIdleRelayout(); void RefreshLabel(); void UpdatePlacesGroupSize(); void UpdateResultViewPadding(); void UpdateScale(double scale); void UpdateVisibleItems(int visible_items); private: std::string _category_id; dash::StyleInterface& _style; nux::VLayout* _group_layout; nux::View* _header_view; nux::HLayout* _header_layout; nux::HLayout* _text_layout; nux::HLayout* _expand_label_layout; nux::HLayout* _expand_layout; nux::VLayout* _child_layout; nux::SpaceLayout* _space_layout; dash::ResultView* _child_view; std::unique_ptr _focus_layer; IconTexture* _icon; StaticCairoText* _name; StaticCairoText* _expand_label; IconTexture* _expand_icon; bool _using_filters_background; std::unique_ptr _background_layer; bool _is_expanded; bool _is_expanded_pushed; unsigned _n_visible_items_in_unexpand_mode; unsigned _n_total_items; std::string _cached_name; nux::Geometry _cached_geometry; bool _coverflow_enabled; bool _disabled_header_count; glib::Source::UniquePtr _relayout_idle; UBusManager _ubus; friend class TestScopeView; }; } // namespace dash } // namespace unity #endif ./dash/FilterMultiRangeButton.cpp0000644000004100000410000002037513437202764017316 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "config.h" #include #include #include "unity-shared/DashStyle.h" #include "FilterMultiRangeButton.h" namespace unity { namespace dash { namespace { const int FONT_SIZE_PX = 10; } NUX_IMPLEMENT_OBJECT_TYPE(FilterMultiRangeButton); FilterMultiRangeButton::FilterMultiRangeButton(NUX_FILE_LINE_DECL) : nux::ToggleButton(NUX_FILE_LINE_PARAM) , scale(1.0) , theme_init_(false) , has_arrow_(MultiRangeArrow::NONE) , side_(MultiRangeSide::CENTER) { InitTheme(); // Controlled by parent widget SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); state_change.connect(sigc::mem_fun(this, &FilterMultiRangeButton::OnActivated)); key_nav_focus_change.connect([this](nux::Area*, bool, nux::KeyNavDirection) { QueueDraw(); }); key_nav_focus_activate.connect([this](nux::Area* area) { Active() ? Deactivate() : Activate(); }); scale.changed.connect(sigc::hide(sigc::mem_fun(this, &FilterMultiRangeButton::InitTheme))); } void FilterMultiRangeButton::OnActivated(nux::Area* area) { if (filter_) filter_->active = Active(); } void FilterMultiRangeButton::OnActiveChanged(bool value) { NeedRedraw(); } void FilterMultiRangeButton::SetFilter(FilterOption::Ptr const& filter) { filter_ = filter; SetActive(filter_->active); } FilterOption::Ptr FilterMultiRangeButton::GetFilter() { return filter_; } void FilterMultiRangeButton::SetVisualSide(MultiRangeSide side) { if (side_ == side) return; side_ = side; QueueDraw(); } void FilterMultiRangeButton::SetHasArrow(MultiRangeArrow value) { if (has_arrow_ == value) return; has_arrow_ = value; QueueDraw(); } long FilterMultiRangeButton::ComputeContentSize() { long ret = nux::ToggleButton::ComputeContentSize(); nux::Geometry const& geo = GetGeometry(); if (theme_init_ && cached_geometry_ != geo) { cached_geometry_ = geo; std::vector sides = {MultiRangeSide::LEFT, MultiRangeSide::RIGHT, MultiRangeSide::CENTER}; std::vector arrows = {MultiRangeArrow::LEFT, MultiRangeArrow::RIGHT, MultiRangeArrow::BOTH, MultiRangeArrow::NONE}; auto func_invalidate = [geo](std::pair& pair) { pair.second->Invalidate(geo); }; for_each (prelight_.begin(), prelight_.end(), func_invalidate); for_each (active_.begin(), active_.end(), func_invalidate); for_each (normal_.begin(), normal_.end(), func_invalidate); for_each (focus_.begin(), focus_.end(), func_invalidate); } return ret; } void FilterMultiRangeButton::InitTheme() { nux::Geometry const& geo = GetGeometry(); std::vector sides = {MultiRangeSide::LEFT, MultiRangeSide::RIGHT, MultiRangeSide::CENTER}; std::vector arrows = {MultiRangeArrow::LEFT, MultiRangeArrow::RIGHT, MultiRangeArrow::BOTH, MultiRangeArrow::NONE}; for (auto arrow : arrows) { for (auto side : sides) { active_[MapKey(arrow, side)].reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &FilterMultiRangeButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRESSED, arrow, side))); normal_[MapKey(arrow, side)].reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &FilterMultiRangeButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_NORMAL, arrow, side))); prelight_[MapKey(arrow, side)].reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &FilterMultiRangeButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRELIGHT, arrow, side))); focus_[MapKey(arrow, side)].reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &FilterMultiRangeButton::RedrawFocusOverlay), arrow, side))); } } SetMinimumHeight(Style::Instance().GetFilterButtonHeight().CP(scale) + (3_em).CP(scale)); theme_init_ = true; QueueDraw(); } void FilterMultiRangeButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state, MultiRangeArrow faked_arrow, MultiRangeSide faked_side) { std::string name("10"); if (filter_) { name = filter_->name; } Arrow arrow; if (faked_arrow == MultiRangeArrow::NONE) arrow = Arrow::NONE; else if (faked_arrow == MultiRangeArrow::LEFT) arrow = Arrow::LEFT; else if (faked_arrow == MultiRangeArrow::BOTH) arrow = Arrow::BOTH; else arrow = Arrow::RIGHT; Segment segment; if (faked_side == MultiRangeSide::LEFT) segment = Segment::LEFT; else if (faked_side == MultiRangeSide::CENTER) segment = Segment::MIDDLE; else segment = Segment::RIGHT; cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); Style::Instance().MultiRangeSegment(cr, faked_state, name, FONT_SIZE_PX, arrow, segment); QueueDraw(); } void FilterMultiRangeButton::RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr, MultiRangeArrow faked_arrow, MultiRangeSide faked_side) { Arrow arrow; if (faked_arrow == MultiRangeArrow::NONE) arrow = Arrow::NONE; else if (faked_arrow == MultiRangeArrow::LEFT) arrow = Arrow::LEFT; else if (faked_arrow == MultiRangeArrow::BOTH) arrow = Arrow::BOTH; else arrow = Arrow::RIGHT; Segment segment; if (faked_side == MultiRangeSide::LEFT) segment = Segment::LEFT; else if (faked_side == MultiRangeSide::CENTER) segment = Segment::MIDDLE; else segment = Segment::RIGHT; cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); Style::Instance().MultiRangeFocusOverlay(cr, arrow, segment); QueueDraw(); } void FilterMultiRangeButton::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry const& geo = GetGeometry(); gPainter.PaintBackground(GfxContext, geo); // set up our texture mode nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); // clear what is behind us unsigned int alpha = 0, src = 0, dest = 0; GfxContext.GetRenderStates().GetBlend(alpha, src, dest); GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::Color col = nux::color::Black; col.alpha = 0; GfxContext.QRP_Color(geo.x, geo.y, geo.width, geo.height, col); nux::BaseTexture* texture = normal_[MapKey(has_arrow_, side_)]->GetTexture(); if (GetVisualState() == nux::ButtonVisualState::VISUAL_STATE_PRELIGHT) { texture = prelight_[MapKey(has_arrow_, side_)]->GetTexture(); } if (Active()) { texture = active_[MapKey(has_arrow_, side_)]->GetTexture(); } if (HasKeyFocus()) { GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height, focus_[MapKey(has_arrow_, side_)]->GetTexture()->GetDeviceTexture(), texxform, nux::color::White); } GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height, texture->GetDeviceTexture(), texxform, nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); GfxContext.GetRenderStates().SetBlend(alpha, src, dest); } } // namespace dash } // namespace unity ./dash/ScopeBarIcon.h0000644000004100000410000000323113437202764014651 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_SCOPE_BAR_ICON_H_ #define UNITY_SCOPE_BAR_ICON_H_ #include #include #include #include #include #include "unity-shared/IconTexture.h" namespace unity { namespace dash { class ScopeBarIcon : public IconTexture { NUX_DECLARE_OBJECT_TYPE(ScopeBarIcon, IconTexture); public: ScopeBarIcon(std::string id, std::string icon_hint, std::string name); nux::Property id; nux::Property name; nux::Property active; nux::Property scale; private: void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); void OnActiveChanged(bool is_active); void UpdateScale(double scale); // Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: typedef std::unique_ptr LayerPtr; const float inactive_opacity_; LayerPtr focus_layer_; }; } } #endif // UNITY_SCOPE_BAR_ICON_H_ ./dash/FilterRatingsButton.cpp0000644000004100000410000000347613437202764016661 0ustar www-datawww-data/* * Copyright 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include "FilterRatingsButton.h" namespace { const int STAR_SIZE = 28; const int STAR_GAP = 10; } namespace unity { namespace dash { NUX_IMPLEMENT_OBJECT_TYPE(FilterRatingsButton); FilterRatingsButton::FilterRatingsButton(NUX_FILE_LINE_DECL) : RatingsButton(STAR_SIZE, STAR_GAP, NUX_FILE_LINE_PARAM) {} void FilterRatingsButton::SetFilter(Filter::Ptr const& filter) { filter_ = std::static_pointer_cast(filter); filter_->rating.changed.connect(sigc::mem_fun(this, &FilterRatingsButton::SetRating)); QueueDraw(); } RatingsFilter::Ptr FilterRatingsButton::GetFilter() const { return filter_; } std::string FilterRatingsButton::GetFilterType() { return "FilterRatingsButton"; } std::string FilterRatingsButton::GetName() const { return "FilterRatingsButton"; } void FilterRatingsButton::SetRating(float rating) { if (filter_) filter_->rating = rating; QueueDraw(); } float FilterRatingsButton::GetRating() const { return (filter_ && filter_->filtering) ? filter_->rating : 0; } } // namespace dash } // namespace unity ./dash/ApplicationStarter.h0000644000004100000410000000215113437202764016152 0ustar www-datawww-data/* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andrea Azzarone */ #ifndef UNITY_SHARED_APPLICATION_STARTER_H #define UNITY_SHARED_APPLICATION_STARTER_H #include #include #include #include namespace unity { class ApplicationStarter : boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~ApplicationStarter() {} virtual bool Launch(std::string const& application_name, Time timestamp) = 0; }; } #endif ./dash/ResultViewGrid.h0000644000004100000410000001132313437202764015262 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_RESULTVIEWGRID_H #define UNITYSHELL_RESULTVIEWGRID_H #include #include "ResultView.h" #include "unity-shared/UBusWrapper.h" namespace unity { namespace dash { // will order its elements in to a grid that expands horizontally before vertically class ResultViewGrid : public ResultView { public: NUX_DECLARE_OBJECT_TYPE(ResultViewGrid, ResultView); ResultViewGrid(NUX_FILE_LINE_DECL); void SetModelRenderer(ResultRenderer* renderer); nux::Property horizontal_spacing; nux::Property vertical_spacing; nux::Property padding; sigc::signal selection_change; virtual int GetSelectedIndex() const; virtual void SetSelectedIndex(int index); virtual unsigned GetIndexAtPosition(int x, int y); virtual void Activate(LocalResult const& local_result, int index, ActivateType type); virtual void RenderResultTexture(ResultViewTexture::Ptr const& result_texture); virtual void GetResultDimensions(int& rows, int& columns); protected: void MouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void MouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags); void MouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags); virtual bool DndSourceDragBegin(); virtual nux::NBitmapData* DndSourceGetDragImage(); virtual std::list DndSourceGetDragTypes(); virtual const char* DndSourceGetDataForType(const char* type, int* size, int* format); virtual void DndSourceDragFinished(nux::DndAction result); virtual bool InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character); virtual bool AcceptKeyNavFocus(); virtual nux::Area* KeyNavIteration(nux::KeyNavDirection direction); virtual void OnKeyNavFocusChange(nux::Area* area, bool has_focus, nux::KeyNavDirection direction); void OnKeyDown(unsigned long event_type, unsigned long event_keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count); virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); virtual long ComputeContentSize(); virtual void UpdateRenderTextures(); virtual void AddResult(Result const& result); virtual void RemoveResult(Result const& result); // This is overridden so we can include position of results. virtual debug::ResultWrapper* CreateResultWrapper(Result const& result, int index); virtual void UpdateResultWrapper(debug::ResultWrapper* wrapper, Result const& result, int index); private: typedef std::tuple ResultListBounds; ResultListBounds GetVisableResults(); void DrawRow(nux::GraphicsEngine& GfxContext, ResultListBounds const& visible_bounds, int row_index, int y_position, nux::Geometry const& absolute_position); void QueueLazyLoad(); void QueueResultsChanged(); bool DoLazyLoad(); void UpdateScale(double scale); int GetItemsPerRow(); void SizeReallocate(); std::tuple GetResultPosition(LocalResult const& local_result); std::tuple GetResultPosition(const unsigned int& index); unsigned mouse_over_index_; int active_index_; nux::Property selected_index_; LocalResult focused_result_; LocalResult activated_result_; unsigned last_lazy_loaded_result_; bool all_results_preloaded_; int last_mouse_down_x_; int last_mouse_down_y_; LocalResult current_drag_result_; unsigned drag_index_; int recorded_dash_width_; int recorded_dash_height_; int mouse_last_x_; int mouse_last_y_; int extra_horizontal_spacing_; UBusManager ubus_; glib::Source::UniquePtr lazy_load_source_; glib::Source::UniquePtr results_changed_idle_; glib::Source::UniquePtr activate_timer_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_RESULTVIEWGRID_H ./dash/DashViewPrivate.h0000644000004100000410000000204413437202764015410 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Biscaro */ #ifndef UNITY_DASH_VIEW_PRIVATE_H_ #define UNITY_DASH_VIEW_PRIVATE_H_ #include #include namespace unity { namespace dash { namespace impl { struct ScopeFilter { std::string id; std::map filters; }; ScopeFilter parse_scope_uri(std::string const& uri); } // namespace impl } // namespace dash } // namespace unity #endif ./dash/FilterRatingsWidget.h0000644000004100000410000000331113437202764016262 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERRATINGSWIDGET_H #define UNITYSHELL_FILTERRATINGSWIDGET_H #include #include #include #include #include #include "FilterAllButton.h" #include "FilterExpanderLabel.h" namespace unity { namespace dash { class FilterBasicButton; class FilterRatingsButton; class FilterRatingsWidget : public FilterExpanderLabel { NUX_DECLARE_OBJECT_TYPE(FilterRatingsWidget, FilterExpanderLabel); public: FilterRatingsWidget(NUX_FILE_LINE_PROTO); void SetFilter(Filter::Ptr const& filter); std::string GetFilterType(); protected: void ClearRedirectedRenderChildArea(); private: void UpdateSize(); FilterAllButton* all_button_; FilterRatingsButton* ratings_; RatingsFilter::Ptr filter_; }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERRATINGSWIDGET_H ./dash/FilterFactory.cpp0000644000004100000410000000477413437202764015467 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include #include "FilterBasicButton.h" #include "FilterFactory.h" #include "FilterGenreWidget.h" #include "FilterMultiRangeWidget.h" #include "FilterRatingsWidget.h" DECLARE_LOGGER(logger, "unity.dash.filter.factory"); namespace { const std::string renderer_type_ratings = "filter-ratings"; const std::string renderer_type_multirange = "filter-multirange"; const std::string renderer_type_check_options = "filter-checkoption"; const std::string renderer_type_check_options_compact = "filter-checkoption-compact"; const std::string renderer_type_radio_options = "filter-radiooption"; } namespace unity { namespace dash { FilterExpanderLabel* FilterFactory::WidgetForFilter(Filter::Ptr const& filter) { std::string filter_type(filter->renderer_name); LOG_DEBUG(logger) << "building filter of type, " << filter_type; FilterExpanderLabel* widget = nullptr; if (filter_type == renderer_type_check_options) { widget = new FilterGenre(2, NUX_TRACKER_LOCATION); } else if (filter_type == renderer_type_check_options_compact) { widget = new FilterGenre(3, NUX_TRACKER_LOCATION); } else if (filter_type == renderer_type_ratings) { widget = new FilterRatingsWidget(NUX_TRACKER_LOCATION); } else if (filter_type == renderer_type_multirange) { widget = new FilterMultiRangeWidget(NUX_TRACKER_LOCATION); } else if (filter_type == renderer_type_radio_options) { widget = new FilterGenre(2, NUX_TRACKER_LOCATION); } else { LOG_WARNING(logger) << "Do not understand filter of type \"" << filter_type << "\""; } if (widget) widget->SetFilter(filter); return widget; } } // namespace dash } // namespace unity ./dash/FilterFactory.h0000644000004100000410000000217713437202764015127 0ustar www-datawww-data/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_FILTERFACTORY_H #define UNITYSHELL_FILTERFACTORY_H #include #include namespace unity { namespace dash { class FilterExpanderLabel; class FilterFactory { public: FilterExpanderLabel* WidgetForFilter(Filter::Ptr const& filter); }; } // namespace dash } // namespace unity #endif // UNITYSHELL_FILTERFACTORY_H ./dash/DashViewPrivate.cpp0000644000004100000410000000357613437202764015756 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Biscaro */ #include "DashViewPrivate.h" #include #include namespace unity { namespace dash { namespace impl { ScopeFilter parse_scope_uri(std::string const& uri) { ScopeFilter filter; filter.id = uri; std::size_t pos = uri.find("?"); // it's a real URI (with parameters) if (pos != std::string::npos) { // id is the uri from begining to the '?' position filter.id = uri.substr(0, pos); // the components are from '?' position to the end std::string components = uri.substr(++pos); // split components in tokens std::vector tokens; boost::split(tokens, components, boost::is_any_of("&")); for (std::string const& token : tokens) { // split each token in a pair std::size_t equals_pos = token.find("="); if (equals_pos != std::string::npos) { std::string key = token.substr(0, equals_pos); std::string value = token.substr(equals_pos + 1); // check if it's a filter if (boost::starts_with(key, "filter_")) { filter.filters[key.substr(7)] = value; } } } } return filter; } } // namespace impl } // namespace dash } // namespace unity ./resources/0000755000004100000410000000000013437202764013265 5ustar www-datawww-data./resources/progress_bar_trough.svg0000644000004100000410000001443213437202764020072 0ustar www-datawww-data image/svg+xml ./resources/suspend_highlight.png0000644000004100000410000000221713437202764017505 0ustar www-datawww-dataPNG  IHDRCUPLTEOtRNS "&)+-.079:<>ACDFGJVXY[`egjlnqvxz{}̧QIDATx]sFזdGQjB%8EIKLp騴|Dw:;sK/fҮö]S’ ېވOuQ^,5 @U J8#^8Gq8q$Ԡ5A jPԠ5A jPԠ5 jPԠUA˟+7i:6zێ{:pw1 |W\}tmB4/-PtU>N}BCG~.hE?4&@@M^W.@ρ ` zd1@8VTg36$ҠK/`*2a[(w Ǫ>?I8T7y~ΦfZL 8)u{ʭq訙[=\ن_pOha5[tVPR){+**%?8?Y6%BHa-O[u.Z*TbXÑ}y>@mU2T&dx@4ܽozr_RPϚƃ ZeCeqD TXw7{NYwv'zjWڮ[Bm鱓Z3@g5 jPԠ5A jPԠ5A jPԠ5A jPw;'JD/p =ue[#e=roDvV%_6>N?>/oI6IENDB`./resources/refine_gradient_panel_single_column.png0000644000004100000410000000016313437202764023215 0ustar www-datawww-dataPNG  IHDR g PLTEstRNS-./0rRIDATxc@pȄa6%OIENDB`./resources/launcher_icon_edge_150.svg0000644000004100000410000002242113437202764020171 0ustar www-datawww-data image/svg+xml ./resources/sheet_style_close_focused.svg0000644000004100000410000002203213437202764021232 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_outline_ltr_37.svg0000644000004100000410000000657513437202764021767 0ustar www-datawww-data image/svg+xml ./resources/squircle_glow_62.png0000644000004100000410000000303513437202764017162 0ustar www-datawww-dataPNG  IHDR>>sDsRGBbKGD pHYs  tIME :2P >IDAThݛnFI٪!i}>@.4& l]uȒ:o/4 "uor9`!\uE/ҵMs>U`itc1IsqFc|cPl97̱sʑZJXbL(Kl@)c=PΑب[8p:FMM~V^G*!z- 7t⦅). $>p kW kebrMK޿.rMcBtMB|O{fKDm# ~qUv4 ?J@G%8V#`3yi smإ[z:VW ه;x]gc$㢸<-<Ґc7HI͈Hcѡ|O]}=ЀM@KNƱI:{80WsK&Q{46J%V(J_lZ?unmPk2RKT{e=pɤ_9  hm(eufuS,IK+Xtbqe \I;o-UIENDB`./resources/maximize_dash_prelight.svg0000644000004100000410000000647513437202764020542 0ustar www-datawww-data image/svg+xml ./resources/squircle_shadow_62.png0000644000004100000410000000175613437202764017507 0ustar www-datawww-dataPNG  IHDR>>sDsRGBbKGD pHYs  tIME `YbnIDAThn0 alŰmXƱ]ZhݮEfFbOJBom[-ҵ3IϽ xʿXI|<@s`PW(<u̹E$.7H>zC`V\$8stmbgz Sϝo஍`Ku[ {G^y_I#d[ x_WfyUOG[Rsp*'w5WIc\cfi|#?\Sե/2w|d^#'Ykc|_R6(b<y-(s{5O@qU.*ts 7 ό h*@mDY7WN9\qXTXR-(E`N<4:/bęIENDB`./resources/round_outline_54x54.png0000644000004100000410000000237213437202764017536 0ustar www-datawww-dataPNG  IHDR66EjsRGBbKGDC pHYs  tIME 2 ȴzIDAThK%EOT;t *B_?+0H+ED{2\tfs}]Tz'CgzX sx ``GIsf`N.`qq PΏuޮ]h/lPVC~;Sy?Cs'+h!C/x M=uM;3[wE՛{,m^ӌeDܟȻ؆k_W +? (^@e= CE ȶ 3ԗxZf(leX5έc;=*v1W2=x Jb@Æj+B^6Xct ahR<-2\t#T Z{YHZ wPH) 2Xr/(2 A$d{kv3:bp˫uiR4ZG| $G$vF 0[^Ѫ;1G':%ԣ[f`y 9z8IS❨ͯ1C  W>>>|kW_]K~e)/b/\OgLtIENDB`./resources/close_dash.svg0000644000004100000410000000540413437202764016115 0ustar www-datawww-data image/svg+xml ./resources/launcher_icon_shine_150.svg0000644000004100000410000000643313437202764020400 0ustar www-datawww-data image/svg+xml ./resources/squircle_edge_54.png0000644000004100000410000000301713437202764017117 0ustar www-datawww-dataPNG  IHDR66PLTE󵵵ώ𳳳賳ųŶ赵ߴڿ󵵵"`etRNS $''****----0033339?????EHKKNNNNW``cccffffffffffiooooruuu~~~[[g-AIDATHǝ͎P-2i)-Pc2+2Ѹ4 \)ܸܘyn0Q"_t:J깗 IP)\)K̏+M]l=8^Hp|Ύz[,Ci `5;5 ջ?Yނg}n'~poba} |~%T uoMBibŘ;fx46)chK暜i3iueH̩a=AFTdmNeR]Bi ;Cedʲ29Dfl Ie211/[’vTNOr_Y*Sdd,̒Ϩ,F[ ۑwȖu*/Ym*r5ߧ6רLKzJ%oA g|[[4ֲ9uh3Ә>Ⓜߦs6PXr U<8E]'<E0o7Jҵ/;dEvU^7[Jw y6Y.@c Vueصp1p׺.~[xrK6Ah%HTl#IENDB`./resources/lens-nav-file.svg0000644000004100000410000000537013437202764016453 0ustar www-datawww-data image/svg+xml ./resources/preview_play.svg0000644000004100000410000000142213437202764016513 0ustar www-datawww-data ./resources/lens-nav-music.svg0000644000004100000410000001123213437202764016646 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_btt_37.svg0000644000004100000410000000517413437202764020212 0ustar www-datawww-data ./resources/emblem_apps.svg0000644000004100000410000000623413437202764016277 0ustar www-datawww-data image/svg+xml ./resources/launcher_bfb.png0000644000004100000410000002176613437202764016421 0ustar www-datawww-dataPNG  IHDRi7@ pHYs B(xtIME 91bKGD)#IDATx͝y$W}?<͌4IF BAZcű^6l +G,`{{a aEC` 1#!4]wV^0]QWUf~w~J~[wpFx?p# [@`^C9E j4Vq r ^ɨ~ޒ@rpoVeGPڎ=`7Wna)#(`Y8Wf%"!(!z_?8W`\4F0\ApۜRi&I ފTW^A?+%NR_]}q 8I/@@ߌȌj Go\dj(F+,6icL0.>> ҢE@@iWxPeuV+jV^lcaDdk1r3TȔj|Wa؍0d;6%p0X "Qx)#T8Ӝ\LC,gzI 6~\>(KDBDD^kP0%7m,Qؤ`?PBX7LtUc2$ɦ\Fc +ClE"Ph4I +FdsF΍H[oؐWg?CH$ФA&ނ@olg2d&"IvhwnEgM/q[QH,%f-V^n;CBd{O @ %+--0n ˱ x ÌG9-Mw^>I=$ByߗtAlߞT\(Y6JvPzhӋހ湙$RA;GeO"xo#$HbqMivvs\]f p;"zԯ|6FjiF&*#/Ve^HyRH^J J j<@m].zx@|\$EL.^K¨T%*UIU;w#TAHkx>I :VYҗ7uHKumyDҠ1fVFjIU⻓56g{a3>8䔛2GjE`Ž.}e+H$q9IQ(~>1{$xQV8*Fe $w8,Ɗq?KS!;u _rutce -4.EbWvܑg|6Գ Z]D±pdQq.+Cr[رUٴǢc3|:]ݖ*2K0_J+ =*<+9XڝೣzRךD$~\A4B1Y`ȯْ'b|,CQ{^ 9 gx`)l aҬ s^HνV'SbruEx|{335ZIYfY"~|70 X4Fn|/^- ~5,.;IuDN{&o^oOf13C:-i߿78wSpO}J[-$\g B :sPLDnӏ{fp'31s,PBunl=޳9ho_̅-sN0[ +rC 1,g;p7yJU"ȏ)ڔ~=2<58F* Z.]wk~5QO-b$JaܙrsEi)ee EmkB彣E-rhh~oN{Fcg/SF&!!!qVNOIDwZ .s6tk% +eg1W{5[p5k3g5u+!`+-ݭA78kFhНv~ߓ S\f~d]?c'EҢjh`?M^Ȯ={9ZD*rLi@S34̲@ൣOɼOwhPJ[>lSz:Bv^A^ǔC,9-r;~;1, Th3nX4'ҠTR mȍ# b,c9=y` )UlNY` 5KO#$QaȏIN; a"` BXC*bDm4ҏL uB4⡟ݶxqx'D2?L~|{KI!\;Ya^Xj"X-H3<41$)-T(xC[WT#$DPxS g9X!r noBn&5? E*Bg(qS7UFjdH2rt {nr X$2~=sx6ioU_!tHhѤ uO7q2r]Tߊ-u#mST&"PcC'^oqD>u]%BR,^OEKBH+j,ȘvRG;F ,#pL. ^7/lߖDء>9/Lm ?Ibxl:Y( %&:9?H W;ݏ"Y NARw i1Uk ƦBTφe\ӕNw_#pݲYlٷ"[A_iG+2X4VJ ,hɞAE*ϧT17~ˑ|EUhŵQF#A Do_f> w-ޞ}Ó;pFY'}iF"TVs]Xeп"p-l;8mX 1Oppq VSʟJ!b]*-#ګ"C0.q1^:U*ԉS(ܩJ`}K ken8.ݰ56W%H+Vel LzBf=c%Z%NqMCbjKbPo޾Hъݴi(bDBŖ"'*˃-t~8NQWbv[Ge:tS3\fK\Llj\z\#GButw =!*bKeO+w'Oo~wn4񳛮Nu$Nӛ4^B!2Rpz`mx\6|[[ęEBXkMyyEaeh[i;La@Bg9934SG2KgLjYT"O,팘T[][qK> !1m g+?G Uj4޷oF*+bE~y .n1k #SL;Ą~& :}}>bOd$;;4Uf1J2(2_N^XR~UFaT*R\ 4iP/_hn3%;~̼cZeR6RB {v ,XhV ok,ӢNmz_m7,j4Јm5UG+$99ƣ$=8`U\(VdĐ2xT U=F\:햟 XίMn'Uڔkc$ʭx EޖFjad$N|y o~ۛvgɌ|Ǫk8Ɔ?tcoH;p[}\cae^L/pyYJpuEVjiR7ub'Vn 8bԨRyoaā <[1O&1wGI sSWV %f"BUFj/c/a^/!x| 7[*53OF>/ɽעj)G1,=FϋXlf(ZYb!;c^DnX~w ;K?i;o-/,cSH%:y6H:U*Ta9O~`yKTG:;]3~]BZ"Le*:M$fRĜޟ=_B ߱!տ(Q{F -N&m= h򞽇۷y-H.{]CXo DYW\se7}+ =)v~=R湥b񺆔$y kyPМko\"b/R/-LRieRn:zN<xj!K|G fpKnpbM{4!) i!Fih4s}7>t'W;~BBG~쥹#}Ԟrq{C^-9bkr|g4\څ^(xȅ-"Mr9?MG?xk'?W߹*ȅ(Dwyѯ'H)ՆyֺKq.̷yLތ/da%s̳@̜|է;.$c83_)a.EEJo]\7U-ȁ9,޲' K]BzߺT^+xؑCe¥Q޴|L .|+5ipU{=gE88$b]'(P7}R]'וs[_t~gkV*U%Kl;x IĀKֱ*y(rPc>3\{Yah1BTF /չM=]w@Cٳg0j"$EÁJ)ȷF]Puppg*v9Jֳeri>.FCE<7٫H4!M^33f!vۙٵCiL .X/t.-Ca9~mz|5;.icX'߻ w[xC`H;}^E\j^ͲN˃HʝKZ kŠX߽U pw殔cϷ9FFfFye9<64kEy]0uliZ>`du,,#0&ɒX4[h#{. {͜>|'~qdtpbx09#V>C<bBMٕNH9!?;t [k[[W>]OEIxB:ӭ>աDcUJ :1]sڧP"4`Z`S9s|uo-SQP Ỷm~s&F}pcLMT'cXO|9q'&G|._~=#g0H+Xiu&D9LcG.M=O'x4S8Wk%,sQj .ӠHpT(ԎZc柝~r #M?5W4~)GR}dm1Y8v@賸,UfSu^ޖ$n[b:`AOMo1UZғ7^8W93}縙x()jϸS٭]_`s ԕV\U8Dݩ:U\zk~P Q>ܖF'79}s9zbFj\?7BXn6GZPK}Rޗ`AsJ !ٔ~E$Yq*^4bL a>ǹ lCbO7Zfy\xPc9mBOn\FCV(CXΑvZ%H6eYpߖN+\K]Ovp6 .+urZnk嚹fY RX #?ͧJZ ,E  V^X2^j-BєwUa.c?c/qS7uR5݂qƝEQ Ӳ鸚6@$2:~P V1,ĥ8RU1r|Ot@'/ ml*Z"5USf{Z{{:Z%Fan!kV -RXE*vC~+BBK I.ReiG%j`9lyz^ ,dSd $2M,__DNˏ؋؍D%VZjJ#k`Z"ȍ s[(\Or*i,Fc(03]AJp42yN&CK![ĭ@G*UT2Z aB 8y|'OM]2b-(1ZaWuy^ P6dm!cX*Q24BkMO*Z&n$^ƹM$87|!%N:A:rh.EJD&-'^QrjD&2ZhE/NkcZJ;:z:O^P5xJ0ϱ%͠!cm)Tr P ,TѤ%H vϙ5H#JTT7`\vj7H3iί|(KفΎ`!ZjvԒe0k0`fGZZi[ԮJ> gKvy`l6zJV]*Xl"Mr/sn#gj邮bTx+ Vi0ʴa5xJ(4~mdQ_gَJ}-dVy1)e]`#zZOM9 O`rS[ߨ/H> "L־V,l+'OJiX=ƶ7˷C`hp|)5Kt-ai1?oETA1,]/4 Ll͞/`If \Zaָ LU 5jぞaq-[[Rc_$YZ,1U*p~wL1/D=\_5FS@>!ie ,dl6y`+q/a" 8ΐ7&uک5Eӳ (f~|F}}A!z_Ȭ'WKS|c,u FKVPu7QTΞ*ʺ8uCt2>@t$ny@d6~0dPZWW 2~ %fKK뇦7 %,>d=#c寊=S+CRc叀j \+1.,٨_'\='i=F2c,>m/m"vtNi0AL :Da௺_M}(]EIENDB`./resources/launcher_icon_shadow_62.svg0000644000004100000410000000711113437202764020473 0ustar www-datawww-data image/svg+xml ./resources/lock_icon.png0000644000004100000410000000037513437202764015740 0ustar www-datawww-dataPNG  IHDRj ?PLTE5[tRNS QTWlyYIDATxՊ 0 !͹2Ĝh NSmE*{f+H茥YhPīs #;'M IENDB`./resources/dash-widgets.json0000644000004100000410000001212213437202764016541 0ustar www-datawww-data{ "stock-icons" : { "checkmark" : ["/usr/share/icons/unity/checkmark.svg", "32px", "32px"], "cross" : ["/usr/share/icons/unity/cross.svg", "32px", "32px"], "grid-view" : ["/usr/share/icons/unity/grid-view.svg", "32px", "32px"], "flow-view" : ["/usr/share/icons/unity/flow-view.svg", "32px", "32px"], "star" : ["/usr/share/icons/unity/star.svg", "32px", "32px"], "triangle" : ["/usr/share/icons/unity/curved-triangle.svg", "32px", "32px"], "bag" : ["/usr/share/icons/unity/bag.svg", "32px", "32px"], "next" : ["/usr/share/icons/unity/next.svg", "32px", "32px"], "prev" : ["/usr/share/icons/unity/prev.svg", "32px", "32px"], "play" : ["/usr/share/icons/unity/play.svg", "32px", "32px"] }, "regular-text" : { "text-color" : "#ffffff", "text-opacity" : 1.0, "text-size" : 13.0, "text-mode" : "normal", "text-weight" : "regular" }, "comments": { "states" : ["ACTIVE", "NORMAL","PRELIGHT","SELECTED","INSENSITIVE"] }, "button-icon": { "color" : ["#ffffff", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF"], "opacity" : [ 1.0, 1.0, 1.0, 0.8, 0.8], "overlay-opacity": [ 0.30, 0.48, 0.48, 0.45, 0.45], "overlay-mode" : [ "normal","multiply", "multiply", "normal", "normal"], "blur-size" : [ 5, 0, 0, 0, 0] }, "icon-only" : { "color" : "#123456", "opacity" : 1.0, "overlay-opacity": 0.2, "overlay-mode" : "normal", "blur-size" : 6 }, "lens-nav-bar" : { "icon-height" : 20, "icon-gap" : 40 }, "button-label": { "border-opacity" : [ 0.8, 0.13, 0.13, 0.13, 0.13], "border-color" : ["#ffffff", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF"], "border-size" : [ 2.0, 1.0, 1.0, 0.5, 0.5], "border-radius" : 4.0, "text-size" : 1.0, "text-color" : ["#ffffff", "#ffffff", "#ffffff", "#ffffff", "#ffffff"], "text-opacity" : [ 1.0, 1.0, 1.0, 1.0, 1.0], "fill-color" : ["#FFFFFF", "#000000", "#000000", "#000000", "#000000"], "fill-opacity" : [ 0.13, 0.0, 0.0, 0.0, 0.0], "overlay-opacity": [ 0.1, 0.1, 0.1, 0.0, 0.0], "overlay-mode" : [ "normal", "normal", "normal", "normal", "normal"], "blur-size" : [ 1, 1, 1, 0, 0] }, "track-view" : { "line-gap" : 26.0, "heading-list-gap" : 30, "left-padding" : 20 }, "row-caption" : { "main-text-color" : "#ffffff", "main-text-opacity" : 1.0, "main-text-size" : 17.0, "main-text-weight" : "regular", "sub-text-color" : "#ffffff", "sub-text-opacity" : 0.5, "sub-text-size" : 13.0, "sub-text-weight" : "regular", "icon-main-gap" : 10, "main-sub-gap" : 15, "sub-arrow-gap" : 10 }, "preview-heading-small" : { "main-title-size" : 23.0, "main-title-color" : "#ffffff", "main-title-opacity" : 1.0, "main-title-mode" : "normal", "main-title-weight" : "regular", "sub-title-size" : 17.0, "sub-title-color" : "#ffffff", "sub-title-opacity" : 1.0, "sub-title-mode" : "normal", "sub-title-weight" : "regular", "main-sub-gap" : 15, "padding" : 10 }, "preview-heading" : { "main-title-size" : 30.0, "main-title-color" : "#ffffff", "main-title-opacity" : 1.0, "main-title-mode" : "normal", "main-title-weight" : "regular", "sub-title-size" : 17.0, "sub-title-color" : "#ffffff", "sub-title-opacity" : 1.0, "sub-title-mode" : "normal", "sub-title-weight" : "regular", "main-sub-gap" : 15, "padding" : 10 }, "scrollbar" : { "color" : "#fff", "opacity" : 1.0, "size" : 8, "buttons-size" : 0, "corner-radius" : 3 }, "scrollbar-overlay": { "color" : "#fff", "opacity" : 1.0, "size" : 3, "corner-radius" : 1.5 }, "scrollbar-track": { "color" : "#fff", "opacity" : 0.4 }, "filter-pane" : { "width" : 330, "title-size" : 17.0, "title-color" : "#ffffff", "title-opacity" : 1.0, "title-mode" : "normal", "title-style" : "bold", "title-arrow-gap" : 10, "button-height" : 30, "border-size" : 1 }, "separator" : { "size" : 1.0, "color" : "#ffffff", "opacity" : 0.15, "overlay-opacity": 0.47, "overlay-mode" : "normal", "blur-size" : 6 }, "filter-caption" : { "text-size" : 17, "text-color" : "#ffffff", "text-opacity" : 1.0, "text-mode" : "normal", "text-weight" : "bold", "text-arrow-gap" : 10 } } ./resources/category_gradient.png0000644000004100000410000001024013437202764017462 0ustar www-datawww-dataPNG  IHDRugO oPLTEP%tRNS  !"#$}A2zIDATxW@ av ^UyO+6鸿yH}Jfa6ќbVbR`[8#\!b)`xuA}ud.TKUX];VGʲcx'׬'{w"GCQ8{0f~Kڥ<%U_̐b1*jvsw,cv{P;x]V1:ϺdQʼ ;);Cmu+|QBλ,(9ٕ]uʹ+ui<Yoz s}unvY2Ru2SNlx;םUr{RAogWغ zE]w5e~B]h;Nݏ[cs]h]ykC]Ŏ6{VvC= FٙzV'(G;)(ԕ#Ew}ΎwĝlwP[9:)$p7ﲬPβi쳸:Ϻ~Y;6)[Y; AGdx.eq/QGu `'aG5®Ow>u$Q wlq_ns~iܡ )Po=xN8;&aQ7pu]l73`xű[Bpb]m1i;}$o>&۶W5_,+4Z(xл{8vQ+L#b _Ⱥ)ŏw+pGĻq" 6;JY̻:b.anf]wY*zcWԁW.S{oͺ@tƮIv]N_?إ%Ep7*و[IFkӊ4ܟ|bwN>T!o wN)Ί|H.<*s!ۢE JwoQp'x;BMyNt嬻kbI"vcEc"}'ZIH%'8ml=Hkd8L^V;?Y<g(Q;(kCuo mwDƔ;<]Sb]U@''^ग़?kt'zٮ8MN/o+vsd.el_]VW=nȒ/]x$`oyt:mjo~gy~YtY7wGxj}n<::,+Ew};'nuRlr磅sw>^DؕҎzv:l^c.i?]޸R=RA.x>y? [-Ac^P4;ZPw.U8MaQ=z^Gmuן;e>G_.{-d,g[jb1)q5Ly!k%D߱ Ov͇ƃ@ %w^?w;vQ$\gq==6_@Ʒc>PG>lN'fw:NZ|Sz"Oba ;T H#CغxIsAlY0opucou{f 3Wœ_# X+ "fuz}{̺W{-IENDB`./resources/dialog_border_corner.png0000644000004100000410000000133513437202764020141 0ustar www-datawww-dataPNG  IHDR ^PLTE90@7O-FV5M<<<qqq hhh```xvt{ywwutjhfkkhihehhhDB?EB@FDA><9-+(<96+)%1/,#!;;; ===#!%"/-+HHH&$!;CT]tRNS "##%&'-3799??@JNNOUWX[_`ddddddeefffgh'pIDATxݏR0 *FD(Zlݭ[u}_ ;h <37~sO"]1D0 , hMbQz4Y3k)bLrm;6gpfm7^3zh$)&eu|iR~]O4C*R7sӱ~lPo8?b7[\kH7 1P!w 5c\=IENDB`./resources/minimize_dash_disabled.svg0000644000004100000410000000474013437202764020462 0ustar www-datawww-data image/svg+xml ./resources/dialog_border_top.png0000644000004100000410000000032313437202764017447 0ustar www-datawww-dataPNG  IHDR(i-PLTE90@7V5M;;;5tRNSdddddd=[/FIDATxλ 0ѱw6 %L@Im|*IDч6b8 <IENDB`./resources/switcher_background.png0000644000004100000410000000234013437202764020021 0ustar www-datawww-dataPNG  IHDRddG>>444...  YntRNS  !!!!""#$$%%%&&''()))**,,-../01112245567789;<>>@ABBCCDDFGGLMNNOPQQRwIDAThn@88m)TX@|SE|bQ*q59cډZ9N:lڲF;,F/\WKqWtC .yݸ 4gJ-AdLE%3hJ!tevcxV̄PBRϜY@2CVe:TɠI5S iZ3MC% UK JI8NbH& ZӪ⊳T08fY3uf24EYUE&Q0CP 0cfUUѶR lUa77ݶb&+t/͵7B ]3lxmhPOO# " z%bn;MquG 4#鶃!ZO(I zjJh]AOzW>W2wdg \rɐdѴvBW3kDIrKehZmU?;ObuY&>7گ/iYif_>:oh巴>¾Y+4sýl8Wz9eh)$[-!taL)=|K#rΎJ"^w"D @"D @"OǦA.\lty86ixθy3ey_sˑ>cs`+&- image/svg+xml ./resources/preview_previous.svg0000644000004100000410000000374313437202764017432 0ustar www-datawww-data ./resources/launcher_arrow_outline_rtl_37.svg0000644000004100000410000000662213437202764021760 0ustar www-datawww-data image/svg+xml ./resources/dialog_border_left.png0000644000004100000410000000033413437202764017601 0ustar www-datawww-dataPNG  IHDR(ͺ 6PLTE90@7V5M;;;tRNS'ddddddqCIDATxcV6v.nN~~^dbdiA>d*- ĸt0'#QG{no=|ۗIENDB`./resources/star_selected.png0000644000004100000410000000130213437202764016610 0ustar www-datawww-dataPNG  IHDRr ߔsRGBbKGD pHYs  tIME.BIDATHǽkqղiV զHʏ悥JY N ȕ\Ȳ"l4j H͏wOO}=s||O.IHCY?Ux"+ۋM1v϶q 'lxAo}f*Y5WMTM} CaELC"aZ>l@ }9֢mAEEފ+%1$iۘ;|s]Ђ \$qF"O17f&Kc<`$ H?9wb4IG9ɧ'滰 `+8,"M<ed_@ȑ/-0N4xYXg0t8QMsENeB }:p~+1K.TO >X_C/-jC(9rlph}觧XW:lV&Җq#xXq(WW9?Ќ`4ݩm\-ȕu-iLG ]h e{,wT"/&iKڷIENDB`./resources/pattern_overlay.png0000644000004100000410000000014713437202764017213 0ustar www-datawww-dataPNG  IHDR/nLPLTEU|ltRNS@OIDATxc`dT<&zIENDB`./resources/warning_icon.png0000644000004100000410000000047113437202764016452 0ustar www-datawww-dataPNG  IHDRW?TPLTE1=tRNS )*IJKPn yIDATx}]0E"h>M>~Mln|CmnpK /|vC ?" ĝ&lLfmN7Yj7̋])9k"rj6ʿDi=IENDB`./resources/progress_bar_fill.svg0000644000004100000410000001204513437202764017506 0ustar www-datawww-data image/svg+xml ./resources/star-outline.svg0000644000004100000410000000156513437202764016443 0ustar www-datawww-data ./resources/unmaximize_dash_disabled.svg0000644000004100000410000000500513437202764021022 0ustar www-datawww-data image/svg+xml ./resources/maximize_dash.svg0000644000004100000410000000470613437202764016637 0ustar www-datawww-data image/svg+xml ./resources/next.svg0000644000004100000410000000221413437202764014763 0ustar www-datawww-data ./resources/lockscreen_highlight.png0000644000004100000410000000165213437202764020156 0ustar www-datawww-dataPNG  IHDRCUPLTEADtRNS "$)+-.0279>CDFGHJMX`begjlns{̊FIIDATx[S\;Q?>)AQPl݈hDjZ7Og:8A܇]'@ɬj<vk,"9f;0ms6m6PXbQ®E.EEEX$ * * * * * * !Ao,I5ɼ-nRw>2Z=B O_^ Agu;Pv=%@t pnΠMoM_HgY 䮠W[p |.-W:uWe =+(@ * Ї Zje`:|PڛT=#WvU1 5NnR)N* _)4#RhZ#ZДJ *5tO]8r+rMpוBm0ZP{ rkKZ^?TPATPATPATPATPATPATh.,x߳(ڋf !q 6,6b@kRms^7٣sڴAϱ6ǙqAIENDB`./resources/maximize_dash_disabled.svg0000644000004100000410000000473413437202764020467 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_btt_19.svg0000644000004100000410000000463413437202764020212 0ustar www-datawww-data ./resources/launcher_arrow_ttb_19.svg0000644000004100000410000000442713437202764020212 0ustar www-datawww-data ./resources/launcher_icon_glow_200.svg0000644000004100000410000001212013437202764020224 0ustar www-datawww-data image/svg+xml ./resources/launcher_pip_ltr_19.svg0000644000004100000410000000626313437202764017660 0ustar www-datawww-data image/svg+xml ./resources/launcher_pip_btt_19.svg0000644000004100000410000000463113437202764017645 0ustar www-datawww-data ./resources/dash_group_unexpand.png0000644000004100000410000000025113437202764020026 0ustar www-datawww-dataPNG  IHDR O"$PLTEw tRNS!il\)IDATxc 8Ab2@,@+D;+HvIENDB`./resources/bag.svg0000644000004100000410000000130613437202764014537 0ustar www-datawww-data ./resources/play.svg0000644000004100000410000000172213437202764014755 0ustar www-datawww-data ./resources/emblem_others.svg0000644000004100000410000000173313437202764016637 0ustar www-datawww-data ./resources/emblem_video.svg0000644000004100000410000000473213437202764016443 0ustar www-datawww-data image/svg+xml ./resources/dash_sheen.png0000644000004100000410000001214613437202764016100 0ustar www-datawww-dataPNG  IHDR6YMPLTEFp^tRNS  "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcȄIDATxv@hX"U$@ I4H\(h}9+e>aN(eOψT:>yAD"-rWRUBpui7 4-Gxk]8rRyt]Z<^}{ V zx0(j6Vnw I>Q} pxBMX`Clbww6`Kbf0%&Í~H$ǝ!$F;:U]xLJ{)mѲLuMꥢe$e2(;Ip~nOt+<f1,"솑$'|3M%a3;ca8Ḣ7f",F (v×0|Z0ݪUe؊pԆEX#;aCoÈ00 pp6a2 Z óaF%х) ciFx$| oYη.نbXE5'\0|W# oa'b`MoXE7%~1H䆑%Hx${n4# cmc pp5ƒ~"A၎J_S2 au2{LEƒa7\=*]̆9(aa,Kp0 ?N 0>m}'֜pB<+aa7gjf8#6\ߺl6xE> IXEF̶&A¸(|g4$U&a~e%OC+k-hN%^,gO2—9C'{f'%QN\/a4(vaf4p R/0*# ˹aUV"fOg$p>@qYbNEXF0pF 0pN ¼0E!T!,"K- ;atOAל{(q~FfNI8\o0\&ODQ~*aF03W$j$W#&aهI8jNEX'ua+unNQAxkEXEco-Kd]-a-@ѕ ϣ qpD 3ˆAQNqEX@Wv˄qa7L…5Bs\ oTW ix΄EX#<(10  s璄a8/0öC¨:aW3V",‹$K3|FnۍaH>LKlXEx/I8՞  #ƙL8+fa^P~LGxpp0DXF# 9'5,m9TFxSv\(#czF Zy0/DXNp%̰v$p%gDX_0~G1Ո8 +$a^8aQ"8ӕ t-DXN_v0@,"GH 77R 3sRE jV6l$l%"„c6.Wk#ܭvak ewaL s㧹x)"J$XMEH-.=a~5 ٣Q." o?B ,"wQxGHa[Ѝ%,"?vO`  SXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXaVXa^ /^Xa/‹VX +Vx +xa^ /^Xa/‹VX +Vx +xa^ /^Xap )VNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNaVNzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFzaFšp סp סp v@`9C 0w ߡ0w s0w s0w s0w s0w s ߡ ߡ ߡ ߡ ߡ ߡ s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s ߡ ߡ ߡp/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/  0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w s0w sw/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ p/ g[7hIENDB`./resources/star_deselected.png0000644000004100000410000000070513437202764017127 0ustar www-datawww-dataPNG  IHDRE/rPLTE0&tRNS  !"#$%&ƝIDATxu  Ej] VRz+ֵ"` Ip.yν5==`n`O,=/E¯ooW: R|?P)(b#|#S`xގS;$ Vi,y|P^tYX wJ qwmlRm)/+ xӪt,íZ/,dˆIlÔ,F\jdޏW2 [/ ŃIENDB`./resources/emblem_books.svg0000644000004100000410000000140513437202764016444 0ustar www-datawww-data ./resources/dash_bottom_left_corner_mask.png0000644000004100000410000000032713437202764021675 0ustar www-datawww-dataPNG  IHDR2\6PLTEctRNS'QWZr?IDATx Aw,WȫuN7{2U ̙v,5&!&f);&J?IENDB`./resources/hibernate.png0000644000004100000410000000264013437202764015736 0ustar www-datawww-dataPNG  IHDRCU#PLTE2sY`tRNS !$'*-036W2u'EO jPԠ5A jPԠ5A jPԠgPԠI/ BRؑpi4VΡ]1(hJ?'/17qrQn+&u_0Z[9~LS;v4_eIZ Jt iB_ZН>v: 8)tu$i]W<|ACNaA>OWI@)a[j$/3}m̀.^F^@YOt ~8`F"/1YCw^y&o`WҜCp@f@MB/`93[@)7]Im [Πl@U2)p4eROiE~Z=h]i00-ӝ1韧Me (TMhj3&\Mg $ 8 U[;eQVBb`g(^p@Ax+j @- :j`['i} {U2kF0\{6IkRiz0hs lj1iQcIZjD/ T:hn ./resources/lockscreen_cof.png0000644000004100000410000001132613437202764016755 0ustar www-datawww-dataPNG  IHDRBBI]c pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATh޵mP vauYC&P$8+1TN}im2iM:$3JQC4QijX5!ej/a1dED(]g_v={/KҀپq^D'H<<&l3}FkT_s3a$=ܷl"u9ΊHE!GDd?b1[ )0}O}$SG[h˩A@10g !4K꭭^_4M@|aW_ڇӶkCEtGqĐ@zΊ_0*zǡ@K㾸?)RlfmVV]iY{G_!Z&1k۾O826mC+7BMKXJ!82ї[Ё_"+P)ϒE:$@qH;B:#2]Wu-%^!|%(B݊W>VK+&˱9}} ɼ.GnfpJwȷ_?꩒EwUfn)\ksmM1L )fY:kʒwֺYqg2 btD!]3&#)1[u3a.]f܆%a*9۵M%ˢ*P2cB_cHJL/$gɶI.#ze5hgN4K)++1+qE = YFٹ"_eY2 ӵ nZO,膾ydYWJ?4 dRߠo7fgyBa Ր6AWtE4sk0ypt#p &\`"#7:"upY#\p_S2 DsFCjB C!/7,8BQ-8"o=&Ouk`A ~~r|}~,(KU[1 ZK> ] Aa: ҍmP$Z*$3͵mPr+pO]uJH#+aCqAzQǦ ~*LʉY!/)OXx~Sۛ33SV}-#kGH~G_]#BQW9?c!%2ЂDjb:Рy_l(/c13=pe'թ5ɝJcCK5;۬Kfu 5e, <"ue=܉R.S~lDu^"EPH!O͏1{}EyP9_ Ձ!xѓH"zBBcˑ^ vbg}lzƞN#⾷sJfvyţ%]o[.a"IO5AihAn䖗jP^F.)z2篺|&T_E&\q#vlQ*"LĄDd @eӛ0>0A޼ʯ4g[bDԔOOҺ+ ޑJC]RIL$ /H #=d|B12HEOc IBK,: I-#tĢ ^C}VIENDB`./resources/squircle_shine_54.png0000644000004100000410000000532513437202764017325 0ustar www-datawww-dataPNG  IHDR66&LVtEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp OJIIDATXýͮG9g|86&VG b˂ ",X@bĚ[`bFB,@$VB |~fe=5s|tkSSU2=Nxxm::n` pFO`% ^\gPn NXr{.+8KX$ ٰa˚5k.g>ka?laNA,GDR R7},a;Q*EQeb( Ė-vb!0H(B9PD"R8ʤqZZƥ.*EeE_VrxYiAEX%"DH%B5/q])P(**.E*&#wC) XSNZe[KS@)YA"\-\]T*(d)wQJ ܳ&."T.II]/OTǃ5"+Edv1vtz-lRZp8ё9$GW"LqrV S"e#<㞻,(Iɉ;wARL)"g2 c_pwfG Y%JNNd?N*. C21VޠA"oX%BI*c MNt$'u|4.VV!](VrB,ha3}Lo tۉcV8&[H`n E/3.Ƿha͂-kA߶ۙIENDB`./resources/launcher_icon_back_150.svg0000644000004100000410000001100613437202764020162 0ustar www-datawww-data image/svg+xml ./resources/lens-nav-photo.svg0000644000004100000410000001123213437202764016657 0ustar www-datawww-data image/svg+xml ./resources/squircle_base_54.png0000644000004100000410000000233113437202764017123 0ustar www-datawww-dataPNG  IHDR66EjsRGBbKGD pHYs  tIME +wYIDAThANHv۱ @)-f\K$8g@G, dMAHbYFyhjb翪&~GDT`(pS6<ec&0uAT6hX`Gnx<Eac4kkki\8omm%`6f666R* d12W4{˛^gݕpZ\ץf9<Տ?FFo 6LNtpp0899iJ/`q[Ja/۶K[7i2̳DeNڷ$"{.Xncfh to]j vuEY ,XfR)%tSJ d Jڪ$R @{ $YnkU0 WDT1M{ފ`J)9`P(d^193E`Q}3G8iVO3WL& FDF bKF$+$PJJ)犅wER"PJq^12Mu3Mi*=(d)˲x\A(`Kq&4cʲ,seYA61qh#"*ٶ+ڶ͟rbr>XRV-//+TW,T*+.Qly^;y)M x燐RJm3]M&`Rh@*b8ժqVVS" j5mjZJD\bumz]wvvb!ۅB`gg'&Ô{VkVk̽Ù:ス=۫_ F3eE+++8R(eiј]ws6>/ ` bf}3̢@ӂ>-DTZ !VWW ? 0c|T&Y˪|̚/eB(_wz\ :Gli:aLaۇU nVIENDB`./resources/emblem_music.svg0000644000004100000410000000201013437202764016440 0ustar www-datawww-data ./resources/minimize_dash.svg0000644000004100000410000000472613437202764016637 0ustar www-datawww-data image/svg+xml ./resources/category_gradient_no_refine.png0000644000004100000410000001627413437202764021523 0ustar www-datawww-dataPNG  IHDR\J PLTEaY,tRNS  !"#$%&'()*+\3IDATx]I0ä/RLH0@ڑ~lC`@Vӡ?TE ?A4xcWqy7kn?`í Yo3'[aAxwCe!Qeœ#KC۠U:i!kq ׋سur`r4٪4_;HLͯJ}{Q]qp\-sC zaAA3q "l- @ o"^83r?,۫@(s1byv2ݫwXN}Riʹ ?㕯d'Hr%vm3uN_Ԗ޽awp^,b4́m_娻trXUBXuHKP%]щ"Lt,XʠjEvx{oщ?Tj*D:%d\1 ۣ8LiKC##9 }.-QϽT_pv>JFf0)9jοP)ӮClFVn++ #*oՖ `)˖`8XUW"yA*9jFplO_7c_O{D"MIpw0ID9at6;g1Ts9 y4igG{uhޮtґwYT"* 6םxT^&afDhs#%6PѐctLp慰&ⱷy_X4Y(C#Y]3l,ĕ|Kw֕N0a{!LwC!B Y#ry6 L%z;:3GS&_핡!ɔLfHk"ۚ /:N;LS+g6Ä>0<X*ު9LqSvYX WVyt܊rbY&DbƸ5#{foSN{y 3\%[idojixF:*QL;/淀B1L) vUDO:蟏w_ȿF,I>S[fdQJ.~8<ܤT/60 \tHḠ {d|S*z?$tD%vű++ʺt'b@<3FqnknE쪥!;:` s*:@¢3 r댥$ILqRPHL?'~>,h2xIf$YiU#4i"9K= ky+XNVد9O`o3|>꿂[R_fGZG\-`^TSR| ))Y=(U|bƗoOy}О3oK j#E">ES̀3?q>v_FYB ̉h"tfZ..NO_e: ?w@QaN ~|Yz/I*&>:I9 _ʿyl%%%+@>iA!GZn+ySJGt6RyKd.+Gϥi3aoQ5iFT{:3$fuf6GD~SeZe07F Ѧx(3*B" }xˤ;p Z{[sZZU;zPt}9]^jZI`ʟges h~ߨ2T@riuR /v} uEz:綱hB}p`GݙQlr9]F#Xӫ ' FO&c$ yIlrgtYhA[TQopfi/:8IwL]%'ә 5mЎZrVֶ֜#ζ u&N#Wki 2{o$y`e,Na}- [?w{E* cWokAa=Fsy᷺ kg{VK7cOwk-e2~ag3mO@b 6qsJkhtxZo\W4v縲<5%\e dQc-CG[:OL$IijH .Ȱߞt8F{${"@!żr(K٬ֆ^iy'd9f8gH  ,v h.x*ty8`d:"2:i,"?%u"0).[dAfAebYʷQoS6 ܞfvwu4RY|RbiK`1!D ruESҢ QEP/j1:-J)7 -XD,TS2G9Q 'AKp$yw/g03Tuq4~y=jŬ-lgi =U\XKL0pC.m``g@~Z1po =6~mdEsyjPBuN_tx+  Oisy@! :tf_U4) W@fCD_/z>I_`tۧҥ(r:rEhqĒB] ,֢S\4|8hxQ—\f14rwt?NA;I xO\;Z[=>+s8()JsR3T;mG nbv{I~ .-dbJ5l1KA CmJR==)pBGӏܱa \ kfxuH4-H88_͡&;jZ:ڿ^9 f|9^gBOg[zR架|P{{,ơ,ovYg9zp6Yr:h*DïBh=[wM;?~օ,kD7_s`KV-[$<++[8<9ԭsݗU$io~>>u_k92eji$ y<Ӈbfޣ?p>oq )4Ά~ݕ3Q䖧NLzc5.^5P bK/~˖Q&'))2ar1r&cX*FL3k"X["~(ZmN&GXԏ` %1Iϟ_1Qd_c{6~'#xougf}N7f+!_/" {n:#S!ͼV[k%ޫ'71s8a:ՂlWڶ'ye BظW{zy/ 9K%_zvP3CFOՔt~;xOJ`6@lo _YsvъߒSԠanw8a_/^SIE1zS{/uw e9ҌDF c9oI`.`WwA¨|U&֢>W(lxHxC$Wչ?H _p]u}d+7Z +[~|4:LFfܥA<1>~W{encW\|!og*9w\ Z͡DÕ {8&2pdnL InM'`߻\륫o?:mR 9$/Ұ|?{);e*iӕr]x ޳\)QC#~ ĶgUᔵmQC޸H \wa~lK[}4_,/> ;\#Q{/ad W?7y=K3HR3W-@{Hg8C8-RHs_ǹzIK>ȹ9=rgΣi>U$}c;'Вvׂt7PsR5v{{&.ŧmbuKY;u8wzr6bǵ~W{,.t[|oA-C"~]!xnX|pżXT.בru -֚sdqXM\->@KqIy>|HYB8i-2]s-׎]}V<ӄk}ߟ5rm<ֿa%W}>SIͣ -)ݲrwKgwm]"γmOeΫo c>eX7q:CZ~}9?١6y˿HM͇ͼF9>뷌;^k~t~(q ۡ|+n;4=+.th|m~] gP.ȧx~vz)q(J8Mw|ZlnCt$\˷D-]%umlߵݛ `[2.!|Gy֢>F @Yc-ůCmz>f)#CqG[uc=mX%iٸ8Ҏ?9Щl1%.tYQϱn:qaPnڰn)9ݼ=/t{]Lo0/룈izrαl٥?7?[wam@l_H<2)wfc+M1O<ٱ&QV6.eC'\\$f>һ[hl6=e^u/'IENDB`./resources/minimize_dash_pressed.svg0000644000004100000410000000473613437202764020365 0ustar www-datawww-data image/svg+xml ./resources/launcher_pressure_effect_rotated.png0000644000004100000410000000017113437202764022561 0ustar www-datawww-dataPNG  IHDRןsBIT|d0IDAT-A @P@SJ3 ̡%\a' G۵?IENDB`./resources/launcher_icon_back_54.svg0000644000004100000410000000462013437202764020111 0ustar www-datawww-data image/svg+xml ./resources/pause.svg0000644000004100000410000000113513437202764015123 0ustar www-datawww-data ./resources/arrow_right.png0000644000004100000410000000047613437202764016331 0ustar www-datawww-dataPNG  IHDR ,sRGBbKGD pHYs  tIME* IDAT8˵AfA''tU 9@*RBK  )md/v槏a6yyo]b'_zTM bR+|a. p\xK@7fI,p/k. |щvP9ۜz,FmF_KE-:.|VLdBIENDB`./resources/lens-nav-home.svg0000644000004100000410000000653213437202764016465 0ustar www-datawww-data image/svg+xml ./resources/restart_highlight.png0000644000004100000410000000240113437202764017503 0ustar www-datawww-dataPNG  IHDRCUPLTE3[tRNS $&)+-.02579:<>CDFGJMRVXY[^`begjlnqsvxz{}/!DIDATxmSWk#(؂RKiXZ`kC\N6[ɻsN͛_f'ɜٛfW:{M:+Lz Ni'5ZǭO/u/ey2nnSR%SDy zCOA#W(0A jPԠ5A jPԠ5A H5Ԡ5Aa@;A@oгWkFQQq'ǟ)m\<zi# sUEShtu`!2:=!T@FA6l%"놞ɅS[YKhCu^Hf\CuZ@=G9{=TQ|̶ jj3u֪Q 4y^8؜KjخQY=OP)o%LrdƚYd:\_^X\^?3%A jPԠ5A jPԠ5A jPԠ5A jP image/svg+xml ./resources/launcher_icon_shadow_200.svg0000644000004100000410000000713413437202764020552 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_outline_btt_19.svg0000644000004100000410000000435113437202764021745 0ustar www-datawww-data ./resources/launcher_pressure_effect.png0000644000004100000410000000030513437202764021036 0ustar www-datawww-dataPNG  IHDR}psRGB pHYs  tIME 52~`tEXtCommentCreated with GIMPWbKGD#2$IDATcf``  , $3H*.}IENDB`./resources/round_shine_54x54.png0000644000004100000410000000334313437202764017164 0ustar www-datawww-dataPNG  IHDR66&LVtEXtSoftwareAdobe ImageReadyqe<IDATXŘK$I?x䣆fF=Bb8܁KpfEXEBB#5 w7YDdVVu .<o[<~ߎa#Si Ja%v_Ưқ4CHS#B'P# 03{oˑaߦ-o Mu}t# 6av55FsoT˗x뿞,۟gfŠ*M=hhVU**͛ E 3pߟxUE7͛op[uZ)TJm~'zoF^?m)Fej ͭpjToּ泪Xp˚E>XŎb:ÏUJt֔,$ӼVjX)V-eՍ 6mZ)F,pČ S "(J5oxiͳrLQ*3#[D0DlI4 .i/n 䎷phuO>>TȬ1%q""ہw|Mocmԅ{yt=k4/)#۬wd[5 tgalDgTug4"1.ڒ=7--Ć׽1F$@ S6A)UN޺fN @Ѣ)dʶjgfa& !lb;z/6uD45emP)#Z^R~A;CfSI4ASxUդ)ceCͼעLW7ƬAXD㋴Jm!enBUQ},/cVe[UxǢEɂ`֓Y68MmB㬳KZl4rpff`fMeLܑ٘EΜ[H1ڍ2AX*O^a&f,٣aqe`z (9 v}jU;"¬9m mtr3b8!mH8g^,0kdCdQr_Ҙ5ls,ْ͢7;\f5,zjæIիU5Qm16AL2ɺI%ה<ނ}m?/_ 'z6^a `l/ne{V?\n>R۾?$>[J?[ c[ᣘWF4vb/.i=}Wfp`OּÎrͶ{kl'a )(?\?j_hiԫ5Q>!;]ya_-_3Æyxoy}%d}kPs@]+,/K>!>:IENDB`./resources/shutdown.png0000644000004100000410000000240313437202764015645 0ustar www-datawww-dataPNG  IHDRCUPLTE}TtRNS !'*-09?EHKNQTWZ]`cfilorux{~9J_IDATxn @?@DFGm4f%~=\FH CX,‡;ItT@TгBZ@;0(/ * * * * * *Z \kSsqzZ!JIsteS%--eRҤ'GKlfrId5;KMz{ /IENDB`./resources/launcher_icon_selected_back_150.svg0000644000004100000410000000470713437202764022044 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_outline_btt_37.svg0000644000004100000410000000507413437202764021750 0ustar www-datawww-data ./resources/launcher_pip_rtl_19.svg0000644000004100000410000000621513437202764017655 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_outline_rtl_19.svg0000644000004100000410000000701313437202764021753 0ustar www-datawww-data image/svg+xml ./resources/lens-nav-app.svg0000644000004100000410000001044013437202764016306 0ustar www-datawww-data image/svg+xml ./resources/close_dash_prelight.svg0000644000004100000410000000770613437202764020022 0ustar www-datawww-data image/svg+xml ./resources/search_spin.svg0000644000004100000410000001212213437202764016302 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_ltr_19.svg0000644000004100000410000000675113437202764020224 0ustar www-datawww-data image/svg+xml ./resources/refine_gradient_dash.png0000644000004100000410000003535013437202764020125 0ustar www-datawww-dataPNG  IHDR?WPLTEztRNS ; :#IDATxan0 :mc@atȝ+ڗ'BzM!*| -um{[w{$_گ[- wj%>?i/LW$\WM[-ά+)k臽sHhU5JF?9؞ 9|ٯ,wup3{/;/=S}?5=zo} ^+w;=||n~Wݟސ<$ߊo);ww%|K//Qrstx5OUz_on tw7`]^gx+oA7<35^gs|;NnDٹށ#9."b_na^}ȏG3 *[k_BXyڽ/'~uA~|Qmޏg~/w_1__j}5?zs]XwB:){7)¥ۀ?obg4t3ow~ܪ_%<薟A}tLsbܣ?y^ o|^)ss.83,_m%'!uՒ6C v (?{`R݉~ .FiI? -esx[ow^sϝ8%*E|Kxh-˓_W͎.)/jwez+/^ؙ>[=Q#vz?,|ߧީ~fyn%w3MQw#,tvW.YOZc z{IR h!^e2>7 _Y?ы ^14:W6_I}]J](z@'D{0ù0~n~׸tG0WdKEן?3woiŠTn}2hQϟ\P(`ʜv?pW%88Gǭc?dxv*5H ؟ ggN+H^6K>s8=i~_R|>??4}^wߊϯ׳[J'/y]݂:|fǢ/$hs3[ U 'g/o|g{W_]e~^V|U{p v|~y>?7[fϠ?į..aQ+loB!~oq'd~?c^=c/-?/o?"E`|۟)G7pʜk\ =WfR~.oPR>`ߪݗ J>se {vJ/oSx ._ORc~KABvm-!|g 0/hI~;?t>2TJ `1=x`<9q\*?/_oN^HI O Z̯Xx-oWΞ/g . N θO~*5ޟ씱{*= G^3?AwszV.:`_x &~h~svY2T7~ |w9~C~Π$ _#C_߇^o.{/nu_1wUpu%XrY_EσWfw6^{?YbB۝w>E}W6`?oޏ ]>;J,9z~݃Ճ2؃GUCX@+okKusG#0_ W,`Z,o㫀~s_9ڸHU,vQ8$w툁^ߞ?s?~o:?8?9\޸%ez}7_RZbǧhY̯p>?agX[ S W(=>c!4`)1u|\|` w X W+O§/XN00G^@*ٿ+' Urt$b߼B`,d+[.䷳X/A+ue `pϯ:^J =?3^`._{y ͖ v U,`#Te,(ak23Q߃mΟg8@eo0Tݫ|_P0 -yL)|o9).B`8.!|of~#]֩5o{O`&%!7NK<0_&SʯR }E.{#zv)Z?: lA;j~{2?Ƀ#d@x_;J i;-P8=?ߍBr.CB!Am]}0A\לceC ]bzg2AX *G2F_t`yf~u6}xp$c#J:`1Bg04\!~8 `BU32?wtoR WPB(& ia6k? %{@%>_,`Ex4@۵38@,3ep3[ t{B`AW*DY~!2w2Jˀ~^ `E!8H<`5%B3(`g@@ ʄr~& 2ǨWf|׭VF^/T=P wE~Of,,3@ <>X_FgX.33@=tb C6pR`4o%xm.ءdx -$[)~gg Wɏp X%!>@{p43C *+L(5A%E-ɺv(`-BiMt4;$!caG z(Ƭo}뀓:}/3բv*e,-= GZE,]e\XѫsDhC裀?-/gЁgj0`}n~{$ofrT`s`c x>Bw]@.O@0p0AD |wdH[;y\R!7ͯrbwGL2ws_x߁z9 rZȊ(ys6pWQ~ xBri **~[*GT@ V)CZa߼Ђ%A p[o?%Egh``b+>?/Kp44_>#w!p1\.J" L0[AV2Aa߀Dp+X_W]q y!hq&A;ⷫj>>bK%%@@Kju+6 RbU#Jo9B'dQjY~_,w=t(~&-|8-淥A(J?uj ]+FX n~<>  Z`^\n~FYUු10|+p&Bۡ#S3k_E{@زGS ,2Br|Y6 2`00g>!i;!MϠK n~;ǻA䘝/-u2$n~ t|ҮP&e wBR9RwP>X+.z=YH \t-1>_42{XX?.zpЫ[0:` V縆ޏ0pLu<AsJ7}BLoHid'G j+jڇVK {@bn y2sY 3LԀ޽zK;s E Ew#)bWye;eH_8A?#L^:t:7,V8aw1Lj~<7V`~+$}Vyn~;xG}(o܄ 僽N:R0+e/Ffа|ߞa pi9 a X ߡT^p._UϗzΠ["IE߀ .U\ v[XvS![[.u x' ^ҵ/oE/̄lT۵p " p4M3#/GX_]CR'2" 6𐽄gz\{o^N-JE^:e2Nk}w8A?:#hWi +0 EVcOH>*g$+D=!M$]u^gh{,3n%,1E07j P#X^q{S n~巜1H_oe/{z+>A7]\ iͰaWR;>:F$ECsފò+3,q>/esg_y,] 8QeWe!CubW^uV3hhR{Z~} ށ5J/dt mZ$_Iw2[~ ˿W75az'8d/vCЁ%,8xce~ʥwh=8 /߀+/3wsy8G(BRH;;8ê:B~8>;p߮2( 18ߵ[/ߣdv}>U}fL0Q߮a`1z¯1t ؂ 6CnLwQHfkl͉ O[X/az'ᰎK#4"5{8ŏz+;0,uU Q;k''Abz}?p`Z̰f|߮rXFf(BXϠCwwpۇf}t*{/,km u_aDy,`7}+<=uHi3J&cv Mow^X1~2|~&Uy`8<\v#H{T淣TNہw&RDpz m+,k(K;p1Z~D>vq3ƑPgMk 蠶ܚKXi$7Cc0nCAt`1 Px3_U 6jH1(sA7e>fͭ7rXb_ 6o]dHEQo_o}XY$y{m5ԾJvXoFo9oUČ/}yu{_k68Fzҹ~ЈV 9,F`xr8>&kH Bح 8~;e}#߂f.LCpۛ;EXe )%?(T->?n>"fTI_$ ~}<4oh 9BA}b87M3۔ޝe_H|8%`cRhv 7'波c {క~Ag`wiJ>JH{#%}g\`Pp^bx?XamtRBoAw=hV[&T֕M3 #~Q/5{ޒ|Ww/wxK<Y`ىU`}%hsɯ&0,S!L.SS F<6;z8<] :~ {o`o"5[ sH(>h+GI1 z{81gi4 [tV+b3QCR~ ߰m~T8jH2J{q,Iy!_iZHsLGab_KwA`vZ)1xE/aKt9o]:]WFaf?Y-K{ x_`CwN|]N`]:.9,X7\א|7sXUC .-CJ/!7 ~֏39,>ˠb.p>hoΊ9,ى5B#pu ޭ})csX0G!LLyD[~qSTd4äJ 759=E6=?%_u__7YCyoTqe_5r\#!k6W}yozRyX,ǡOK BNV^N1l/TjH -w-%}g !=_gN.,mA(om 'EwO"uBK׽AxbxoHF, Ë٣u]{쯃>,X6bXpY^sf4}7oDzԮ~goUBǁů,8w%Ict:J1 N;! !R_6Jg6‹9szԊyɛg^,+WpyVVoHLX_=c%op,G}:׍F,ajhߐ,a>è21:bK7?ZW_+M .Ye u7Z)Q !|+"2e+ع?Jw`X)_ lG";AsT7r<%0XQx1=Х~0Muw27lq[*s 8᭿u'#D^N{Wr: ,7!/蹇rNJ>,ЏNo`u5G n㯸K9[X7Xz&9aTQ(XN=T?~;7X6ot)?E3QjtG h>AgUNX}C_硿YVjeG>ʂYࢽu$# JHJ)U*r*ߐ}TSӁ%+?J0h~hQގ'4Hu$o0l?c0MO_dɖa ~ḧ́7t7u[yC#]o9ın:-jB#tyB`,2'uvXLVk;·6jچͧ<6L{4:0z`px+ $u>6"2|sO#Zd{E!Ɛ/Wo`A1Bel84Zk974Ch򈦆"\&B{?Ǡ࿡Fi`Zoh{^,lc o}o`^3_z9ӄH3XNJyqO8ыE9XzvrLډ'!:Qh4D?;Ku >hw;BZw6c4 _r6p"zk ͢1LWYbIh3πKX6!Z7n~r^u%YBsYmMDwwz?~ߧW%rW-N8|(go2e8=c$Ws$<380bww)Mf>a9'ڵStx|ڨIrB7K@Oi =pcBk +`mDU[J09 }JGv8kZT6g$)8=c%-]|,ߐș'EUΐfz$J1!-)v:"i`M([1!Α,V&D~eY@}Np"i;寸Opb ޗ>Gߔ h"E ;D:$iJ%frg`Ys:l= QqJvsw>ɿOIȗscf +&Lte"D2Y1C eK| +B{'d^C' Z-l֮jП yV=#}^ .cY=BSǛA3=)}9Dz:B^Ú7>]}u<>oX&D>Á6Gp\8xw9sQhO]I!\"tԝ  ãcK{`b$/[a|im B;JrV|Y0XϒPoE x{zM3֥o{V _n^ NHc]Ke5[g9hǽK8>Xd3.k oeO -#\e 4}/xϏ9g01j} X,\;,(> Hv΂#Kc7l# %_r1㚻)cyT_6'_Gg]]> v_l00!@<.MIq6 FK #+tBXSW#<<x{wZ\ct'xyyRuP u U0x q_6X*Qk;Ǚ0R3w_!:""x⽸>ef{eqCY IW//,iO] ^xK|{Rm?KxiJhݨ\?lsW$y$Txhi@We.'ICIENDB`./resources/shutdown_highlight.png0000644000004100000410000000233613437202764017701 0ustar www-datawww-dataPNG  IHDRCUPLTE+StRNS "$&)+-.02579:@}Rpх>A7S H6AbXHs Y(B@ȇ.{Eh 8-rzj/-t;jShAŠ}`\ 4ΧUPS{.ǿ:X y-4fR)Awj |= PW@Ku/xz;ՠG=`z(@@4Up.Y'V`' t1-vYEν*TW2mхWDWX)CeH6*[рROtѕC_X?ҍ*]g.B2wg  ]>P?PY|g' Ng\P3Z5A jPԠ5A jPԠ5A jPԠ5Ayw0/E5o€ gB0%xEHP)áfhc$ٍQ~;k88Uu֣zIENDB`./resources/preview_pause.svg0000644000004100000410000000106713437202764016670 0ustar www-datawww-data ./resources/hibernate_highlight.png0000644000004100000410000000255713437202764017774 0ustar www-datawww-dataPNG  IHDRCUPLTEr(e^tRNS "$&)+-.02579:<>ACDFGHJORTVXY[^`begjlnqsvxz{}IDATxkSg/lw`61M9К4J#."~mt"yaevgòoo۵,f#yQȵq\F^ATyx {@\C()P+3>)}DԤDA ɠ5A jPԠ5A jPԠ5A q5A jПoj -[\Cv[S[9|}M7w pUnKߺǰ&r =}Ġ}iijo ͭ7D%n]q$u!nr^yX )8 6ua.xt!|/( !e/ɝI*0h/&u- RgI9t{|(^h)7ol{N!/YN4i w4mNj'qi,-ŰPq=Ϡx?VkŵWbxiWcEai]`; sʺSe`T;3]rz^z[wBCn%+ h9jBe Y7t}PEy s [r q IW5j4]VښRGT@v:Zk!q|Ƞս/<_Tjªo :ke׺ *kO2\+FR(T^׻vŠ5A jPԠ5A jPԠ5A jPԠ5A3ЎQ'9/EOk+=OBb%x8I(ə"[LQr $Ok1Zϱqd/kkIENDB`./resources/overlay_top_left_tile.png0000644000004100000410000000072413437202764020370 0ustar www-datawww-dataPNG  IHDR;֕J pHYs  tIME stEXtCommentCreated with GIMPWNIDAT(ϝMJAt͈8abW(*$@H]fvDBd$H?.`zQ]ֆ3@2 !VaE)0a1F=]]l(`Vu~q9Xfgc92ILnR@ ==Ю8bss''SɞIENDB`./resources/video_missing.png0000644000004100000410000000070213437202764016631 0ustar www-datawww-dataPNG  IHDR`@<~PLTE}})tRNS"%'=BEHMSSU[kx~7IDATxˮP E7G|rAqf΀섐)5Xi ͩ(" &0 WKux r$@~z/ײ}6 f~5Ak$G^|(+2޳( Om0ڴlT9Ac5hmVz\e7yE^4.hh}&0YA%{RR`êIENDB`./resources/information_icon.svg0000644000004100000410000000165613437202764017353 0ustar www-datawww-data ./resources/dash_bottom_left_corner.png0000644000004100000410000000130313437202764020655 0ustar www-datawww-dataPNG  IHDR2\#PLTE|||:::onlYWUsssZXUNMJFDACA>32/32/985)'$*'%;;; " #!,)&+)& 3atRNS  "###$'((*,,--0112345789::;<<=>@ABBCCCDEEFGJNPSTZ\_ddddddfffijPpIDAT8ͽN1.@D** ?dSh8>gM3GԷ^0LVݺf9d_5ŇCߩMDjm _%R 4qX !ĩ% G8o'>^_I!3{3Rđ]/$)?<^Ȥbֲ"1k3HY4YxW8v@DXDU(|؂ JIENDB`./resources/dash_bottom_right_corner.png0000644000004100000410000000211713437202764021044 0ustar www-datawww-dataPNG  IHDR22)xPLTEtttccc<<???@@ABCCDGHNOUUXY]_cddddddeeeeffghjjZbIDATxs@D"JZ4&055hP4xV%j=i[oz߲Ү:o߼ zuS|.ҏh%^#h%Z/A(-[Sl;K2e*U#-UoV㈴n[͖<[>ti[>v]˻m(T-k"-os*%|s9^y$!nYu;`Cڲd$"Z~=9$wɨec]-_ukXeY֔K3߬t׆-?߾yu߹7CeR)U37MuZVђTyg״`֕{L_ ӨkJ70:Z ҇S+^&neh$:][rQH >ZTEJA_EFQ,E!X ,c+C0oh¹?IENDB`./resources/sheet_style_close_focused_pressed.svg0000644000004100000410000001737613437202764022776 0ustar www-datawww-data image/svg+xml ./resources/dash_top_right_corner.png0000644000004100000410000000133713437202764020345 0ustar www-datawww-dataPNG  IHDR2Dv;PLTE|||:::}{ysroonlkigSRNB@=FDBCA>0.+><9975-+(32. ;;;!  " /-)#!+)%+)&(itRNS  "###$'(*,,--0112345778999::;<<=>?@ABBCDEFGJNNPWXZ[_`ddddddeeffghjj(IDAT8KKA{z(Hb<S h6OxֆPTw'\tݴdZ9jn5dV$!#Rvrìv}\_Y^^/Χ<0Cv7ڥ]Ra|]kfU0iEaX^-qDJ":t`RϓD+=qRXVv$+5QcqoБ~@Q.IENDB`./resources/panel_shadow.png0000644000004100000410000000023713437202764016441 0ustar www-datawww-dataPNG  IHDR*sRGB pHYs  tIME *يbKGD̿#IDATc`P` @xJL|LWIENDB`./resources/dash_group_expand.png0000644000004100000410000000023313437202764017463 0ustar www-datawww-dataPNG  IHDR O"PLTER tRNS !i͘#IDATxc@lL0' a14u N`IENDB`./resources/suspend.png0000644000004100000410000000231113437202764015451 0ustar www-datawww-dataPNG  IHDRCUPLTET PtRNS !$'*36EHKNQTWZ`cfilrux{~9Q15IDATx]SFGV"[Q$dY5$CM손и$V%ʸKi{3pѬlrv/;(eyqsBPO{vju'V~؄Кu{Abk{DB{w\eȖ3.,~7\vG9ɇ {5klSyP1b<;]@<4H^C>#y(d$|$$ I.@HK7$E,!vX4퉂f$C+~6(24c3r& M 9$D] ץ[\b:Lyz{*h>}䍰~e$ YmШ򡤪d$1ɻty w5BHeCY(\(u5,w> n횡n>U:'tQ7/ҕ-]CT~7>]|>e\fC2a R8sAa<@a_LY–8LY*7mߎrnކRݓO_,t?(T UBP*T UBP*T~ UP*T Bjn1o(˔F=3Rƀ!=ic 9{>r; ~5sؙq7rIENDB`./resources/dash_top_tile.png0000644000004100000410000000031013437202764016603 0ustar www-datawww-dataPNG  IHDR!g 6PLTE;;;tRNS'2 ./resources/launcher_icon_glow_62.svg.save0000644000004100000410000000644413437202764021123 0ustar www-datawww-data image/svg+xml ./resources/prev.svg0000644000004100000410000000222113437202764014757 0ustar www-datawww-data ./resources/refine_gradient_corner.png0000644000004100000410000000023513437202764020470 0ustar www-datawww-dataPNG  IHDR ?PLTE tRNS r(IDATx}Ʒ 0H 21ӉxR9IENDB`./resources/emblem_clothes.svg0000644000004100000410000000124213437202764016767 0ustar www-datawww-data ./resources/album_missing_preview.png0000644000004100000410000000162213437202764020366 0ustar www-datawww-dataPNG  IHDR||9PLTEF4DtRNS $'*-36YZ=ϙ _w.?[q>+FwoM<ǣ\p{713Xm|rtwsݕǜys+mu>2fįSt;fįYf\|[ ŧW|G|_|G|_|/}/k=ƿ_gC>5Zww5(~0>]L ,,EX>&(19ʢͨ$K.,,.A?K?Yp.K?u, ݨ_ image/svg+xml ./resources/search_close.svg0000644000004100000410000001022513437202764016440 0ustar www-datawww-data image/svg+xml ./resources/dash_noise.png0000644000004100000410000005656313437202764016126 0ustar www-datawww-dataPNG  IHDRL\sRGB pHYs  tIME d{rbKGD\IDATz0%B8:8iw矙5O皨^Ѫh!~0WE$DM ت%H!y" eFZJ T-rM$BIkι(~VD|\G4[` K1v CZoac,L‹(ڼ@å ȏγLDb}/(8Onl3POLփH H?dh8b0Gט{:{+A}? P%oв{/IG,0x[6n]RjC<1IoOO22"~jνJK^zg%L3/9Y cIL*U[ єYh8ې'6H!QN iԳŴwT))hHXuEHvg poi>KWa9Y &!`οgCxNlFFZn]~<(IqIMک5C|fK+i8]4 +vk&?N/gѝSaNrLs_JpŻƤ^ GLJ|iy߶˘Q$^nTHVhWcw2<}J 0B[\DfX,i|FTxhRs-1sn5 \-@_Ǚ&j_RS]#ȴ m/@Ӓ|\ꢭxVlj0J`S Q WZݦ.6f$\S^Uy ``䏖Y%R_':8Ge8*@u_ZUnF>Y>lV*Rf xJLv|77n`RW̒RjSl00Q)Ƿ>\-e8LtccP-aGP[x^}i$[_yZT `GRQBTJk쯴tMג~zoOND<1! daΛ:DPP.[w񌟤$0̀WDA&dnc3[e!a]=x`JC3 `cBmcN}~|;I"IJChX1I"S|.Nռ;0Opcpߟ7ۃ"N3وT2c3jS@x 7ob-8CDG\rcȱ1NvԬ`S;B=لL i ,'9tHƻ8uyujYxfR|m]秐ɌeO0-Օʷz~Z11ʹJh9L+!R%4^8Crp+ASs4l_}\6{K1jn%pjk +ET+6:hjkrm ]wZP l$j@N&8V䰃QO%,%t~(uEŹք]Q_Q&R~g{K*3w9Qn]Kr/ DN܂En$ t9w}~ |j{UIIZ+A,T&Si!Xcb IoUoa[eSvgz PK۪W58 n1<'}m\a$3jt\ j\@"* ?, .@X%6RHit!(۱PV.'ϩNpu"m?7EHaLh~'"*ҟ5'撨Հ?kdQcN7Cs;sʍ[W'3z[$n=)[&ِwHUiNjzKt!Kz5b_F}iscѡ} @Ģm7P`B jX8$&7{*NRU8'\w}PO8 o܈[" p,(4DN'P[>2jtRj3j (7CYze+xf!ׯ/<,sfbh &ӿ1)>t#S%fU"kK*PWE/tk4(3Ê7+#tޗ{2tkPѲe?PjCQ ' O"ZaA'N#Sw 8 QG_g{g۹Sa!IDwme2<4P(I Z8Q&G+JI/`\)FI}/ %Gm+fF,"IjK hw8) ꣭,ywvY2Mz}bJ1k0jb/(0Ć!ƌ& CXi~251h}:(s,6逨%2Q-,sPC!gLĦHu6nb* ܊쨆 Rȼ-睞*ݪ@UVW&q$UY)"̃/YjA½&0%#I@Ƒ(F'ZHU4a Spm2[[KBB-Xzq0xxSf%3d*᜗G2̤00#0R 2y3)'o0q~ǻ#K,7/1MJIDCާ+_Xnˋ (r]'3:u1!q#b(LU׍"f$Q[P+3vxolj;r{ ,/F( EMJPo)8*I`:&8"=MsK vʼn(mLV[*Uǝ,XAæK5v 8 $,W^X0- Z5Q&dpն/ʸ 'FjܙbUym;i;!+!΂#x&5›Bq\iwg`oIg&%D +Lu6Eq3%`Ä 72ar8\Tfۛb{[mF|go%V1͊2X.;)/V58{U{h@¦ݮEYɀy3k2e|7r5/Sh_D卿olAX?yf`v:Lۇt?t` 6=;RxroרQޘt鶬FW)},db o6enX*SH(P#OoW"&؝q\cƿ4NH.e$UN#m]zerfpXDtLQʬRkEaL۽_Ud*uNFw4"\q},n{ ߈ewp1n}CHuA֛*Y̑L)ó'6?ٗ$)جPN xM- v0jA 7Be"ۅV=E*-Jna .ijMAR~z(X,6N#g;;%^~w=aE[)) Ӕf0@g IgHR ec*6F1H~'Ŕeԉrݘ#S z̴H-[ޱ7?D䧥78~?Mi JL:>nU\ZL tm=9 ]3EK6@^2|Мp+|n'4OՆ0-t Pu _MC-Yl2 K0e\xcf,:론](l|eW }Br+b2 ə Y97C0JFs{..WjC[R,JRӣ lx*X}{dL"`] oʴo欚A4ݝF˞JoIt6paaO Z KhI~.BeatnWA*qn~7c-1KCTrƦDa-()&=7D;`fu?G:犋[Lw+iH,!qQ=U˰TD[ȊGi˧ ̦0},- \wJgۼ^L&I!6́6N) 3q漇iڸKٙ;L%@Fb >Fڮ@cZ&z K( Teu*ʢ0.ZҾ&m\M0ی6 VGDM_wPM!cb$)D}?9yvJ 9%51HP]@IFnYް!5zxynn-aH6aPN`y/{On7-'#*NȘZQW}GB;RcQkKQ)"a,gǐ._1.^S\vd7`= OSz3XEQqq#먊B-vcuTh|9%^W6ah}Ʒ Z13^B:1b--MmFmU6G6S2cH9 < A,.Nr70T4G;\z"y$L>FH{%?Z2=0NTVEb -97oҐlf'Wl+m IYNoS BΏ RU.7s@ixMH~HoƘ=DHZ!xB{9stÁ4-),0,*Vvz͕J9Ѥ<N9uAV|hm.3({!S~ԯ]BVkg<%)|&&L'Y(9r:=H;P5y q[V)}0x-PAN&3r"~N/t۾եIv4=` \42'YU[A9;s`RuLLWJA$ 6k_̘DQA_Q^`5;5m@h<6NI>[ʉ O)ǚ(P/",Z4Sᖴz_13Y_V!д@%ۉ ;?g}6 zDq}dX>'x&0V>.{-9f M&l<}S7ᶝ*0H`=aoX=LR]0p]י7/3"8OoWmd `-FOHdƄb&{4NVc)@`04o\[kromKE!9"`z8H:+=2D6Ԍ.7FFjhNo~+m*aV}L<9̉xmƨg<ֈإC:#w~Ɲ& ;j!s$zbR^S]yfbyolCnrDjQ ~dLiLj2O%W[y˰' #2@UTz= BܤVxJ-31`c,U ׮{w(-eK) ŊN"$Vr@]l0/Tɠ@l+]ZNZ#9YcD[2|" uw7/5p( K}I7yUÄAn[CA3W{ye#Qqn3yMJpx͞5QlD- [N(.˿mZR4``>kxgf4)൥G J+Z*9\йt,)a<_-3c1 [ z]8JHO]m:o6pKl~[:fre0sk3_5Wk'^u]M,#-zcxhwb:Cݧ?t`9d7淀0uu12ԖR[S l,rX}g7_<O\Zc^GTG@CDљLl7%pt ZTpോb8 `:L\\fxWHu.;i}d*wPny;-rLS XYIp+$3oNfnhfEYs)0𕿬C;9z_i:ӓ_hjˋIcJk0~m740&E&H+Ik:`+ >9{H,F}3-KD8F =.XA]C{":B߁4o Ii FFO)_2ENQljaT2[>ɨDujiVX*c76Q- E/9B!l]yKXwH;R+tlOVܕh;j+SܷEcN>#׸GHh*2V J:cbǺa4g@f_/, åUjIʠQM38%w - #,%Vq~y(skQd?jcMdiygwG912~QtuFei+^@R W(!GkzHn޽ 2? \*I(I;?I^{`؀W #3o؈Z yt߰W/+ jU>jM5rɈWEXӤ$O9vE1.h:j]:w DbJ7A=.ciZlLPHI EF&VV%(.<;eJ_:#l0$nz-6 %! c8ʋ٘*MAFǹ huv5EHū#*YF}Lp4$V-:]V ˫̖ͺBQ~n.8Јס fnv?@31eY)UR!f?j,3w w_ȭ,5͙{iGY )"Aɂ/ϳمN8 emmF'{o1k*3&gAfdxGYL1qr2 dq9ĭc TWֻ[>I (zm7 p*[aqt@$ GC\yThx@S; 8QT8d$` ty`d %l9Ul6 W{_yr@]+`h4bQM~rM̰# nP8¼cw|p .Ky]h{qq`"8\ZZ8P^gB&bJnzCbq[]l|+$}wyIK”lJ)v2FZ'Wv8\,6O|VyKB̶mm"cCNХN25pPzvM LF =iDƙK8cfK"_}6{'¦0HkBa&mS."%DЫFsN<6i]\ߚp гŒx$Hx1P=yy`EqO~PD&+άk:.azJy'ܩBėeTNS;ǮLBEݾS[VlEj#]@:B1K:6[Fﹻ(SK3.⒒4c" %bh+EOl\i`aZ.@շvI-|;G[lc)0NLKNV581J""0$L .Q֘2JL`G%i,\_*%>>|x8?I}ڀ`!&^ۃ)`w72!=0j"6Wuఛ̅` ;ܸFDdeNd(Ʒ%f3t;VAngy^Ct {j\M}C>ϓb1R_U[+R'٣<4VXx Q+ߥ監U)xGG7' &CO\{Yݱ ۇG10Qf]QY ?I#m\&D畐lszA8z bۋ[NG4mXTPe86:ni.[>ɪأ49W[, pAe>`Ha)cH$L:_ơnt;LNtxܚypb7߹D{Gv']XJe+j=EŹDx= 7|H{߱`4rS.bZn3GC( VP6YFHZyIm#Z@TbmlE:BHW^/B->JMwؚ^y-uV62o)S.7Vq֐ y+5b¨4l=b?֟rN52ʓ8[lFdm\gro5(NsWk"Cb[!"+fyf)mHfvW35Pwe}g)TX,x łkBpW㿆Q]-#$>4 @B*&Y8 *si8'!Lb`$=_Ą,ؐÔCpjWX<)c<8@1_foIEAo7" r7ʥXt6-_[_M>B[ۏ;4l -⬤GwiYY6ՀfZ{<<1}ڐbCnk.,^c^"<`d ՕS~!]NiǏcڞ$Ur.?XDo8lG ZQT b>VF(Y#K;-|)uFn=+[bcCq2*c\" XfLD.i1Ȁ3]2Gxn "{h0$T?KIjmNkz18϶fh,04!.N;Y{UK2(hJi( |/,}Xz0W -jc"sVytHSkY3 mf/+?i{B~Tgmǡ01𝍲AXAq_UTLpo߁۞_sӡ`~bjW: ˵g"LLͲD.k,k!jB;MI`)[yچiüv6M[7|L.H;}aLa5#/AhI@rnۼgsUeؔWH_hr¹Jo1{t'FlW0~릥BBA9]2`rg³vv |0U}zGs,T!wbF}#X6HQ$ 7[ڑْ_]D)|M;70^Yz] <"vc^&˅|ĶcRlɣx :ʱ q,'ѕE,gG~-:[V)aNf#lKɚJYIS*?yYIa1It9FLlU8Q$}7xCٝC 0*ބ)ܯ Ƙ 6U4P* Nr3 Q0K5=Q Ҹ&BZlF3$ĝou >{32%%)'o{ ,}Fc5ۃҩ0AUg&[Ұpo}@bM`Ck} >q+kX@ 0oXr\nfoP`ɘv;ֆxwˋ.5&b2 Qe텼O-Tzi`}n7YIWB0`^7zB@qT;R:evsLAܧ11e ibMDr`Mf7yְCkɂozMڱwЊ zŸ4{@$Y;C$gN(ݞNz.~cƸR@В w1cÑ^S/'Kz ̱#J3 NN:%W|ƩՓ;׀a(۳.Dҭ8:jk$9! O]+ӝ:M0R.Q YF5<|?'zt3K(}HA:>fʰS CiIL~GuއϪU/=5r&v8,)}B7_;T~L6ۅZDI:=2]ڢ 6CY.],@k5V3P%nɒ^sX`Y/"{ ^ j= ,i M>{G F[FI\kS{ٹy߸l_-ϸoEf\`qP=Z콪ÜʉH…ZqbPSmƀ)sEi5A̱LoD+_vPXqV?usJ7LF{Owc=p¿hd;|8k}Y;m(bh•&o9瘱8;їI TfBΗ+9jb߀a G/= HM]|mRz7%;򩸄wo >\曂GE4 ioH;ʼnW5ML}h7Xhv5u6 l՘ߙC k(7~` JLrfseH&_}ފKNB2cy<gP~.+SyM4WR^`~iźݗ@uww.1##CawڭEihG{rOEy> Ÿ}!xX%`٭.]v^Q| 8F)CmH)$MM.6_E/@2(UzC55=NV;$2k)&!k^L<-/M{x?F`.ŒýقA`˯t|>W򩮀"u<ƙH*$#&dDER ϵ!-R?0=0įZ,7~ڵ>JQX9ktNnlm%`.SE-T1Df mϮ d>[8s,deG#`\Gr̬qљ)Pb<1C~PٝcHxyOsGX>Q~+<0joKE|neL\;IOTn$}"F^o+\ 2٫xW%:^Ye!˚kr!h]NF+< Cw%$^i0Ep5~cTo!AVl][6(Bԉ~ @!Xy=6#M #RP񖒡YE/V H-˵;ˆ8,_(04E;N"Mm" CH[[f[ ZP Abh=F r]O"u=F_W a]X֛xw+*=ؗhMnQe+OƘLnpDn)F '@a8xD+AV|zLFba>iUۈOAݸ[w~4x`TǪt=&9vKKl| @C"3lmx:TJ 5  }2c$"O2~'R!?*t@jوV#BP,7V߰b T `/M0sz)hZ|E >l0uqDz?Vx5i;+(6tUu8~phcGn ?xqK`1ϻ 7ULټygczprWOVhWmʱB.N\)='8dOKzb΀$ %*3?y goq]vJJjm.$Rs>0_Ϫƾ}Luv@3%3&Ϟ%+3>僽,4()u =5|Sd j ~ >rҡXqLiq#0%34$fDYl$r A~Lo%[Y w ~`Qu"W:'5#ԬߪdlqM0}T`R8; `p7I:THPb#=zO,ή{70<K%`Ya6] NG5x6c`ګS B̀-nlǑ`gY_Y>8":MR BԸR/MC\bfjp:2 ˃ sR2Vr&v3"v2 V\^LsPO|갽+23{Qml@60(]C u΂aaft ɓμI3}PÌ] ƚ@KH6`Է)%\ <}0)Iy0E!q|JR [MhPs@+2,G5Š(D: zFMn=()= 9cJIG~LjϮs:\*Okq}Rͭ#^{)b[00H"#8G1G&b\EcprmHm 6n0OsILR.nj訯.D^. zU]&Stt5"8 jP|nD'zyM"rGЕǀ $F\n{uh_A+4Lj9Yt5Av?̲cQ)^xȨ=钛dy8,zOvkFcc*_,N I@TKLȚm'5rܟSn] 4K] Rk~Y jW--Mdlgx(^DԚ`3]~~GwR^?&Zb{|_\qC鍓Q"w%OV^1pw][)kS#d+$'] Rt1b_&9I5!Wcs>jϖDzC@! 覧]>C+EdcRB0[:[@-āi%&nԵ3ٝ|^>U8>K_99UeoM6i-k@~6HzkFSЩÍ!1h~xܕ( [moC_ nqt!8_<ϼ to [rg_^l{a<ǖ{EM2B3 W>O%~u|vC٠P-=&i3!cD%=p?79"12wTD]!f{5ل"yfoh+KYĊ}Ӿg#DtOxhwL}A2Q;ħs^lX^fnrr6X\}[A|vbC,+kD݄$?JGp E0 KK۪౛7ID7)7mw+(z[wq^'̦ Ѕdh`D+BfP$s/MRY^vCZDծ"Cg ? Z"}(-dYln^'ﶩ`ʂ<^-2wFKBg +%<_ I0t@4WJ^>"Y4ݯ:@2a-nGg&2σݼt ctgyXQu 1Kz=QXQ]19cOT-hVJ'7\Urd(fm/Lm3vxѮeB`&E؍7Y#]S-L4Id# gmkX 05# Uǀ#vϚw|sJo+Y/١W*[.zQ۠s p& Bzħ+.ݮE%*zlOpZL1gdu!1m}KzL#(-<_SD޳m&c!B]"xBRKA @y҆kGnb5?jZ B dj:4㢋4ϻG|R e]M5 Ci-7' >0ןwZg^dA֯M1#HIa"he}HCLoQ`5`]آrT6&hŰo,3w^S!<.pR㋏-|ZSpНr {0hw+s9?,:,E|躅@z 7B] X]0CC 87Y18]wp63in[Tz'f-eGx0%vob>8 <_L GS3H) >\D߿w0 P[;Ye Hfr{kcA^X4FR|Z)g5,gI9!r9Q⑺I dDϜ$mg|Ha1;{@: _vC*utEANE!w6K}"~4yx1Pde'ԕO֜`Kx 1Y^7D^VSpC @5+Na`G$(?gS7w̗K > = <: ghq]l+ MKl7A~,qa֮[K9Ѹ] K8+(WetF׍/a[ W01n- Qo^}^L`?` (DͲGEh;! ib$ CZ<pz@9z&!5ۀZjyp1g6d6h`QDQd !RD${]qK^&\ 1j|a,FRw>foFyһ45[Qܱ>R@[(ЫthqN:96G+α5uOV²{*2p)+ܝy%2Xi"D"8](?'Rp~Anm:D]XI(ތ^w3`N7f5=| {pH7yG j*˂ 8Шq"C`]`ihL%|XV!̗.ACͽo˰N% mNuCߙW|wĽ>b1~p/Q>LpB52W،eǚJ|Hkea`d^aE71a:%0 Pp\[Vv>9E"M$wշj9ܐ2!yA5OX27&!g -c6pLy8u"l9s<t+#Q8k/CqMr7~,:ˤMt`1Ika'@-%Yf&u% &juڼa)#^`b yzтˈWtpCǬabW֢2AGn5QS-o6NX7 $kY,p/`5xMA>&NIĚzBhgѧN# *)@+h` r+#95*1@(#Ԋશ֓#}K;h 3Fj d."2c!qވ+ڟ#mQ;|B%וV=FM}^W;*:D76c7 oX39^AkDɎEY<\ (GRceiL$.ocU ^(%n>"g*/BI{(BUje,!ENzxxCa#"~𤦦X_YlKdZ[} *I`yz3p[,i6_Y,3p9YH&)1*ki~G tѸA5ֿ7Fv {4⁑TqkR$e-SHi #|!"]T:OK [_4nĤbPh7b<:֊^7yt 6pp5*EOgOY]<='<@z>@Hjl7φZ~q~C`PQuFK=qoJQEX2%3=V#chSȩ|_zI!DgljD>e4|߷}YLAA"76_,Šqo6X&&`[a. ;nsA-\!;%hVA~ɊH6W}nt#ֈ,sMR{aj0h{ƠV>c,rJ%S+ om?IENDB`./resources/dash_bottom_right_corner_mask.png0000644000004100000410000000025713437202764022062 0ustar www-datawww-dataPNG  IHDR22)xPLTE$| tRNS ?Nà?7IDATx) @nEawVb,'i>6&Ks+N@ IO.TbP0IENDB`./resources/preview_next.svg0000644000004100000410000000227013437202764016526 0ustar www-datawww-data ./resources/squircle_base_selected_54.png0000644000004100000410000000062513437202764020777 0ustar www-datawww-dataPNG  IHDR66HPLTEf9tRNS !,7CNYdozv"IDATHց `6{`-DCTjϡgOLF ޙ推É^z w?Xb̮c10FS%aheTDkd%9-;I{s[%,(&U}QK4AְrzW@haV(/ v_.>-!SsZ[]seiGxWWGsnPIENDB`./resources/unmaximize_dash.svg0000644000004100000410000000475713437202764017210 0ustar www-datawww-data image/svg+xml ./resources/launcher_pip_ltr_37.svg0000644000004100000410000000651613437202764017661 0ustar www-datawww-data image/svg+xml ./resources/launcher_arrow_outline_ltr_19.svg0000644000004100000410000001017313437202764021754 0ustar www-datawww-data image/svg+xml ./resources/unmaximize_dash_prelight.svg0000644000004100000410000000661613437202764021102 0ustar www-datawww-data image/svg+xml ./resources/album_missing.png0000644000004100000410000000115613437202764016627 0ustar www-datawww-dataPNG  IHDR``F PLTE\=tRNS!%258:BBCEPSX[knpsvwx{|QQ&IDATxKO@@V|?Par2j #5nzo/9d\S\X3,tİZ"q` 1 @XoףYj\>㳩:=3@4૗ގHO8Gow{pm2)PV 1[ E6@JoX4?׫_Z|Ձ@y R(/y_ר- oG image/svg+xml ./resources/launcher_arrow_ltr_37.svg0000644000004100000410000000665313437202764020225 0ustar www-datawww-data image/svg+xml ./resources/dash_top_edge.png0000644000004100000410000000043713437202764016564 0ustar www-datawww-dataPNG  IHDRDCQPLTExTltRNS (bIDATx1 r *L|p_\)0DSuU+QvCIENDB`./resources/close_dash_pressed.svg0000644000004100000410000000543513437202764017646 0ustar www-datawww-data image/svg+xml ./resources/searchingthedashlegalnotice.html0000644000004100000410000001400413437202764021665 0ustar www-datawww-datasearching the dash legal notice

         Searching in the dash - Legal notice

This search function is provided to you by Canonical Group Limited (Canonical). This legal notice applies to searching in the dash and incorporates the terms of Canonical's legal notice (and privacy policy).

Collection and use of data

When you enter a search term into the dash Ubuntu will search your Ubuntu computer and will record the search terms locally.

Unless you have opted out (see the “Online Search” section below), we will also send your keystrokes as a search term to productsearch.ubuntu.com and selected third parties so that we may complement your search results with online search results from such third parties including: Facebook, Twitter, BBC and Amazon. Canonical and these selected third parties will collect your search terms and use them to provide you with search results while using Ubuntu.

By searching in the dash you consent to:

  1. the collection and use of your search terms and IP address in this way; and
  2. the storage of your search terms and IP address by Canonical and such selected third parties (if applicable).

Canonical will only use your search terms and IP address in accordance with this legal notice and our privacy policy. Please see our privacy policy for further information about how Canonical protects your personal information. For information on how our selected third parties may use your information, please see their privacy policies.

Online Search

You may restrict your dash so that we don’t send searches to third parties and you don't receive online search results. To do this go to the Privacy panel and toggle the ‘Include online search results’ option to off. The Privacy panel can be found in your System Settings or via a dash search. For a current list of our selected third parties, please see www.ubuntu.com/privacypolicy/thirdparties.

Changes

Although most changes are likely to be minor, Canonical may change this legal notice from time to time, and at Canonical's sole discretion. Please check this page from time to time for any changes to this legal notice as we will not be able to notify you directly.

How to contact us

Please submit any questions or comments about searching in the dash or this legal notice by contacting us at the following address: Canonical Group Ltd, 5th Floor, Blue Fin Building, 110 Southwark Street, London, England, SE1 0SU.

./resources/search_circle.svg0000644000004100000410000001121013437202764016567 0ustar www-datawww-data image/svg+xml ./resources/refine_gradient_panel.png0000644000004100000410000000323313437202764020300 0ustar www-datawww-dataPNG  IHDR, pHYs  tIME  7IPLTEW+1tRNS  !"#$%&'()*+,-./0bKGDHQIDATx]v0 }kc[rsi(%@I"{gj/l` ?*־~o >==Mp[gI5wqp a+t@ij~7(P1!(AM{8\5oh+Y> 쇃]}D`?Ͻ_H!j2P lroC0DЇY> &]UC0į W˥h,ccG{_{Ŏa`0dDRC]$:ŏwCqo>~C(mOPg0"#+["dEPQk1#tɗϽrarv`.Z#R$ʈcx1*ĸI|vsV$zk; vP֣ðrQ0G#s(6;lIzB$OI!ڇjW`оJK}TlعX.KVЦn=CSS'; TBE>gm_GScLE`O-8b83/_ L+iN |^_t8sR@j)Fr^wL_Q-a{=}T~^,A$ }񻥹#\K.3/v]a:[~YQ6dA4y1Ԛ^Nl:)e u>j̛fBF#CI*xZc-5}f ~NY_;"bф9FTk}%ufdz#(ukYHrY SbGCB [+#bZy t,վ/?r:ĂvLi8N iuQ vb?bNHnuֳ(1IENDB`./resources/logout_highlight.png0000644000004100000410000000217113437202764017334 0ustar www-datawww-dataPNG  IHDRCUPLTET QtRNS "$&)+-079:<>CDGHTVXYbegjlnqsvxz{}̞ѱIDATxnf4n!AR2Qܥ[R¹ "Ms?)ɇ_EM_>Wl&$d…(*wս&* +D@%T)oĉQ#8QTh8A2A jPԠ5A jPԠ5A jPԠ5A jq\? xzՊX+j])>cey36/ {\tp*SOVG>¡h,:Ƀf"gZ9Q IwDkq^lmP"ghX6\~-E?t]-'I8kh|~&K?S_HZ\m!+P^,hL3IMNVCRR+p[e 9x I-]Y'PG:+@=(fN@ Vck/vmi?JTxL%w(jWn"YB?GV1: 75G~eJz:. z@e`Rv41QvH%|EٵcEyEj#Æo7/ob @9wbX5+aQ;Ԡ5A jPԠ5A jPԠ5A jPԠ5Aw"NDoYr ^ri;+e򪮭=rg_ҺE֋t.;l>N>_phJfIENDB`./resources/launcher_icon_selected_back_54.svg0000644000004100000410000000462013437202764021761 0ustar www-datawww-data image/svg+xml ./resources/launcher_pip_rtl_37.svg0000644000004100000410000000635713437202764017664 0ustar www-datawww-data image/svg+xml ./resources/search_magnify.svg0000644000004100000410000001225713437202764016774 0ustar www-datawww-data image/svg+xml ./resources/star_highlight.png0000644000004100000410000000076113437202764016777 0ustar www-datawww-dataPNG  IHDRr ߔsRGBbKGD pHYs  tIME  qIDATHǽKQҢfJBB D)TГPoBPrVFYǝ? s|g;CqW8vfNcm Xj_w e{jff`c "#?jxYm7 7jKnRsP7 image/svg+xml ./resources/round_glow_62x62.png0000644000004100000410000000123313437202764017020 0ustar www-datawww-dataPNG  IHDR>>DXvPLTEEd ?tRNS !&.13355788 ./resources/dash_bottom_border_tile.png0000644000004100000410000000032313437202764020646 0ustar www-datawww-dataPNG  IHDR2L 6PLTE;;;TtRNS'2=,XYh4;>@ 0^q4$MxZyc.%@'n|ͭ:^nfN5౐[vUC`_v@+;^ LۮWv~ٔn>@V/{5y&OY] F +2;̊$G_Ks)Xx{GecD&^/E%`2v=z \/{U 0Zvv7 ZTީz )0^!I@CLl;n.f{l5~lpQnyUC.iu&ZݿP,[ * * * * * * *f6s(cogš6uTfX)cdHOأy*NMJBedlؙqQIENDB`./resources/launcher_arrow_rtl_19.svg0000644000004100000410000000674313437202764020225 0ustar www-datawww-data image/svg+xml ./resources/lens-nav-video.svg0000644000004100000410000001003213437202764016631 0ustar www-datawww-data image/svg+xml ./resources/dash_bottom_border_tile_mask.png0000644000004100000410000000014713437202764021665 0ustar www-datawww-dataPNG  IHDR2PLTEٟtRNS@fIDATxc0!^,$[IENDB`./resources/dialog_close_press.png0000644000004100000410000000122213437202764017630 0ustar www-datawww-dataPNG  IHDRשPLTE $$$(((***...444:::???DDDOOOTTTXXX]]]aaa1vNtRNS!EHKNQ`cffhijlopqqrstvxxz}c IDATxuZ@E"D,, wI8?υ)Q)g7r,\ L[{[kgh~k BR`?+cI`}R@O>U5r#TDܶ:MK]KڢiaG5P4ԉ&CS}TK_j0] Rg:Хa>Ms&x^2n.h?dgd· *AѮk_r!Rwp{}Y0EHIENDB`./resources/round_corner_54x54.png0000644000004100000410000000042713437202764017346 0ustar www-datawww-dataPNG  IHDR66HPLTE?BtRNSBEHT((pIDATx DQDQA Q ߩQ ܜɿFksяZDhOe)($,\ $eigVA\UL2eʔ)?(?OWm(9,\>P۴nmLw7GAIENDB`./resources/round_glow_hl_62x62.png0000644000004100000410000000233313437202764017505 0ustar www-datawww-dataPNG  IHDR>>DXvPLTEcccEEEWWWpppۺ_tRNS  !!((),,-//118899>>BBCCEEFFIIJVWW^bcckkpqqsstuutZIDATH1oAqH e:ihAB PbŎo}3)$$$Λّ `TMDT[W@bV3n)"%.bъ1nuuhմ9+hpAigˋ|AeRf#ŊHgȘ8_v5Xc3Wk|9[<_ۯa8 }g>uL@}k11I?'׎b"a&fÑ2ff$c`"14]T@' i{ @+opjSUN;}%Kn@0wK=P.fpZq5+]{s 7gi0/NzS#1c \]\V|㛷Wvga&^U?om]9KK&m~0!eNUBîC2O!Là7-)a!BC S5}2a0́jj 3}>^IENDB`./resources/sheet_style_close_focused_prelight.svg0000644000004100000410000002177413437202764023144 0ustar www-datawww-data image/svg+xml ./resources/dialog_close_highlight.png0000644000004100000410000000116313437202764020447 0ustar www-datawww-dataPNG  IHDRשPLTE%oGtRNS!369@ABDEFHIKMNQRUWY\`cfilortxy{} c]IDATxum[0gV+P{ 3 "HT`#7^]O}mwn}vEY;CjT ?{sJnOdI:~BN 5y$ mnN-': 14ajv.nX;k[SI\6 jKmyq^;٠ ~ o!c· ė>Y}T-񫊓znqLIx: IENDB`./resources/launcher_icon_shine_54.svg0000644000004100000410000000632213437202764020320 0ustar www-datawww-data image/svg+xml ./resources/lens-nav-people.svg0000644000004100000410000001075513437202764017023 0ustar www-datawww-data image/svg+xml ./resources/unmaximize_dash_pressed.svg0000644000004100000410000000500313437202764020716 0ustar www-datawww-data image/svg+xml ./resources/search_close.png0000644000004100000410000000031313437202764016422 0ustar www-datawww-dataPNG  IHDRש*PLTEjM- tRNS PǡCIDATx0Qy8BmM{5H!g%1EBx$`4=qY0h=IENDB`./resources/kylin_login_activate.svg0000644000004100000410000000115013437202764020201 0ustar www-datawww-data ./resources/lockscreen.png0000644000004100000410000000174013437202764016125 0ustar www-datawww-dataPNG  IHDRCUPLTEF4DtRNS !'-039՞"_r JmWYNw^64xa+@ZHy>aԅX@Vz+19;zGyw/;qPUE RJ0\ \ӡ%?Ss6fWjm65 (P@ (P@ (P@*Ѓ`@5dVYjn}Pb4BM/沾*(:/˷|2TAeo8/Cd0/2uUA (P;$:Q-mH+3ǁ|y ^:(CmP=(CP (P_5zqSz*yu>qg?/fؤqJަ7+>Z@ (P@ (P@ (P@7h4WxGs)z4Gsq4\"vDR&&=Y\m$ٶyngD#3lv}Ih^IENDB`./resources/launcher_icon_glow_62.png0000644000004100000410000000070413437202764020144 0ustar www-datawww-dataPNG  IHDR>>DXvoPLTEP%tRNS $&'()+,059;<=>@A?a5IDATx׹0 EQla1;Eߘ0CFM ح8sYiT:ܘVij%Oetpk<~^Y@̯* sT{mwT%Z帺6֭c*!׳kuI!u,{Ѿ4p?`6rzC9s̙3gO8,$ԓxqЦqf }'#Yx;IENDB`./resources/dialog_close.png0000644000004100000410000000122213437202764016414 0ustar www-datawww-dataPNG  IHDRשPLTE???FFFIIINNNWWW^^^fffmmmyyy~~~U~NtRNS!369@ABDEFHIKKMNQRUWY\`cfiloortxy{}mIDATxu^@ǬniE'e{bfD$yiF8;z;k#xXTYcz&"3BGߑIENDB`./resources/lens-nav-gwibber.svg0000644000004100000410000000514013437202764017150 0ustar www-datawww-data image/svg+xml ./plugins/0000755000004100000410000000000013437202764012734 5ustar www-datawww-data./plugins/unity-mt-grab-handles/0000755000004100000410000000000013437202764017047 5ustar www-datawww-data./plugins/unity-mt-grab-handles/CMakeLists.txt0000644000004100000410000000115313437202764021607 0ustar www-datawww-datafind_package (Compiz REQUIRED) include (CompizPlugin) # Guard against Compiz altering global state. # https://bugs.launchpad.net/compiz/+bug/1096807 if(CMAKE_BUILD_TYPE STREQUAL "") set(revert_compiz TRUE) endif() set (libdir ${CMAKE_INSTALL_LIBDIR}) set (includedir ${CMAKE_INSTALL_INCLUDEDIR}) set (libdir ${CMAKE_INSTALL_LIBDIR}) set (datadir ${CMAKE_INSTALL_FULL_DATADIR}) compiz_plugin (unitymtgrabhandles PKGDEPS nux-4.0>=4.0.0 PLUGINDEPS composite opengl CFLAGSADD -std=c++0x) if(revert_compiz) set (CMAKE_BUILD_TYPE "" CACHE STRING "Build type (Debug/Release/RelWithDebInfo/MinSizeRe)" FORCE) endif() ./plugins/unity-mt-grab-handles/src/0000755000004100000410000000000013437202764017636 5ustar www-datawww-data./plugins/unity-mt-grab-handles/src/unity-mt-texture.cpp0000644000004100000410000000221613437202764023627 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include "unity-mt-texture.h" std::shared_ptr unity::MT::Texture::Factory::mDefault; unity::MT::Texture::Factory::Factory () { } unity::MT::Texture::Factory::~Factory () { } void unity::MT::Texture::Factory::SetDefault (Factory *f) { mDefault.reset (f); } std::shared_ptr unity::MT::Texture::Factory::Default () { return mDefault; } unity::MT::Texture::Texture () { } unity::MT::Texture::~Texture () { } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-group.cpp0000644000004100000410000001213313437202764025424 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle-group.h" unsigned int unity::MT::FADE_MSEC = 0; void unity::MT::GrabHandleGroup::show(unsigned int handles) { for(const unity::MT::GrabHandle::Ptr & handle : mHandles) if (handles & handle->id ()) handle->show(); mState = State::FADE_IN; } void unity::MT::GrabHandleGroup::hide() { for(const unity::MT::GrabHandle::Ptr & handle : mHandles) handle->hide(); mState = State::FADE_OUT; } void unity::MT::GrabHandleGroup::raiseHandle(const std::shared_ptr &h) { mOwner->raiseGrabHandle (h); } bool unity::MT::GrabHandleGroup::animate(unsigned int msec) { mMoreAnimate = false; switch (mState) { case State::FADE_IN: mOpacity += ((float) msec / (float) unity::MT::FADE_MSEC) * std::numeric_limits ::max (); if (mOpacity >= std::numeric_limits ::max ()) { mOpacity = std::numeric_limits ::max (); mState = State::NONE; } break; case State::FADE_OUT: mOpacity -= ((float) msec / (float) unity::MT::FADE_MSEC) * std::numeric_limits ::max (); if (mOpacity <= 0) { mOpacity = 0; mState = State::NONE; } break; default: break; } mMoreAnimate = mState != State::NONE; return mMoreAnimate; } int unity::MT::GrabHandleGroup::opacity() { return mOpacity; } bool unity::MT::GrabHandleGroup::visible() { return mOpacity > 0.0f; } bool unity::MT::GrabHandleGroup::needsAnimate() { return mMoreAnimate; } void unity::MT::GrabHandleGroup::relayout(const nux::Geometry& rect, bool hard) { /* Each grab handle at each vertex, eg: * * 1 - topleft * 2 - top * 3 - topright * 4 - right * 5 - bottom-right * 6 - bottom * 7 - bottom-left * 8 - left */ const float pos[9][2] = { {0.0f, 0.0f}, {0.5f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.5f}, {1.0f, 1.0f}, {0.5f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.5f}, {0.5f, 0.5f} /* middle */ }; for (unsigned int i = 0; i < NUM_HANDLES; i++) { unity::MT::GrabHandle::Ptr & handle = mHandles.at(i); handle->reposition (rect.x + rect.width * pos[i][0] - handle->width () / 2, rect.y + rect.height * pos[i][1] - handle->height () / 2, unity::MT::PositionSet | (hard ? unity::MT::PositionLock : 0)); } } unity::MT::GrabHandleGroup::GrabHandleGroup(GrabHandleWindow *owner, std::vector &textures) : mState(State::NONE), mOpacity(0.0f), mMoreAnimate(false), mOwner(owner) { } unity::MT::GrabHandleGroup::Ptr unity::MT::GrabHandleGroup::create (GrabHandleWindow *owner, std::vector &textures) { unity::MT::GrabHandleGroup::Ptr p = unity::MT::GrabHandleGroup::Ptr (new unity::MT::GrabHandleGroup (owner, textures)); for (unsigned int i = 0; i < NUM_HANDLES; i++) p->mHandles.push_back(unity::MT::GrabHandle::create (textures.at(i).first, textures.at(i).second.width, textures.at(i).second.height, p, handlesMask.find (i)->second)); return p; } unity::MT::GrabHandleGroup::~GrabHandleGroup() { for (unity::MT::GrabHandle::Ptr & handle : mHandles) handle->damage (nux::Geometry (handle->x (), handle->y (), handle->width (), handle->height ())); } void unity::MT::GrabHandleGroup::requestMovement (int x, int y, unsigned int direction, unsigned int button) { mOwner->requestMovement (x, y, direction, button); } std::vector unity::MT::GrabHandleGroup::layout(unsigned int handles) { std::vector layout; for(const unity::MT::GrabHandle::Ptr & handle : mHandles) if (handle->id () & handles) layout.push_back (handle->layout ()); return layout; } void unity::MT::GrabHandleGroup::forEachHandle (const std::function &f) { for (unity::MT::GrabHandle::Ptr &h : mHandles) f (h); } ./plugins/unity-mt-grab-handles/src/unity-mt-texture.h0000644000004100000410000000275613437202764023305 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLES_TEXTURE_H #define _UNITY_MT_GRAB_HANDLES_TEXTURE_H #include #include namespace unity { namespace MT { class Texture { public: typedef std::shared_ptr Ptr; virtual ~Texture (); class Factory : boost::noncopyable { public: virtual ~Factory (); virtual unity::MT::Texture::Ptr create () = 0; static void SetDefault (Factory *); static std::shared_ptr Default (); protected: Factory (); private: static std::shared_ptr mDefault; }; protected: Texture (); }; typedef std::pair TextureSize; typedef std::pair TextureLayout; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-layout.h0000644000004100000410000000204713437202764025255 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_LAYOUT_H #define _UNITY_MT_GRAB_HANDLE_LAYOUT_H namespace unity { namespace MT { extern unsigned int MaximizedVertMask; extern unsigned int MaximizedHorzMask; extern unsigned int MoveMask; extern unsigned int ResizeMask; unsigned int getLayoutForMask (unsigned int state, unsigned int actions); }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.cpp0000644000004100000410000005304513437202764024464 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handles.h" #include COMPIZ_PLUGIN_20090315(unitymtgrabhandles, UnityMTGrabHandlesPluginVTable); unsigned int unity::MT::MaximizedHorzMask = CompWindowStateMaximizedHorzMask; unsigned int unity::MT::MaximizedVertMask = CompWindowStateMaximizedVertMask; unsigned int unity::MT::MoveMask = CompWindowActionMoveMask; unsigned int unity::MT::ResizeMask = CompWindowActionResizeMask; void unity::MT::X11TextureFactory::setActiveWrap (const GLTexture::List &t) { mWrap = t; } unity::MT::Texture::Ptr unity::MT::X11TextureFactory::create () { unity::MT::Texture::Ptr tp(static_cast (new unity::MT::X11Texture(mWrap))); return tp; } unity::MT::X11Texture::X11Texture (const GLTexture::List &t) { mTexture = t; } const GLTexture::List & unity::MT::X11Texture::get () { return mTexture; } unity::MT::X11ImplFactory::X11ImplFactory (Display *dpy) : mDpy (dpy) { } unity::MT::GrabHandle::Impl * unity::MT::X11ImplFactory::create (const GrabHandle::Ptr &handle) { unity::MT::GrabHandle::Impl *impl = new X11GrabHandleImpl (mDpy, handle); return impl; } unity::MT::X11GrabHandleImpl::X11GrabHandleImpl (Display *dpy, const GrabHandle::Ptr &h) : mGrabHandle (h), mIpw (None), mDpy (dpy) { } void unity::MT::X11GrabHandleImpl::show () { if (mIpw) { XMapWindow (mDpy, mIpw); return; } XSetWindowAttributes xswa; xswa.override_redirect = TRUE; unity::MT::GrabHandle::Ptr gh = mGrabHandle.lock (); mIpw = XCreateWindow(mDpy, DefaultRootWindow (mDpy), -100, -100, gh->width (), gh->height (), 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &xswa); UnityMTGrabHandlesScreen::get(screen)->addHandleWindow(gh, mIpw); XMapWindow (mDpy, mIpw); } void unity::MT::X11GrabHandleImpl::hide () { if (mIpw) XUnmapWindow (mDpy, mIpw); } void unity::MT::X11GrabHandleImpl::lockPosition (int x, int y, unsigned int flags) { XWindowChanges xwc; unsigned int vm = 0; if (!mIpw) return; if (flags & unity::MT::PositionSet) { xwc.x = x; xwc.y = y; vm |= CWX | CWY; } unity::MT::GrabHandle::Ptr gh = mGrabHandle.lock (); gh->raise (); XConfigureWindow(screen->dpy(), mIpw, vm, &xwc); XSelectInput(screen->dpy(), mIpw, ButtonPressMask | ButtonReleaseMask); } unity::MT::X11GrabHandleImpl::~X11GrabHandleImpl () { if (mIpw) { UnityMTGrabHandlesScreen::get(screen)->removeHandleWindow(mIpw); XDestroyWindow(mDpy, mIpw); } } void unity::MT::X11GrabHandleImpl::buttonPress (int x, int y, unsigned int button) const { unity::MT::GrabHandle::Ptr gh = mGrabHandle.lock (); gh->requestMovement (x, y, button); } void UnityMTGrabHandlesWindow::raiseGrabHandle (const std::shared_ptr &h) { UnityMTGrabHandlesScreen::get (screen)->raiseHandle (h, window->frame ()); } void UnityMTGrabHandlesWindow::requestMovement (int x, int y, unsigned int direction, unsigned int button) { /* Send _NET_MOVERESIZE to root window so that a button-1 * press on this window will start resizing the window around */ XEvent event; if (screen->getOption("raise_on_click")) window->updateAttributes(CompStackingUpdateModeAboveFullscreen); if (window->id() != screen->activeWindow()) if (window->focus()) window->moveInputFocusTo(); event.xclient.type = ClientMessage; event.xclient.display = screen->dpy (); event.xclient.serial = 0; event.xclient.send_event = true; event.xclient.window = window->id(); event.xclient.message_type = Atoms::wmMoveResize; event.xclient.format = 32; event.xclient.data.l[0] = x; event.xclient.data.l[1] = y; event.xclient.data.l[2] = direction; event.xclient.data.l[3] = button; event.xclient.data.l[4] = 1; XSendEvent(screen->dpy(), screen->root(), false, SubstructureRedirectMask | SubstructureNotifyMask, &event); } /* Super speed hack */ static bool sortPointers(void *p1, void *p2) { return (void*) p1 < (void*) p2; } void UnityMTGrabHandlesScreen::raiseHandle (const std::shared_ptr &h, Window owner) { for (const auto &pair : mInputHandles) { const unity::MT::GrabHandle::Ptr gh = pair.second.lock(); if (*gh == *h) { unsigned int mask = CWSibling | CWStackMode; XWindowChanges xwc; xwc.stack_mode = Above; xwc.sibling = owner; XConfigureWindow (screen->dpy (), pair.first, mask, &xwc); } } } void UnityMTGrabHandlesScreen::handleEvent(XEvent* event) { CompWindow* w; w = NULL; switch (event->type) { case FocusIn: case FocusOut: if (event->xfocus.mode == NotifyUngrab) { for(CompWindow * w : screen->windows()) { UnityMTGrabHandlesWindow* mtwindow = UnityMTGrabHandlesWindow::get(w); if (mtwindow->handleTimerActive()) mtwindow->resetTimer(); } } break; case ClientMessage: if (event->xclient.message_type == mCompResizeWindowAtom) { CompWindow* w = screen->findWindow(event->xclient.window); if (w) { CompRect r; UMTGH_WINDOW(w); r.setGeometry(event->xclient.data.l[0] - w->input().left, event->xclient.data.l[1] - w->input().top, event->xclient.data.l[2] + w->input().left + w->input().right, event->xclient.data.l[3] + w->input().top + w->input().bottom); uw->relayout(r, false); } } break; case PropertyNotify: /* Stacking order of managed clients changed, check old * stacking order and ensure stacking of handles * that were changed in the stack */ if (event->xproperty.atom == Atoms::clientListStacking) { CompWindowVector invalidated(0); CompWindowVector clients = screen->clientList(true); CompWindowVector oldClients = mLastClientListStacking; CompWindowVector clientListStacking = screen->clientList(true); /* Windows can be removed and added from the client list * here at the same time (eg hide/unhide launcher ... racy) * so we need to check if the client list contains the same * windows as it used to. Sort both lists and compare ... */ std::sort(clients.begin(), clients.end(), sortPointers); std::sort(oldClients.begin(), oldClients.end(), sortPointers); if (clients != mLastClientListStacking) invalidated = clients; else { CompWindowVector::const_iterator cit = clientListStacking.begin(); CompWindowVector::const_iterator oit = mLastClientListStacking.begin(); for (; cit != clientListStacking.end(); ++cit, oit++) { /* All clients from this point onwards in cit are invalidated * so splice the list to the end of the new client list * and update the stacking of handles there */ if ((*cit)->id() != (*oit)->id()) { invalidated.push_back((*cit)); } } } for(CompWindow * w : invalidated) UnityMTGrabHandlesWindow::get(w)->restackHandles(); mLastClientListStacking = clients; } break; case ButtonPress: { if (event->xbutton.button != 1) break; auto it = mInputHandles.find(event->xbutton.window); if (it != mInputHandles.end()) { const unity::MT::GrabHandle::Ptr gh = it->second.lock(); if (gh) gh->buttonPress (event->xbutton.x_root, event->xbutton.y_root, event->xbutton.button); } break; } case ConfigureNotify: w = screen->findTopLevelWindow(event->xconfigure.window); if (w) UnityMTGrabHandlesWindow::get(w)->relayout(w->inputRect(), true); break; case MapNotify: { auto it = mInputHandles.find(event->xmap.window); if (it != mInputHandles.end()) { const unity::MT::GrabHandle::Ptr gh = it->second.lock(); if (gh) gh->reposition (0, 0, unity::MT::PositionLock); } break; } default: break; } screen->handleEvent(event); } void UnityMTGrabHandlesScreen::donePaint() { if (mMoreAnimate) { for (const unity::MT::GrabHandleGroup::Ptr &handles : mGrabHandles) { if (handles->needsAnimate()) { handles->forEachHandle ([this](const unity::MT::GrabHandle::Ptr &h) { h->damage (nux::Geometry (h->x (), h->y (), h->width (), h->height ())); }); } } } cScreen->donePaint(); } void UnityMTGrabHandlesScreen::preparePaint(int msec) { if (mMoreAnimate) { mMoreAnimate = false; for(const unity::MT::GrabHandleGroup::Ptr &handles : mGrabHandles) { mMoreAnimate |= handles->animate(msec); } } cScreen->preparePaint(msec); } bool UnityMTGrabHandlesWindow::handleTimerActive() { return mTimer.active (); } bool UnityMTGrabHandlesWindow::allowHandles() { /* Not on override redirect windows */ if (window->overrideRedirect()) return false; return true; } void UnityMTGrabHandlesWindow::getOutputExtents(CompWindowExtents& output) { auto f = [this, &output] (const unity::MT::GrabHandle::Ptr &h) { output.left = std::max (window->borderRect().left() + h->width () / 2, static_cast (output.left)); output.right = std::max (window->borderRect().right() + h->width () / 2, static_cast (output.right)); output.top = std::max (window->borderRect().top() + h->height () / 2, static_cast (output.top)); output.bottom = std::max (window->borderRect().bottom() + h->height () / 2, static_cast (output.bottom)); }; if (mHandles) { /* Only care about the handle on the outside */ mHandles->forEachHandle (f); } else window->getOutputExtents(output); } bool UnityMTGrabHandlesWindow::glDraw(const GLMatrix& transform, const GLWindowPaintAttrib& attrib, const CompRegion& region, unsigned int mask) { /* Draw the window on the bottom, we will be drawing the * handles on top */ bool status = gWindow->glDraw(transform, attrib, region, mask); if (mHandles && mHandles->visible()) { unsigned int allowedHandles = unity::MT::getLayoutForMask (window->state (), window->actions ()); unsigned int handle = 0; for(unity::MT::TextureLayout layout : mHandles->layout (allowedHandles)) { /* We want to set the geometry of the handle to the window * region */ CompRegion reg = CompRegion(layout.second.x, layout.second.y, layout.second.width, layout.second.height); for(GLTexture * tex : static_cast(layout.first.get())->get()) { GLTexture::MatrixList matl; GLTexture::Matrix mat = tex->matrix(); CompRegion paintRegion(region); GLWindowPaintAttrib wAttrib(attrib); /* We can reset the window geometry since it will be * re-added later */ gWindow->vertexBuffer()->begin(); /* Not sure what this does, but it is necessary * (adjusts for scale?) */ mat.x0 -= mat.xx * reg.boundingRect().x1(); mat.y0 -= mat.yy * reg.boundingRect().y1(); matl.push_back(mat); if (mask & PAINT_WINDOW_TRANSFORMED_MASK) paintRegion = CompRegion::infinite(); /* Now allow plugins to mess with the geometry of our * dim (so we get a nice render for things like * wobbly etc etc */ gWindow->glAddGeometry(matl, reg, paintRegion); if (gWindow->vertexBuffer()->end()) { wAttrib.opacity = mHandles->opacity(); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); /* Draw the dim texture with all of it's modified * geometry glory */ gWindow->glDrawTexture(tex, transform, wAttrib, mask | PAINT_WINDOW_BLEND_MASK | PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK); /* Texture rendering tear-down */ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } } handle++; } } return status; } void UnityMTGrabHandlesWindow::relayout(const CompRect& r, bool hard) { if (mHandles) mHandles->relayout(nux::Geometry (r.x (), r.y (), r.width (), r.height ()), hard); } void UnityMTGrabHandlesWindow::grabNotify(int x, int y, unsigned int state, unsigned int mask) { window->grabNotify(x, y, state, mask); } void UnityMTGrabHandlesWindow::moveNotify(int dx, int dy, bool immediate) { if (mHandles) mHandles->relayout(nux::Geometry (window->inputRect ().x (), window->inputRect ().y (), window->inputRect ().width (), window->inputRect ().height ()), false); window->moveNotify(dx, dy, immediate); } void UnityMTGrabHandlesWindow::ungrabNotify() { window->ungrabNotify(); } bool UnityMTGrabHandlesWindow::handlesVisible() { if (!mHandles) return false; return mHandles->visible(); } void UnityMTGrabHandlesWindow::hideHandles() { if (mHandles) mHandles->hide(); window->updateWindowOutputExtents(); cWindow->damageOutputExtents(); disableTimer(); } bool UnityMTGrabHandlesWindow::onHideTimeout() { CompOption::Vector o (1); CompOption::Value v; if (screen->grabbed()) return true; v.set ((int) window->id ()); o[0].setName ("window", CompOption::TypeInt); o[0].set (v); UnityMTGrabHandlesScreen::get (screen)->hideHandles (NULL, 0, o); return false; } void UnityMTGrabHandlesWindow::resetTimer() { mTimer.stop (); mTimer.setTimes (2000, 2200); mTimer.start (); } void UnityMTGrabHandlesWindow::disableTimer() { mTimer.stop (); } void UnityMTGrabHandlesWindow::showHandles(bool use_timer) { UMTGH_SCREEN (screen); if (!mHandles) { mHandles = unity::MT::GrabHandleGroup::create (this, us->textures ()); us->addHandles(mHandles); } if (!mHandles->visible()) { unsigned int showingMask = unity::MT::getLayoutForMask (window->state (), window->actions ()); activate(); mHandles->show(showingMask); mHandles->relayout(nux::Geometry (window->inputRect().x (), window->inputRect().y (), window->inputRect().width(), window->inputRect().height()), true); window->updateWindowOutputExtents(); cWindow->damageOutputExtents(); } if (use_timer) resetTimer(); else disableTimer(); } void UnityMTGrabHandlesWindow::restackHandles() { if (!mHandles) return; mHandles->forEachHandle ([this](const unity::MT::GrabHandle::Ptr &h) { h->reposition (0, 0, unity::MT::PositionLock); }); } void UnityMTGrabHandlesScreen::addHandleWindow(const unity::MT::GrabHandle::Ptr &h, Window w) { mInputHandles.insert(std::make_pair(w, h)); } void UnityMTGrabHandlesScreen::removeHandleWindow(Window w) { mInputHandles.erase(w); } void UnityMTGrabHandlesScreen::addHandles(const unity::MT::GrabHandleGroup::Ptr &handles) { mGrabHandles.push_back(handles); } void UnityMTGrabHandlesScreen::removeHandles(const unity::MT::GrabHandleGroup::Ptr &handles) { mGrabHandles.remove(handles); mMoreAnimate = true; } bool UnityMTGrabHandlesScreen::toggleHandles(CompAction* action, CompAction::State state, CompOption::Vector& options) { CompWindow* w = screen->findWindow(CompOption::getIntOptionNamed(options, "window", 0)); if (w) { UMTGH_WINDOW(w); if (!uw->allowHandles()) return false; if (uw->handlesVisible()) uw->hideHandles(); else uw->showHandles(true); mMoreAnimate = true; } return true; } bool UnityMTGrabHandlesScreen::showHandles(CompAction* action, CompAction::State state, CompOption::Vector& options) { CompWindow* w = screen->findWindow(CompOption::getIntOptionNamed(options, "window", 0)); bool use_timer = CompOption::getBoolOptionNamed(options, "use-timer", true); if (w) { UMTGH_WINDOW(w); if (!uw->allowHandles()) return false; uw->showHandles(use_timer); if (!uw->handlesVisible()) mMoreAnimate = true; } return true; } bool UnityMTGrabHandlesScreen::hideHandles(CompAction* action, CompAction::State state, CompOption::Vector& options) { CompWindow* w = screen->findWindow(CompOption::getIntOptionNamed(options, "window", 0)); if (w) { UMTGH_WINDOW(w); if (!uw->allowHandles()) return false; if (uw->handlesVisible()) { uw->hideHandles(); mMoreAnimate = true; } } return true; } void UnityMTGrabHandlesScreen::optionChanged (CompOption *option, UnitymtgrabhandlesOptions::Options num) { if (num == UnitymtgrabhandlesOptions::FadeDuration) { unity::MT::FADE_MSEC = optionGetFadeDuration (); } } UnityMTGrabHandlesScreen::UnityMTGrabHandlesScreen(CompScreen* s) : PluginClassHandler (s), cScreen(CompositeScreen::get(s)), gScreen(GLScreen::get(s)), mGrabHandles(0), mHandleTextures(0), mLastClientListStacking(screen->clientList(true)), mCompResizeWindowAtom(XInternAtom(screen->dpy(), "_COMPIZ_RESIZE_NOTIFY", 0)), mMoreAnimate(false) { unity::MT::GrabHandle::ImplFactory::SetDefault (new unity::MT::X11ImplFactory (screen->dpy ())); unity::MT::Texture::Factory::SetDefault (new unity::MT::X11TextureFactory ()); ScreenInterface::setHandler(s); CompositeScreenInterface::setHandler(cScreen); GLScreenInterface::setHandler(gScreen); mHandleTextures.resize(unity::MT::NUM_HANDLES); for (unsigned int i = 0; i < unity::MT::NUM_HANDLES; i++) { CompString fname = "handle-"; CompString pname("unitymtgrabhandles"); CompSize size; fname = compPrintf("%s%i.png", fname.c_str(), i); GLTexture::List t = GLTexture::readImageToTexture(fname, pname, size); (static_cast(unity::MT::Texture::Factory::Default().get())->setActiveWrap(t)); mHandleTextures.at(i).first = unity::MT::Texture::Factory::Default ()->create (); mHandleTextures.at (i).second.width = size.width (); mHandleTextures.at (i).second.height = size.height (); } unity::MT::FADE_MSEC = optionGetFadeDuration (); optionSetToggleHandlesKeyInitiate(boost::bind(&UnityMTGrabHandlesScreen::toggleHandles, this, _1, _2, _3)); optionSetShowHandlesKeyInitiate(boost::bind(&UnityMTGrabHandlesScreen::showHandles, this, _1, _2, _3)); optionSetHideHandlesKeyInitiate(boost::bind(&UnityMTGrabHandlesScreen::hideHandles, this, _1, _2, _3)); optionSetFadeDurationNotify(boost::bind(&UnityMTGrabHandlesScreen::optionChanged, this, _1, _2)); } UnityMTGrabHandlesScreen::~UnityMTGrabHandlesScreen() { mGrabHandles.clear (); } UnityMTGrabHandlesWindow::UnityMTGrabHandlesWindow(CompWindow* w) : PluginClassHandler (w), window(w), cWindow(CompositeWindow::get(w)), gWindow(GLWindow::get(w)), mHandles() { WindowInterface::setHandler(window); CompositeWindowInterface::setHandler(cWindow); GLWindowInterface::setHandler(gWindow); mTimer.setCallback (boost::bind (&UnityMTGrabHandlesWindow::onHideTimeout, this)); } UnityMTGrabHandlesWindow::~UnityMTGrabHandlesWindow() { mTimer.stop (); if (mHandles) { UnityMTGrabHandlesScreen::get(screen)->removeHandles(mHandles); } } bool UnityMTGrabHandlesPluginVTable::init() { if (!CompPlugin::checkPluginABI("core", CORE_ABIVERSION) || !CompPlugin::checkPluginABI("composite", COMPIZ_COMPOSITE_ABI) || !CompPlugin::checkPluginABI("opengl", COMPIZ_OPENGL_ABI)) return false; return true; } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle.h0000644000004100000410000001074713437202764023750 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_H #define _UNITY_MT_GRAB_HANDLE_H #include #include #include #include #include "unity-mt-texture.h" #include "unity-mt-grab-handle-window.h" namespace unity { namespace MT { static const unsigned int NUM_HANDLES = 9; /* Update the server side position */ static const unsigned int PositionLock = (1 << 0); /* Update the client side position */ static const unsigned int PositionSet = (1 << 2); static const unsigned int TopLeftHandle = (1 << 0); static const unsigned int TopHandle = (1 << 1); static const unsigned int TopRightHandle = (1 << 2); static const unsigned int RightHandle = (1 << 3); static const unsigned int BottomRightHandle = (1 << 4); static const unsigned int BottomHandle = (1 << 5); static const unsigned int BottomLeftHandle = (1 << 6); static const unsigned int LeftHandle = (1 << 7); static const unsigned int MiddleHandle = (1 << 8); static const std::map maskHandles = { { TopLeftHandle, 0 }, { TopHandle, 1 }, { TopRightHandle, 2 }, { RightHandle, 3 }, { BottomRightHandle, 4}, { BottomHandle, 5 }, { BottomLeftHandle, 6 }, { LeftHandle, 7 }, { MiddleHandle, 8 } }; static const std::map handlesMask = { { 0, TopLeftHandle }, { 1, TopHandle }, { 2, TopRightHandle }, { 3, RightHandle }, { 4, BottomRightHandle}, { 5, BottomHandle }, { 6, BottomLeftHandle }, { 7, LeftHandle }, { 8, MiddleHandle } }; class GrabHandleGroup; class GrabHandle : public std::enable_shared_from_this , boost::noncopyable { public: typedef std::shared_ptr Ptr; static GrabHandle::Ptr create (Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id); ~GrabHandle(); bool operator== (const GrabHandle &other) const { return mId == other.mId; } bool operator!= (const GrabHandle &other) const { return !(*this == other); } void buttonPress (int x, int y, unsigned int button) const; void requestMovement (int x, int y, unsigned int button) const; void reposition(int x, int y, unsigned int flags); void reposition(int x, int y, unsigned int flags) const; void show(); void hide(); void raise() const; TextureLayout layout(); unsigned int id () const { return mId; } unsigned int width () const { return mRect.width; } unsigned int height () const { return mRect.height; } int x () const { return mRect.x; } int y () const { return mRect.y; } void damage (const nux::Geometry &g) const { mImpl->damage (g); } public: class Impl : boost::noncopyable { public: virtual ~Impl () {}; virtual void show () = 0; virtual void hide () = 0; virtual void buttonPress (int x, int y, unsigned int button) const = 0; virtual void lockPosition (int x, int y, unsigned int flags) = 0; virtual void damage (const nux::Geometry &g) = 0; }; class ImplFactory; private: GrabHandle(Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id); std::weak_ptr mOwner; Texture::Ptr mTexture; unsigned int mId; nux::Geometry mRect; Impl *mImpl; }; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-layout.cpp0000644000004100000410000000671313437202764025614 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle-layout.h" #include "unity-mt-grab-handle.h" unsigned int unity::MT::getLayoutForMask (unsigned int state, unsigned int actions) { unsigned int allHandles = 0; for (unsigned int i = 0; i < NUM_HANDLES; i++) { allHandles |= (1 << i); } struct _skipInfo { /* All must match in order for skipping to apply */ unsigned int state; /* Match if in state */ unsigned int notstate; /* Match if not in state */ unsigned int actions; /* Match if in actions */ unsigned int notactions; /* Match if not in actions */ unsigned int allowOnly; }; const unsigned int numSkipInfo = 5; const struct _skipInfo skip[5] = { /* Vertically maximized, don't care * about left or right handles, or * the movement handle */ { MaximizedVertMask, MaximizedHorzMask, 0, static_cast(~0), LeftHandle | RightHandle | MiddleHandle }, /* Horizontally maximized, don't care * about top or bottom handles, or * the movement handle */ { MaximizedHorzMask, MaximizedVertMask, 0, static_cast(~0), TopHandle | BottomHandle | MiddleHandle }, /* Maximized, don't care about the movement * handle */ { MaximizedVertMask | MaximizedHorzMask, 0, 0, static_cast(~0), MiddleHandle }, /* Immovable, don't show move handle */ { 0, static_cast(~0), static_cast(~0), MoveMask, TopLeftHandle | TopHandle | TopRightHandle | LeftHandle | RightHandle | BottomLeftHandle | BottomHandle | BottomRightHandle }, /* Not resizable, don't show resize handle */ { 0, static_cast(~0), static_cast(~0), ResizeMask, MiddleHandle }, }; /* Set the high bit if it was zero */ if (!state) state |= 0x8000; /* Set the high bit if it was zero */ if (!actions) actions |= 0x8000; for (unsigned int j = 0; j < numSkipInfo; j++) { const bool exactState = skip[j].state && skip[j].state != static_cast (~0); const bool exactActions = skip[j].actions && skip[j].actions != static_cast (~0); bool stateMatch = false; bool actionMatch = false; if (exactState) stateMatch = (skip[j].state & state) == skip[j].state; else stateMatch = skip[j].state & state; stateMatch &= !(state & skip[j].notstate); if (exactActions) actionMatch = (skip[j].actions & actions) == skip[j].actions; else actionMatch = skip[j].actions & actions; actionMatch &= !(actions & skip[j].notactions); if (stateMatch || actionMatch) allHandles &= skip[j].allowOnly; } return allHandles; } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle.cpp0000644000004100000410000000612413437202764024275 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle.h" #include "unity-mt-grab-handle-group.h" #include "unity-mt-grab-handle-window.h" #include "unity-mt-texture.h" #include "unity-mt-grab-handle-impl-factory.h" void unity::MT::GrabHandle::buttonPress (int x, int y, unsigned int button) const { mImpl->buttonPress (x, y, button); } void unity::MT::GrabHandle::requestMovement (int x, int y, unsigned int button) const { unity::MT::GrabHandleGroup::Ptr ghg = mOwner.lock (); ghg->requestMovement (x, y, (maskHandles.find (mId))->second, button); } void unity::MT::GrabHandle::show () { mImpl->show (); } void unity::MT::GrabHandle::hide () { mImpl->hide (); } void unity::MT::GrabHandle::raise () const { unity::MT::GrabHandleGroup::Ptr ghg = mOwner.lock (); std::shared_ptr gh = shared_from_this (); ghg->raiseHandle (gh); } void unity::MT::GrabHandle::reposition(int x, int y, unsigned int flags) { damage (mRect); if (flags & PositionSet) { mRect.x = x; mRect.y = y; } if (flags & PositionLock) { mImpl->lockPosition (x, y, flags); } damage (mRect); } void unity::MT::GrabHandle::reposition(int x, int y, unsigned int flags) const { if (flags & PositionLock) { mImpl->lockPosition (x, y, flags); } } unity::MT::TextureLayout unity::MT::GrabHandle::layout() { return TextureLayout(mTexture, mRect); } unity::MT::GrabHandle::GrabHandle(Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id) : mOwner(owner), mTexture (texture), mId(id), mRect (0, 0, width, height), mImpl (NULL) { } unity::MT::GrabHandle::Ptr unity::MT::GrabHandle::create (Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id) { unity::MT::GrabHandle::Ptr p (new unity::MT::GrabHandle (texture, width, height, owner, id)); p->mImpl = unity::MT::GrabHandle::ImplFactory::Default ()->create (p); return p; } unity::MT::GrabHandle::~GrabHandle() { delete mImpl; } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.h0000644000004100000410000001426313437202764024130 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include #include #include #include #include #include #include #include #include "unity-mt-texture.h" #include "unity-mt-grab-handle.h" #include "unity-mt-grab-handle-group.h" #include "unity-mt-grab-handle-window.h" #include "unity-mt-grab-handle-impl-factory.h" #include "unity-mt-grab-handle-layout.h" #include "unitymtgrabhandles_options.h" namespace unity { namespace MT { class DummyDamager { public: void damage (const nux::Geometry &g) { std::cout << "Damage rects: " << std::endl; std::cout << "x: " << g.x << " y: " << g.y << " width: " << g.width << " height: " << g.height << std::endl; } }; class X11TextureFactory : public Texture::Factory { public: void setActiveWrap (const GLTexture::List &); Texture::Ptr create (); private: GLTexture::List mWrap; }; class X11Texture : public Texture { public: typedef std::shared_ptr Ptr; X11Texture (const GLTexture::List &t); const GLTexture::List & get (); private: GLTexture::List mTexture; }; class X11ImplFactory : public GrabHandle::ImplFactory { public: X11ImplFactory (Display *dpy); GrabHandle::Impl * create (const GrabHandle::Ptr &h); private: Display *mDpy; }; class X11GrabHandleImpl : public GrabHandle::Impl { public: X11GrabHandleImpl (Display *dpy, const GrabHandle::Ptr &h); ~X11GrabHandleImpl (); public: void show (); void hide (); void buttonPress (int x, int y, unsigned int button) const; void lockPosition (int x, int y, unsigned int flags); void damage (const nux::Geometry &g) { CompRegion r (g.x, g.y, g.width, g.height); CompositeScreen::get (screen)->damageRegion (r); } private: std::weak_ptr mGrabHandle; Window mIpw; Display *mDpy; }; }; }; class UnityMTGrabHandlesScreen : public PluginClassHandler , public ScreenInterface, public CompositeScreenInterface, public GLScreenInterface, public UnitymtgrabhandlesOptions { public: UnityMTGrabHandlesScreen(CompScreen*); ~UnityMTGrabHandlesScreen(); CompositeScreen* cScreen; GLScreen* gScreen; public: void handleEvent(XEvent*); bool toggleHandles(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showHandles(CompAction* action, CompAction::State state, CompOption::Vector& options); bool hideHandles(CompAction* action, CompAction::State state, CompOption::Vector& options); void optionChanged (CompOption *option, UnitymtgrabhandlesOptions::Options num); void addHandles(const unity::MT::GrabHandleGroup::Ptr & handles); void removeHandles(const unity::MT::GrabHandleGroup::Ptr & handles); void addHandleWindow(const unity::MT::GrabHandle::Ptr &, Window); void removeHandleWindow(Window); void preparePaint(int); void donePaint(); void raiseHandle (const std::shared_ptr &, Window owner); std::vector & textures() { return mHandleTextures; } private: std::list mGrabHandles; std::vector mHandleTextures; std::map > mInputHandles; CompWindowVector mLastClientListStacking; Atom mCompResizeWindowAtom; bool mMoreAnimate; }; #define UMTGH_SCREEN(screen) \ UnityMTGrabHandlesScreen *us = UnityMTGrabHandlesScreen::get (screen) class UnityMTGrabHandlesWindow : public PluginClassHandler , public WindowInterface, public CompositeWindowInterface, public GLWindowInterface, public unity::MT::GrabHandleWindow { public: UnityMTGrabHandlesWindow(CompWindow*); ~UnityMTGrabHandlesWindow(); CompWindow* window; CompositeWindow* cWindow; GLWindow* gWindow; public: bool allowHandles(); bool handleTimerActive(); void grabNotify(int, int, unsigned int, unsigned int); void ungrabNotify(); void relayout(const CompRect&, bool); void getOutputExtents(CompWindowExtents& output); void moveNotify(int dx, int dy, bool immediate); bool glDraw(const GLMatrix&, const GLWindowPaintAttrib&, const CompRegion&, unsigned int); bool handlesVisible(); void hideHandles(); void showHandles(bool use_timer); void restackHandles(); void resetTimer(); void disableTimer(); protected: void raiseGrabHandle (const std::shared_ptr &h); void requestMovement (int x, int y, unsigned int direction, unsigned int button); private: bool onHideTimeout(); unity::MT::GrabHandleGroup::Ptr mHandles; CompTimer mTimer; }; #define UMTGH_WINDOW(window) \ UnityMTGrabHandlesWindow *uw = UnityMTGrabHandlesWindow::get (window) class UnityMTGrabHandlesPluginVTable : public CompPlugin::VTableForScreenAndWindow < UnityMTGrabHandlesScreen, UnityMTGrabHandlesWindow > { public: bool init(); }; ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-impl-factory.cpp0000644000004100000410000000211713437202764026677 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle-impl-factory.h" #include "unity-mt-grab-handle.h" std::shared_ptr unity::MT::GrabHandle::ImplFactory::mDefault; std::shared_ptr unity::MT::GrabHandle::ImplFactory::Default() { return mDefault; } void unity::MT::GrabHandle::ImplFactory::SetDefault (ImplFactory *factory) { mDefault.reset (factory); } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-group.h0000644000004100000410000000442613437202764025077 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_GROUP_H #define _UNITY_MT_GRAB_HANDLE_GROUP_H #include #include #include #include #include "unity-mt-grab-handle.h" #include "unity-mt-texture.h" #include "unity-mt-grab-handle-window.h" namespace unity { namespace MT { extern unsigned int FADE_MSEC; class GrabHandleGroup : public std::enable_shared_from_this , boost::noncopyable { public: typedef std::shared_ptr Ptr; static GrabHandleGroup::Ptr create (GrabHandleWindow *owner, std::vector &textures); ~GrabHandleGroup(); void relayout(const nux::Geometry&, bool); void restack(); bool visible(); bool animate(unsigned int); bool needsAnimate(); int opacity(); void hide(); void show(unsigned int handles = ~0); void raiseHandle (const std::shared_ptr &); std::vector layout(unsigned int handles); void forEachHandle (const std::function &); void requestMovement (int x, int y, unsigned int direction, unsigned int button); private: GrabHandleGroup(GrabHandleWindow *owner, std::vector &textures); enum class State { FADE_IN = 1, FADE_OUT, NONE }; State mState; int mOpacity; bool mMoreAnimate; std::vector mHandles; GrabHandleWindow *mOwner; }; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-window.h0000644000004100000410000000226213437202764025246 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_WINDOW_H #define _UNITY_MT_GRAB_HANDLE_WINDOW_H #include namespace unity { namespace MT { class GrabHandle; class GrabHandleWindow { public: virtual ~GrabHandleWindow () {}; virtual void requestMovement (int x, int y, unsigned int direction, unsigned int button) = 0; virtual void raiseGrabHandle (const std::shared_ptr &) = 0; }; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-impl-factory.h0000644000004100000410000000242013437202764026341 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_IMPL_FACTORY_H #define _UNITY_MT_GRAB_HANDLE_IMPL_FACTORY_H #include #include #include #include #include "unity-mt-grab-handle.h" namespace unity { namespace MT { class GrabHandle::ImplFactory { public: virtual ~ImplFactory() {}; static std::shared_ptr Default(); static void SetDefault(ImplFactory *); virtual GrabHandle::Impl * create(const GrabHandle::Ptr &h) = 0; protected: static std::shared_ptr mDefault; ImplFactory() {}; }; }; }; #endif ./plugins/unity-mt-grab-handles/unitymtgrabhandles.xml.in0000644000004100000410000000234013437202764024101 0ustar www-datawww-data <_short>Unity MT Grab Handles <_long>Small touch-based grab handles to move and resize a window opengl composite imgpng composite opengl cube rotate imgpng imgsvg imgjpeg ./plugins/unity-mt-grab-handles/images/0000755000004100000410000000000013437202764020314 5ustar www-datawww-data./plugins/unity-mt-grab-handles/images/handle-0.png0000644000004100000410000000616713437202764022424 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZKl$GL1cw7KPrA"H!qAqqP!BB!!7d<]5x@X*Ը_W# 2Ǡ~AHTa-lTa@ NK-Gz%8ASG}6rY}F)fس A w` wvƯ7޲uF1`#y_Fb, }"w4*y:- g#baB8޵1"&fN7`?(JƒL.ݠ\{>7l, usWg-(& K?*DYv$ ˞5oz?bO57p CNٓФ}ٯSLkaRv?vvlزTQ_kV W.&O/iOpJBB@)ܭ3V96֫6'Hba'/j3cwtp jRr4ek5=H̵e$4OeR u1up>c6fk+`}'cU s_]?>;ex4X Y\ C'J8- dFk4ƿSiϒFuPE `QMaľaB߶zQ@8Q8hS-׃v?+dRO.^,oN40op!Y\^᭐)|ABBf :yx{拏mYD'S]w?! 9N h u I "/*V/)1z6m-`̎v>j+,h._Lnj#FoEir?ڭq@mIό)\zzi%i1%?U\8t!03%O8?YSvj{ͽz GƐiD32 u9ZI?YHA~VSE^Hi'՜hsԍ"!C Kt'x3${ -]BLz3YbXLüVNc.\?c:vhy؎{\~{vg9n Mھ j9xcJ766%MI<0a 7ĆSutJQcr|ǦltvYT ERYyK8dx8#pd!D` LgԡS 7|!!8xzinhL[o]2vb _w\A,̂(L7ZR5G\ `hzSVYUM$,t"⇤C29:,b-CRNrm֫'A`,`/D4MCs㚘K ˜|e`a`~ ?6qcg,Cjx@U㇦8a- OYցVFkX㹼^ US\!--Tal27Vx5 z6=G`$T32 aw6pᇘ0N{MC,d^[V;{$D׹$QP޴0YS6ݓ?=}ð<[cNF+NLxlr3.At9 ^rHG Kӡ؁*uN4FŢxW8 ڍ:u@$/-:6[ugw hV6\`pwl~SFWe^MȀ I7M/J 2@ݑ:*u1$mW\sq}]kz5N{{ͲU[Az5tf/atYd߄'DŽyQs#^ +лcm{vnw]17LQs8ǤeA5ϧү֌_{x2f)'{+rFXՈ1c*ʈ7*h祪]+Zlrp;}q5͚ͦmWԴse2atk^Z5vĸAi<pz=V"F?ޟ\ʥzV{Fq raQ׮aV f ZlH)ȋu܎ cL>;SfFM0uZo7ٹifL=PNU.Xֆ m{o%&.jCdگbg̾_zQ7湆VƊkj)H߹M=?)K5WT;TicN6.vgW)1#5')~d/%4E.zn>-H.[Zaڪ6['*7rH%Ȋתgu Jh(%S4Mb`EY_(JKIϫ$*aq&5apݤ$3z둔5+9)%I$VgH41F-')7bNMtNy#DFB H? 7Z[Dh1e%{z QLP H̏1vK/C;Q#C@!#]O;8x&Cf^o #yRG5݋g`MFIENDB`./plugins/unity-mt-grab-handles/images/handle-2.png0000644000004100000410000000616513437202764022424 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZo$G껧xvr6$H$@"$B%?x%BBHBB ވ% `ج^{xjvjg!,j]~u#Ģ#Gąn >XPFb}vh %7Ԍ|zJAS^IM&Z͎b͹ DŽka Lv`0Z:p9!U 6Ƅs1!9 ƘWh ho9_{K z ҴGhcgO1cw^`6O٣wz2ؚl $Gj$4酇Kk MQqp>k {呗Znf aVC ·O}łNq (˜WsxY"jyB yY 'E|W)n,lm&CV252k4G_Zη%u^@ +8 )qzr!/ ;Rϣn)4} 44 20){s-A@?dOFmNy0&/#(|I)}Xknvwhx{s8P8q6cZ$$7;܁#v(t4M T( BaL&*9<8^~ߧV,.nFqI捾i=Qy-t3c'rsxQ*O*byRWs|Y.kLC "ȘTr>O &%f)I1+aoOrѓn6{sG XVy2_hc>q~Y\Z's:!3n#绁oufDb~frsFu٧=IpN%3@͑yE^V(?~E+pp$ HXK8>GdH$EF*W/@3iɒ\{r ᆄo1Ei`AytaDy3Yb(gY`x "fVoM`P,FR $qb $G;Daϰv΋3<[Fc{0b¯\Ly!bД?rp#mHL=snQT5qbP8sa؝@3bAEUvð֚U77ns 7U9`&GO$-hGPwPpag8ڰa5$،L?gKo,R$fru@("3v65FMkmȅM?_v,#j8m'"NHHsK'!_ RqzQ/qybR^ B $5Zu7̊:mmE#y dIT#i߶8a\0"z"WY?yc(!VV2Ջݨ9Akv<b ?;JVQs (eg)Ak^.?uCDql°ӋO63pnnq@}(KFXU*">ꀓ|*޽a9U"j(@@"(jϏ= m'l#\{5D hz-#hZo]4߀1`MnMشS,ݕ4caX a6,񈚳p{ujv֮~`:=X2ƛљ LBMjh* }^npĄ'Xf:c{ft,w=wӝWK5L 紈SʘQ >aGCN†^85VУg|ߺAU]cD?5eh$:ճ7ګuQ%8xEȳ(WnӵTFVA4?_Wvs ƣHMZN(M5P G]ϊ%ߪh§_iEsz)Z42Js.&z/^O hUSel-fwnG)ŇxEhemV!ܫ_+AW-(X'9Loaֳb @)UQM{HZ/ם94~NU סy?_1KEߚyfz獖O;1^"r4) kʧɯPONJ`RU0Q_WxtxW~Ws7ѩki#X3t~<$nΙi(UJ%slᘧw[ EHʪٜl[3o<2TxYIENDB`./plugins/unity-mt-grab-handles/images/handle-7.png0000644000004100000410000000614313437202764022425 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZocG?3jcǎ$=IvmnW E-R+C Hx≿^*$@ *B+!$Њ^жB%v7Ď23|ɱ7)iH9; '8?=3z?V qaܸڇȵOc=NRz؀N@ M||\3:f.*%l f]yfų7C Iha_X/3; rVSPRM_.Ƣ!Ţ(H RgT(9]Zk<'"hC#F`4P%aԼpEHwvNr '4o%~`1@(+Xý+YNGTꮖB /i[r·}|=~ `c<8si CVnTlDNvq=]Occehqե$VҔ /+ܝeICD}Bp}m g/փ$iEkO=A vOyQ=Laը_ݑALIM}#y}zGW>/;xsUs옡..N~4׭Lamf02hFM`X7BW({>ѯH|ۿ9ff89w,=qJd4} ;~1ETЕ'AR5r&e@@͓]Mp kX00(=QAȇa0='Md:h嗜t4&e_#ɼ6GGR^K6؇lvVdh2Pgn(k*6 Z,g˲> MxiDnY$@RPnr,1?DLPVTEfɌ0$3J\ *BU_5(h\8xزM2iY*rM]S{"B 4vw4[f>.@p\}w6{Y 26w{h+&@(Z(Sw\ʒKe'gՀD{B* DRU}L$*RT$&dlԜ_2X0~-HL 2 %|]]q R5 ˩+d .TduE@f3 (\<[-Iu1][U2: a"j͆Tb-&#p`PY qvJO-HDett\(%X15f2.M6ʅ{(?~4s5$抚l HBPSPr !%F\qRv#!$E ufvps! ׶%}|"c}AaQAykY ,eढdd;4Aϱed#>VW1aLHkQ.K M AOG8?53yBB` *Of'f/hoc$qJCUeigg/dfqxSFzsI iՋH !,>r}(d0#ko;!0֎D][g4Cg7x|@Az7/,}jGU1GA| Jڵj 1s 34B]<S6o^ XEc1*p\JuÖOjhj$f"1]~j$+ema9*+ ep4ը ! !׬By]\vڰ!z& zYimUL8uW.䁳p̝x\>5aЦã@:V6lntMV8\Dfc Vk@ooas˩\_ CϦ,/G Z֡au,B@B]O_.>eTNXmz“"ZV@hh1ND BUA$(bcQ!C2$PƹFƹ'^k;(OXiZ 5w/Í`[56Aa@Bۢ$\nrsƭ][ cS0n95 WwZ)GCeL=ԻҦ!':Pz.!AH X=Ӵ=q 4q<ݿ;Qkm9_3V cr*CvՌ> ?gZ*?ٝD7%} ʲKӄ:PM[IUoN(0'1/ߌg =Һ|덐ŅbD[iLVSNĴ[KUu{sj.ْZy_YNy$Z7:ɉ5ѫXnE_-y ^UaDrEd%;+J"F~UxV3Ex/#W)b"Hmd|eT:jDVl!Lb=׮2xL$ޗzOr ݂v, P {H!=^O&'` aNǗ4z}0N䃁#|Qͧ3O܇g`SFyIENDB`./plugins/unity-mt-grab-handles/images/handle-8.png0000644000004100000410000001407513437202764022431 0ustar www-datawww-dataPNG  IHDRVVUVatEXtSoftwareAdobe ImageReadyqe<IDATx]\G}sٝwZ%Y>v`LB J$L*O! *lc+v0cÖVҬ9}{4~zz3{[dʪz}{Lwާ?~v;;wr307+|(ߝ| Buw:Zok߁}>O/h<| _oXty}B)@Ax=19,8dqq4>Z|L=V3e[w2Nc@Ec\ehi8alI[#S8NXy\?GcMP$97&]|b z8]M l`"0{s4a8QĐAk6(N8AbA`;)IKcdt[~vGUKva$"V`#4t|p>5#(s 5J+m&Oy?͈rBM/~3˖ vC :-cG7oh!̪O26<;% "f㯶~xNq-݀*ޙ E[q";W'u 2ie.uƥΕA&ضްHBN^቉\- {<=l`nߥY)rku'[MAkGh7cZҿ' >yg/_=w T$m+8ը ٹRڊa/˺혹`tc<JdWJCwS FYzUɏl\'ms(PL];Rĕѕ+lbڎv\ЇOD꞉4Lf픤kί_Vn7-cǓ&qn ɽMsZb|vy*G}X ;\P~@kP YIY1gWB=swXXi~NTkɋ}7nvoѼB\D}C} عv\ 2uR įH}DIrj;g!qCbϫbn)성a7X?%ލ g48 8`YNɉGHFJ™I&g@\+*\׀K'kغng4ȈU&1֑4 PA u1 8ki \ǵu *ǥ6'O˿7;upȯ-,?XK4 h!I|f en䶻Eax*Gи8z8Ǧ4F1$siNwB}X `؊fw1fZ|j!mb4,t*p0GWmWi6kbbڦHѮ ^Uμ' FAz[a,H`Scʩc8I٣?8fsx#w9PNsa0CKI҇KPTlvndWQQ%zjcG$C 4L)MFoDžD 2Z MlˑVmGW\2мfvIϡ @xb\X޹aߊ30#~E+qڑCp(@178^FfHNCG&C#qڹ# K$呅YKtcgK&Ɋ jε'4=C !MSdѩ|Asz!}yҲX^plCsaxa?7ͥ%J5ZNb8Sg鵕}yj}O^{陧JMrw@`R'샸l`-=<oP~k &>@}4tio[Ɇ٨if8͚l֖¼-h-ĦsaZY벫b-C_oHc|uc`rԗTclfT.TKg|-;-gmEEopy߫$;nYx8x /ܗ J[*NbvXNHȄE'4+C˶|8Kym"˃~,iSK&=A#Z&s4|P yOS>PФrJ6xIRk^]0\apm>潈4'#L6Ut6"_MSOz&+p}> 9߯-}ri;51:kˡYtpJۭ?6= (^1&h)ELrA 'DϏ>&$R@coY캾~Dl"5 tS ڡ>pTcui^?4i1i4 8hgY6k^[>t}ǚtj/6 nfl)]Sp^ ;z]04&RK-GhZJP]k d"0%c?PGCY} N$ǁs- .v,K56dSb͝Si^"p!IsA,%&28AyVn8nԖ9VSǓ(H&,-r8'0pexpB4֮׬Z;粘PZgk_8-fcn[Pq}&;TCi8Mcb?- ID"QR틯yL T y ]( sX:|Ǧ]MDkDON w5uk뾵ȸ'*6lQt)qbv"1{}б72dmX8קYK)I~lv3:s'^&S5-ׅZso>%m9Њ†'֍##!s181CRDIӇfɃ'r PpB5L9nIAC,$%ZӡCoK$X"GHAqܝԷIJm +oD{`yi<_:"SAH6um=xw4`h"UU1߈GhY|2ڡكm%վsHķA#>ږRjsQ̝l{$RhLACCsߏXP(^;Cvexi1L#`Hf7::#lzREju I1FaAhxSht!Y]S`d*+j̟TsmUo\,U޵Ƣ @m7="yrW/ΜGѱ¼Om.o6Pӏ]Owֿ;eܠUρ&<] jJ+fZ3j-7ʕsWn5j\W4~SG&a.58ЂVë_/qpa.(8\EۭEa-Du ^ҳ?N#޲c(Iք$}x(kd<r= oWmZ9ѭ劬V7̈́4R;A@‘R>8}j.WZ jjlזR:} TMkkv& ¦WziT>{X:pbŧ*o gP~rߜ_0V 0262MF.hlۓp8.ڲ7}"ns-fߨO[CAmE7[ҳ9M] b~hM*vV<Y6ȫkxXސоLZKZ mML&٭TQ cQ)*{W){0ADl؍/*qH*_oy7\)g} F6^mO-iLɚL;:`Ryz08.202>r"%L% xn;JEIZ/WP]We: ď˼JD4QyRJsL2L8 ;TƾW OmYrg?? qi d7zaz]t9?XEź= xy۰Oe#-98C9np,:BN{WK0as[#t<{U-s0p٘/sta8:tXmC@u疸&p-FjVh em?d뷠[Sx\a}.7TlAsօ(-ױ}wS/z|j3hڋ[ @i]BX"bww8m1j8[ $Ų7Jc4B6 hf L?3rըͫTbb;Y -*K+0+N_Y{N].$} . n'\TBݪ+`uaN\pz9+7tQ<Ӱǯc@}_=-n&d,,"jim߽'.LJVnşPTC98)A0Kz;:sc \|UkC[Vf7elkZ>f---jr,Y+Y|Wj֌p’%؎R\31n=vtY۶{䝇 "PLy^fmmZj3<jEv>O:, [AzF캖X W[U]V>ϵ몲]JM rŀjwO}1-Ed~pa;UH 3`+ Ho{r%.3E[Kg'9W/<_;lUBp{y64ЇgΨ-q.k5O}\xB=7y ZoSON^} #zVB[]^kvco,"vFhީXC הӢ#,g$qZ̰AGag[9aDbOS*ݚ9%6;vjSĿ>%zޞa ݼ1'F l`/YOrͦeϠ/>j+jsbVmz4Vx>\dW8G%EoSK}*3 YwՀ/#Yu={c>s95tXlƻ"Z;^Csq%mr)W=% KW{h9/gFi ǘG?fGmcWPwE2a&|Ÿ9mhŤ8Xko8x15DX<ĝbE BôpEgpo0s&V=Li\Y+SW*^ުK4<͈X@C#؞ R2oۿw(I*EXEBIJqn9$ jJrZ6ǦI=^צipϥ})˦xb,R00B>2$k(nLapם(#%&=lI@di%Xq|fy,e3^k9EwѬáW>BZn~`^R l\S $SApȍ·#-c؋lX+ j "\+I.j. ֭v~" X ,t nm}3Q'#A&@Y]3"n=,)کR}3PmEQbb3 İoG"J/pb^)^; tР[y6T/L%IENDB`./plugins/unity-mt-grab-handles/images/handle-4.png0000644000004100000410000000616713437202764022430 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZKl$GL1cw7KPrA"H!qAqqP!BB!!7d<]5x@X*Ը_W# 2Ǡ~AHTa-lTa@ NK-Gz%8ASG}6rY}F)fس A w` wvƯ7޲uF1`#y_Fb, }"w4*y:- g#baB8޵1"&fN7`?(JƒL.ݠ\{>7l, usWg-(& K?*DYv$ ˞5oz?bO57p CNٓФ}ٯSLkaRv?vvlزTQ_kV W.&O/iOpJBB@)ܭ3V96֫6'Hba'/j3cwtp jRr4ek5=H̵e$4OeR u1up>c6fk+`}'cU s_]?>;ex4X Y\ C'J8- dFk4ƿSiϒFuPE `QMaľaB߶zQ@8Q8hS-׃v?+dRO.^,oN40op!Y\^᭐)|ABBf :yx{拏mYD'S]w?! 9N h u I "/*V/)1z6m-`̎v>j+,h._Lnj#FoEir?ڭq@mIό)\zzi%i1%?U\8t!03%O8?YSvj{ͽz GƐiD32 u9ZI?YHA~VSE^Hi'՜hsԍ"!C Kt'x3${ -]BLz3YbXLüVNc.\?c:vhy؎{\~{vg9n Mھ j9xcJ766%MI<0a 7ĆSutJQcr|ǦltvYT ERYyK8dx8#pd!D` LgԡS 7|!!8xzinhL[o]2vb _w\A,̂(L7ZR5G\ `hzSVYUM$,t"⇤C29:,b-CRNrm֫'A`,`/D4MCs㚘K ˜|e`a`~ ?6qcg,Cjx@U㇦8a- OYցVFkX㹼^ US\!--Tal27Vx5 z6=G`$T32 aw6pᇘ0N{MC,d^[V;{$D׹$QP޴0YS6ݓ?=}ð<[cNF+NLxlr3.At9 ^rHG Kӡ؁*uN4FŢxW8 ڍ:u@$/-:6[ugw hV6\`pwl~SFWe^MȀ I7M/J 2@ݑ:*u1$mW\sq}]kz5N{{ͲU[Az5tf/atYd߄'DŽyQs#^ +лcm{vnw]17LQs8ǤeA5ϧү֌_{x2f)'{+rFXՈ1c*ʈ7*h祪]+Zlrp;}q5͚ͦmWԴse2atk^Z5vĸAi<pz=V"F?ޟ\ʥzV{Fq raQ׮aV f ZlH)ȋu܎ cL>;SfFM0uZo7ٹifL=PNU.Xֆ m{o%&.jCdگbg̾_zQ7湆VƊkj)H߹M=?)K5WT;TicN6.vgW)1#5')~d/%4E.zn>-H.[Zaڪ6['*7rH%Ȋתgu Jh(%S4Mb`EY_(JKIϫ$*aq&5apݤ$3z둔5+9)%I$VgH41F-')7bNMtNy#DFB H? 7Z[Dh1e%{z QLP H̏1vK/C;Q#C@!#]O;8x&Cf^o #yRG5݋g`MFIENDB`./plugins/unity-mt-grab-handles/images/handle-1.png0000644000004100000410000000612713437202764022421 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZُ#G諭cܳ; ͩ$y /R $xx^"$@H$B@!9Hًlvv_}wWuǞt{v6bT.Wu# ^9 @2;lD2q M9kJ軜I@L'^m^`c`(/J>MoaZ6%pIw;-kaUsxxZqUxt\).BF-9%SJ[9[0~r¾~~$!8̳93ApE8CkIF4 y)Nd){<1>T" `/=Zz|[l*:j!I B 3efF`fks3/^1E d{W(kM\v,UMFLEE/J ]&HHHR6'/ C+6(9ղHk/,DC9q.8*x4Y$(I-dPTI1Fqmڦu>sQ84ѩWfW/?>ܾdHD2i}R~x~\el%kh`%aHUQ-H^|yq%KDYAW*l4͇w Z>zB/(Or?v}Ã6X]{$#j2])x>z BHPzOt%^d2H$CkKR彾H )qZOTԱ"Yh/byiOckQVYSDUJOjs'.ߥR^-Md+r(/4z %S1A|T)=S\7?0Gt ^ #U<)baTˌMr ӟ$XBpwV]R!F Y&՚YHa~O/4|XXW= BWdY8~2N׬w/5p Nh7hƶCLųr-1ފ;z5 ڥ7%Ue0Ble.<*ʅD2SFvdkhr0DC3> KUh^m⇌?IHD$Ͷm"rWm:-OfȽ۞$4t3 "|QB} $ˊ߭;_HW+mf{љW)qP"@(BӦIZo6ݝy4~N֙}o5K}j_}s(nkl4t\*"#iu+괞%6|E3ͱ@z8D+?Vw#/)~ 4HM|ͭp'e2SiQuT}AOmdah,(L(f6VW͍g1.vnFhZ ibd|Z_S_oi"nS8`k6.|~˫]0gҏ<'ݍO+ FWx#IS~ ÔH?L5 P^p2Lbd&57$*QIJeK5 4rQD҇kp=GG%dq044@Lpʛa},/ |^DT{ų 0jl(+IENDB`./plugins/unity-mt-grab-handles/images/handle-6.png0000644000004100000410000000616513437202764022430 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZo$G껧xvr6$H$@"$B%?x%BBHBB ވ% `ج^{xjvjg!,j]~u#Ģ#Gąn >XPFb}vh %7Ԍ|zJAS^IM&Z͎b͹ DŽka Lv`0Z:p9!U 6Ƅs1!9 ƘWh ho9_{K z ҴGhcgO1cw^`6O٣wz2ؚl $Gj$4酇Kk MQqp>k {呗Znf aVC ·O}łNq (˜WsxY"jyB yY 'E|W)n,lm&CV252k4G_Zη%u^@ +8 )qzr!/ ;Rϣn)4} 44 20){s-A@?dOFmNy0&/#(|I)}Xknvwhx{s8P8q6cZ$$7;܁#v(t4M T( BaL&*9<8^~ߧV,.nFqI捾i=Qy-t3c'rsxQ*O*byRWs|Y.kLC "ȘTr>O &%f)I1+aoOrѓn6{sG XVy2_hc>q~Y\Z's:!3n#绁oufDb~frsFu٧=IpN%3@͑yE^V(?~E+pp$ HXK8>GdH$EF*W/@3iɒ\{r ᆄo1Ei`AytaDy3Yb(gY`x "fVoM`P,FR $qb $G;Daϰv΋3<[Fc{0b¯\Ly!bД?rp#mHL=snQT5qbP8sa؝@3bAEUvð֚U77ns 7U9`&GO$-hGPwPpag8ڰa5$،L?gKo,R$fru@("3v65FMkmȅM?_v,#j8m'"NHHsK'!_ RqzQ/qybR^ B $5Zu7̊:mmE#y dIT#i߶8a\0"z"WY?yc(!VV2Ջݨ9Akv<b ?;JVQs (eg)Ak^.?uCDql°ӋO63pnnq@}(KFXU*">ꀓ|*޽a9U"j(@@"(jϏ= m'l#\{5D hz-#hZo]4߀1`MnMشS,ݕ4caX a6,񈚳p{ujv֮~`:=X2ƛљ LBMjh* }^npĄ'Xf:c{ft,w=wӝWK5L 紈SʘQ >aGCN†^85VУg|ߺAU]cD?5eh$:ճ7ګuQ%8xEȳ(WnӵTFVA4?_Wvs ƣHMZN(M5P G]ϊ%ߪh§_iEsz)Z42Js.&z/^O hUSel-fwnG)ŇxEhemV!ܫ_+AW-(X'9Loaֳb @)UQM{HZ/ם94~NU סy?_1KEߚyfz獖O;1^"r4) kʧɯPONJ`RU0Q_WxtxW~Ws7ѩki#X3t~<$nΙi(UJ%slᘧw[ EHʪٜl[3o<2TxYIENDB`./plugins/unity-mt-grab-handles/images/handle-3.png0000644000004100000410000000614313437202764022421 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZocG?3jcǎ$=IvmnW E-R+C Hx≿^*$@ *B+!$Њ^жB%v7Ď23|ɱ7)iH9; '8?=3z?V qaܸڇȵOc=NRz؀N@ M||\3:f.*%l f]yfų7C Iha_X/3; rVSPRM_.Ƣ!Ţ(H RgT(9]Zk<'"hC#F`4P%aԼpEHwvNr '4o%~`1@(+Xý+YNGTꮖB /i[r·}|=~ `c<8si CVnTlDNvq=]Occehqե$VҔ /+ܝeICD}Bp}m g/փ$iEkO=A vOyQ=Laը_ݑALIM}#y}zGW>/;xsUs옡..N~4׭Lamf02hFM`X7BW({>ѯH|ۿ9ff89w,=qJd4} ;~1ETЕ'AR5r&e@@͓]Mp kX00(=QAȇa0='Md:h嗜t4&e_#ɼ6GGR^K6؇lvVdh2Pgn(k*6 Z,g˲> MxiDnY$@RPnr,1?DLPVTEfɌ0$3J\ *BU_5(h\8xزM2iY*rM]S{"B 4vw4[f>.@p\}w6{Y 26w{h+&@(Z(Sw\ʒKe'gՀD{B* DRU}L$*RT$&dlԜ_2X0~-HL 2 %|]]q R5 ˩+d .TduE@f3 (\<[-Iu1][U2: a"j͆Tb-&#p`PY qvJO-HDett\(%X15f2.M6ʅ{(?~4s5$抚l HBPSPr !%F\qRv#!$E ufvps! ׶%}|"c}AaQAykY ,eढdd;4Aϱed#>VW1aLHkQ.K M AOG8?53yBB` *Of'f/hoc$qJCUeigg/dfqxSFzsI iՋH !,>r}(d0#ko;!0֎D][g4Cg7x|@Az7/,}jGU1GA| Jڵj 1s 34B]<S6o^ XEc1*p\JuÖOjhj$f"1]~j$+ema9*+ ep4ը ! !׬By]\vڰ!z& zYimUL8uW.䁳p̝x\>5aЦã@:V6lntMV8\Dfc Vk@ooas˩\_ CϦ,/G Z֡au,B@B]O_.>eTNXmz“"ZV@hh1ND BUA$(bcQ!C2$PƹFƹ'^k;(OXiZ 5w/Í`[56Aa@Bۢ$\nrsƭ][ cS0n95 WwZ)GCeL=ԻҦ!':Pz.!AH X=Ӵ=q 4q<ݿ;Qkm9_3V cr*CvՌ> ?gZ*?ٝD7%} ʲKӄ:PM[IUoN(0'1/ߌg =Һ|덐ŅbD[iLVSNĴ[KUu{sj.ْZy_YNy$Z7:ɉ5ѫXnE_-y ^UaDrEd%;+J"F~UxV3Ex/#W)b"Hmd|eT:jDVl!Lb=׮2xL$ޗzOr ݂v, P {H!=^O&'` aNǗ4z}0N䃁#|Qͧ3O܇g`SFyIENDB`./plugins/unity-mt-grab-handles/images/handle-5.png0000644000004100000410000000612713437202764022425 0ustar www-datawww-dataPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZُ#G諭cܳ; ͩ$y /R $xx^"$@H$B@!9Hًlvv_}wWuǞt{v6bT.Wu# ^9 @2;lD2q M9kJ軜I@L'^m^`c`(/J>MoaZ6%pIw;-kaUsxxZqUxt\).BF-9%SJ[9[0~r¾~~$!8̳93ApE8CkIF4 y)Nd){<1>T" `/=Zz|[l*:j!I B 3efF`fks3/^1E d{W(kM\v,UMFLEE/J ]&HHHR6'/ C+6(9ղHk/,DC9q.8*x4Y$(I-dPTI1Fqmڦu>sQ84ѩWfW/?>ܾdHD2i}R~x~\el%kh`%aHUQ-H^|yq%KDYAW*l4͇w Z>zB/(Or?v}Ã6X]{$#j2])x>z BHPzOt%^d2H$CkKR彾H )qZOTԱ"Yh/byiOckQVYSDUJOjs'.ߥR^-Md+r(/4z %S1A|T)=S\7?0Gt ^ #U<)baTˌMr ӟ$XBpwV]R!F Y&՚YHa~O/4|XXW= BWdY8~2N׬w/5p Nh7hƶCLųr-1ފ;z5 ڥ7%Ue0Ble.<*ʅD2SFvdkhr0DC3> KUh^m⇌?IHD$Ͷm"rWm:-OfȽ۞$4t3 "|QB} $ˊ߭;_HW+mf{љW)qP"@(BӦIZo6ݝy4~N֙}o5K}j_}s(nkl4t\*"#iu+괞%6|E3ͱ@z8D+?Vw#/)~ 4HM|ͭp'e2SiQuT}AOmdah,(L(f6VW͍g1.vnFhZ ibd|Z_S_oi"nS8`k6.|~˫]0gҏ<'ݍO+ FWx#IS~ ÔH?L5 P^p2Lbd&57$*QIJeK5 4rQD҇kp=GG%dq044@Lpʛa},/ |^DT{ų 0jl(+IENDB`./plugins/unityshell/0000755000004100000410000000000013437202764015134 5ustar www-datawww-data./plugins/unityshell/CMakeLists.txt0000644000004100000410000000365613437202764017706 0ustar www-datawww-datafind_package (Compiz REQUIRED) include (CompizPlugin) set (COMPIZ_PLUGIN_INSTALL_TYPE "package") # Guard against Compiz altering global state. # https://bugs.launchpad.net/compiz/+bug/1096807 if(CMAKE_BUILD_TYPE STREQUAL "") set(revert_compiz TRUE) endif() set (libdir ${CMAKE_INSTALL_LIBDIR}) set (includedir ${CMAKE_INSTALL_INCLUDEDIR}) set (libdir ${CMAKE_INSTALL_LIBDIR}) set (datadir ${CMAKE_INSTALL_FULL_DATADIR}) compiz_plugin (unityshell PKGDEPS ${UNITY_PLUGIN_DEPS} PLUGINDEPS composite opengl compiztoolbox scale CFLAGSADD "-DINSTALLPREFIX='\"${CMAKE_INSTALL_PREFIX}\"' -DPKGDATADIR='\"${PKGDATADIR}\"' -I${CMAKE_BINARY_DIR} -I${CMAKE_SOURCE_DIR} ${BOOT_LOGGER_FLAG} -DGETTEXT_PACKAGE='\"unity\"' ${MAINTAINER_CXXFLAGS} -I${CMAKE_SOURCE_DIR}/dash/ -I${CMAKE_SOURCE_DIR}/launcher/ -I${CMAKE_SOURCE_DIR}/lockscreen/ -I${CMAKE_SOURCE_DIR}/hud/ -I${CMAKE_SOURCE_DIR}/panel/ -I${CMAKE_SOURCE_DIR}/shortcuts/ -I${CMAKE_SOURCE_DIR}/shutdown/ -I${CMAKE_SOURCE_DIR}/unity-shared/" LIBDIRS "${CMAKE_BINARY_DIR}/UnityCore" ) if(revert_compiz) set (CMAKE_BUILD_TYPE "" CACHE STRING "Build type (Debug/Release/RelWithDebInfo/MinSizeRe)" FORCE) endif() set(UNITY_PRIVATE_LIBS a11y-lib dash-lib decorations-lib hud-lib launcher-lib lockscreen-lib panel-lib shortcuts-lib shutdown-lib switcher-lib unity-core-${UNITY_API_VERSION} unity-shared unity-shared-bamf unity-shared-compiz) add_dependencies(unityshell ${UNITY_PRIVATE_LIBS}) target_link_libraries(unityshell ${UNITY_PRIVATE_LIBS} pam) set_target_properties(unityshell PROPERTIES INSTALL_RPATH "${CACHED_UNITY_PRIVATE_DEPS_LIBRARY_DIRS}" INSTALL_RPATH_USE_LINK_PATH TRUE) # # Data # install (FILES plugin-unityshell.png DESTINATION ${COMPIZ_DATADIR}/ccsm/icons/hicolor/64x64/apps) ./plugins/unityshell/src/0000755000004100000410000000000013437202764015723 5ustar www-datawww-data./plugins/unityshell/src/UnityShowdesktopHandler.cpp0000644000004100000410000001275213437202764023277 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #include #include "UnityShowdesktopHandler.h" #include "unity-shared/UnitySettings.h" namespace unity { ShowdesktopHandlerWindowInterface::~ShowdesktopHandlerWindowInterface() { } /* 300 ms */ const unsigned int ShowdesktopHandler::fade_time = 300; std::list ShowdesktopHandler::animating_windows (0); bool ShowdesktopHandler::ShouldHide (ShowdesktopHandlerWindowInterface *wi) { if (wi->OverrideRedirect()) return false; if (!wi->Managed()) return false; if (wi->Grabbed()) return false; if (wi->DesktopOrDock()) return false; if (wi->SkipTaskbarOrPager()) return false; if (wi->Hidden()) if ((wi->ShowDesktopMode() || wi->Shaded() || wi->Minimized())) return false; return true; } guint32 ShowdesktopHandler::inhibiting_xid = 0; void ShowdesktopHandler::InhibitLeaveShowdesktopMode (guint32 xid) { if (!inhibiting_xid) inhibiting_xid = xid; } void ShowdesktopHandler::AllowLeaveShowdesktopMode (guint32 xid) { if (inhibiting_xid == xid) inhibiting_xid = 0; } guint32 ShowdesktopHandler::InhibitingXid() { return inhibiting_xid; } ShowdesktopHandler::ShowdesktopHandler (ShowdesktopHandlerWindowInterface *wi, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire_interface) : showdesktop_handler_window_interface_(wi) , lock_acquire_interface_(lock_acquire_interface) , remover_() , state_(StateVisible) , progress_(0.0f) , was_hidden_(false) { } ShowdesktopHandler::~ShowdesktopHandler() { } void ShowdesktopHandler::FadeOut() { if (state_ != StateVisible && state_ != StateFadeIn) return; state_ = ShowdesktopHandler::StateFadeOut; progress_ = Settings::Instance().low_gfx() ? 1.0f : 0.0f; was_hidden_ = showdesktop_handler_window_interface_->Hidden(); if (!was_hidden_) { showdesktop_handler_window_interface_->Hide(); showdesktop_handler_window_interface_->NotifyHidden(); remover_ = lock_acquire_interface_->InputRemover(); if (std::find (animating_windows.begin(), animating_windows.end(), showdesktop_handler_window_interface_) == animating_windows.end()) animating_windows.push_back (showdesktop_handler_window_interface_); } } void ShowdesktopHandler::FadeIn() { if (state_ != StateInvisible && state_ != StateFadeOut) return; state_ = ShowdesktopHandler::StateFadeIn; if (!was_hidden_) { showdesktop_handler_window_interface_->Show(); showdesktop_handler_window_interface_->NotifyShown(); remover_.reset(); if (std::find (animating_windows.begin(), animating_windows.end(), showdesktop_handler_window_interface_) == animating_windows.end()) animating_windows.push_back(showdesktop_handler_window_interface_); } } ShowdesktopHandlerWindowInterface::PostPaintAction ShowdesktopHandler::Animate (unsigned int ms) { float inc = ms / static_cast (fade_time); if (state_ == ShowdesktopHandler::StateFadeOut) { progress_ = Settings::Instance().low_gfx() ? 1.0f : progress_ + inc; if (progress_ >= 1.0f) { progress_ = 1.0f; state_ = StateInvisible; } } else if (state_ == StateFadeIn) { progress_ = Settings::Instance().low_gfx() ? 0.0f : progress_ - inc; if (progress_ <= 0.0f) { progress_ = 0.0f; state_ = StateVisible; } } else if (state_ == StateVisible) return ShowdesktopHandlerWindowInterface::PostPaintAction::Remove; else if (state_ == StateInvisible) return ShowdesktopHandlerWindowInterface::PostPaintAction::Wait; return ShowdesktopHandlerWindowInterface::PostPaintAction::Damage; } void ShowdesktopHandler::PaintOpacity (unsigned short &opacity) { if (progress_ == 0.0f) opacity = std::numeric_limits ::max(); else opacity *= (1.0f - progress_); } unsigned int ShowdesktopHandler::GetPaintMask() { return (progress_ == 1.0f) ? showdesktop_handler_window_interface_->NoCoreInstanceMask() : 0; } void ShowdesktopHandler::HandleShapeEvent() { /* Ignore sent events from the InputRemover */ if (remover_) { remover_->refresh(); } } void ShowdesktopHandler::WindowFocusChangeNotify() { if (showdesktop_handler_window_interface_->Minimized()) { for (ShowdesktopHandlerWindowInterface *w : animating_windows) w->DisableFocus(); showdesktop_handler_window_interface_->MoveFocusAway(); for (ShowdesktopHandlerWindowInterface *w : animating_windows) w->EnableFocus(); } } void ShowdesktopHandler::UpdateFrameRegion (CompRegion &r) { r = CompRegion(); /* Ensure no other plugins can touch this frame region */ showdesktop_handler_window_interface_->OverrideFrameRegion (r); } } ./plugins/unityshell/src/MultiMonitor.h0000644000004100000410000000154213437202764020540 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Jason Smith */ #ifndef MULTIMONITOR_H #define MULTIMONITOR_H namespace unity { namespace monitors { const unsigned MAX = 6; } } #endif./plugins/unityshell/src/GesturalWindowSwitcher.h0000644000004100000410000000542513437202764022571 0ustar www-datawww-data/* * GesturalWindowSwitcher.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel d'Andrada */ #ifndef GESTURAL_WINDOW_SWITCHER_H #define GESTURAL_WINDOW_SWITCHER_H #include #include "CompoundGestureRecognizer.h" namespace unity { class UnityScreen; class GesturalWindowSwitcherPrivate; /* Manipulates the window switcher according to multi-touch gestures The following gestural interactions with the window switcher are implemented: 1. 3-fingers double tap -> switches to previous window 2. 3-fingers tap followed by 3-fingers hold -> shows window switcher - drag those 3-fingers -> change selected window icon - release fingers -> selects window and closes switcher 3. 3-fingers tap followed by 3-fingers hold -> shows window switcher - release fingers -> switcher will kept being shown for some seconds still - drag with one or three fingers -> change selected window - release finger(s) -> selects window and closes switcher 4. 3-fingers tap followed by 3-fingers hold -> shows window switcher - release fingers -> switcher will kept being shown for some seconds still - tap on some window icon -> selects that icon and closes the switcher */ class GesturalWindowSwitcher : public nux::GestureTarget { public: GesturalWindowSwitcher(); virtual ~GesturalWindowSwitcher(); // in milliseconds static const int SWITCHER_TIME_AFTER_DOUBLE_TAP = 350; static const int SWITCHER_TIME_AFTER_HOLD_RELEASED = 7000; // How far, in screen pixels, a drag gesture must go in order // to trigger a change in the selected window. static const float DRAG_DELTA_FOR_CHANGING_SELECTION; // How far, in screen pixels, a mouse pointer must move in order // to be considered dragging the switcher. static const float MOUSE_DRAG_THRESHOLD; virtual nux::GestureDeliveryRequest GestureEvent(nux::GestureEvent const& event); private: GesturalWindowSwitcherPrivate* p; }; typedef std::shared_ptr ShPtGesturalWindowSwitcher; } // namespace unity #endif // GESTURAL_WINDOW_SWITCHER_H ./plugins/unityshell/src/WindowMinimizeSpeedController.h0000644000004100000410000000346013437202764024075 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #ifndef WINDOWMINIMIZESPEEDCONTROLLER_H #define WINDOWMINIMIZESPEEDCONTROLLER_H #include #include #include #include typedef struct _GSettings GSettings; using namespace unity; class WindowMinimizeSpeedController { public: WindowMinimizeSpeedController(); void UpdateCount(); int getDuration(); sigc::signal DurationChanged; private: void SetDuration(); glib::Object _settings; int _minimize_count; int _minimize_speed_threshold; int _minimize_slow_duration; int _minimize_fast_duration; glib::Signal _minimize_count_changed; glib::Signal _minimize_speed_threshold_changed; glib::Signal _minimize_slow_duration_changed; glib::Signal _minimize_fast_duration_changed; int _duration; }; #endif // WINDOWMINIMIZESPEEDCONTROLLER_H ./plugins/unityshell/src/UnityShowdesktopHandler.h0000644000004100000410000001114413437202764022736 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #ifndef UNITY_SHOWDESKTOP_HANDLER_H #define UNITY_SHOWDESKTOP_HANDLER_H #include #include #include #include #include "inputremover.h" namespace unity { class ShowdesktopHandlerWindowInterface { public: enum class PostPaintAction { Wait = 0, Damage = 1, Remove = 2 }; virtual ~ShowdesktopHandlerWindowInterface (); void EnableFocus () { DoEnableFocus (); } void DisableFocus () { DoDisableFocus (); } bool OverrideRedirect () { return IsOverrideRedirect (); } bool Managed () { return IsManaged (); } bool Grabbed () { return IsGrabbed (); } bool DesktopOrDock () { return IsDesktopOrDock (); } bool SkipTaskbarOrPager () { return IsSkipTaskbarOrPager (); } bool Hidden () { return IsHidden (); } bool Shaded () { return IsShaded (); } bool Minimized () { return IsMinimized (); } bool ShowDesktopMode () { return IsInShowdesktopMode (); } void OverrideFrameRegion (CompRegion &r) { return DoOverrideFrameRegion (r); } void Hide () { DoHide (); } void NotifyHidden () { DoNotifyHidden (); } void Show () { DoShow (); } void NotifyShown () { DoNotifyShown (); } void MoveFocusAway () { DoMoveFocusAway (); } PostPaintAction HandleAnimations (unsigned int ms) { return DoHandleAnimations (ms); } void AddDamage () { DoAddDamage (); } void DeleteHandler () { DoDeleteHandler (); } unsigned int NoCoreInstanceMask () { return GetNoCoreInstanceMask (); } private: virtual void DoEnableFocus () = 0; virtual void DoDisableFocus () = 0; virtual bool IsOverrideRedirect () = 0; virtual bool IsManaged () = 0; virtual bool IsGrabbed () = 0; virtual bool IsDesktopOrDock () = 0; virtual bool IsSkipTaskbarOrPager () = 0; virtual bool IsHidden () = 0; virtual bool IsInShowdesktopMode () = 0; virtual bool IsShaded () = 0; virtual bool IsMinimized () = 0; virtual void DoOverrideFrameRegion (CompRegion &) = 0; virtual void DoHide () = 0; virtual void DoNotifyHidden () = 0; virtual void DoShow () = 0; virtual void DoNotifyShown () = 0; virtual void DoMoveFocusAway () = 0; virtual PostPaintAction DoHandleAnimations (unsigned int ms) = 0; virtual void DoAddDamage () = 0; virtual void DoDeleteHandler () = 0; virtual unsigned int GetNoCoreInstanceMask () = 0; }; class ShowdesktopHandler { public: ShowdesktopHandler (ShowdesktopHandlerWindowInterface *uwi, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire_interface); ~ShowdesktopHandler (); typedef enum { StateVisible = 0, StateFadeOut = 1, StateFadeIn = 2, StateInvisible = 3 } State; public: void FadeOut (); void FadeIn (); ShowdesktopHandlerWindowInterface::PostPaintAction Animate (unsigned int ms); void PaintOpacity (unsigned short &opacity); unsigned int GetPaintMask (); void HandleShapeEvent (); void WindowFocusChangeNotify (); void UpdateFrameRegion (CompRegion &r); ShowdesktopHandler::State GetState (); static const unsigned int fade_time; static std::list animating_windows; static bool ShouldHide (ShowdesktopHandlerWindowInterface *); static void InhibitLeaveShowdesktopMode (guint32 xid); static void AllowLeaveShowdesktopMode (guint32 xid); static guint32 InhibitingXid (); private: ShowdesktopHandlerWindowInterface *showdesktop_handler_window_interface_; compiz::WindowInputRemoverLockAcquireInterface *lock_acquire_interface_; compiz::WindowInputRemoverLock::Ptr remover_; ShowdesktopHandler::State state_; float progress_; bool was_hidden_; static guint32 inhibiting_xid; }; } #endif ./plugins/unityshell/src/unityshell_glow.cpp0000644000004100000410000003200213437202764021654 0ustar www-datawww-data/** * Copyright : (C) 2006-2012 by Patrick Niklaus, Roi Cohen, * Danny Baumann, Sam Spilsbury * Authors: Patrick Niklaus * Roi Cohen * Danny Baumann * Sam Spilsbury * Marco Trevisan * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * **/ #include #include "unityshell.h" #include "unityshell_glow.h" #include "decorations/glow_texture.h" namespace unity { /* * UnityWindow::paintGlow * * Takes our glow texture, stretches the appropriate positions in the glow texture, * adds those geometries (so plugins like wobby and deform this texture correctly) * and then draws the glow texture with this geometry (plugins like wobbly and friends * will automatically deform the texture based on our set geometry) */ void UnityWindow::paintGlow(GLMatrix const& transform, GLWindowPaintAttrib const& attrib, glow::Quads const& glow_quads, GLTexture::List const& outline_texture, nux::Color const& color, unsigned mask) { GLushort colorData[4]; colorData[0] = color.red * 0xffff; colorData[1] = color.green * 0xffff; colorData[2] = color.blue * 0xffff; colorData[3] = color.alpha * 0xffff; gWindow->vertexBuffer()->begin (); /* There are 8 glow parts of the glow texture which we wish to paint * separately with different transformations */ for (unsigned i = 0; i < unsigned(glow::QuadPos::LAST); ++i) { /* Using precalculated quads here */ glow::Quads::Quad const& quad = glow_quads[static_cast(i)]; if (quad.box.x1() < quad.box.x2() && quad.box.y1() < quad.box.y2()) { GLTexture::MatrixList matl = { quad.matrix }; /* Add color data for all 6 vertices of the quad */ for (int n = 0; n < 6; n++) gWindow->vertexBuffer()->addColors(1, colorData); CompRegion reg(quad.box); gWindow->glAddGeometry(matl, reg, reg); } } if (gWindow->vertexBuffer()->end()) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* we use PAINT_WINDOW_TRANSFORMED_MASK here to force the usage of a good texture filter */ for (GLTexture *tex : outline_texture) { mask |= PAINT_WINDOW_BLEND_MASK | PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK; gWindow->glDrawTexture(tex, transform, attrib, mask); } glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GLScreen::get(screen)->setTexEnvMode (GL_REPLACE); } } /* * UnityWindow::computeGlowQuads * * This function computures the matrix transformation required for each * part of the glow texture which we wish to stretch to some rectangular * dimentions * * There are eight quads different parts of the texture which we wish to * paint here, the 4 sides and four corners, eg: * * ------------------ * | 1 | 4 | 6 | * ------------- ------------------ * | 1 | 4 | 6 | | | | | * ------------- | | | | * | 2 | n | 7 | -> | 2 | n | 7 | * ------------- | | | | * | 3 | 5 | 8 | | | | | * ------------- ------------------ * | 3 | 5 | 8 | * ------------------ * * In this example here, 2, 4, 5 and 7 are stretched, and the matrices for * each quad rect adjusted accordingly for it's size compared to the original * texture size. * * When we are adjusting the matrices here, the initial size of each corner has * a size of of "1.0f", so according to 2x2 matrix rules, * while you will see here that matrix->xx is (1 / glowSize) * where glowSize is the size the user specifies they want their glow to extend. * (likewise, matrix->yy is adjusted similarly for corners and for top/bottom) * * matrix->x0 and matrix->y0 here are set to be the top left edge of the rect * adjusted by the matrix scale factor (matrix->xx and matrix->yy) * */ glow::Quads UnityWindow::computeGlowQuads(nux::Geometry const& geo, GLTexture::List const& texture, int glow_size) { glow::Quads glow_quads; if (texture.empty()) return glow_quads; int x1, x2, y1, y2; int glow_offset; GLTexture::Matrix const& matrix = texture.front()->matrix(); CompRect *box; GLTexture::Matrix *quadMatrix; glow_size = glow_size * texture::GLOW_SIZE / (texture::GLOW_SIZE - texture::GLOW_OFFSET); glow_offset = (glow_size * texture::GLOW_OFFSET / texture::GLOW_SIZE) + 1; /* Top left corner */ box = &glow_quads[glow::QuadPos::TOPLEFT].box; glow_quads[glow::QuadPos::TOPLEFT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::TOPLEFT].matrix; /* Set the desired rect dimentions * for the part of the glow we are painting */ x1 = geo.x - glow_size + glow_offset; y1 = geo.y - glow_size + glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling both parts of the texture in a positive direction * here (left to right top to bottom) * * The base position (x0 and y0) here requires us to move backwards * on the x and y dimentions by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 1.0f / glow_size; quadMatrix->yy = 1.0f / glow_size; quadMatrix->x0 = -(x1 * quadMatrix->xx); quadMatrix->y0 = -(y1 * quadMatrix->yy); x2 = std::min(geo.x + glow_offset, geo.x + (geo.width / 2)); y2 = std::min(geo.y + glow_offset, geo.y + (geo.height / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Top right corner */ box = &glow_quads[glow::QuadPos::TOPRIGHT].box; glow_quads[glow::QuadPos::TOPRIGHT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::TOPRIGHT].matrix; /* Set the desired rect dimentions * for the part of the glow we are painting */ x1 = geo.x + geo.width - glow_offset; y1 = geo.y - glow_size + glow_offset; x2 = geo.x + geo.width + glow_size - glow_offset; /* * 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling the y part of the texture in a positive direction * and the x part in a negative direction here * (right to left top to bottom) * * The base position (x0 and y0) here requires us to move backwards * on the y dimention and forwards on x by the calculated rect dimentions * multiplied by the scale factors (since we are moving forward on x we * need the inverse of that which is 1 - x1 * xx */ quadMatrix->xx = -1.0f / glow_size; quadMatrix->yy = 1.0f / glow_size; quadMatrix->x0 = 1.0 - (x1 * quadMatrix->xx); quadMatrix->y0 = -(y1 * quadMatrix->yy); x1 = std::max(geo.x + geo.width - glow_offset, geo.x + (geo.width / 2)); y2 = std::min(geo.y + glow_offset, geo.y + (geo.height / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Bottom left corner */ box = &glow_quads[glow::QuadPos::BOTTOMLEFT].box; glow_quads[glow::QuadPos::BOTTOMLEFT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::BOTTOMLEFT].matrix; x1 = geo.x - glow_size + glow_offset; y1 = geo.y + geo.height - glow_offset; x2 = geo.x + glow_offset; y2 = geo.y + geo.height + glow_size - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling the x part of the texture in a positive direction * and the y part in a negative direction here * (left to right bottom to top) * * The base position (x0 and y0) here requires us to move backwards * on the x dimention and forwards on y by the calculated rect dimentions * multiplied by the scale factors (since we are moving forward on x we * need the inverse of that which is 1 - y1 * yy */ quadMatrix->xx = 1.0f / glow_size; quadMatrix->yy = -1.0f / glow_size; quadMatrix->x0 = -(x1 * quadMatrix->xx); quadMatrix->y0 = 1.0f - (y1 * quadMatrix->yy); y1 = std::max(geo.y + geo.height - glow_offset, geo.y + (geo.height / 2)); x2 = std::min(x2, geo.x + (geo.width / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Bottom right corner */ box = &glow_quads[glow::QuadPos::BOTTOMRIGHT].box; glow_quads[glow::QuadPos::BOTTOMRIGHT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::BOTTOMRIGHT].matrix; x1 = geo.x + geo.width - glow_offset; y1 = geo.y + geo.height - glow_offset; x2 = geo.x + geo.width + glow_size - glow_offset; y2 = geo.y + geo.height + glow_size - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling the both parts of the texture in a negative direction * (right to left bottom to top) * * The base position (x0 and y0) here requires us to move forwards * on both dimentions by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = -1.0f / glow_size; quadMatrix->yy = -1.0f / glow_size; quadMatrix->x0 = 1.0 - (x1 * quadMatrix->xx); quadMatrix->y0 = 1.0 - (y1 * quadMatrix->yy); x1 = std::max(geo.x + geo.width - glow_offset, geo.x + (geo.width / 2)); y1 = std::max(geo.y + geo.height - glow_offset, geo.y + (geo.height / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Top edge */ box = &glow_quads[glow::QuadPos::TOP].box; glow_quads[glow::QuadPos::TOP].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::TOP].matrix; x1 = geo.x + glow_offset; y1 = geo.y - glow_size + glow_offset; x2 = geo.x + geo.width - glow_offset; y2 = geo.y + glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the x part of the texture here, but we * are scaling on the y part in a positive direciton * * The base position (y0) here requires us to move backwards * on the x dimention and forwards on y by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 0.0f; quadMatrix->yy = 1.0f / glow_size; quadMatrix->x0 = 1.0; quadMatrix->y0 = -(y1 * quadMatrix->yy); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Bottom edge */ box = &glow_quads[glow::QuadPos::BOTTOM].box; glow_quads[glow::QuadPos::BOTTOM].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::BOTTOM].matrix; x1 = geo.x + glow_offset; y1 = geo.y + geo.height - glow_offset; x2 = geo.x + geo.width - glow_offset; y2 = geo.y + geo.height + glow_size - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the x part of the texture here, but we * are scaling on the y part in a negative direciton * * The base position (y0) here requires us to move forwards * on y by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 0.0f; quadMatrix->yy = -1.0f / glow_size; quadMatrix->x0 = 1.0; quadMatrix->y0 = 1.0 - (y1 * quadMatrix->yy); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Left edge */ box = &glow_quads[glow::QuadPos::LEFT].box; glow_quads[glow::QuadPos::LEFT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::LEFT].matrix; x1 = geo.x - glow_size + glow_offset; y1 = geo.y + glow_offset; x2 = geo.x + glow_offset; y2 = geo.y + geo.height - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the y part of the texture here, but we * are scaling on the x part in a positive direciton * * The base position (x0) here requires us to move backwards * on x by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 1.0f / glow_size; quadMatrix->yy = 0.0f; quadMatrix->x0 = -(x1 * quadMatrix->xx); quadMatrix->y0 = 1.0; box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Right edge */ box = &glow_quads[glow::QuadPos::RIGHT].box; glow_quads[glow::QuadPos::RIGHT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::RIGHT].matrix; x1 = geo.x + geo.width - glow_offset; y1 = geo.y + glow_offset; x2 = geo.x + geo.width + glow_size - glow_offset; y2 = geo.y + geo.height - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the y part of the texture here, but we * are scaling on the x part in a negative direciton * * The base position (x0) here requires us to move forwards * on x by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = -1.0f / glow_size; quadMatrix->yy = 0.0f; quadMatrix->x0 = 1.0 - (x1 * quadMatrix->xx); quadMatrix->y0 = 1.0; box->setGeometry(x1, y1, x2 - x1, y2 - y1); return glow_quads; } } // Unity namespace ./plugins/unityshell/src/CompoundGestureRecognizer.h0000644000004100000410000000376513437202764023262 0ustar www-datawww-data/* * CompoundGestureRecognizer.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel d'Andrada */ #ifndef COMPOUND_GESTURE_RECOGNIZER_H #define COMPOUND_GESTURE_RECOGNIZER_H #include namespace nux { class GestureEvent; } namespace unity { enum class RecognitionResult { NONE, DOUBLE_TAP_RECOGNIZED, /*! Returned when a double-tap is recognized */ TAP_AND_HOLD_RECOGNIZED, /*!< Returned when a "tap and hold" is recognized At this point the user is still "holding". I.e., his fingers are still on the touchscreen or trackpad. */ }; class CompoundGestureRecognizerPrivate; /*! Recognizes compound gestures. I.e. high level gestures that are maded up by two sequencial regular gestures (like a tap followed by a second tap). */ class CompoundGestureRecognizer { public: // in milliseconds static const int MAX_TIME_BETWEEN_GESTURES = 600; static const int MAX_TAP_TIME = 300; static const int HOLD_TIME = 600; CompoundGestureRecognizer(); virtual ~CompoundGestureRecognizer(); virtual RecognitionResult GestureEvent(nux::GestureEvent const& event); private: CompoundGestureRecognizerPrivate* p; }; } // namespace unity #endif // COMPOUND_GESTURE_RECOGNIZER_H ./plugins/unityshell/src/comptransientfor.h0000644000004100000410000000265513437202764021501 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_COMPIZTRANSIENTFOR_H #define _COMPIZ_COMPIZTRANSIENTFOR_H #include #include #include #include "transientfor.h" // Will be merged back into compiz namespace compiz { class PrivateCompTransientForReader; class CompTransientForReader : public X11TransientForReader { public: CompTransientForReader (CompWindow *w); virtual ~CompTransientForReader (); bool isTransientFor (unsigned int transient); bool isGroupTransientFor (unsigned int transient); protected: unsigned int getAncestor (); private: PrivateCompTransientForReader *priv; }; } #endif ./plugins/unityshell/src/ScreenIntrospection.h0000644000004100000410000000227013437202764022075 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan (Treviño) */ #ifndef SCREEN_INTROSPECTION_H #define SCREEN_INTROSPECTION_H #include "Introspectable.h" #include namespace unity { namespace debug { class ScreenIntrospection : public Introspectable { public: ScreenIntrospection(CompScreen* screen); protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); private: CompScreen* screen_; }; } } #endif ./plugins/unityshell/src/unityshell.h0000644000004100000410000004606413437202764020306 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-16 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #ifndef UNITYSHELL_H #define UNITYSHELL_H #include #include #include #include #include #include #include #include #include #include // These fixes some definitions from the composite header #ifdef COLOR #define COMPIZ_COMPOSITE_COLOR 0xffff #undef COLOR #endif #ifdef OPAQUE #define COMPIZ_COMPOSITE_OPAQUE 0xffff #undef OPAQUE #endif #ifdef BRIGHT #define COMPIZ_COMPOSITE_BRIGHT 0xffff #undef BRIGHT #endif #include "unityshell_options.h" #include "Introspectable.h" #include "DashController.h" #include "UnitySettings.h" #include "DashStyle.h" #include "EdgeBarrierController.h" #include "FavoriteStoreGSettings.h" #include "InputMonitor.h" #include "ShortcutController.h" #include "LauncherController.h" #include "LockScreenController.h" #include "LockScreenSettings.h" #include "PanelController.h" #include "PanelStyle.h" #include "UScreen.h" #include "DebugDBusInterface.h" #include "ScreenIntrospection.h" #include "ScreenSaverDBusManager.h" #include "SwitcherController.h" #include "SessionController.h" #include "SessionDBusManager.h" #include "SpreadWidgets.h" #include "UBusWrapper.h" #include "UnityshellPrivate.h" #include "UnityShowdesktopHandler.h" #include "ThumbnailGenerator.h" #include "MenuManager.h" #include "UnityCore/SessionManager.h" #include "compizminimizedwindowhandler.h" #include "BGHash.h" #include #include #include "HudController.h" #include "WindowMinimizeSpeedController.h" #include "unityshell_glow.h" namespace unity { class UnityWindow; namespace decoration { class Manager; class Window; enum class WidgetState : unsigned; } namespace compiz_utils { struct CairoContext; struct PixmapTexture; } /* base screen class */ class UnityScreen : public debug::Introspectable, public sigc::trackable, public ScreenInterface, public CompositeScreenInterface, public GLScreenInterface, public ScaleScreenInterface, public BaseSwitchScreen, public PluginClassHandler , public CompAction::Container, public UnityshellOptions { public: UnityScreen(CompScreen* s); ~UnityScreen(); switcher::Controller::Ptr switcher_controller(); launcher::Controller::Ptr launcher_controller(); lockscreen::Controller::Ptr lockscreen_controller(); void SetUpAndShowSwitcher(switcher::ShowMode show_mode = switcher::ShowMode::CURRENT_VIEWPORT); protected: void damageCutoff() override; void preparePaint(int ms) override; void donePaint() override; void handleCompizEvent(const char *pluginName, const char *eventName, CompOption::Vector &o) override; void damageRegion(const CompRegion ®ion) override; /* paint on top of all windows if we could not find a window * to paint underneath */ bool glPaintOutput(const GLScreenPaintAttrib&, const GLMatrix&, const CompRegion&, CompOutput*, unsigned int) override; /* paint in the special case that the output is transformed */ void glPaintTransformedOutput(const GLScreenPaintAttrib&, const GLMatrix&, const CompRegion&, CompOutput*, unsigned int) override; /* handle X11 events */ void handleEvent(XEvent*) override; void addSupportedAtoms(std::vector&) override; /* handle showdesktop */ void enterShowDesktopMode() override; void leaveShowDesktopMode(CompWindow *w) override; /* window scaling */ bool layoutSlotsAndAssignWindows() override; bool getMipmap() override { return false; } /* Handle changes in the number of workspaces by showing the switcher * or not showing the switcher */ bool setOptionForPlugin(const char* plugin, const char* name, CompOption::Value& v) override; /* init plugin actions for screen */ bool initPluginForScreen(CompPlugin* p) override; void outputChangeNotify() override; void averageColorChangeNotify(const unsigned short *color) override; CompAction::Vector& getActions() override; std::string GetName() const override; void AddProperties(debug::IntrospectionData&) override; private: static void InitNuxThread(nux::NThread* thread, void* data); void InitUnityComponents(); bool InitPluginActions(); void InitAltTabNextWindow(); /* prepares nux for drawing */ void nuxPrologue(); /* pops nux draw stack */ void nuxEpilogue(); /* nux draw wrapper */ void paintOutput(); void paintPanelShadow(CompRegion const& clip); void setPanelShadowMatrix(const GLMatrix& matrix); void updateBlurDamage(); bool showMenuBarInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showMenuBarTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showLauncherKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showLauncherKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showPanelFirstMenuKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showPanelFirstMenuKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool executeCommand(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showDesktopKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool spreadAppWindowsInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool spreadAppWindowsAnywhereInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool setKeyboardFocusKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabInitiateCommon(CompAction* action, switcher::ShowMode mode); bool altTabTerminateCommon(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabForwardAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabPrevAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabNextWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabPrevWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool ShowHudInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool ShowHudTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool LockScreenInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); /* handle option changes and change settings inside of the * panel and dock views */ void optionChanged(CompOption*, Options num); void OnMinimizeDurationChanged(); void NeedsRelayout(); void ScheduleRelayout(guint timeout); bool forcePaintOnTop(); void OnLockScreenRequested(); void OnScreenLocked(); void OnScreenUnlocked(); void SaveLockStamp(bool); std::string GetLockStampFile() const; bool DoesPointIntersectUnityGeos(nux::Point const& pt); ui::LayoutWindow::Ptr GetSwitcherDetailLayoutWindow(Window window) const; enum CancelActionTarget { LAUNCHER_SWITCHER, SHORTCUT_HINT }; void SendExecuteCommand(); void EnsureSuperKeybindings(); void CreateSuperNewAction(char shortcut, impl::ActionModifiers flag); void EnableCancelAction(CancelActionTarget target, bool enabled, int modifiers = 0); void compizDamageNux(CompRegion const& region); void determineNuxDamage(CompRegion &nux_damage); void Relayout(); void RaiseInputWindows(); static void OnStartKeyNav(GVariant* data, void* value); static void OnExitKeyNav(GVariant* data, void* value); void restartLauncherKeyNav(); bool ShowHud(); void RaiseOSK(); void OnDashRealized(); void OnLauncherStartKeyNav(GVariant* data); void OnLauncherEndKeyNav(GVariant* data); void OnSwitcherDetailChanged(bool detail); void OnRedrawRequested(); void OnInitiateSpread(); void OnTerminateSpread(); void LoadPanelShadowTexture(); void DamagePanelShadow(); void OnViewHidden(nux::BaseWindow *bw); void RestoreWindow(GVariant* data); bool SaveInputThenFocus(const guint xid); void UpdateDecorationStyle(); void InitGesturesSupport(); void UpdateGesturesSupport(); void DrawPanelUnderDash(); void FillShadowRectForOutput(CompRect &shadowRect, CompOutput const &output); unsigned CompizModifiersToNux(unsigned input) const; unsigned XModifiersToNux(unsigned input) const; void UpdateCloseWindowKey(CompAction::KeyBinding const&); void UpdateActivateIndicatorsKey(); void DamageBlurUpdateRegion(nux::Geometry const&); void ShowFirstRunHints(); void SpreadAppWindows(bool anywhere); bool queryForShader(); Window GetNextActiveWindow() const; void SetNextActiveWindow(Window next_active_window); /* We store these to avoid unecessary calls to ::get */ CompScreen* screen; CompositeScreen* cScreen; GLScreen* gScreen; ScaleScreen* sScreen; std::unique_ptr tick_source_; std::unique_ptr animation_controller_; Settings unity_settings_; dash::Style dash_style_; panel::Style panel_style_; internal::FavoriteStoreGSettings favorite_store_; ThumbnailGenerator thumbnail_generator_; lockscreen::Settings lockscreen_settings_; input::Monitor input_monitor_; /* The window thread should be the last thing removed, as c++ does it in reverse order */ std::unique_ptr wt; WindowManager& WM; menu::Manager::Ptr menus_; std::shared_ptr deco_manager_; /* These must stay below the window thread, please keep the order */ launcher::Controller::Ptr launcher_controller_; dash::Controller::Ptr dash_controller_; panel::Controller::Ptr panel_controller_; debug::Introspectable *introspectable_switcher_controller_; switcher::Controller::Ptr switcher_controller_; hud::Controller::Ptr hud_controller_; shortcut::Controller::Ptr shortcut_controller_; session::DBusManager::Ptr session_dbus_manager_; session::Controller::Ptr session_controller_; lockscreen::DBusManager::Ptr screensaver_dbus_manager_; lockscreen::Controller::Ptr lockscreen_controller_; ui::EdgeBarrierController::Ptr edge_barriers_; debug::DebugDBusInterface debugger_; std::unique_ptr bghash_; spread::Widgets::Ptr spread_widgets_; session::Manager::Ptr session_; /* Subscription for gestures that manipulate Unity launcher */ std::unique_ptr gestures_sub_launcher_; /* Subscription for gestures that manipulate Unity dash */ std::unique_ptr gestures_sub_dash_; /* Subscription for gestures that manipulate windows. */ std::unique_ptr gestures_sub_windows_; bool needsRelayout; bool super_keypressed_; typedef std::shared_ptr CompActionPtr; typedef std::vector ShortcutActions; ShortcutActions _shortcut_actions; std::map _escape_actions; std::unordered_map windows_for_monitor_; /* keyboard-nav mode */ CompWindow* newFocusedWindow; CompWindow* lastFocusedWindow; GLTexture::List _shadow_texture; /* handle paint order */ bool doShellRepaint; bool didShellRepaint; bool allowWindowPaint; CompOutput* last_output_; /* a small count-down work-a-around * to force full redraws of the shell * a certain number of frames after a * suspend / resume cycle */ unsigned int force_draw_countdown_; CompRegion panelShadowPainted; CompRegion nuxRegion; CompRegion fullscreenRegion; CompWindow* firstWindowAboveShell; CompWindow* onboard_; ::GLFramebufferObject *oldFbo; int overlay_monitor_; CompScreen::GrabHandle grab_index_; CompWindowList fullscreen_windows_; bool painting_tray_; unsigned int tray_paint_mask_; unsigned int last_scroll_event_; int hud_keypress_time_; int first_menu_keypress_time_; GLMatrix panel_shadow_matrix_; bool paint_panel_under_dash_; std::unordered_set fake_decorated_windows_; bool scale_just_activated_; WindowMinimizeSpeedController minimize_speed_controller_; debug::ScreenIntrospection screen_introspection_; UBusManager ubus_manager_; glib::SourceManager sources_; connection::Wrapper hud_ungrab_slot_; connection::Manager launcher_size_connections_; CompRegion buffered_compiz_damage_this_frame_; CompRegion buffered_compiz_damage_last_frame_; bool ignore_redraw_request_; bool dirty_helpers_on_this_frame_; bool is_desktop_active_; bool key_nav_mode_requested_; uint64_t big_tick_; unsigned int back_buffer_age_; Window next_active_window_; friend class UnityWindow; friend class debug::ScreenIntrospection; friend class decoration::Manager; }; class UnityWindow : public WindowInterface, public GLWindowInterface, public CompositeWindowInterface, public ShowdesktopHandlerWindowInterface, public compiz::WindowInputRemoverLockAcquireInterface, public WrapableHandler, public BaseSwitchWindow, public PluginClassHandler , public debug::Introspectable, public sigc::trackable { public: UnityWindow(CompWindow*); ~UnityWindow(); void minimize() override; void unminimize() override; bool minimized() const override; bool focus() override; void activate() override; //! Emited when CompWindowNotifyBeforeDestroy is received sigc::signal being_destroyed; protected: void updateFrameRegion(CompRegion ®ion) override; void getOutputExtents(CompWindowExtents& output) override; /* occlusion detection * and window hiding */ bool glPaint(GLWindowPaintAttrib const&, GLMatrix const&, CompRegion const&, unsigned mask) override; /* basic window draw function */ bool glDraw(GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask) override; bool damageRect(bool initial, CompRect const&) override; void updateIconPos(int &wx, int &wy, int x, int y, float width, float height) override; void windowNotify(CompWindowNotify n) override; void moveNotify(int x, int y, bool immediate) override; void resizeNotify(int x, int y, int w, int h) override; void stateChangeNotify(unsigned int lastState) override; bool place(CompPoint& pos) override; void scalePaintDecoration(GLWindowPaintAttrib const&, GLMatrix const&, CompRegion const&, unsigned mask) override; std::string GetName() const override; void AddProperties(debug::IntrospectionData&) override; private: typedef compiz::CompizMinimizedWindowHandler UnityMinimizedHandler; typedef std::shared_ptr PixmapTexturePtr; void DoEnableFocus(); void DoDisableFocus(); bool IsOverrideRedirect(); bool IsManaged(); bool IsGrabbed(); bool IsDesktopOrDock(); bool IsSkipTaskbarOrPager(); bool IsHidden(); bool IsInShowdesktopMode(); bool IsShaded(); bool IsMinimized(); bool CanBypassLockScreen() const; void DoOverrideFrameRegion(CompRegion &r); void DoHide(); void DoNotifyHidden(); void DoShow(); void DoNotifyShown(); void OnInitiateSpread(); void OnTerminateSpread(); CompPoint tryNotIntersectUI(CompPoint& pos); nux::Geometry GetScaledGeometry(); nux::Geometry GetLayoutWindowGeometry(); void enterShowDesktop(); void leaveShowDesktop(); bool HandleAnimations(unsigned int ms); bool handleEvent(XEvent *event); void paintThumbnail(nux::Geometry const& bounding, float parent_alpha, float alpha, float scale_ratio, unsigned deco_height, bool selected); void DoAddDamage(); ShowdesktopHandlerWindowInterface::PostPaintAction DoHandleAnimations(unsigned int ms); void DoMoveFocusAway(); void DoDeleteHandler(); unsigned int GetNoCoreInstanceMask(); compiz::WindowInputRemoverLock::Ptr GetInputRemover(); void RenderDecoration(compiz_utils::CairoContext const&, double aspect = 1.0f); void RenderTitle(compiz_utils::CairoContext const&, int x, int y, int width, int height, double aspect = 1.0f); void DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const&, GLMatrix const&, unsigned mask, int x, int y, double aspect = 1.0f); void paintFakeDecoration(nux::Geometry const& geo, GLWindowPaintAttrib const& attrib, GLMatrix const& transform, unsigned int mask, bool highlighted, double scale); void paintInnerGlow(nux::Geometry glow_geo, GLMatrix const&, GLWindowPaintAttrib const&, unsigned mask); glow::Quads computeGlowQuads(nux::Geometry const& geo, GLTexture::List const& texture, int glow_size); void paintGlow(GLMatrix const&, GLWindowPaintAttrib const&, glow::Quads const&, GLTexture::List const&, nux::Color const&, unsigned mask); void BuildDecorationTexture(); void CleanupCachedTextures(); public: std::unique_ptr mMinimizeHandler; private: CompWindow* window; CompositeWindow* cWindow; GLWindow* gWindow; std::unique_ptr mShowdesktopHandler; nux::Geometry last_bound; PixmapTexturePtr decoration_tex_; PixmapTexturePtr decoration_selected_tex_; std::string decoration_title_; compiz::WindowInputRemoverLock::Weak input_remover_; decoration::WidgetState close_icon_state_; nux::Geometry close_button_geo_; std::shared_ptr deco_win_; bool middle_clicked_; bool need_fake_deco_redraw_; bool is_nux_window_; glib::Source::UniquePtr focus_desktop_timeout_; friend class UnityScreen; friend UnityMinimizedHandler; }; /** * Your vTable class is some basic info about the plugin that core uses. */ class UnityPluginVTable : public CompPlugin::VTableForScreenAndWindow { public: bool init(); }; } // namespace unity #endif // UNITYSHELL_H ./plugins/unityshell/src/UnityGestureBroker.cpp0000644000004100000410000000656113437202764022253 0ustar www-datawww-data/* * UnityGestureBroker.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "UnityGestureBroker.h" #include "UnityGestureTarget.h" #include "WindowGestureTarget.h" UnityGestureBroker::UnityGestureBroker() : nux::GestureBroker() { unity_target.reset(new UnityGestureTarget); gestural_window_switcher_.reset(new unity::GesturalWindowSwitcher); } std::vector UnityGestureBroker::FindGestureTargets(const nux::GestureEvent &event) { std::vector targets; const std::vector &touches = event.GetTouches(); if (touches.size() == 4) { targets.push_back(unity_target); } else if (touches.size() == 3) { targets.push_back(gestural_window_switcher_); CompWindow *window = FindWindowHitByGesture(event); if (window && event.IsDirectTouch()) { targets.push_back(nux::ShPtGestureTarget(new WindowGestureTarget(window))); } } return targets; } CompWindow *UnityGestureBroker::FindWindowHitByGesture(const nux::GestureEvent &event) { if (event.IsDirectTouch()) { /* If a direct device is being used (e.g., a touchscreen), all touch points must hit the same window */ CompWindow *last_window = nullptr; const std::vector &touches = event.GetTouches(); for (auto touch : touches) { CompWindow *window = FindCompWindowAtPos(touch.x, touch.y); if (last_window) { if (window != last_window) { return nullptr; } } else { last_window = window; } } return last_window; } else { /* If a indirect device is being used (e.g., a trackpad), the individual touch points are not in screen coordinates and therefore it doesn't make sense to hit-test them individually against the window tree. Instead, we use just the focus point, which is the same as the cursor position in this case (which is in screen coordinates). */ return FindCompWindowAtPos(event.GetFocus().x, event.GetFocus().y); } } CompWindow* UnityGestureBroker::FindCompWindowAtPos(int pos_x, int pos_y) { const CompWindowVector& client_list_stacking = screen->clientList(true); for (auto iter = client_list_stacking.rbegin(), end = client_list_stacking.rend(); iter != end; ++iter) { CompWindow* window = *iter; if (window->minimized()) continue; if (window->state() & CompWindowStateHiddenMask) continue; if (pos_x >= window->x() && pos_x <= (window->width() + window->x()) && pos_y >= window->y() && pos_y <= (window->height() + window->y())) return window; } return nullptr; } ./plugins/unityshell/src/perf-logger.vala0000644000004100000410000000614213437202764021004 0ustar www-datawww-data/* * Copyright (C) 2009-2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Gord Allott * */ namespace Perf { public class ProcessInfo { public ProcessInfo (string name) { this.name = name; start = 0; end = 0; } public string name; public double start; public double end; } public static TimelineLogger? timeline_singleton; public static bool is_logging; public class TimelineLogger : Object { private Timer global_timer; private Gee.HashMap process_map; public static unowned Perf.TimelineLogger get_default () { if (Perf.timeline_singleton == null) { Perf.timeline_singleton = new Perf.TimelineLogger (); } return Perf.timeline_singleton; } construct { this.process_map = new Gee.HashMap (); this.global_timer = new Timer (); this.global_timer.start (); } public void start_process (string name) { if (name in this.process_map.keys) { warning ("already started process: %s", name); return; } var info = new ProcessInfo (name); this.process_map[name] = info; info.start = this.global_timer.elapsed (); } public void end_process (string name) { double end_time = this.global_timer.elapsed (); print ("the end time is %f", end_time); if (name in this.process_map.keys) { this.process_map[name].end = end_time; } else { warning ("process %s not started", name); } } public void write_log (string filename) { debug ("Writing performance log file: %s...", filename); var log_file = File.new_for_path (filename); FileOutputStream file_stream; try { if (!log_file.query_exists (null)) { file_stream = log_file.create (FileCreateFlags.NONE, null); } else { file_stream = log_file.replace (null, false, FileCreateFlags.NONE, null); } var output_stream = new DataOutputStream (file_stream); foreach (ProcessInfo info in this.process_map.values) { string name = info.name.replace (",", ";"); string outline = "%s, %f, %f\n".printf(name, info.start, info.end); output_stream.put_string (outline, null); } file_stream.close (null); } catch (Error e) { warning (e.message); } debug ("Done writing performance log file: %s", filename); } } } ./plugins/unityshell/src/comptransientfor.cpp0000644000004100000410000000433413437202764022030 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "comptransientfor.h" namespace compiz { class PrivateCompTransientForReader { public: PrivateCompTransientForReader () {}; CompWindow *mWindow; }; } compiz::CompTransientForReader::CompTransientForReader (CompWindow *w) : X11TransientForReader (screen->dpy (), w->id ()) { priv = new PrivateCompTransientForReader (); priv->mWindow = w; } compiz::CompTransientForReader::~CompTransientForReader () { delete priv; } unsigned int compiz::CompTransientForReader::getAncestor () { return priv->mWindow->transientFor (); } bool compiz::CompTransientForReader::isTransientFor (unsigned int ancestor) { if (!ancestor || !priv->mWindow->id ()) return false; return priv->mWindow->transientFor () == ancestor; } bool compiz::CompTransientForReader::isGroupTransientFor (unsigned int clientLeader) { if (!clientLeader || !priv->mWindow->id ()) return false; if (priv->mWindow->transientFor () == None || priv->mWindow->transientFor () == screen->root ()) { if (priv->mWindow->type () & (CompWindowTypeUtilMask | CompWindowTypeToolbarMask | CompWindowTypeMenuMask | CompWindowTypeDialogMask | CompWindowTypeModalDialogMask)) { if (priv->mWindow->clientLeader () == clientLeader) return true; } } return false; } ./plugins/unityshell/src/WindowMinimizeSpeedController.cpp0000644000004100000410000000772713437202764024442 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #include #include #include #include "WindowMinimizeSpeedController.h" DECLARE_LOGGER(logger, "unity.shell.compiz.minimizer"); namespace { namespace local { const std::string UNITY_SCHEMA = "com.canonical.Unity"; } } WindowMinimizeSpeedController::WindowMinimizeSpeedController() : _settings(g_settings_new(local::UNITY_SCHEMA.c_str())) , _minimize_count(g_settings_get_int(_settings, "minimize-count")) , _minimize_speed_threshold(g_settings_get_int(_settings, "minimize-speed-threshold")) , _minimize_slow_duration(g_settings_get_int(_settings, "minimize-slow-duration")) , _minimize_fast_duration(g_settings_get_int(_settings, "minimize-fast-duration")) , _duration(200) // going to be overridden anyway, but at least it is initialised { _minimize_count_changed.Connect(_settings, "changed::minimize-count", [this] (GSettings*, gchar* name) { _minimize_count = g_settings_get_int(_settings, name); SetDuration(); }); _minimize_speed_threshold_changed.Connect(_settings, "changed::minimize-speed-threshold", [this] (GSettings*, gchar* name) { _minimize_speed_threshold = g_settings_get_int(_settings, name); SetDuration(); }); _minimize_fast_duration_changed.Connect(_settings, "changed::minimize-fast-duration", [this] (GSettings*, gchar* name) { _minimize_fast_duration = g_settings_get_int(_settings, name); SetDuration(); }); _minimize_slow_duration_changed.Connect(_settings, "changed::minimize-slow-duration", [this] (GSettings*, gchar* name) { _minimize_slow_duration = g_settings_get_int(_settings, name); SetDuration(); }); } void WindowMinimizeSpeedController::UpdateCount() { if (_minimize_count < _minimize_speed_threshold) { _minimize_count += 1; g_settings_set_int(_settings, "minimize-count", _minimize_count); } } int WindowMinimizeSpeedController::getDuration() { return _duration; } void WindowMinimizeSpeedController::SetDuration() { /* Perform some sanity checks on the configuration values */ if (_minimize_fast_duration > _minimize_slow_duration) { LOG_WARN(logger) << "Configuration mismatch: minimize-fast-duration (" << _minimize_fast_duration << ") is longer than minimize-slow-duration (" << _minimize_slow_duration << "). Not changing speed."; return; } if (_minimize_count < 0) _minimize_count = 0; if (_minimize_count > _minimize_speed_threshold) _minimize_count = _minimize_speed_threshold; /* Adjust the speed so that it gets linearly closer to maximum speed as we approach the threshold */ int speed_range = _minimize_slow_duration - _minimize_fast_duration; float position = (_minimize_speed_threshold <= 0) ? 1.0 : static_cast(_minimize_count) / _minimize_speed_threshold; int duration = _minimize_slow_duration - std::ceil(position * speed_range); if (duration != _duration) { _duration = duration; DurationChanged.emit(); } } ./plugins/unityshell/src/UnityGestureBroker.h0000644000004100000410000000300613437202764021707 0ustar www-datawww-data/* * UnityGestureBroker.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #ifndef UNITY_GESTURE_BROKER #define UNITY_GESTURE_BROKER #include #include #include "GesturalWindowSwitcher.h" class UnityGestureBroker : public nux::GestureBroker { public: UnityGestureBroker(); virtual ~UnityGestureBroker() = default; private: std::vector virtual FindGestureTargets(const nux::GestureEvent &event); CompWindow *FindWindowHitByGesture(const nux::GestureEvent &event); /*! Returns the top-most CompWindow at the given position, if any. */ CompWindow* FindCompWindowAtPos(int pos_x, int pos_y); nux::ShPtGestureTarget unity_target; unity::ShPtGesturalWindowSwitcher gestural_window_switcher_; }; #endif // UNITY_GESTURE_BROKER ./plugins/unityshell/src/minimizedwindowhandler.cpp0000644000004100000410000002051213437202764023202 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "minimizedwindowhandler.h" #include "inputremover.h" #include namespace compiz { class PrivateMinimizedWindowHandler { public: PrivateMinimizedWindowHandler () {}; Display *mDpy; unsigned int mXid; std::list mTransients; WindowInputRemoverLock::Ptr mRemover; WindowInputRemoverLockAcquireInterface *mLockAcquire; }; } bool compiz::MinimizedWindowHandler::contains (boost::shared_ptr mw) { for (MinimizedWindowHandler::Ptr h : priv->mTransients) { if (h->priv->mXid == mw->priv->mXid) return true; } return false; } void compiz::MinimizedWindowHandler::setVisibility (bool visible, Window shapeWin) { if (!visible && !priv->mRemover) { priv->mRemover = priv->mLockAcquire->InputRemover(); if (!priv->mRemover) return; } else if (visible && priv->mRemover) { priv->mRemover.reset(); } } std::vector compiz::MinimizedWindowHandler::getTransients () { std::vector transients; compiz::X11TransientForReader *reader = new compiz::X11TransientForReader (priv->mDpy, priv->mXid); transients = reader->getTransients(); delete reader; return transients; } void compiz::MinimizedWindowHandler::minimize () { Atom wmState = XInternAtom (priv->mDpy, "WM_STATE", 0); Atom netWmState = XInternAtom (priv->mDpy, "_NET_WM_STATE", 0); Atom netWmStateHidden = XInternAtom (priv->mDpy, "_NET_WM_STATE_HIDDEN", 0); Atom actualType; int actualFormat; unsigned long nItems, nLeft; void *prop; unsigned long data[2]; Window root = DefaultRootWindow (priv->mDpy), parent = priv->mXid, lastParent = priv->mXid; Window *children; unsigned int nchildren; compiz::MinimizedWindowHandler::Ptr holder = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, 0, priv->mLockAcquire)); auto predicate_this = boost::bind (&compiz::MinimizedWindowHandler::contains, this, _1); auto predicate_holder = !boost::bind (&compiz::MinimizedWindowHandler::contains, holder.get(), _1); std::vector transients = getTransients (); for (unsigned int &w : transients) { compiz::MinimizedWindowHandler::Ptr p = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, w, priv->mLockAcquire)); holder->priv->mTransients.push_back (p); } priv->mTransients.remove_if (predicate_holder); holder->priv->mTransients.remove_if (predicate_this); for (MinimizedWindowHandler::Ptr &mw : holder->priv->mTransients) priv->mTransients.push_back (mw); for (MinimizedWindowHandler::Ptr &mw : priv->mTransients) mw->minimize(); do { if (XQueryTree (priv->mDpy, parent, &root, &parent, &children, &nchildren)) { if (root != parent) lastParent = parent; XFree (children); } else root = parent; } while (root != parent); setVisibility (false, lastParent); /* Change the WM_STATE to IconicState */ data[0] = IconicState; data[1] = None; XChangeProperty (priv->mDpy, priv->mXid, wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); if (XGetWindowProperty (priv->mDpy, priv->mXid, netWmState, 0L, 512L, false, XA_ATOM, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **) &prop) == Success) { if (actualType == XA_ATOM && actualFormat == 32 && nItems && !nLeft) { Atom *data = (Atom *) prop; /* Don't append _NET_WM_STATE_HIDDEN */ while (nItems--) if (*data++ == netWmStateHidden) netWmStateHidden = 0; } if (prop) XFree (prop); } /* Add _NET_WM_STATE_HIDDEN */ if (netWmStateHidden) XChangeProperty (priv->mDpy, priv->mXid, netWmState, XA_ATOM, 32, PropModeAppend, (const unsigned char *) &netWmStateHidden, 1); } void compiz::MinimizedWindowHandler::unminimize () { Atom wmState = XInternAtom (priv->mDpy, "WM_STATE", 0); Atom netWmState = XInternAtom (priv->mDpy, "_NET_WM_STATE", 0); Atom netWmStateHidden = XInternAtom (priv->mDpy, "_NET_WM_STATE_HIDDEN", 0); Atom *nextState = NULL; unsigned int nextStateSize = 0; Atom actualType; int actualFormat; unsigned long nItems, nLeft; void *prop; unsigned long data[2]; Window root = DefaultRootWindow (priv->mDpy), parent = priv->mXid, lastParent = priv->mXid; Window *children; unsigned int nchildren; compiz::MinimizedWindowHandler::Ptr holder = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, 0, priv->mLockAcquire)); auto predicate_this = boost::bind (&compiz::MinimizedWindowHandler::contains, this, _1); auto predicate_holder = !boost::bind (&compiz::MinimizedWindowHandler::contains, holder.get(), _1); std::vector transients = getTransients(); for (unsigned int &w : transients) { compiz::MinimizedWindowHandler::Ptr p = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, w, priv->mLockAcquire)); holder->priv->mTransients.push_back (p); } priv->mTransients.remove_if (predicate_holder); holder->priv->mTransients.remove_if (predicate_this); for (MinimizedWindowHandler::Ptr &mw : holder->priv->mTransients) priv->mTransients.push_back (mw); for (MinimizedWindowHandler::Ptr &mw : priv->mTransients) mw->unminimize(); do { if (XQueryTree (priv->mDpy, parent, &root, &parent, &children, &nchildren)) { if (root != parent) lastParent = parent; XFree (children); } else root = parent; } while (root != parent); setVisibility (true, lastParent); data[0] = NormalState; data[1] = None; XChangeProperty (priv->mDpy, priv->mXid, wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); if (XGetWindowProperty (priv->mDpy, priv->mXid, netWmState, 0L, 512L, false, XA_ATOM, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **) &prop) == Success) { if (actualType == XA_ATOM && actualFormat == 32 && nItems && !nLeft) { Atom *data = (Atom *) prop; Atom *pbegin = NULL; int count = 0; nextStateSize = nItems; nextState = reinterpret_cast(malloc(sizeof(Atom) * nextStateSize)); pbegin = nextState = (Atom *) memcpy (nextState, data, sizeof (Atom) * nextStateSize); /* Remove _NET_WM_STATE_HIDDEN */ while (nItems--) { if (*nextState++ == netWmStateHidden) { nextState = (Atom *) memmove (nextState - 1, nextState, nItems); pbegin = nextState - count; nextStateSize--; pbegin = (Atom *) realloc (pbegin, sizeof (Atom) * nextStateSize); } count++; } nextState = pbegin; } XFree (prop); } /* Write new _NET_WM_STATE */ if (nextState) XChangeProperty (priv->mDpy, priv->mXid, netWmState, XA_ATOM, 32, PropModeReplace, (const unsigned char *) nextState, nextStateSize); else XDeleteProperty (priv->mDpy, priv->mXid, netWmState); } compiz::MinimizedWindowHandler::MinimizedWindowHandler (Display *dpy, unsigned int xid, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire) { priv = new PrivateMinimizedWindowHandler; priv->mDpy = dpy; priv->mXid = xid; priv->mLockAcquire = lock_acquire; } compiz::MinimizedWindowHandler::~MinimizedWindowHandler () { delete priv; } ./plugins/unityshell/src/WindowGestureTarget.h0000644000004100000410000000353413437202764022056 0ustar www-datawww-data/* * WindowGestureTarget.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #ifndef WINDOW_GESTURE_TARGET_H #define WINDOW_GESTURE_TARGET_H #include #include #include // compiz stuff class WindowGestureTarget : public nux::GestureTarget { public: WindowGestureTarget(CompWindow *window); virtual ~WindowGestureTarget(); virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event); CompWindow *window() {return window_;} private: virtual bool Equals(const nux::GestureTarget& other) const; void StartWindowMove(const nux::GestureEvent &event); void MoveWindow(const nux::GestureEvent &event); void EndWindowMove(const nux::GestureEvent &event); void MaximizeOrRestoreWindowDueToPinch(const nux::GestureEvent &event); bool WindowCanMove(); void NullifyWindowPointer(); void RemoveDragGrab(); CompWindow *window_; CompScreen::GrabHandle drag_grab_; bool started_window_move_; bool window_restored_by_pinch_; unity::connection::Wrapper window_destruction_conn_; }; #endif // WINDOW_GESTURE_TARGET_H ./plugins/unityshell/src/minimizedwindowhandler.h0000644000004100000410000000327713437202764022660 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_MINIMIZEDWINDOWHANDLER_H #define _COMPIZ_MINIMIZEDWINDOWHANDLER_H #include #include #include "transientfor.h" #include "inputremover.h" #include #include // Will be merged back into compiz namespace compiz { class PrivateMinimizedWindowHandler; class MinimizedWindowHandler { public: MinimizedWindowHandler (Display *dpy, unsigned int xid, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire); virtual ~MinimizedWindowHandler (); virtual void minimize (); virtual void unminimize (); void setVisibility (bool visible, Window shapeWin); typedef boost::shared_ptr Ptr; bool contains (boost::shared_ptr mw); protected: virtual std::vector getTransients (); private: PrivateMinimizedWindowHandler *priv; }; } #endif ./plugins/unityshell/src/compizminimizedwindowhandler.h0000644000004100000410000002465113437202764024101 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_COMPIZMINIMIZEDWINDOWHANDLER_H #define _COMPIZ_COMPIZMINIMIZEDWINDOWHANDLER_H #include #include "minimizedwindowhandler.h" #include "comptransientfor.h" #include // Will be merged back into compiz namespace compiz { class PrivateCompizMinimizedWindowHandler { public: PrivateCompizMinimizedWindowHandler () {}; CompWindow *mWindow; }; template class CompizMinimizedWindowHandler: public MinimizedWindowHandler { public: CompizMinimizedWindowHandler (CompWindow *w, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire); ~CompizMinimizedWindowHandler (); void setVisibility (bool visible); unsigned int getPaintMask (); void minimize (); void unminimize (); void updateFrameRegion (CompRegion &r); void windowNotify (CompWindowNotify n); static void setFunctions (bool keepMinimized); static void handleCompizEvent (const char *, const char *, CompOption::Vector &); static void handleEvent (XEvent *event); static std::list minimizingWindows; typedef CompizMinimizedWindowHandler Type; typedef std::list List; protected: std::vector getTransients (); private: PrivateCompizMinimizedWindowHandler *priv; static bool handleEvents; static List minimizedWindows; }; } /* XXX minimizedWindows should be removed because it is dangerous to keep * a list of windows separate to compiz-core. The list could get out of * sync and cause more crashes like LP: #918329, LP: #864758. */ template typename compiz::CompizMinimizedWindowHandler::List compiz::CompizMinimizedWindowHandler::minimizedWindows; template CompWindowList compiz::CompizMinimizedWindowHandler::minimizingWindows; template bool compiz::CompizMinimizedWindowHandler::handleEvents = true; template compiz::CompizMinimizedWindowHandler::CompizMinimizedWindowHandler(CompWindow *w, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire) : MinimizedWindowHandler (screen->dpy(), w->id(), lock_acquire) { priv = new PrivateCompizMinimizedWindowHandler(); priv->mWindow = w; } template compiz::CompizMinimizedWindowHandler::~CompizMinimizedWindowHandler () { minimizingWindows.remove (priv->mWindow); minimizedWindows.remove (this); } template std::vector compiz::CompizMinimizedWindowHandler::getTransients () { std::vector transients; for (CompWindow *w : screen->windows()) { compiz::CompTransientForReader reader (w); if (reader.isTransientFor (priv->mWindow->id()) || reader.isGroupTransientFor (priv->mWindow->id())) transients.push_back (w->id()); } return transients; } template void compiz::CompizMinimizedWindowHandler::setVisibility (bool visible) { MinimizedWindowHandler::setVisibility (visible, priv->mWindow->id()); CompositeWindow::get (priv->mWindow)->addDamage(); } template void compiz::CompizMinimizedWindowHandler::minimize () { Atom wmState = XInternAtom (screen->dpy(), "WM_STATE", 0); unsigned long data[2]; std::vector transients = getTransients(); handleEvents = true; priv->mWindow->windowNotify (CompWindowNotifyMinimize); priv->mWindow->changeState (priv->mWindow->state() | CompWindowStateHiddenMask); minimizedWindows.push_back (this); for (unsigned int &w : transients) { CompWindow *win = screen->findWindow (w); if (win && win->isMapped()) { Window *w = Window::get (win); if (!w->mMinimizeHandler) w->mMinimizeHandler.reset (new Type (win, w)); w->mMinimizeHandler->minimize(); } } priv->mWindow->windowNotify (CompWindowNotifyHide); setVisibility (false); data[0] = IconicState; data[1] = None; XChangeProperty (screen->dpy(), priv->mWindow->id(), wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); priv->mWindow->changeState (priv->mWindow->state() | CompWindowStateHiddenMask); /* Don't allow other windows to be focused by moveInputFocusToOtherWindow */ for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), false); priv->mWindow->moveInputFocusToOtherWindow(); for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), true); } template void compiz::CompizMinimizedWindowHandler::windowNotify (CompWindowNotify n) { if (n == CompWindowNotifyFocusChange && priv->mWindow->minimized()) { for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), false); priv->mWindow->moveInputFocusToOtherWindow(); for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), true); } } template void compiz::CompizMinimizedWindowHandler::updateFrameRegion (CompRegion &r) { unsigned int oldUpdateFrameRegionIndex; r = CompRegion(); /* Ensure no other plugins can touch this frame region */ oldUpdateFrameRegionIndex = priv->mWindow->updateFrameRegionGetCurrentIndex(); priv->mWindow->updateFrameRegionSetCurrentIndex (MAXSHORT); priv->mWindow->updateFrameRegion (r); priv->mWindow->updateFrameRegionSetCurrentIndex (oldUpdateFrameRegionIndex); } template void compiz::CompizMinimizedWindowHandler::unminimize() { Atom wmState = XInternAtom (screen->dpy(), "WM_STATE", 0); unsigned long data[2]; std::vector transients = getTransients(); minimizedWindows.remove (this); priv->mWindow->focusSetEnabled (Window::get (priv->mWindow), true); priv->mWindow->windowNotify (CompWindowNotifyUnminimize); priv->mWindow->changeState (priv->mWindow->state() & ~CompWindowStateHiddenMask); priv->mWindow->windowNotify (CompWindowNotifyShow); for (unsigned int &w : transients) { CompWindow *win = screen->findWindow (w); if (win && win->isMapped()) { Window *w = Window::get (win); if (w && w->mMinimizeHandler) { w->mMinimizeHandler->unminimize(); w->mMinimizeHandler.reset(); } } } setVisibility (true); priv->mWindow->changeState (priv->mWindow->state() & ~CompWindowStateHiddenMask); data[0] = NormalState; data[1] = None; XChangeProperty (screen->dpy(), priv->mWindow->id(), wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); } template unsigned int compiz::CompizMinimizedWindowHandler::getPaintMask () { bool doMask = true; for (CompWindow *w : minimizingWindows) { if (w->id() == priv->mWindow->id()) doMask = false; break; } if (doMask) return PAINT_WINDOW_NO_CORE_INSTANCE_MASK; return 0; } template void compiz::CompizMinimizedWindowHandler::handleCompizEvent (const char *pluginName, const char *eventName, CompOption::Vector &o) { if (!handleEvents) return; if (strncmp (pluginName, "animation", 9) == 0 && strncmp (eventName, "window_animation", 16) == 0) { if (CompOption::getStringOptionNamed (o, "type", "") == "minimize") { CompWindow *w = screen->findWindow (CompOption::getIntOptionNamed ( o, "window", 0)); if (w) { if (CompOption::getBoolOptionNamed (o, "active", false)) minimizingWindows.push_back (w); else { /* Ugly hack for LP#977189 */ CompositeWindow::get (w)->release(); GLWindow::get (w)->release(); minimizingWindows.remove (w); } } } } if (!CompOption::getBoolOptionNamed (o, "active", false) && minimizingWindows.empty()) { handleEvents = false; } } template void compiz::CompizMinimizedWindowHandler::handleEvent (XEvent *event) { /* Ignore sent events from the InputRemover */ if (screen->XShape() && event->type == screen->shapeEvent() + ShapeNotify && !event->xany.send_event) { CompWindow *w = screen->findWindow (((XShapeEvent *) event)->window); if (w) { Window *pw = Window::get (w); Type *compizMinimizeHandler = pw->mMinimizeHandler.get(); /* Restore and re-save input shape and remove */ if (compizMinimizeHandler) { compizMinimizeHandler->setVisibility (true); compizMinimizeHandler->setVisibility (false); } } } } template void compiz::CompizMinimizedWindowHandler::setFunctions (bool keepMinimized) { for (CompWindow *w : screen->windows()) { bool m = w->minimized(); bool enable = keepMinimized && w->mapNum() > 0; if (m) w->unminimize(); w->minimizeSetEnabled (Window::get (w), enable); w->unminimizeSetEnabled (Window::get (w), enable); w->minimizedSetEnabled (Window::get (w), enable); if (m) Window::get (w)->window->minimize(); } } #endif ./plugins/unityshell/src/unityshell.cpp0000644000004100000410000046252013437202764020640 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unityshell.cpp * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #include #include #include #include #include #include #include #include #include #include "CompizUtils.h" #include "BaseWindowRaiserImp.h" #include "IconRenderer.h" #include "Launcher.h" #include "LauncherIcon.h" #include "LauncherController.h" #include "SwitcherController.h" #include "SwitcherView.h" #include "PanelView.h" #include "PluginAdapter.h" #include "QuicklistManager.h" #include "ThemeSettings.h" #include "Timer.h" #include "XKeyboardUtil.h" #include "unityshell.h" #include "BackgroundEffectHelper.h" #include "UnityGestureBroker.h" #include "launcher/XdndCollectionWindowImp.h" #include "launcher/XdndManagerImp.h" #include "launcher/XdndStartStopNotifierImp.h" #include "CompizShortcutModeller.h" #include "GnomeKeyGrabber.h" #include "RawPixel.h" #include "decorations/DecorationsDataPool.h" #include "decorations/DecorationsManager.h" #include #include #include #include #include #include #include #include #include #include #include #include "a11y/unitya11y.h" #include "UBusMessages.h" #include "UBusWrapper.h" #include "UScreen.h" #include "config.h" #include "unity-shared/UnitySettings.h" /* FIXME: once we get a better method to add the toplevel windows to the accessible root object, this include would not be required */ #include "a11y/unity-util-accessible.h" /* Set up vtable symbols */ COMPIZ_PLUGIN_20090315(unityshell, unity::UnityPluginVTable); static void save_state() { #ifndef USE_GLES glPushAttrib(GL_ALL_ATTRIB_BITS); glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); #endif } static void restore_state() { #ifndef USE_GLES glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); #endif } namespace cgl = compiz::opengl; namespace unity { using namespace launcher; using launcher::AbstractLauncherIcon; using launcher::Launcher; using ui::LayoutWindow; using util::Timer; DECLARE_LOGGER(logger, "unity.shell.compiz"); namespace { UnityScreen* uScreen = nullptr; void reset_glib_logging(); void configure_logging(); void capture_g_log_calls(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data); #ifndef USE_GLES gboolean is_extension_supported(const gchar* extensions, const gchar* extension); gfloat get_opengl_version_f32(const gchar* version_string); #endif inline CompRegion CompRegionFromNuxGeo(nux::Geometry const& geo) { return CompRegion(geo.x, geo.y, geo.width, geo.height); } inline CompRect CompRectFromNuxGeo(nux::Geometry const& geo) { return CompRect(geo.x, geo.y, geo.width, geo.height); } inline nux::Geometry NuxGeometryFromCompRect(CompRect const& rect) { return nux::Geometry(rect.x(), rect.y(), rect.width(), rect.height()); } inline nux::Color NuxColorFromCompizColor(unsigned short* color) { return nux::Color(color[0]/65535.0f, color[1]/65535.0f, color[2]/65535.0f, color[3]/65535.0f); } namespace local { // Tap duration in milliseconds. const int ALT_TAP_DURATION = 250; const unsigned int SCROLL_DOWN_BUTTON = 6; const unsigned int SCROLL_UP_BUTTON = 7; const int MAX_BUFFER_AGE = 11; const int FRAMES_TO_REDRAW_ON_RESUME = 10; const RawPixel SCALE_PADDING = 40_em; const RawPixel SCALE_SPACING = 20_em; const std::string RELAYOUT_TIMEOUT = "relayout-timeout"; const std::string HUD_UNGRAB_WAIT = "hud-ungrab-wait"; const std::string FIRST_RUN_STAMP = "first_run.stamp"; const std::string LOCKED_STAMP = "locked.stamp"; } // namespace local namespace atom { Atom _UNITY_SHELL = 0; Atom _UNITY_SAVED_WINDOW_SHAPE = 0; } } // anon namespace UnityScreen::UnityScreen(CompScreen* screen) : BaseSwitchScreen(screen) , PluginClassHandler (screen) , screen(screen) , cScreen(CompositeScreen::get(screen)) , gScreen(GLScreen::get(screen)) , sScreen(ScaleScreen::get(screen)) , WM(PluginAdapter::Initialize(screen)) , menus_(std::make_shared(std::make_shared(), std::make_shared())) , deco_manager_(std::make_shared(menus_)) , debugger_(this) , session_(std::make_shared()) , needsRelayout(false) , super_keypressed_(false) , newFocusedWindow(nullptr) , doShellRepaint(false) , didShellRepaint(false) , allowWindowPaint(false) , last_output_(nullptr) , force_draw_countdown_(0) , firstWindowAboveShell(nullptr) , onboard_(nullptr) , grab_index_(0) , painting_tray_ (false) , last_scroll_event_(0) , hud_keypress_time_(0) , first_menu_keypress_time_(0) , paint_panel_under_dash_(false) , scale_just_activated_(false) , screen_introspection_(screen) , ignore_redraw_request_(false) , dirty_helpers_on_this_frame_(false) , is_desktop_active_(false) , key_nav_mode_requested_(false) , big_tick_(0) , back_buffer_age_(0) , next_active_window_(0) { Timer timer; #ifndef USE_GLES gfloat version; gchar* extensions; #endif bool failed = false; configure_logging(); LOG_DEBUG(logger) << __PRETTY_FUNCTION__; int (*old_handler)(Display*, XErrorEvent*); old_handler = XSetErrorHandler(NULL); #ifndef USE_GLES /* Ensure OpenGL version is 1.4+. */ version = get_opengl_version_f32((const gchar*) glGetString(GL_VERSION)); if (version < 1.4f) { compLogMessage("unityshell", CompLogLevelError, "OpenGL 1.4+ not supported\n"); setFailed (); failed = true; } /* Ensure OpenGL extensions required by the Unity plugin are available. */ extensions = (gchar*) glGetString(GL_EXTENSIONS); if (!is_extension_supported(extensions, "GL_ARB_vertex_program")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_vertex_program not supported\n"); setFailed (); failed = true; } if (!is_extension_supported(extensions, "GL_ARB_fragment_program")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_fragment_program not supported\n"); setFailed (); failed = true; } if (!is_extension_supported(extensions, "GL_ARB_vertex_buffer_object")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_vertex_buffer_object not supported\n"); setFailed (); failed = true; } if (!is_extension_supported(extensions, "GL_ARB_framebuffer_object")) { if (!is_extension_supported(extensions, "GL_EXT_framebuffer_object")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_framebuffer_object or GL_EXT_framebuffer_object " "not supported\n"); setFailed(); failed = true; } } if (!is_extension_supported(extensions, "GL_ARB_texture_non_power_of_two")) { if (!is_extension_supported(extensions, "GL_ARB_texture_rectangle")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_texture_non_power_of_two or " "GL_ARB_texture_rectangle not supported\n"); setFailed (); failed = true; } } //In case of software rendering then enable lowgfx mode. std::string lowgfx_env = glib::gchar_to_string(getenv("UNITY_LOW_GFX_MODE")); std::string renderer = ANSI_TO_TCHAR(NUX_REINTERPRET_CAST(const char *, glGetString(GL_RENDERER))); if (renderer.find("Software Rasterizer") != std::string::npos || renderer.find("Mesa X11") != std::string::npos || renderer.find("llvmpipe") != std::string::npos || renderer.find("softpipe") != std::string::npos || atoi(lowgfx_env.c_str()) == 1) { if (lowgfx_env.empty() || atoi(lowgfx_env.c_str()) != 0) unity_settings_.supports_3d = false; } Settings::Instance().low_gfx.changed.connect(sigc::track_obj([this] (bool low_gfx) { BackgroundEffectHelper::blur_type = low_gfx ? BLUR_NONE : (unity::BlurType) optionGetDashBlurExperimental(); }, *this)); #endif if (!failed) { notify_init("unityshell"); XSetErrorHandler(old_handler); /* Wrap compiz interfaces */ ScreenInterface::setHandler(screen); CompositeScreenInterface::setHandler(cScreen); GLScreenInterface::setHandler(gScreen); ScaleScreenInterface::setHandler(sScreen); atom::_UNITY_SHELL = XInternAtom(screen->dpy(), "_UNITY_SHELL", False); atom::_UNITY_SAVED_WINDOW_SHAPE = XInternAtom(screen->dpy(), "_UNITY_SAVED_WINDOW_SHAPE", False); screen->updateSupportedWmHints(); nux::NuxInitialize(0); #ifndef USE_GLES wt.reset(nux::CreateFromForeignWindow(cScreen->output(), glXGetCurrentContext(), &UnityScreen::InitNuxThread, this)); #else wt.reset(nux::CreateFromForeignWindow(cScreen->output(), eglGetCurrentContext(), &UnityScreen::InitNuxThread, this)); #endif tick_source_.reset(new na::TickSource); animation_controller_.reset(new na::AnimationController(*tick_source_)); wt->RedrawRequested.connect(sigc::mem_fun(this, &UnityScreen::OnRedrawRequested)); unity_a11y_init(wt.get()); /* i18n init */ bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); wt->Run(NULL); uScreen = this; optionSetShowMenuBarInitiate(boost::bind(&UnityScreen::showMenuBarInitiate, this, _1, _2, _3)); optionSetShowMenuBarTerminate(boost::bind(&UnityScreen::showMenuBarTerminate, this, _1, _2, _3)); optionSetLockScreenInitiate(boost::bind(&UnityScreen::LockScreenInitiate, this, _1, _2, _3)); optionSetOverrideDecorationThemeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShadowXOffsetNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShadowYOffsetNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetActiveShadowRadiusNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetInactiveShadowRadiusNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetActiveShadowColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetInactiveShadowColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShowHudInitiate(boost::bind(&UnityScreen::ShowHudInitiate, this, _1, _2, _3)); optionSetShowHudTerminate(boost::bind(&UnityScreen::ShowHudTerminate, this, _1, _2, _3)); optionSetBackgroundColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherHideModeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetBacklightModeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetRevealTriggerNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLaunchAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetUrgentAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetPanelOpacityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetPanelOpacityMaximizedToggleNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusFadeinNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusFadeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusDiscoveryDurationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusDiscoveryFadeinNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusDiscoveryFadeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherOpacityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetIconSizeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAutohideAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDashBlurExperimentalNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShortcutOverlayNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShowLauncherInitiate(boost::bind(&UnityScreen::showLauncherKeyInitiate, this, _1, _2, _3)); optionSetShowLauncherTerminate(boost::bind(&UnityScreen::showLauncherKeyTerminate, this, _1, _2, _3)); optionSetKeyboardFocusInitiate(boost::bind(&UnityScreen::setKeyboardFocusKeyInitiate, this, _1, _2, _3)); //optionSetKeyboardFocusTerminate (boost::bind (&UnityScreen::setKeyboardFocusKeyTerminate, this, _1, _2, _3)); optionSetExecuteCommandInitiate(boost::bind(&UnityScreen::executeCommand, this, _1, _2, _3)); optionSetShowDesktopKeyInitiate(boost::bind(&UnityScreen::showDesktopKeyInitiate, this, _1, _2, _3)); optionSetPanelFirstMenuInitiate(boost::bind(&UnityScreen::showPanelFirstMenuKeyInitiate, this, _1, _2, _3)); optionSetPanelFirstMenuTerminate(boost::bind(&UnityScreen::showPanelFirstMenuKeyTerminate, this, _1, _2, _3)); optionSetPanelFirstMenuNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetSpreadAppWindowsInitiate(boost::bind(&UnityScreen::spreadAppWindowsInitiate, this, _1, _2, _3)); optionSetSpreadAppWindowsAnywhereInitiate(boost::bind(&UnityScreen::spreadAppWindowsAnywhereInitiate, this, _1, _2, _3)); optionSetAutomaximizeValueNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDashTapDurationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAltTabTimeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAltTabBiasViewportNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetSwitchStrictlyBetweenApplicationsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDisableShowDesktopNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDisableMouseNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAltTabForwardAllInitiate(boost::bind(&UnityScreen::altTabForwardAllInitiate, this, _1, _2, _3)); optionSetAltTabForwardInitiate(boost::bind(&UnityScreen::altTabForwardInitiate, this, _1, _2, _3)); optionSetAltTabForwardTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); optionSetAltTabForwardAllTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); optionSetAltTabPrevAllInitiate(boost::bind(&UnityScreen::altTabPrevAllInitiate, this, _1, _2, _3)); optionSetAltTabPrevInitiate(boost::bind(&UnityScreen::altTabPrevInitiate, this, _1, _2, _3)); optionSetAltTabNextWindowInitiate(boost::bind(&UnityScreen::altTabNextWindowInitiate, this, _1, _2, _3)); optionSetAltTabNextWindowTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); optionSetAltTabPrevWindowInitiate(boost::bind(&UnityScreen::altTabPrevWindowInitiate, this, _1, _2, _3)); optionSetLauncherSwitcherForwardInitiate(boost::bind(&UnityScreen::launcherSwitcherForwardInitiate, this, _1, _2, _3)); optionSetLauncherSwitcherPrevInitiate(boost::bind(&UnityScreen::launcherSwitcherPrevInitiate, this, _1, _2, _3)); optionSetLauncherSwitcherForwardTerminate(boost::bind(&UnityScreen::launcherSwitcherTerminate, this, _1, _2, _3)); optionSetStopVelocityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetRevealPressureNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetEdgeResponsivenessNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetOvercomePressureNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDecayRateNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShowMinimizedWindowsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetEdgePassedDisabledMsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetNumLaunchersNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherCaptureMouseNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetScrollInactiveIconsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherMinimizeWindowNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_START_KEY_NAV, sigc::mem_fun(this, &UnityScreen::OnLauncherStartKeyNav)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_START_KEY_SWITCHER, sigc::mem_fun(this, &UnityScreen::OnLauncherStartKeyNav)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_END_KEY_NAV, sigc::mem_fun(this, &UnityScreen::OnLauncherEndKeyNav)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_END_KEY_SWITCHER, sigc::mem_fun(this, &UnityScreen::OnLauncherEndKeyNav)); auto init_plugins_cb = sigc::mem_fun(this, &UnityScreen::InitPluginActions); sources_.Add(std::make_shared(init_plugins_cb, glib::Source::Priority::DEFAULT)); Settings::Instance().gestures_changed.connect(sigc::mem_fun(this, &UnityScreen::UpdateGesturesSupport)); InitGesturesSupport(); LoadPanelShadowTexture(); theme::Settings::Get()->theme.changed.connect(sigc::hide(sigc::mem_fun(this, &UnityScreen::LoadPanelShadowTexture))); ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, [this](GVariant * data) { unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; int width, height; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); overlay_monitor_ = overlay_monitor; RaiseInputWindows(); }); Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());; XSelectInput(display, GDK_ROOT_WINDOW(), PropertyChangeMask); LOG_INFO(logger) << "UnityScreen constructed: " << timer.ElapsedSeconds() << "s"; UScreen::GetDefault()->resuming.connect([this] { /* Force paint local::FRAMES_TO_REDRAW_ON_RESUME frames on resume */ force_draw_countdown_ += local::FRAMES_TO_REDRAW_ON_RESUME; }); Introspectable::AddChild(deco_manager_.get()); auto const& deco_style = decoration::Style::Get(); auto deco_style_cb = sigc::hide(sigc::mem_fun(this, &UnityScreen::UpdateDecorationStyle)); deco_style->theme.changed.connect(deco_style_cb); deco_style->title_font.changed.connect(deco_style_cb); UpdateDecorationStyle(); minimize_speed_controller_.DurationChanged.connect( sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged) ); WM.initiate_spread.connect(sigc::mem_fun(this, &UnityScreen::OnInitiateSpread)); WM.terminate_spread.connect(sigc::mem_fun(this, &UnityScreen::OnTerminateSpread)); WM.initiate_expo.connect(sigc::mem_fun(this, &UnityScreen::DamagePanelShadow)); WM.terminate_expo.connect(sigc::mem_fun(this, &UnityScreen::DamagePanelShadow)); Introspectable::AddChild(&WM); Introspectable::AddChild(&screen_introspection_); /* Create blur backup texture */ auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); gpu_device->backup_texture0_ = gpu_device->CreateSystemCapableDeviceTexture(screen->width(), screen->height(), 1, nux::BITFMT_R8G8B8A8, NUX_TRACKER_LOCATION); auto const& blur_update_cb = sigc::mem_fun(this, &UnityScreen::DamageBlurUpdateRegion); BackgroundEffectHelper::blur_region_needs_update_.connect(blur_update_cb); /* Track whole damage on the very first frame */ cScreen->damageScreen(); } } UnityScreen::~UnityScreen() { notify_uninit(); unity_a11y_finalize(); QuicklistManager::Destroy(); decoration::DataPool::Reset(); if (!session_->AutomaticLogin()) SaveLockStamp(false); reset_glib_logging(); screen->addSupportedAtomsSetEnabled(this, false); screen->updateSupportedWmHints(); } void UnityScreen::InitAltTabNextWindow() { Display* display = screen->dpy(); KeySym tab_keysym = XStringToKeysym("Tab"); KeySym above_tab_keysym = keyboard::get_key_above_key_symbol(display, tab_keysym); if (above_tab_keysym != NoSymbol) { { std::ostringstream sout; sout << "" << XKeysymToString(above_tab_keysym); screen->removeAction(&optionGetAltTabNextWindow()); CompAction action = CompAction(); action.keyFromString(sout.str()); action.setState (CompAction::StateInitKey | CompAction::StateAutoGrab); mOptions[UnityshellOptions::AltTabNextWindow].value().set (action); screen->addAction (&mOptions[UnityshellOptions::AltTabNextWindow].value ().action ()); optionSetAltTabNextWindowInitiate(boost::bind(&UnityScreen::altTabNextWindowInitiate, this, _1, _2, _3)); optionSetAltTabNextWindowTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); } { std::ostringstream sout; sout << "" << XKeysymToString(above_tab_keysym); screen->removeAction(&optionGetAltTabPrevWindow()); CompAction action = CompAction(); action.keyFromString(sout.str()); action.setState (CompAction::StateInitKey | CompAction::StateAutoGrab); mOptions[UnityshellOptions::AltTabPrevWindow].value().set (action); screen->addAction (&mOptions[UnityshellOptions::AltTabPrevWindow].value ().action ()); optionSetAltTabPrevWindowInitiate(boost::bind(&UnityScreen::altTabPrevWindowInitiate, this, _1, _2, _3)); } } else { LOG_WARN(logger) << "Could not find key above tab!"; } } void UnityScreen::OnInitiateSpread() { scale_just_activated_ = super_keypressed_; spread_widgets_ = std::make_shared(); spread_widgets_->GetFilter()->text.changed.connect([this] (std::string const& filter) { if (filter.empty()) { sScreen->relayoutSlots(CompMatch::emptyMatch); } else { CompMatch windows_match; auto const& filtered_windows = spread_widgets_->GetFilter()->FilteredWindows(); for (auto const& swin : sScreen->getWindows()) { if (!swin->window || filtered_windows.find(swin->window->id()) != filtered_windows.end()) continue; if (UnityWindow* uwin = UnityWindow::get(swin->window)) { uwin->OnTerminateSpread(); fake_decorated_windows_.erase(uwin); } } for (auto xid : filtered_windows) windows_match |= "xid="+std::to_string(xid); auto match = sScreen->getCustomMatch(); match &= windows_match; sScreen->relayoutSlots(match); } }); for (auto const& swin : sScreen->getWindows()) { if (!swin->window) continue; auto* uwin = UnityWindow::get(swin->window); fake_decorated_windows_.insert(uwin); uwin->OnInitiateSpread(); } } void UnityScreen::OnTerminateSpread() { spread_widgets_.reset(); for (auto const& swin : sScreen->getWindows()) { if (!swin->window) continue; if (UnityWindow* uwin = UnityWindow::get(swin->window)) uwin->OnTerminateSpread(); } fake_decorated_windows_.clear(); } void UnityScreen::DamagePanelShadow() { CompRect panelShadow; for (CompOutput const& output : screen->outputDevs()) { FillShadowRectForOutput(panelShadow, output); cScreen->damageRegion(CompRegion(panelShadow)); } } void UnityScreen::OnViewHidden(nux::BaseWindow *bw) { /* Count this as regular damage */ auto const& geo = bw->GetAbsoluteGeometry(); cScreen->damageRegion(CompRegionFromNuxGeo(geo)); } void UnityScreen::EnsureSuperKeybindings() { for (auto action : _shortcut_actions) screen->removeAction(action.get()); _shortcut_actions.clear (); for (auto shortcut : launcher_controller_->GetAllShortcuts()) { CreateSuperNewAction(shortcut, impl::ActionModifiers::NONE); CreateSuperNewAction(shortcut, impl::ActionModifiers::USE_NUMPAD); CreateSuperNewAction(shortcut, impl::ActionModifiers::USE_SHIFT); CreateSuperNewAction(shortcut, impl::ActionModifiers::USE_SHIFT_NUMPAD); } for (auto shortcut : dash_controller_->GetAllShortcuts()) CreateSuperNewAction(shortcut, impl::ActionModifiers::NONE); } void UnityScreen::CreateSuperNewAction(char shortcut, impl::ActionModifiers flag) { CompActionPtr action(new CompAction()); const std::string key(optionGetShowLauncher().keyToString()); CompAction::KeyBinding binding; binding.fromString(impl::CreateActionString(key, shortcut, flag)); action->setKey(binding); screen->addAction(action.get()); _shortcut_actions.push_back(action); } void UnityScreen::nuxPrologue() { #ifndef USE_GLES /* Vertex lighting isn't used in Unity, we disable that state as it could have * been leaked by another plugin. That should theoretically be switched off * right after PushAttrib since ENABLE_BIT is meant to restore the LIGHTING * bit, but we do that here in order to workaround a bug (?) in the NVIDIA * drivers (lp:703140). */ glDisable(GL_LIGHTING); #endif save_state(); glGetError(); } void UnityScreen::nuxEpilogue() { #ifndef USE_GLES glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* In some unknown place inside nux drawing we change the viewport without * setting it back to the default one, so we need to restore it before allowing * compiz to take the scene */ auto* o = last_output_; glViewport(o->x(), screen->height() - o->y2(), o->width(), o->height()); glDepthRange(0, 1); #else glDepthRangef(0, 1); #endif restore_state(); gScreen->resetRasterPos(); glDisable(GL_SCISSOR_TEST); } void UnityScreen::LoadPanelShadowTexture() { CompString name(theme::Settings::Get()->ThemedFilePath("panel_shadow", {PKGDATADIR})); CompString pname; CompSize size; _shadow_texture = GLTexture::readImageToTexture(name, pname, size); } void UnityScreen::setPanelShadowMatrix(GLMatrix const& matrix) { panel_shadow_matrix_ = matrix; } void UnityScreen::FillShadowRectForOutput(CompRect& shadowRect, CompOutput const& output) { if (_shadow_texture.empty()) return; int monitor = WM.MonitorGeometryIn(NuxGeometryFromCompRect(output)); float panel_h = panel_style_.PanelHeight(monitor); float shadowX = output.x(); float shadowY = output.y() + panel_h; float shadowWidth = output.width(); float shadowHeight = _shadow_texture[0]->height() * unity_settings_.em(monitor)->DPIScale(); shadowRect.setGeometry(shadowX, shadowY, shadowWidth, shadowHeight); } void UnityScreen::paintPanelShadow(CompRegion const& clip) { // You have no shadow texture. But how? if (_shadow_texture.empty() || !_shadow_texture[0]) return; if (panel_controller_->opacity() == 0.0f) return; if (sources_.GetSource(local::RELAYOUT_TIMEOUT)) return; if (WM.IsExpoActive()) return; CompOutput* output = last_output_; if (fullscreenRegion.contains(*output)) return; if (launcher_controller_->IsOverlayOpen()) { auto const& uscreen = UScreen::GetDefault(); if (uscreen->GetMonitorAtPosition(output->x(), output->y()) == overlay_monitor_) return; } CompRect shadowRect; FillShadowRectForOutput(shadowRect, *output); CompRegion redraw(clip); redraw &= shadowRect; redraw -= panelShadowPainted; if (redraw.isEmpty()) return; panelShadowPainted |= redraw; for (auto const& r : redraw.rects()) { for (GLTexture* tex : _shadow_texture) { std::vector vertexData; std::vector textureData; std::vector colorData; GLVertexBuffer *streamingBuffer = GLVertexBuffer::streamingBuffer(); bool wasBlend = glIsEnabled(GL_BLEND); if (!wasBlend) glEnable(GL_BLEND); GL::activeTexture(GL_TEXTURE0); tex->enable(GLTexture::Fast); glTexParameteri(tex->target(), GL_TEXTURE_WRAP_S, GL_REPEAT); colorData = { 0xFFFF, 0xFFFF, 0xFFFF, (GLushort)(panel_controller_->opacity() * 0xFFFF) }; // Sub-rectangle of the shadow needing redrawing: float x1 = r.x1(); float y1 = r.y1(); float x2 = r.x2(); float y2 = r.y2(); // Texture coordinates of the above rectangle: float tx1 = (x1 - shadowRect.x()) / shadowRect.width(); float ty1 = (y1 - shadowRect.y()) / shadowRect.height(); float tx2 = (x2 - shadowRect.x()) / shadowRect.width(); float ty2 = (y2 - shadowRect.y()) / shadowRect.height(); vertexData = { x1, y1, 0, x1, y2, 0, x2, y1, 0, x2, y2, 0, }; textureData = { tx1, ty1, tx1, ty2, tx2, ty1, tx2, ty2, }; streamingBuffer->begin(GL_TRIANGLE_STRIP); streamingBuffer->addColors(1, &colorData[0]); streamingBuffer->addVertices(4, &vertexData[0]); streamingBuffer->addTexCoords(0, 4, &textureData[0]); streamingBuffer->end(); streamingBuffer->render(panel_shadow_matrix_); tex->disable(); if (!wasBlend) glDisable(GL_BLEND); } } } void UnityWindow::updateIconPos(int &wx, int &wy, int x, int y, float width, float height) { wx = x + (last_bound.width - width) / 2; wy = y + (last_bound.height - height) / 2; } void UnityScreen::UpdateDecorationStyle() { for (UnityWindow* uwin : fake_decorated_windows_) uwin->CleanupCachedTextures(); if (optionGetOverrideDecorationTheme()) { deco_manager_->active_shadow_color = NuxColorFromCompizColor(optionGetActiveShadowColor()); deco_manager_->inactive_shadow_color = NuxColorFromCompizColor(optionGetInactiveShadowColor()); deco_manager_->active_shadow_radius = optionGetActiveShadowRadius(); deco_manager_->inactive_shadow_radius = optionGetInactiveShadowRadius(); deco_manager_->shadow_offset = nux::Point(optionGetShadowXOffset(), optionGetShadowYOffset()); } else { auto const& style = decoration::Style::Get(); deco_manager_->shadow_offset = style->ShadowOffset(); deco_manager_->active_shadow_color = style->ActiveShadowColor(); deco_manager_->active_shadow_radius = style->ActiveShadowRadius(); deco_manager_->inactive_shadow_color = style->InactiveShadowColor(); deco_manager_->inactive_shadow_radius = style->InactiveShadowRadius(); } } void UnityScreen::DamageBlurUpdateRegion(nux::Geometry const& blur_update) { cScreen->damageRegion(CompRegionFromNuxGeo(blur_update)); } void UnityScreen::paintOutput() { CompOutput *output = last_output_; DrawPanelUnderDash(); /* Bind the currently bound draw framebuffer to the read framebuffer binding. * The reason being that we want to use the results of nux images being * drawn to this framebuffer in glCopyTexSubImage2D operations */ GLint current_draw_binding = 0, old_read_binding = 0; #ifndef USE_GLES glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &old_read_binding); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, ¤t_draw_binding); if (old_read_binding != current_draw_binding) (*GL::bindFramebuffer) (GL_READ_FRAMEBUFFER_BINDING_EXT, current_draw_binding); #else glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_read_binding); current_draw_binding = old_read_binding; #endif BackgroundEffectHelper::monitor_rect_.Set(0, 0, screen->width(), screen->height()); // If we have dirty helpers re-copy the backbuffer into a texture if (dirty_helpers_on_this_frame_) { /* We are using a CompRegion here so that we can combine rectangles * where it might make sense to. Saves calls into OpenGL */ CompRegion blur_region; for (auto const& blur_geometry : BackgroundEffectHelper::GetBlurGeometries()) { auto blur_rect = CompRectFromNuxGeo(blur_geometry); blur_region += (blur_rect & *output); } /* Copy from the read buffer into the backup texture */ auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); GLuint backup_texture_id = gpu_device->backup_texture0_->GetOpenGLID(); GLuint surface_target = gpu_device->backup_texture0_->GetSurfaceLevel(0)->GetSurfaceTarget(); CHECKGL(glEnable(surface_target)); CHECKGL(glBindTexture(surface_target, backup_texture_id)); for (CompRect const& rect : blur_region.rects()) { int x = nux::Clamp(rect.x(), 0, screen->width()); int y = nux::Clamp(screen->height() - rect.y2(), 0, screen->height()); int width = std::min(screen->width() - rect.x(), rect.width()); int height = std::min(screen->height() - y, rect.height()); CHECKGL(glCopyTexSubImage2D(surface_target, 0, x, y, x, y, width, height)); } CHECKGL(glDisable(surface_target)); back_buffer_age_ = 0; } nux::Geometry const& outputGeo = NuxGeometryFromCompRect(*output); wt->GetWindowCompositor().SetReferenceFramebuffer(current_draw_binding, old_read_binding, outputGeo); nuxPrologue(); wt->RenderInterfaceFromForeignCmd(outputGeo); nuxEpilogue(); for (Window tray_xid : panel_controller_->GetTrayXids()) { if (tray_xid && !allowWindowPaint) { CompWindow *tray = screen->findWindow (tray_xid); if (tray) { GLMatrix oTransform; UnityWindow *uTrayWindow = UnityWindow::get (tray); GLWindowPaintAttrib attrib (uTrayWindow->gWindow->lastPaintAttrib()); unsigned int oldGlAddGeometryIndex = uTrayWindow->gWindow->glAddGeometryGetCurrentIndex (); unsigned int oldGlDrawIndex = uTrayWindow->gWindow->glDrawGetCurrentIndex (); attrib.opacity = COMPIZ_COMPOSITE_OPAQUE; attrib.brightness = COMPIZ_COMPOSITE_BRIGHT; attrib.saturation = COMPIZ_COMPOSITE_COLOR; oTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA); painting_tray_ = true; /* force the use of the core functions */ uTrayWindow->gWindow->glDrawSetCurrentIndex (MAXSHORT); uTrayWindow->gWindow->glAddGeometrySetCurrentIndex ( MAXSHORT); uTrayWindow->gWindow->glDraw (oTransform, attrib, CompRegion::infinite (), PAINT_WINDOW_TRANSFORMED_MASK | PAINT_WINDOW_BLEND_MASK | PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK); uTrayWindow->gWindow->glAddGeometrySetCurrentIndex (oldGlAddGeometryIndex); uTrayWindow->gWindow->glDrawSetCurrentIndex (oldGlDrawIndex); painting_tray_ = false; } } } if (switcher_controller_->detail()) { auto const& targets = switcher_controller_->ExternalRenderTargets(); for (LayoutWindow::Ptr const& target : targets) { if (CompWindow* window = screen->findWindow(target->xid)) { UnityWindow *unity_window = UnityWindow::get(window); double parent_alpha = switcher_controller_->Opacity(); unity_window->paintThumbnail(target->result, target->alpha, parent_alpha, target->scale, target->decoration_height, target->selected); } } } doShellRepaint = false; didShellRepaint = true; } void UnityScreen::DrawPanelUnderDash() { if (!paint_panel_under_dash_ || (!dash_controller_->IsVisible() && !hud_controller_->IsVisible())) return; auto const& output_dev = screen->currentOutputDev(); if (last_output_->id() != output_dev.id()) return; auto graphics_engine = nux::GetGraphicsDisplay()->GetGraphicsEngine(); if (!graphics_engine->UsingGLSLCodePath()) return; graphics_engine->ResetModelViewMatrixStack(); graphics_engine->Push2DTranslationModelViewMatrix(0.0f, 0.0f, 0.0f); graphics_engine->ResetProjectionMatrix(); graphics_engine->SetOrthographicProjectionMatrix(output_dev.width(), output_dev.height()); nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_CLAMP); int monitor = WM.MonitorGeometryIn(NuxGeometryFromCompRect(output_dev)); auto const& texture = panel_style_.GetBackground(monitor)->GetDeviceTexture(); graphics_engine->QRP_GLSL_1Tex(0, 0, output_dev.width(), texture->GetHeight(), texture, texxform, nux::color::White); } bool UnityScreen::forcePaintOnTop() { if (!allowWindowPaint || lockscreen_controller_->IsLocked() || (dash_controller_->IsVisible() && !nux::GetGraphicsDisplay()->PointerIsGrabbed()) || hud_controller_->IsVisible() || session_controller_->Visible()) { return true; } if (!fullscreen_windows_.empty()) { if (menus_->menu_open()) return true; if (switcher_controller_->Visible() || WM.IsExpoActive()) { if (!screen->grabbed() || screen->otherGrabExist(nullptr)) return true; } } return false; } void UnityScreen::EnableCancelAction(CancelActionTarget target, bool enabled, int modifiers) { if (enabled) { /* Create a new keybinding for the Escape key and the current modifiers, * compiz will take of the ref-counting of the repeated actions */ KeyCode escape = XKeysymToKeycode(screen->dpy(), XK_Escape); CompAction::KeyBinding binding(escape, modifiers); CompActionPtr &escape_action = _escape_actions[target]; escape_action = CompActionPtr(new CompAction()); escape_action->setKey(binding); screen->addAction(escape_action.get()); } else if (!enabled && _escape_actions[target].get()) { screen->removeAction(_escape_actions[target].get()); _escape_actions.erase(target); } } void UnityScreen::enterShowDesktopMode () { for (CompWindow *w : screen->windows ()) { CompPoint const& viewport = w->defaultViewport(); UnityWindow *uw = UnityWindow::get (w); if (viewport == uScreen->screen->vp() && ShowdesktopHandler::ShouldHide (static_cast (uw))) { UnityWindow::get (w)->enterShowDesktop (); // the animation plugin does strange things here ... // if this notification is sent // w->windowNotify (CompWindowNotifyEnterShowDesktopMode); } if (w->type() & CompWindowTypeDesktopMask) w->moveInputFocusTo(); } if (dash_controller_->IsVisible()) dash_controller_->HideDash(); if (hud_controller_->IsVisible()) hud_controller_->HideHud(); PluginAdapter::Default().OnShowDesktop(); /* Disable the focus handler as we will report that * minimized windows can be focused which will * allow them to enter showdesktop mode. That's * no good */ for (CompWindow *w : screen->windows ()) { UnityWindow *uw = UnityWindow::get (w); w->focusSetEnabled (uw, false); } screen->enterShowDesktopMode (); for (CompWindow *w : screen->windows ()) { UnityWindow *uw = UnityWindow::get (w); w->focusSetEnabled (uw, true); } } void UnityScreen::leaveShowDesktopMode (CompWindow *w) { /* Where a window is inhibiting, only allow the window * that is inhibiting the leave show desktop to actually * fade in again - all other windows should remain faded out */ if (!ShowdesktopHandler::InhibitingXid ()) { for (CompWindow *cw : screen->windows ()) { CompPoint const& viewport = cw->defaultViewport(); if (viewport == uScreen->screen->vp() && cw->inShowDesktopMode ()) { UnityWindow::get (cw)->leaveShowDesktop (); // the animation plugin does strange things here ... // if this notification is sent //cw->windowNotify (CompWindowNotifyLeaveShowDesktopMode); } } PluginAdapter::Default().OnLeaveDesktop(); if (w) { CompPoint const& viewport = w->defaultViewport(); if (viewport == uScreen->screen->vp()) screen->leaveShowDesktopMode (w); } else { screen->focusDefaultWindow(); } } else { CompWindow *cw = screen->findWindow (ShowdesktopHandler::InhibitingXid ()); if (cw) { if (cw->inShowDesktopMode ()) { UnityWindow::get (cw)->leaveShowDesktop (); } } } } bool UnityScreen::DoesPointIntersectUnityGeos(nux::Point const& pt) { auto launchers = launcher_controller_->launchers(); for (auto launcher : launchers) { nux::Geometry hud_geo = launcher->GetAbsoluteGeometry(); if (launcher->Hidden()) continue; if (hud_geo.IsInside(pt)) { return true; } } for (nux::Geometry const& panel_geo : panel_controller_->GetGeometries ()) { if (panel_geo.IsInside(pt)) { return true; } } return false; } LayoutWindow::Ptr UnityScreen::GetSwitcherDetailLayoutWindow(Window window) const { LayoutWindow::Vector const& targets = switcher_controller_->ExternalRenderTargets(); for (LayoutWindow::Ptr const& target : targets) { if (target->xid == window) return target; } return nullptr; } void UnityWindow::enterShowDesktop () { if (!mShowdesktopHandler) mShowdesktopHandler.reset(new ShowdesktopHandler(static_cast (this), static_cast (this))); window->setShowDesktopMode (true); mShowdesktopHandler->FadeOut (); } void UnityWindow::leaveShowDesktop () { if (mShowdesktopHandler) { mShowdesktopHandler->FadeIn (); window->setShowDesktopMode (false); } } void UnityWindow::activate () { uScreen->SetNextActiveWindow(window->id()); ShowdesktopHandler::InhibitLeaveShowdesktopMode (window->id ()); window->activate (); ShowdesktopHandler::AllowLeaveShowdesktopMode (window->id ()); PluginAdapter::Default().OnLeaveDesktop(); } void UnityWindow::DoEnableFocus () { window->focusSetEnabled (this, true); } void UnityWindow::DoDisableFocus () { window->focusSetEnabled (this, false); } bool UnityWindow::IsOverrideRedirect () { return window->overrideRedirect (); } bool UnityWindow::IsManaged () { return window->managed (); } bool UnityWindow::IsGrabbed () { return window->grabbed (); } bool UnityWindow::IsDesktopOrDock () { return (window->type () & (CompWindowTypeDesktopMask | CompWindowTypeDockMask)); } bool UnityWindow::IsSkipTaskbarOrPager () { return (window->state () & (CompWindowStateSkipTaskbarMask | CompWindowStateSkipPagerMask)); } bool UnityWindow::IsInShowdesktopMode () { return window->inShowDesktopMode (); } bool UnityWindow::IsHidden () { return window->state () & CompWindowStateHiddenMask; } bool UnityWindow::IsShaded () { return window->shaded (); } bool UnityWindow::IsMinimized () { return window->minimized (); } bool UnityWindow::CanBypassLockScreen() const { if (window->type() == CompWindowTypePopupMenuMask && uScreen->lockscreen_controller_->HasOpenMenu()) { return true; } if (window == uScreen->onboard_) return true; return false; } void UnityWindow::DoOverrideFrameRegion(CompRegion ®ion) { unsigned int oldUpdateFrameRegionIndex = window->updateFrameRegionGetCurrentIndex(); window->updateFrameRegionSetCurrentIndex(MAXSHORT); window->updateFrameRegion(region); window->updateFrameRegionSetCurrentIndex(oldUpdateFrameRegionIndex); } void UnityWindow::DoHide () { window->changeState (window->state () | CompWindowStateHiddenMask); } void UnityWindow::DoNotifyHidden () { window->windowNotify (CompWindowNotifyHide); } void UnityWindow::DoShow () { window->changeState (window->state () & ~(CompWindowStateHiddenMask)); } void UnityWindow::DoNotifyShown () { window->windowNotify (CompWindowNotifyShow); } void UnityWindow::DoMoveFocusAway () { window->moveInputFocusToOtherWindow (); } ShowdesktopHandlerWindowInterface::PostPaintAction UnityWindow::DoHandleAnimations (unsigned int ms) { ShowdesktopHandlerWindowInterface::PostPaintAction action = ShowdesktopHandlerWindowInterface::PostPaintAction::Wait; if (mShowdesktopHandler) action = mShowdesktopHandler->Animate (ms); return action; } void UnityWindow::DoAddDamage() { cWindow->addDamage(); } void UnityWindow::DoDeleteHandler () { mShowdesktopHandler.reset(); window->updateFrameRegion(); } compiz::WindowInputRemoverLock::Ptr UnityWindow::GetInputRemover () { if (!input_remover_.expired ()) return input_remover_.lock (); compiz::WindowInputRemoverLock::Ptr ret (new compiz::WindowInputRemoverLock ( new compiz::WindowInputRemover (screen->dpy (), window->id (), window->id ()))); input_remover_ = ret; return ret; } unsigned int UnityWindow::GetNoCoreInstanceMask () { return PAINT_WINDOW_NO_CORE_INSTANCE_MASK; } bool UnityWindow::handleEvent(XEvent *event) { bool handled = false; switch(event->type) { case MotionNotify: if (close_icon_state_ != decoration::WidgetState::PRESSED) { auto old_state = close_icon_state_; if (close_button_geo_.IsPointInside(event->xmotion.x_root, event->xmotion.y_root)) { close_icon_state_ = decoration::WidgetState::PRELIGHT; } else { close_icon_state_ = decoration::WidgetState::NORMAL; } if (old_state != close_icon_state_) { cWindow->addDamageRect(CompRectFromNuxGeo(close_button_geo_)); } } break; case ButtonPress: if (event->xbutton.button == Button1 && close_button_geo_.IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) { close_icon_state_ = decoration::WidgetState::PRESSED; cWindow->addDamageRect(CompRectFromNuxGeo(close_button_geo_)); handled = true; } else if (event->xbutton.button == Button2 && (GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root) || GetLayoutWindowGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))) { middle_clicked_ = true; handled = true; } break; case ButtonRelease: { bool was_pressed = (close_icon_state_ == decoration::WidgetState::PRESSED); if (close_icon_state_ != decoration::WidgetState::NORMAL) { close_icon_state_ = decoration::WidgetState::NORMAL; cWindow->addDamageRect(CompRectFromNuxGeo(close_button_geo_)); } if (was_pressed) { if (close_button_geo_.IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) { window->close(0); handled = true; } } if (middle_clicked_) { if (event->xbutton.button == Button2 && (GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root) || GetLayoutWindowGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))) { window->close(0); handled = true; } middle_clicked_ = false; } } break; default: if (!event->xany.send_event && screen->XShape() && event->type == screen->shapeEvent() + ShapeNotify) { if (mShowdesktopHandler) { mShowdesktopHandler->HandleShapeEvent(); handled = true; } } } return handled; } /* called whenever we need to repaint parts of the screen */ bool UnityScreen::glPaintOutput(const GLScreenPaintAttrib& attrib, const GLMatrix& transform, const CompRegion& region, CompOutput* output, unsigned int mask) { if (G_UNLIKELY(lockscreen_controller_->IsPaintInhibited())) { CHECKGL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); CHECKGL(glClear(GL_COLOR_BUFFER_BIT)); return true; } bool ret; /* * Very important! * Don't waste GPU and CPU rendering the shell on every frame if you don't * need to. Doing so on every frame causes Nux to hog the GPU and slow down * ALL rendering. (LP: #988079) */ bool force = forcePaintOnTop(); doShellRepaint = force || ( !region.isEmpty() && ( !wt->GetDrawList().empty() || !wt->GetPresentationListGeometries().empty() || (mask & PAINT_SCREEN_FULL_MASK) ) ); allowWindowPaint = true; last_output_ = output; paint_panel_under_dash_ = false; // CompRegion has no clear() method. So this is the fastest alternative. fullscreenRegion = CompRegion(); nuxRegion = CompRegion(); windows_for_monitor_.clear(); /* glPaintOutput is part of the opengl plugin, so we need the GLScreen base class. */ ret = gScreen->glPaintOutput(attrib, transform, region, output, mask); if (doShellRepaint && !force && fullscreenRegion.contains(*output)) doShellRepaint = false; if (doShellRepaint) paintOutput(); return ret; } /* called whenever a plugin needs to paint the entire scene * transformed */ void UnityScreen::glPaintTransformedOutput(const GLScreenPaintAttrib& attrib, const GLMatrix& transform, const CompRegion& region, CompOutput* output, unsigned int mask) { allowWindowPaint = false; /* PAINT_SCREEN_FULL_MASK means that we are ignoring the damage * region and redrawing the whole screen, so we should make all * nux windows be added to the presentation list that intersect * this output. * * However, damaging nux has a side effect of notifying compiz * through OnRedrawRequested that we need to queue another frame. * In most cases that would be desirable, and in the case where * we did that in damageCutoff, it would not be a problem as compiz * does not queue up new frames for damage that can be processed * on the current frame. However, we're now past damage cutoff, but * a change in circumstances has required that we redraw all the nux * windows on this frame. As such, we need to ensure that damagePending * is not called as a result of queuing windows for redraw, as that * would effectively result in a damage feedback loop in plugins that * require screen transformations (eg, new frame -> plugin redraws full * screen -> we reach this point and request another redraw implicitly) */ if (mask & PAINT_SCREEN_FULL_MASK) { ignore_redraw_request_ = true; compizDamageNux(CompRegionRef(output->region())); ignore_redraw_request_ = false; } gScreen->glPaintTransformedOutput(attrib, transform, region, output, mask); paintPanelShadow(region); } void UnityScreen::updateBlurDamage() { /* If there are enabled helpers, we want to apply damage * based on how old our tracking fbo is since we may need * to redraw some of the blur regions if there has been * damage since we last bound it * * XXX: Unfortunately there's a nasty feedback loop here, and not * a whole lot we can do about it. If part of the damage from any frame * intersects a nux window, we have to mark the entire region that the * nux window covers as damaged, because nux does not have any concept * of geometry clipping. That damage will feed back to us on the next frame. */ if (BackgroundEffectHelper::HasEnabledHelpers()) { cScreen->applyDamageForFrameAge(back_buffer_age_); /* * Prioritise user interaction over active blur updates. So the general * slowness of the active blur doesn't affect the UI interaction performance. * * Also, BackgroundEffectHelper::ProcessDamage() is causing a feedback loop * while the dash is open. Calling it results in the NEXT frame (and the * current one?) to get some damage. This GetDrawList().empty() check avoids * that feedback loop and allows us to idle correctly. * * We are doing damage processing for the blurs here, as this represents * the most up to date compiz damage under the nux windows. */ if (wt->GetDrawList().empty()) { CompRect::vector const& rects(buffered_compiz_damage_this_frame_.rects()); for (CompRect const& r : rects) { BackgroundEffectHelper::ProcessDamage(NuxGeometryFromCompRect(r)); } } } } void UnityScreen::damageCutoff() { if (force_draw_countdown_ > 0) { typedef nux::WindowCompositor::WeakBaseWindowPtr WeakBaseWindowPtr; /* We have to force-redraw the whole scene because * of a bug in the nvidia driver that causes framebuffers * to be trashed on resume for a few swaps */ wt->GetWindowCompositor().ForEachBaseWindow([] (WeakBaseWindowPtr const& w) { w->QueueDraw(); }); --force_draw_countdown_; } /* At this point we want to take all of the compiz damage * for this frame and use it to determine which blur regions * need to be redrawn. We don't want to do this any later because * the nux damage is logically on top of the blurs and doesn't * affect them */ updateBlurDamage(); /* Determine nux region damage last */ cScreen->damageCutoff(); CompRegion damage_buffer, last_damage_buffer; do { last_damage_buffer = damage_buffer; /* First apply any damage accumulated to nux to see * what windows need to be redrawn there */ compizDamageNux(buffered_compiz_damage_this_frame_); /* Apply the redraw regions to compiz so that we can * draw this frame with that region included */ determineNuxDamage(damage_buffer); /* We want to track the nux damage here as we will use it to * determine if we need to present other nux windows too */ cScreen->damageRegion(damage_buffer); /* If we had to put more damage into the damage buffer then * damage compiz with it and keep going */ } while (last_damage_buffer != damage_buffer); /* Clear damage buffer */ buffered_compiz_damage_last_frame_ = buffered_compiz_damage_this_frame_; buffered_compiz_damage_this_frame_ = CompRegion(); /* Tell nux that any damaged windows should be redrawn on the next * frame and not this one */ wt->ForeignFrameCutoff(); /* We need to track this per-frame to figure out whether or not * to bind the contents fbo on each monitor pass */ dirty_helpers_on_this_frame_ = BackgroundEffectHelper::HasDirtyHelpers(); } void UnityScreen::preparePaint(int ms) { cScreen->preparePaint(ms); big_tick_ += ms*1000; tick_source_->tick(big_tick_); for (ShowdesktopHandlerWindowInterface *wi : ShowdesktopHandler::animating_windows) wi->HandleAnimations (ms); didShellRepaint = false; panelShadowPainted = CompRegion(); firstWindowAboveShell = NULL; } void UnityScreen::donePaint() { if (G_UNLIKELY(lockscreen_controller_->IsPaintInhibited())) { lockscreen_controller_->MarkBufferHasCleared(); } /* * It's only safe to clear the draw list if drawing actually occurred * (i.e. the shell was not obscured behind a fullscreen window). * If you clear the draw list and drawing has not occured then you'd be * left with all your views thinking they're queued for drawing still and * would refuse to redraw when you return from fullscreen. * I think this is a Nux bug. ClearDrawList should ideally also mark all * the queued views as draw_cmd_queued_=false. */ /* To prevent any potential overflow problems, we are assuming here * that compiz caps the maximum number of frames tracked at 10, so * don't increment the age any more than local::MAX_BUFFER_AGE */ if (back_buffer_age_ < local::MAX_BUFFER_AGE) ++back_buffer_age_; if (didShellRepaint) wt->ClearDrawList(); /* Tell nux that a new frame is now beginning and any damaged windows should * now be painted on this frame */ wt->ForeignFrameEnded(); if (animation_controller_->HasRunningAnimations()) OnRedrawRequested(); for (auto it = ShowdesktopHandler::animating_windows.begin(); it != ShowdesktopHandler::animating_windows.end();) { auto const& wi = *it; auto action = wi->HandleAnimations(0); if (action == ShowdesktopHandlerWindowInterface::PostPaintAction::Remove) { wi->DeleteHandler(); it = ShowdesktopHandler::animating_windows.erase(it); continue; } else if (action == ShowdesktopHandlerWindowInterface::PostPaintAction::Damage) { wi->AddDamage(); } ++it; } cScreen->donePaint(); } void redraw_view_if_damaged(nux::ObjectPtr const& view, CompRegion const& damage) { if (!view || view->IsRedrawNeeded()) return; auto const& geo = view->GetAbsoluteGeometry(); if (damage.intersects(CompRectFromNuxGeo(geo))) view->RedrawBlur(); } void UnityScreen::compizDamageNux(CompRegion const& damage) { /* Ask nux to present anything in our damage region * * Note: This is using a new nux API, to "present" windows * to the screen, as opposed to drawing them. The difference is * important. The former will just draw the window backing texture * directly to the screen, the latter will re-draw the entire window. * * The former is a lot faster, do not use QueueDraw unless the contents * of the window need to be re-drawn. */ auto const& rects = damage.rects(); for (CompRect const& r : rects) { auto const& geo = NuxGeometryFromCompRect(r); wt->PresentWindowsIntersectingGeometryOnThisFrame(geo); } auto const& launchers = launcher_controller_->launchers(); for (auto const& launcher : launchers) { if (!launcher->Hidden()) { redraw_view_if_damaged(launcher->GetActiveTooltip(), damage); } } if (QuicklistManager* qm = QuicklistManager::Default()) { redraw_view_if_damaged(qm->Current(), damage); } } /* Grab changed nux regions and add damage rects for them */ void UnityScreen::determineNuxDamage(CompRegion& nux_damage) { /* Fetch all the dirty geometry from nux and aggregate it */ auto const& dirty = wt->GetPresentationListGeometries(); auto const& panels_geometries = panel_controller_->GetGeometries(); for (auto const& dirty_geo : dirty) { nux_damage += CompRegionFromNuxGeo(dirty_geo); /* Special case, we need to redraw the panel shadow on panel updates */ for (auto const& panel_geo : panels_geometries) { if (!dirty_geo.IsIntersecting(panel_geo)) continue; for (CompOutput const& o : screen->outputDevs()) { CompRect shadowRect; FillShadowRectForOutput(shadowRect, o); nux_damage += shadowRect; } } } } void UnityScreen::addSupportedAtoms(std::vector& atoms) { screen->addSupportedAtoms(atoms); atoms.push_back(atom::_UNITY_SHELL); atoms.push_back(atom::_UNITY_SAVED_WINDOW_SHAPE); deco_manager_->AddSupportedAtoms(atoms); } /* handle X Events */ void UnityScreen::handleEvent(XEvent* event) { bool skip_other_plugins = false; PluginAdapter& wm = PluginAdapter::Default(); if (deco_manager_->HandleEventBefore(event)) return; switch (event->type) { case FocusIn: case FocusOut: if (event->xfocus.mode == NotifyGrab) wm.OnScreenGrabbed(); else if (event->xfocus.mode == NotifyUngrab) wm.OnScreenUngrabbed(); else if (!screen->grabbed() && event->xfocus.mode == NotifyWhileGrabbed) wm.OnScreenGrabbed(); if (key_nav_mode_requested_) { // Close any overlay that is open. if (launcher_controller_->IsOverlayOpen()) { dash_controller_->HideDash(); hud_controller_->HideHud(); } key_nav_mode_requested_ = false; launcher_controller_->KeyNavGrab(); } break; case MotionNotify: if (wm.IsScaleActive()) { if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } else if (switcher_controller_->detail()) { Window win = switcher_controller_->GetCurrentSelection().window_; CompWindow* w = screen->findWindow(win); if (w) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } break; case ButtonPress: if (shortcut_controller_->Visible()) { shortcut_controller_->Hide(); } if (super_keypressed_) { launcher_controller_->KeyNavTerminate(false); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); } if (wm.IsScaleActive()) { if (spread_widgets_) { auto const& spread_filter = spread_widgets_->GetFilter(); if (spread_filter && spread_filter->Visible()) skip_other_plugins = spread_filter->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root); } if (!skip_other_plugins) { if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } } else if (switcher_controller_->detail()) { Window win = switcher_controller_->GetCurrentSelection().window_; CompWindow* w = screen->findWindow(win); if (w) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } if (dash_controller_->IsVisible()) { int panel_height = panel_style_.PanelHeight(dash_controller_->Monitor()); nux::Point pt(event->xbutton.x_root, event->xbutton.y_root); nux::Geometry const& dash = dash_controller_->GetInputWindowGeometry(); nux::Geometry const& dash_geo = nux::Geometry(dash.x, dash.y, dash.width, dash.height + panel_height); Window dash_xid = dash_controller_->window()->GetInputWindowId(); Window top_xid = wm.GetTopWindowAbove(dash_xid); nux::Geometry const& always_on_top_geo = wm.GetWindowGeometry(top_xid); bool indicator_clicked = panel_controller_->IsMouseInsideIndicator(pt); bool outside_dash = !dash_geo.IsInside(pt) && !DoesPointIntersectUnityGeos(pt); if ((outside_dash || indicator_clicked) && !always_on_top_geo.IsInside(pt)) { if (indicator_clicked) { // We must skip the Dash hide animation, otherwise the indicators // wont recive the mouse click event dash_controller_->QuicklyHideDash(); } else { dash_controller_->HideDash(); } } } else if (hud_controller_->IsVisible()) { nux::Point pt(event->xbutton.x_root, event->xbutton.y_root); nux::Geometry const& hud_geo = hud_controller_->GetInputWindowGeometry(); Window hud_xid = hud_controller_->window()->GetInputWindowId(); Window top_xid = wm.GetTopWindowAbove(hud_xid); nux::Geometry const& on_top_geo = wm.GetWindowGeometry(top_xid); if (!hud_geo.IsInside(pt) && !DoesPointIntersectUnityGeos(pt) && !on_top_geo.IsInside(pt)) { hud_controller_->HideHud(); } } else if (switcher_controller_->Visible()) { nux::Point pt(event->xbutton.x_root, event->xbutton.y_root); nux::Geometry const& switcher_geo = switcher_controller_->GetInputWindowGeometry(); if (!switcher_geo.IsInside(pt)) { switcher_controller_->Hide(false); } } break; case ButtonRelease: if (switcher_controller_->detail()) { Window win = switcher_controller_->GetCurrentSelection().window_; CompWindow* w = screen->findWindow(win); if (w) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } else if (wm.IsScaleActive()) { if (spread_widgets_) { auto const& spread_filter = spread_widgets_->GetFilter(); if (spread_filter && spread_filter->Visible()) skip_other_plugins = spread_filter->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root); } if (!skip_other_plugins) { if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) skip_other_plugins = skip_other_plugins || UnityWindow::get(w)->handleEvent(event); } } break; case KeyPress: { if (shortcut_controller_->Visible()) { shortcut_controller_->Hide(); } if (super_keypressed_) { /* We need an idle to postpone this action, after the current event * has been processed */ sources_.AddIdle([this] { shortcut_controller_->SetEnabled(false); shortcut_controller_->Hide(); LOG_DEBUG(logger) << "Hiding shortcut controller due to keypress event."; EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, false); return false; }); } KeySym key_sym = XkbKeycodeToKeysym(event->xany.display, event->xkey.keycode, 0, 0); if (launcher_controller_->KeyNavIsActive()) { if (key_sym == XK_Up) { launcher_controller_->KeyNavPrevious(); break; } else if (key_sym == XK_Down) { launcher_controller_->KeyNavNext(); break; } } else if (switcher_controller_->Visible()) { auto const& close_key = wm.close_window_key(); if (key_sym == close_key.second && XModifiersToNux(event->xkey.state) == close_key.first) { switcher_controller_->Hide(false); skip_other_plugins = true; break; } } if (super_keypressed_) { if (IsKeypadKey(key_sym)) { key_sym = XkbKeycodeToKeysym(event->xany.display, event->xkey.keycode, 0, 1); key_sym = key_sym - XK_KP_0 + XK_0; } skip_other_plugins = launcher_controller_->HandleLauncherKeyEvent(XModifiersToNux(event->xkey.state), key_sym, event->xkey.time); if (!skip_other_plugins) skip_other_plugins = dash_controller_->CheckShortcutActivation(XKeysymToString(key_sym)); if (skip_other_plugins && launcher_controller_->KeyNavIsActive()) { launcher_controller_->KeyNavTerminate(false); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); } } if (spread_widgets_ && spread_widgets_->GetFilter()->Visible()) { if (key_sym == XK_Escape) { skip_other_plugins = true; spread_widgets_->GetFilter()->text = ""; } } break; } case MapRequest: ShowdesktopHandler::InhibitLeaveShowdesktopMode (event->xmaprequest.window); break; default: if (screen->shapeEvent() + ShapeNotify == event->type) { Window xid = event->xany.window; CompWindow *w = screen->findWindow(xid); if (w) { UnityWindow *uw = UnityWindow::get(w); uw->handleEvent(event); } } break; } compiz::CompizMinimizedWindowHandler::handleEvent(event); // avoid further propagation (key conflict for instance) if (!skip_other_plugins) screen->handleEvent(event); if (deco_manager_->HandleEventAfter(event)) return; if (event->type == MapRequest) ShowdesktopHandler::AllowLeaveShowdesktopMode(event->xmaprequest.window); if (switcher_controller_->Visible() && switcher_controller_->mouse_disabled() && (event->type == MotionNotify || event->type == ButtonPress || event->type == ButtonRelease)) { skip_other_plugins = true; } if (spread_widgets_ && spread_widgets_->GetFilter() && spread_widgets_->GetFilter()->Visible()) skip_other_plugins = false; if (!skip_other_plugins && screen->otherGrabExist("deco", "move", "switcher", "resize", nullptr)) { wt->ProcessForeignEvent(event, nullptr); } } void UnityScreen::damageRegion(const CompRegion ®ion) { buffered_compiz_damage_this_frame_ += region; cScreen->damageRegion(region); } void UnityScreen::handleCompizEvent(const char* plugin, const char* event, CompOption::Vector& option) { PluginAdapter& adapter = PluginAdapter::Default(); adapter.NotifyCompizEvent(plugin, event, option); compiz::CompizMinimizedWindowHandler::handleCompizEvent(plugin, event, option); screen->handleCompizEvent(plugin, event, option); } bool UnityScreen::showMenuBarInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (state & CompAction::StateInitKey) { action->setState(action->state() | CompAction::StateTermKey); menus_->show_menus = true; } return false; } bool UnityScreen::showMenuBarTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (state & CompAction::StateTermKey) { action->setState(action->state() & ~CompAction::StateTermKey); menus_->show_menus = false; } return false; } bool UnityScreen::showLauncherKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // to receive the Terminate event if (state & CompAction::StateInitKey) action->setState(action->state() | CompAction::StateTermKey); super_keypressed_ = true; int when = CompOption::getIntOptionNamed(options, "time"); launcher_controller_->HandleLauncherKeyPress(when); EnsureSuperKeybindings (); if (!shortcut_controller_->Visible() && shortcut_controller_->IsEnabled()) { if (shortcut_controller_->Show()) { LOG_DEBUG(logger) << "Showing shortcut hint."; EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, true, action->key().modifiers()); } } return true; } bool UnityScreen::showLauncherKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // Remember StateCancel and StateCommit will be broadcast to all actions // so we need to verify that we are actually being toggled... if (!(state & CompAction::StateTermKey)) return false; if (state & CompAction::StateCancel) return false; bool was_tap = state & CompAction::StateTermTapped; bool tap_handled = false; LOG_DEBUG(logger) << "Super released: " << (was_tap ? "tapped" : "released"); int when = CompOption::getIntOptionNamed(options, "time"); // hack...if the scale just wasn't activated AND the 'when' time is within time to start the // dash then assume was_tap is also true, since the ScalePlugin doesn't accept that state... PluginAdapter& adapter = PluginAdapter::Default(); if (adapter.IsScaleActive() && !scale_just_activated_ && launcher_controller_->AboutToShowDash(true, when)) { adapter.TerminateScale(); was_tap = true; } else if (scale_just_activated_) { scale_just_activated_ = false; } if (launcher_controller_->AboutToShowDash(was_tap, when)) { if (hud_controller_->IsVisible()) { hud_controller_->HideHud(); } if (QuicklistManager::Default()->Current()) { QuicklistManager::Default()->Current()->Hide(); } if (!dash_controller_->IsVisible()) { if (dash_controller_->ShowDash()) { tap_handled = true; ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::GOTO_DASH_URI, "")); } } else if (dash_controller_->IsCommandLensOpen()) { tap_handled = true; ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::GOTO_DASH_URI, "")); } else { dash_controller_->HideDash(); tap_handled = true; } } super_keypressed_ = false; launcher_controller_->KeyNavTerminate(true); launcher_controller_->HandleLauncherKeyRelease(was_tap, when); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); shortcut_controller_->SetEnabled(optionGetShortcutOverlay()); shortcut_controller_->Hide(); LOG_DEBUG(logger) << "Hiding shortcut controller"; EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, false); action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return (was_tap && tap_handled) || !was_tap; } bool UnityScreen::showPanelFirstMenuKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { /* In order to avoid too many events when keeping the keybinding pressed, * that would make the unity-panel-service to go crazy (see bug #948522) * we need to filter them, just considering an event every 750 ms */ int event_time = CompOption::getIntOptionNamed(options, "time"); if (event_time - first_menu_keypress_time_ < 750) { first_menu_keypress_time_ = event_time; return false; } first_menu_keypress_time_ = event_time; /* Even if we do nothing on key terminate, we must enable it, not to to hide * the menus entries after that a menu has been shown and hidden via the * keyboard and the Alt key is still pressed */ action->setState(action->state() | CompAction::StateTermKey); menus_->open_first.emit(); return true; } bool UnityScreen::showPanelFirstMenuKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return true; } void UnityScreen::SendExecuteCommand() { if (hud_controller_->IsVisible()) { hud_controller_->HideHud(); } PluginAdapter& adapter = PluginAdapter::Default(); if (adapter.IsScaleActive()) { adapter.TerminateScale(); } if (dash_controller_->IsCommandLensOpen()) { ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); } else { ubus_manager_.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL, glib::Source::Priority::HIGH); ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "commands.scope", dash::ScopeHandledType::GOTO_DASH_URI, ""), glib::Source::Priority::LOW); } } bool UnityScreen::executeCommand(CompAction* action, CompAction::State state, CompOption::Vector& options) { SendExecuteCommand(); return true; } bool UnityScreen::showDesktopKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { WM.ShowDesktop(); return true; } void UnityScreen::SpreadAppWindows(bool anywhere) { if (ApplicationPtr const& active_app = ApplicationManager::Default().GetActiveApplication()) { std::vector windows; for (auto& window : active_app->GetWindows()) { if (anywhere || WM.IsWindowOnCurrentDesktop(window->window_id())) windows.push_back(window->window_id()); } WM.ScaleWindowGroup(windows, 0, true); } } bool UnityScreen::spreadAppWindowsInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { SpreadAppWindows(false); return true; } bool UnityScreen::spreadAppWindowsAnywhereInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { SpreadAppWindows(true); return true; } bool UnityScreen::setKeyboardFocusKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (WM.IsScaleActive()) WM.TerminateScale(); else if (WM.IsExpoActive()) WM.TerminateExpo(); key_nav_mode_requested_ = true; return true; } bool UnityScreen::altTabInitiateCommon(CompAction* action, switcher::ShowMode show_mode) { if (!grab_index_) { auto cursor = switcher_controller_->mouse_disabled() ? screen->invisibleCursor() : screen->normalCursor(); grab_index_ = screen->pushGrab(cursor, "unity-switcher"); } if (WM.IsScaleActive()) WM.TerminateScale(); launcher_controller_->ClearTooltips(); /* Create a new keybinding for scroll buttons and current modifiers */ CompAction scroll_up; CompAction scroll_down; scroll_up.setButton(CompAction::ButtonBinding(local::SCROLL_UP_BUTTON, action->key().modifiers())); scroll_down.setButton(CompAction::ButtonBinding(local::SCROLL_DOWN_BUTTON, action->key().modifiers())); screen->addAction(&scroll_up); screen->addAction(&scroll_down); menus_->show_menus = false; SetUpAndShowSwitcher(show_mode); return true; } void UnityScreen::SetUpAndShowSwitcher(switcher::ShowMode show_mode) { RaiseInputWindows(); if (!optionGetAltTabBiasViewport()) { if (show_mode == switcher::ShowMode::CURRENT_VIEWPORT) show_mode = switcher::ShowMode::ALL; else show_mode = switcher::ShowMode::CURRENT_VIEWPORT; } auto results = launcher_controller_->GetAltTabIcons(show_mode == switcher::ShowMode::CURRENT_VIEWPORT, switcher_controller_->show_desktop_disabled()); if (switcher_controller_->CanShowSwitcher(results)) switcher_controller_->Show(show_mode, switcher::SortMode::FOCUS_ORDER, results); } bool UnityScreen::altTabTerminateCommon(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (grab_index_) { // remove grab before calling hide so workspace switcher doesn't fail screen->removeGrab(grab_index_, NULL); grab_index_ = 0; } /* Removing the scroll actions */ CompAction scroll_up; CompAction scroll_down; scroll_up.setButton(CompAction::ButtonBinding(local::SCROLL_UP_BUTTON, action->key().modifiers())); scroll_down.setButton(CompAction::ButtonBinding(local::SCROLL_DOWN_BUTTON, action->key().modifiers())); screen->removeAction(&scroll_up); screen->removeAction(&scroll_down); bool accept_state = (state & CompAction::StateCancel) == 0; switcher_controller_->Hide(accept_state); action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return true; } bool UnityScreen::altTabForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) switcher_controller_->Next(); else altTabInitiateCommon(action, switcher::ShowMode::CURRENT_VIEWPORT); action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::altTabForwardAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (WM.IsWallActive()) return false; else if (switcher_controller_->Visible()) switcher_controller_->Next(); else altTabInitiateCommon(action, switcher::ShowMode::ALL); action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::altTabPrevAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) { switcher_controller_->Prev(); return true; } return false; } bool UnityScreen::altTabPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) { switcher_controller_->Prev(); return true; } return false; } bool UnityScreen::altTabNextWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (!switcher_controller_->Visible()) { altTabInitiateCommon(action, switcher::ShowMode::CURRENT_VIEWPORT); switcher_controller_->Select((switcher_controller_->StartIndex())); // always select the current application switcher_controller_->InitiateDetail(); } else if (switcher_controller_->detail()) { switcher_controller_->NextDetail(); } else { switcher_controller_->SetDetail(true); } action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::altTabPrevWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) { switcher_controller_->PrevDetail(); return true; } return false; } bool UnityScreen::launcherSwitcherForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (!launcher_controller_->KeyNavIsActive()) { int modifiers = action->key().modifiers(); launcher_controller_->KeyNavActivate(); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, true, modifiers); KeyCode down_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Down")); KeyCode up_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Up")); CompAction down_action; down_action.setKey(CompAction::KeyBinding(down_key, modifiers)); screen->addAction(&down_action); CompAction up_action; up_action.setKey(CompAction::KeyBinding(up_key, modifiers)); screen->addAction(&up_action); } else { launcher_controller_->KeyNavNext(); } action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::launcherSwitcherPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { launcher_controller_->KeyNavPrevious(); return true; } bool UnityScreen::launcherSwitcherTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { bool accept_state = (state & CompAction::StateCancel) == 0; launcher_controller_->KeyNavTerminate(accept_state); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); KeyCode down_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Down")); KeyCode up_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Up")); CompAction down_action; down_action.setKey(CompAction::KeyBinding(down_key, action->key().modifiers())); screen->removeAction(&down_action); CompAction up_action; up_action.setKey(CompAction::KeyBinding(up_key, action->key().modifiers())); screen->removeAction(&up_action); action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return true; } void UnityScreen::OnLauncherStartKeyNav(GVariant* data) { // Put the launcher BaseWindow at the top of the BaseWindow stack. The // input focus coming from the XinputWindow will be processed by the // launcher BaseWindow only. Then the Launcher BaseWindow will decide // which View will get the input focus. if (SaveInputThenFocus(launcher_controller_->KeyNavLauncherInputWindowId())) launcher_controller_->PushToFront(); } void UnityScreen::OnLauncherEndKeyNav(GVariant* data) { // Return input-focus to previously focused window (before key-nav-mode was // entered) if (data && g_variant_get_boolean(data)) PluginAdapter::Default().RestoreInputFocus(); } void UnityScreen::OnSwitcherDetailChanged(bool detail) { if (detail) { for (LayoutWindow::Ptr const& target : switcher_controller_->ExternalRenderTargets()) { if (CompWindow* window = screen->findWindow(target->xid)) { auto* uwin = UnityWindow::get(window); uwin->close_icon_state_ = decoration::WidgetState::NORMAL; uwin->middle_clicked_ = false; fake_decorated_windows_.insert(uwin); } } } else { for (UnityWindow* uwin : fake_decorated_windows_) uwin->CleanupCachedTextures(); fake_decorated_windows_.clear(); } } bool UnityScreen::SaveInputThenFocus(const guint xid) { // get CompWindow* newFocusedWindow = screen->findWindow(xid); // check if currently focused window isn't it self if (xid != screen->activeWindow()) PluginAdapter::Default().SaveInputFocus(); // set input-focus on window if (newFocusedWindow) { newFocusedWindow->moveInputFocusTo(); return true; } return false; } bool UnityScreen::ShowHud() { if (switcher_controller_->Visible()) { LOG_ERROR(logger) << "Switcher is visible when showing HUD: this should never happen"; return false; // early exit if the switcher is open } if (hud_controller_->IsVisible()) { hud_controller_->HideHud(); } else { // Handles closing KeyNav (Alt+F1) if the hud is about to show if (launcher_controller_->KeyNavIsActive()) launcher_controller_->KeyNavTerminate(false); if (dash_controller_->IsVisible()) dash_controller_->HideDash(); if (QuicklistManager::Default()->Current()) QuicklistManager::Default()->Current()->Hide(); if (WM.IsScreenGrabbed()) { hud_ungrab_slot_ = WM.screen_ungrabbed.connect([this] { ShowHud(); }); // Let's wait ungrab event for maximum a couple of seconds... sources_.AddTimeoutSeconds(2, [this] { hud_ungrab_slot_->disconnect(); return false; }, local::HUD_UNGRAB_WAIT); return false; } hud_ungrab_slot_->disconnect(); hud_controller_->ShowHud(); } // Consume the event. return true; } bool UnityScreen::ShowHudInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // Look to see if there is a keycode. If there is, then this isn't a // modifier only keybinding. if (options[6].type() != CompOption::TypeUnset) { int key_code = options[6].value().i(); LOG_DEBUG(logger) << "HUD initiate key code: " << key_code; // show it now, no timings or terminate needed. return ShowHud(); } else { LOG_DEBUG(logger) << "HUD initiate key code option not set, modifier only keypress."; } // to receive the Terminate event if (state & CompAction::StateInitKey) action->setState(action->state() | CompAction::StateTermKey); hud_keypress_time_ = CompOption::getIntOptionNamed(options, "time"); // pass key through return false; } bool UnityScreen::ShowHudTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // Remember StateCancel and StateCommit will be broadcast to all actions // so we need to verify that we are actually being toggled... if (!(state & CompAction::StateTermKey)) return false; action->setState(action->state() & ~CompAction::StateTermKey); // If we have a modifier only keypress, check for tap and timing. if (!(state & CompAction::StateTermTapped)) return false; int release_time = CompOption::getIntOptionNamed(options, "time"); int tap_duration = release_time - hud_keypress_time_; if (tap_duration > local::ALT_TAP_DURATION) { LOG_DEBUG(logger) << "Tap too long"; return false; } return ShowHud(); } bool UnityScreen::LockScreenInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { sources_.AddIdle([this] { session_controller_->LockScreen(); return false; }); return true; } unsigned UnityScreen::CompizModifiersToNux(unsigned input) const { unsigned modifiers = 0; if (input & CompAltMask) { input &= ~CompAltMask; input |= Mod1Mask; } if (modifiers & CompSuperMask) { input &= ~CompSuperMask; input |= Mod4Mask; } return XModifiersToNux(input); } unsigned UnityScreen::XModifiersToNux(unsigned input) const { unsigned modifiers = 0; if (input & Mod1Mask) modifiers |= nux::KEY_MODIFIER_ALT; if (input & ShiftMask) modifiers |= nux::KEY_MODIFIER_SHIFT; if (input & ControlMask) modifiers |= nux::KEY_MODIFIER_CTRL; if (input & Mod4Mask) modifiers |= nux::KEY_MODIFIER_SUPER; return modifiers; } void UnityScreen::UpdateCloseWindowKey(CompAction::KeyBinding const& keybind) { KeySym keysym = XkbKeycodeToKeysym(screen->dpy(), keybind.keycode(), 0, 0); unsigned modifiers = CompizModifiersToNux(keybind.modifiers()); WM.close_window_key = std::make_pair(modifiers, keysym); } void UnityScreen::UpdateActivateIndicatorsKey() { CompAction::KeyBinding const& keybind = optionGetPanelFirstMenu().key(); KeySym keysym = XkbKeycodeToKeysym(screen->dpy(), keybind.keycode(), 0, 0); unsigned modifiers = CompizModifiersToNux(keybind.modifiers()); WM.activate_indicators_key = std::make_pair(modifiers, keysym); } bool UnityScreen::InitPluginActions() { PluginAdapter& adapter = PluginAdapter::Default(); if (CompPlugin* p = CompPlugin::find("core")) { for (CompOption& option : p->vTable->getOptions()) { if (option.name() == "close_window_key") { UpdateCloseWindowKey(option.value().action().key()); break; } } } if (CompPlugin* p = CompPlugin::find("expo")) { MultiActionList expoActions; for (CompOption& option : p->vTable->getOptions()) { std::string const& option_name = option.name(); if (!expoActions.HasPrimary() && (option_name == "expo_key" || option_name == "expo_button" || option_name == "expo_edge")) { CompAction* action = &option.value().action(); expoActions.AddNewAction(option_name, action, true); } else if (option_name == "exit_button") { CompAction* action = &option.value().action(); expoActions.AddNewAction(option_name, action, false); } } adapter.SetExpoAction(expoActions); } if (CompPlugin* p = CompPlugin::find("scale")) { MultiActionList scaleActions; for (CompOption& option : p->vTable->getOptions()) { std::string const& option_name = option.name(); if (option_name == "initiate_all_key" || option_name == "initiate_all_edge" || option_name == "initiate_key" || option_name == "initiate_button" || option_name == "initiate_edge" || option_name == "initiate_group_key" || option_name == "initiate_group_button" || option_name == "initiate_group_edge" || option_name == "initiate_output_key" || option_name == "initiate_output_button" || option_name == "initiate_output_edge") { CompAction* action = &option.value().action(); scaleActions.AddNewAction(option_name, action, false); } else if (option_name == "initiate_all_button") { CompAction* action = &option.value().action(); scaleActions.AddNewAction(option_name, action, true); } } adapter.SetScaleAction(scaleActions); } if (CompPlugin* p = CompPlugin::find("unitymtgrabhandles")) { foreach(CompOption & option, p->vTable->getOptions()) { if (option.name() == "show_handles_key") adapter.SetShowHandlesAction(&option.value().action()); else if (option.name() == "hide_handles_key") adapter.SetHideHandlesAction(&option.value().action()); else if (option.name() == "toggle_handles_key") adapter.SetToggleHandlesAction(&option.value().action()); } } if (CompPlugin* p = CompPlugin::find("decor")) { LOG_ERROR(logger) << "Decoration plugin is active, disabling it..."; screen->finiPluginForScreen(p); p->vTable->finiScreen(screen); CompPlugin::getPlugins().remove(p); CompPlugin::unload(p); } return false; } /* Set up expo and scale actions on the launcher */ bool UnityScreen::initPluginForScreen(CompPlugin* p) { if (p->vTable->name() == "expo" || p->vTable->name() == "scale") { InitPluginActions(); } bool result = screen->initPluginForScreen(p); if (p->vTable->name() == "unityshell") InitAltTabNextWindow(); return result; } void UnityScreen::AddProperties(debug::IntrospectionData& introspection) {} std::string UnityScreen::GetName() const { return "Unity"; } void UnityScreen::RaiseInputWindows() { std::vector const& xwns = nux::XInputWindow::NativeHandleList(); for (auto window : xwns) { CompWindow* cwin = screen->findWindow(window); if (cwin) cwin->raise(); } } /* detect occlusions * * core passes down the PAINT_WINDOW_OCCLUSION_DETECTION * mask when it is doing occlusion detection, so use that * order to fill our occlusion buffer which we'll flip * to nux later */ bool UnityWindow::glPaint(const GLWindowPaintAttrib& attrib, const GLMatrix& matrix, const CompRegion& region, unsigned int mask) { /* * The occlusion pass tests windows from TOP to BOTTOM. That's opposite to * the actual painting loop. * * Detect uScreen->fullscreenRegion here. That represents the region which * fully covers the shell on its output. It does not include regular windows * stacked above the shell like DnD icons or Onboard etc. */ if (G_UNLIKELY(is_nux_window_)) { if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) { uScreen->nuxRegion += window->geometry(); uScreen->nuxRegion -= uScreen->fullscreenRegion; } if (window->id() == screen->activeWindow() && !(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)) { if (!mask) uScreen->panelShadowPainted = CompRect(); uScreen->paintPanelShadow(region); } return false; // Ensure nux windows are never painted by compiz } else if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) { static const unsigned int nonOcclusionBits = PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK | PAINT_WINDOW_NO_CORE_INSTANCE_MASK; if (window->isMapped() && window->defaultViewport() == uScreen->screen->vp()) { int monitor = window->outputDevice(); auto it = uScreen->windows_for_monitor_.find(monitor); if (it != end(uScreen->windows_for_monitor_)) ++(it->second); else uScreen->windows_for_monitor_[monitor] = 1; if (!(mask & nonOcclusionBits) && (window->state() & CompWindowStateFullscreenMask && !window->minimized() && !window->inShowDesktopMode()) && uScreen->windows_for_monitor_[monitor] == 1) // And I've been advised to test other things, but they don't work: // && (attrib.opacity == OPAQUE)) <-- Doesn't work; Only set in glDraw // && !window->alpha() <-- Doesn't work; Opaque windows often have alpha { uScreen->fullscreenRegion += window->geometry(); } if (uScreen->nuxRegion.isEmpty()) uScreen->firstWindowAboveShell = window; } } GLWindowPaintAttrib wAttrib = attrib; if (uScreen->lockscreen_controller_->IsLocked() && uScreen->lockscreen_controller_->opacity() == 1.0) { if (!window->minimized() && !CanBypassLockScreen()) { // For some reasons PAINT_WINDOW_NO_CORE_INSTANCE_MASK doesn't work here // (well, it works too much, as it applies to menus too), so we need // to paint the windows at the proper opacity, overriding any other // paint plugin (animation, fade?) that might interfere with us. wAttrib.opacity = 0.0; int old_index = gWindow->glPaintGetCurrentIndex(); gWindow->glPaintSetCurrentIndex(MAXSHORT); deco_win_->Paint(matrix, wAttrib, region, mask); bool ret = gWindow->glPaint(wAttrib, matrix, region, mask); gWindow->glPaintSetCurrentIndex(old_index); return ret; } } if (mMinimizeHandler) { mask |= mMinimizeHandler->getPaintMask (); } else if (mShowdesktopHandler) { mShowdesktopHandler->PaintOpacity (wAttrib.opacity); mask |= mShowdesktopHandler->GetPaintMask (); } std::vector const& tray_xids = uScreen->panel_controller_->GetTrayXids(); if (std::find(tray_xids.begin(), tray_xids.end(), window->id()) != tray_xids.end() && !uScreen->allowWindowPaint) { if (!uScreen->painting_tray_) { uScreen->tray_paint_mask_ = mask; mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK; } } if (uScreen->WM.IsScaleActive() && uScreen->sScreen->getSelectedWindow() == window->id()) { nux::Geometry const& scaled_geo = GetScaledGeometry(); paintInnerGlow(scaled_geo, matrix, attrib, mask); } if (uScreen->session_controller_->Visible()) { // Let's darken the other windows if the session dialog is visible wAttrib.brightness *= 0.75f; } deco_win_->Paint(matrix, wAttrib, region, mask); return gWindow->glPaint(wAttrib, matrix, region, mask); } /* handle window painting in an opengl context * * we want to paint underneath other windows here, * so we need to find if this window is actually * stacked on top of one of the nux input windows * and if so paint nux and stop us from painting * other windows or on top of the whole screen */ bool UnityWindow::glDraw(const GLMatrix& matrix, #ifndef USE_MODERN_COMPIZ_GL GLFragment::Attrib& attrib, #else const GLWindowPaintAttrib& attrib, #endif const CompRegion& region, unsigned int mask) { auto window_state = window->state(); auto window_type = window->type(); bool locked = uScreen->lockscreen_controller_->IsLocked(); if (uScreen->doShellRepaint && !uScreen->paint_panel_under_dash_ && window_type == CompWindowTypeNormalMask) { if ((window_state & MAXIMIZE_STATE) && window->onCurrentDesktop() && !window->overrideRedirect() && window->managed()) { CompPoint const& viewport = window->defaultViewport(); unsigned output = window->outputDevice(); if (viewport == uScreen->screen->vp() && output == uScreen->screen->currentOutputDev().id()) { uScreen->paint_panel_under_dash_ = true; } } } if (uScreen->doShellRepaint && window == uScreen->onboard_) { uScreen->paintOutput(); } else if (uScreen->doShellRepaint && window == uScreen->firstWindowAboveShell && !uScreen->forcePaintOnTop() && !uScreen->fullscreenRegion.contains(window->geometry())) { uScreen->paintOutput(); } else if (locked && CanBypassLockScreen()) { uScreen->paintOutput(); } enum class DrawPanelShadow { NO, BELOW_WINDOW, OVER_WINDOW, }; auto draw_panel_shadow = DrawPanelShadow::NO; if (!(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)) { Window active_window = screen->activeWindow(); if (G_UNLIKELY(window_type == CompWindowTypeDesktopMask)) { uScreen->setPanelShadowMatrix(matrix); if (active_window == 0 || active_window == window->id()) { if (PluginAdapter::Default().IsWindowOnTop(window->id())) { draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; } uScreen->is_desktop_active_ = true; } } else { if (window->id() == active_window) { draw_panel_shadow = DrawPanelShadow::BELOW_WINDOW; uScreen->is_desktop_active_ = false; if (!(window_state & CompWindowStateMaximizedVertMask) && !(window_state & CompWindowStateFullscreenMask) && !(window_type & CompWindowTypeFullscreenMask)) { auto const& output = uScreen->screen->currentOutputDev(); int monitor = uScreen->WM.MonitorGeometryIn(NuxGeometryFromCompRect(output)); if (window->y() - window->border().top < output.y() + uScreen->panel_style_.PanelHeight(monitor)) { draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; } } } else if (uScreen->menus_->integrated_menus()) { draw_panel_shadow = DrawPanelShadow::BELOW_WINDOW; } else { if (uScreen->is_desktop_active_) { if (PluginAdapter::Default().IsWindowOnTop(window->id())) { draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; uScreen->panelShadowPainted = CompRegion(); } } } } } if (locked) draw_panel_shadow = DrawPanelShadow::NO; if (draw_panel_shadow == DrawPanelShadow::BELOW_WINDOW) uScreen->paintPanelShadow(region); deco_win_->Draw(matrix, attrib, region, mask); bool ret = gWindow->glDraw(matrix, attrib, region, mask); if (draw_panel_shadow == DrawPanelShadow::OVER_WINDOW) uScreen->paintPanelShadow(region); return ret; } bool UnityWindow::damageRect(bool initial, CompRect const& rect) { if (uScreen->lockscreen_controller_->IsLocked() && !CanBypassLockScreen()) return true; if (initial) deco_win_->Update(); return cWindow->damageRect(initial, rect); } void UnityScreen::OnMinimizeDurationChanged () { /* Update the compiz plugin setting with the new computed speed so that it * will be used in the following minimizations */ if (CompPlugin* p = CompPlugin::find("animation")) { CompOption::Vector &opts = p->vTable->getOptions(); for (CompOption &o : opts) { if (o.name() == "minimize_durations") { /* minimize_durations is a list value, but minimize applies only to * normal windows, so there's always one value */ CompOption::Value& value = o.value(); CompOption::Value::Vector& list = value.list(); CompOption::Value::Vector::iterator i = list.begin(); if (i != list.end()) i->set(minimize_speed_controller_.getDuration()); value.set(list); screen->setOptionForPlugin(p->vTable->name().c_str(), o.name().c_str(), value); break; } } } else { LOG_WARN(logger) << "Animation plugin not found. Can't set minimize speed."; } } void UnityWindow::minimize () { if (!window->managed ()) return; if (!mMinimizeHandler) { mMinimizeHandler.reset (new UnityMinimizedHandler (window, this)); mMinimizeHandler->minimize (); } } void UnityWindow::unminimize () { if (mMinimizeHandler) { mMinimizeHandler->unminimize (); mMinimizeHandler.reset (); } } bool UnityWindow::focus () { if (!mMinimizeHandler) return window->focus (); if (window->overrideRedirect ()) return false; if (!window->managed ()) return false; if (!window->onCurrentDesktop ()) return false; /* Only withdrawn windows * which are marked hidden * are excluded */ if (!window->shaded () && !window->minimized () && (window->state () & CompWindowStateHiddenMask)) return false; if (window->geometry ().x () + window->geometry ().width () <= 0 || window->geometry ().y () + window->geometry ().height () <= 0 || window->geometry ().x () >= (int) screen->width ()|| window->geometry ().y () >= (int) screen->height ()) return false; return true; } bool UnityWindow::minimized () const { return mMinimizeHandler.get () != nullptr; } /* Called whenever a window is mapped, unmapped, minimized etc */ void UnityWindow::windowNotify(CompWindowNotify n) { PluginAdapter::Default().Notify(window, n); switch (n) { case CompWindowNotifyMap: deco_win_->Update(); deco_win_->UpdateDecorationPosition(); if (window->type() == CompWindowTypeDesktopMask) { if (!focus_desktop_timeout_) { focus_desktop_timeout_.reset(new glib::Timeout(1000, [this] { for (CompWindow *w : screen->clientList()) { if (!(w->type() & NO_FOCUS_MASK) && w->focus ()) { focus_desktop_timeout_ = nullptr; return false; } } window->moveInputFocusTo(); focus_desktop_timeout_ = nullptr; return false; })); } } else if (WindowManager::Default().IsOnscreenKeyboard(window->id())) { uScreen->onboard_ = window; uScreen->RaiseOSK(); } /* Fall through an re-evaluate wraps on map and unmap too */ case CompWindowNotifyUnmap: if (uScreen->optionGetShowMinimizedWindows() && window->mapNum() && !window->pendingUnmaps()) { bool wasMinimized = window->minimized (); if (wasMinimized) window->unminimize (); window->focusSetEnabled (this, true); window->minimizeSetEnabled (this, true); window->unminimizeSetEnabled (this, true); window->minimizedSetEnabled (this, true); if (wasMinimized) window->minimize (); } else { window->focusSetEnabled (this, false); window->minimizeSetEnabled (this, false); window->unminimizeSetEnabled (this, false); window->minimizedSetEnabled (this, false); } deco_win_->Update(); deco_win_->UpdateDecorationPosition(); PluginAdapter::Default().UpdateShowDesktopState(); break; case CompWindowNotifyBeforeDestroy: deco_win_->Undecorate(); being_destroyed.emit(); break; case CompWindowNotifyMinimize: /* Updating the count in dconf will trigger a "changed" signal to which * the method setting the new animation speed is attached */ uScreen->minimize_speed_controller_.UpdateCount(); break; case CompWindowNotifyUnreparent: deco_win_->Undecorate(); break; case CompWindowNotifyReparent: deco_win_->Update(); break; default: break; } window->windowNotify(n); if (mMinimizeHandler) { /* The minimize handler will short circuit the frame * region update func and ensure that the frame * does not have a region */ mMinimizeHandler->windowNotify (n); } else if (mShowdesktopHandler) { if (n == CompWindowNotifyFocusChange) mShowdesktopHandler->WindowFocusChangeNotify (); } // We do this after the notify to ensure input focus has actually been moved. if (n == CompWindowNotifyFocusChange) { if (uScreen->dash_controller_->IsVisible()) { uScreen->dash_controller_->ReFocusKeyInput(); } else if (uScreen->hud_controller_->IsVisible()) { uScreen->hud_controller_->ReFocusKeyInput(); } } } void UnityWindow::stateChangeNotify(unsigned int lastState) { if (window->state() & CompWindowStateFullscreenMask && !(lastState & CompWindowStateFullscreenMask)) { uScreen->fullscreen_windows_.push_back(window); } else if (lastState & CompWindowStateFullscreenMask && !(window->state() & CompWindowStateFullscreenMask)) { uScreen->fullscreen_windows_.remove(window); } deco_win_->UpdateWindowState(lastState); PluginAdapter::Default().NotifyStateChange(window, window->state(), lastState); window->stateChangeNotify(lastState); } void UnityWindow::updateFrameRegion(CompRegion ®ion) { /* The minimize handler will short circuit the frame * region update func and ensure that the frame * does not have a region */ if (mMinimizeHandler) { mMinimizeHandler->updateFrameRegion(region); } else if (mShowdesktopHandler) { mShowdesktopHandler->UpdateFrameRegion(region); } else { window->updateFrameRegion(region); deco_win_->UpdateFrameRegion(region); } } void UnityWindow::getOutputExtents(CompWindowExtents& output) { window->getOutputExtents(output); deco_win_->UpdateOutputExtents(output); } void UnityWindow::moveNotify(int x, int y, bool immediate) { deco_win_->UpdateDecorationPositionDelayed(); PluginAdapter::Default().NotifyMoved(window, x, y); window->moveNotify(x, y, immediate); } void UnityWindow::resizeNotify(int x, int y, int w, int h) { deco_win_->UpdateDecorationPositionDelayed(); CleanupCachedTextures(); PluginAdapter::Default().NotifyResized(window, x, y, w, h); window->resizeNotify(x, y, w, h); } CompPoint UnityWindow::tryNotIntersectUI(CompPoint& pos) { auto const& window_geo = window->borderRect(); nux::Geometry target_monitor; nux::Point result(pos.x(), pos.y()); // seriously why does compiz not track monitors XRandR style??? auto const& monitors = UScreen::GetDefault()->GetMonitors(); for (auto const& monitor : monitors) { if (monitor.IsInside(result)) { target_monitor = monitor; break; } } auto const& launchers = uScreen->launcher_controller_->launchers(); for (auto const& launcher : launchers) { if (launcher->options()->hide_mode == LAUNCHER_HIDE_AUTOHIDE && launcher->Hidden()) continue; auto const& geo = launcher->GetAbsoluteGeometry(); if (geo.IsInside(result)) { if (geo.x + geo.width + 1 + window_geo.width() < target_monitor.x + target_monitor.width) { result.x = geo.x + geo.width + 1; } } } for (nux::Geometry const& geo : uScreen->panel_controller_->GetGeometries()) { if (geo.IsInside(result)) { if (geo.y + geo.height + window_geo.height() < target_monitor.y + target_monitor.height) { result.y = geo.y + geo.height; } } } pos.setX(result.x); pos.setY(result.y); return pos; } bool UnityWindow::place(CompPoint& pos) { bool was_maximized = PluginAdapter::Default().MaximizeIfBigEnough(window); if (!was_maximized) { deco_win_->Update(); bool result = window->place(pos); if (window->type() & NO_FOCUS_MASK) return result; pos = tryNotIntersectUI(pos); return result; } return true; } /* Start up nux after OpenGL is initialized */ void UnityScreen::InitNuxThread(nux::NThread* thread, void* data) { Timer timer; static_cast(data)->InitUnityComponents(); nux::ColorLayer background(nux::color::Transparent); static_cast(thread)->SetWindowBackgroundPaintLayer(&background); LOG_INFO(logger) << "UnityScreen::InitNuxThread: " << timer.ElapsedSeconds() << "s"; } void UnityScreen::OnRedrawRequested() { if (!ignore_redraw_request_) cScreen->damagePending(); } /* Handle option changes and plug that into nux windows */ void UnityScreen::optionChanged(CompOption* opt, UnityshellOptions::Options num) { // Note: perhaps we should put the options here into the controller. unity::launcher::Options::Ptr launcher_options = launcher_controller_->options(); switch (num) { case UnityshellOptions::NumLaunchers: launcher_controller_->multiple_launchers = optionGetNumLaunchers() == 0; dash_controller_->use_primary = !launcher_controller_->multiple_launchers(); hud_controller_->multiple_launchers = launcher_controller_->multiple_launchers(); break; case UnityshellOptions::LauncherCaptureMouse: launcher_options->edge_resist = optionGetLauncherCaptureMouse(); break; case UnityshellOptions::ScrollInactiveIcons: launcher_options->scroll_inactive_icons = optionGetScrollInactiveIcons(); break; case UnityshellOptions::LauncherMinimizeWindow: launcher_options->minimize_window_on_click = optionGetLauncherMinimizeWindow(); break; case UnityshellOptions::BackgroundColor: { auto override_color = NuxColorFromCompizColor(optionGetBackgroundColor()); override_color.red = override_color.red / override_color.alpha; override_color.green = override_color.green / override_color.alpha; override_color.blue = override_color.blue / override_color.alpha; bghash_->OverrideColor(override_color); break; } case UnityshellOptions::OverrideDecorationTheme: case UnityshellOptions::ActiveShadowColor: case UnityshellOptions::ActiveShadowRadius: case UnityshellOptions::InactiveShadowColor: case UnityshellOptions::InactiveShadowRadius: case UnityshellOptions::ShadowXOffset: case UnityshellOptions::ShadowYOffset: { UpdateDecorationStyle(); break; } case UnityshellOptions::LauncherHideMode: { launcher_options->hide_mode = (launcher::LauncherHideMode) optionGetLauncherHideMode(); hud_controller_->launcher_locked_out = (launcher_options->hide_mode == LAUNCHER_HIDE_NEVER); int scale_offset = (launcher_options->hide_mode == LAUNCHER_HIDE_NEVER) ? 0 : launcher_controller_->launcher().GetWidth(); CompOption::Value v(scale_offset); CompOption::Value bv(0); if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { screen->setOptionForPlugin("scale", "x_offset", v); screen->setOptionForPlugin("scale", "y_bottom_offset", bv); } else { screen->setOptionForPlugin("scale", "x_offset", bv); screen->setOptionForPlugin("scale", "y_bottom_offset", v); } break; } case UnityshellOptions::BacklightMode: launcher_options->backlight_mode = (unity::launcher::BacklightMode) optionGetBacklightMode(); break; case UnityshellOptions::RevealTrigger: launcher_options->reveal_trigger = (unity::launcher::RevealTrigger) optionGetRevealTrigger(); break; case UnityshellOptions::LaunchAnimation: launcher_options->launch_animation = (unity::launcher::LaunchAnimation) optionGetLaunchAnimation(); break; case UnityshellOptions::UrgentAnimation: launcher_options->urgent_animation = (unity::launcher::UrgentAnimation) optionGetUrgentAnimation(); break; case UnityshellOptions::PanelOpacity: panel_controller_->SetOpacity(optionGetPanelOpacity()); break; case UnityshellOptions::PanelOpacityMaximizedToggle: panel_controller_->SetOpacityMaximizedToggle(optionGetPanelOpacityMaximizedToggle()); break; case UnityshellOptions::MenusFadein: menus_->fadein = optionGetMenusFadein(); break; case UnityshellOptions::MenusFadeout: menus_->fadeout = optionGetMenusFadeout(); break; case UnityshellOptions::MenusDiscoveryFadein: menus_->discovery_fadein = optionGetMenusDiscoveryFadein(); break; case UnityshellOptions::MenusDiscoveryFadeout: menus_->discovery_fadeout = optionGetMenusDiscoveryFadeout(); break; case UnityshellOptions::MenusDiscoveryDuration: menus_->discovery = optionGetMenusDiscoveryDuration(); break; case UnityshellOptions::LauncherOpacity: launcher_options->background_alpha = optionGetLauncherOpacity(); break; case UnityshellOptions::IconSize: { launcher_options->icon_size = optionGetIconSize(); launcher_options->tile_size = optionGetIconSize() + 6; hud_controller_->icon_size = launcher_options->icon_size(); hud_controller_->tile_size = launcher_options->tile_size(); break; } case UnityshellOptions::AutohideAnimation: launcher_options->auto_hide_animation = (unity::launcher::AutoHideAnimation) optionGetAutohideAnimation(); break; case UnityshellOptions::DashBlurExperimental: BackgroundEffectHelper::blur_type = (unity::BlurType)optionGetDashBlurExperimental(); break; case UnityshellOptions::AutomaximizeValue: PluginAdapter::Default().SetCoverageAreaBeforeAutomaximize(optionGetAutomaximizeValue() / 100.0f); break; case UnityshellOptions::DashTapDuration: launcher_options->super_tap_duration = optionGetDashTapDuration(); break; case UnityshellOptions::AltTabTimeout: switcher_controller_->detail_on_timeout = optionGetAltTabTimeout(); case UnityshellOptions::AltTabBiasViewport: PluginAdapter::Default().bias_active_to_viewport = optionGetAltTabBiasViewport(); break; case UnityshellOptions::SwitchStrictlyBetweenApplications: switcher_controller_->first_selection_mode = optionGetSwitchStrictlyBetweenApplications() ? switcher::FirstSelectionMode::LAST_ACTIVE_APP : switcher::FirstSelectionMode::LAST_ACTIVE_VIEW; break; case UnityshellOptions::DisableShowDesktop: switcher_controller_->show_desktop_disabled = optionGetDisableShowDesktop(); break; case UnityshellOptions::DisableMouse: switcher_controller_->mouse_disabled = optionGetDisableMouse(); break; case UnityshellOptions::ShowMinimizedWindows: compiz::CompizMinimizedWindowHandler::setFunctions (optionGetShowMinimizedWindows ()); screen->enterShowDesktopModeSetEnabled (this, optionGetShowMinimizedWindows ()); screen->leaveShowDesktopModeSetEnabled (this, optionGetShowMinimizedWindows ()); break; case UnityshellOptions::ShortcutOverlay: shortcut_controller_->SetEnabled(optionGetShortcutOverlay()); break; case UnityshellOptions::DecayRate: launcher_options->edge_decay_rate = optionGetDecayRate() * 100; break; case UnityshellOptions::OvercomePressure: launcher_options->edge_overcome_pressure = optionGetOvercomePressure() * 100; break; case UnityshellOptions::StopVelocity: launcher_options->edge_stop_velocity = optionGetStopVelocity() * 100; break; case UnityshellOptions::RevealPressure: launcher_options->edge_reveal_pressure = optionGetRevealPressure() * 100; break; case UnityshellOptions::EdgeResponsiveness: launcher_options->edge_responsiveness = optionGetEdgeResponsiveness(); break; case UnityshellOptions::EdgePassedDisabledMs: launcher_options->edge_passed_disabled_ms = optionGetEdgePassedDisabledMs(); break; case UnityshellOptions::PanelFirstMenu: UpdateActivateIndicatorsKey(); break; default: break; } } void UnityScreen::NeedsRelayout() { needsRelayout = true; } void UnityScreen::ScheduleRelayout(guint timeout) { if (!sources_.GetSource(local::RELAYOUT_TIMEOUT)) { sources_.AddTimeout(timeout, [this] { NeedsRelayout(); Relayout(); cScreen->damageScreen(); return false; }, local::RELAYOUT_TIMEOUT); } } void UnityScreen::Relayout() { if (!needsRelayout) return; UScreen *uscreen = UScreen::GetDefault(); int primary_monitor = uscreen->GetPrimaryMonitor(); auto const& geo = uscreen->GetMonitorGeometry(primary_monitor); wt->SetWindowSize(geo.width, geo.height); LOG_DEBUG(logger) << "Setting to primary screen rect; " << geo; needsRelayout = false; DamagePanelShadow(); } /* Handle changes in the number of workspaces by showing the switcher * or not showing the switcher */ bool UnityScreen::setOptionForPlugin(const char* plugin, const char* name, CompOption::Value& v) { bool status = screen->setOptionForPlugin(plugin, name, v); if (status) { if (strcmp(plugin, "core") == 0) { if (strcmp(name, "hsize") == 0 || strcmp(name, "vsize") == 0) { WM.viewport_layout_changed.emit(screen->vpSize().width(), screen->vpSize().height()); } else if (strcmp(name, "close_window_key") == 0) { UpdateCloseWindowKey(v.action().key()); } } } return status; } void UnityScreen::outputChangeNotify() { screen->outputChangeNotify (); auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); gpu_device->backup_texture0_ = gpu_device->CreateSystemCapableDeviceTexture(screen->width(), screen->height(), 1, nux::BITFMT_R8G8B8A8, NUX_TRACKER_LOCATION); ScheduleRelayout(500); } void UnityScreen::averageColorChangeNotify(const unsigned short *color) { bghash_->UpdateColor(color, nux::animation::Animation::State::Running); screen->averageColorChangeNotify (color); } bool UnityScreen::layoutSlotsAndAssignWindows() { auto const& scaled_windows = sScreen->getWindows(); for (auto const& output : screen->outputDevs()) { ui::LayoutWindow::Vector layout_windows; int monitor = UScreen::GetDefault()->GetMonitorAtPosition(output.centerX(), output.centerY()); double monitor_scale = unity_settings_.em(monitor)->DPIScale(); for (ScaleWindow *sw : scaled_windows) { if (sw->window && sw->window->outputDevice() == static_cast(output.id())) { UnityWindow::get(sw->window)->deco_win_->scaled = true; layout_windows.emplace_back(std::make_shared(sw->window->id())); } } auto max_bounds = NuxGeometryFromCompRect(output.workArea()); if (launcher_controller_->options()->hide_mode != LAUNCHER_HIDE_NEVER) { if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { int monitor_width = unity_settings_.LauncherSize(monitor); max_bounds.x += monitor_width; max_bounds.width -= monitor_width; } else if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) { int launcher_size = unity_settings_.LauncherSize(monitor); max_bounds.height -= launcher_size; } } nux::Geometry final_bounds; ui::LayoutSystem layout; layout.max_row_height = max_bounds.height; layout.spacing = local::SCALE_SPACING.CP(monitor_scale); int padding = local::SCALE_PADDING.CP(monitor_scale); max_bounds.Expand(-padding, -padding); layout.LayoutWindowsNearest(layout_windows, max_bounds, final_bounds); for (auto const& lw : layout_windows) { auto sw_it = std::find_if(scaled_windows.begin(), scaled_windows.end(), [&lw] (ScaleWindow* sw) { return sw->window->id() == lw->xid; }); if (sw_it == scaled_windows.end()) continue; ScaleWindow* sw = *sw_it; ScaleSlot slot(CompRectFromNuxGeo(lw->result)); slot.scale = lw->scale; float sx = lw->geo.width * slot.scale; float sy = lw->geo.height * slot.scale; float cx = (slot.x1() + slot.x2()) / 2; float cy = (slot.y1() + slot.y2()) / 2; CompWindow *w = sw->window; cx += w->input().left * slot.scale; cy += w->input().top * slot.scale; slot.setGeometry(cx - sx / 2, cy - sy / 2, sx, sy); slot.filled = true; sw->setSlot(slot); } } return true; } void UnityScreen::OnDashRealized() { RaiseOSK(); } void UnityScreen::OnLockScreenRequested() { if (switcher_controller_->Visible()) switcher_controller_->Hide(false); if (dash_controller_->IsVisible()) dash_controller_->HideDash(); if (hud_controller_->IsVisible()) hud_controller_->HideHud(); if (session_controller_->Visible()) session_controller_->Hide(); menus_->Indicators()->CloseActiveEntry(); launcher_controller_->ClearTooltips(); if (launcher_controller_->KeyNavIsActive()) launcher_controller_->KeyNavTerminate(false); if (QuicklistManager::Default()->Current()) QuicklistManager::Default()->Current()->Hide(); if (WM.IsScaleActive()) WM.TerminateScale(); if (WM.IsExpoActive()) WM.TerminateExpo(); RaiseOSK(); } void UnityScreen::OnScreenLocked() { SaveLockStamp(true); for (auto& option : getOptions()) { if (option.isAction()) { auto& value = option.value(); if (value != mOptions[UnityshellOptions::PanelFirstMenu].value()) screen->removeAction(&value.action()); } } for (auto& action : getActions()) screen->removeAction(&action); // We notify that super/alt have been released, to avoid to leave unity in inconsistent state CompOption::Vector options(1); options.back().setName("time", CompOption::TypeInt); options.back().value().set(screen->getCurrentTime()); showLauncherKeyTerminate(&optionGetShowLauncher(), CompAction::StateTermKey, options); showMenuBarTerminate(&optionGetShowMenuBar(), CompAction::StateTermKey, options); // We disable the edge barriers, to avoid blocking the mouse pointer during lockscreen edge_barriers_->force_disable = true; UpdateGesturesSupport(); } void UnityScreen::OnScreenUnlocked() { SaveLockStamp(false); for (auto& option : getOptions()) { if (option.isAction()) screen->addAction(&option.value().action()); } for (auto& action : getActions()) screen->addAction(&action); edge_barriers_->force_disable = false; UpdateGesturesSupport(); } std::string UnityScreen::GetLockStampFile() const { std::string cache_dir; if (session_->AutomaticLogin()) cache_dir = DesktopUtilities::GetUserCacheDirectory(); else cache_dir = DesktopUtilities::GetUserRuntimeDirectory(); if (cache_dir.empty()) return std::string(); return cache_dir+local::LOCKED_STAMP; } void UnityScreen::SaveLockStamp(bool save) { std::string file_path = GetLockStampFile(); if (file_path.empty()) return; if (save) { glib::Error error; g_file_set_contents(file_path.c_str(), "", 0, &error); if (error) { LOG_ERROR(logger) << "Impossible to save the unity locked stamp file: " << error; } } else { if (g_unlink(file_path.c_str()) < 0) { LOG_ERROR(logger) << "Impossible to delete the unity locked stamp file"; } } } void UnityScreen::RaiseOSK() { /* stack the onboard window above us */ if (onboard_) { if (nux::BaseWindow* dash = dash_controller_->window()) { Window xid = dash->GetInputWindowId(); XSetTransientForHint(screen->dpy(), onboard_->id(), xid); onboard_->raise(); } } } /* Start up the unity components */ void UnityScreen::InitUnityComponents() { Timer timer; nux::GetWindowCompositor().sigHiddenViewWindow.connect(sigc::mem_fun(this, &UnityScreen::OnViewHidden)); bghash_.reset(new BGHash()); bghash_->UpdateColor(screen->averageColor(), nux::animation::Animation::State::Stopped); LOG_INFO(logger) << "InitUnityComponents-BGHash " << timer.ElapsedSeconds() << "s"; auto xdnd_collection_window = std::make_shared(); auto xdnd_start_stop_notifier = std::make_shared(); auto xdnd_manager = std::make_shared(xdnd_start_stop_notifier, xdnd_collection_window); edge_barriers_ = std::make_shared(); launcher_controller_ = std::make_shared(xdnd_manager, edge_barriers_); Introspectable::AddChild(launcher_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-Launcher " << timer.ElapsedSeconds() << "s"; switcher_controller_ = std::make_shared(); switcher_controller_->detail.changed.connect(sigc::mem_fun(this, &UnityScreen::OnSwitcherDetailChanged)); Introspectable::AddChild(switcher_controller_.get()); launcher_controller_->icon_added.connect(sigc::mem_fun(switcher_controller_.get(), &switcher::Controller::AddIcon)); launcher_controller_->icon_removed.connect(sigc::mem_fun(switcher_controller_.get(), &switcher::Controller::RemoveIcon)); LOG_INFO(logger) << "InitUnityComponents-Switcher " << timer.ElapsedSeconds() << "s"; /* Setup panel */ timer.Reset(); panel_controller_ = std::make_shared(menus_, edge_barriers_); Introspectable::AddChild(panel_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-Panel " << timer.ElapsedSeconds() << "s"; /* Setup Places */ dash_controller_ = std::make_shared(); dash_controller_->on_realize.connect(sigc::mem_fun(this, &UnityScreen::OnDashRealized)); Introspectable::AddChild(dash_controller_.get()); /* Setup Hud */ hud_controller_ = std::make_shared(); auto hide_mode = (unity::launcher::LauncherHideMode) optionGetLauncherHideMode(); hud_controller_->launcher_locked_out = (hide_mode == unity::launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER); hud_controller_->multiple_launchers = (optionGetNumLaunchers() == 0); hud_controller_->icon_size = launcher_controller_->options()->icon_size(); hud_controller_->tile_size = launcher_controller_->options()->tile_size(); Introspectable::AddChild(hud_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-Hud " << timer.ElapsedSeconds() << "s"; // Setup Shortcut Hint auto base_window_raiser = std::make_shared(); auto shortcuts_modeller = std::make_shared(); shortcut_controller_ = std::make_shared(base_window_raiser, shortcuts_modeller); Introspectable::AddChild(shortcut_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-ShortcutHints " << timer.ElapsedSeconds() << "s"; ShowFirstRunHints(); // Setup Session Controller session_->lock_requested.connect(sigc::mem_fun(this, &UnityScreen::OnLockScreenRequested)); session_->prompt_lock_requested.connect(sigc::mem_fun(this, &UnityScreen::OnLockScreenRequested)); session_->locked.connect(sigc::mem_fun(this, &UnityScreen::OnScreenLocked)); session_->unlocked.connect(sigc::mem_fun(this, &UnityScreen::OnScreenUnlocked)); session_dbus_manager_ = std::make_shared(session_); session_controller_ = std::make_shared(session_); LOG_INFO(logger) << "InitUnityComponents-Session " << timer.ElapsedSeconds() << "s"; Introspectable::AddChild(session_controller_.get()); // Setup Lockscreen Controller screensaver_dbus_manager_ = std::make_shared(session_); lockscreen_controller_ = std::make_shared(screensaver_dbus_manager_, session_, menus_->KeyGrabber()); UpdateActivateIndicatorsKey(); LOG_INFO(logger) << "InitUnityComponents-Lockscreen " << timer.ElapsedSeconds() << "s"; if (g_file_test(GetLockStampFile().c_str(), G_FILE_TEST_EXISTS)) session_->PromptLockScreen(); auto on_launcher_size_changed = [this] (nux::Area* area, int w, int h) { /* The launcher geometry includes 1px used to draw the right/top margin * that must not be considered when drawing an overlay */ auto* launcher = static_cast(area); auto launcher_position = Settings::Instance().launcher_position(); int size = 0; if (launcher_position == LauncherPosition::LEFT) size = w; else size = h; int launcher_size = size - (1_em).CP(unity_settings_.em(launcher->monitor)->DPIScale()); unity::Settings::Instance().SetLauncherSize(launcher_size, launcher->monitor); int adjustment_x = 0; if (launcher_position == LauncherPosition::LEFT) adjustment_x = launcher_size; shortcut_controller_->SetAdjustment(adjustment_x, panel_style_.PanelHeight(launcher->monitor)); CompOption::Value v(launcher_size); if (launcher_position == LauncherPosition::LEFT) { screen->setOptionForPlugin("expo", "x_offset", v); if (launcher_controller_->options()->hide_mode == LAUNCHER_HIDE_NEVER) v.set(0); screen->setOptionForPlugin("scale", "x_offset", v); v.set(0); screen->setOptionForPlugin("expo", "y_bottom_offset", v); screen->setOptionForPlugin("scale", "y_bottom_offset", v); } else { screen->setOptionForPlugin("expo", "y_bottom_offset", v); if (launcher_controller_->options()->hide_mode == LAUNCHER_HIDE_NEVER) v.set(0); screen->setOptionForPlugin("scale", "y_bottom_offset", v); v.set(0); screen->setOptionForPlugin("expo", "x_offset", v); screen->setOptionForPlugin("scale", "x_offset", v); } }; auto check_launchers_size = [this, on_launcher_size_changed] { launcher_size_connections_.Clear(); for (auto const& launcher : launcher_controller_->launchers()) { launcher_size_connections_.Add(launcher->size_changed.connect(on_launcher_size_changed)); on_launcher_size_changed(launcher.GetPointer(), launcher->GetWidth(), launcher->GetHeight()); } }; UScreen::GetDefault()->changed.connect(sigc::track_obj([this, check_launchers_size] (int, std::vector const&) { check_launchers_size(); }, *this)); Settings::Instance().launcher_position.changed.connect(sigc::track_obj([this, check_launchers_size] (LauncherPosition const&) { check_launchers_size(); }, *this)); check_launchers_size(); launcher_controller_->options()->scroll_inactive_icons = optionGetScrollInactiveIcons(); launcher_controller_->options()->minimize_window_on_click = optionGetLauncherMinimizeWindow(); ScheduleRelayout(0); } switcher::Controller::Ptr UnityScreen::switcher_controller() { return switcher_controller_; } launcher::Controller::Ptr UnityScreen::launcher_controller() { return launcher_controller_; } lockscreen::Controller::Ptr UnityScreen::lockscreen_controller() { return lockscreen_controller_; } void UnityScreen::UpdateGesturesSupport() { auto& s = Settings::Instance(); bool locked = lockscreen_controller_ && lockscreen_controller_->IsLocked(); (!locked && s.gestures_launcher_drag()) ? gestures_sub_launcher_->Activate() : gestures_sub_launcher_->Deactivate(); (!locked && s.gestures_dash_tap()) ? gestures_sub_dash_->Activate() : gestures_sub_dash_->Deactivate(); (!locked && s.gestures_windows_drag_pinch()) ? gestures_sub_windows_->Activate() : gestures_sub_windows_->Deactivate(); } void UnityScreen::InitGesturesSupport() { std::unique_ptr gesture_broker(new UnityGestureBroker); wt->GetWindowCompositor().SetGestureBroker(std::move(gesture_broker)); gestures_sub_launcher_.reset(new nux::GesturesSubscription); gestures_sub_launcher_->SetGestureClasses(nux::DRAG_GESTURE); gestures_sub_launcher_->SetNumTouches(4); gestures_sub_launcher_->SetWindowId(GDK_ROOT_WINDOW()); gestures_sub_dash_.reset(new nux::GesturesSubscription); gestures_sub_dash_->SetGestureClasses(nux::TAP_GESTURE); gestures_sub_dash_->SetNumTouches(4); gestures_sub_dash_->SetWindowId(GDK_ROOT_WINDOW()); gestures_sub_windows_.reset(new nux::GesturesSubscription); gestures_sub_windows_->SetGestureClasses(nux::TOUCH_GESTURE | nux::DRAG_GESTURE | nux::PINCH_GESTURE); gestures_sub_windows_->SetNumTouches(3); gestures_sub_windows_->SetWindowId(GDK_ROOT_WINDOW()); // Apply the user's settings UpdateGesturesSupport(); } CompAction::Vector& UnityScreen::getActions() { return menus_->KeyGrabber()->GetActions(); } void UnityScreen::ShowFirstRunHints() { sources_.AddTimeoutSeconds(2, [this] { auto const& config_dir = DesktopUtilities::GetUserConfigDirectory(); if (!config_dir.empty() && !g_file_test((config_dir+local::FIRST_RUN_STAMP).c_str(), G_FILE_TEST_EXISTS)) { // We focus the panel, so the shortcut hint will be hidden at first user input auto const& panels = panel_controller_->panels(); if (!panels.empty()) { auto panel_win = static_cast(panels.front()->GetTopLevelViewWindow()); SaveInputThenFocus(panel_win->GetInputWindowId()); } shortcut_controller_->first_run = true; shortcut_controller_->Show(); glib::Error error; g_file_set_contents((config_dir+local::FIRST_RUN_STAMP).c_str(), "", 0, &error); if (error) { LOG_ERROR(logger) << "Impossible to save the unity stamp file: " << error; } } return false; }); } Window UnityScreen::GetNextActiveWindow() const { return next_active_window_; } void UnityScreen::SetNextActiveWindow(Window next_active_window) { next_active_window_ = next_active_window; } /* Window init */ namespace { bool WindowHasInconsistentShapeRects(Display *d, Window w) { int n; Atom *atoms = XListProperties(d, w, &n); bool has_inconsistent_shape = false; for (int i = 0; i < n; ++i) { if (atoms[i] == atom::_UNITY_SAVED_WINDOW_SHAPE) { has_inconsistent_shape = true; break; } } XFree(atoms); return has_inconsistent_shape; } } UnityWindow::UnityWindow(CompWindow* window) : BaseSwitchWindow(static_cast(uScreen), window) , PluginClassHandler(window) , window(window) , cWindow(CompositeWindow::get(window)) , gWindow(GLWindow::get(window)) , close_icon_state_(decoration::WidgetState::NORMAL) , deco_win_(uScreen->deco_manager_->HandleWindow(window)) , need_fake_deco_redraw_(false) , is_nux_window_(PluginAdapter::IsNuxWindow(window)) { WindowInterface::setHandler(window); GLWindowInterface::setHandler(gWindow); CompositeWindowInterface::setHandler(cWindow); ScaleWindowInterface::setHandler(ScaleWindow::get(window)); PluginAdapter::Default().OnLeaveDesktop(); /* This needs to happen before we set our wrapable functions, since we * need to ask core (and not ourselves) whether or not the window is * minimized */ if (uScreen->optionGetShowMinimizedWindows() && window->mapNum() && WindowHasInconsistentShapeRects(screen->dpy (), window->id())) { /* Query the core function */ window->minimizedSetEnabled (this, false); bool wasMinimized = window->minimized (); if (wasMinimized) window->unminimize (); window->minimizedSetEnabled (this, true); if (wasMinimized) window->minimize (); } else { window->minimizeSetEnabled (this, false); window->unminimizeSetEnabled (this, false); window->minimizedSetEnabled (this, false); } /* Keep this after the optionGetShowMIntrospectable.hinimizedWindows branch */ if (window->state() & CompWindowStateFullscreenMask) uScreen->fullscreen_windows_.push_back(window); if (WindowManager::Default().IsOnscreenKeyboard(window->id()) && window->isViewable()) { uScreen->onboard_ = window; uScreen->RaiseOSK(); } } void UnityWindow::AddProperties(debug::IntrospectionData& introspection) { Window xid = window->id(); auto const& swins = uScreen->sScreen->getWindows(); WindowManager& wm = uScreen->WM; bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end(); introspection .add(scaled ? GetScaledGeometry() : wm.GetWindowGeometry(xid)) .add("xid", xid) .add("title", wm.GetWindowName(xid)) .add("fake_decorated", uScreen->fake_decorated_windows_.find(this) != uScreen->fake_decorated_windows_.end()) .add("maximized", wm.IsWindowMaximized(xid)) .add("horizontally_maximized", wm.IsWindowHorizontallyMaximized(xid)) .add("vertically_maximized", wm.IsWindowVerticallyMaximized(xid)) .add("minimized", wm.IsWindowMinimized(xid)) .add("scaled", scaled) .add("scaled_close_geo", close_button_geo_) .add("scaled_close_x", close_button_geo_.x) .add("scaled_close_y", close_button_geo_.y) .add("scaled_close_width", close_button_geo_.width) .add("scaled_close_height", close_button_geo_.height); } std::string UnityWindow::GetName() const { return "Window"; } void UnityWindow::DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const& attrib, GLMatrix const& transform, unsigned int mask, int x, int y, double scale) { for (auto const& texture : textures) { if (!texture) continue; gWindow->vertexBuffer()->begin(); if (texture->width() && texture->height()) { GLTexture::MatrixList ml({texture->matrix()}); CompRegion texture_region(0, 0, texture->width(), texture->height()); gWindow->glAddGeometry(ml, texture_region, texture_region); } if (gWindow->vertexBuffer()->end()) { GLMatrix wTransform(transform); wTransform.translate(x, y, 0.0f); wTransform.scale(scale, scale, 1.0f); gWindow->glDrawTexture(texture, wTransform, attrib, mask); } } } void UnityWindow::RenderDecoration(compiz_utils::CairoContext const& ctx, double aspect) { if (aspect <= 0) return; using namespace decoration; aspect *= deco_win_->dpi_scale(); double w = ctx.width() / aspect; double h = ctx.height() / aspect; Style::Get()->DrawSide(Side::TOP, WidgetState::NORMAL, ctx, w, h); } void UnityWindow::RenderTitle(compiz_utils::CairoContext const& ctx, int x, int y, int width, int height, double aspect) { using namespace decoration; auto const& style = Style::Get(); auto const& title = deco_win_->title(); auto text_size = style->TitleNaturalSize(title); x += style->TitleIndent(); y += (height - text_size.height)/2; cairo_save(ctx); cairo_scale(ctx, 1.0f/aspect, 1.0f/aspect); cairo_translate(ctx, x, y); style->DrawTitle(title, WidgetState::NORMAL, ctx, width - x, height); cairo_restore(ctx); } void UnityWindow::BuildDecorationTexture() { auto const& border = decoration::Style::Get()->Border(); if (border.top) { double dpi_scale = deco_win_->dpi_scale(); compiz_utils::CairoContext context(window->borderRect().width(), border.top * dpi_scale, dpi_scale); RenderDecoration(context); decoration_tex_ = context; } } void UnityWindow::CleanupCachedTextures() { decoration_tex_.reset(); decoration_selected_tex_.reset(); decoration_title_.clear(); } void UnityWindow::paintFakeDecoration(nux::Geometry const& geo, GLWindowPaintAttrib const& attrib, GLMatrix const& transform, unsigned int mask, bool highlighted, double scale) { mask |= PAINT_WINDOW_BLEND_MASK; if (!decoration_tex_ && compiz_utils::IsWindowFullyDecorable(window)) BuildDecorationTexture(); if (!highlighted) { if (decoration_tex_) DrawTexture(*decoration_tex_, attrib, transform, mask, geo.x, geo.y, scale); close_button_geo_.Set(0, 0, 0, 0); } else { auto const& style = decoration::Style::Get(); double dpi_scale = deco_win_->dpi_scale(); int width = geo.width; int height = style->Border().top * dpi_scale; auto const& padding = style->Padding(decoration::Side::TOP); bool redraw_decoration = true; compiz_utils::SimpleTexture::Ptr close_texture; if (decoration_selected_tex_) { auto* texture = decoration_selected_tex_->texture(); if (texture && texture->width() == width && texture->height() == height) { if (decoration_title_ == deco_win_->title()) redraw_decoration = false; } } if (window->actions() & CompWindowActionCloseMask) { using namespace decoration; close_texture = DataPool::Get()->ButtonTexture(dpi_scale, WindowButtonType::CLOSE, close_icon_state_); } if (redraw_decoration) { if (width != 0 && height != 0) { compiz_utils::CairoContext context(width, height, scale * dpi_scale); RenderDecoration(context, scale); // Draw window title int text_x = padding.left + (close_texture ? close_texture->width() : 0) / dpi_scale; RenderTitle(context, text_x, padding.top, (width - padding.right) / dpi_scale, height / dpi_scale, scale); decoration_selected_tex_ = context; decoration_title_ = deco_win_->title(); uScreen->damageRegion(CompRegionFromNuxGeo(geo)); need_fake_deco_redraw_ = true; if (decoration_tex_) DrawTexture(*decoration_tex_, attrib, transform, mask, geo.x, geo.y, scale); return; // Let's draw this at next repaint cycle } else { decoration_selected_tex_.reset(); redraw_decoration = false; } } else { need_fake_deco_redraw_ = false; } if (decoration_selected_tex_) DrawTexture(*decoration_selected_tex_, attrib, transform, mask, geo.x, geo.y); if (close_texture) { int w = close_texture->width(); int h = close_texture->height(); int x = geo.x + padding.left * dpi_scale; int y = geo.y + padding.top * dpi_scale + (height - w) / 2.0f; close_button_geo_.Set(x, y, w, h); DrawTexture(*close_texture, attrib, transform, mask, x, y); } else { close_button_geo_.Set(0, 0, 0, 0); } } uScreen->fake_decorated_windows_.insert(this); } void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib, GLMatrix const& transform, CompRegion const& region, unsigned int mask) { ScaleWindow* scale_win = ScaleWindow::get(window); scale_win->scalePaintDecoration(attrib, transform, region, mask); if (!scale_win->hasSlot()) // animation not finished return; auto state = uScreen->sScreen->getState(); if (state != ScaleScreen::Wait && state != ScaleScreen::Out && !need_fake_deco_redraw_) return; nux::Geometry const& scale_geo = GetScaledGeometry(); auto const& pos = scale_win->getCurrentPosition(); auto deco_attrib = attrib; deco_attrib.opacity = COMPIZ_COMPOSITE_OPAQUE; bool highlighted = (uScreen->sScreen->getSelectedWindow() == window->id()); paintFakeDecoration(scale_geo, deco_attrib, transform, mask, highlighted, pos.scale); } nux::Geometry UnityWindow::GetLayoutWindowGeometry() { auto const& layout_window = uScreen->GetSwitcherDetailLayoutWindow(window->id()); if (layout_window) return layout_window->result; return nux::Geometry(); } nux::Geometry UnityWindow::GetScaledGeometry() { if (!uScreen->WM.IsScaleActive()) return nux::Geometry(); ScaleWindow* scale_win = ScaleWindow::get(window); ScalePosition const& pos = scale_win->getCurrentPosition(); auto const& border_rect = window->borderRect(); auto const& deco_ext = window->border(); const unsigned width = std::round(border_rect.width() * pos.scale); const unsigned height = std::round(border_rect.height() * pos.scale); const int x = pos.x() + window->x() - std::round(deco_ext.left * pos.scale); const int y = pos.y() + window->y() - std::round(deco_ext.top * pos.scale); return nux::Geometry(x, y, width, height); } void UnityWindow::OnInitiateSpread() { close_icon_state_ = decoration::WidgetState::NORMAL; middle_clicked_ = false; deco_win_->scaled = true; if (IsInShowdesktopMode()) { if (mShowdesktopHandler) { mShowdesktopHandler->FadeIn(); } } } void UnityWindow::OnTerminateSpread() { CleanupCachedTextures(); deco_win_->scaled = false; if (IsInShowdesktopMode()) { if (uScreen->GetNextActiveWindow() != window->id()) { if (!mShowdesktopHandler) mShowdesktopHandler.reset(new ShowdesktopHandler(static_cast (this), static_cast (this))); mShowdesktopHandler->FadeOut(); } else { window->setShowDesktopMode(false); } } } void UnityWindow::paintInnerGlow(nux::Geometry glow_geo, GLMatrix const& matrix, GLWindowPaintAttrib const& attrib, unsigned mask) { using namespace decoration; auto const& style = Style::Get(); double dpi_scale = deco_win_->dpi_scale(); unsigned glow_size = std::round(style->GlowSize() * dpi_scale); auto const& glow_texture = DataPool::Get()->GlowTexture(); if (!glow_size || !glow_texture) return; auto const& radius = style->CornerRadius(); int decoration_radius = std::max({radius.top, radius.left, radius.right, radius.bottom}); if (decoration_radius > 0) { // We paint the glow below the window edges to correctly // render the rounded corners int inside_glow = decoration_radius * dpi_scale / 4; glow_size += inside_glow; glow_geo.Expand(-inside_glow, -inside_glow); } glow::Quads const& quads = computeGlowQuads(glow_geo, *glow_texture, glow_size); paintGlow(matrix, attrib, quads, *glow_texture, style->GlowColor(), mask); } void UnityWindow::paintThumbnail(nux::Geometry const& geo, float alpha, float parent_alpha, float scale_ratio, unsigned deco_height, bool selected) { GLMatrix matrix; matrix.toScreenSpace(uScreen->last_output_, -DEFAULT_Z_CAMERA); last_bound = geo; GLWindowPaintAttrib attrib = gWindow->lastPaintAttrib(); attrib.opacity = (alpha * parent_alpha * COMPIZ_COMPOSITE_OPAQUE); unsigned mask = gWindow->lastMask(); nux::Geometry thumb_geo = geo; if (selected) paintInnerGlow(thumb_geo, matrix, attrib, mask); thumb_geo.y += std::round(deco_height * 0.5f * scale_ratio); nux::Geometry const& g = thumb_geo; paintThumb(attrib, matrix, mask, g.x, g.y, g.width, g.height, g.width, g.height); mask |= PAINT_WINDOW_BLEND_MASK; attrib.opacity = parent_alpha * COMPIZ_COMPOSITE_OPAQUE; // The thumbnail is still animating, don't draw the decoration as selected if (selected && alpha < 1.0f) selected = false; paintFakeDecoration(geo, attrib, matrix, mask, selected, scale_ratio); } UnityWindow::~UnityWindow() { if (uScreen->newFocusedWindow && UnityWindow::get(uScreen->newFocusedWindow) == this) uScreen->newFocusedWindow = NULL; uScreen->deco_manager_->UnHandleWindow(window); if (!window->destroyed ()) { bool wasMinimized = window->minimized (); if (wasMinimized) window->unminimize (); window->focusSetEnabled (this, false); window->minimizeSetEnabled (this, false); window->unminimizeSetEnabled (this, false); if (wasMinimized) window->minimize (); } ShowdesktopHandler::animating_windows.remove (static_cast (this)); if (window->state () & CompWindowStateFullscreenMask) uScreen->fullscreen_windows_.remove(window); if (window == uScreen->onboard_) uScreen->onboard_ = nullptr; uScreen->fake_decorated_windows_.erase(this); PluginAdapter::Default().OnWindowClosed(window); } /* vtable init */ bool UnityPluginVTable::init() { if (!CompPlugin::checkPluginABI("core", CORE_ABIVERSION)) return false; if (!CompPlugin::checkPluginABI("composite", COMPIZ_COMPOSITE_ABI)) return false; if (!CompPlugin::checkPluginABI("opengl", COMPIZ_OPENGL_ABI)) return false; unity_a11y_preset_environment(); /* * GTK needs to be initialized or else unity's gdk/gtk calls will crash. * This is already done in compiz' main() if using ubuntu packages, but not * if you're using the regular (upstream) compiz. * Admittedly this is the same as what the "gtkloader" plugin does. But it * is faster, more efficient (one less plugin in memory), and more reliable * to do the init here where its needed. And yes, init'ing multiple times is * safe, and does nothing after the first init. */ if (!gtk_init_check(&programArgc, &programArgv)) { compLogMessage("unityshell", CompLogLevelError, "GTK init failed\n"); return false; } return true; } namespace debug { ScreenIntrospection::ScreenIntrospection(CompScreen* screen) : screen_(screen) {} std::string ScreenIntrospection::GetName() const { return "Screen"; } void ScreenIntrospection::AddProperties(debug::IntrospectionData& introspection) {} Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren() { IntrospectableList children({uScreen->spread_widgets_ ? uScreen->spread_widgets_->GetFilter().get() : nullptr}); for (auto const& win : screen_->windows()) children.push_back(UnityWindow::get(win)); return children; } } // debug namespace namespace { void reset_glib_logging() { // Reinstate the default glib logger. g_log_set_default_handler(g_log_default_handler, NULL); } void configure_logging() { // The default behaviour of the logging infrastructure is to send all output // to std::cout for warning or above. // TODO: write a file output handler that keeps track of backups. nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); g_log_set_default_handler(capture_g_log_calls, NULL); } /* Checks whether an extension is supported by the GLX or OpenGL implementation * given the extension name and the list of supported extensions. */ #ifndef USE_GLES gboolean is_extension_supported(const gchar* extensions, const gchar* extension) { if (extensions != NULL && extension != NULL) { const gsize len = strlen(extension); gchar* p = (gchar*) extensions; gchar* end = p + strlen(p); while (p < end) { const gsize size = strcspn(p, " "); if (len == size && strncmp(extension, p, size) == 0) return TRUE; p += size + 1; } } return FALSE; } /* Gets the OpenGL version as a floating-point number given the string. */ gfloat get_opengl_version_f32(const gchar* version_string) { gfloat version = 0.0f; gint32 i; for (i = 0; isdigit(version_string[i]); ++i) version = version * 10.0f + (version_string[i] - 48); if ((version_string[i] == '.' || version_string[i] == ',') && isdigit(version_string[i + 1])) { version = version * 10.0f + (version_string[i + 1] - 48); return (version + 0.1f) * 0.1f; } else return 0.0f; } #endif nux::logging::Level glog_level_to_nux(GLogLevelFlags log_level) { // For some weird reason, ERROR is more critical than CRITICAL in gnome. if (log_level & G_LOG_LEVEL_ERROR) return nux::logging::Critical; if (log_level & G_LOG_LEVEL_CRITICAL) return nux::logging::Error; if (log_level & G_LOG_LEVEL_WARNING) return nux::logging::Warning; if (log_level & G_LOG_LEVEL_MESSAGE || log_level & G_LOG_LEVEL_INFO) return nux::logging::Info; // default to debug. return nux::logging::Debug; } void capture_g_log_calls(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data) { // If the environment variable is set, we capture the backtrace. static bool glog_backtrace = ::getenv("UNITY_LOG_GLOG_BACKTRACE"); // If nothing else, all log messages from unity should be identified as such std::string module("unity"); if (log_domain) { module += '.' + log_domain; } nux::logging::Logger logger(module); nux::logging::Level level = glog_level_to_nux(log_level); if (level >= logger.GetEffectiveLogLevel()) { std::string backtrace; if (glog_backtrace && level >= nux::logging::Warning) { backtrace = "\n" + nux::logging::Backtrace(); } nux::logging::LogStream(level, logger.module(), "", 0).stream() << message << backtrace; } } } // anonymous namespace } // namespace unity ./plugins/unityshell/src/inputremover.cpp0000644000004100000410000003634113437202764021175 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "inputremover.h" #include #include #include #include #include namespace { const unsigned int propVersion = 2; void CheckRectanglesCount(XRectangle *rects, int *count, unsigned int width, unsigned int height, unsigned int border) { /* check if the returned shape exactly matches the window shape - * if that is true, the window currently has no set input shape */ if ((*count == 1) && (rects[0].x == -((int) border)) && (rects[0].y == -((int) border)) && (rects[0].width == (width + border)) && (rects[0].height == (height + border))) { *count = 0; } } bool CheckWindowExists(Display *dpy, Window shapeWindow, unsigned int *width, unsigned int *height, unsigned int *border) { Window root; int x, y; unsigned int depth; /* FIXME: There should be a generic GetGeometry request we can * use here in order to avoid a round-trip */ if (!XGetGeometry (dpy, shapeWindow, &root, &x, &y, width, height, border, &depth)) { return false; } return true; } XRectangle * QueryRectangles(Display *dpy, Window shapeWindow, int *count, int *ordering, int kind) { return XShapeGetRectangles (dpy, shapeWindow, kind, count, ordering); } } compiz::WindowInputRemoverInterface::~WindowInputRemoverInterface () { } compiz::WindowInputRemover::WindowInputRemover (Display *dpy, Window shapeWindow, Window propWindow) : mDpy (dpy), mProperty (XInternAtom (mDpy, "_UNITY_SAVED_WINDOW_SHAPE", False)), mShapeWindow (shapeWindow), mPropWindow (propWindow), mShapeMask (0), mInputRects (NULL), mNInputRects (0), mInputRectOrdering (0), mRemoved (false) { /* FIXME: roundtrip */ XShapeQueryExtension (mDpy, &mShapeEvent, &mShapeError); /* Check to see if the propWindow has a saved shape, * if so, it means that we are coming from a restart or * a crash where it wasn't properly restored, so we need * to restore the saved input shape before doing anything */ XRectangle *rects; int count = 0, ordering; if (queryProperty(&rects, &count, &ordering)) { bool rectangles_restored = false; unsigned int width, height, border; if (CheckWindowExists(mDpy, mShapeWindow, &width, &height, &border)) if (checkRectangles(rects, &count, ordering, width, height, border)) if (saveRectangles(rects, count, ordering)) { /* Tell the shape restore engine that we've got a removed * input shape here */ mRemoved = true; if (restoreInput()) rectangles_restored = true; } /* Something failed and we couldn't restore the * rectangles. Don't leak them */ if (!rectangles_restored) { free (rects); } } } compiz::WindowInputRemover::~WindowInputRemover () { if (mRemoved) restore (); /* Remove the window property as we have already successfully restored * the window shape */ clearProperty(); } void compiz::WindowInputRemover::sendShapeNotify () { /* Send a synthetic ShapeNotify event to the client and parent window * since we ignored shape events when setting visibility * in order to avoid cycling in the shape handling code - * ignore the sent shape notify event since that will * be send_event = true * * NB: We must send ShapeNotify events to both the client * window and to the root window with SubstructureRedirectMask * since NoEventMask will only deliver the event to the client * (see xserver/dix/events.c on the handling of CantBeFiltered * messages) * * NB: This code will break if you don't have this patch on the * X Server since XSendEvent for non-core events are broken. * * http://lists.x.org/archives/xorg-devel/2011-September/024996.html */ XShapeEvent xsev; XEvent *xev = (XEvent *) &xsev; Window rootReturn, parentReturn; Window childReturn; Window *children; int x, y, xOffset, yOffset; unsigned int width, height, depth, border, nchildren; memset (&xsev, 0, sizeof (XShapeEvent)); /* XXX: libXShape is weird and range checks the event * type on event_to_wire so ensure that we are setting * the event type on the right range */ xsev.type = (mShapeEvent - ShapeNotify) & 0x7f; /* We must explicitly fill in these values to avoid padding errors */ xsev.serial = 0L; xsev.send_event = TRUE; xsev.display = mDpy; xsev.window = mShapeWindow; if (!mRemoved) { /* FIXME: these roundtrips suck */ if (!XGetGeometry (mDpy, mShapeWindow, &rootReturn, &x, &y, &width, &height, &depth, &border)) return; if (!XQueryTree (mDpy, mShapeWindow, &rootReturn, &parentReturn, &children, &nchildren)) return; /* We need to translate the co-ordinates of the origin to the * client window to its parent to find out the offset of its * position so that we can subtract that from the final bounding * rect of the window shape according to the Shape extension * specification */ XTranslateCoordinates (mDpy, mShapeWindow, parentReturn, 0, 0, &xOffset, &yOffset, &childReturn); xsev.kind = ShapeInput; /* Calculate extents of the bounding shape */ if (!mNInputRects) { /* No set input shape, we must use the client geometry */ xsev.x = x - xOffset; xsev.y = y - yOffset; xsev.width = width; xsev.height = height; xsev.shaped = false; } else { Region inputRegion = XCreateRegion (); for (int i = 0; i < mNInputRects; i++) XUnionRectWithRegion (&(mInputRects[i]), inputRegion, inputRegion); xsev.x = inputRegion->extents.x1 - xOffset; xsev.y = inputRegion->extents.y1 - yOffset; xsev.width = inputRegion->extents.x2 - inputRegion->extents.x1; xsev.height = inputRegion->extents.y2 - inputRegion->extents.y1; xsev.shaped = true; XDestroyRegion (inputRegion); } xsev.time = CurrentTime; XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev); XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev); if (children) XFree (children); } else { XQueryTree (mDpy, mShapeWindow, &rootReturn, &parentReturn, &children, &nchildren); xsev.x = 0; xsev.y = 0; xsev.width = 0; xsev.height = 0; xsev.shaped = true; xsev.kind = ShapeInput; /* ShapeInput is null */ xsev.time = CurrentTime; XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev); XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev); } } bool compiz::WindowInputRemover::checkRectangles(XRectangle *input, int *nInput, int inputOrdering, unsigned int width, unsigned int height, unsigned int border) { CheckRectanglesCount(input, nInput, width, height, border); /* There may be other sanity checks in future */ return true; } bool compiz::WindowInputRemover::queryShapeRectangles(XRectangle **input, int *nInput, int *inputOrdering, unsigned int *width, unsigned int *height, unsigned int *border) { if (!CheckWindowExists(mDpy, mShapeWindow, width, height, border)) return false; *input = QueryRectangles(mDpy, mShapeWindow, nInput, inputOrdering, ShapeInput); return true; } bool compiz::WindowInputRemover::saveRectangles(XRectangle *input, int nInput, int inputOrdering) { if (mInputRects) XFree (mInputRects); mInputRects = input; mNInputRects = nInput; mInputRectOrdering = inputOrdering; return true; } void compiz::WindowInputRemover::clearRectangles () { /* Revert save action to local memory */ if (mInputRects) XFree (mInputRects); mNInputRects = 0; mInputRectOrdering = 0; mShapeMask = 0; mRemoved = false; } bool compiz::WindowInputRemover::writeProperty (XRectangle *input, int nInput, int inputOrdering) { Atom type = XA_CARDINAL; int fmt = 32; const unsigned int headerSize = 3; /* * -> version * -> nInput * -> inputOrdering * -> nBounding * -> boundingOrdering * * + * * nRectangles * 4 */ const size_t dataSize = headerSize + (nInput * 4); std::vector data(dataSize); data[0] = propVersion; data[1] = nInput; data[2] = inputOrdering; for (int i = 0; i < nInput; ++i) { const unsigned int position = headerSize + (i * 4); data[position + 0] = input[i].x; data[position + 1] = input[i].y; data[position + 2] = input[i].width; data[position + 3] = input[i].height; } /* No need to check return code, always returns 0 */ XChangeProperty(mDpy, mPropWindow, mProperty, type, fmt, PropModeReplace, reinterpret_cast(data.data()), dataSize); return true; } bool compiz::WindowInputRemover::queryProperty(XRectangle **input, int *nInput, int *inputOrdering) { Atom type = XA_CARDINAL; int fmt = 32; Atom actualType; int actualFmt; unsigned long nItems; unsigned long nLeft; unsigned char *propData; const unsigned long headerLength = 3L; /* First query the first five bytes to figure out how * long the rest of the property is going to be */ if (!XGetWindowProperty(mDpy, mPropWindow, mProperty, 0L, headerLength, FALSE, type, &actualType, &actualFmt, &nItems, &nLeft, &propData)) { return false; } /* If type or format is mismatched, return false */ if (actualType != type || actualFmt != fmt || headerLength != nItems) { XFree (propData); return false; } /* XXX: the cast to void * before the reinterpret_cast is a hack to calm down * gcc on ARM machines and its misalignment cast errors */ unsigned long *headerData = reinterpret_cast(static_cast(propData)); /* If version is mismatched, return false */ if (headerData[0] != propVersion) return false; /* New length is nInput * 4 + nBounding * 4 + headerSize */ unsigned long fullLength = *nInput * 4 + headerLength; /* Free data and get the rest */ XFree (propData); if (!XGetWindowProperty(mDpy, mPropWindow, mProperty, 0L, fullLength, FALSE, type, &actualType, &actualFmt, &nItems, &nLeft, &propData)) { return false; } /* Check to make sure we got everything */ if (fullLength != nItems) { printf ("warning, did not get full legnth"); return false; } unsigned long *data = reinterpret_cast(static_cast(propData)); /* Read in the header */ *nInput = data[1]; *inputOrdering = data[2]; /* Read in the rectangles */ *input = reinterpret_cast(calloc(1, sizeof(XRectangle) * *nInput)); for (int i = 0; i < *nInput; ++i) { const unsigned int position = headerLength + i * 4; (*input)[i].x = data[position + 0]; (*input)[i].y = data[position + 1]; (*input[i]).width = data[position + 2]; (*input[i]).height = data[position + 3]; } XFree (propData); return true; } void compiz::WindowInputRemover::clearProperty() { XDeleteProperty(mDpy, mPropWindow, mProperty); } bool compiz::WindowInputRemover::saveInput () { XRectangle *rects; int count = 0, ordering; unsigned int width, height, border; /* Never save input for a cleared window */ if (mRemoved) return false; if (!queryShapeRectangles(&rects, &count, &ordering, &width, &height, &border)) { clearRectangles (); return false; } if (!checkRectangles(rects, &count, ordering, width, height, border)) { clearRectangles (); return false; } if (!writeProperty(rects, count, ordering)) { clearRectangles (); return false; } mShapeMask = XShapeInputSelected (mDpy, mShapeWindow); saveRectangles(rects, count, ordering); return true; } bool compiz::WindowInputRemover::removeInput () { if (!mNInputRects) if (!save ()) return false; XShapeSelectInput (mDpy, mShapeWindow, NoEventMask); XShapeCombineRectangles (mDpy, mShapeWindow, ShapeInput, 0, 0, NULL, 0, ShapeSet, 0); XShapeSelectInput (mDpy, mShapeWindow, mShapeMask); sendShapeNotify (); mRemoved = true; return true; } bool compiz::WindowInputRemover::restoreInput () { XShapeSelectInput (mDpy, mShapeWindow, NoEventMask); if (mRemoved) { if (mNInputRects) { XShapeCombineRectangles (mDpy, mShapeWindow, ShapeInput, 0, 0, mInputRects, mNInputRects, ShapeSet, mInputRectOrdering); } else { XShapeCombineMask (mDpy, mShapeWindow, ShapeInput, 0, 0, None, ShapeSet); } if (mInputRects) { XFree (mInputRects); mInputRects = NULL; mNInputRects = 0; } } XShapeSelectInput (mDpy, mShapeWindow, mShapeMask); mRemoved = false; sendShapeNotify (); return true; } ./plugins/unityshell/src/transientfor.h0000644000004100000410000000301413437202764020610 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_TRANSIENTFORHANDLER_H #define _COMPIZ_TRANSIENTFORHANDLER_H #include #include #include #include #include // Will be merged back into compiz namespace compiz { class PrivateX11TransientForReader; class X11TransientForReader { public: X11TransientForReader (Display *dpy, Window xid); virtual ~X11TransientForReader (); bool isTransientFor (unsigned int ancestor); bool isGroupTransientFor (unsigned int clientLeader); std::vector getTransients (); static Atom wmTransientFor; static Atom wmClientLeader; protected: virtual unsigned int getAncestor (); private: PrivateX11TransientForReader *priv; }; } #endif ./plugins/unityshell/src/inputremover.h0000644000004100000410000000746013437202764020642 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_INPUTREMOVER_H #define _COMPIZ_INPUTREMOVER_H #include #include #include #include // Will be merged back into compiz namespace compiz { class WindowInputRemoverInterface { public: typedef std::shared_ptr Ptr; bool save () { return saveInput (); } bool remove () { return removeInput (); } bool restore () { return restoreInput (); } virtual ~WindowInputRemoverInterface (); protected: virtual bool saveInput () = 0; virtual bool removeInput () = 0; virtual bool restoreInput () = 0; }; class WindowInputRemover : public WindowInputRemoverInterface { public: WindowInputRemover (Display *, Window shapeWindow, Window propWindow); ~WindowInputRemover (); private: bool saveInput (); bool removeInput (); bool restoreInput (); void sendShapeNotify (); bool queryShapeRectangles(XRectangle **input, int *nInput, int *inputOrdering, unsigned int *width, unsigned int *height, unsigned int *border); bool queryProperty(XRectangle **input, int *nInput, int *inputOrdering); bool writeProperty(XRectangle *input, int nInput, int inputOrdering); bool checkRectangles(XRectangle *input, int *nInput, int inputOrdering, unsigned int width, unsigned int height, unsigned int border); bool saveRectangles(XRectangle *input, int nInput, int inputOrdering); void clearProperty (); void clearRectangles (); Display *mDpy; Atom mProperty; Window mShapeWindow; Window mPropWindow; unsigned long mShapeMask; XRectangle *mInputRects; int mNInputRects; int mInputRectOrdering; bool mRemoved; int mShapeEvent; int mShapeError; }; class WindowInputRemoverLock { public: typedef std::shared_ptr Ptr; typedef std::weak_ptr Weak; WindowInputRemoverLock (WindowInputRemoverInterface *remover) : remover_ (remover) { remover->save(); remover->remove(); } ~WindowInputRemoverLock () { remover_->restore(); delete remover_; } void refresh () { remover_->save(); remover_->remove(); } private: WindowInputRemoverInterface *remover_; }; class WindowInputRemoverLockAcquireInterface { public: virtual ~WindowInputRemoverLockAcquireInterface () {} WindowInputRemoverLock::Ptr InputRemover () { return GetInputRemover (); } private: virtual WindowInputRemoverLock::Ptr GetInputRemover () = 0; }; } #endif ./plugins/unityshell/src/UnityshellPrivate.cpp0000644000004100000410000000242213437202764022122 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andrea Azzarone */ #include "UnityshellPrivate.h" namespace unity { namespace impl { std::string CreateActionString(std::string const& modifiers, char shortcut, ActionModifiers flag) { std::string ret(modifiers); if (flag == ActionModifiers::USE_SHIFT || flag == ActionModifiers::USE_SHIFT_NUMPAD) ret += ""; if (flag == ActionModifiers::USE_NUMPAD || flag == ActionModifiers::USE_SHIFT_NUMPAD) ret += "KP_"; ret += shortcut; return ret; } } // namespace impl } // namespace unity ./plugins/unityshell/src/WindowGestureTarget.cpp0000644000004100000410000001331713437202764022411 0ustar www-datawww-data/* * WindowGestureTarget.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "WindowGestureTarget.h" #include // otherwise unityshell.h inclusion will cause failures #include #include "unityshell.h" // To make the gesture tests pass, this has to be a local include. #include "PluginAdapter.h" using namespace nux; WindowGestureTarget::WindowGestureTarget(CompWindow *window) : window_(window), drag_grab_(0), started_window_move_(false), window_restored_by_pinch_(false) { // A workaround for the lack of weak pointers. unity::UnityWindow *unity_window = unity::UnityWindow::get(window); window_destruction_conn_ = unity_window->being_destroyed.connect( sigc::mem_fun(this, &WindowGestureTarget::NullifyWindowPointer)); } WindowGestureTarget::~WindowGestureTarget() { if (drag_grab_) { if (window_) window_->ungrabNotify(); screen->removeGrab(drag_grab_, NULL); } } void WindowGestureTarget::NullifyWindowPointer() { window_ = nullptr; } GestureDeliveryRequest WindowGestureTarget::GestureEvent(const nux::GestureEvent &event) { if (!window_) return GestureDeliveryRequest::NONE; switch (event.type) { case nux::EVENT_GESTURE_BEGIN: PluginAdapter::Default().ShowGrabHandles(window_, false); break; case EVENT_GESTURE_UPDATE: if (event.GetGestureClasses() & PINCH_GESTURE) MaximizeOrRestoreWindowDueToPinch(event); if (event.GetGestureClasses() & DRAG_GESTURE) { if (WindowCanMove()) { if (!started_window_move_) { StartWindowMove(event); started_window_move_ = true; } MoveWindow(event); } } break; default: // EVENT_GESTURE_END | EVENT_GESTURE_LOST if (event.GetGestureClasses() & DRAG_GESTURE) { EndWindowMove(event); started_window_move_ = false; } PluginAdapter::Default().ShowGrabHandles(window_, true); break; }; return GestureDeliveryRequest::NONE; } bool WindowGestureTarget::WindowCanMove() { if (!(window_->actions() & CompWindowActionMoveMask)) return false; /* Don't allow windows to be dragged if completely maximized */ if ((window_->state() & MAXIMIZE_STATE) == MAXIMIZE_STATE) return false; /* Don't start moving a window that has just been restored. The user is likely still performing the pinch and not expecting the window to start moving */ if (window_restored_by_pinch_) return false; return true; } void WindowGestureTarget::MaximizeOrRestoreWindowDueToPinch(const nux::GestureEvent &event) { if (event.GetRadius() > 1.25f) { window_->maximize(MAXIMIZE_STATE); RemoveDragGrab(); window_restored_by_pinch_ = false; } else if (event.GetRadius() < 0.8f) { if (window_->state() & MAXIMIZE_STATE) { window_->maximize(0); RemoveDragGrab(); window_restored_by_pinch_ = true; } } } void WindowGestureTarget::StartWindowMove(const nux::GestureEvent &event) { if (!event.IsDirectTouch()) { drag_grab_ = screen->pushGrab(screen->cursorCache(XC_fleur), "unity"); window_->grabNotify(window_->serverGeometry().x(), window_->serverGeometry().y(), 0, CompWindowGrabMoveMask | CompWindowGrabButtonMask); } } void WindowGestureTarget::MoveWindow(const nux::GestureEvent &event) { const nux::Point2D &delta = event.GetDelta(); unsigned int px = std::max(std::min(pointerX + static_cast(delta.x), screen->width()), 0); unsigned int py = std::max(std::min(pointerY + static_cast(delta.y), screen->height()), 0); if (window_->state() & CompWindowStateMaximizedVertMask) py = pointerY; if (window_->state() & CompWindowStateMaximizedHorzMask) px = pointerX; if (!event.IsDirectTouch()) { /* FIXME: CompScreen::warpPointer filters out motion events which other plugins may need to process, but for most cases in core they should be filtered out. */ XWarpPointer(screen->dpy (), None, screen->root (), 0, 0, 0, 0, px, py); } XSync(screen->dpy (), false); window_->move(px - pointerX, py - pointerY, false); pointerX = px; pointerY = py; } void WindowGestureTarget::EndWindowMove(const nux::GestureEvent &event) { window_->ungrabNotify(); RemoveDragGrab(); } void WindowGestureTarget::RemoveDragGrab() { if (drag_grab_) { screen->removeGrab(drag_grab_, NULL); drag_grab_ = 0; } } bool WindowGestureTarget::Equals(const nux::GestureTarget& other) const { const WindowGestureTarget *window_target = dynamic_cast(&other); if (window_target) { if (window_ && window_target->window_) return window_->id() == window_target->window_->id(); else return window_ == window_target->window_; } else { return false; } } ./plugins/unityshell/src/GesturalWindowSwitcher.cpp0000644000004100000410000003117413437202764023124 0ustar www-datawww-data/* * GesturalWindowSwitcher.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel d'Andrada */ #include "GesturalWindowSwitcher.h" #include #include #include #include #include "SwitcherView.h" #include "unityshell.h" DECLARE_LOGGER(logger, "unity.gesture.switcher"); using namespace nux; using namespace unity; const float GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION = 100.0f; const float GesturalWindowSwitcher::MOUSE_DRAG_THRESHOLD = 20.0f; namespace unity { class GesturalWindowSwitcherPrivate { public: GesturalWindowSwitcherPrivate(); void CloseSwitcherAfterTimeout(int timeout); bool OnCloseSwitcherTimeout(); void CloseSwitcher(); // show the switcher and select the next application/window void InitiateSwitcherNext(); // show the switcher and select the previous application/window void InitiateSwitcherPrevious(); void ProcessAccumulatedHorizontalDrag(); nux::GestureDeliveryRequest GestureEvent(nux::GestureEvent const& event); nux::GestureDeliveryRequest WaitingCompoundGesture(nux::GestureEvent const& event); nux::GestureDeliveryRequest WaitingEndOfTapAndHold(nux::GestureEvent const& event); nux::GestureDeliveryRequest WaitingSwitcherManipulation(nux::GestureEvent const& event); nux::GestureDeliveryRequest DraggingSwitcher(nux::GestureEvent const& event); nux::GestureDeliveryRequest RecognizingMouseClickOrDrag(nux::GestureEvent const& event); nux::GestureDeliveryRequest DraggingSwitcherWithMouse(nux::GestureEvent const& event); void ProcessSwitcherViewMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); void ProcessSwitcherViewMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); void ProcessSwitcherViewMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void ConnectToSwitcherViewMouseEvents(); enum class State { WaitingCompoundGesture, WaitingEndOfTapAndHold, WaitingSwitcherManipulation, DraggingSwitcher, RecognizingMouseClickOrDrag, DraggingSwitcherWithMouse, WaitingMandatorySwitcherClose, } state; unity::UnityScreen* unity_screen; unity::switcher::Controller::Ptr switcher_controller; CompoundGestureRecognizer gesture_recognizer; CompTimer timer_close_switcher; float accumulated_horizontal_drag; int index_icon_hit; connection::Manager connections_; }; } /////////////////////////////////////////// // private class GesturalWindowSwitcherPrivate::GesturalWindowSwitcherPrivate() : accumulated_horizontal_drag(0.0f) { state = State::WaitingCompoundGesture; unity_screen = unity::UnityScreen::get(screen); switcher_controller = unity_screen->switcher_controller(); timer_close_switcher.setCallback( boost::bind(&GesturalWindowSwitcherPrivate::OnCloseSwitcherTimeout, this)); connections_.Add(switcher_controller->ConnectToViewBuilt( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ConnectToSwitcherViewMouseEvents))); } GestureDeliveryRequest GesturalWindowSwitcherPrivate::GestureEvent(nux::GestureEvent const& event) { if (unity_screen->lockscreen_controller()->IsLocked()) return GestureDeliveryRequest::NONE; switch (state) { case State::WaitingCompoundGesture: return WaitingCompoundGesture(event); break; case State::WaitingEndOfTapAndHold: return WaitingEndOfTapAndHold(event); break; case State::WaitingSwitcherManipulation: return WaitingSwitcherManipulation(event); break; case State::DraggingSwitcher: return DraggingSwitcher(event); break; case State::RecognizingMouseClickOrDrag: return RecognizingMouseClickOrDrag(event); break; case State::DraggingSwitcherWithMouse: return DraggingSwitcherWithMouse(event); break; case State::WaitingMandatorySwitcherClose: // do nothing return GestureDeliveryRequest::NONE; break; default: g_assert(false); // should never happen return GestureDeliveryRequest::NONE; break; } } GestureDeliveryRequest GesturalWindowSwitcherPrivate::WaitingCompoundGesture(nux::GestureEvent const& event) { GestureDeliveryRequest request = GestureDeliveryRequest::NONE; switch (gesture_recognizer.GestureEvent(event)) { case RecognitionResult::NONE: // Do nothing; break; case RecognitionResult::DOUBLE_TAP_RECOGNIZED: InitiateSwitcherNext(); CloseSwitcherAfterTimeout(GesturalWindowSwitcher::SWITCHER_TIME_AFTER_DOUBLE_TAP); break; default: // RecognitionResult::TAP_AND_HOLD_RECOGNIZED: InitiateSwitcherNext(); request = GestureDeliveryRequest::EXCLUSIVITY; state = State::WaitingEndOfTapAndHold; } return request; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::WaitingEndOfTapAndHold(nux::GestureEvent const& event) { GestureDeliveryRequest request = GestureDeliveryRequest::NONE; if (event.type == EVENT_GESTURE_BEGIN) { LOG_ERROR(logger) << "There should be no simultaneous/overlapping gestures."; return request; } if (event.type == EVENT_GESTURE_UPDATE) { if (event.GetGestureClasses() & nux::DRAG_GESTURE) { state = State::DraggingSwitcher; accumulated_horizontal_drag = 0.0f; request = DraggingSwitcher(event); } } else // event.type == EVENT_GESTURE_END { CloseSwitcherAfterTimeout(GesturalWindowSwitcher::SWITCHER_TIME_AFTER_HOLD_RELEASED); state = State::WaitingSwitcherManipulation; } return request; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::WaitingSwitcherManipulation(nux::GestureEvent const& event) { GestureDeliveryRequest request = GestureDeliveryRequest::NONE; if (event.type == EVENT_GESTURE_BEGIN) { // Don't leak gestures to windows behind the switcher request = GestureDeliveryRequest::EXCLUSIVITY; } if (event.GetGestureClasses() & nux::DRAG_GESTURE) { state = State::DraggingSwitcher; timer_close_switcher.stop(); DraggingSwitcher(event); } return request; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::DraggingSwitcher(nux::GestureEvent const& event) { if (event.type == EVENT_GESTURE_BEGIN) { LOG_ERROR(logger) << "There should be no simultaneous/overlapping gestures."; return GestureDeliveryRequest::NONE; } if (!(event.GetGestureClasses() & nux::DRAG_GESTURE)) { LOG_ERROR(logger) << "Didn't get the expected drag gesture."; return GestureDeliveryRequest::NONE; } if (event.type == EVENT_GESTURE_UPDATE) { accumulated_horizontal_drag += event.GetDelta().x; ProcessAccumulatedHorizontalDrag(); } else // event.type == EVENT_GESTURE_END { CloseSwitcher(); state = State::WaitingCompoundGesture; } return GestureDeliveryRequest::NONE; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::RecognizingMouseClickOrDrag(nux::GestureEvent const& event) { // Mouse press event has come but gestures have precedence over mouse // for interacting with the switcher. // Therefore act in the same way is if the mouse press didn't come return WaitingSwitcherManipulation(event); } GestureDeliveryRequest GesturalWindowSwitcherPrivate::DraggingSwitcherWithMouse(nux::GestureEvent const& event) { // Mouse press event has come but gestures have precedence over mouse // for interacting with the switcher. // Therefore act in the same way is if the mouse press didn't come return WaitingSwitcherManipulation(event); } void GesturalWindowSwitcherPrivate::CloseSwitcherAfterTimeout(int timeout) { timer_close_switcher.stop(); // min and max timeouts timer_close_switcher.setTimes(timeout, timeout+50); timer_close_switcher.start(); } bool GesturalWindowSwitcherPrivate::OnCloseSwitcherTimeout() { switch (state) { case State::WaitingSwitcherManipulation: case State::WaitingMandatorySwitcherClose: state = State::WaitingCompoundGesture; CloseSwitcher(); break; default: CloseSwitcher(); } // I'm assuming that by returning false I'm telling the timer to stop. return false; } void GesturalWindowSwitcherPrivate::CloseSwitcher() { switcher_controller->Hide(); } void GesturalWindowSwitcherPrivate::InitiateSwitcherNext() { timer_close_switcher.stop(); if (switcher_controller->Visible()) switcher_controller->Next(); else { unity_screen->SetUpAndShowSwitcher(); } } void GesturalWindowSwitcherPrivate::InitiateSwitcherPrevious() { timer_close_switcher.stop(); if (switcher_controller->Visible()) { switcher_controller->Prev(); } } void GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) { if (state != State::WaitingSwitcherManipulation) return; // Don't close the switcher while the mouse is pressed over it timer_close_switcher.stop(); state = State::RecognizingMouseClickOrDrag; auto view = switcher_controller->GetView(); index_icon_hit = view->IconIndexAt(x, y); accumulated_horizontal_drag = 0.0f; } void GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) { switch (state) { case State::RecognizingMouseClickOrDrag: if (index_icon_hit >= 0) { // it was a click after all. switcher_controller->Select(index_icon_hit); // it was not a double tap gesture but we use the same timeout CloseSwitcherAfterTimeout(GesturalWindowSwitcher::SWITCHER_TIME_AFTER_DOUBLE_TAP); state = State::WaitingMandatorySwitcherClose; } else { CloseSwitcher(); state = State::WaitingCompoundGesture; } break; case State::DraggingSwitcherWithMouse: CloseSwitcher(); state = State::WaitingCompoundGesture; break; default: // do nothing break; } } void GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { switch (state) { case State::RecognizingMouseClickOrDrag: accumulated_horizontal_drag += dx; if (fabsf(accumulated_horizontal_drag) >= GesturalWindowSwitcher::MOUSE_DRAG_THRESHOLD) { state = State::DraggingSwitcherWithMouse; ProcessAccumulatedHorizontalDrag(); } break; case State::DraggingSwitcherWithMouse: accumulated_horizontal_drag += dx; ProcessAccumulatedHorizontalDrag(); break; default: // do nothing break; } } void GesturalWindowSwitcherPrivate::ProcessAccumulatedHorizontalDrag() { if (accumulated_horizontal_drag >= GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION) { InitiateSwitcherNext(); accumulated_horizontal_drag = 0.0f; } else if (accumulated_horizontal_drag <= -GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION) { InitiateSwitcherPrevious(); accumulated_horizontal_drag = 0.0f; } } void GesturalWindowSwitcherPrivate::ConnectToSwitcherViewMouseEvents() { auto switcher_view = switcher_controller->GetView(); g_assert(switcher_view); connections_.Add(switcher_view->mouse_down.connect( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDown))); connections_.Add(switcher_view->mouse_up.connect( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseUp))); connections_.Add(switcher_view->mouse_drag.connect( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDrag))); } /////////////////////////////////////////// // public class GesturalWindowSwitcher::GesturalWindowSwitcher() : p(new GesturalWindowSwitcherPrivate) { } GesturalWindowSwitcher::~GesturalWindowSwitcher() { delete p; } GestureDeliveryRequest GesturalWindowSwitcher::GestureEvent(nux::GestureEvent const& event) { return p->GestureEvent(event); } ./plugins/unityshell/src/UnityshellPrivate.h0000644000004100000410000000233113437202764021566 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_UNITYSHELL_PRIVATE_H #define UNITYSHELL_UNITYSHELL_PRIVATE_H #include namespace unity { namespace impl { enum class ActionModifiers { NONE = 0, USE_NUMPAD, USE_SHIFT, USE_SHIFT_NUMPAD }; std::string CreateActionString(std::string const& modifiers, char shortcut, ActionModifiers flag = ActionModifiers::NONE); } // namespace impl } // namespace unity #endif // UNITYSHELL_UNITYSHELL_PRIVATE_H ./plugins/unityshell/src/CompoundGestureRecognizer.cpp0000644000004100000410000001511113437202764023601 0ustar www-datawww-data/* * CompoundGestureRecognizer.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel d'Andrada */ #include "CompoundGestureRecognizer.h" #include #include DECLARE_LOGGER(logger, "unity.gesture.recognizer"); namespace unity { class CompoundGestureRecognizerPrivate { public: CompoundGestureRecognizerPrivate(); enum class State { WaitingFirstTapBegin, WaitingFirstTapEnd, WaitingSecondGestureBegin, RecognizingSecondGesture }; RecognitionResult GestureEvent(nux::GestureEvent const& event); RecognitionResult WaitingFirstTapBegin(nux::GestureEvent const& event); RecognitionResult WaitingFirstTapEnd(nux::GestureEvent const& event); RecognitionResult WaitingSecondGestureBegin(nux::GestureEvent const& event); RecognitionResult RecognizingSecondGesture(nux::GestureEvent const& event); void ResetStateMachine(); State state; class GestureInfo { public: GestureInfo() {Clear();} int begin_time; int end_time; int id; int Duration() const {return end_time - begin_time;} void Clear() {begin_time = end_time = id = -1;} }; GestureInfo first_gesture; GestureInfo second_gesture; }; } using namespace unity; /////////////////////////////////////////// // private class CompoundGestureRecognizerPrivate::CompoundGestureRecognizerPrivate() : state(State::WaitingFirstTapBegin) { } RecognitionResult CompoundGestureRecognizerPrivate::GestureEvent(nux::GestureEvent const& event) { switch (state) { case State::WaitingFirstTapBegin: return WaitingFirstTapBegin(event); break; case State::WaitingFirstTapEnd: return WaitingFirstTapEnd(event); break; case State::WaitingSecondGestureBegin: return WaitingSecondGestureBegin(event); break; default: // State::RecognizingSecondGesture: return RecognizingSecondGesture(event); } } RecognitionResult CompoundGestureRecognizerPrivate::WaitingFirstTapBegin(nux::GestureEvent const& event) { if (event.type == nux::EVENT_GESTURE_BEGIN) { first_gesture.id = event.GetGestureId(); first_gesture.begin_time = event.GetTimestamp(); state = State::WaitingFirstTapEnd; } return RecognitionResult::NONE; } RecognitionResult CompoundGestureRecognizerPrivate::WaitingFirstTapEnd(nux::GestureEvent const& event) { if (event.type != nux::EVENT_GESTURE_END) return RecognitionResult::NONE; if (first_gesture.id != event.GetGestureId()) { ResetStateMachine(); return RecognitionResult::NONE; } if (event.GetGestureClasses() != nux::TOUCH_GESTURE) { // some other gesture class such as drag or pinch was also recognized, // meaning that the touch points moved too much and therefore it cannot // be a tap. ResetStateMachine(); return RecognitionResult::NONE; } first_gesture.end_time = event.GetTimestamp(); if (first_gesture.Duration() > CompoundGestureRecognizer::MAX_TAP_TIME) { // can't be a tap. it took too long ResetStateMachine(); return RecognitionResult::NONE; } state = State::WaitingSecondGestureBegin; return RecognitionResult::NONE; } RecognitionResult CompoundGestureRecognizerPrivate::WaitingSecondGestureBegin( nux::GestureEvent const& event) { if (event.type != nux::EVENT_GESTURE_BEGIN) { // that's not right. there shouldn't be any ongoing gesture now. ResetStateMachine(); return RecognitionResult::NONE; } if (event.GetGestureClasses() != nux::TOUCH_GESTURE) { ResetStateMachine(); return RecognitionResult::NONE; } int interval = event.GetTimestamp() - first_gesture.end_time; if (interval > CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES) { ResetStateMachine(); // consider it as the possible first tap of a new compound gesture GestureEvent(event); return RecognitionResult::NONE; } second_gesture.id = event.GetGestureId(); second_gesture.begin_time = event.GetTimestamp(); state = State::RecognizingSecondGesture; return RecognitionResult::NONE; } RecognitionResult CompoundGestureRecognizerPrivate::RecognizingSecondGesture( nux::GestureEvent const& event) { if (event.GetGestureId() != second_gesture.id) { // no simultaneous gestures ResetStateMachine(); return RecognitionResult::NONE; } if (event.GetGestureClasses() != nux::TOUCH_GESTURE) { // some other gesture class such as drag or pinch was also recognized, // meaning that the touch points moved too much and therefore it cannot // be a tap or a hold ResetStateMachine(); return RecognitionResult::NONE; } RecognitionResult result = RecognitionResult::NONE; if (event.type == nux::EVENT_GESTURE_UPDATE) { if (event.GetTimestamp() - second_gesture.begin_time >= CompoundGestureRecognizer::HOLD_TIME) { result = RecognitionResult::TAP_AND_HOLD_RECOGNIZED; ResetStateMachine(); } } else if (event.type == nux::EVENT_GESTURE_END) { second_gesture.end_time = event.GetTimestamp(); if (second_gesture.Duration() <= CompoundGestureRecognizer::MAX_TAP_TIME) { result = RecognitionResult::DOUBLE_TAP_RECOGNIZED; } ResetStateMachine(); } else { // This really shouldn't happen. LOG_ERROR(logger) << "Unexpected gesture type." " CompoundGestureRecognizer left in an undefined state."; } return result; } void CompoundGestureRecognizerPrivate::ResetStateMachine() { first_gesture.Clear(); second_gesture.Clear(); state = State::WaitingFirstTapBegin; } /////////////////////////////////////////// // public class CompoundGestureRecognizer::CompoundGestureRecognizer() : p(new CompoundGestureRecognizerPrivate) { } CompoundGestureRecognizer::~CompoundGestureRecognizer() { delete p; } RecognitionResult CompoundGestureRecognizer::GestureEvent(nux::GestureEvent const& event) { return p->GestureEvent(event); } ./plugins/unityshell/src/UnityGestureTarget.h0000644000004100000410000000246013437202764021714 0ustar www-datawww-data/* * UnityGestureTarget.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #ifndef UNITY_GESTURE_TARGET_H #define UNITY_GESTURE_TARGET_H #include /* Target for Unity-level gestures. I.e., for gestures that act on Unity elements, such as the dash or launcher. */ class UnityGestureTarget : public nux::GestureTarget { public: UnityGestureTarget(); virtual ~UnityGestureTarget() {} virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event); private: nux::ObjectWeakPtr launcher; }; #endif // UNITY_GESTURE_TARGET_H ./plugins/unityshell/src/unityshell_glow.h0000644000004100000410000000340513437202764021326 0ustar www-datawww-data/** * Copyright : (C) 2006-2012 by Patrick Niklaus, Roi Cohen, * Danny Baumann, Sam Spilsbury * Authors: Patrick Niklaus * Roi Cohen * Danny Baumann * Sam Spilsbury * Marco Trevisan * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * **/ #ifndef _UNITYSHELL_GLOW_H #define _UNITYSHELL_GLOW_H #include #include namespace unity { namespace glow { enum class QuadPos { TOPLEFT = 0, TOPRIGHT, BOTTOMLEFT, BOTTOMRIGHT, TOP, BOTTOM, LEFT, RIGHT, LAST }; struct Quads { /* Each glow quad contains a 2x2 scale + positional matrix * (the 3rd column is not used since that is for matrix skew * operations which we do not care about) * and also a CompRect which describes the size and position of * the quad on the glow */ struct Quad { CompRect box; GLTexture::Matrix matrix; }; Quad& operator[](QuadPos position) { return inner_vector_[unsigned(position)]; } Quad const& operator[](QuadPos position) const { return inner_vector_[unsigned(position)]; } private: Quad inner_vector_[unsigned(QuadPos::LAST)]; }; } // namespace glow } // namepsace unity #endif ./plugins/unityshell/src/transientfor.cpp0000644000004100000410000001374713437202764021161 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "transientfor.h" Atom compiz::X11TransientForReader::wmTransientFor = None; Atom compiz::X11TransientForReader::wmClientLeader = None; namespace compiz { class PrivateX11TransientForReader { public: PrivateX11TransientForReader () {}; Window mXid; Display *mDpy; }; } unsigned int compiz::X11TransientForReader::getAncestor () { Window serverAncestor = None; unsigned long nItems, nLeft; int actualFormat; Atom actualType; void *prop; if (XGetWindowProperty (priv->mDpy, priv->mXid, wmTransientFor, 0L, 2L, false, XA_WINDOW, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_WINDOW && actualFormat == 32 && nLeft == 0 && nItems == 1) { Window *data = static_cast (prop); serverAncestor = *data; } XFree (prop); } return serverAncestor; } bool compiz::X11TransientForReader::isTransientFor (unsigned int ancestor) { if (!ancestor || !priv->mXid) return false; return ancestor == getAncestor (); } bool compiz::X11TransientForReader::isGroupTransientFor (unsigned int clientLeader) { Window serverClientLeader = None; Window ancestor = getAncestor (); unsigned long nItems, nLeft; int actualFormat; Atom actualType; void *prop; std::vector strings; std::list atoms; if (!clientLeader || !priv->mXid) if (XGetWindowProperty (priv->mDpy, priv->mXid, wmClientLeader, 0L, 2L, false, XA_WINDOW, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_WINDOW && actualFormat == 32 && nLeft == 0 && nItems == 1) { Window *data = static_cast (prop); serverClientLeader = *data; } XFree (prop); } /* Check if the returned client leader matches * the requested one */ if (serverClientLeader == clientLeader && clientLeader != priv->mXid) { if (ancestor == None || ancestor == DefaultRootWindow (priv->mDpy)) { Atom wmWindowType = XInternAtom (priv->mDpy, "_NET_WM_WINDOW_TYPE", 0); /* We need to get some common type strings */ strings.push_back ("_NET_WM_WINDOW_TYPE_UTILITY"); strings.push_back ("_NET_WM_WINDOW_TYPE_TOOLBAR"); strings.push_back ("_NET_WM_WINDOW_TYPE_MENU"); strings.push_back ("_NET_WM_WINDOW_TYPE_DIALOG"); for (std::string &s : strings) { atoms.push_back (XInternAtom (priv->mDpy, s.c_str (), 0)); } const unsigned int atomsSize = atoms.size (); /* Now get the window type and check to see if this is a type that we * should consider to be part of a window group by this client leader */ if (XGetWindowProperty (priv->mDpy, priv->mXid, wmWindowType, 0L, 15L, false, XA_ATOM, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_ATOM && actualFormat == 32 && nLeft == 0 && nItems) { Atom *data = static_cast (prop); while (nItems--) { atoms.remove (*data++); } } } /* Means something was found */ if (atomsSize != atoms.size ()) return true; } } return false; } std::vector compiz::X11TransientForReader::getTransients () { unsigned long nItems, nLeft; int actualFormat; Atom actualType; Atom wmClientList; void *prop; std::vector transients; std::vector clientList; wmClientList = XInternAtom (priv->mDpy, "_NET_CLIENT_LIST", 0); if (XGetWindowProperty (priv->mDpy, DefaultRootWindow (priv->mDpy), wmClientList, 0L, 512L, false, XA_WINDOW, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_WINDOW && actualFormat == 32 && nItems && !nLeft) { Window *data = static_cast (prop); while (nItems--) { clientList.push_back (*data++); } } XFree (prop); } /* Now check all the windows in this client list * for the transient state (note that this won't * work for override redirect windows since there's * no concept of a client list for override redirect * windows, but it doesn't matter anyways in this * [external] case) */ for (Window &w : clientList) { X11TransientForReader *reader = new X11TransientForReader (priv->mDpy, w); if (reader->isTransientFor (priv->mXid) || reader->isGroupTransientFor (priv->mXid)) transients.push_back (w); delete reader; } return transients; } compiz::X11TransientForReader::X11TransientForReader (Display *dpy, Window xid) { priv = new PrivateX11TransientForReader (); priv->mXid = xid; priv->mDpy = dpy; if (!wmTransientFor) wmTransientFor = XInternAtom (dpy, "WM_TRANSIENT_FOR", 0); if (!wmClientLeader) wmClientLeader = XInternAtom (dpy, "WM_CLIENT_LEADER", 0); } compiz::X11TransientForReader::~X11TransientForReader () { delete priv; } ./plugins/unityshell/src/UnityGestureTarget.cpp0000644000004100000410000000342313437202764022247 0ustar www-datawww-data/* * UnityGestureTarget.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "UnityGestureTarget.h" #include // otherwise unityshell.h inclusion will cause failures #include "unityshell.h" #include "Launcher.h" #include "UBusMessages.h" #include "UBusWrapper.h" using namespace nux; UnityGestureTarget::UnityGestureTarget() { launcher = &unity::UnityScreen::get(screen)->launcher_controller()->launcher(); } GestureDeliveryRequest UnityGestureTarget::GestureEvent(const nux::GestureEvent &event) { if (UnityScreen::get(screen)->lockscreen_controller()->IsLocked()) return GestureDeliveryRequest::NONE; if (event.GetGestureClasses() & DRAG_GESTURE) { if (launcher.IsValid()) launcher->GestureEvent(event); } else if (event.GetGestureClasses() == TAP_GESTURE && event.type == EVENT_GESTURE_END) { UBusManager::SendMessage(UBUS_DASH_ABOUT_TO_SHOW); UBusManager::SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::NOT_HANDLED, "")); } return GestureDeliveryRequest::NONE; } ./plugins/unityshell/unityshell.xml.in0000644000004100000410000006613713437202764020500 0ustar www-datawww-data <_short>Ubuntu Unity Plugin <_long>Plugin to draw the Unity Shell Desktop decorations fadetodesktop bailer detection composite opengl mousepoll move resize compiztoolbox place session animation regex cube rotate cubeaddon vpswitch fade staticswitcher scale expo ezoom wall showrepaint opengl compiztoolbox scale expo move resize decor scalefilter gnomecompat <_short>General <_short>Decorations <_short>Launcher <_short>Menus <_short>Switcher ./plugins/unityshell/plugin-unityshell.png0000644000004100000410000001235613437202764021345 0ustar www-datawww-dataPNG  IHDR@@nAHsRGBbKGDC pHYs  tIME ;tEXtCommentCreated with GIMPWIIDATx[y|e~Η(T"4 ue@7eFq06IۤI)mB閔IuDFpEdD~֤͂ t!ywMަ{gysksU7*nPfD#7]B-7[(H Hj{[2YH5ŬVT"eRȫt?S;70%ɫyW$b@;`@wѶ1#t9ﴞ3==y(#EG+!2 *tCWUGy*f Fy4ej7䅇ʃIZBqu_R(L1}j]I.ܘd;6U5.S@H5[VhtV5"U8_Y-~4ӄ2>G':im]ZR1ʺ޵\=0ɳ")I(ub(fkjL E&N0l"J 3ɜPt27MN dc~bD;gv7۹6U5%ꮚ) ,ٲ2?]dPe !רN+VvVRP( BHA{>IhͩzB V}5WΜ@KE-_Z$Փg&n'qX0I2#TUĸY_bI JҰuIöڰ}aJ=(NQ5R(qH@Tukj>RPq4xPRc,obUYSaP kh,`^{SO*xM:pw&T4`|IED*R9gTuGIsg[Ƃe(#q΢/ JJK7c_ {cϟވRXR~j`/;P^]%R!J# %B u]?)rxjN\{CU \k;6W?lUMtfUlt^zʶhݕMAp̆`Ha0}h@^e7Gi1 ')Hak8A빓^P|橇lȐEBgubc]Mln%7.<=,Ht="`;͈8h l:8:rsmNPu~8!\ d,~sV 9SfyQ`Ɣ?*w!KbQ%ʹeQ12 :iNƩKHb}Ŝ3X}H1)BHtgo=7v,( =1$N䤹g3I#Aٸ1NTԖ2^k3v{^7YhIf@VQ_gzZ0 gEųz[0118o헽k;庶Tщ|,v!~X\ `ϫ;|Ɉp7Rs6o7~-@XN? 2ㄘY !!Z$| z L:DR%FIC&\@_{l|XJPo?ػko"!-c,<,i!8,q%w^_ĴT)?S&k6U07]# ϊ|%1>aVݶNXvMFY2LbMj!izĊJ"q $3k+F#x(:waD>tcdH> R{s Ls(UČPAkV0C2 > StS.= x?=1~CΆp9Ǘ:}r <gJ##x͓˰HE϶¢Ru @@!AF'xׇߋ) DUQ5xPn^߿FZo,v@I *å-|vB ( n,YJVD438Gbhxۻ UU&U~%&K'Ladzl18t)Z8=mfKky&d" \K8l]$9z9fv5K>"Rbg-B6WoW讚\I nIȠ " X["— ,Im3(Ć'HYTe b 5QQ<L$#]A:!SPe=\r2wz(9JAf"`0n ,m9ĦQRp{W50W4*>AXR; l*TC2= =\t+P!=El*RJX3Mgʮ1>ѷs4K2 6ٖ]ѳꑑ=#48Q~XFYe. e g&}dQ\zwTmʹ[j@Gn1]o: GY2r٢M0R(wW͛Bo8^Of`m "\/xIcgnmi/(zR&)a aq}Q ebX|3Dcq=aԸmG{mlf݆_rj*O y[^j{R)IԗUEEs]e?-UF BӖ ;fbGcC샏nc׫;0~c=3{[Zrspѭwɭǟ75'G|h:n*ꤴw-oۙFm/,)PH Ցl֤tw)іcK~Nnδ1H-WI_[o ɘ8o4w<[V׶ӭ 5"TX# öX84߶dx 7}pPoP*PD!H;Eh?tvzna-Cf! BtPgilLЌ?l؀Kc< ={| Ƌϯu[ZmܫvKzTLL#+8]Ԡd !qs5(8Nk.aX <8O޻OVCP2z\v^ <@.;!/P# BT雊RfH@':ͱc_1Kڱ=ntxć.]i4mK[qDeTԗH#n1EϙCRY¡tP0{{a䙒>a4n[w(PޤEPץo'ؘ#߄2c8n[66UI$TЍCl)ݶIC_RmM?e! .Fh\Qp)rb?C_Ӌ:Is8v mu鍾QG.~{l[V4|e{0.Zp6 ', ͚Nu#!J#3Qkp$1quP8A9 ѷAF8g#o݂1Fsюtɓ9E(o'٦l(f`_hl,Ƽ`.eWV_9ع|!w'UЬcs I>=?߷v&6SW'qm@= ~M _Lhh۹@aBUEb;]:˚ڵ_IӓIӿڐ6\e9`\\@Fa4B]d J= M5]j隈# !"wן}<~pUwh"[_0kz@kfcaYc"S H`_%F]ft'(]~iɂ.?),+I֥5c\A,WO:n!clbOg.ٕLϝmHAPp`7*jǗ b0$dFlÇ5%k]hp~. W@q d&LbIΑ8N;#f%RR $ŏmchpԚߑ/@$r}. A.>9D+RDIENDB`./plugins/networkarearegion/0000755000004100000410000000000013437202764016462 5ustar www-datawww-data./plugins/networkarearegion/CMakeLists.txt0000644000004100000410000000104213437202764021217 0ustar www-datawww-datafind_package (Compiz REQUIRED) include (CompizPlugin) # Guard against Compiz altering global state. # https://bugs.launchpad.net/compiz/+bug/1096807 if(CMAKE_BUILD_TYPE STREQUAL "") set(revert_compiz TRUE) endif() set (libdir ${CMAKE_INSTALL_LIBDIR}) set (includedir ${CMAKE_INSTALL_INCLUDEDIR}) set (libdir ${CMAKE_INSTALL_LIBDIR}) set (datadir ${CMAKE_INSTALL_FULL_DATADIR}) compiz_plugin (networkarearegion) if(revert_compiz) set (CMAKE_BUILD_TYPE "" CACHE STRING "Build type (Debug/Release/RelWithDebInfo/MinSizeRe)" FORCE) endif() ./plugins/networkarearegion/src/0000755000004100000410000000000013437202764017251 5ustar www-datawww-data./plugins/networkarearegion/src/networkarearegion.h0000644000004100000410000000347113437202764023155 0ustar www-datawww-data/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include #include #include #include #include "networkarearegion_options.h" class UnityNETWorkareaRegionScreen : public PluginClassHandler , public ScreenInterface, public NetworkarearegionOptions { public: UnityNETWorkareaRegionScreen(CompScreen*); ~UnityNETWorkareaRegionScreen(); void outputChangeNotify(); void setProperty(); void handleEvent(XEvent* event); void addSupportedAtoms(std::vector &atoms); private: Atom mUnityNETWorkareaRegionAtom; }; class UnityNETWorkareaRegionWindow : public PluginClassHandler , public WindowInterface { public: UnityNETWorkareaRegionWindow(CompWindow*); ~UnityNETWorkareaRegionWindow(); CompWindow* window; void moveNotify(int dx, int dy, bool immediate); void resizeNotify(int dx, int dy, int dwidth, int dheight); }; class UnityNETWorkareaRegionPluginVTable : public CompPlugin::VTableForScreenAndWindow { public: bool init(); }; ./plugins/networkarearegion/src/networkarearegion.cpp0000644000004100000410000001116113437202764023503 0ustar www-datawww-data/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include "networkarearegion.h" COMPIZ_PLUGIN_20090315(networkarearegion, UnityNETWorkareaRegionPluginVTable); void UnityNETWorkareaRegionScreen::setProperty() { CompRegion sr; unsigned long* data; unsigned int dataSize; unsigned int offset = 0; sr = sr.united(CompRect(0, 0, screen->width(), screen->height())); foreach(CompWindow * w, screen->clientList()) { if (!w->struts()) continue; sr -= CompRect(w->struts()->left.x, w->struts()->left.y, w->struts()->left.width, w->struts()->left.height); sr -= CompRect(w->struts()->right.x, w->struts()->right.y, w->struts()->right.width, w->struts()->right.height); sr -= CompRect(w->struts()->top.x, w->struts()->top.y, w->struts()->top.width, w->struts()->top.height); sr -= CompRect(w->struts()->bottom.x, w->struts()->bottom.y, w->struts()->bottom.width, w->struts()->bottom.height); } dataSize = sr.rects().size() * 4; data = new unsigned long[dataSize]; foreach(const CompRect & r, sr.rects()) { data[offset * 4 + 0] = r.x(); data[offset * 4 + 1] = r.y(); data[offset * 4 + 2] = r.width(); data[offset * 4 + 3] = r.height(); offset++; } XChangeProperty(screen->dpy(), screen->root(), mUnityNETWorkareaRegionAtom, XA_CARDINAL, 32, PropModeReplace, (const unsigned char*) data, dataSize); delete[] data; } void UnityNETWorkareaRegionScreen::outputChangeNotify() { setProperty(); screen->outputChangeNotify (); } void UnityNETWorkareaRegionScreen::handleEvent(XEvent* event) { screen->handleEvent(event); switch (event->type) { case PropertyNotify: if (event->xproperty.atom == (Atom) Atoms::wmStrut || event->xproperty.atom == (Atom) Atoms::wmStrutPartial) { CompWindow* w = screen->findWindow(event->xproperty.window); if (w) { if (w->struts()) { UnityNETWorkareaRegionWindow* unwmh = UnityNETWorkareaRegionWindow::get(w); w->moveNotifySetEnabled(unwmh, true); w->resizeNotifySetEnabled(unwmh, true); /* The struts got updated, so we need to set the property again */ setProperty(); } } } } } void UnityNETWorkareaRegionWindow::moveNotify(int dx, int dy, bool immediate) { UnityNETWorkareaRegionScreen::get(screen)->setProperty(); window->moveNotify(dx, dy, immediate); } void UnityNETWorkareaRegionWindow::resizeNotify(int dx, int dy, int dwidth, int dheight) { UnityNETWorkareaRegionScreen::get(screen)->setProperty(); window->resizeNotify(dx, dy, dwidth, dheight); } void UnityNETWorkareaRegionScreen::addSupportedAtoms(std::vector &atoms) { atoms.push_back(mUnityNETWorkareaRegionAtom); screen->addSupportedAtoms(atoms); } UnityNETWorkareaRegionScreen::UnityNETWorkareaRegionScreen(CompScreen* s) : PluginClassHandler (s), mUnityNETWorkareaRegionAtom(XInternAtom(screen->dpy(), "_UNITY_NET_WORKAREA_REGION", 0)) { ScreenInterface::setHandler(screen); screen->updateSupportedWmHints(); } UnityNETWorkareaRegionScreen::~UnityNETWorkareaRegionScreen() { /* Delete the property and the bit saying we support it */ screen->addSupportedAtomsSetEnabled(this, false); screen->updateSupportedWmHints(); XDeleteProperty(screen->dpy(), screen->root(), mUnityNETWorkareaRegionAtom); } UnityNETWorkareaRegionWindow::UnityNETWorkareaRegionWindow(CompWindow* w) : PluginClassHandler (w), window(w) { if (w->struts()) { UnityNETWorkareaRegionScreen::get(screen)->setProperty(); WindowInterface::setHandler(w, true); } else WindowInterface::setHandler(w, false); } UnityNETWorkareaRegionWindow::~UnityNETWorkareaRegionWindow() { if (window->struts()) UnityNETWorkareaRegionScreen::get(screen)->setProperty(); } bool UnityNETWorkareaRegionPluginVTable::init() { if (!CompPlugin::checkPluginABI("core", CORE_ABIVERSION)) return false; return true; } ./plugins/networkarearegion/networkarearegion.xml.in0000644000004100000410000000057213437202764023343 0ustar www-datawww-data <_short>Unity Scrollbars Support <_long>Support _UNITY_NET_WORKAREA_REGION Utility composite opengl ./guides/0000755000004100000410000000000013437202764012533 5ustar www-datawww-data./guides/CMakeLists.txt0000644000004100000410000000131313437202764015271 0ustar www-datawww-dataPROJECT(guides) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cppguide.html COMMAND xsltproc -o ${CMAKE_CURRENT_BINARY_DIR}/cppguide.html ${CMAKE_CURRENT_SOURCE_DIR}/styleguide.xsl ${CMAKE_CURRENT_SOURCE_DIR}/cppguide.xml DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/styleguide.xsl ${CMAKE_CURRENT_SOURCE_DIR}/cppguide.xml ) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/styleguide.css COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/styleguide.css ${CMAKE_CURRENT_BINARY_DIR}/styleguide.css DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/styleguide.css ) ADD_CUSTOM_TARGET(guides ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/cppguide.html ${CMAKE_CURRENT_BINARY_DIR}/styleguide.css) ./guides/styleguide.xsl0000644000004100000410000011101213437202764015435 0ustar www-datawww-data <xsl:value-of select="@title"/>

Each style point has a summary for which additional information is available by toggling the accompanying arrow button that looks this way: . You may toggle all summaries with the big arrow button:

Toggle all summaries

Parting Words

javascript:ShowHideByName(' ',' ') ?showone=# link
display: inline display: none

Definition:

Pros:

Cons:

Decision:

TODO:


           
           
           
           
             
               
               
             
           
         

           
           
           
           
             
               
               
             
           
         

             
           

                             
                           
Table of Contents
_ ./guides/cppguide.xml0000644000004100000410000050316613437202764015070 0ustar www-datawww-data

Revision 4.1

Tim Penhey
Neil J. Patel
This style guide contains many details that are initially hidden from view. They are marked by the triangle icon, which you see here on your left. Click it now. You should see "Hooray" appear below.

Hooray! Now you know you can expand points to get more details. Alternatively, there's an "expand all" at the top of this document.

As every C++ programmer knows, the language has many powerful features, but this power brings with it complexity, which in turn can make code more bug-prone and harder to read and maintain.

The goal of this guide is to manage this complexity by describing in detail the dos and don'ts of writing C++ code. These rules exist to keep the code base manageable while still allowing coders to use C++ language features productively.

Style, also known as readability, is what we call the conventions that govern our C++ code. The term Style is a bit of a misnomer, since these conventions cover far more than just source file formatting.

One way in which we keep the code base manageable is by enforcing consistency. It is very important that any programmer be able to look at another's code and quickly understand it. Maintaining a uniform style and following conventions means that we can more easily use "pattern-matching" to infer what various symbols are and what invariants are true about them. Creating common, required idioms and patterns makes code much easier to understand. In some cases there might be good arguments for changing certain style rules, but we nonetheless keep things as they are in order to preserve consistency.

Another issue this guide addresses is that of C++ feature bloat. C++ is a huge language with many advanced features. In some cases we constrain, or even ban, use of certain features. We do this to keep code simple and to avoid the various common errors and problems that these features can cause. This guide lists these features and explains why their use is restricted.

Note that this guide is not a C++ tutorial: we assume that the reader is familiar with the language.

In general, every .cpp file should have an associated .h file. There are some common exceptions, such as unittests and small .cpp files containing just a main() function.

Correct use of header files can make a huge difference to the readability, size and performance of your code.

The following rules will guide you through the various pitfalls of using header files.

All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be <PROJECT>_<PATH>_<FILE>_H_.

To guarantee uniqueness, they should be based on the full path in a project's source tree. For example, the file foo/src/bar/baz.h in project foo should have the following guard:

#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_
Don't use an #include when a forward declaration would suffice.

When you include a header file you introduce a dependency that will cause your code to be recompiled whenever the header file changes. If your header file includes other header files, any change to those files will cause any code that includes your header to be recompiled. Therefore, we prefer to minimize includes, particularly includes of header files in other header files.

You can significantly reduce the number of header files you need to include in your own header files by using forward declarations. For example, if your header file uses the File class in ways that do not require access to the declaration of the File class, your header file can just forward declare class File; instead of having to #include "file/base/file.h".

How can we use a class Foo in a header file without access to its definition?

  • We can declare data members of type Foo* or Foo&.
  • We can declare (but not define) functions with arguments, and/or return values, of type Foo. (One exception is if an argument Foo or const Foo& has a non-explicit, one-argument constructor, in which case we need the full definition to support automatic type conversion.)
  • We can declare static data members of type Foo. This is because static data members are defined outside the class definition.

On the other hand, you must include the header file for Foo if your class subclasses Foo or has a data member of type Foo.

Sometimes it makes sense to have pointer (or better, scoped_ptr) members instead of object members. However, this complicates code readability and imposes a performance penalty, so avoid doing this transformation if the only purpose is to minimize includes in header files.

Of course, .cpp files typically do require the definitions of the classes they use, and usually have to include several header files.

If you use a symbol Foo in your source file, you should bring in a definition for Foo yourself, either via an #include or via a forward declaration. Do not depend on the symbol being brought in transitively via headers not directly included. One exception is if Foo is used in myfile.cpp, it's ok to #include (or forward-declare) Foo in myfile.h, instead of myfile.cpp.
Define functions inline only when they are small, say, 10 lines or less. You can declare functions in a way that allows the compiler to expand them inline rather than calling them through the usual function call mechanism. Inlining a function can generate more efficient object code, as long as the inlined function is small. Feel free to inline accessors and mutators, and other short, performance-critical functions. Overuse of inlining can actually make programs slower. Depending on a function's size, inlining it can cause the code size to increase or decrease. Inlining a very small accessor function will usually decrease code size while inlining a very large function can dramatically increase code size. On modern processors smaller code usually runs faster due to better use of the instruction cache.

A decent rule of thumb is to not inline a function if it is more than 10 lines long. Beware of destructors, which are often longer than they appear because of implicit member- and base-destructor calls!

Another useful rule of thumb: it's typically not cost effective to inline functions with loops or switch statements (unless, in the common case, the loop or switch statement is never executed).

It is important to know that functions are not always inlined even if they are declared as such; for example, virtual and recursive functions are not normally inlined. Usually recursive functions should not be inline. The main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators.

You may use file names with a -inl.h suffix to define complex inline functions when needed.

The definition of an inline function needs to be in a header file, so that the compiler has the definition available for inlining at the call sites. However, implementation code properly belongs in .cpp files, and we do not like to have much actual code in .h files unless there is a readability or performance advantage.

If an inline function definition is short, with very little, if any, logic in it, you should put the code in your .h file. For example, accessors and mutators should certainly be inside a class definition. More complex inline functions may also be put in a .h file for the convenience of the implementer and callers, though if this makes the .h file too unwieldy you can instead put that code in a separate -inl.h file. This separates the implementation from the class definition, while still allowing the implementation to be included where necessary.

Another use of -inl.h files is for definitions of function templates. This can be used to keep your template definitions easy to read.

Do not forget that a -inl.h file requires a #define guard just like any other header file.

When defining a function, parameter order is: inputs, then outputs.

Parameters to C/C++ functions are either input to the function, output from the function, or both. Input parameters are usually values or const references, while output and input/output parameters will be non-const pointers. When ordering function parameters, put all input-only parameters before any output parameters. In particular, do not add new parameters to the end of the function just because they are new; place new input-only parameters before the output parameters.

This is not a hard-and-fast rule. Parameters that are both input and output (often classes/structs) muddy the waters, and, as always, consistency with related functions may require you to bend the rule.

Use standard order for readability and to avoid hidden dependencies: C library, C++ library, other libraries' .h, your project's .h.

All of a project's header files should be listed as descendants of the project's source directory without use of UNIX directory shortcuts . (the current directory) or .. (the parent directory). For example, my-awesome-project/src/base/logging.h should be included as

#include "base/logging.h"

In dir/foo.cpp or dir/foo_test.cpp, whose main purpose is to implement or test the stuff in dir2/foo2.h, order your includes as follows:

  1. dir2/foo2.h (preferred location — see details below).
  2. C system files.
  3. C++ system files.
  4. Other libraries' .h files.
  5. Your project's .h files.

The preferred ordering reduces hidden dependencies. We want every header file to be compilable on its own. The easiest way to achieve this is to make sure that every one of them is the first .h file #included in some .cpp.

dir/foo.cpp and dir2/foo2.h are often in the same directory (e.g. base/basictypes_test.cpp and base/basictypes.h), but can be in different directories too.

Within each section it is nice to order the includes alphabetically.

For example, the includes in my-awesome-project/src/foo/internal/fooserver.cpp might look like this:

#include "foo/public/fooserver.h" // Preferred location. #include <sys/types.h> #include <unistd.h> #include <hash_map> #include <vector> #include "base/basictypes.h" #include "base/commandlineflags.h" #include "foo/public/bar.h"
Unnamed namespaces in .cpp files are encouraged. With named namespaces, choose the name based on the project, and possibly its path. Do not use a using-directive in a header file. Namespaces subdivide the global scope into distinct, named scopes, and so are useful for preventing name collisions in the global scope.

Namespaces provide a (hierarchical) axis of naming, in addition to the (also hierarchical) name axis provided by classes.

For example, if two different projects have a class Foo in the global scope, these symbols may collide at compile time or at runtime. If each project places their code in a namespace, project1::Foo and project2::Foo are now distinct symbols that do not collide.

Namespaces can be confusing, because they provide an additional (hierarchical) axis of naming, in addition to the (also hierarchical) name axis provided by classes.

Use of unnamed spaces in header files can easily cause violations of the C++ One Definition Rule (ODR).

Use namespaces according to the policy described below.

  • Unnamed namespaces are allowed and even encouraged in .cpp files, to avoid runtime naming conflicts: namespace // This is in a .cpp file. { // The content of a namespace is not indented enum { UNUSED, EOF, ERROR }; // Commonly used tokens. bool AtEof() { return pos_ == EOF; } // Uses our namespace's EOF. } // namespace

    However, file-scope declarations that are associated with a particular class may be declared in that class as types, static data members or static member functions rather than as members of an unnamed namespace. Terminate the unnamed namespace as shown, with a comment // namespace.

  • Do not use unnamed namespaces in .h files.

Named namespaces should be used as follows:

  • Namespaces wrap the entire source file after includes, gflags definitions/declarations, and forward declarations of classes from other namespaces: // In the .h file namespace mynamespace { // All declarations are within the namespace scope. // Notice the lack of indentation. class MyClass { public: ... void Foo(); }; } // namespace mynamespace // In the .cpp file namespace mynamespace { // Definition of functions is within scope of the namespace. void MyClass::Foo() { ... } } // namespace mynamespace

    The typical .cpp file might have more complex detail, including the need to reference classes in other namespaces.

    #include "a.h" DEFINE_bool(someflag, false, "dummy flag"); class C; // Forward declaration of class C in the global namespace. namespace a { class A; } // Forward declaration of a::A. namespace b { ...code for b... // Code goes against the left margin. } // namespace b
  • Do not declare anything in namespace std, not even forward declarations of standard library classes. Declaring entities in namespace std is undefined behavior, i.e., not portable. To declare entities from the standard library, include the appropriate header file.
  • You may use a using-directive to make all names from a namespace available, but only in a source file.
  • You may use a using-declaration anywhere in a .cpp file, and in functions, methods or classes in .h files. // OK in .cpp files. // Must be in a function, method or class in .h files. using ::foo::bar;
  • Namespace aliases are allowed anywhere in a .cpp file, anywhere inside the named namespace that wraps an entire .h file, and in functions and methods. // Shorten access to some commonly used names in .cpp files. namespace fbz = ::foo::bar::baz; // Shorten access to some commonly used names (in a .h file). namespace librarian { // The following alias is available to all files including // this header (in namespace librarian): // alias names should therefore be chosen consistently // within a project. namespace pd_s = ::pipeline_diagnostics::sidetable; inline void my_inline_function() { // namespace alias local to a function (or method). namespace fbz = ::foo::bar::baz; ... } } // namespace librarian

    Note that an alias in a .h file is visible to everyone #including that file, so public headers (those available outside a project) and headers transitively #included by them, should avoid defining aliases, as part of the general goal of keeping public APIs as small as possible.

Although you may use public nested classes when they are part of an interface, consider a namespace to keep declarations out of the global scope. A class can define another class within it; this is also called a member class. class Foo { private: // Bar is a member class, nested within Foo. class Bar { ... }; }; This is useful when the nested (or member) class is only used by the enclosing class; making it a member puts it in the enclosing class scope rather than polluting the outer scope with the class name. Nested classes can be forward declared within the enclosing class and then defined in the .cpp file to avoid including the nested class definition in the enclosing class declaration, since the nested class definition is usually only relevant to the implementation. Nested classes can be forward-declared only within the definition of the enclosing class. Thus, any header file manipulating a Foo::Bar* pointer will have to include the full class declaration for Foo. Do not make nested classes public unless they are actually part of the interface, e.g., a class that holds a set of options for some method. Prefer nonmember functions within a namespace or static member functions to global functions; use completely global functions rarely. Nonmember and static member functions can be useful in some situations. Putting nonmember functions in a namespace avoids polluting the global namespace. Nonmember and static member functions may make more sense as members of a new class, especially if they access external resources or have significant dependencies.

Sometimes it is useful, or even necessary, to define a function not bound to a class instance. Such a function can be either a static member or a nonmember function. Nonmember functions should not depend on external variables, and should nearly always exist in a namespace. Rather than creating classes only to group static member functions which do not share static data, use namespaces instead.

Functions defined in the same compilation unit as production classes may introduce unnecessary coupling and link-time dependencies when directly called from other compilation units; static member functions are particularly susceptible to this. Consider extracting a new class, or placing the functions in a namespace possibly in a separate library.

If you must define a nonmember function and it is only needed in its .cpp file, use an unnamed namespace or static linkage (eg static int Foo() {...}) to limit its scope.

Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.

C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.

int i; i = f(); // Bad -- initialization separate from declaration. int j = g(); // Good -- declaration has initialization.

Note that gcc implements for (int i = 0; i < 10; ++i) correctly (the scope of i is only the scope of the for loop), so you can then reuse i in another for loop in the same scope. It also correctly scopes declarations in if and while statements, e.g.

while (const char* p = strchr(str, '/')) str = p + 1;

There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope.

// Inefficient implementation: for (int i = 0; i < 1000000; ++i) { Foo f; // My ctor and dtor get called 1000000 times each. f.DoSomething(i); }

It may be more efficient to declare such a variable used in a loop outside that loop:

Foo f; // My ctor and dtor get called once each. for (int i = 0; i < 1000000; ++i) { f.DoSomething(i); }
Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.

Objects with static storage duration, including global variables, static variables, static class member variables, and function static variables, must be Plain Old Data (POD): only ints, chars, floats, or pointers, or arrays/structs of POD.

The order in which class constructors and initializers for static variables are called is only partially specified in C++ and can even change from build to build, which can cause bugs that are difficult to find. Therefore in addition to banning globals of class type, we do not allow static POD variables to be initialized with the result of a function, unless that function (such as getenv(), or getpid()) does not itself depend on any other globals.

Likewise, the order in which destructors are called is defined to be the reverse of the order in which the constructors were called. Since constructor order is indeterminate, so is destructor order. For example, at program-end time a static variable might have been destroyed, but code still running -- perhaps in another thread -- tries to access it and fails. Or the destructor for a static 'string' variable might be run prior to the destructor for another variable that contains a reference to that string.

As a result we only allow static variables to contain POD data. This rule completely disallows vector (use C arrays instead), or string (use const char []).

If you need a static or global variable of a class type, consider initializing a pointer (which will never be freed), from either your main() function or from pthread_once(). Note that this must be a raw pointer, not a "smart" pointer, since the smart pointer's destructor will have the order-of-destructor issue that we are trying to avoid.

Classes are the fundamental unit of code in C++. Naturally, we use them extensively. This section lists the main dos and don'ts you should follow when writing a class. In general, constructors should merely set member variables to their initial values. Any complex initialization should go in an explicit Init() method. It is possible to perform initialization in the body of the constructor. Convenience in typing. No need to worry about whether the class has been initialized or not. The problems with doing work in constructors are:
  • There is no easy way for constructors to signal errors, short of using exceptions (which are forbidden).
  • If the work fails, we now have an object whose initialization code failed, so it may be an indeterminate state.
  • If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. Future modification to your class can quietly introduce this problem even if your class is not currently subclassed, causing much confusion.
  • If someone creates a global variable of this type (which is against the rules, but still), the constructor code will be called before main(), possibly breaking some implicit assumptions in the constructor code. For instance, gflags will not yet have been initialized.
If your object requires non-trivial initialization, consider having an explicit Init() method. In particular, constructors should not call virtual functions, attempt to raise errors, access potentially uninitialized global variables, etc.
You must define a default constructor if your class defines member variables and has no other constructors. Otherwise the compiler will do it for you, badly. The default constructor is called when we new a class object with no arguments. It is always called when calling new[] (for arrays). Initializing structures by default, to hold "impossible" values, makes debugging much easier. Extra work for you, the code writer.

If your class defines member variables and has no other constructors you must define a default constructor (one that takes no arguments). It should preferably initialize the object in such a way that its internal state is consistent and valid.

The reason for this is that if you have no other constructors and do not define a default constructor, the compiler will generate one for you. This compiler generated constructor may not initialize your object sensibly.

If your class inherits from an existing class but you add no new member variables, you are not required to have a default constructor.

Use the C++ keyword explicit for constructors with one argument. Normally, if a constructor takes one argument, it can be used as a conversion. For instance, if you define Foo::Foo(string name) and then pass a string to a function that expects a Foo, the constructor will be called to convert the string into a Foo and will pass the Foo to your function for you. This can be convenient but is also a source of trouble when things get converted and new objects created without you meaning them to. Declaring a constructor explicit prevents it from being invoked implicitly as a conversion. Avoids undesirable conversions. None.

We require all single argument constructors to be explicit. Always put explicit in front of one-argument constructors in the class definition: explicit Foo(string name);

The exception is copy constructors, which, in the rare cases when we allow them, should probably not be explicit. Classes that are intended to be transparent wrappers around other classes are also exceptions. Such exceptions should be clearly marked with comments.

Provide a copy constructor and assignment operator only when necessary. Otherwise, disable them with DISALLOW_COPY_AND_ASSIGN. The copy constructor and assignment operator are used to create copies of objects. The copy constructor is implicitly invoked by the compiler in some situations, e.g. passing objects by value. Copy constructors make it easy to copy objects. STL containers require that all contents be copyable and assignable. Copy constructors can be more efficient than CopyFrom()-style workarounds because they combine construction with copying, the compiler can elide them in some contexts, and they make it easier to avoid heap allocation. Implicit copying of objects in C++ is a rich source of bugs and of performance problems. It also reduces readability, as it becomes hard to track which objects are being passed around by value as opposed to by reference, and therefore where changes to an object are reflected.

Few classes need to be copyable. Most should have neither a copy constructor nor an assignment operator. In many situations, a pointer or reference will work just as well as a copied value, with better performance. For example, you can pass function parameters by reference or pointer instead of by value, and you can store pointers rather than objects in an STL container.

If your class needs to be copyable, prefer providing a copy method, such as CopyFrom() or Clone(), rather than a copy constructor, because such methods cannot be invoked implicitly. If a copy method is insufficient in your situation (e.g. for performance reasons, or because your class needs to be stored by value in an STL container), provide both a copy constructor and assignment operator.

If your class does not need a copy constructor or assignment operator, you must explicitly disable them. To do so, add dummy declarations for the copy constructor and assignment operator in the private: section of your class, but do not provide any corresponding definition (so that any attempt to use them results in a link error).

For convenience, a DISALLOW_COPY_AND_ASSIGN macro can be used:

// A macro to disallow the copy constructor and operator= functions // This should be used in the private: declarations for a class #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&)

Then, in class Foo:

class Foo { public: Foo(int f); ~Foo(); private: DISALLOW_COPY_AND_ASSIGN(Foo); };

Use a struct only for passive objects that carry data; everything else is a class.

The struct and class keywords behave almost identically in C++. We add our own semantic meanings to each keyword, so you should use the appropriate keyword for the data-type you're defining.

structs should be used for passive objects that carry data, and may have associated constants, but lack any functionality other than access/setting the data members. The accessing/setting of fields is done by directly accessing the fields rather than through method invocations. Methods should not provide behavior but should only be used to set up the data members, e.g., constructor, destructor, Initialize(), Reset(), Validate().

If more functionality is required, a class is more appropriate. If in doubt, make it a class.

For consistency with STL, you can use struct instead of class for functors and traits.

Note that member variables in structs and classes have different naming rules.

Composition is often more appropriate than inheritance. When using inheritance, make it public. When a sub-class inherits from a base class, it includes the definitions of all the data and operations that the parent base class defines. In practice, inheritance is used in two major ways in C++: implementation inheritance, in which actual code is inherited by the child, and interface inheritance, in which only method names are inherited. Implementation inheritance reduces code size by re-using the base class code as it specializes an existing type. Because inheritance is a compile-time declaration, you and the compiler can understand the operation and detect errors. Interface inheritance can be used to programmatically enforce that a class expose a particular API. Again, the compiler can detect errors, in this case, when a class does not define a necessary method of the API. For implementation inheritance, because the code implementing a sub-class is spread between the base and the sub-class, it can be more difficult to understand an implementation. The sub-class cannot override functions that are not virtual, so the sub-class cannot change implementation. The base class may also define some data members, so that specifies physical layout of the base class.

All inheritance should be public. If you want to do private inheritance, you should be including an instance of the base class as a member instead.

Do not overuse implementation inheritance. Composition is often more appropriate. Try to restrict use of inheritance to the "is-a" case: Bar subclasses Foo if it can reasonably be said that Bar "is a kind of" Foo.

Make your destructor virtual if necessary. If your class has virtual methods, its destructor should be virtual.

Limit the use of protected to those member functions that might need to be accessed from subclasses. Note that data members should be private.

When redefining an inherited virtual function, explicitly declare it virtual in the declaration of the derived class. Rationale: If virtual is omitted, the reader has to check all ancestors of the class in question to determine if the function is virtual or not.

Only very rarely is multiple implementation inheritance actually useful. We allow multiple inheritance only when at most one of the base classes has an implementation; all other base classes must be pure interface classes tagged with the Interface suffix. Multiple inheritance allows a sub-class to have more than one base class. We distinguish between base classes that are pure interfaces and those that have an implementation. Multiple implementation inheritance may let you re-use even more code than single inheritance (see Inheritance). Only very rarely is multiple implementation inheritance actually useful. When multiple implementation inheritance seems like the solution, you can usually find a different, more explicit, and cleaner solution. Multiple inheritance is allowed only when all superclasses, with the possible exception of the first one, are pure interfaces. In order to ensure that they remain pure interfaces, they must end with the Interface suffix. There is an exception to this rule on Windows. Classes that satisfy certain conditions are allowed, but not required, to end with an Interface suffix.

A class is a pure interface if it meets the following requirements:

  • It has only public pure virtual ("= 0") methods and static methods (but see below for destructor).
  • It may not have non-static data members.
  • It need not have any constructors defined. If a constructor is provided, it must take no arguments and it must be protected.
  • If it is a subclass, it may only be derived from classes that satisfy these conditions and are tagged with the Interface suffix.

An interface class can never be directly instantiated because of the pure virtual method(s) it declares. To make sure all implementations of the interface can be destroyed correctly, they must also declare a virtual destructor (in an exception to the first rule, this should not be pure). See Stroustrup, The C++ Programming Language, 3rd edition, section 12.4 for details.

Tagging a class with the Interface suffix lets others know that they must not add implemented methods or non static data members. This is particularly important in the case of multiple inheritance. Additionally, the interface concept is already well-understood by Java programmers. The Interface suffix lengthens the class name, which can make it harder to read and understand. Also, the interface property may be considered an implementation detail that shouldn't be exposed to clients. A class may end with Interface only if it meets the above requirements. We do not require the converse, however: classes that meet the above requirements are not required to end with Interface.
Do not overload operators except in rare, special circumstances. A class can define that operators such as + and / operate on the class as if it were a built-in type. Can make code appear more intuitive because a class will behave in the same way as built-in types (such as int). Overloaded operators are more playful names for functions that are less-colorfully named, such as Equals() or Add(). For some template functions to work correctly, you may need to define operators. While operator overloading can make code more intuitive, it has several drawbacks:
  • It can fool our intuition into thinking that expensive operations are cheap, built-in operations.
  • It is much harder to find the call sites for overloaded operators. Searching for Equals() is much easier than searching for relevant invocations of ==.
  • Some operators work on pointers too, making it easy to introduce bugs. Foo + 4 may do one thing, while &Foo + 4 does something totally different. The compiler does not complain for either of these, making this very hard to debug.
Overloading also has surprising ramifications. For instance, if a class overloads unary operator&, it cannot safely be forward-declared.

In general, do not overload operators. The assignment operator (operator=), in particular, is insidious and should be avoided. You can define functions like Equals() and CopyFrom() if you need them. Likewise, avoid the dangerous unary operator& at all costs, if there's any possibility the class might be forward-declared.

However, there may be rare cases where you need to overload an operator to interoperate with templates or "standard" C++ classes (such as operator<<(ostream&, const T&) for logging). These are acceptable if fully justified, but you should try to avoid these whenever possible. In particular, do not overload operator== or operator< just so that your class can be used as a key in an STL container; instead, you should create equality and comparison functor types when declaring the container.

Some of the STL algorithms do require you to overload operator==, and you may do so in these cases, provided you document why.

See also Copy Constructors and Function Overloading.

Make data members private, and provide access to them through accessor functions as needed (for technical reasons, we allow data members of a test fixture class to be protected when using Google Test). Typically a variable would be called foo_ and the accessor function foo(). You may also want a mutator function set_foo(). Exception: static const data members (typically called kFoo) need not be private.

The definitions of accessors are usually inlined in the header file.

See also Inheritance and Function Names.

Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.

Your class definition should start with its public: section, followed by its protected: section and then its private: section. If any of these sections are empty, omit them.

Within each section, the declarations generally should be in the following order:

  • Typedefs and Enums
  • Constants (static const data members)
  • Constructors
  • Destructor
  • Methods, including static methods
  • Data Members (except static const data members)

Friend declarations should always be in the private section, and the DISALLOW_COPY_AND_ASSIGN macro invocation should be at the end of the private: section. It should be the last thing in the class. See Copy Constructors.

Method definitions in the corresponding .cpp file should be the same as the declaration order, as much as possible.

Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be defined inline. See Inline Functions for more details.

Prefer small and focused functions.

We recognize that long functions are sometimes appropriate, so no hard limit is placed on functions length. If a function exceeds about 40 lines, think about whether it can be broken up without harming the structure of the program.

Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code.

You could find long and complicated functions when working with some code. Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces.

There are various tricks and utilities that we use to make C++ code more robust, and various ways we use C++ that may differ from what you see elsewhere.

If you actually need pointer semantics, std::unique_ptr is great. You should only use std::shared_ptr with a non-const referent when it is truly necessary to share ownership of an object (e.g. inside an STL container). You should never use auto_ptr. "Smart" pointers are objects that act like pointers, but automate management of the underlying memory. Smart pointers are extremely useful for preventing memory leaks, and are essential for writing exception-safe code. They also formalize and document the ownership of dynamically allocated memory. We prefer designs in which objects have single, fixed owners. Smart pointers which enable sharing or transfer of ownership can act as a tempting alternative to a careful design of ownership semantics, leading to confusing code and even bugs in which memory is never deleted. The semantics of smart pointers (especially auto_ptr) can be nonobvious and confusing. The exception-safety benefits of smart pointers are not decisive, since we do not allow exceptions.
unique_ptr
Straightforward and risk-free. Use wherever appropriate.
auto_ptr
Confusing and bug-prone ownership-transfer semantics. Do not use.
shared_ptr
Safe with const referents (i.e. shared_ptr<const T>). Reference-counted pointers with non-const referents can occasionally be the best design, but try to rewrite with single owners where possible.
Use cpplint.py to detect style errors.

cpplint.py is a tool that reads a source file and identifies many style errors. It is not perfect, and has both false positives and false negatives, but it is still a valuable tool. False positives can be ignored by putting // NOLINT at the end of the line.

Some projects have instructions on how to run cpplint.py from their project tools. If the project you are contributing to does not, you can download cpplint.py separately.

Most parameters passed by reference should be labeled const. In C, if a function needs to modify a variable, the parameter must use a pointer, eg int foo(int *pval). In C++, the function can alternatively declare a reference parameter: int foo(int& val). Defining a parameter as reference avoids ugly code like (*pval)++. Necessary for some applications like copy constructors. Makes it clear, unlike with pointers, that NULL is not a possible value. References can be confusing, as they have value syntax but pointer semantics.

Within function parameter lists all references must be const:

void Foo(const string &in, string *out);

In fact it is a very strong convention in Unity code that input arguments are values or const references while output arguments are pointers. Input parameters may be const pointers. Non-const reference parameters are allowed but there must be a valid reason for it, a strong preference is given to const reference parameters.

One case when you might want an input parameter to be a const pointer is if you want to emphasize that the argument is not copied, so it must exist for the lifetime of the object; it is usually best to document this in comments as well. STL adapters such as bind2nd and mem_fun do not permit reference parameters, so you must declare functions with pointer parameters in these cases, too.

Use overloaded functions (including constructors) only if a reader looking at a call site can get a good idea of what is happening without having to first figure out exactly which overload is being called.

You may write a function that takes a string const& and overload it with another that takes const char*.

class MyClass { public: void Analyze(string const& text); void Analyze(const char *text, size_t textlen); };
Overloading can make code more intuitive by allowing an identically-named function to take different arguments. It may be necessary for templatized code, and it can be convenient for Visitors. If a function is overloaded by the argument types alone, a reader may have to understand C++'s complex matching rules in order to tell what's going on. Also many people are confused by the semantics of inheritance if a derived class overrides only some of the variants of a function. If you want to overload a function, consider qualifying the name with some information about the arguments, e.g., AppendString(), AppendInt() rather than just Append().
We do not allow default function parameters, except in a few uncommon situations explained below. Often you have a function that uses lots of default values, but occasionally you want to override the defaults. Default parameters allow an easy way to do this without having to define many functions for the rare exceptions. People often figure out how to use an API by looking at existing code that uses it. Default parameters are more difficult to maintain because copy-and-paste from previous code may not reveal all the parameters. Copy-and-pasting of code segments can cause major problems when the default arguments are not appropriate for the new code.

Except as described below, we require all arguments to be explicitly specified, to force programmers to consider the API and the values they are passing for each argument rather than silently accepting defaults they may not be aware of.

One specific exception is when default arguments are used to simulate variable-length argument lists.

// Support up to 4 params by using a default empty AlphaNum. string StrCat(AlphaNum const& a, AlphaNum const& b = gEmptyAlphaNum, AlphaNum const& c = gEmptyAlphaNum, AlphaNum const& d = gEmptyAlphaNum);
We do not allow variable-length arrays or alloca(). Variable-length arrays have natural-looking syntax. Both variable-length arrays and alloca() are very efficient. Variable-length arrays and alloca are not part of Standard C++. More importantly, they allocate a data-dependent amount of stack space that can trigger difficult-to-find memory overwriting bugs: "It ran fine on my machine, but dies mysteriously in production". Use a safe allocator instead, such as unique_ptr. We allow use of friend classes and functions, within reason.

Friends should usually be defined in the same file so that the reader does not have to look in another file to find uses of the private members of a class. A common use of friend is to have a FooBuilder class be a friend of Foo so that it can construct the inner state of Foo correctly, without exposing this state to the world. In some cases it may be useful to make a unittest class a friend of the class it tests.

Friends extend, but do not break, the encapsulation boundary of a class. In some cases this is better than making a member public when you want to give only one other class access to it. However, most classes should interact with other classes solely through their public members.

We do not use C++ exceptions.
  • Exceptions allow higher levels of an application to decide how to handle "can't happen" failures in deeply nested functions, without the obscuring and error-prone bookkeeping of error codes.
  • Exceptions are used by most other modern languages. Using them in C++ would make it more consistent with Python, Java, and the C++ that others are familiar with.
  • Some third-party C++ libraries use exceptions, and turning them off internally makes it harder to integrate with those libraries.
  • Exceptions are the only way for a constructor to fail. We can simulate this with a factory function or an Init() method, but these require heap allocation or a new "invalid" state, respectively.
  • Exceptions are really handy in testing frameworks.
  • When you add a throw statement to an existing function, you must examine all of its transitive callers. Either they must make at least the basic exception safety guarantee, or they must never catch the exception and be happy with the program terminating as a result. For instance, if f() calls g() calls h(), and h throws an exception that f catches, g has to be careful or it may not clean up properly.
  • More generally, exceptions make the control flow of programs difficult to evaluate by looking at code: functions may return in places you don't expect. This causes maintainability and debugging difficulties. You can minimize this cost via some rules on how and where exceptions can be used, but at the cost of more that a developer needs to know and understand.
  • Exception safety requires both RAII and different coding practices. Lots of supporting machinery is needed to make writing correct exception-safe code easy. Further, to avoid requiring readers to understand the entire call graph, exception-safe code must isolate logic that writes to persistent state into a "commit" phase. This will have both benefits and costs (perhaps where you're forced to obfuscate code to isolate the commit). Allowing exceptions would force us to always pay those costs even when they're not worth it.
  • Turning on exceptions adds data to each binary produced, increasing compile time (probably slightly) and possibly increasing address space pressure.
  • The availability of exceptions may encourage developers to throw them when they are not appropriate or recover from them when it's not safe to do so. For example, invalid user input should not cause exceptions to be thrown. We would need to make the style guide even longer to document these restrictions!

On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code, the introduction of exceptions has implications on all dependent code. If exceptions can be propagated beyond a new project, it also becomes problematic to integrate the new project into existing exception-free code. Because most existing C++ code of Unity is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions.

Given that Unity's existing code is not exception-tolerant, the costs of using exceptions are somewhat greater than the costs in a new project. The conversion process would be slow and error-prone. We don't believe that the available alternatives to exceptions, such as error codes and assertions, introduce a significant burden.

Our advice against using exceptions is not predicated on philosophical or moral grounds, but practical ones. Things would probably be different if we had to do it all over again from scratch.

We do not use Run Time Type Information (RTTI). RTTI allows a programmer to query the C++ class of an object at run time.

It is useful in some unittests. For example, it is useful in tests of factory classes where the test has to verify that a newly created object has the expected dynamic type.

In rare circumstances, it is useful even outside of tests.

A query of type during run-time typically means a design problem. If you need to know the type of an object at runtime, that is often an indication that you should reconsider the design of your class.

Do not use RTTI, except in unittests. If you find yourself in need of writing code that behaves differently based on the class of an object, consider one of the alternatives to querying the type.

Virtual methods are the preferred way of executing different code paths depending on a specific subclass type. This puts the work within the object itself.

If the work belongs outside the object and instead in some processing code, consider a double-dispatch solution, such as the Visitor design pattern. This allows a facility outside the object itself to determine the type of class using the built-in type system.

If you think you truly cannot use those ideas, you may use RTTI. But think twice about it. :-) Then think twice again. Do not hand-implement an RTTI-like workaround. The arguments against RTTI apply just as much to workarounds like class hierarchies with type tags.

Use C++ casts like static_cast<>(). Do not use other cast formats like int y = (int)x; or int y = int(x);. C++ introduced a different cast system from C that distinguishes the types of cast operations. The problem with C casts is the ambiguity of the operation; sometimes you are doing a conversion (e.g., (int)3.5) and sometimes you are doing a cast (e.g., (int)"hello"); C++ casts avoid this. Additionally C++ casts are more visible when searching for them. The syntax is nasty.

Do not use C-style casts. Instead, use these C++-style casts.

  • Use static_cast as the equivalent of a C-style cast that does value conversion, or when you need to explicitly up-cast a pointer from a class to its superclass.
  • Use const_cast to remove the const qualifier (see const).
  • Use reinterpret_cast to do unsafe conversions of pointer types to and from integer and other pointer types. Use this only if you know what you are doing and you understand the aliasing issues.
  • Do not use dynamic_cast except in test code. If you need to know type information at runtime in this way outside of a unittest, you probably have a design flaw.
Use streams only for logging. Streams are a replacement for printf() and scanf(). With streams, you do not need to know the type of the object you are printing. You do not have problems with format strings not matching the argument list. (Though with gcc, you do not have that problem with printf either.) Streams have automatic constructors and destructors that open and close the relevant files. Streams make it difficult to do functionality like pread(). Some formatting (particularly the common format string idiom %.*s) is difficult if not impossible to do efficiently using streams without using printf-like hacks. Streams do not support operator reordering (the %1s directive), which is helpful for internationalization.

Do not use streams, except where required by a logging interface. Use printf-like routines instead.

There are various pros and cons to using streams, but in this case, as in many other cases, consistency trumps the debate. Do not use streams in your code.

There has been debate on this issue, so this explains the reasoning in greater depth. Recall the Only One Way guiding principle: we want to make sure that whenever we do a certain type of I/O, the code looks the same in all those places. Because of this, we do not want to allow users to decide between using streams or using printf plus Read/Write/etc. Instead, we should settle on one or the other. We made an exception for logging because it is a pretty specialized application, and for historical reasons.

Proponents of streams have argued that streams are the obvious choice of the two, but the issue is not actually so clear. For every advantage of streams they point out, there is an equivalent disadvantage. The biggest advantage is that you do not need to know the type of the object to be printing. This is a fair point. But, there is a downside: you can easily use the wrong type, and the compiler will not warn you. It is easy to make this kind of mistake without knowing when using streams.

cout << this; // Prints the address cout << *this; // Prints the contents

The compiler does not generate an error because << has been overloaded. We discourage overloading for just this reason.

Some say printf formatting is ugly and hard to read, but streams are often no better. Consider the following two fragments, both with the same typo. Which is easier to discover?

cerr << "Error connecting to '" << foo->bar()->hostname.first << ":" << foo->bar()->hostname.second << ": " << strerror(errno); fprintf(stderr, "Error connecting to '%s:%u: %s", foo->bar()->hostname.first, foo->bar()->hostname.second, strerror(errno));

And so on and so forth for any issue you might bring up. (You could argue, "Things would be better with the right wrappers," but if it is true for one scheme, is it not also true for the other? Also, remember the goal is to make the language smaller, not add yet more machinery that someone has to learn.)

Either path would yield different advantages and disadvantages, and there is not a clearly superior solution. The simplicity doctrine mandates we settle on one of them though, and the majority decision was on printf + read/write.

Use prefix form (++i) of the increment and decrement operators with iterators and other template objects. When a variable is incremented (++i or i++) or decremented (--i or i--) and the value of the expression is not used, one must decide whether to preincrement (decrement) or postincrement (decrement). When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression. If i is an iterator or other non-scalar type, copying i could be expensive. Since the two types of increment behave the same when the value is ignored, why not just always pre-increment? The tradition developed, in C, of using post-increment when the expression value is not used, especially in for loops. Some find post-increment easier to read, since the "subject" (i) precedes the "verb" (++), just like in English. For simple scalar (non-object) values there is no reason to prefer one form and we allow either. For iterators and other template types, use pre-increment. We strongly recommend that you use const whenever it makes sense to do so. Declared variables and parameters can be preceded by the keyword const to indicate the variables are not changed (e.g., const int foo). Class functions can have the const qualifier to indicate the function does not change the state of the class member variables (e.g., class Foo { int Bar(char c) const; };). Easier for people to understand how variables are being used. Allows the compiler to do better type checking, and, conceivably, generate better code. Helps people convince themselves of program correctness because they know the functions they call are limited in how they can modify your variables. Helps people know what functions are safe to use without locks in multi-threaded programs. const is viral: if you pass a const variable to a function, that function must have const in its prototype (or the variable will need a const_cast). This can be a particular problem when calling library functions.

const variables, data members, methods and arguments add a level of compile-time type checking; it is better to detect errors as soon as possible. Therefore we strongly recommend that you use const whenever it makes sense to do so:

  • If a function does not modify an argument passed by reference or by pointer, that argument should be const.
  • Declare methods to be const whenever possible. Accessors should almost always be const. Other methods should be const if they do not modify any data members, do not call any non-const methods, and do not return a non-const pointer or non-const reference to a data member.
  • Consider making data members const whenever they do not need to be modified after construction.

However, do not go crazy with const. Something like const int * const * const x; is likely overkill, even if it accurately describes how const x is. Focus on what's really useful to know: in this case, const int** x is probably sufficient.

The mutable keyword is allowed but is unsafe when used with threads, so thread safety should be carefully considered first.

We favor the form int const* foo to const int* foo. This keeps the const with the type modifier (& or *).

An exception to this is const char* due to existing C convention.

That said, while we encourage putting const after the type, we do not require it. But be consistent with the code around you!

Of the built-in C++ integer types, the only one used is int. If a program needs a variable of a different size, use a precise-width integer type from <stdint.h>, such as int16_t. C++ does not specify the sizes of its integer types. Typically people assume that short is 16 bits, int is 32 bits, long is 32 bits and long long is 64 bits. Uniformity of declaration. The sizes of integral types in C++ can vary based on compiler and architecture.

<stdint.h> defines types like int16_t, uint32_t, int64_t, etc. You should always use those in preference to short, unsigned long long and the like, when you need a guarantee on the size of an integer. Of the C integer types, only int should be used. When appropriate, you are welcome to use standard types like size_t and ptrdiff_t.

We use int very often, for integers we know are not going to be too big, e.g., loop counters. Use plain old int for such things. You should assume that an int is at least 32 bits, but don't assume that it has more than 32 bits. If you need a 64-bit integer type, use int64_t or uint64_t.

For integers we know can be "big", use int64_t.

You should not use the unsigned integer types such as uint32_t, unless the quantity you are representing is really a bit pattern rather than a number, or unless you need defined twos-complement overflow. In particular, do not use unsigned types to say a number will never be negative. Instead, use assertions for this.

Some people, including some textbook authors, recommend using unsigned types to represent numbers that are never negative. This is intended as a form of self-documentation. However, in C, the advantages of such documentation are outweighed by the real bugs it can introduce. Consider:

for (unsigned int i = foo.Length()-1; i >= 0; --i) ...

This code will never terminate! Sometimes gcc will notice this bug and warn you, but often it will not. Equally bad bugs can occur when comparing signed and unsigned variables. Basically, C's type-promotion scheme causes unsigned types to behave differently than one might expect.

So, document that a variable is non-negative using assertions. Don't use an unsigned type.

Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.
  • printf() specifiers for some types are not cleanly portable between 32-bit and 64-bit systems. C99 defines some portable format specifiers. Unfortunately, MSVC 7.1 does not understand some of these specifiers and the standard is missing a few, so we have to define our own ugly versions in some cases (in the style of the standard include file inttypes.h):

    // printf macros for size_t, in the style of inttypes.h #ifdef _LP64 #define __PRIS_PREFIX "z" #else #define __PRIS_PREFIX #endif // Use these macros after a % in a printf format string // to get correct 32/64 bit behavior, like this: // size_t size = records.size(); // printf("%"PRIuS"\n", size); #define PRIdS __PRIS_PREFIX "d" #define PRIxS __PRIS_PREFIX "x" #define PRIuS __PRIS_PREFIX "u" #define PRIXS __PRIS_PREFIX "X" #define PRIoS __PRIS_PREFIX "o"
    Type DO NOT use DO use Notes
    void * (or any pointer) %lx %p
    int64_t %qd, %lld %"PRId64"
    uint64_t %qu, %llu, %llx %"PRIu64", %"PRIx64"
    size_t %u %"PRIuS", %"PRIxS" C99 specifies %zu
    ptrdiff_t %d %"PRIdS" C99 specifies %zd

    Note that the PRI* macros expand to independent strings which are concatenated by the compiler. Hence if you are using a non-constant format string, you need to insert the value of the macro into the format, rather than the name. It is still possible, as usual, to include length specifiers, etc., after the % when using the PRI* macros. So, e.g. printf("x = %30"PRIuS"\n", x) would expand on 32-bit Linux to printf("x = %30" "u" "\n", x), which the compiler will treat as printf("x = %30u\n", x).

  • Remember that sizeof(void *) != sizeof(int). Use intptr_t if you want a pointer-sized integer.
  • You may need to be careful with structure alignments, particularly for structures being stored on disk. Any class/structure with a int64_t/uint64_t member will by default end up being 8-byte aligned on a 64-bit system. If you have such structures being shared on disk between 32-bit and 64-bit code, you will need to ensure that they are packed the same on both architectures. Most compilers offer a way to alter structure alignment. For gcc, you can use __attribute__((packed)). MSVC offers #pragma pack() and __declspec(align()).
  • Use the LL or ULL suffixes as needed to create 64-bit constants. For example: int64_t my_value = 0x123456789LL; uint64_t my_mask = 3ULL << 48;
  • If you really need different code on 32-bit and 64-bit systems, use #ifdef _LP64 to choose between the code variants. (But please avoid this if possible, and keep any such changes localized.)
Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.

Macros mean that the code you see is not the same as the code the compiler sees. This can introduce unexpected behavior, especially since macros have global scope.

Luckily, macros are not nearly as necessary in C++ as they are in C. Instead of using a macro to inline performance-critical code, use an inline function. Instead of using a macro to store a constant, use a const variable. Instead of using a macro to "abbreviate" a long variable name, use a reference. Instead of using a macro to conditionally compile code ... well, don't do that at all (except, of course, for the #define guards to prevent double inclusion of header files). It makes testing much more difficult.

Macros can do things these other techniques cannot, and you do see them in the codebase, especially in the lower-level libraries. And some of their special features (like stringifying, concatenation, and so forth) are not available through the language proper. But before using a macro, consider carefully whether there's a non-macro way to achieve the same result.

The following usage pattern will avoid many problems with macros; if you use macros, follow it whenever possible:

  • Don't define macros in a .h file.
  • #define macros right before you use them, and #undef them right after.
  • Do not just #undef an existing macro before replacing it with your own; instead, pick a name that's likely to be unique.
  • Try not to use macros that expand to unbalanced C++ constructs, or at least document that behavior well.
  • Prefer not using ## to generate function/class/variable names.
Use 0 for integers, 0.0 for reals, nullptr for pointers, and '\0' for chars.

Use 0 for integers and 0.0 for reals. This is not controversial.

For pointers (address values), C++11 added the nullptr construct. This allows the compiler to do additional checks, and is the preferred NULL pointer value.

Use '\0' for chars. This is the correct type and also makes code more readable.

Use sizeof(varname) instead of sizeof(type) whenever possible.

Use sizeof(varname) because it will update appropriately if the type of the variable changes. sizeof(type) may make sense in some cases, but should generally be avoided because it can fall out of sync if the variable's type changes.

Struct data; memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(Struct));

Use only approved libraries from the Boost library collection. The Boost library collection is a popular collection of peer-reviewed, free, open-source C++ libraries. Boost code is generally very high-quality, is widely portable, and fills many important gaps in the C++ standard library, such as type traits, better binders, and better smart pointers. It also provides an implementation of the TR1 extension to the standard library. Some Boost libraries encourage coding practices which can hamper readability, such as metaprogramming and other advanced template techniques, and an excessively "functional" style of programming.
In order to maintain a high level of readability for all contributors who might read and maintain code, we only allow an approved subset of Boost features. Currently, the following libraries are permitted:
  • Call Traits from boost/call_traits.hpp
  • Compressed Pair from boost/compressed_pair.hpp
  • Pointer Container from boost/ptr_container except serialization and wrappers for containers not in the C++03 standard (ptr_circular_buffer.hpp and ptr_unordered*)
  • Array from boost/array.hpp
  • The Boost Graph Library (BGL) from boost/graph, except serialization (adj_list_serialize.hpp) and parallel/distributed algorithms and data structures (boost/graph/parallel/* and boost/graph/distributed/*).
  • Property Map from boost/property_map, except parallel/distributed property maps (boost/property_map/parallel/*).
  • The part of Iterator that deals with defining iterators: boost/iterator/iterator_adaptor.hpp, boost/iterator/iterator_facade.hpp, and boost/function_output_iterator.hpp
We are actively considering adding other Boost features to the list, so this rule may be relaxed in the future.
Use only approved libraries and language extensions from C++11. C++11 is the current ISO C++ standard. It contains significant changes both to the language and libraries from the older standard. No compiler currently implements the entire new specification. C++11 standardizes some common C++ extensions that we use already, allows shorthands for some operations, and has some safety improvements. Care is needed with some C++11 features that may not be fully implemented.

The range based for is the preferred way to iterate over containers when simple iteration is needed.

The auto keyword is acceptable within reason.

Strong enums are preferred where the integer values are not explicitly needed.

Lambda functions are acceptable within reason.

Use unique_ptr and shared_ptr from <memory>.

The most important consistency rules are those that govern naming. The style of a name immediately informs us what sort of thing the named entity is: a type, a variable, a function, a constant, a macro, etc., without requiring us to search for the declaration of that entity. The pattern-matching engine in our brains relies a great deal on these naming rules.

Naming rules are pretty arbitrary, but we feel that consistency is more important than individual preferences in this area, so regardless of whether you find them sensible or not, the rules are the rules.

Function names, variable names, and filenames should be descriptive; eschew abbreviation. Types and variables should be nouns, while functions should be "command" verbs.

Give as descriptive a name as possible, within reason. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Examples of well-chosen names:

int num_errors; // Good. int num_completed_connections; // Good.

Poorly-chosen names use ambiguous abbreviations or arbitrary characters that do not convey meaning:

int n; // Bad - meaningless. int nerr; // Bad - ambiguous abbreviation. int n_comp_conns; // Bad - ambiguous abbreviation.

Type and variable names should typically be nouns: e.g., FileOpener, num_errors.

Function names should typically be imperative (that is they should be commands): e.g., OpenFile(), set_num_errors(). There is an exception for accessors, which, described more completely in Function Names, should be named the same as the variable they access.

Do not use abbreviations unless they are extremely well known outside your project. For example:

// Good // These show proper names with no abbreviations. int num_dns_connections; // Most people know what "DNS" stands for. int price_count_reader; // OK, price count. Makes sense. // Bad! // Abbreviations can be confusing or ambiguous outside a small group. int wgc_connections; // Only your group knows what this stands for. int pc_reader; // Lots of things can be abbreviated "pc".

Never abbreviate by leaving out letters:

int error_count; // Good. int error_cnt; // Bad.
Filenames should be all lowercase and can include underscores (_) or dashes (-). Follow the convention that your project uses. If there is no consistent local pattern to follow, prefer "_".

Examples of acceptable file names:

my_useful_class.cpp
my-useful-class.cpp
myusefulclass.cpp
myusefulclass_test.cpp // _unittest and _regtest are deprecated.

C++ files should end in .cpp and header files should end in .h.

Do not use filenames that already exist in /usr/include, such as db.h.

In general, make your filenames very specific. For example, use http_server_logs.h rather than logs.h. A very common case is to have a pair of files called, e.g., foo_bar.h and foo_bar.cpp, defining a class called FooBar.

Inline functions must be in a .h file. If your inline functions are very short, they should go directly into your .h file. However, if your inline functions include a lot of code, they may go into a third file that ends in -inl.h. In a class with a lot of inline code, your class could have three files:

url_table.h // The class declaration. url_table.cpp // The class definition. url_table-inl.h // Inline functions that include lots of code.

See also the section -inl.h Files

Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum.

The names of all types — classes, structs, typedefs, and enums — have the same naming convention. Type names should start with a capital letter and have a capital letter for each new word. No underscores. For example:

// classes and structs class UrlTable ... class UrlTableTester ... struct UrlTableProperties ... // typedefs typedef hash_map<UrlTableProperties *, string> PropertiesMap; // enums enum UrlTableErrors ...
Variable names are all lowercase, with underscores between words. Class member variables have trailing underscores. For instance: my_exciting_local_variable, my_exciting_member_variable_.

For example:

string table_name; // OK - uses underscore. string tablename; // OK - all lowercase. string tableName; // Bad - mixed case.

Data members (also called instance variables or member variables) are lowercase with optional underscores like regular variable names, but always end with a trailing underscore.

string table_name_; // OK - underscore at end. string tablename_; // OK.

Data members in structs should be named like regular variables without the trailing underscores that data members in classes have.

struct UrlTableProperties { string name; int num_entries; }

See Structs vs. Classes for a discussion of when to use a struct versus a class.

There are no special requirements for global variables, which should be rare in any case, but if you use one, consider prefixing it with g_ or some other marker to easily distinguish it from local variables.

Use upper-case with underscores between words: DAYS_IN_A_WEEK.

All compile-time constants, whether they are declared locally, globally, or as part of a class, follow a slightly different naming convention from other variables. Use upper-case with underscores.

const int DAYS_IN_A_WEEK = 7;
Regular functions have mixed case; accessors and mutators match the name of the variable: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().

Functions should start with a capital letter and have a capital letter for each new word. No underscores.

If your function crashes upon an error, you should append OrDie to the function name. This only applies to functions which could be used by production code and to errors that are reasonably likely to occur during normal operation.

AddTableEntry() DeleteUrl() OpenFileOrDie()

Accessors and mutators (get and set functions) should match the name of the variable they are getting and setting. This shows an excerpt of a class whose instance variable is num_entries_.

class MyClass { public: ... int num_entries() const { return num_entries_; } void set_num_entries(int num_entries) { num_entries_ = num_entries; } private: int num_entries_; };

You may also use lowercase letters for other very short inlined functions. For example if a function were so cheap you would not cache the value if you were calling it in a loop, then lowercase naming would be acceptable.

Namespace names are all lower-case, and based on project names and possibly their directory structure: my_awesome_project.

See Namespaces for a discussion of namespaces and how to name them.

Enumerators should be named either like constants and macros: ENUM_NAME.

Preferably, the individual enumerators should be named like constants. The enumeration name, UrlTableErrors, is a type, and therefore mixed case.

enum UrlTableErrors { OK, OUT_OF_MEMORY, MALFORMED_INPUT, };
You're not really going to define a macro, are you? If you do, they're like this: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.

Please see the description of macros; in general macros should not be used. However, if they are absolutely needed, then they should be named with all capitals and underscores.

#define ROUND(x) ... #define PI_ROUNDED 3.0
If you are naming something that is analogous to an existing C or C++ entity then you can follow the existing naming convention scheme.

bigopen()
function name, follows form of open()
uint
typedef
bigpos
struct or class, follows form of pos
sparse_hash_map
STL-like entity; follows STL naming conventions
LONGLONG_MAX
a constant, as in INT_MAX

Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.

When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!

Use either the // or /* */ syntax, as long as you are consistent.

You can use either the // or the /* */ syntax; however, // is much more common. Be consistent with how you comment and what style you use where.

Start each file with a copyright notice, followed by a description of the contents of the file.

Every file should contain the following items, in order:

  • a optional mode line, // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
  • a copyright statement (for example, Copyright (C) 2011 Canonical Ltd)
  • the license boilerplate.
  • an author line to identify the original author of the file

If you make significant changes to a file that someone else originally wrote, add yourself to the author line. This can be very helpful when another contributor has questions about the file and needs to know whom to contact about it.

Every file should have a comment at the top, below the copyright notice and author line, that describes the contents of the file.

Generally a .h file will describe the classes that are declared in the file with an overview of what they are for and how they are used. A .cpp file should contain more information about implementation details or discussions of tricky algorithms. If you feel the implementation details or a discussion of the algorithms would be useful for someone reading the .h, feel free to put it there instead, but mention in the .cpp that the documentation is in the .h file.

Do not duplicate comments in both the .h and the .cpp. Duplicated comments diverge.

Every class definition should have an accompanying comment that describes what it is for and how it should be used. // Iterates over the contents of a GargantuanTable. Sample usage: // GargantuanTableIterator* iter = table->NewIterator(); // for (iter->Seek("foo"); !iter->done(); iter->Next()) { // process(iter->key(), iter->value()); // } // delete iter; class GargantuanTableIterator { ... };

If you have already described a class in detail in the comments at the top of your file feel free to simply state "See comment at top of file for a complete description", but be sure to have some sort of comment.

Document the synchronization assumptions the class makes, if any. If an instance of the class can be accessed by multiple threads, take extra care to document the rules and invariants surrounding multithreaded use.

Declaration comments describe use of the function; comments at the definition of a function describe operation.

Every function declaration should have comments immediately preceding it that describe what the function does and how to use it. These comments should be descriptive ("Opens the file") rather than imperative ("Open the file"); the comment describes the function, it does not tell the function what to do. In general, these comments do not describe how the function performs its task. Instead, that should be left to comments in the function definition.

Types of things to mention in comments at the function declaration:

  • What the inputs and outputs are.
  • For class member functions: whether the object remembers reference arguments beyond the duration of the method call, and whether it will free them or not.
  • If the function allocates memory that the caller must free.
  • Whether any of the arguments can be NULL.
  • If there are any performance implications of how a function is used.
  • If the function is re-entrant. What are its synchronization assumptions?

Here is an example:

// Returns an iterator for this table. It is the client's // responsibility to delete the iterator when it is done with it, // and it must not use the iterator once the GargantuanTable object // on which the iterator was created has been deleted. // // The iterator is initially positioned at the beginning of the table. // // This method is equivalent to: // Iterator* iter = table->NewIterator(); // iter->Seek(""); // return iter; // If you are going to immediately seek to another place in the // returned iterator, it will be faster to use NewIterator() // and avoid the extra seek. Iterator* GetIterator() const;

However, do not be unnecessarily verbose or state the completely obvious. Notice below that it is not necessary to say "returns false otherwise" because this is implied.

// Returns true if the table cannot hold any more entries. bool IsTableFull();

When commenting constructors and destructors, remember that the person reading your code knows what constructors and destructors are for, so comments that just say something like "destroys this object" are not useful. Document what constructors do with their arguments (for example, if they take ownership of pointers), and what cleanup the destructor does. If this is trivial, just skip the comment. It is quite common for destructors not to have a header comment.

Each function definition should have a comment describing what the function does if there's anything tricky about how it does its job. For example, in the definition comment you might describe any coding tricks you use, give an overview of the steps you go through, or explain why you chose to implement the function in the way you did rather than using a viable alternative. For instance, you might mention why it must acquire a lock for the first half of the function but why it is not needed for the second half.

Note you should not just repeat the comments given with the function declaration, in the .h file or wherever. It's okay to recapitulate briefly what the function does, but the focus of the comments should be on how it does it.

In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.

Each class data member (also called an instance variable or member variable) should have a comment describing what it is used for. If the variable can take sentinel values with special meanings, such as NULL or -1, document this. For example:

private: // Keeps track of the total number of entries in the table. // Used to ensure we do not go over the limit. -1 means // that we don't yet know how many entries the table has. int num_total_entries_;

As with data members, all global variables should have a comment describing what they are and what they are used for. For example:

// The total number of tests cases that we run through in this regression test. const int NUM_TEST_CASES = 6;
In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.

Tricky or complicated code blocks should have comments before them. Example:

// Divide result by two, taking into account that x // contains the carry from the add. for (int i = 0; i < result->size(); i++) { x = (x << 8) + (*result)[i]; (*result)[i] = x >> 1; x &= 1; }

Also, lines that are non-obvious should get a comment at the end of the line. These end-of-line comments should be separated from the code by 2 spaces. Example:

// If we have enough memory, mmap the data portion too. mmap_budget = max<int64>(0, mmap_budget - index_->length()); if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock)) return; // Error already logged.

Note that there are both comments that describe what the code is doing, and comments that mention that an error has already been logged when the function returns.

If you have several comments on subsequent lines, it can often be more readable to line them up:

DoSomething(); // Comment here so the comments line up. DoSomethingElseThatIsLonger(); // Comment here so there are two spaces between // the code and the comment. { // One space before comment when opening a new scope is allowed, // thus the comment lines up with the following comments and code. DoSomethingElse(); // Two spaces before line comments normally. }

When you pass in NULL, boolean, or literal integer values to functions, you should consider adding a comment about what they are, or make your code self-documenting by using constants. For example, compare:

bool success = CalculateSomething(interesting_value, 10, false, NULL); // What are these arguments??

versus:

bool success = CalculateSomething(interesting_value, 10, // Default base value. false, // Not the first time we're calling this. NULL); // No callback.

Or alternatively, constants or self-describing variables:

const int kDefaultBaseValue = 10; const bool kFirstTimeCalling = false; Callback *null_callback = NULL; bool success = CalculateSomething(interesting_value, kDefaultBaseValue, kFirstTimeCalling, null_callback);

Note that you should never describe the code itself. Assume that the person reading the code knows C++ better than you do, even though he or she does not know what you are trying to do:

// Now go through the b array and make sure that if i occurs, // the next element is i+1. ... // Geez. What a useless comment.
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.

Comments should usually be written as complete sentences with proper capitalization and periods at the end. Shorter comments, such as comments at the end of a line of code, can sometimes be less formal, but you should be consistent with your style. Complete sentences are more readable, and they provide some assurance that the comment is complete and not an unfinished thought.

Although it can be frustrating to have a code reviewer point out that you are using a comma when you should be using a semicolon, it is very important that source code maintain a high level of clarity and readability. Proper punctuation, spelling, and grammar help with that goal.

Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.

TODOs should include the string TODO in all caps, followed by the name, e-mail address, or other identifier of the person who can best provide context about the problem referenced by the TODO. A colon is optional. The main purpose is to have a consistent TODO format that can be searched to find the person who can provide more details upon request. A TODO is not a commitment that the person referenced will fix the problem. Thus when you create a TODO, it is almost always your name that is given.

// TODO(kl@gmail.com): Use a "*" here for concatenation operator. // TODO(Zeke) change this to use relations.

If your TODO is of the form "At a future date do something" make sure that you either include a very specific date ("Fix by November 2005") or a very specific event ("Remove this code when all clients can handle XML responses.").

Mark deprecated interface points with DEPRECATED comments.

You can mark an interface as deprecated by writing a comment containing the word DEPRECATED in all caps. The comment goes either before the declaration of the interface or on the same line as the declaration.

After the word DEPRECATED, write your name, e-mail address, or other identifier in parentheses.

A deprecation comment must include simple, clear directions for people to fix their callsites. In C++, you can implement a deprecated function as an inline function that calls the new interface point.

Marking an interface point DEPRECATED will not magically cause any callsites to change. If you want people to actually stop using the deprecated facility, you will have to fix the callsites yourself or recruit a crew to help you.

New code should not contain calls to deprecated interface points. Use the new interface point instead. If you cannot understand the directions, find the person who created the deprecation and ask them for help using the new interface point.

Coding style and formatting are pretty arbitrary, but a project is much easier to follow if everyone uses the same style. Individuals may not agree with every aspect of the formatting rules, and some of the rules may take some getting used to, but it is important that all project contributors follow the style rules so that they can all read and understand everyone's code easily.

Each line of text in your code should be at most 80 characters long.

We recognize that this rule is controversial, but so much existing code already adheres to it, and we feel that consistency is important.

Those who favor this rule argue that it is rude to force them to resize their windows and there is no need for anything longer. Some folks are used to having several code windows side-by-side, and thus don't have room to widen their windows in any case. People set up their work environment assuming a particular maximum window width, and 80 columns has been the traditional standard. Why change it? Proponents of change argue that a wider line can make code more readable. The 80-column limit is an hidebound throwback to 1960s mainframes; modern equipment has wide screens that can easily show longer lines.

80 characters is the maximum.

Exception: if a comment line contains an example command or a literal URL longer than 80 characters, that line may be longer than 80 characters for ease of cut and paste.

Exception: an #include statement with a long path may exceed 80 columns. Try to avoid situations where this becomes necessary.

Exception: you needn't be concerned about header guards that exceed the maximum length.

Non-ASCII characters should be rare, and must use UTF-8 formatting.

You shouldn't hard-code user-facing text in source, even English, so use of non-ASCII characters should be rare. However, in certain cases it is appropriate to include such words in your code. For example, if your code parses data files from foreign sources, it may be appropriate to hard-code the non-ASCII string(s) used in those data files as delimiters. More commonly, unittest code (which does not need to be localized) might contain non-ASCII strings. In such cases, you should use UTF-8, since that is an encoding understood by most tools able to handle more than just ASCII. Hex encoding is also OK, and encouraged where it enhances readability — for example, "\xEF\xBB\xBF" is the Unicode zero-width no-break space character, which would be invisible if included in the source as straight UTF-8.

Use only spaces, and indent 2 spaces at a time.

We use spaces for indentation. Do not use tabs in your code. You should set your editor to emit spaces when you hit the tab key.

Return type on the same line as function name, parameters on the same line if they fit.

Functions look like this:

ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) { DoSomething(); ... }

If you have too much text to fit on one line:

ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2, Type par_name3) { DoSomething(); ... }

or if you cannot fit even the first parameter:

ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // 4 space indent Type par_name2, Type par_name3) { DoSomething(); // 2 space indent ... }

Some points to note:

  • The return type is always on the same line as the function name.
  • The open parenthesis is always on the same line as the function name.
  • There is never a space between the function name and the open parenthesis.
  • There is never a space between the parentheses and the parameters.
  • The open curly brace is always at the end of the same line as the last parameter.
  • The close curly brace is either on the last line by itself or (if other style rules permit) on the same line as the open curly brace.
  • There should be a space between the close parenthesis and the open curly brace.
  • All parameters should be named, with identical names in the declaration and implementation.
  • All parameters should be aligned if possible.
  • Default indentation is 2 spaces.
  • Wrapped parameters have a 4 space indent.

If your function is const, the const keyword should be on the same line as the last parameter:

// Everything in this function signature fits on a single line ReturnType FunctionName(Type par) const { ... } // This function signature requires multiple lines, but // the const keyword is on the line with the last parameter. ReturnType ReallyLongFunctionName(Type par1, Type par2) const { ... }

If some parameters are unused, comment out the variable name in the function definition:

// Always have named parameters in interfaces. class Shape { public: virtual void Rotate(double radians) = 0; } // Always have named parameters in the declaration. class Circle : public Shape { public: virtual void Rotate(double radians); } // Comment out unused named parameters in definitions. void Circle::Rotate(double /*radians*/) {} // Bad - if someone wants to implement later, it's not clear what the // variable means. void Circle::Rotate(double) {}
On one line if it fits; otherwise, wrap arguments at the parenthesis.

Function calls have the following format:

bool retval = DoSomething(argument1, argument2, argument3);

If the arguments do not all fit on one line, they should be broken up onto multiple lines, with each subsequent line aligned with the first argument. Do not add spaces after the open paren or before the close paren:

bool retval = DoSomething(averyveryveryverylongargument1, argument2, argument3);

If the function has many arguments, consider having one per line if this makes the code more readable:

bool retval = DoSomething(argument1, argument2, argument3, argument4);

If the function signature is so long that it cannot fit within the maximum line length, you may place all arguments on subsequent lines:

if (...) { ... ... if (...) { DoSomethingThatRequiresALongFunctionName( very_long_argument1, // 4 space indent argument2, argument3, argument4); }
Prefer no spaces inside parentheses. The else keyword belongs on a new line. if (condition) // no spaces inside parentheses { ... // 2 space indent. } else // The else goes on a new line. { ... }

Note that in all cases you must have a space between the if and the open parenthesis.

if(condition) // Bad - space missing after IF. if (condition) // Good - proper space after IF.

Short conditional statements may be written on one line if this enhances readability. You may use this only when the line is brief and the statement does not use the else clause.

if (x == kFoo) return new Foo(); if (x == kBar) return new Bar();

This is not allowed when the if statement has an else:

// Not allowed - IF statement on one line when there is an ELSE clause if (x) DoThis(); else DoThat();

In general, curly braces are not required for single-line statements, but they are allowed if you like them; conditional or loop statements with complex conditions or statements may be more readable with curly braces. Some projects require that an if must always always have an accompanying brace.

if (condition) DoSomething(); // 2 space indent. if (condition) { DoSomething(); // 2 space indent. }

However, if one part of an if-else statement uses curly braces, the other part must too:

// Not allowed - curly on IF but not ELSE if (condition) { foo; } else bar; // Not allowed - curly on ELSE but not IF if (condition) foo; else { bar; } // Curly braces around both IF and ELSE required because // one of the clauses used braces. if (condition) { foo; } else { bar; }
Switch statements may use braces for blocks. Empty loop bodies should use {} or continue.

case blocks in switch statements can have curly braces or not, depending on your preference. If you do include curly braces they should be placed as shown below.

If not conditional on an enumerated value, switch statements should always have a default case (in the case of an enumerated value, the compiler will warn you if any values are not handled). If the default case should never execute, simply assert:

switch (var) { case 0: // 2 space indent { ... // 4 space indent break; } case 1: { ... break; } default: { assert(false); } }

Empty loop bodies should use {} or continue, but not a single semicolon.

while (condition) { // Repeat test until it returns false. } for (int i = 0; i < kSomeNumber; ++i) {} // Good - empty body. while (condition) continue; // Good - continue indicates no logic. while (condition); // Bad - looks like part of do/while loop.
No spaces around period or arrow. Pointer operators do not have trailing spaces.

The following are examples of correctly-formatted pointer and reference expressions:

x = *p; p = &x; x = r.y; x = r->y;

Note that:

  • There are no spaces around the period or arrow when accessing a member.
  • Pointer operators have no space after the * or &.

When declaring a pointer variable or argument, you should place the asterisk adjacent to the type:

// These are fine char* c; string const& str; char * c; // Bad - spaces on both sides of * char *c ; // Bad - * next to variable name string const & str; // Bad - spaces on both sides of &

You should do this consistently within a single file, so, when modifying an existing file, use the style in that file.

When you have a boolean expression that is longer than the standard line length, be consistent in how you break up the lines.

In this example, the logical AND operator is always at the end of the lines:

if (this_one_thing > this_other_thing && a_third_thing == a_fourth_thing && yet_another && last_one) { ... }

Note that when the code wraps in this example, both of the && logical AND operators are at the end of the line. Feel free to insert extra parentheses judiciously, because they can be very helpful in increasing readability when used appropriately. Also note that you should always use the punctuation operators, such as && and ~, rather than the word operators, such as and and compl.

Do not needlessly surround the return expression with parentheses.

Use parentheses in return expr; only where you would use them in x = expr;.

return result; // No parentheses in the simple case. return (some_long_condition && // Parentheses ok to make a complex another_condition); // expression more readable. return (value); // You wouldn't write var = (value); return(result); // return is not a function!
Your choice of = or ().

You may choose between = and (); the following are all correct:

int x = 3; int x(3); string name("Some Name"); string name = "Some Name";
The hash mark that starts a preprocessor directive should always be at the beginning of the line.

Even when preprocessor directives are within the body of indented code, the directives should start at the beginning of the line.

// Good - directives at beginning of line if (lopsided_score) { #if DISASTER_PENDING // Correct -- Starts at beginning of line DropEverything(); # if NOTIFY // OK but not required -- Spaces after # NotifyClient(); # endif #endif BackToNormal(); } // Bad - indented directives if (lopsided_score) { #if DISASTER_PENDING // Wrong! The "#if" should be at beginning of line DropEverything(); #endif // Wrong! Do not indent "#endif" BackToNormal(); }
Sections in public, protected and private order.

The basic format for a class declaration (lacking the comments, see Class Comments for a discussion of what comments are needed) is:

class MyClass : public OtherClass { public: MyClass(); // Regular 2 space indent. explicit MyClass(int var); ~MyClass() {} void SomeFunction(); void SomeFunctionThatDoesNothing() {} void set_some_var(int var) { some_var_ = var; } int some_var() const { return some_var_; } private: bool SomeInternalFunction(); int some_var_; int some_other_var_; DISALLOW_COPY_AND_ASSIGN(MyClass); };

Things to note:

  • Any base class name should be on the same line as the subclass name, subject to the 80-column limit.
  • The public:, protected:, and private: keywords are not indented.
  • Except for the first instance, these keywords should be preceded by a blank line. This rule is optional in small classes.
  • Do not leave a blank line after these keywords.
  • The public section should be first, followed by the protected and finally the private section.
  • See Declaration Order for rules on ordering declarations within each of these sections.
Constructor initializer lists can be all on one line or with subsequent lines indented four spaces.

There are two acceptable formats for initializer lists:

// When it all fits on one line: MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {}

or

// When it requires multiple lines, indent 4 spaces, putting the colon on // the first initializer line, and commas inline with the colon: MyClass::MyClass(int var) : some_var_(var) // 4 space indent , some_other_var_(var + 1) // lined up { ... DoSomething(); ... }
The contents of namespaces are not indented.

Namespaces do not add an extra level of indentation. For example, use:

namespace { void foo() // Correct. No extra indentation within namespace. { ... } } // namespace

Do not indent within a namespace:

namespace { // Wrong. Indented when it should not be. void foo() { ... } } // namespace

When declaring nested namespaces, put each namespace on its own line, with the opening brace on the line following.

namespace foo { namespace bar {
Use of horizontal whitespace depends on location. Never put trailing whitespace at the end of a line. int i = 0; // Semicolons usually have no space before them. int x[] = { 0 }; // Spaces inside braces for array initialization are int x[] = {0}; // optional. If you use them, put them on both sides! // Spaces around the colon in inheritance and initializer lists. class Foo : public Bar { public: // For inline function implementations, put spaces between the braces // and the implementation itself. Foo(int b) : Bar(), baz_(b) {} // No spaces inside empty braces. void Reset() { baz_ = 0; } // Spaces separating braces from implementation. ...

Adding trailing whitespace can cause extra work for others editing the same file, when they merge, as can removing existing trailing whitespace. So: Don't introduce trailing whitespace. Remove it if you're already changing that line, or do it in a separate clean-up operation (preferably when no-one else is working on the file).

x = 0; // Assignment operators always have spaces around // them. x = -5; // No spaces separating unary operators and their ++x; // arguments. if (x && !y) ... v = w * x + y / z; // Binary operators usually have spaces around them, v = w*x + y/z; // but it's okay to remove spaces around factors. v = w * (x + z); // Parentheses should have no spaces inside them. vector<string> x; // No spaces inside the angle y = static_cast<char*>(x); // brackets (< and >), before // <, or between >( in a cast. vector<char *> x; // Spaces between type and pointer are // okay, but be consistent. set<list<string>> x; // C++11 now allows >> to close templates. set<list<string> > x; // Older C++ requiree a space in > >, and is allowed.
Minimize use of vertical whitespace.

This is more a principle than a rule: don't use blank lines when you don't have to. In particular, don't put more than one or two blank lines between functions, resist starting functions with a blank line, don't end functions with a blank line, and be discriminating with your use of blank lines inside functions.

The basic principle is: The more code that fits on one screen, the easier it is to follow and understand the control flow of the program. Of course, readability can suffer from code being too dense as well as too spread out, so use your judgement. But in general, minimize use of vertical whitespace.

Some rules of thumb to help when blank lines may be useful:

  • Blank lines at the beginning or end of a function very rarely help readability.
  • Blank lines inside a chain of if-else blocks may well help readability.

The coding conventions described above are mandatory. However, like all good rules, these sometimes have exceptions, which we discuss here.

You may diverge from the rules when dealing with code that does not conform to this style guide.

If you find yourself modifying code that was written to specifications other than those presented by this guide, you may have to diverge from these rules in order to stay consistent with the local conventions in that code. If you are in doubt about how to do this, ask the original author or the person currently responsible for the code. Remember that consistency includes local consistency, too.

Use common sense and BE CONSISTENT.

If you are editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around their if clauses, you should, too. If their comments have little boxes of stars around them, make your comments have little boxes of stars around them too.

The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you are saying, rather than on how you are saying it. We present global style rules here so people know the vocabulary. But local style is also important. If code you add to a file looks drastically different from the existing code around it, the discontinuity throws readers out of their rhythm when they go to read it. Try to avoid this.

OK, enough writing about writing code; the code itself is much more interesting. Have fun!

./guides/styleguide.css0000644000004100000410000000434713437202764015433 0ustar www-datawww-databody { background-color: #fff; color: #333; font-family: sans-serif; font-size: 10pt; margin-right: 100px; margin-left: 100px; } h1, h2, h3, h4, h5, h6, .toc_title { color: #06c; margin-top: 2em; margin-bottom: 1em; } h1 { text-align: center; font-size: 18pt; } h2, .toc_title { font-weight: bold; font-size: 12pt; margin-left: -40px; } h3, h4, h5, h6 { font-size: 10pt; margin-left: -20px; } .toc_category, .toc_stylepoint { font-size: 10pt; padding-top: .3em; padding-bottom: .3em; } table { border-collapse: collapse; } td, th { border: 1px solid #ccc; padding: 2px 12px; font-size: 10pt; } .toc td, .toc th { border-width: 1px 5px; } code, samp, var { color: #060; } pre { font-size: 10pt; display: block; color: #060; background-color: #f8fff8; border-color: #f0fff0; border-style: solid; border-top-width: 1px; border-bottom-width: 1px; border-right-width: 1px; border-left-width: 5px; padding-left: 12px; padding-right: 12px; padding-top: 4px; padding-bottom: 4px; } pre.badcode { color: #c00; background-color: #fff8f8; border-color: #fff0f0; } .showhide_button { float: left; cursor: pointer; border-width: 1px; border-style: solid; border-color: #ddd #aaa #aaa #ddd; padding: 0 3px 1px; margin: 0 4px 8px 0; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; } .link_button { float: left; display: none; background-color: #f8f8ff; border-color: #f0f0ff; border-style: solid; border-width: 1px; font-size: 75%; margin-top: 0; margin-left: -50px; padding: 4px; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; } address { text-align: right; } hr { margin-top: 3.5em; border-width: 1px; color: #fff; } .stylepoint_section { display: block; margin-bottom: 1em; color: #5588ff; font-family: sans-serif; font-size: 90%; font-weight: bold; margin-left: -2%; } .stylepoint_subsection { color: #667799; font-family: sans-serif; font-size: 90%; font-weight: bold; margin-left: -1%; } .stylepoint_subsubsection { color: #667799; font-family: sans-serif; font-size: 80%; font-weight: bold; margin-left: 0; } .revision { text-align: right; } ./services/0000755000004100000410000000000013437202764013076 5ustar www-datawww-data./services/unity-screen-locked.target0000644000004100000410000000027013437202764020171 0ustar www-datawww-data[Unit] Description=A target that, when running, represents the screen being locked Requires=unity7.service Wants=unity-panel-service-lockscreen.service PartOf=graphical-session.target ./services/CMakeLists.txt0000644000004100000410000000556213437202764015646 0ustar www-datawww-data# # Panel Service # set(UNITY_PANEL_SERVICE_DEPS atk atk-bridge-2.0 gio-2.0>=2.30.0 gobject-2.0 gthread-2.0 gtk+-3.0>=3.3 indicator3-0.4>=12.10.2 x11 libido3-0.1>=13.0.0 ) pkg_check_modules(SERVICE_DEPS REQUIRED ${UNITY_PANEL_SERVICE_DEPS}) set(PANEL_SOURCES panel-a11y.c panel-a11y.h panel-indicator-accessible.c panel-indicator-accessible.h panel-indicator-entry-accessible.c panel-indicator-entry-accessible.h panel-main.c panel-root-accessible.c panel-root-accessible.h panel-service.c panel-service.h panel-util-accessible.c panel-util-accessible.h) set(CFLAGS ${SERVICE_DEPS_CFLAGS} ${SERVICE_DEPS_CFLAGS_OTHER} "-Werror -Wall -Wno-error=deprecated-declarations" ) string (REPLACE ";" " " CFLAGS "${CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CFLAGS}") include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}) set(LIBS ${SERVICE_DEPS_LIBRARIES}) set(LIB_PATHS ${SERVICE_DEPS_LIBRARY_DIRS}) link_directories(${LIB_PATHS}) add_executable(unity-panel-service ${PANEL_SOURCES}) target_link_libraries(unity-panel-service ${LIBS}) install(TARGETS unity-panel-service DESTINATION ${UNITY_INSTALL_LIBDIR}) configure_file(unity-panel-service.conf.in ${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service.conf) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service.conf DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) configure_file(unity-panel-service-lockscreen.conf.in ${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service-lockscreen.conf) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service-lockscreen.conf DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) ## ## Systemd Unit Files ## # where to install # Uncomment when we drop Vivid # pkg_get_variable(SYSTEMD_USER_DIR systemd systemduserunitdir) set (SYSTEMD_USER_DIR "/usr/lib/systemd/user") message (STATUS "${SYSTEMD_USER_DIR} is the systemd user unit file install dir") configure_file (unity-panel-service.service.in "${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service.service") configure_file (unity-panel-service-lockscreen.service.in "${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service-lockscreen.service") install( FILES "${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service.service" "${CMAKE_CURRENT_BINARY_DIR}/unity-panel-service-lockscreen.service" "${CMAKE_CURRENT_SOURCE_DIR}/unity-screen-locked.target" DESTINATION "${SYSTEMD_USER_DIR}") ## ## Upstart systemd override Job File ## set (UPSTART_SYSTEMD_OVERRIDE_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/systemd-session/upstart") message (STATUS "${UPSTART_SYSTEMD_OVERRIDE_DIR} is the Upstart override Job File for systemd dir") install (FILES unity-panel-service.override unity-panel-service-lockscreen.override DESTINATION "${UPSTART_SYSTEMD_OVERRIDE_DIR}") ./services/panel-service.c0000644000004100000410000023104613437202764016005 0ustar www-datawww-data// -*- Mode: C; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel * Rodrigo Moya * Marco Trevisan (Treviño) */ #include "config.h" #include "panel-service.h" #include "panel-service-private.h" #include #include #include #include #include #include #include #include #include G_DEFINE_TYPE (PanelService, panel_service, G_TYPE_OBJECT); #define GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_SERVICE, PanelServicePrivate)) #define NOTIFY_TIMEOUT 80 #define N_TIMEOUT_SLOTS 50 #define MAX_INDICATOR_ENTRIES 500 #define NUX_VERTICAL_SCROLL_DELTA 120 #define NUX_HORIZONTAL_SCROLL_DELTA (NUX_VERTICAL_SCROLL_DELTA ^ 2) #define COMPIZ_OPTION_SCHEMA "org.compiz.unityshell" #define COMPIZ_OPTION_PATH "/org/compiz/profiles/unity/plugins/" #define MENU_TOGGLE_KEYBINDING_KEY "panel-first-menu" #define SHOW_DASH_KEY "show-launcher" #define SHOW_HUD_KEY "show-hud" static PanelService *static_service = NULL; static gboolean lockscreen_mode = FALSE; struct _PanelServicePrivate { GSList *indicators; GSList *dropdown_entries; GSList *removed_entries; GHashTable *id2entry_hash; GHashTable *panel2entries_hash; IndicatorObject *appmenu_indicator; guint timeouts[N_TIMEOUT_SLOTS]; guint remove_idle; IndicatorObjectEntry *last_entry; IndicatorObjectEntry *last_dropdown_entry; const gchar *last_panel; GtkMenu *last_menu; gint32 last_x; gint32 last_y; gint last_left; gint last_top; gint last_right; gint last_bottom; guint32 last_menu_button; guint64 last_open_time; GSettings *gsettings; KeyBinding menu_toggle; KeyBinding show_dash; KeyBinding show_hud; IndicatorObjectEntry *pressed_entry; gboolean use_event; }; /* Globals */ static gboolean suppress_signals = FALSE; static void (*default_menu_shell_deactivate) (GtkMenuShell *menu_shell); enum { ENTRY_ACTIVATED = 0, RE_SYNC, ENTRY_ACTIVATE_REQUEST, GEOMETRIES_CHANGED, INDICATORS_CLEARED, LAST_SIGNAL }; enum { SYNC_WAITING = G_MAXUINT, SYNC_NEUTRAL = 0, }; static guint32 _service_signals[LAST_SIGNAL] = { 0 }; static const gchar * indicator_order[][2] = { {APPMENU_INDICATOR_NAME, NULL}, /* indicator-appmenu" */ {"libapplication.so", NULL}, /* indicator-application" */ {"floating-indicators", NULL}, /* position-less NG indicators */ {"libprintersmenu.so", NULL}, /* indicator-printers */ {"libapplication.so", "gsd-keyboard-xkb"}, /* keyboard layout selector */ {"libmessaging.so", NULL}, /* indicator-messages */ {"libpower.so", NULL}, /* indicator-power */ {"libbluetooth.so", NULL}, /* indicator-bluetooth */ {"libnetwork.so", NULL}, /* indicator-network */ {"libnetworkmenu.so", NULL}, /* indicator-network */ {"libapplication.so", "nm-applet"}, /* network manager */ {"libsoundmenu.so", NULL}, /* indicator-sound */ {"libdatetime.so", NULL}, /* indicator-datetime */ {"libsession.so", NULL}, /* indicator-session */ {NULL, NULL} }; /* Forwards */ static void load_indicator (PanelService *, IndicatorObject *, const gchar *); static void load_indicators (PanelService *); static void load_indicators_from_indicator_files (PanelService *); static void sort_indicators (PanelService *); static void notify_object (IndicatorObject *object); static void update_keybinding (GSettings *, const gchar *, gpointer); static void emit_upstart_event (const gchar *); static void menu_shell_deactivate_override (GtkMenuShell *menu_shell); static gchar * get_indicator_entry_id_by_entry (IndicatorObjectEntry *entry); static IndicatorObjectEntry * get_indicator_entry_by_id (PanelService *self, const gchar *entry_id); static GdkFilterReturn event_filter (GdkXEvent *, GdkEvent *, PanelService *); /* * GObject stuff */ static void panel_service_class_dispose (GObject *self) { PanelServicePrivate *priv = PANEL_SERVICE (self)->priv; gint i; g_idle_remove_by_data (self); gdk_window_remove_filter (NULL, (GdkFilterFunc)event_filter, self); if (!lockscreen_mode) emit_upstart_event ("indicator-services-end"); if (GTK_IS_WIDGET (priv->last_menu) && gtk_widget_get_realized (GTK_WIDGET (priv->last_menu))) { panel_service_close_active_entry (PANEL_SERVICE (self)); g_signal_handlers_disconnect_by_data (priv->last_menu, self); priv->last_menu = NULL; } for (i = 0; i < N_TIMEOUT_SLOTS; i++) { if (priv->timeouts[i] > 0 && priv->timeouts[i] != SYNC_WAITING) { g_source_remove (priv->timeouts[i]); priv->timeouts[i] = 0; } } if (G_IS_OBJECT (priv->gsettings)) { g_signal_handlers_disconnect_matched (priv->gsettings, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, update_keybinding, NULL); g_object_unref (priv->gsettings); priv->gsettings = NULL; } if (priv->dropdown_entries) { g_slist_free_full (priv->dropdown_entries, g_free); priv->dropdown_entries = NULL; } G_OBJECT_CLASS (panel_service_parent_class)->dispose (self); } void panel_service_clear_remote_data (PanelService *self) { g_return_if_fail (PANEL_IS_SERVICE (self)); g_hash_table_remove_all (self->priv->panel2entries_hash); } static void panel_service_class_finalize (GObject *object) { PanelServicePrivate *priv = PANEL_SERVICE (object)->priv; g_hash_table_destroy (priv->id2entry_hash); g_hash_table_destroy (priv->panel2entries_hash); static_service = NULL; G_OBJECT_CLASS (panel_service_parent_class)->finalize (object); } static void panel_service_class_init (PanelServiceClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->dispose = panel_service_class_dispose; obj_class->finalize = panel_service_class_finalize; /* Signals */ _service_signals[ENTRY_ACTIVATED] = g_signal_new ("entry-activated", G_OBJECT_CLASS_TYPE (obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_UINT, G_TYPE_UINT); _service_signals[RE_SYNC] = g_signal_new ("re-sync", G_OBJECT_CLASS_TYPE (obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); _service_signals[ENTRY_ACTIVATE_REQUEST] = g_signal_new ("entry-activate-request", G_OBJECT_CLASS_TYPE (obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); _service_signals[GEOMETRIES_CHANGED] = g_signal_new ("geometries-changed", G_OBJECT_CLASS_TYPE (obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 6, G_TYPE_OBJECT, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); _service_signals[INDICATORS_CLEARED] = g_signal_new ("indicators-cleared", G_OBJECT_CLASS_TYPE (obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); g_type_class_add_private (obj_class, sizeof (PanelServicePrivate)); } static gboolean rect_contains_point (GdkRectangle* rect, gint x, gint y) { if (!rect) return FALSE; return (x >= rect->x && x <= (rect->x + rect->width) && y >= rect->y && y <= (rect->y + rect->height)); } gboolean entry_has_dropdown_id (const gchar *entry_id) { return g_str_has_suffix (entry_id, "-dropdown"); } IndicatorObjectEntry * get_entry_at (PanelService *self, gint x, gint y) { GHashTableIter panel_iter, entries_iter; gpointer key, value, k, v; g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash); while (g_hash_table_iter_next (&panel_iter, &key, &value)) { GHashTable *entry2geometry_hash = value; g_hash_table_iter_init (&entries_iter, entry2geometry_hash); while (g_hash_table_iter_next (&entries_iter, &k, &v)) { IndicatorObjectEntry *entry = k; GdkRectangle *geo = v; if (rect_contains_point (geo, x, y)) { return entry; } } } return NULL; } static IndicatorObjectEntry * get_entry_at_panel (PanelService *self, const gchar *panel, gint x, gint y) { GHashTable *entry2geometry_hash; GHashTableIter entries_iter; gpointer key, value; if (!panel) return NULL; entry2geometry_hash = g_hash_table_lookup (self->priv->panel2entries_hash, panel); if (!entry2geometry_hash) return NULL; g_hash_table_iter_init (&entries_iter, entry2geometry_hash); while (g_hash_table_iter_next (&entries_iter, &key, &value)) { IndicatorObjectEntry *entry = key; GdkRectangle *geo = value; if (rect_contains_point (geo, x, y)) { return entry; } } return NULL; } static const gchar* get_panel_for_parent_at (PanelService *self, guint parent, gint x, gint y) { GHashTableIter panel_iter, entries_iter; gpointer key, value, k, v; g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash); while (g_hash_table_iter_next (&panel_iter, &key, &value)) { const gchar *panel_id = key; GHashTable *entry2geometry_hash = value; g_hash_table_iter_init (&entries_iter, entry2geometry_hash); while (g_hash_table_iter_next (&entries_iter, &k, &v)) { IndicatorObjectEntry *entry = k; GdkRectangle *geo = v; /* The entry might be invalid at this point (as it could have been * removed, but still not synced), so it's better to double check */ if (g_slist_find (self->priv->removed_entries, entry)) continue; if (!parent || entry->parent_window == parent) { if (rect_contains_point (geo, x, y)) { return panel_id; } } } } return NULL; } static IndicatorObject * get_entry_parent_indicator (IndicatorObjectEntry *entry) { if (!entry || !INDICATOR_IS_OBJECT (entry->parent_object)) return NULL; return INDICATOR_OBJECT (entry->parent_object); } static gchar * get_indicator_entry_id_by_entry (IndicatorObjectEntry *entry) { gchar *entry_id = NULL; if (g_slist_find (static_service->priv->dropdown_entries, entry)) { return g_strdup (entry->name_hint); } if (entry) entry_id = g_strdup_printf ("%p", entry); return entry_id; } static IndicatorObjectEntry * get_indicator_entry_by_id (PanelService *self, const gchar *entry_id) { IndicatorObjectEntry *entry; entry = g_hash_table_lookup (self->priv->id2entry_hash, entry_id); if (entry) { if (g_slist_find (self->priv->indicators, entry->parent_object)) { if (!INDICATOR_IS_OBJECT (entry->parent_object)) entry = NULL; } else { entry = NULL; } if (!entry) { g_warning("The entry id '%s' you're trying to lookup is not a valid IndicatorObjectEntry!", entry_id); } } if (!entry && entry_has_dropdown_id (entry_id)) { /* Unity might register some "fake" dropdown entries that it might use to * to present long menu bars (right now only for appmenu indicator) */ entry = g_new0 (IndicatorObjectEntry, 1); entry->parent_object = self->priv->appmenu_indicator; entry->name_hint = g_strdup (entry_id); self->priv->dropdown_entries = g_slist_append (self->priv->dropdown_entries, entry); /* Now the Hashtable owns the name_hint, so no need to manually free it */ g_hash_table_insert (self->priv->id2entry_hash, (gpointer)entry->name_hint, entry); } return entry; } static const gchar * get_indicator_entry_id_by_menu (PanelService *self, GtkMenu *menu) { GHashTableIter iter; IndicatorObjectEntry* entry; gchar *id; g_hash_table_iter_init (&iter, self->priv->id2entry_hash); while (g_hash_table_iter_next (&iter, (gpointer*) &id, (gpointer*) &entry)) { if (entry && entry->menu == menu) { return id; } } return NULL; } static void ensure_entry_menu_is_closed (PanelService *self, const gchar *panel_id, IndicatorObjectEntry *entry) { PanelServicePrivate *priv = self->priv; /* If the entry has been removed let's make sure that its menu is closed */ if (GTK_IS_MENU (priv->last_menu) && priv->last_menu == entry->menu) { if (!priv->last_panel || !panel_id || g_strcmp0 (priv->last_panel, panel_id) == 0) { panel_service_close_active_entry (self); } } } static void reinject_key_event_to_root_window (XIDeviceEvent *ev) { XKeyEvent kev; kev.display = ev->display; kev.window = ev->root; kev.root = ev->root; kev.subwindow = None; kev.time = ev->time; kev.x = ev->event_x; kev.y = ev->event_x; kev.x_root = ev->root_x; kev.y_root = ev->root_y; kev.same_screen = True; kev.keycode = ev->detail; kev.state = ev->mods.base; kev.type = ev->evtype; XSendEvent (kev.display, kev.root, True, KeyPressMask, (XEvent*) &kev); XFlush (kev.display); } static gboolean event_matches_keybinding (guint32 modifiers, KeySym key, KeyBinding *kb) { if (modifiers == kb->modifiers && key != NoSymbol) { if (key == kb->key || key == kb->fallback) { return TRUE; } } return FALSE; } static gboolean is_special_keysym (KeySym keysym) { /* Multimedia keys, see X11/XF86keysym.h */ if (keysym >= 0x1008FF00 && keysym <= 0x1008FFFF) return TRUE; return FALSE; } static gboolean is_control_keysym (KeySym keysym) { if (!is_special_keysym (keysym)) return FALSE; /* Display backlight controls */ if (keysym >= 0x1008FF01 && keysym <= 0x1008FF0F) return TRUE; switch (keysym) { case XF86XK_Battery: case XF86XK_Bluetooth: case XF86XK_WLAN: case XF86XK_UWB: return !lockscreen_mode; case XF86XK_Suspend: case XF86XK_Hibernate: case XF86XK_Sleep: case XF86XK_PowerOff: case XF86XK_ScreenSaver: return lockscreen_mode; } const gchar *keystr = XKeysymToString (keysym); if (g_str_has_prefix (keystr, "XF86Audio") || g_str_has_prefix (keystr, "XF86Touchpad")) { return TRUE; } return FALSE; } static gboolean is_allowed_keysym (KeySym keysym) { if (keysym == XK_Print) return TRUE; if (is_special_keysym (keysym)) return TRUE; return FALSE; } static GdkFilterReturn event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self) { PanelServicePrivate *priv = self->priv; XEvent *e = (XEvent *)ev; GdkFilterReturn ret = GDK_FILTER_CONTINUE; if (!PANEL_IS_SERVICE (self)) { g_warning ("%s: Invalid PanelService instance", G_STRLOC); return ret; } if (!GTK_IS_WIDGET (self->priv->last_menu)) return ret; /* Use XI2 to read the event data */ XGenericEventCookie *cookie = &e->xcookie; if (cookie->type != GenericEvent) return ret; XIDeviceEvent *event = cookie->data; if (!event) return ret; switch (event->evtype) { case XI_KeyPress: { KeySym keysym = XkbKeycodeToKeysym (event->display, event->detail, 0, 0); if (lockscreen_mode) { if (is_control_keysym (keysym)) { reinject_key_event_to_root_window (event); ret = GDK_FILTER_REMOVE; } break; } if (event_matches_keybinding (event->mods.base, keysym, &priv->menu_toggle) || event_matches_keybinding (event->mods.base, keysym, &priv->show_dash) || event_matches_keybinding (event->mods.base, keysym, &priv->show_hud)) { panel_service_close_active_entry (self); ret = GDK_FILTER_REMOVE; } else if (event->mods.base != GDK_CONTROL_MASK) { if (!IsModifierKey (keysym) && (event->mods.base != 0 || is_allowed_keysym (keysym))) { if (!is_control_keysym (keysym)) panel_service_close_active_entry (self); reinject_key_event_to_root_window (event); ret = GDK_FILTER_REMOVE; } } break; } case XI_ButtonPress: { priv->pressed_entry = get_entry_at_panel (self, priv->last_panel, event->root_x, event->root_y); priv->use_event = (priv->pressed_entry == NULL); if (priv->pressed_entry) ret = GDK_FILTER_REMOVE; break; } case XI_ButtonRelease: { IndicatorObjectEntry *entry = NULL; gboolean event_is_a_click = FALSE; entry = get_entry_at_panel (self, priv->last_panel, event->root_x, event->root_y); if (event->detail == 1 || event->detail == 3) { /* Consider only right and left clicks over the indicators entries */ event_is_a_click = TRUE; } else if (entry && event->detail == 2) { /* Middle clicks over an appmenu entry are considered just like * all other clicks */ if (get_entry_parent_indicator (entry) == priv->appmenu_indicator) { event_is_a_click = TRUE; } } if (event_is_a_click) { if (priv->use_event) { priv->use_event = FALSE; } else { if (entry) { if (entry != priv->pressed_entry) { ret = GDK_FILTER_REMOVE; priv->use_event = TRUE; } else if (priv->last_entry && entry != priv->last_entry) { /* If we were navigating over indicators using the keyboard * and now we click over the indicator under the mouse, we * must force it to show back again, not make it close */ gchar *entry_id = get_indicator_entry_id_by_entry (entry); g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id); g_free (entry_id); } } } } else if (entry && (event->detail == 2 || event->detail >= 4 || event->detail <= 7)) { /* If we're scrolling or middle-clicking over an indicator * (which is not an appmenu entry) then we need to send the * event to the indicator itself, and avoid it to close */ gchar *entry_id = get_indicator_entry_id_by_entry (entry); if (event->detail >= 4 || event->detail <= 7) { gint32 delta = (event->detail >= 6) ? NUX_HORIZONTAL_SCROLL_DELTA : NUX_VERTICAL_SCROLL_DELTA; delta = (event->detail % 2 == 0) ? delta : delta * -1; panel_service_scroll_entry (self, entry_id, delta); } else if (event->detail == 2 && entry == priv->pressed_entry) { panel_service_secondary_activate_entry (self, entry_id); } ret = GDK_FILTER_REMOVE; g_free (entry_id); } break; } } return ret; } static gboolean initial_resync (PanelService *self) { g_signal_emit (self, _service_signals[RE_SYNC], 0, ""); return G_SOURCE_REMOVE; } static gboolean ready_signal (PanelService *self) { if (!lockscreen_mode) emit_upstart_event ("indicator-services-start"); return G_SOURCE_REMOVE; } static void update_keybinding (GSettings *settings, const gchar *key, gpointer data) { KeyBinding *kb = data; gchar *binding = g_settings_get_string (settings, key); parse_string_keybinding (binding, kb); g_free (binding); } void parse_string_keybinding (const char *str, KeyBinding *kb) { kb->key = NoSymbol; kb->fallback = NoSymbol; kb->modifiers = 0; gchar *binding = g_strdup (str); gchar *keystart = (binding) ? strrchr (binding, '>') : NULL; if (!keystart) keystart = binding; while (keystart && *keystart != '\0' && !g_ascii_isalnum (*keystart)) keystart++; gchar *keyend = keystart; while (keyend && g_ascii_isalnum (*keyend)) keyend++; if (keystart != keyend) { gchar *keystr = g_strndup (keystart, keyend-keystart); kb->key = XStringToKeysym (keystr); g_free (keystr); } else { /* Parsing the case where we only have meta-keys */ keyend = (binding) ? strrchr (binding, '>') : NULL; keyend = keyend ? keyend - 1 : NULL; keystart = keyend; while (keystart && keystart > binding && g_ascii_isalnum (*keystart)) keystart--; if (keystart != keyend) { gchar *keystr = g_strndup (keystart+1, keyend-keystart); gchar *left = g_strconcat (keystr, "_L", NULL); kb->key = XStringToKeysym (left); gchar *right = g_strconcat (keystr, "_R", NULL); kb->fallback = XStringToKeysym (right); g_free (left); g_free (right); g_free (keystr); keystr = g_strndup (binding, keystart-binding); g_free (binding); binding = keystr; } } if (kb->key != NoSymbol) { if (g_strrstr (binding, "")) { kb->modifiers |= ShiftMask; } if (g_strrstr (binding, "") || g_strrstr (binding, "")) { kb->modifiers |= ControlMask; } if (g_strrstr (binding, "") || g_strrstr (binding, "")) { kb->modifiers |= AltMask; } if (g_strrstr (binding, "")) { kb->modifiers |= SuperMask; } } g_free (binding); } static void emit_upstart_event (const gchar *event) { const gchar *upstartsession = g_getenv ("UPSTART_SESSION"); if (!upstartsession) return; GError *error = NULL; GDBusConnection* conn = g_dbus_connection_new_for_address_sync (upstartsession, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, NULL, NULL, &error); if (error) { g_warning ("Unable to connect to Upstart session: %s", error->message); g_error_free (error); return; } GVariant *result = g_dbus_connection_call_sync (conn, "com.ubuntu.Upstart", "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", "EmitEvent", g_variant_new ("(sasb)", event, NULL, 0), NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, &error); if (error) { g_warning ("Unable to emit Upstart event: %s", error->message); g_error_free (error); } else { g_variant_unref (result); } g_object_unref (conn); } static void panel_service_init (PanelService *self) { PanelServicePrivate *priv; priv = self->priv = GET_PRIVATE (self); gdk_window_add_filter (NULL, (GdkFilterFunc)event_filter, self); priv->id2entry_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); priv->panel2entries_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy); priv->gsettings = g_settings_new_with_path (COMPIZ_OPTION_SCHEMA, COMPIZ_OPTION_PATH); g_signal_connect (priv->gsettings, "changed::"MENU_TOGGLE_KEYBINDING_KEY, G_CALLBACK (update_keybinding), &priv->menu_toggle); g_signal_connect (priv->gsettings, "changed::"SHOW_DASH_KEY, G_CALLBACK (update_keybinding), &priv->show_dash); g_signal_connect (priv->gsettings, "changed::"SHOW_HUD_KEY, G_CALLBACK (update_keybinding), &priv->show_hud); update_keybinding (priv->gsettings, MENU_TOGGLE_KEYBINDING_KEY, &priv->menu_toggle); update_keybinding (priv->gsettings, SHOW_DASH_KEY, &priv->show_dash); update_keybinding (priv->gsettings, SHOW_HUD_KEY, &priv->show_hud); } static gboolean panel_service_check_cleared (PanelService *self) { if (self->priv->indicators == NULL) { g_signal_emit (self, _service_signals[INDICATORS_CLEARED], 0); return G_SOURCE_REMOVE; } return G_SOURCE_CONTINUE; } static void panel_service_actually_remove_indicator (PanelService *self, IndicatorObject *indicator) { g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (INDICATOR_IS_OBJECT (indicator)); GList *entries, *l; gpointer timeout; gint position; g_signal_handlers_disconnect_by_data (indicator, self); position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (indicator), "position")); if (self->priv->timeouts[position] > 0) { g_source_remove (self->priv->timeouts[position]); self->priv->timeouts[position] = SYNC_NEUTRAL; } timeout = g_object_get_data (G_OBJECT (indicator), "remove-timeout"); if (timeout) { g_source_remove (GPOINTER_TO_UINT (timeout)); g_object_set_data (G_OBJECT (indicator), "remove-timeout", GUINT_TO_POINTER (0)); } entries = indicator_object_get_entries (indicator); if (entries) { for (l = entries; l; l = l->next) { IndicatorObjectEntry *entry; gchar *entry_id; GHashTableIter iter; gpointer key, value; GSList *ll; entry = l->data; if (entry->label) { g_signal_handlers_disconnect_by_data (entry->label, indicator); } if (entry->image) { g_signal_handlers_disconnect_by_data (entry->image, indicator); } if ((ll = g_slist_find (self->priv->dropdown_entries, entry))) { self->priv->dropdown_entries = g_slist_delete_link (self->priv->dropdown_entries, ll); g_hash_table_remove (self->priv->id2entry_hash, entry->name_hint); g_free (entry); } else { entry_id = get_indicator_entry_id_by_entry (entry); g_hash_table_remove (self->priv->id2entry_hash, entry_id); g_free (entry_id); } g_hash_table_iter_init (&iter, self->priv->panel2entries_hash); while (g_hash_table_iter_next (&iter, &key, &value)) { GHashTable *entry2geometry_hash = value; if (g_hash_table_size (entry2geometry_hash) > 1) g_hash_table_remove (entry2geometry_hash, entry); else g_hash_table_iter_remove (&iter); } } g_list_free (entries); } self->priv->indicators = g_slist_remove (self->priv->indicators, indicator); if (g_object_is_floating (G_OBJECT (indicator))) { g_object_ref_sink (G_OBJECT (indicator)); } if (self->priv->appmenu_indicator == indicator) self->priv->appmenu_indicator = NULL; g_object_unref (G_OBJECT (indicator)); } static gboolean panel_service_indicator_remove_timeout (IndicatorObject *indicator) { PanelService *self = panel_service_get_default (); panel_service_actually_remove_indicator (self, indicator); return FALSE; } static PanelService * get_or_init_static_service (gboolean* is_new) { if (is_new) *is_new = FALSE; if (!PANEL_IS_SERVICE (static_service)) { static_service = g_object_new (PANEL_TYPE_SERVICE, NULL); if (is_new) *is_new = TRUE; } return static_service; } static void initial_load_default_or_custom_indicators (PanelService *self, GList *indicators) { GList *l; suppress_signals = TRUE; if (!indicators) { if (!lockscreen_mode) { load_indicators (self); } load_indicators_from_indicator_files (self); sort_indicators (self); } else { for (l = indicators; l; l = l->next) { IndicatorObject *object = l->data; if (INDICATOR_IS_OBJECT (object)) panel_service_add_indicator (self, object); } } suppress_signals = FALSE; g_idle_add ((GSourceFunc)initial_resync, self); g_idle_add ((GSourceFunc)ready_signal, self); } PanelService * panel_service_get_default () { gboolean is_new; PanelService *self = get_or_init_static_service (&is_new); if (is_new) { initial_load_default_or_custom_indicators (self, NULL); } return self; } PanelService * panel_service_get_default_with_indicators (GList *indicators) { gboolean is_new; PanelService *self = get_or_init_static_service (&is_new); if (is_new && indicators) { initial_load_default_or_custom_indicators (self, indicators); } return self; } void panel_service_set_lockscreen_mode (gboolean enable) { lockscreen_mode = enable; } guint panel_service_get_n_indicators (PanelService *self) { g_return_val_if_fail (PANEL_IS_SERVICE (self), 0); return g_slist_length (self->priv->indicators); } IndicatorObject * panel_service_get_indicator_nth (PanelService *self, guint position) { g_return_val_if_fail (PANEL_IS_SERVICE (self), NULL); return (IndicatorObject *) g_slist_nth_data (self->priv->indicators, position); } IndicatorObject * panel_service_get_indicator (PanelService *self, const gchar *indicator_id) { g_return_val_if_fail (PANEL_IS_SERVICE (self), NULL); GSList *l; for (l = self->priv->indicators; l; l = l->next) { if (g_strcmp0 (indicator_id, g_object_get_data (G_OBJECT (l->data), "id")) == 0) { return (IndicatorObject *) l->data; } } return NULL; } void panel_service_add_indicator (PanelService *self, IndicatorObject *indicator) { g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (INDICATOR_IS_OBJECT (indicator)); g_object_ref (indicator); load_indicator (self, indicator, NULL); } static void panel_service_prepare_indicator_removal (PanelService *self, IndicatorObject *indicator, gboolean notify) { g_object_set_data (G_OBJECT (indicator), "remove", GINT_TO_POINTER (TRUE)); gpointer timeout = g_object_get_data (G_OBJECT (indicator), "remove-timeout"); if (timeout) { g_source_remove (GPOINTER_TO_UINT (timeout)); } if (notify) { notify_object (indicator); } guint id = g_timeout_add_seconds (1, (GSourceFunc) panel_service_indicator_remove_timeout, indicator); g_object_set_data (G_OBJECT (indicator), "remove-timeout", GUINT_TO_POINTER (id)); } void panel_service_remove_indicator (PanelService *self, IndicatorObject *indicator) { g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (INDICATOR_IS_OBJECT (indicator)); panel_service_prepare_indicator_removal (self, indicator, TRUE); } void panel_service_clear_indicators (PanelService *self) { g_return_if_fail (PANEL_IS_SERVICE (self)); GSList *l = self->priv->indicators; while (l) { IndicatorObject *ind = l->data; l = l->next; panel_service_prepare_indicator_removal (self, ind, FALSE); } g_signal_emit (self, _service_signals[RE_SYNC], 0, ""); g_idle_add ((GSourceFunc)panel_service_check_cleared, self); } /* * Private Methods */ static gboolean actually_notify_object (IndicatorObject *object) { PanelService *self; PanelServicePrivate *priv; gint position; if (!PANEL_IS_SERVICE (static_service)) return FALSE; if (!INDICATOR_IS_OBJECT (object)) return FALSE; self = panel_service_get_default (); priv = self->priv; position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (object), "position")); priv->timeouts[position] = SYNC_WAITING; if (!suppress_signals) g_signal_emit (self, _service_signals[RE_SYNC], 0, g_object_get_data (G_OBJECT (object), "id")); return G_SOURCE_REMOVE; } static void notify_object (IndicatorObject *object) { PanelService *self; PanelServicePrivate *priv; gint position; if (suppress_signals) return; self = panel_service_get_default (); priv = self->priv; position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (object), "position")); if (priv->timeouts[position] == SYNC_WAITING) { /* No need to ping again as we're waiting for the client to sync anyway */ return; } else if (priv->timeouts[position] != SYNC_NEUTRAL) { /* We were going to signal that a sync was needed, but since there's been another change let's * hold off a little longer so we're not flooding the client */ g_source_remove (priv->timeouts[position]); } priv->timeouts[position] = g_timeout_add (NOTIFY_TIMEOUT, (GSourceFunc)actually_notify_object, object); } static void on_entry_property_changed (GObject *o, GParamSpec *pspec, IndicatorObject *object) { notify_object (object); } static void on_entry_changed (GObject *o, IndicatorObject *object) { notify_object (object); } static void on_entry_added (IndicatorObject *object, IndicatorObjectEntry *entry, PanelService *self) { g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (entry != NULL); self->priv->removed_entries = g_slist_remove (self->priv->removed_entries, entry); gchar *entry_id = get_indicator_entry_id_by_entry (entry); g_hash_table_insert (self->priv->id2entry_hash, entry_id, entry); if (GTK_IS_LABEL (entry->label)) { g_signal_connect (entry->label, "notify::label", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->label, "notify::sensitive", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->label, "show", G_CALLBACK (on_entry_changed), object); g_signal_connect (entry->label, "hide", G_CALLBACK (on_entry_changed), object); } if (GTK_IS_IMAGE (entry->image)) { g_signal_connect (entry->image, "notify::storage-type", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->image, "notify::file", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->image, "notify::gicon", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->image, "notify::icon-name", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->image, "notify::pixbuf", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->image, "notify::stock", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->image, "notify::sensitive", G_CALLBACK (on_entry_property_changed), object); g_signal_connect (entry->image, "show", G_CALLBACK (on_entry_changed), object); g_signal_connect (entry->image, "hide", G_CALLBACK (on_entry_changed), object); } notify_object (object); } static gboolean on_removed_idle (PanelService *self) { GHashTableIter iter; GHashTable *entry2geometry_hash; IndicatorObjectEntry *entry; gpointer value; GSList *l; for (l = self->priv->removed_entries; l; l = l->next) { entry = l->data; ensure_entry_menu_is_closed (self, NULL, entry); g_hash_table_iter_init (&iter, self->priv->panel2entries_hash); while (g_hash_table_iter_next (&iter, NULL, &value)) { if ((entry2geometry_hash = value)) { g_hash_table_remove (entry2geometry_hash, entry); if (g_hash_table_size (entry2geometry_hash) == 0) { g_hash_table_iter_remove (&iter); } } } } g_slist_free (self->priv->removed_entries); self->priv->removed_entries = NULL; self->priv->remove_idle = 0; return G_SOURCE_REMOVE; } static void on_entry_removed (IndicatorObject *object, IndicatorObjectEntry *entry, PanelService *self) { g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (entry != NULL); gchar *entry_id = get_indicator_entry_id_by_entry (entry); g_hash_table_remove (self->priv->id2entry_hash, entry_id); g_free (entry_id); if (entry->label) { g_signal_handlers_disconnect_by_data (entry->label, object); } if (entry->image) { g_signal_handlers_disconnect_by_data (entry->image, object); } self->priv->removed_entries = g_slist_append (self->priv->removed_entries, entry); if (self->priv->remove_idle) g_source_remove (self->priv->remove_idle); self->priv->remove_idle = g_idle_add ((GSourceFunc) on_removed_idle, self); notify_object (object); } static void on_entry_moved (IndicatorObject *object, IndicatorObjectEntry *entry, PanelService *self) { notify_object (object); } static void on_indicator_menu_show (IndicatorObject *object, IndicatorObjectEntry *entry, guint32 timestamp, PanelService *self) { gchar *entry_id; g_return_if_fail (PANEL_IS_SERVICE (self)); if (!entry) { panel_service_close_active_entry (self); return; } entry_id = get_indicator_entry_id_by_entry (entry); g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id); g_free (entry_id); } static const gchar * indicator_environment[] = { "unity", "unity-3d", "unity-panel-service", "unity-all-menus", NULL }; static void load_indicator (PanelService *self, IndicatorObject *object, const gchar *_name) { PanelServicePrivate *priv = self->priv; gchar *name; GList *entries, *entry; indicator_object_set_environment (object, (GStrv)indicator_environment); if (_name != NULL) name = g_strdup (_name); else name = g_strdup_printf ("%p", object); priv->indicators = g_slist_append (priv->indicators, object); if (!priv->appmenu_indicator && g_strcmp0 (name, APPMENU_INDICATOR_NAME) == 0) priv->appmenu_indicator = object; g_object_set_data_full (G_OBJECT (object), "id", g_strdup (name), g_free); g_signal_connect (object, INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK (on_entry_added), self); g_signal_connect (object, INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, G_CALLBACK (on_entry_removed), self); g_signal_connect (object, INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED, G_CALLBACK (on_entry_moved), self); g_signal_connect (object, INDICATOR_OBJECT_SIGNAL_MENU_SHOW, G_CALLBACK (on_indicator_menu_show), self); entries = indicator_object_get_entries (object); for (entry = entries; entry != NULL; entry = entry->next) { on_entry_added (object, entry->data, self); } g_list_free (entries); g_free (name); } static void load_indicators (PanelService *self) { GDir *dir; const gchar *name; if (!g_file_test (INDICATORDIR, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { g_warning ("%s does not exist, cannot read any indicators", INDICATORDIR); gtk_main_quit (); } dir = g_dir_open (INDICATORDIR, 0, NULL); while ((name = g_dir_read_name (dir)) != NULL) { IndicatorObject *object; gchar *path; if (!g_str_has_suffix (name, ".so")) continue; path = g_build_filename (INDICATORDIR, name, NULL); g_debug ("Loading: %s", path); object = indicator_object_new_from_file (path); if (object == NULL) { g_warning ("Unable to load '%s'", path); g_free (path); continue; } load_indicator (self, object, name); g_free (path); } g_dir_close (dir); } static void load_indicators_from_indicator_files (PanelService *self) { GDir *dir; const gchar *name; GError *error = NULL; dir = g_dir_open (INDICATOR_SERVICE_DIR, 0, &error); if (!dir) { g_warning ("unable to open indicator service file directory: %s", error->message); g_error_free (error); return; } while ((name = g_dir_read_name (dir))) { gchar *filename; IndicatorNg *indicator; filename = g_build_filename (INDICATOR_SERVICE_DIR, name, NULL); indicator = indicator_ng_new_for_profile (filename, !lockscreen_mode ? "desktop" : "desktop_lockscreen", &error); if (indicator) { load_indicator (self, INDICATOR_OBJECT (indicator), name); } else { g_warning ("unable to load '%s': %s", name, error->message); g_clear_error (&error); } g_free (filename); } g_dir_close (dir); } static gint name2order (const gchar * name, const gchar * hint) { int i; for (i = 0; indicator_order[i][0] != NULL; i++) { if (g_strcmp0(name, indicator_order[i][0]) == 0 && g_strcmp0(hint, indicator_order[i][1]) == 0) { return i; } } return -1; } static gint name2priority (const gchar * name, const gchar * hint) { gint order = name2order (name, hint); if (order > -1) return order * MAX_INDICATOR_ENTRIES; return order; } static void sort_indicators (PanelService *self) { GSList *i; int k = 0; int floating_indicators = 0; int max_priority = G_N_ELEMENTS(indicator_order) * MAX_INDICATOR_ENTRIES; for (i = self->priv->indicators; i; i = i->next) { IndicatorObject *io = i->data; gint pos; pos = indicator_object_get_position (io); /* Continue using the state ordering as long as there are still * plugins statically defined in this file. Give them a much * higher position though, so that they appear to the right of the * indicators that return a proper position */ if (pos < 0) { gint prio = name2priority (g_object_get_data (G_OBJECT (io), "id"), NULL); if (prio < 0) { prio = name2priority ("floating-indicators", NULL) + floating_indicators; floating_indicators++; } pos = max_priority - prio; } /* unity's concept of priorities is inverse to ours right now */ g_object_set_data (G_OBJECT (i->data), "priority", GINT_TO_POINTER (max_priority - pos)); g_object_set_data (G_OBJECT (i->data), "position", GINT_TO_POINTER (k)); self->priv->timeouts[k] = SYNC_NEUTRAL; k++; } } static gchar * gtk_image_to_data (GtkImage *image, guint32 *storage_type) { if (!GTK_IS_IMAGE (image)) return NULL; *storage_type = gtk_image_get_storage_type (image); gchar *ret = NULL; switch (*storage_type) { case GTK_IMAGE_PIXBUF: { gchar *file; g_object_get (image, "file", &file, NULL); if (file) { if (file[0] != '\0') { *storage_type = GTK_IMAGE_ICON_NAME; ret = file; break; } g_free (file); } GdkPixbuf *pixbuf; gchar *buffer = NULL; gsize buffer_size = 0; GError *error = NULL; pixbuf = gtk_image_get_pixbuf (image); if (gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &buffer_size, "png", &error, NULL)) { ret = g_base64_encode ((const guchar *)buffer, buffer_size); g_free (buffer); } else { g_warning ("Unable to convert pixbuf to png data: '%s'", error ? error->message : "unknown"); if (error) g_error_free (error); } break; } case GTK_IMAGE_STOCK: { g_object_get (G_OBJECT (image), "stock", &ret, NULL); break; } case GTK_IMAGE_ICON_NAME: { g_object_get (G_OBJECT (image), "icon-name", &ret, NULL); break; } case GTK_IMAGE_GICON: { GIcon *icon = NULL; gtk_image_get_gicon (image, &icon, NULL); if (G_IS_ICON (icon)) ret = g_icon_to_string (icon); break; } case GTK_IMAGE_EMPTY: { break; } default: { g_warning ("Unable to support GtkImageType: %u", *storage_type); } } return ret; } static void indicator_entry_to_variant (IndicatorObjectEntry *entry, const gchar *id, const gchar *indicator_id, GVariantBuilder *b, gint prio) { gboolean is_label = GTK_IS_LABEL (entry->label); gboolean is_image = GTK_IS_IMAGE (entry->image); guint32 image_type = 0; gchar *image_data = gtk_image_to_data (entry->image, &image_type); g_variant_builder_add (b, ENTRY_SIGNATURE, indicator_id, id, entry->name_hint ? entry->name_hint : "", entry->parent_window, is_label ? gtk_label_get_label (entry->label) : "", is_label ? gtk_widget_get_sensitive (GTK_WIDGET (entry->label)) : FALSE, is_label ? gtk_widget_get_visible (GTK_WIDGET (entry->label)) : FALSE, is_image ? image_type : 0, image_data ? image_data : "", is_image ? gtk_widget_get_sensitive (GTK_WIDGET (entry->image)) : FALSE, is_image ? gtk_widget_get_visible (GTK_WIDGET (entry->image)) : FALSE, prio); g_free (image_data); } static void indicator_entry_null_to_variant (const gchar *indicator_id, GVariantBuilder *b) { g_variant_builder_add (b, ENTRY_SIGNATURE, indicator_id, "", "", 0, "", FALSE, FALSE, 0, "", FALSE, FALSE, -1); } static void indicator_object_full_to_variant (PanelService *self, IndicatorObject *object, const gchar *indicator_id, GVariantBuilder *b) { GList *entries, *e; GHashTable *index_hash = NULL; gint parent_prio = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (object), "priority")); entries = indicator_object_get_entries (object); guint index = 0; if (entries) { if (object == self->priv->appmenu_indicator) index_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); for (e = entries; e; e = e->next) { gint prio = -1; IndicatorObjectEntry *entry = e->data; gchar *id = get_indicator_entry_id_by_entry (entry); if (entry->name_hint) { prio = name2priority (indicator_id, entry->name_hint); } if (prio < 0) { if (index_hash) { index = GPOINTER_TO_UINT (g_hash_table_lookup (index_hash, GUINT_TO_POINTER (entry->parent_window))); } prio = parent_prio + index; index++; if (index_hash) { g_hash_table_insert (index_hash, GUINT_TO_POINTER (entry->parent_window), GUINT_TO_POINTER (index)); } } indicator_entry_to_variant (entry, id, indicator_id, b, prio); g_free (id); } if (index_hash) g_hash_table_destroy (index_hash); g_list_free (entries); } else { /* Add a null entry to indicate that there is an indicator here, it's just empty */ indicator_entry_null_to_variant (indicator_id, b); } } static void indicator_object_to_variant (PanelService *self, IndicatorObject *object, const gchar *indicator_id, GVariantBuilder *b) { if (!GPOINTER_TO_INT (g_object_get_data (G_OBJECT (object), "remove"))) { indicator_object_full_to_variant (self, object, indicator_id, b); } else { indicator_entry_null_to_variant (indicator_id, b); panel_service_actually_remove_indicator (self, object); } } static int get_monitor_at (gint x, gint y) { gint i; gdouble scale; GdkScreen *screen = gdk_screen_get_default (); gint monitors = gdk_screen_get_n_monitors (screen); for (i = 0; i < monitors; ++i) { GdkRectangle rect = { 0 }; gdk_screen_get_monitor_geometry (screen, i, &rect); scale = gdk_screen_get_monitor_scale_factor (screen, i); if (scale != 1.0) { rect.x *= scale; rect.y *= scale; rect.width *= scale; rect.height *= scale; } if (rect_contains_point (&rect, x, y)) { return i; } } return gdk_screen_get_monitor_at_point (screen, x, y); } static int get_monitor_scale_at (gint x, gint y) { gint monitor = get_monitor_at (x, y); return gdk_screen_get_monitor_scale_factor (gdk_screen_get_default (), monitor); } static void positon_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push, gpointer user_data) { PanelService *self = PANEL_SERVICE (user_data); PanelServicePrivate *priv = self->priv; GdkScreen *screen = gdk_screen_get_default (); gint monitor = get_monitor_at (priv->last_x, priv->last_y); gtk_menu_set_monitor (menu, monitor); gint scale = gdk_screen_get_monitor_scale_factor (screen, monitor); *x = priv->last_x / scale; *y = priv->last_y / scale; *push = TRUE; } static void on_active_menu_hidden (GtkMenu *menu, PanelService *self) { PanelServicePrivate *priv = self->priv; GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_GET_CLASS (priv->last_menu); g_signal_handlers_disconnect_by_data (priv->last_menu, self); if (menu_shell_class && default_menu_shell_deactivate && menu_shell_class->deactivate != default_menu_shell_deactivate) { menu_shell_class->deactivate = default_menu_shell_deactivate; } priv->last_x = 0; priv->last_y = 0; priv->last_menu_button = 0; priv->last_panel = NULL; priv->last_menu = NULL; priv->last_entry = NULL; priv->last_left = 0; priv->last_right = 0; priv->last_top = 0; priv->last_bottom = 0; priv->last_open_time = 0; priv->use_event = FALSE; priv->pressed_entry = NULL; g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, "", "", 0, 0, 0, 0); } /* * Public Methods */ GVariant * panel_service_sync (PanelService *self) { GVariantBuilder b; IndicatorObject *indicator; GSList *i; gint position; g_variant_builder_init (&b, G_VARIANT_TYPE ("("ENTRY_ARRAY_SIGNATURE")")); g_variant_builder_open (&b, G_VARIANT_TYPE (ENTRY_ARRAY_SIGNATURE)); for (i = self->priv->indicators; i;) { /* An indicator could be removed during this cycle, so we should be safe */ indicator = i->data; i = i->next; const gchar *indicator_id = g_object_get_data (G_OBJECT (indicator), "id"); position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (indicator), "position")); indicator_object_to_variant (self, indicator, indicator_id, &b); /* Set the sync back to neutral */ self->priv->timeouts[position] = SYNC_NEUTRAL; } g_variant_builder_close (&b); return g_variant_builder_end (&b); } GVariant * panel_service_sync_one (PanelService *self, const gchar *indicator_id) { GVariantBuilder b; GSList *i; g_variant_builder_init (&b, G_VARIANT_TYPE ("("ENTRY_ARRAY_SIGNATURE")")); g_variant_builder_open (&b, G_VARIANT_TYPE (ENTRY_ARRAY_SIGNATURE)); for (i = self->priv->indicators; i; i = i->next) { if (g_strcmp0 (indicator_id, g_object_get_data (G_OBJECT (i->data), "id")) == 0) { gint position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (i->data), "position")); indicator_object_to_variant (self, i->data, indicator_id, &b); /* Set the sync back to neutral */ self->priv->timeouts[position] = SYNC_NEUTRAL; break; } } g_variant_builder_close (&b); return g_variant_builder_end (&b); } void panel_service_sync_geometry (PanelService *self, const gchar *panel_id, const gchar *entry_id, gint x, gint y, gint width, gint height) { IndicatorObject *object; IndicatorObjectEntry *entry; GHashTable *entry2geometry_hash; gboolean valid_entry = TRUE; PanelServicePrivate *priv = self->priv; entry = get_indicator_entry_by_id (self, entry_id); /* If the entry we read is not valid, maybe it has already been removed * or unparented, so we need to make sure that the related key on the * entry2geometry_hash is correctly removed and the value is free'd */ if (!entry) { IndicatorObjectEntry *invalid_entry; if (sscanf (entry_id, "%p", &invalid_entry) == 1) { entry = invalid_entry; valid_entry = FALSE; } } if (entry) { entry2geometry_hash = g_hash_table_lookup (priv->panel2entries_hash, panel_id); if (width < 0 || height < 0 || !valid_entry) { if (valid_entry) { GSList *l; ensure_entry_menu_is_closed (self, panel_id, entry); if ((l = g_slist_find (self->priv->dropdown_entries, entry))) { valid_entry = FALSE; self->priv->dropdown_entries = g_slist_delete_link (self->priv->dropdown_entries, l); g_hash_table_remove (self->priv->id2entry_hash, entry->name_hint); g_free (entry); } } if (entry2geometry_hash) { g_hash_table_remove (entry2geometry_hash, entry); if (g_hash_table_size (entry2geometry_hash) == 0) { g_hash_table_remove (priv->panel2entries_hash, panel_id); } } } else { GdkRectangle *geo = NULL; if (entry2geometry_hash == NULL) { entry2geometry_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); g_hash_table_insert (priv->panel2entries_hash, g_strdup (panel_id), entry2geometry_hash); } else { geo = g_hash_table_lookup (entry2geometry_hash, entry); } if (geo == NULL) { geo = g_new0 (GdkRectangle, 1); g_hash_table_insert (entry2geometry_hash, entry, geo); } /* If the current entry geometry has changed, we need to move the menu * accordingly to the change we recorded! */ if (GTK_IS_MENU (priv->last_menu) && priv->last_menu == entry->menu && g_strcmp0 (priv->last_panel, panel_id) == 0) { GtkWidget *top_widget = gtk_widget_get_toplevel (GTK_WIDGET (priv->last_menu)); if (GTK_IS_WINDOW (top_widget)) { GtkWindow *top_win = GTK_WINDOW (top_widget); gint old_x, old_y; gint scale = get_monitor_scale_at (x, y); gtk_window_get_position (top_win, &old_x, &old_y); gtk_window_move (top_win, old_x - (geo->x - x) / scale, old_y - (geo->y - y) / scale); } } geo->x = x; geo->y = y; geo->width = width; geo->height = height; } if (valid_entry) { object = get_entry_parent_indicator (entry); g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height); } } } static gboolean panel_service_entry_is_visible (PanelService *self, IndicatorObjectEntry *entry) { g_return_val_if_fail (PANEL_IS_SERVICE (self), FALSE); g_return_val_if_fail (entry != NULL, FALSE); if (GTK_IS_LABEL (entry->label)) { if (gtk_widget_get_visible (GTK_WIDGET (entry->label)) && gtk_widget_is_sensitive (GTK_WIDGET (entry->label))) { return TRUE; } } if (GTK_IS_IMAGE (entry->image)) { if (gtk_widget_get_visible (GTK_WIDGET (entry->image)) && gtk_widget_is_sensitive (GTK_WIDGET (entry->image))) { return TRUE; } } if (g_slist_find (self->priv->dropdown_entries, entry)) return TRUE; return FALSE; } static gboolean panel_service_entry_is_visible_on_panel (PanelService *self, IndicatorObjectEntry *entry, const gchar *panel_id) { GHashTable *entry2geometry_hash; GHashTableIter panel_iter; gpointer key, value; gboolean found_geo; g_return_val_if_fail (PANEL_IS_SERVICE (self), FALSE); g_return_val_if_fail (entry != NULL, FALSE); found_geo = FALSE; if (panel_id) { entry2geometry_hash = g_hash_table_lookup (self->priv->panel2entries_hash, panel_id); if (entry2geometry_hash && g_hash_table_lookup (entry2geometry_hash, entry)) { found_geo = TRUE; } } else { g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash); while (g_hash_table_iter_next (&panel_iter, &key, &value) && !found_geo) { entry2geometry_hash = value; if (g_hash_table_lookup (entry2geometry_hash, entry)) { found_geo = TRUE; } } } if (!found_geo) return FALSE; return panel_service_entry_is_visible (self, entry); } static int indicator_entry_compare_func (gpointer* v1, gpointer* v2) { return (GPOINTER_TO_INT (v1[1]) > GPOINTER_TO_INT (v2[1])) ? 1 : -1; } static void activate_next_prev_menu (PanelService *self, IndicatorObjectEntry *entry, GtkMenuDirectionType direction) { IndicatorObjectEntry *new_entry; PanelServicePrivate *priv = self->priv; GSList *indicators = priv->indicators; GList *ordered_entries = NULL; GList *entries; gchar *id; GSList *l, *sl; GList *ll; for (l = indicators; l; l = l->next) { IndicatorObject *object = l->data; gint parent_priority = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (object), "priority")); entries = indicator_object_get_entries (object); const gchar *indicator_id = g_object_get_data (G_OBJECT (object), "id"); if (entries) { int index = 0; if (!priv->last_dropdown_entry) { for (sl = priv->dropdown_entries; sl; sl = sl->next) { IndicatorObjectEntry *dropdown = sl->data; if (dropdown->parent_object == object) entries = g_list_append (entries, dropdown); } } for (ll = entries; ll; ll = ll->next) { gint prio = -1; new_entry = ll->data; if (!priv->last_dropdown_entry || new_entry != entry) { if (!panel_service_entry_is_visible_on_panel (self, new_entry, priv->last_panel)) continue; } if (new_entry->name_hint) { prio = name2priority (indicator_id, new_entry->name_hint); } if (prio < 0) { prio = parent_priority + index; index++; } gpointer *values = g_new (gpointer, 2); values[0] = new_entry; values[1] = GINT_TO_POINTER (prio); ordered_entries = g_list_insert_sorted (ordered_entries, values, (GCompareFunc) indicator_entry_compare_func); } g_list_free (entries); } } new_entry = NULL; for (ll = ordered_entries; ll; ll = ll->next) { gpointer *values = ll->data; if (entry == values[0]) { if (direction == GTK_MENU_DIR_CHILD) { values = ll->next ? ll->next->data : ordered_entries->data; } else { values = ll->prev ? ll->prev->data : g_list_last (ordered_entries)->data; } new_entry = values[0]; break; } } if (new_entry) { id = get_indicator_entry_id_by_entry (new_entry); g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, id); g_free (id); } g_list_free_full (ordered_entries, g_free); } static void on_active_menu_move_current (GtkMenu *menu, GtkMenuDirectionType direction, PanelService *self) { PanelServicePrivate *priv; IndicatorObjectEntry *entry; g_return_if_fail (PANEL_IS_SERVICE (self)); priv = self->priv; /* Not interested in up or down */ if (direction == GTK_MENU_DIR_NEXT || direction == GTK_MENU_DIR_PREV) return; /* We don't want to distrupt going into submenus */ if (direction == GTK_MENU_DIR_CHILD) { GList *children, *c; children = gtk_container_get_children (GTK_CONTAINER (menu)); for (c = children; c; c = c->next) { GtkWidget *item = (GtkWidget *)c->data; if (GTK_IS_MENU_ITEM (item) && gtk_widget_get_state_flags (item) & GTK_STATE_FLAG_PRELIGHT && gtk_menu_item_get_submenu (GTK_MENU_ITEM (item))) { /* Skip direction due to there being a submenu, * and we don't want to inhibit going into that */ g_list_free (children); return; } } g_list_free (children); entry = (priv->last_dropdown_entry) ? priv->last_dropdown_entry : priv->last_entry; } else { entry = priv->last_entry; } /* Find the next/prev indicator */ activate_next_prev_menu (self, entry, direction); } static void menuitem_activated (GtkWidget *menuitem, IndicatorObjectEntry *entry) { IndicatorObject *object = get_entry_parent_indicator (entry); indicator_object_entry_activate (object, entry, CurrentTime); } static void menu_shell_deactivate_override (GtkMenuShell *menu_shell) { PanelService *self = panel_service_get_default (); const gchar *entry_id; if (gtk_get_current_event () && GTK_MENU (menu_shell) == self->priv->last_menu) { guint64 ms_open = (g_get_monotonic_time () - self->priv->last_open_time) / 1000; if (ms_open < 50) { /* If the menu shell deactivation was requested by an event, we ensure this * didn't happen too early to activation, or there could be a race causing * no menu to appear. Also since the item is now marked as inactive, we should * manually highlight it. */ entry_id = get_indicator_entry_id_by_menu (self, self->priv->last_menu); if (entry_id) g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id); return; } } default_menu_shell_deactivate (menu_shell); } static void panel_service_show_entry_common (PanelService *self, IndicatorObject *object, IndicatorObjectEntry *entry, GtkMenu *fake_menu, guint32 xid, gint32 x, gint32 y, guint32 button) { PanelServicePrivate *priv; GtkWidget *last_menu; GtkMenuShellClass *menu_shell_class; g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (INDICATOR_IS_OBJECT (object)); g_return_if_fail (entry); priv = self->priv; if (priv->last_entry == entry) return; last_menu = GTK_WIDGET (priv->last_menu); if (GTK_IS_MENU (priv->last_menu)) { priv->last_x = 0; priv->last_y = 0; g_signal_handlers_disconnect_by_data (priv->last_menu, self); priv->last_panel = NULL; priv->last_entry = NULL; priv->last_menu = NULL; priv->last_menu_button = 0; } if (entry != NULL) { if (GTK_IS_MENU (entry->menu) || fake_menu) { if (xid > 0) { indicator_object_entry_activate_window (object, entry, xid, CurrentTime); } else { indicator_object_entry_activate (object, entry, CurrentTime); } priv->last_menu = (fake_menu) ? fake_menu : entry->menu; } else { /* For some reason, this entry doesn't have a menu. To simplify the rest of the code and to keep scrubbing fluidly, we'll create a stub menu for the duration of this scrub. */ priv->last_menu = GTK_MENU (gtk_menu_new ()); GtkWidget *menu_item = gtk_menu_item_new_with_label (_("Activate")); gtk_menu_shell_append (GTK_MENU_SHELL (priv->last_menu), menu_item); gtk_widget_show (menu_item); g_signal_connect (priv->last_menu, "deactivate", G_CALLBACK (gtk_widget_destroy), NULL); g_signal_connect (priv->last_menu, "destroy", G_CALLBACK (gtk_widget_destroyed), &priv->last_menu); g_signal_connect (menu_item, "activate", G_CALLBACK (menuitem_activated), entry); } priv->last_entry = entry; priv->last_x = x; priv->last_y = y; priv->last_menu_button = button; priv->last_panel = get_panel_for_parent_at (self, xid, x, y); g_signal_connect (priv->last_menu, "hide", G_CALLBACK (on_active_menu_hidden), self); g_signal_connect_after (priv->last_menu, "move-current", G_CALLBACK (on_active_menu_move_current), self); /* Override the menu deactivation in order to prevent it to close too early */ menu_shell_class = GTK_MENU_SHELL_GET_CLASS (priv->last_menu); if (menu_shell_class && menu_shell_class->deactivate != menu_shell_deactivate_override) { default_menu_shell_deactivate = menu_shell_class->deactivate; menu_shell_class->deactivate = menu_shell_deactivate_override; } gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (priv->last_menu), TRUE); gtk_menu_popup (priv->last_menu, NULL, NULL, positon_menu, self, button, CurrentTime); gboolean visible = gtk_widget_is_visible (GTK_WIDGET (priv->last_menu)); if (!visible) { /* If the menu is not visible at this point, it's very likely that's * due to a keyboard grab, so let's try with a menu with no key-grab */ gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (priv->last_menu), FALSE); gtk_menu_popup (priv->last_menu, NULL, NULL, positon_menu, self, button, CurrentTime); visible = gtk_widget_is_visible (GTK_WIDGET (priv->last_menu)); } if (visible) { gtk_menu_reposition (priv->last_menu); GdkWindow *gdkwin = gtk_widget_get_window (GTK_WIDGET (priv->last_menu)); gint left=0, top=0, width=0, height=0; gdk_window_get_geometry (gdkwin, NULL, NULL, &width, &height); gdk_window_get_origin (gdkwin, &left, &top); gchar *entry_id = get_indicator_entry_id_by_entry (entry); g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, priv->last_panel, entry_id, left, top, width, height); g_free (entry_id); priv->last_left = left; priv->last_right = left + width -1; priv->last_top = top; priv->last_bottom = top + height -1; priv->last_open_time = g_get_monotonic_time (); } else { on_active_menu_hidden (priv->last_menu, self); } } /* We popdown the old one last so we don't accidently send key focus back to the * active application (which will make it change colour (as state changes), which * then looks like flickering to the user. */ if (GTK_IS_MENU (last_menu)) gtk_menu_popdown (GTK_MENU (last_menu)); } void panel_service_show_entry (PanelService *self, const gchar *entry_id, guint32 xid, gint32 x, gint32 y, guint32 button) { IndicatorObject *object; IndicatorObjectEntry *entry; g_return_if_fail (PANEL_IS_SERVICE (self)); entry = get_indicator_entry_by_id (self, entry_id); object = get_entry_parent_indicator (entry); panel_service_show_entry_common (self, object, entry, NULL, xid, x, y, button); } static void on_drop_down_menu_hidden (GtkWidget *menu) { GList *l, *children; children = gtk_container_get_children (GTK_CONTAINER (menu)); for (l = children; l; l = l->next) { if (!GTK_IS_MENU_ITEM (l->data)) continue; GtkMenuItem *menu_item = GTK_MENU_ITEM (l->data); GtkWidget *entry_menu = gtk_menu_item_get_submenu (menu_item); GtkWidget *old_attached = g_object_get_data (G_OBJECT (entry_menu), "ups-attached-widget"); gtk_menu_item_set_submenu (menu_item, NULL); if (old_attached) gtk_menu_attach_to_widget (GTK_MENU (entry_menu), old_attached, NULL); } g_list_free (children); gtk_widget_destroy (menu); } void panel_service_show_entries (PanelService *self, gchar **entries, const gchar *selected, guint32 xid, gint32 x, gint32 y) { gint i; IndicatorObject *object; IndicatorObjectEntry *entry, *selected_entry, *first_entry, *last_entry; GtkWidget *menu; g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (entries && entries[0]); first_entry = get_indicator_entry_by_id (self, entries[0]); if (first_entry == self->priv->last_entry) return; if (!first_entry) { g_warning ("Impossible to show entries for an invalid entry or object"); return; } last_entry = NULL; menu = gtk_menu_new (); selected_entry = get_indicator_entry_by_id (self, selected); object = get_entry_parent_indicator (first_entry); for (i = 0; entries[i]; ++i) { entry = get_indicator_entry_by_id (self, entries[i]); if (entry != first_entry && !last_entry) continue; GtkWidget *menu_item; const char *label = NULL; if (GTK_IS_LABEL (entry->label)) label = gtk_label_get_label (entry->label); if (GTK_IS_IMAGE (entry->image)) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS if (label) { menu_item = gtk_image_menu_item_new_with_mnemonic (label); } else { menu_item = gtk_image_menu_item_new (); } gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), GTK_WIDGET (entry->image)); gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menu_item), TRUE); G_GNUC_END_IGNORE_DEPRECATIONS } else if (label) { menu_item = gtk_menu_item_new_with_mnemonic (label); } else { continue; } GtkWidget *old_attached = gtk_menu_get_attach_widget (entry->menu); if (old_attached) { g_object_set_data (G_OBJECT (entry->menu), "ups-attached-widget", old_attached); gtk_menu_detach (entry->menu); } gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), GTK_WIDGET (entry->menu)); gtk_widget_show (menu_item); last_entry = entry; if (entry == selected_entry) gtk_menu_shell_select_item (GTK_MENU_SHELL (menu), menu_item); } if (!last_entry) { g_warning ("No valid entries to show"); gtk_widget_destroy (menu); return; } g_signal_connect_after (menu, "hide", G_CALLBACK (on_drop_down_menu_hidden), NULL); g_signal_connect (menu, "destroy", G_CALLBACK (gtk_widget_destroyed), &self->priv->last_dropdown_entry); panel_service_show_entry_common (self, object, first_entry, GTK_MENU (menu), xid, x, y, 1); self->priv->last_dropdown_entry = last_entry; } void panel_service_show_app_menu (PanelService *self, guint32 xid, gint32 x, gint32 y) { IndicatorObject *object; IndicatorObjectEntry *entry; GList *entries; g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (INDICATOR_IS_OBJECT (self->priv->appmenu_indicator)); object = self->priv->appmenu_indicator; entries = indicator_object_get_entries (object); if (entries) { entry = entries->data; g_list_free (entries); panel_service_show_entry_common (self, object, entry, NULL, xid, x, y, 1); } } void panel_service_secondary_activate_entry (PanelService *self, const gchar *entry_id) { IndicatorObject *object; IndicatorObjectEntry *entry; g_return_if_fail (PANEL_IS_SERVICE (self)); entry = get_indicator_entry_by_id (self, entry_id); g_return_if_fail (entry); object = get_entry_parent_indicator (entry); g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry, CurrentTime); } void panel_service_scroll_entry (PanelService *self, const gchar *entry_id, gint32 delta) { IndicatorObject *object; IndicatorObjectEntry *entry; g_return_if_fail (PANEL_IS_SERVICE (self)); entry = get_indicator_entry_by_id (self, entry_id); g_return_if_fail (entry); GdkScrollDirection direction = G_MAXINT; switch (delta) { case NUX_VERTICAL_SCROLL_DELTA: direction = INDICATOR_OBJECT_SCROLL_UP; break; case -NUX_VERTICAL_SCROLL_DELTA: direction = INDICATOR_OBJECT_SCROLL_DOWN; break; case NUX_HORIZONTAL_SCROLL_DELTA: direction = INDICATOR_OBJECT_SCROLL_LEFT; break; case -NUX_HORIZONTAL_SCROLL_DELTA: direction = INDICATOR_OBJECT_SCROLL_RIGHT; break; } if (direction != G_MAXINT) { object = get_entry_parent_indicator (entry); g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry, 1, direction); } } void panel_service_close_active_entry (PanelService *self) { g_return_if_fail (PANEL_IS_SERVICE (self)); if (GTK_IS_MENU (self->priv->last_menu)) { self->priv->last_open_time = 0; gtk_menu_popdown (GTK_MENU (self->priv->last_menu)); } } ./services/panel-a11y.h0000644000004100000410000000147313437202764015124 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #ifndef _PANEL_A11Y_H_ #define _PANEL_A11Y_H_ #include G_BEGIN_DECLS void panel_a11y_init (void); G_END_DECLS #endif ./services/unity-panel-service.service.in0000644000004100000410000000041013437202764020763 0ustar www-datawww-data[Unit] Description=Backing Service for the Unity Panel After=unity7.service PartOf=graphical-session.target BindsTo=indicators-pre.target [Service] ExecStartPre=@UNITY_LIBDIR@/systemd-prestart-check ExecStart=@UNITY_LIBDIR@/unity-panel-service Restart=on-failure ./services/panel-service.h0000644000004100000410000001144213437202764016006 0ustar www-datawww-data/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel */ #ifndef _PANEL_SERVICE_H_ #define _PANEL_SERVICE_H_ #include #include #include G_BEGIN_DECLS #define PANEL_TYPE_SERVICE (panel_service_get_type ()) #define PANEL_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ PANEL_TYPE_SERVICE, PanelService)) #define PANEL_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ PANEL_TYPE_SERVICE, PanelServiceClass)) #define PANEL_IS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ PANEL_TYPE_SERVICE)) #define PANEL_IS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ PANEL_TYPE_SERVICE)) #define PANEL_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ PANEL_TYPE_SERVICE, PanelServiceClass)) typedef struct _PanelService PanelService; typedef struct _PanelServiceClass PanelServiceClass; typedef struct _PanelServicePrivate PanelServicePrivate; struct _PanelService { GObject parent; PanelServicePrivate *priv; }; struct _PanelServiceClass { GObjectClass parent_class; }; GType panel_service_get_type (void) G_GNUC_CONST; PanelService * panel_service_get_default (); PanelService * panel_service_get_default_with_indicators (GList *indicators); void panel_service_set_lockscreen_mode (gboolean enable); guint panel_service_get_n_indicators (PanelService *self); IndicatorObject * panel_service_get_indicator_nth (PanelService *self, guint position); IndicatorObject * panel_service_get_indicator (PanelService *self, const gchar *indicator_id); void panel_service_add_indicator (PanelService *self, IndicatorObject *indicator); void panel_service_remove_indicator (PanelService *self, IndicatorObject *indicator); void panel_service_clear_indicators (PanelService *self); void panel_service_clear_remote_data (PanelService *self); GVariant * panel_service_sync (PanelService *self); GVariant * panel_service_sync_one (PanelService *self, const gchar *indicator_id); void panel_service_sync_geometry (PanelService *self, const gchar *indicator_id, const gchar *entry_id, gint x, gint y, gint width, gint height); void panel_service_show_entry (PanelService *self, const gchar *entry_id, guint32 xid, gint32 x, gint32 y, guint32 button); void panel_service_show_entries (PanelService *self, gchar **entries, const gchar *selected, guint32 xid, gint32 x, gint32 y); void panel_service_show_app_menu (PanelService *self, guint32 xid, gint32 x, gint32 y); void panel_service_secondary_activate_entry (PanelService *self, const gchar *entry_id); void panel_service_scroll_entry (PanelService *self, const gchar *entry_id, gint32 delta); void panel_service_close_active_entry (PanelService *self); G_END_DECLS #endif /* _PANEL_SERVICE_H_ */ ./services/unity-panel-service-lockscreen.override0000644000004100000410000000000713437202764022665 0ustar www-datawww-datamanual ./services/unity-panel-service-lockscreen.service.in0000644000004100000410000000037413437202764023122 0ustar www-datawww-data[Unit] Description=Backing Service for the Unity Panel in Lockscreen mode PartOf=unity-screen-locked.target [Service] ExecStartPre=@UNITY_LIBDIR@/systemd-prestart-check ExecStart=@UNITY_LIBDIR@/unity-panel-service --lockscreen-mode Restart=on-failure ./services/panel-indicator-entry-accessible.c0000644000004100000410000002456313437202764021557 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #include "panel-indicator-entry-accessible.h" #include "panel-service.h" /* AtkObject methods */ static void piea_component_interface_init (AtkComponentIface *iface); static void panel_indicator_entry_accessible_initialize (AtkObject *accessible, gpointer data); static gint panel_indicator_entry_accessible_get_n_children (AtkObject *accessible); static AtkObject *panel_indicator_entry_accessible_ref_child (AtkObject *accessible, gint i); static AtkStateSet *panel_indicator_entry_accessible_ref_state_set (AtkObject *accessible); struct _PanelIndicatorEntryAccessiblePrivate { IndicatorObjectEntry *entry; GtkMenu * menu; PanelService *service; gint x; gint y; gint width; gint height; gboolean active; }; G_DEFINE_TYPE_WITH_CODE(PanelIndicatorEntryAccessible, panel_indicator_entry_accessible, ATK_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, piea_component_interface_init)) #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE, PanelIndicatorEntryAccessiblePrivate)) static void on_entry_activated_cb (PanelService *service, const gchar *panel, const gchar *entry_id, gint x, gint y, guint w, guint h, gpointer user_data) { gchar *s; gboolean adding = FALSE; PanelIndicatorEntryAccessible *piea; g_return_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (user_data)); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (user_data); /* The PanelService sends us a string containing the pointer to the IndicatorObjectEntry */ s = g_strdup_printf ("%p", piea->priv->entry); if (g_str_equal (s, entry_id)) { adding = TRUE; piea->priv->active = TRUE; } else { piea->priv->active = FALSE; } /* Notify AT's about the states' changes */ atk_object_notify_state_change (ATK_OBJECT (piea), ATK_STATE_ACTIVE, adding); atk_object_notify_state_change (ATK_OBJECT (piea), ATK_STATE_FOCUSED, adding); atk_object_notify_state_change (ATK_OBJECT (piea), ATK_STATE_SHOWING, adding); g_free (s); } static void on_geometries_changed_cb (PanelService *service, IndicatorObject *object, IndicatorObjectEntry *entry, gint x, gint y, gint width, gint height, gpointer user_data) { PanelIndicatorEntryAccessible *piea; AtkRectangle rect; piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (user_data); g_return_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (piea)); if (entry != piea->priv->entry) return; piea->priv->x = x; piea->priv->y = y; piea->priv->width = width; piea->priv->height = height; /* Notify ATK objects of change of coordinates */ rect.x = piea->priv->x; rect.y = piea->priv->y; rect.width = piea->priv->width; rect.height = piea->priv->height; g_signal_emit_by_name (ATK_COMPONENT (piea), "bounds-changed", &rect); } static void panel_indicator_entry_accessible_dispose (GObject *object) { PanelIndicatorEntryAccessible *piea; g_return_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (object)); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (object); if (piea->priv != NULL) { piea->priv->entry = NULL; g_clear_object(&piea->priv->menu); } G_OBJECT_CLASS (panel_indicator_entry_accessible_parent_class)->dispose (object); return; } static void panel_indicator_entry_accessible_finalize (GObject *object) { PanelIndicatorEntryAccessible *piea; g_return_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (object)); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (object); if (piea->priv != NULL) { g_signal_handlers_disconnect_by_func (piea->priv->service, on_entry_activated_cb, piea); g_signal_handlers_disconnect_by_func (piea->priv->service, on_geometries_changed_cb, piea); } G_OBJECT_CLASS (panel_indicator_entry_accessible_parent_class)->finalize (object); } static void panel_indicator_entry_accessible_class_init (PanelIndicatorEntryAccessibleClass *klass) { GObjectClass *object_class; AtkObjectClass *atk_class; /* GObject */ object_class = G_OBJECT_CLASS (klass); object_class->dispose = panel_indicator_entry_accessible_dispose; object_class->finalize = panel_indicator_entry_accessible_finalize; /* AtkObject */ atk_class = ATK_OBJECT_CLASS (klass); atk_class->initialize = panel_indicator_entry_accessible_initialize; atk_class->get_n_children = panel_indicator_entry_accessible_get_n_children; atk_class->ref_child = panel_indicator_entry_accessible_ref_child; atk_class->ref_state_set = panel_indicator_entry_accessible_ref_state_set; g_type_class_add_private (object_class, sizeof (PanelIndicatorEntryAccessiblePrivate)); } static void panel_indicator_entry_accessible_init (PanelIndicatorEntryAccessible *piea) { piea->priv = GET_PRIVATE (piea); piea->priv->x = piea->priv->y = piea->priv->width = piea->priv->height = 0; /* Set up signals for listening to service changes */ piea->priv->active = FALSE; piea->priv->service = panel_service_get_default (); g_signal_connect (piea->priv->service, "geometries-changed", G_CALLBACK (on_geometries_changed_cb), piea); g_signal_connect (piea->priv->service, "entry-activated", G_CALLBACK (on_entry_activated_cb), piea); } AtkObject * panel_indicator_entry_accessible_new (IndicatorObjectEntry *entry) { PanelIndicatorEntryAccessible *piea; piea = g_object_new (PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE, NULL); atk_object_initialize (ATK_OBJECT (piea), entry); return ATK_OBJECT (piea); } IndicatorObjectEntry * panel_indicator_entry_accessible_get_entry (PanelIndicatorEntryAccessible *piea) { g_return_val_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (piea), NULL); return piea->priv->entry; } /* Implementation of AtkObject methods */ static void panel_indicator_entry_accessible_get_extents (AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type) { PanelIndicatorEntryAccessible *piea; g_return_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (component)); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (component); /* We ignore AtkCoordType for now, as the panel is always at the top left corner and so relative and absolute coordinates are the same */ *x = piea->priv->x; *y = piea->priv->y; *width = piea->priv->width; *height = piea->priv->height; } static void piea_component_interface_init (AtkComponentIface *iface) { g_return_if_fail (iface != NULL); iface->get_extents = panel_indicator_entry_accessible_get_extents; } static void panel_indicator_entry_accessible_initialize (AtkObject *accessible, gpointer data) { PanelIndicatorEntryAccessible *piea; g_return_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (accessible)); ATK_OBJECT_CLASS (panel_indicator_entry_accessible_parent_class)->initialize (accessible, data); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible); piea->priv->entry = (IndicatorObjectEntry *) data; if (piea->priv->entry->menu != NULL) { piea->priv->menu = g_object_ref(piea->priv->entry->menu); } if (GTK_IS_LABEL (piea->priv->entry->label)) { atk_object_set_role (accessible, ATK_ROLE_LABEL); atk_object_set_name (accessible, gtk_label_get_text (piea->priv->entry->label)); } if (GTK_IS_IMAGE (piea->priv->entry->image)) { atk_object_set_role (accessible, ATK_ROLE_IMAGE); if (piea->priv->entry->accessible_desc != NULL) atk_object_set_name (accessible, piea->priv->entry->accessible_desc); } if (piea->priv->entry->accessible_desc != NULL) atk_object_set_description (accessible, piea->priv->entry->accessible_desc); } static gint panel_indicator_entry_accessible_get_n_children (AtkObject *accessible) { PanelIndicatorEntryAccessible *piea; gint n_children = 0; g_return_val_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (accessible), 0); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible); if (piea->priv->entry != NULL && piea->priv->entry->parent_object && piea->priv->menu != NULL && GTK_IS_MENU (piea->priv->menu)) n_children = 1; return n_children; } static AtkObject * panel_indicator_entry_accessible_ref_child (AtkObject *accessible, gint i) { PanelIndicatorEntryAccessible *piea; AtkObject *child = NULL; g_return_val_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (accessible), NULL); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible); if (piea->priv->entry && piea->priv->entry->parent_object && GTK_IS_MENU (piea->priv->entry->menu)) { child = gtk_widget_get_accessible (GTK_WIDGET (piea->priv->entry->menu)); atk_object_set_parent (child, accessible); } if (child != NULL) return g_object_ref (child); else return NULL; } static AtkStateSet * panel_indicator_entry_accessible_ref_state_set (AtkObject *accessible) { AtkStateSet *state_set; PanelIndicatorEntryAccessible *piea; g_return_val_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (accessible), NULL); piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible); /* Retrieve state_set from parent_class */ state_set = ATK_OBJECT_CLASS (panel_indicator_entry_accessible_parent_class)->ref_state_set (accessible); atk_state_set_add_state (state_set, ATK_STATE_ENABLED); atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE); atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL); atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE); atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); if (piea->priv->active) { atk_state_set_add_state (state_set, ATK_STATE_ACTIVE); atk_state_set_add_state (state_set, ATK_STATE_FOCUSED); atk_state_set_add_state (state_set, ATK_STATE_SHOWING); } return state_set; } ./services/panel-indicator-accessible.c0000644000004100000410000002667613437202764020427 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #include #include "panel-indicator-accessible.h" #include "panel-indicator-entry-accessible.h" #include "panel-service.h" /* AtkObject methods */ static void pia_component_interface_init (AtkComponentIface *iface); static void panel_indicator_accessible_initialize (AtkObject *accessible, gpointer data); static gint panel_indicator_accessible_get_n_children (AtkObject *accessible); static AtkObject *panel_indicator_accessible_ref_child (AtkObject *accessible, gint i); static AtkStateSet *panel_indicator_accessible_ref_state_set (AtkObject *accessible); struct _PanelIndicatorAccessiblePrivate { IndicatorObject *indicator; PanelService *service; GSList *a11y_children; gint x; gint y; gint width; gint height; }; G_DEFINE_TYPE_WITH_CODE(PanelIndicatorAccessible, panel_indicator_accessible, ATK_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, pia_component_interface_init)) #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_INDICATOR_ACCESSIBLE, PanelIndicatorAccessiblePrivate)) /* Indicator callbacks */ static void on_indicator_entry_added (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data) { AtkObject *accessible; PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (user_data); accessible = panel_indicator_entry_accessible_new (entry); if (accessible != NULL) { atk_object_set_parent (accessible, ATK_OBJECT (pia)); pia->priv->a11y_children = g_slist_append (pia->priv->a11y_children, accessible); g_signal_emit_by_name (ATK_OBJECT (pia), "children-changed::add", g_slist_length (pia->priv->a11y_children) - 1, accessible); } } static void on_indicator_entry_removed (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data) { GSList *l; guint count = 0; PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (user_data); gboolean found = FALSE; AtkObject *accessible = NULL; for (l = pia->priv->a11y_children; l != NULL; l = g_slist_next (l)) { accessible = ATK_OBJECT (l->data); if (entry == panel_indicator_entry_accessible_get_entry (PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible))) { found = TRUE; break; } else count++; } if (found) { pia->priv->a11y_children = g_slist_remove (pia->priv->a11y_children, accessible); g_signal_emit_by_name (ATK_OBJECT (pia), "children-changed::remove", count, accessible); g_object_unref (accessible); } } static void on_accessible_desc_updated (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data) { GSList *l; PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (user_data); gboolean found = FALSE; AtkObject *entry_accessible = NULL; AtkObject *widget_accessible = NULL; for (l = pia->priv->a11y_children; l != NULL; l = l->next) { entry_accessible = ATK_OBJECT (l->data); if (entry == panel_indicator_entry_accessible_get_entry (PANEL_INDICATOR_ENTRY_ACCESSIBLE (entry_accessible))) { found = TRUE; break; } } if (!found) return; if (GTK_IS_LABEL (entry->label)) { widget_accessible = gtk_widget_get_accessible (GTK_WIDGET (entry->label)); } else if (GTK_IS_IMAGE (entry->image)) { widget_accessible = gtk_widget_get_accessible (GTK_WIDGET (entry->image)); } else { g_warning ("a11y: Current entry is not a label or a image."); } if (ATK_IS_OBJECT (widget_accessible)) { gchar *name = (gchar*) atk_object_get_name (widget_accessible); gchar *description = (gchar*) entry->accessible_desc; if (name == NULL) name = ""; if (description == NULL) description = ""; atk_object_set_name (entry_accessible, name); atk_object_set_description (entry_accessible, description); } } static void on_geometries_changed_cb (PanelService *service, IndicatorObject *object, IndicatorObjectEntry *entry, gint x, gint y, gint width, gint height, gpointer user_data) { PanelIndicatorAccessible *pia; AtkRectangle rect; GSList *l; gboolean minimum_set = FALSE; pia = PANEL_INDICATOR_ACCESSIBLE (user_data); g_return_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (pia)); if (object != pia->priv->indicator) return; /* Iterate over all children to get width and height */ pia->priv->width = pia->priv->height = 0; for (l = pia->priv->a11y_children; l != NULL; l = l->next) { gint e_x, e_y, e_width, e_height; AtkObject *accessible = ATK_OBJECT (l->data); atk_component_get_extents (ATK_COMPONENT (accessible), &e_x, &e_y, &e_width, &e_height, ATK_XY_SCREEN); if (minimum_set) { if (e_x < pia->priv->x) pia->priv->x = e_x; if (e_y < pia->priv->y) pia->priv->y = e_y; } else { pia->priv->x = e_x; pia->priv->y = e_y; minimum_set = TRUE; } pia->priv->width += e_width; if (e_height > pia->priv->height) pia->priv->height = e_height; } /* Notify ATK objects of change of coordinates */ rect.x = pia->priv->x; rect.y = pia->priv->y; rect.width = pia->priv->width; rect.height = pia->priv->height; g_signal_emit_by_name (ATK_COMPONENT (pia), "bounds-changed", &rect); } static void panel_indicator_accessible_finalize (GObject *object) { PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (object); if (pia->priv != NULL) { if (pia->priv->indicator != NULL) { g_signal_handlers_disconnect_by_func (pia->priv->indicator, on_indicator_entry_added, pia); g_signal_handlers_disconnect_by_func (pia->priv->indicator, on_indicator_entry_removed, pia); g_signal_handlers_disconnect_by_func (pia->priv->indicator, on_accessible_desc_updated, pia); g_object_unref (G_OBJECT (pia->priv->indicator)); } if (pia->priv->a11y_children != NULL) { g_slist_free_full(pia->priv->a11y_children, g_object_unref); pia->priv->a11y_children = NULL; } g_signal_handlers_disconnect_by_func (pia->priv->service, on_geometries_changed_cb, pia); } G_OBJECT_CLASS (panel_indicator_accessible_parent_class)->finalize (object); } static void panel_indicator_accessible_class_init (PanelIndicatorAccessibleClass *klass) { GObjectClass *object_class; AtkObjectClass *atk_class; /* GObject */ object_class = G_OBJECT_CLASS (klass); object_class->finalize = panel_indicator_accessible_finalize; /* AtkObject */ atk_class = ATK_OBJECT_CLASS (klass); atk_class->initialize = panel_indicator_accessible_initialize; atk_class->get_n_children = panel_indicator_accessible_get_n_children; atk_class->ref_child = panel_indicator_accessible_ref_child; atk_class->ref_state_set = panel_indicator_accessible_ref_state_set; g_type_class_add_private (object_class, sizeof (PanelIndicatorAccessiblePrivate)); } static void panel_indicator_accessible_init (PanelIndicatorAccessible *pia) { pia->priv = GET_PRIVATE (pia); pia->priv->a11y_children = NULL; pia->priv->x = pia->priv->y = pia->priv->width = pia->priv->height = 0; /* Set up signals for listening to service changes */ pia->priv->service = panel_service_get_default (); g_signal_connect (pia->priv->service, "geometries-changed", G_CALLBACK (on_geometries_changed_cb), pia); } AtkObject * panel_indicator_accessible_new (IndicatorObject *indicator) { PanelIndicatorAccessible *pia; pia = g_object_new (PANEL_TYPE_INDICATOR_ACCESSIBLE, NULL); atk_object_initialize (ATK_OBJECT (pia), indicator); return ATK_OBJECT (pia); } /* Implementation of AtkObject methods */ static void panel_indicator_accessible_get_extents (AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type) { PanelIndicatorAccessible *pia; g_return_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (component)); pia = PANEL_INDICATOR_ACCESSIBLE (component); /* We ignore AtkCoordType for now, as the panel is always at the top left corner and so relative and absolute coordinates are the same */ *x = pia->priv->x; *y = pia->priv->y; *width = pia->priv->width; *height = pia->priv->height; } static void pia_component_interface_init (AtkComponentIface *iface) { g_return_if_fail (iface != NULL); iface->get_extents = panel_indicator_accessible_get_extents; } static void panel_indicator_accessible_initialize (AtkObject *accessible, gpointer data) { PanelIndicatorAccessible *pia; GList *entries, *l; g_return_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (accessible)); ATK_OBJECT_CLASS (panel_indicator_accessible_parent_class)->initialize (accessible, data); pia = PANEL_INDICATOR_ACCESSIBLE (accessible); atk_object_set_role (accessible, ATK_ROLE_PANEL); /* Setup the indicator object */ pia->priv->indicator = g_object_ref (data); g_signal_connect (G_OBJECT (pia->priv->indicator), "entry-added", G_CALLBACK (on_indicator_entry_added), pia); g_signal_connect (G_OBJECT (pia->priv->indicator), "entry-removed", G_CALLBACK (on_indicator_entry_removed), pia); g_signal_connect (G_OBJECT (pia->priv->indicator), "accessible_desc_update", G_CALLBACK (on_accessible_desc_updated), pia); /* Retrieve all entries and create their accessible objects */ entries = indicator_object_get_entries (pia->priv->indicator); for (l = entries; l != NULL; l = l->next) { AtkObject *child; IndicatorObjectEntry *entry = (IndicatorObjectEntry *) l->data; child = panel_indicator_entry_accessible_new (entry); atk_object_set_parent (child, accessible); pia->priv->a11y_children = g_slist_append (pia->priv->a11y_children, child); } g_list_free (entries); } static gint panel_indicator_accessible_get_n_children (AtkObject *accessible) { PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (accessible); g_return_val_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (pia), 0); return g_slist_length (pia->priv->a11y_children); } static AtkObject * panel_indicator_accessible_ref_child (AtkObject *accessible, gint i) { PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (accessible); g_return_val_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (pia), NULL); return g_object_ref (g_slist_nth_data (pia->priv->a11y_children, i)); } static AtkStateSet * panel_indicator_accessible_ref_state_set (AtkObject *accessible) { AtkStateSet *state_set; g_return_val_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (accessible), NULL); /* Retrieve state_set from parent_class */ state_set = ATK_OBJECT_CLASS (panel_indicator_accessible_parent_class)->ref_state_set (accessible); atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); return state_set; } ./services/panel-util-accessible.c0000644000004100000410000000275713437202764017422 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #include "panel-root-accessible.h" #include "panel-util-accessible.h" G_DEFINE_TYPE(PanelUtilAccessible, panel_util_accessible, ATK_TYPE_UTIL) /* AtkUtil methods */ static AtkObject *panel_util_accessible_get_root (void); static AtkObject *root = NULL; /* GObject methods implementation */ static void panel_util_accessible_class_init (PanelUtilAccessibleClass *klass) { AtkUtilClass *atk_class; g_debug ("Initializing PanelUtilAccessible class"); /* AtkUtil */ atk_class = g_type_class_peek (ATK_TYPE_UTIL); atk_class->get_root = panel_util_accessible_get_root; } static void panel_util_accessible_init (PanelUtilAccessible *panel_util) { } /* AtkUtil methods implementation */ static AtkObject * panel_util_accessible_get_root (void) { if (!root) root = panel_root_accessible_new (); return root; } ./services/panel-indicator-accessible.h0000644000004100000410000000425313437202764020417 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #ifndef _PANEL_INDICATOR_ACCESSIBLE_H_ #define _PANEL_INDICATOR_ACCESSIBLE_H_ #include #include #include G_BEGIN_DECLS #define PANEL_TYPE_INDICATOR_ACCESSIBLE (panel_indicator_accessible_get_type ()) #define PANEL_INDICATOR_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PANEL_TYPE_INDICATOR_ACCESSIBLE, PanelIndicatorAccessible)) #define PANEL_INDICATOR_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANEL_TYPE_INDICATOR_ACCESSIBLE, PanelIndicatorAccessibleClass)) #define PANEL_IS_INDICATOR_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PANEL_TYPE_INDICATOR_ACCESSIBLE)) #define PANEL_IS_INDICATOR_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANEL_TYPE_INDICATOR_ACCESSIBLE)) #define PANEL_INDICATOR_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANEL_TYPE_INDICATOR_ACCESSIBLE, PanelIndicatorAccessibleClass)) typedef struct _PanelIndicatorAccessible PanelIndicatorAccessible; typedef struct _PanelIndicatorAccessibleClass PanelIndicatorAccessibleClass; typedef struct _PanelIndicatorAccessiblePrivate PanelIndicatorAccessiblePrivate; struct _PanelIndicatorAccessible { AtkObject parent; PanelIndicatorAccessiblePrivate *priv; }; struct _PanelIndicatorAccessibleClass { AtkObjectClass parent_class; }; GType panel_indicator_accessible_get_type (void); AtkObject *panel_indicator_accessible_new (IndicatorObject *indicator); G_END_DECLS #endif ./services/panel-service-private.h0000644000004100000410000000304513437202764017456 0ustar www-datawww-data/* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan */ #ifndef _PANEL_SERVICE_PRIVATE_H_ #define _PANEL_SERVICE_PRIVATE_H_ #include #include #include G_BEGIN_DECLS #ifdef __cplusplus extern "C" { #endif #define UPS_NAME_DESKTOP "com.canonical.Unity.Panel.Service.Desktop" #define UPS_NAME_LOCKSCREEN "com.canonical.Unity.Panel.Service.LockScreen" #define UPS_PATH "/com/canonical/Unity/Panel/Service" #define UPS_IFACE "com.canonical.Unity.Panel.Service" #define APPMENU_INDICATOR_NAME "libappmenu.so" #define ENTRY_SIGNATURE "(sssusbbusbbi)" #define ENTRY_ARRAY_SIGNATURE "a" ENTRY_SIGNATURE "" #define AltMask Mod1Mask #define SuperMask Mod4Mask typedef struct _KeyBinding { KeySym key; KeySym fallback; guint32 modifiers; } KeyBinding; void parse_string_keybinding (const char *, KeyBinding *); #ifdef __cplusplus } #endif G_END_DECLS #endif /* _PANEL_SERVICE_PRIVATE_H_ */ ./services/panel-root-accessible.h0000644000004100000410000000364513437202764017432 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #ifndef _PANEL_ROOT_ACCESSIBLE_H_ #define _PANEL_ROOT_ACCESSIBLE_H_ #include G_BEGIN_DECLS #define PANEL_TYPE_ROOT_ACCESSIBLE (panel_root_accessible_get_type ()) #define PANEL_ROOT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PANEL_TYPE_ROOT_ACCESSIBLE, PanelRootAccessible)) #define PANEL_ROOT_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANEL_TYPE_ROOT_ACCESSIBLE, PanelRootAccessibleClass)) #define PANEL_IS_ROOT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PANEL_TYPE_ROOT_ACCESSIBLE)) #define PANEL_IS_ROOT_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANEL_TYPE_ROOT_ACCESSIBLE)) #define PANEL_ROOT_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANEL_TYPE_ROOT_ACCESSIBLE, PanelRootAccessibleClass)) typedef struct _PanelRootAccessible PanelRootAccessible; typedef struct _PanelRootAccessibleClass PanelRootAccessibleClass; typedef struct _PanelRootAccessiblePrivate PanelRootAccessiblePrivate; struct _PanelRootAccessible { AtkObject parent; PanelRootAccessiblePrivate *priv; }; struct _PanelRootAccessibleClass { AtkObjectClass parent_class; }; GType panel_root_accessible_get_type (void); AtkObject *panel_root_accessible_new (void); #endif ./services/panel-root-accessible.c0000644000004100000410000001274013437202764017421 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #include #include "panel-indicator-accessible.h" #include "panel-root-accessible.h" #include "panel-service.h" G_DEFINE_TYPE(PanelRootAccessible, panel_root_accessible, ATK_TYPE_OBJECT) #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_ROOT_ACCESSIBLE, PanelRootAccessiblePrivate)) /* AtkObject methods */ static void panel_root_accessible_initialize (AtkObject *accessible, gpointer data); static gint panel_root_accessible_get_n_children (AtkObject *accessible); static AtkObject *panel_root_accessible_ref_child (AtkObject *accessible, gint i); static AtkObject *panel_root_accessible_get_parent (AtkObject *accessible); struct _PanelRootAccessiblePrivate { PanelService *service; GSList *a11y_children; }; static void panel_root_accessible_finalize (GObject *object) { PanelRootAccessible *root = PANEL_ROOT_ACCESSIBLE (object); if ((root->priv != NULL) && (root->priv->a11y_children != NULL)) { g_slist_free_full(root->priv->a11y_children, g_object_unref); root->priv->a11y_children = NULL; } } static void panel_root_accessible_class_init (PanelRootAccessibleClass *klass) { GObjectClass *object_class; AtkObjectClass *atk_class; /* GObject */ object_class = G_OBJECT_CLASS (klass); object_class->finalize = panel_root_accessible_finalize; /* AtkObject */ atk_class = ATK_OBJECT_CLASS (klass); atk_class->initialize = panel_root_accessible_initialize; atk_class->get_n_children = panel_root_accessible_get_n_children; atk_class->ref_child = panel_root_accessible_ref_child; atk_class->get_parent = panel_root_accessible_get_parent; g_type_class_add_private (object_class, sizeof (PanelRootAccessiblePrivate)); } static void panel_root_accessible_init (PanelRootAccessible *root) { root->priv = GET_PRIVATE (root); root->priv->a11y_children = NULL; root->priv->service = panel_service_get_default (); } AtkObject * panel_root_accessible_new (void) { AtkObject *accessible; accessible = ATK_OBJECT (g_object_new (PANEL_TYPE_ROOT_ACCESSIBLE, NULL)); atk_object_initialize (accessible, NULL); return accessible; } static void on_indicator_removed (gpointer *data, GObject *removed_indicator) { PanelRootAccessible *root = data[0]; AtkObject *child = data[1]; g_return_if_fail (PANEL_IS_ROOT_ACCESSIBLE (root)); g_return_if_fail (ATK_IS_OBJECT (child)); gint index = g_slist_index (root->priv->a11y_children, child); if (index >= 0) { root->priv->a11y_children = g_slist_remove (root->priv->a11y_children, child); g_signal_emit_by_name (root, "children-changed::remove", index, child); g_object_unref (child); } g_free (data); } /* Implementation of AtkObject methods */ static void panel_root_accessible_initialize (AtkObject *accessible, gpointer data) { gint n_children, i; PanelRootAccessible *root = PANEL_ROOT_ACCESSIBLE (accessible); g_return_if_fail (PANEL_IS_ROOT_ACCESSIBLE (accessible)); ATK_OBJECT_CLASS (panel_root_accessible_parent_class)->initialize (accessible, data); accessible->role = ATK_ROLE_APPLICATION; atk_object_set_name (accessible, g_get_prgname ()); atk_object_set_parent (accessible, NULL); /* Retrieve all indicators and create their accessible objects */ n_children = panel_service_get_n_indicators (root->priv->service); for (i = 0; i < n_children; i++) { IndicatorObject *indicator; indicator = panel_service_get_indicator_nth (root->priv->service, i); if (INDICATOR_IS_OBJECT (indicator)) { AtkObject *child; gpointer *weak_data; child = panel_indicator_accessible_new (indicator); /* FIXME: use proper signals once we support dynamic adding/removing * of indicators */ weak_data = g_new0 (gpointer, 2); weak_data[0] = root; weak_data[1] = child; g_object_weak_ref (G_OBJECT (indicator), (GWeakNotify) on_indicator_removed, weak_data); atk_object_set_parent (child, accessible); root->priv->a11y_children = g_slist_append (root->priv->a11y_children, child); } } } static gint panel_root_accessible_get_n_children (AtkObject *accessible) { PanelRootAccessible *root = PANEL_ROOT_ACCESSIBLE (accessible); g_return_val_if_fail (PANEL_IS_ROOT_ACCESSIBLE (accessible), 0); return g_slist_length (root->priv->a11y_children); } static AtkObject * panel_root_accessible_ref_child (AtkObject *accessible, gint i) { PanelRootAccessible *root = PANEL_ROOT_ACCESSIBLE (accessible); g_return_val_if_fail (PANEL_IS_ROOT_ACCESSIBLE (accessible), NULL); return g_object_ref (g_slist_nth_data (root->priv->a11y_children, i)); } static AtkObject * panel_root_accessible_get_parent (AtkObject *accessible) { g_return_val_if_fail (PANEL_IS_ROOT_ACCESSIBLE (accessible), NULL); return NULL; } ./services/panel-util-accessible.h0000644000004100000410000000337213437202764017421 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #ifndef _PANEL_UTIL_ACCESSIBLE_H_ #define _PANEL_UTIL_ACCESSIBLE_H_ #include G_BEGIN_DECLS #define PANEL_TYPE_UTIL_ACCESSIBLE (panel_util_accessible_get_type ()) #define PANEL_UTIL_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PANEL_TYPE_UTIL_ACCESSIBLE, PanelUtilAccessible)) #define PANEL_UTIL_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANEL_TYPE_UTIL_ACCESSIBLE, PanelUtilAccessibleClass)) #define PANEL_IS_UTIL_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PANEL_TYPE_UTIL_ACCESSIBLE)) #define PANEL_IS_UTIL_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANEL_TYPE_UTIL_ACCESSIBLE)) #define PANEL_UTIL_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANEL_TYPE_UTIL_ACCESSIBLE, PanelUtilAccessibleClass)) typedef struct _PanelUtilAccessible PanelUtilAccessible; typedef struct _PanelUtilAccessibleClass PanelUtilAccessibleClass; struct _PanelUtilAccessible { AtkUtil parent; }; struct _PanelUtilAccessibleClass { AtkUtilClass parent_class; }; GType panel_util_accessible_get_type (void); G_END_DECLS #endif ./services/unity-panel-service.conf.in0000644000004100000410000000104013437202764020250 0ustar www-datawww-datadescription "Backing Service for the Unity Panel" author "Ted Gould " # These should be used when we have Unity full under upstart, but while # we have the pre-start work around for gnome-session starting it we're # going to comment them out. # # start on started unity7 # stop on stopped unity7 start on desktop-start DESKTOP_SESSION=ubuntu stop on desktop-end pre-start exec @UNITY_LIBDIR@/upstart-prestart-check emits indicator-services-start emits indicator-services-end respawn exec @UNITY_LIBDIR@/unity-panel-service ./services/panel-main.c0000644000004100000410000004051313437202764015266 0ustar www-datawww-data// -*- Mode: C; tab-width:2; indent-tabs-mode: t; c-basic-offset: 2 -*- /* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Neil Jagdish Patel * Rodrigo Moya */ #include #include #include #include #include #include "config.h" #include "panel-a11y.h" #include "panel-service.h" #include "panel-service-private.h" static GDBusNodeInfo *introspection_data = NULL; static const gchar introspection_xml[] = "" " " "" " " " " " " "" " " " " " " " " "" " " " " " " " " "" " " " " " " "" " " " " " " " " " " " " " " "" " " " " " " " " " " " " " " "" " " " " " " " " " " "" " " " " " " "" " " " " " " " " "" "" " " "" " " " " " " " " " " "" " " " " " " "" " " " " " " "" " " "" " " ""; /* Forwards */ static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data); static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, NULL }; /* Methods */ static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { PanelService *service = PANEL_SERVICE (user_data); if (g_strcmp0 (method_name, "Sync") == 0) { g_dbus_method_invocation_return_value (invocation, panel_service_sync (service)); } else if (g_strcmp0 (method_name, "SyncOne") == 0) { gchar *id; g_variant_get (parameters, "(s)", &id, NULL); g_dbus_method_invocation_return_value (invocation, panel_service_sync_one (service, id)); g_free (id); } else if (g_strcmp0 (method_name, "SyncGeometries") == 0) { GVariantIter *iter; gchar *panel_id, *entry_id; gint x, y, width, height; g_variant_get (parameters, "(sa(siiii))", &panel_id, &iter); if (panel_id) { if (iter) { while (g_variant_iter_loop (iter, "(siiii)", &entry_id, &x, &y, &width, &height)) { panel_service_sync_geometry (service, panel_id, entry_id, x, y, width, height); } g_variant_iter_free (iter); } g_free (panel_id); } g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "GetIconPaths") == 0) { gchar **paths; gint len; GVariant *ret_value; gtk_icon_theme_get_search_path (gtk_icon_theme_get_default (), &paths, &len); ret_value = g_variant_new("(@as)", g_variant_new_strv ((const gchar **) paths, len)); g_dbus_method_invocation_return_value (invocation, ret_value); g_strfreev (paths); } else if (g_strcmp0 (method_name, "ShowEntry") == 0) { guint32 xid; gchar *entry_id; gint32 x; gint32 y; guint32 button; g_variant_get (parameters, "(suiiu)", &entry_id, &xid, &x, &y, &button); panel_service_show_entry (service, entry_id, xid, x, y, button); g_dbus_method_invocation_return_value (invocation, NULL); g_free (entry_id); } else if (g_strcmp0 (method_name, "ShowEntriesDropdown") == 0) { guint32 xid; gchar **entries; gchar *selected; gint32 x; gint32 y; g_variant_get (parameters, "(^assuii)", &entries, &selected, &xid, &x, &y); panel_service_show_entries (service, entries, selected, xid, x, y); g_dbus_method_invocation_return_value (invocation, NULL); g_strfreev (entries); g_free (selected); } else if (g_strcmp0 (method_name, "ShowAppMenu") == 0) { guint32 xid; gint32 x; gint32 y; g_variant_get (parameters, "(uii)", &xid, &x, &y); panel_service_show_app_menu (service, xid, x, y); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "SecondaryActivateEntry") == 0) { gchar *entry_id; g_variant_get (parameters, "(s)", &entry_id); panel_service_secondary_activate_entry (service, entry_id); g_dbus_method_invocation_return_value (invocation, NULL); g_free (entry_id); } else if (g_strcmp0 (method_name, "ScrollEntry") == 0) { gchar *entry_id; gint32 delta; g_variant_get (parameters, "(si)", &entry_id, &delta, NULL); panel_service_scroll_entry (service, entry_id, delta); g_dbus_method_invocation_return_value (invocation, NULL); g_free(entry_id); } else if (g_strcmp0 (method_name, "CloseActiveEntry") == 0) { panel_service_close_active_entry (service); g_dbus_method_invocation_return_value (invocation, NULL); } } static void on_service_resync (PanelService *service, const gchar *indicator_id, GDBusConnection *connection) { GError *error = NULL; g_dbus_connection_emit_signal (connection, NULL, UPS_PATH, UPS_IFACE, "ReSync", g_variant_new ("(s)", indicator_id), &error); if (error) { g_warning ("Unable to emit ReSync signal: %s", error->message); g_error_free (error); } } static void on_service_entry_activated (PanelService *service, const gchar *panel_id, const gchar *entry_id, gint x, gint y, guint w, guint h, GDBusConnection *connection) { GError *error = NULL; g_dbus_connection_emit_signal (connection, NULL, UPS_PATH, UPS_IFACE, "EntryActivated", g_variant_new ("(ss(iiuu))", panel_id ? panel_id : "", entry_id ? entry_id : "", x, y, w, h), &error); if (error) { g_warning ("Unable to emit EntryActivated signal: %s", error->message); g_error_free (error); } } static void on_service_entry_activate_request (PanelService *service, const gchar *entry_id, GDBusConnection *connection) { GError *error = NULL; g_debug ("%s, entry_id: %s", G_STRFUNC, entry_id); g_dbus_connection_emit_signal (connection, NULL, UPS_PATH, UPS_IFACE, "EntryActivateRequest", g_variant_new ("(s)", entry_id), &error); if (error) { g_warning ("Unable to emit EntryActivateRequest signal: %s", error->message); g_error_free (error); } } static void on_icon_theme_changed (GtkIconTheme* theme, GDBusConnection *connection) { GError *error = NULL; g_dbus_connection_emit_signal (connection, NULL, UPS_PATH, UPS_IFACE, "IconPathsChanged", NULL, &error); if (error) { g_warning ("Unable to emit IconPathsChanged signal: %s", error->message); g_error_free (error); } } void on_unity_dbus_name_owner_changed_cb (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { PanelService *service; const gchar *owner_name; const gchar *old_address; const gchar *new_address; g_variant_get (parameters, "(&s&s&s)", &owner_name, &old_address, &new_address); g_debug ("%s: %s, %s -> %s", G_STRFUNC, owner_name, old_address, new_address); service = user_data; if (!new_address || *new_address == '\0') { panel_service_clear_remote_data (service); } } static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { PanelService *service = PANEL_SERVICE (user_data); guint reg_id; reg_id = g_dbus_connection_register_object (connection, UPS_PATH, introspection_data->interfaces[0], &interface_vtable, service, NULL, NULL); g_signal_connect (service, "re-sync", G_CALLBACK (on_service_resync), connection); g_signal_connect (service, "entry-activated", G_CALLBACK (on_service_entry_activated), connection); g_signal_connect (service, "entry-activate-request", G_CALLBACK (on_service_entry_activate_request), connection); g_signal_connect (gtk_icon_theme_get_default(), "changed", G_CALLBACK (on_icon_theme_changed), connection); g_dbus_connection_signal_subscribe (connection, /*sender*/ NULL, /*interface_name*/ "org.freedesktop.DBus", /*member*/ "NameOwnerChanged", /*object_path*/ "/org/freedesktop/DBus", /*arg0*/ "com.canonical.Unity", G_DBUS_SIGNAL_FLAGS_NONE, on_unity_dbus_name_owner_changed_cb, service, NULL); g_debug ("%s", G_STRFUNC); g_assert (reg_id > 0); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { g_debug ("Name Acquired"); } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { PanelService *service = PANEL_SERVICE (user_data); g_debug ("%s", G_STRFUNC); if (service != NULL) g_signal_handlers_disconnect_by_data (service, connection); gtk_main_quit (); } static gboolean on_unix_signal (gpointer data) { PanelService *service = PANEL_SERVICE (data); g_signal_connect (service, "indicators-cleared", G_CALLBACK (gtk_main_quit), NULL); panel_service_clear_indicators (service); return FALSE; } static void discard_log_message (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { return; } gint main (gint argc, gchar **argv) { PanelService *service; guint owner_id; gboolean lockscreen_mode = FALSE; GError *error = NULL; GOptionContext *context; GOptionEntry entries[] = { { "lockscreen-mode", 0, 0, G_OPTION_ARG_NONE, &lockscreen_mode, "Load indicators for the lockscreen", NULL }, { NULL }}; g_unsetenv ("UBUNTU_MENUPROXY"); g_setenv ("NO_AT_BRIDGE", "1", TRUE); g_unsetenv ("NO_GAIL"); gtk_init (&argc, &argv); gtk_icon_theme_append_search_path (gtk_icon_theme_get_default(), INDICATORICONDIR); ido_init (); context = g_option_context_new ("- Unity Panel Service"); g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print ("unity-panel-service: %s\n", error->message); g_print ("Try --help for more information.\n"); return 1; } panel_service_set_lockscreen_mode (lockscreen_mode); if (g_getenv ("SILENT_PANEL_SERVICE") != NULL) { g_log_set_default_handler (discard_log_message, NULL); } introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (introspection_data != NULL); panel_a11y_init (); service = panel_service_get_default (); owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, !lockscreen_mode ? UPS_NAME_DESKTOP : UPS_NAME_LOCKSCREEN, G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, service, NULL); g_unix_signal_add (SIGINT, on_unix_signal, service); g_unix_signal_add (SIGTERM, on_unix_signal, service); gtk_main (); g_bus_unown_name (owner_id); g_dbus_node_info_unref (introspection_data); g_object_unref (service); return 0; } ./services/panel-indicator-entry-accessible.h0000644000004100000410000000470713437202764021562 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #ifndef _PANEL_INDICATOR_ENTRY_ACCESSIBLE_H_ #define _PANEL_INDICATOR_ENTRY_ACCESSIBLE_H_ #include #include #include G_BEGIN_DECLS #define PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE (panel_indicator_entry_accessible_get_type ()) #define PANEL_INDICATOR_ENTRY_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE, PanelIndicatorEntryAccessible)) #define PANEL_INDICATOR_ENTRY_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE, PanelIndicatorEntryAccessibleClass)) #define PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE)) #define PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE)) #define PANEL_INDICATOR_ENTRY_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE, PanelIndicatorEntryAccessibleClass)) typedef struct _PanelIndicatorEntryAccessible PanelIndicatorEntryAccessible; typedef struct _PanelIndicatorEntryAccessibleClass PanelIndicatorEntryAccessibleClass; typedef struct _PanelIndicatorEntryAccessiblePrivate PanelIndicatorEntryAccessiblePrivate; struct _PanelIndicatorEntryAccessible { AtkObject parent; PanelIndicatorEntryAccessiblePrivate *priv; }; struct _PanelIndicatorEntryAccessibleClass { AtkObjectClass parent_class; }; GType panel_indicator_entry_accessible_get_type (void); AtkObject *panel_indicator_entry_accessible_new (IndicatorObjectEntry *entry); IndicatorObjectEntry *panel_indicator_entry_accessible_get_entry (PanelIndicatorEntryAccessible *piea); G_END_DECLS #endif ./services/unity-panel-service.override0000644000004100000410000000000713437202764020537 0ustar www-datawww-datamanual ./services/panel-a11y.c0000644000004100000410000000224313437202764015113 0ustar www-datawww-data/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Rodrigo Moya */ #include #include #include "panel-a11y.h" #include "panel-util-accessible.h" static gboolean a11y_initialized = FALSE; void panel_a11y_init (void) { if (a11y_initialized) return; /* Restore environment to load AT bridge */ g_unsetenv ("NO_AT_BRIDGE"); g_unsetenv ("NO_GAIL"); /* Load PanelUtilAccessible class */ g_type_class_unref (g_type_class_ref (PANEL_TYPE_UTIL_ACCESSIBLE)); atk_bridge_adaptor_init(NULL, NULL); a11y_initialized = TRUE; } ./services/unity-panel-service-lockscreen.conf.in0000644000004100000410000000042213437202764022401 0ustar www-datawww-datadescription "Backing Service for the Unity Panel" author "Andrea Azzarone " start on desktop-lock stop on desktop-unlock pre-start exec @UNITY_LIBDIR@/upstart-prestart-check respawn exec @UNITY_LIBDIR@/unity-panel-service --lockscreen-mode ./COPYING0000644000004100000410000010437413437202764012317 0ustar www-datawww-data GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ./decorations/0000755000004100000410000000000013437202764013565 5ustar www-datawww-data./decorations/DecorationsEdge.cpp0000644000004100000410000000666013437202764017340 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan */ #include #include "DecorationsEdge.h" #include "DecorationsDataPool.h" namespace unity { namespace decoration { namespace { unsigned TypeToDirection(Edge::Type type) { switch (type) { case Edge::Type::TOP: return WmMoveResizeSizeTop; case Edge::Type::TOP_LEFT: return WmMoveResizeSizeTopLeft; case Edge::Type::TOP_RIGHT: return WmMoveResizeSizeTopRight; case Edge::Type::LEFT: return WmMoveResizeSizeLeft; case Edge::Type::RIGHT: return WmMoveResizeSizeRight; case Edge::Type::BOTTOM: return WmMoveResizeSizeBottom; case Edge::Type::BOTTOM_LEFT: return WmMoveResizeSizeBottomLeft; case Edge::Type::BOTTOM_RIGHT: return WmMoveResizeSizeBottomRight; case Edge::Type::GRAB: return WmMoveResizeMove; default: return WmMoveResizeCancel; } } } Edge::Edge(CompWindow* win, Type t) : win_(win) , type_(t) { unsigned mask = (t == Type::GRAB) ? CompWindowActionMoveMask : CompWindowActionResizeMask; sensitive = (win_->actions() & mask); mouse_owner.changed.connect([this] (bool over) { if (over) XDefineCursor(screen->dpy(), win_->frame(), DataPool::Get()->EdgeCursor(type_)); else XUndefineCursor(screen->dpy(), win_->frame()); }); } Edge::Type Edge::GetType() const { return type_; } CompWindow* Edge::Window() const { return win_; } void Edge::ButtonDownEvent(CompPoint const& p, unsigned button, Time timestamp) { XEvent ev; auto* dpy = screen->dpy(); ev.xclient.type = ClientMessage; ev.xclient.display = screen->dpy(); ev.xclient.serial = 0; ev.xclient.send_event = True; ev.xclient.window = win_->id(); ev.xclient.message_type = Atoms::wmMoveResize; ev.xclient.format = 32; ev.xclient.data.l[0] = p.x(); ev.xclient.data.l[1] = p.y(); ev.xclient.data.l[2] = TypeToDirection(type_); ev.xclient.data.l[3] = button; ev.xclient.data.l[4] = 1; XUngrabPointer(dpy, timestamp); XUngrabKeyboard(dpy, timestamp); auto mask = SubstructureRedirectMask | SubstructureNotifyMask; XSendEvent(dpy, screen->root(), False, mask, &ev); XSync(dpy, False); } std::string Edge::GetName() const { switch (type_) { case Type::TOP: return "TopEdge"; case Type::TOP_LEFT: return "TopLeftEdge"; case Type::TOP_RIGHT: return "TopRightEdge"; case Type::LEFT: return "LeftEdge"; case Type::RIGHT: return "RightEdge"; case Type::BOTTOM: return "BottomEdge"; case Type::BOTTOM_LEFT: return "BottomLeftEdge"; case Type::BOTTOM_RIGHT: return "BottomRightEdge"; case Type::GRAB: return "GrabEdge"; default: return "Edge"; } } } // decoration namespace } // unity namespace ./decorations/CMakeLists.txt0000644000004100000410000000261213437202764016326 0ustar www-datawww-datafind_package (PkgConfig) pkg_check_modules (COMPIZ_COMPOSITE REQUIRED "compiz-composite") set (CFLAGS ${COMPIZ_COMPOSITE_CFLAGS} ${CACHED_UNITY_DEPS_CFLAGS} ${CACHED_UNITY_DEPS_CFLAGS_OTHER} ${PIC_FLAGS} ) string (REPLACE ";" " " CFLAGS "${CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}") include_directories (.. ../UnityCore ../unity-shared ${CMAKE_BINARY_DIR}) # This makes linker to include library dir in RUNPATH find_library (COMPIZ_COMPOSITE_LIB composite ${COMPIZ_COMPOSITE_LIBDIR}) set (LIBS ${CACHED_UNITY_DEPS_LDFLAGS} ${COMPIZ_COMPOSITE_LIB} ${COMPIZ_COMPOSITE_LDFLAGS} unity-shared-compiz) # # Headers & Sources # set (DECORATION_SOURCES DecorationsManager.cpp DecoratedWindow.cpp DecorationsWidgets.cpp DecorationsWindowButton.cpp DecorationsEdge.cpp DecorationsGrabEdge.cpp DecorationsEdgeBorders.cpp DecorationsTitle.cpp DecorationsInputMixer.cpp DecorationsSlidingLayout.cpp DecorationsMenuLayout.cpp DecorationsMenuEntry.cpp DecorationsMenuDropdown.cpp DecorationsForceQuitDialog.cpp DecorationsDataPool.cpp DecorationsShape.cpp ) add_library (decorations-lib STATIC ${DECORATION_SOURCES}) target_link_libraries (decorations-lib ${LIBS}) add_dependencies (decorations-lib unity-core-${UNITY_API_VERSION} unity-shared-compiz) add_pch(pch/decorations_pch.hh decorations-lib) ./decorations/DecorationsSlidingLayout.h0000644000004100000410000000320413437202764020717 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2014 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan */ #ifndef UNITY_DECORATIONS_SLIDING_LAYOUT #define UNITY_DECORATIONS_SLIDING_LAYOUT #include #include "DecorationsWidgets.h" namespace unity { namespace decoration { class SlidingLayout : public BasicContainer { public: typedef std::shared_ptr Ptr; SlidingLayout(); nux::Property fadein; nux::Property fadeout; nux::Property override_main_item; void SetMainItem(Item::Ptr const& main); void SetInputItem(Item::Ptr const& input); protected: void Draw(GLWindow*, GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask) override; std::string GetName() const override { return "SlidingLayout"; } private: void DoRelayout() override; void StartAnimation(); nux::animation::AnimateValue fade_animator_; }; } // decoration namespace } // unity namespace #endif // UNITY_DECORATIONS_SLIDING_LAYOUT ./decorations/DecorationsInputMixer.h0000644000004100000410000000361113437202764020236 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan */ #ifndef UNITY_DECORATION_INPUT_MIXER #define UNITY_DECORATION_INPUT_MIXER #include "DecorationsWidgets.h" namespace unity { namespace decoration { class InputMixer { public: typedef std::shared_ptr Ptr; InputMixer(); void PushToFront(Item::Ptr const&); void PushToBack(Item::Ptr const&); void Remove(Item::Ptr const&); Item::List const& Items() const; Item::Ptr const& GetMouseOwner() const; void EnterEvent(CompPoint const&); void MotionEvent(CompPoint const&, Time); void LeaveEvent(CompPoint const&); void ButtonDownEvent(CompPoint const&, unsigned button, Time); void ButtonUpEvent(CompPoint const&, unsigned button, Time); void UngrabPointer(); void ForceMouseOwnerCheck(); private: InputMixer(InputMixer const&) = delete; InputMixer& operator=(InputMixer const&) = delete; void UpdateMouseOwner(CompPoint const&); void UnsetMouseOwner(); Item::Ptr GetMatchingItem(CompPoint const&); Item::Ptr GetMatchingItemRecursive(Item::List const&, CompPoint const&); Item::List items_; Item::Ptr last_mouse_owner_; bool mouse_down_; bool recheck_owner_; }; } // decoration namespace } // unity namespace #endif ./decorations/DecorationsMenuLayout.h0000644000004100000410000000354113437202764020236 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2014 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan */ #ifndef UNITY_DECORATIONS_MENU_LAYOUT #define UNITY_DECORATIONS_MENU_LAYOUT #include #include "DecorationsWidgets.h" #include "MenuManager.h" namespace unity { namespace decoration { class MenuDropdown; class MenuLayout : public Layout { public: typedef std::shared_ptr Ptr; MenuLayout(menu::Manager::Ptr const&, CompWindow*); nux::Property active; nux::Property show_now; void Setup(); bool ActivateMenu(std::string const& entry_id); bool ActivateMenu(CompPoint const&); void ChildrenGeometries(indicator::EntryLocationMap&) const; std::string const& MenubarId() const; protected: void DoRelayout() override; std::string GetName() const override { return "MenuLayout"; } private: void OnEntryMouseOwnershipChanged(bool); void OnEntryActiveChanged(bool); void OnEntryShowNowChanged(bool); menu::Manager::Ptr menu_manager_; CompWindow* win_; glib::Source::UniquePtr show_now_timeout_; std::shared_ptr dropdown_; std::string menubar_id_; }; } // decoration namespace } // unity namespace #endif // UNITY_DECORATIONS_MENU_LAYOUT ./decorations/glow_texture.h0000644000004100000410000003231713437202764016474 0ustar www-datawww-data/** * * * Copyright : (C) 2006-2010 by Patrick Niklaus, Roi Cohen, * Danny Baumann, Sam Spilsbury * Authors: Patrick Niklaus * Roi Cohen * Danny Baumann * Sam Spilsbury * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * **/ #ifndef UNITY_TEXTURE_GLOW_H #define UNITY_TEXTURE_GLOW_H namespace unity { namespace texture { /* * glowTex */ const unsigned GLOW_SIZE = 32; const int GLOW_OFFSET = 21; const char GLOW[4097] = { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377" "\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377" "\377\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\6\377\377" "\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377" "\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6" "\377\377\377\14\377\377\377\14\377\377\377\14\377\377\377\14\377\377\377" "\14\377\377\377\14\377\377\377\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\14" "\377\377\377\14\377\377\377\22\377\377\377\22\377\377\377\22\377\377\377" "\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377" "\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377" "\35\377\377\377\35\377\377\377\35\377\377\377\35\377\377\377\35\377\377\377" "\35\377\377\377\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377" "\377\6\377\377\377\6\377\377\377\14\377\377\377\22\377\377\377\27\377\377" "\377\27\377\377\377\35\377\377\377#\377\377\377'\377\377\377'\377\377\377" "+\377\377\377+\377\377\377+\377\377\377+\377\377\377+\377\377\377+\377\377" "\377+\377\377\377+\377\377\377+\377\377\3771\377\377\3771\377\377\3771\377" "\377\3771\377\377\3771\377\377\3771\377\377\3771\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\22\377\377\377" "\27\377\377\377\35\377\377\377#\377\377\377+\377\377\3771\377\377\3776\377" "\377\377<\377\377\377>\377\377\377C\377\377\377I\377\377\377I\377\377\377" "I\377\377\377I\377\377\377I\377\377\377I\377\377\377I\377\377\377I\377\377" "\377L\377\377\377L\377\377\377L\377\377\377L\377\377\377L\377\377\377L\377" "\377\377L\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377" "\377\377\22\377\377\377\27\377\377\377#\377\377\377+\377\377\3776\377\377" "\377C\377\377\377L\377\377\377U\377\377\377]\377\377\377`\377\377\377d\377" "\377\377h\377\377\377k\377\377\377k\377\377\377k\377\377\377k\377\377\377" "k\377\377\377k\377\377\377k\377\377\377p\377\377\377p\377\377\377p\377\377" "\377p\377\377\377p\377\377\377p\377\377\377p\0\0\0\0\0\0\0\0\0\0\0\0\377" "\377\377\6\377\377\377\14\377\377\377\22\314\314\314\35\377\377\377'\377" "\377\3771\377\377\377>\357\357\357P\377\377\377]\363\363\363k\365\365\365" "v\365\365\365|\377\377\377\202\367\367\367\210\367\367\367\214\367\367\367" "\216\367\367\367\221\367\367\367\221\367\367\367\221\367\367\367\221\367" "\367\367\221\367\367\367\221\367\367\367\224\367\367\367\224\367\367\367" "\224\367\367\367\224\367\367\367\224\367\367\367\224\367\367\367\224\0\0" "\0\0\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\22\377\377\377\27\377" "\377\377'\377\377\3776\377\377\377I\377\377\377Y\377\377\377k\376\376\376" "y\377\377\377\210\377\377\377\224\377\377\377\235\377\377\377\245\377\377" "\377\253\377\377\377\255\377\377\377\262\377\377\377\262\377\377\377\263" "\377\377\377\263\377\377\377\263\377\377\377\263\377\377\377\263\377\377" "\377\266\377\377\377\266\377\377\377\266\377\377\377\266\377\377\377\266" "\377\377\377\266\377\377\377\266\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377" "\14\377\377\377\27\377\377\377#\377\377\3771\377\377\377I\377\377\377]\377" "\377\377r\377\377\377\205\377\377\377\231\377\377\377\247\377\377\377\263" "\377\377\377\275\377\377\377\304\377\377\377\310\377\377\377\313\377\377" "\377\316\377\377\377\320\377\377\377\320\377\377\377\320\377\377\377\320" "\377\377\377\320\377\377\377\320\377\377\377\322\377\377\377\322\377\377" "\377\322\377\377\377\322\377\377\377\322\377\377\377\322\377\377\377\322" "\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\22\377\377\377\35\377\377" "\377+\377\377\377>\377\377\377Y\377\377\377r\377\377\377\210\376\376\376" "\237\377\377\377\262\377\377\377\302\377\377\377\313\377\377\377\324\377" "\377\377\332\376\376\376\336\377\377\377\341\377\377\377\342\377\377\377" "\344\377\377\377\344\377\377\377\344\377\377\377\344\377\377\377\344\377" "\377\377\344\377\377\377\345\377\377\377\345\377\377\377\345\377\377\377" "\345\377\377\377\345\377\377\377\345\377\377\377\345\0\0\0\0\377\377\377" "\6\377\377\377\14\377\377\377\27\377\377\377#\377\377\3776\377\377\377P\377" "\377\377k\377\377\377\205\376\376\376\237\372\372\372\266\377\377\377\307" "\373\373\373\325\373\373\373\337\374\374\374\345\374\374\374\352\374\374" "\374\355\374\374\374\357\374\374\374\360\374\374\374\361\374\374\374\361" "\374\374\374\362\374\374\374\362\374\374\374\362\374\374\374\362\374\374" "\374\362\374\374\374\362\374\374\374\362\374\374\374\362\374\374\374\362" "\374\374\374\362\374\374\374\362\0\0\0\0\377\377\377\6\377\377\377\14\377" "\377\377\35\377\377\377+\377\377\377C\377\377\377]\377\377\377|\377\377\377" "\231\377\377\377\263\377\377\377\307\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377" "\377\377\6\377\377\377\22\324\324\324#\377\377\3771\377\377\377L\363\363" "\363k\377\377\377\210\377\377\377\247\377\377\377\302\377\377\377\325\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\22\377\377" "\377#\377\377\377<\377\377\377U\377\377\377v\377\377\377\226\377\377\377" "\263\377\377\377\315\377\377\377\337\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377" "\6\377\377\377\14\377\377\377\27\377\377\377'\377\377\377>\377\377\377]\377" "\377\377|\370\370\370\237\377\377\377\275\373\373\373\325\377\377\377\345" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\27\377" "\377\377+\377\377\377C\377\377\377`\377\377\377\202\377\377\377\247\377\377" "\377\304\377\377\377\332\377\377\377\352\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377" "\377\6\377\377\377\14\377\377\377\27\377\377\377+\377\377\377C\377\377\377" "d\377\377\377\210\377\377\377\253\377\377\377\310\376\376\376\336\374\374" "\374\355\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377" "\35\377\377\377+\377\377\377I\377\377\377h\377\377\377\214\377\377\377\260" "\377\377\377\313\374\374\374\342\374\374\374\357\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\377\377\377\6\377\377\377\14\377\377\377\35\342\342\3421\377\377\377I\377" "\377\377k\377\377\377\216\377\377\377\262\377\377\377\316\374\374\374\344" "\377\377\377\360\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377" "\377\377\35\377\377\3771\377\377\377L\377\377\377k\377\377\377\221\377\377" "\377\263\377\377\377\320\377\377\377\344\377\377\377\361\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377" "\377L\377\377\377k\377\377\377\221\377\377\377\263\377\377\377\320\377\377" "\377\344\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377" "\14\377\377\377\35\377\377\3771\377\377\377L\364\364\364p\377\377\377\221" "\372\372\372\266\377\377\377\320\374\374\374\345\377\377\377\362\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\377" "1\377\377\377L\377\377\377p\377\377\377\221\377\377\377\266\373\373\373\322" "\377\377\377\345\377\377\377\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377" "\377\377\14\377\377\377\35\377\377\3771\377\377\377L\377\377\377p\377\377" "\377\221\377\377\377\266\373\373\373\322\377\377\377\345\377\377\377\362" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377" "\377\3771\377\377\377L\377\377\377p\377\377\377\221\377\377\377\266\373\373" "\373\322\377\377\377\345\377\377\377\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377" "\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377\377L\377\377\377" "p\367\367\367\224\377\377\377\266\377\377\377\322\377\377\377\345\374\374" "\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377" "\35\377\377\3771\377\377\377L\377\377\377p\367\367\367\224\377\377\377\266" "\377\377\377\322\377\377\377\345\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\377\377\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377\377L\377" "\377\377p\367\367\367\224\377\377\377\266\377\377\377\322\377\377\377\345" "\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377" "\377\377\35\377\377\3771\377\377\377L\377\377\377p\367\367\367\224\377\377" "\377\266\377\377\377\322\377\377\377\345\374\374\374\362\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377" "\377L\377\377\377p\367\367\367\224\377\377\377\266\377\377\377\322\377\377" "\377\345\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377" "\14\377\377\377\35\377\377\3771\377\377\377L\377\377\377p\367\367\367\224" "\377\377\377\266\377\377\377\322\377\377\377\345\374\374\374\362\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\377" "1\377\377\377L\377\377\377p\367\367\367\224\377\377\377\266\377\377\377\322" "\377\377\377\345\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", }; } // Namespace texture } // Namespace unity #endif ./decorations/DecoratedWindow.h0000644000004100000410000000421313437202764017020 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan */ #ifndef UNITY_DECORATED_WINDOW #define UNITY_DECORATED_WINDOW #include "Introspectable.h" #include #include class CompRegion; class CompWindow; class GLWindowPaintAttrib; class GLMatrix; namespace compiz { namespace window { namespace extents { class Extents; } } } namespace unity { namespace decoration { class Window : public debug::Introspectable { public: typedef std::shared_ptr Ptr; Window(CompWindow*); virtual ~Window() = default; nux::RWProperty title; nux::Property scaled; nux::ROProperty dpi_scale; CompWindow* GetCompWindow(); void Update(); void Undecorate(); void UpdateDecorationPosition(); void UpdateDecorationPositionDelayed(); void UpdateFrameRegion(CompRegion&); void UpdateOutputExtents(compiz::window::extents::Extents&); void UpdateWindowState(unsigned old_state); void Paint(GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask); void Draw(GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask); protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); private: Window(Window const&) = delete; Window& operator=(Window const&) = delete; friend class Manager; struct Impl; std::unique_ptr impl_; }; } // decoration namespace } // unity namespace #endif ./decorations/DecorationsForceQuitDialog.h0000644000004100000410000000247613437202764021163 0ustar www-datawww-data// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2014 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Marco Trevisan */ #ifndef __UNITY_DECORATIONS_FORCE_QUIT_DIALOG__ #define __UNITY_DECORATIONS_FORCE_QUIT_DIALOG__ #include #include "CompizUtils.h" namespace unity { namespace decoration { class ForceQuitDialog { public: typedef std::shared_ptr Ptr; ForceQuitDialog(CompWindow*, Time); ~ForceQuitDialog(); nux::Property