File indexing completed on 2024-05-12 05:34:32

0001 /*
0002     this file is part of the oxygen gtk engine
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004     SPDX-FileCopyrightText: 2010 Ruslan Kabatsayev <b7.10110111@gmail.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "oxygenanimations.h"
0010 #include "../oxygengtkutils.h"
0011 #include "../oxygenqtsettings.h"
0012 #include "../config.h"
0013 
0014 #include <cassert>
0015 #include <iostream>
0016 
0017 namespace Oxygen
0018 {
0019 
0020     //_________________________________________
0021     Animations::Animations( void ):
0022         _innerShadowsEnabled( true ),
0023         _hooksInitialized( false )
0024     {
0025         #if OXYGEN_DEBUG
0026         std::cerr << "Oxygen::Animations::Animations" << std::endl;
0027         #endif
0028 
0029         // create engines
0030         registerEngine( _backgroundHintEngine = new BackgroundHintEngine( this ) );
0031         registerEngine( _comboEngine = new ComboEngine( this ) );
0032         registerEngine( _comboBoxEngine = new ComboBoxEngine( this ) );
0033         registerEngine( _comboBoxEntryEngine = new ComboBoxEntryEngine( this ) );
0034         registerEngine( _dialogEngine = new DialogEngine( this ) );
0035         registerEngine( _flatWidgetEngine = new FlatWidgetEngine( this ) );
0036         registerEngine( _groupBoxEngine = new GroupBoxEngine( this ) );
0037         registerEngine( _groupBoxLabelEngine = new GroupBoxLabelEngine( this ) );
0038         registerEngine( _hoverEngine = new HoverEngine( this ) );
0039         registerEngine( _mainWindowEngine = new MainWindowEngine( this ) );
0040         registerEngine( _menuItemEngine = new MenuItemEngine( this ) );
0041         registerEngine( _panedEngine = new PanedEngine( this ) );
0042         registerEngine( _scrollBarEngine = new ScrollBarEngine( this ) );
0043         registerEngine( _scrolledWindowEngine = new ScrolledWindowEngine( this ) );
0044         registerEngine( _innerShadowEngine = new InnerShadowEngine( this ) );
0045         registerEngine( _tabWidgetEngine = new TabWidgetEngine( this ) );
0046         registerEngine( _treeViewEngine = new TreeViewEngine( this ) );
0047         registerEngine( _widgetSizeEngine = new WidgetSizeEngine( this ) );
0048 
0049         // animations specific engines
0050         registerEngine( _widgetStateEngine = new WidgetStateEngine( this ) );
0051         registerEngine( _arrowStateEngine = new ArrowStateEngine( this ) );
0052         registerEngine( _scrollBarStateEngine = new ScrollBarStateEngine( this ) );
0053         registerEngine( _tabWidgetStateEngine = new TabWidgetStateEngine( this ) );
0054         registerEngine( _treeViewStateEngine = new TreeViewStateEngine( this ) );
0055         registerEngine( _menuBarStateEngine = new MenuBarStateEngine( this ) );
0056         registerEngine( _menuStateEngine = new MenuStateEngine( this ) );
0057         registerEngine( _toolBarStateEngine = new ToolBarStateEngine( this ) );
0058 
0059     }
0060 
0061     //____________________________________________________________________________________________
0062     Animations::~Animations( void )
0063     {
0064         #if OXYGEN_DEBUG
0065         std::cerr << "Oxygen::Animations::~Animations" << std::endl;
0066         #endif
0067 
0068         // delete all engines
0069         for( BaseEngine::List::iterator iter = _engines.begin(); iter != _engines.end(); ++iter )
0070         { delete *iter; }
0071 
0072         // disconnect all signals from map
0073         for( WidgetMap::iterator iter = _allWidgets.begin(); iter != _allWidgets.end(); iter++ )
0074         { iter->second.disconnect(); }
0075 
0076         // clear hooks
0077         _sizeAllocationHook.disconnect();
0078         _realizationHook.disconnect();
0079         _innerShadowHook.disconnect();
0080 
0081     }
0082 
0083     //_________________________________________
0084     void Animations::initialize( const QtSettings& settings )
0085     {
0086 
0087         const bool animationsEnabled( settings.animationsEnabled() );
0088 
0089         // pass animations configuration to engines
0090         _widgetStateEngine->setApplicationName( settings.applicationName() );
0091         _widgetStateEngine->setEnabled( animationsEnabled && settings.genericAnimationsEnabled() );
0092         _widgetStateEngine->setDuration( settings.genericAnimationsDuration() );
0093 
0094         _arrowStateEngine->setApplicationName( settings.applicationName() );
0095         _arrowStateEngine->setEnabled( animationsEnabled && settings.genericAnimationsEnabled() );
0096         _arrowStateEngine->setDuration( settings.genericAnimationsDuration() );
0097 
0098         _scrollBarStateEngine->setApplicationName( settings.applicationName() );
0099         _scrollBarStateEngine->setEnabled( animationsEnabled && settings.genericAnimationsEnabled() );
0100         _scrollBarStateEngine->setDuration( settings.genericAnimationsDuration() );
0101 
0102         _tabWidgetStateEngine->setApplicationName( settings.applicationName() );
0103         _tabWidgetStateEngine->setEnabled( animationsEnabled && settings.genericAnimationsEnabled() );
0104         _tabWidgetStateEngine->setDuration( settings.genericAnimationsDuration() );
0105 
0106         _treeViewStateEngine->setApplicationName( settings.applicationName() );
0107         _treeViewStateEngine->setEnabled( animationsEnabled && settings.genericAnimationsEnabled() );
0108         _treeViewStateEngine->setDuration( settings.genericAnimationsDuration() );
0109 
0110         _menuBarStateEngine->setApplicationName( settings.applicationName() );
0111         _menuBarStateEngine->setAnimationsEnabled( animationsEnabled && (settings.menuBarAnimationType() != NoAnimation) );
0112         _menuBarStateEngine->setFollowMouse( settings.menuBarAnimationType() == FollowMouse );
0113         _menuBarStateEngine->setDuration( settings.menuBarAnimationsDuration() );
0114         _menuBarStateEngine->setFollowMouseAnimationsDuration( settings.menuBarFollowMouseAnimationsDuration() );
0115 
0116         _menuStateEngine->setApplicationName( settings.applicationName() );
0117         _menuStateEngine->setEnabled( animationsEnabled && (settings.menuAnimationType() != NoAnimation) );
0118         _menuStateEngine->setFollowMouse( settings.menuAnimationType() == FollowMouse );
0119         _menuStateEngine->setDuration( settings.menuAnimationsDuration() );
0120         _menuStateEngine->setFollowMouseAnimationsDuration( settings.menuFollowMouseAnimationsDuration() );
0121 
0122         // for now, only Fade animations mode is supported for toolbar animations
0123         _toolBarStateEngine->setApplicationName( settings.applicationName() );
0124         _toolBarStateEngine->setEnabled( animationsEnabled && (settings.toolBarAnimationType() != NoAnimation) );
0125         _toolBarStateEngine->setFollowMouse(  settings.toolBarAnimationType() == FollowMouse );
0126         _toolBarStateEngine->setDuration( settings.genericAnimationsDuration() );
0127         _toolBarStateEngine->setFollowMouseAnimationsDuration( settings.toolBarAnimationsDuration() );
0128 
0129         // background hint engine
0130         _backgroundHintEngine->setUseBackgroundGradient( settings.useBackgroundGradient() );
0131 
0132     }
0133 
0134     //____________________________________________________________________________________________
0135     void Animations::initializeHooks( void )
0136     {
0137         if( _hooksInitialized ) return;
0138 
0139         // https://bugzilla.gnome.org/show_bug.cgi?id=643416
0140         #if ENABLE_INNER_SHADOWS_HACK
0141         if(!getenv("OXYGEN_DISABLE_INNER_SHADOWS_HACK"))
0142         { _innerShadowHook.connect( "realize", (GSignalEmissionHook)innerShadowHook, this ); }
0143         #endif
0144 
0145         _sizeAllocationHook.connect( "size-allocate", (GSignalEmissionHook)sizeAllocationHook, this );
0146         _realizationHook.connect( "realize", (GSignalEmissionHook)realizationHook, this );
0147 
0148         _hooksInitialized = true;
0149     }
0150 
0151     //____________________________________________________________________________________________
0152     bool Animations::registerWidget( GtkWidget* widget )
0153     {
0154 
0155         if( _allWidgets.find( widget ) != _allWidgets.end() ) return false;
0156 
0157         #if OXYGEN_DEBUG
0158         std::cerr << "Oxygen::Animations::registerWidget - " << widget << " (" << (widget ? G_OBJECT_TYPE_NAME( widget ):"0x0") << ")" << std::endl;
0159         #endif
0160 
0161         Signal destroyId;
0162         destroyId.connect( G_OBJECT( widget ), "destroy", G_CALLBACK( destroyNotifyEvent ), this );
0163         _allWidgets.insert( std::make_pair( widget, destroyId ) );
0164         return true;
0165 
0166     }
0167 
0168     //____________________________________________________________________________________________
0169     void Animations::unregisterWidget( GtkWidget* widget )
0170     {
0171 
0172         #if OXYGEN_DEBUG
0173         std::cerr << "Oxygen::Animations::unregisterWidget - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl;
0174         #endif
0175 
0176         // find in map
0177         WidgetMap::iterator iter( _allWidgets.find( widget ) );
0178         assert( iter != _allWidgets.end() );
0179 
0180         // disconnect signal
0181         iter->second.disconnect();
0182 
0183         // erase from map
0184         _allWidgets.erase( widget );
0185 
0186         // erase from all maps
0187         for( BaseEngine::List::iterator iter = _engines.begin(); iter != _engines.end(); ++iter )
0188         { (*iter)->unregisterWidget( widget ); }
0189 
0190     }
0191 
0192     //____________________________________________________________________________________________
0193     void Animations::setEnabled( bool value )
0194     {
0195 
0196         for( BaseEngine::List::iterator iter = _engines.begin(); iter != _engines.end(); ++iter )
0197         { (*iter)->setEnabled( value ); }
0198 
0199     }
0200 
0201     //____________________________________________________________________________________________
0202     gboolean Animations::destroyNotifyEvent( GtkWidget* widget, gpointer data )
0203     {
0204         static_cast<Animations*>(data)->unregisterWidget( widget );
0205         return FALSE;
0206     }
0207 
0208     //____________________________________________________________________________________________
0209     gboolean Animations::sizeAllocationHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data )
0210     {
0211 
0212         // get widget from params
0213         GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) );
0214 
0215         // check type
0216         if( !GTK_IS_WIDGET( widget ) ) return FALSE;
0217 
0218         // cast data
0219         Animations& animations( *static_cast<Animations*>(data) );
0220 
0221         // groupbox labels
0222         #if ENABLE_GROUPBOX_HACK
0223         if( animations.groupBoxLabelEngine().contains( widget ) )
0224         {
0225             animations.groupBoxLabelEngine().adjustSize( widget );
0226             return TRUE;
0227         }
0228         #endif
0229 
0230         #if ENABLE_COMBOBOX_LIST_RESIZE
0231         // comboboxes
0232         if( !GTK_IS_WINDOW( widget ) ) return TRUE;
0233 
0234         GtkWindow* window( GTK_WINDOW( widget ) );
0235         if( gtk_window_get_type_hint( window ) != GDK_WINDOW_TYPE_HINT_COMBO ) return TRUE;
0236 
0237         GtkWidget* combobox = animations.comboBoxEngine().find( widget );
0238         if( !combobox ) combobox = animations.comboBoxEntryEngine().find( widget );
0239         if( !combobox ) combobox = animations.comboEngine().find( widget );
0240         if( !combobox ) return true;
0241 
0242         int w, h;
0243         gtk_window_get_size( window, &w, &h );
0244 
0245         gint targetX, dummy, y;
0246         gtk_window_get_position( window, &dummy, &y );
0247         gdk_window_get_origin( gtk_widget_get_window( combobox ), &targetX, &dummy );
0248 
0249         const GtkAllocation comboAllocation( Gtk::gtk_widget_get_allocation( combobox ) );
0250         int uglyShadowWidth=!Gtk::gdk_default_screen_is_composited();
0251         gtk_window_move( window, targetX + comboAllocation.x + 3 - uglyShadowWidth, y );
0252 
0253         const GtkAllocation widgetAllocation( Gtk::gtk_widget_get_allocation( widget ) );
0254         gtk_widget_set_size_request( widget, comboAllocation.width - 6 + 2*uglyShadowWidth, widgetAllocation.height );
0255         #endif
0256 
0257         return TRUE;
0258 
0259     }
0260 
0261     //____________________________________________________________________________________________
0262     gboolean Animations::innerShadowHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data )
0263     {
0264 
0265         #if GTK_CHECK_VERSION(2,24,2)
0266 
0267         // get widget from params
0268         GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) );
0269 
0270         // check type
0271         if( !GTK_IS_WIDGET( widget ) ) return FALSE;
0272 
0273         // check enabled state
0274         Animations& animations( *static_cast<Animations*>(data) );
0275         if( !animations.innerShadowsEnabled() ) return TRUE;
0276 
0277         // blacklist
0278         if( Gtk::g_object_is_a( G_OBJECT( widget ), "SwtFixed" ) ) return TRUE;
0279         if( Gtk::g_object_is_a( G_OBJECT( widget ), "GtkPizza" ) ) return TRUE;
0280 
0281         GtkWidget* parent(gtk_widget_get_parent(widget));
0282         if( !GTK_IS_SCROLLED_WINDOW( parent ) ) return TRUE;
0283 
0284         GtkWidget* child(gtk_bin_get_child(GTK_BIN(parent)));
0285         if(child!=widget) return TRUE;
0286 
0287         #if OXYGEN_DEBUG
0288         std::cerr
0289             << "Oxygen::Animations::innerShadowHook -"
0290             << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME(widget) << ")"
0291             << " parent: " << parent << " (" << G_OBJECT_TYPE_NAME(parent) << ")"
0292             << " widget path: " << Gtk::gtk_widget_path( widget )
0293             << " isTreeView: " << (GTK_IS_TREE_VIEW(widget)?"true":"false")
0294             << " isTextView: " << (GTK_IS_TEXT_VIEW(widget)?"true":"false")
0295             << std::endl;
0296         #endif
0297 
0298         // force shadow type on known windows
0299         if( Gtk::gtk_scrolled_window_force_sunken( parent ) )
0300         { gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( parent ), GTK_SHADOW_IN ); }
0301 
0302         animations.innerShadowEngine().registerWidget( parent );
0303         animations.innerShadowEngine().registerChild( parent, widget );
0304 
0305         #endif  // Gtk version
0306         return TRUE;
0307 
0308     }
0309 
0310     //____________________________________________________________________________________________
0311     gboolean Animations::realizationHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data )
0312     {
0313 
0314         // get widget from params
0315         GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) );
0316 
0317         // check type
0318         if( !GTK_IS_WIDGET( widget ) ) return FALSE;
0319 
0320         if( GTK_IS_NOTEBOOK( widget ) )
0321         { gtk_notebook_set_show_border( GTK_NOTEBOOK(widget), FALSE ); }
0322 
0323         #if ENABLE_GROUPBOX_HACK
0324         if( GTK_IS_LABEL( widget ) &&  GTK_IS_FRAME( gtk_widget_get_parent( widget ) ) )
0325         {
0326 
0327             GtkFrame *frame( GTK_FRAME( gtk_widget_get_parent( widget ) ) );
0328             if( widget == gtk_frame_get_label_widget( frame ) && !Gtk::gtk_widget_find_parent( widget, "GtkPizza" ) )
0329             {
0330                 #if OXYGEN_DEBUG
0331                 std::cout
0332                     << "Oxygen::Animations::realizationHook -"
0333                     << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0334                     << " parent: " << frame << " (" << G_OBJECT_TYPE_NAME( frame ) << ")"
0335                     << std::endl;
0336                 #endif
0337 
0338                 // modify alignment
0339                 gtk_frame_set_label_align( frame, 0.5, 0.0 );
0340                 gtk_frame_set_shadow_type( frame, GTK_SHADOW_OUT );
0341 
0342                 // register to engine
0343                 Animations& animations( *static_cast<Animations*>(data) );
0344                 animations.groupBoxLabelEngine().registerWidget( widget );
0345                 animations.groupBoxLabelEngine().adjustSize( widget );
0346 
0347             }
0348 
0349         }
0350         #endif
0351 
0352         return TRUE;
0353 
0354     }
0355 
0356 }