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

0001 /*
0002     this file is part of the oxygen gtk engine
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "oxygentoolbarstatedata.h"
0009 #include "../oxygengtkutils.h"
0010 #include "../config.h"
0011 
0012 #include <gtk/gtk.h>
0013 
0014 namespace Oxygen
0015 {
0016 
0017     //________________________________________________________________________________
0018     const int ToolBarStateData::_timeOut = 50;
0019     void ToolBarStateData::connect( GtkWidget* widget )
0020     {
0021 
0022         #if OXYGEN_DEBUG
0023         std::cerr
0024             << "Oxygen::ToolBarStateData::connect - "
0025             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0026             << std::endl;
0027         #endif
0028 
0029         // store widget
0030         _target = widget;
0031 
0032         // connect signals
0033         _leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( leaveNotifyEvent ), this );
0034 
0035         // connect timeLines
0036         _current._timeLine.connect( (GSourceFunc)delayedUpdate, this );
0037         _previous._timeLine.connect( (GSourceFunc)delayedUpdate, this );
0038 
0039         // set directions
0040         _current._timeLine.setDirection( TimeLine::Forward );
0041         _previous._timeLine.setDirection( TimeLine::Backward );
0042 
0043         // follow mouse animation
0044         FollowMouseData::connect( (GSourceFunc)followMouseUpdate, this );
0045 
0046     }
0047 
0048     //________________________________________________________________________________
0049     void ToolBarStateData::disconnect( GtkWidget* widget )
0050     {
0051 
0052         #if OXYGEN_DEBUG
0053         std::cerr
0054             << "Oxygen::ToolBarStateData::disconnect - "
0055             << " " << _target << " (" << (_target ? G_OBJECT_TYPE_NAME( _target ) : "0x0") << ")"
0056             << std::endl;
0057         #endif
0058 
0059         _target = 0L;
0060 
0061         // disconnect signal
0062         _leaveId.disconnect();
0063 
0064         // disconnect timelines
0065         _current._timeLine.disconnect();
0066         _previous._timeLine.disconnect();
0067         _timer.stop();
0068 
0069         // disconnect all children
0070         for( HoverDataMap::iterator iter = _hoverData.begin(); iter != _hoverData.end(); ++iter )
0071         { iter->second.disconnect(); }
0072 
0073         _hoverData.clear();
0074 
0075         FollowMouseData::disconnect();
0076 
0077     }
0078 
0079     //________________________________________________________________________________
0080     void ToolBarStateData::registerChild( GtkWidget* widget, bool value )
0081     {
0082 
0083         // make sure widget is not already in map
0084         if( _hoverData.find( widget ) == _hoverData.end() )
0085         {
0086 
0087             #if OXYGEN_DEBUG
0088             std::cerr
0089                 << "Oxygen::ToolBarStateData::registerChild -"
0090                 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0091                 << std::endl;
0092             #endif
0093 
0094             // allocate new Hover data
0095             HoverData data;
0096             data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
0097             data._enterId.connect( G_OBJECT(widget), "enter-notify-event", G_CALLBACK( childEnterNotifyEvent ), this );
0098             data._leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( childLeaveNotifyEvent ), this );
0099 
0100             // and insert in map
0101             _hoverData.insert( std::make_pair( widget, data ) );
0102             updateState( widget, value, false );
0103 
0104 
0105         }
0106 
0107     }
0108 
0109     //________________________________________________________________________________
0110     void ToolBarStateData::unregisterChild( GtkWidget* widget )
0111     {
0112 
0113         #if OXYGEN_DEBUG
0114         std::cerr
0115             << "Oxygen::ToolBarStateData::unregisterChild -"
0116             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0117             << std::endl;
0118         #endif
0119 
0120         // loopup in hover map
0121         HoverDataMap::iterator iter( _hoverData.find( widget ) );
0122         if( iter != _hoverData.end() )
0123         {
0124             iter->second.disconnect();
0125             _hoverData.erase( iter );
0126         }
0127 
0128         // disconnect previous and current
0129         if( widget == _previous._widget )
0130         {
0131             _previous._widget = 0L;
0132             _previous._timeLine.disconnect();
0133         }
0134 
0135         if( widget == _current._widget )
0136         {
0137             _current._widget = 0L;
0138             _current._timeLine.disconnect();
0139         }
0140 
0141     }
0142 
0143     //________________________________________________________________________________
0144     bool ToolBarStateData::updateState( GtkWidget* widget, bool state, bool delayed )
0145     {
0146 
0147         const GdkRectangle rect( widget ? Gtk::gtk_widget_get_allocation( widget ) : Gtk::gdk_rectangle() );
0148 
0149         if( state && widget != _current._widget )
0150         {
0151 
0152             // stop timer
0153             if( _timer.isRunning() ) _timer.stop();
0154 
0155             // stop current animation if running
0156             if( _current._timeLine.isRunning() ) _current._timeLine.stop();
0157 
0158             // stop previous animation if running
0159             if( _current.isValid() )
0160             {
0161                 if( _previous._timeLine.isRunning() ) _previous._timeLine.stop();
0162 
0163                 if( _previous.isValid() )
0164                 { _dirtyRect = _previous._rect; }
0165 
0166                 // move current to previous
0167                 _previous.copy( _current );
0168             }
0169 
0170             // assign new widget to current and start animation
0171             const GdkRectangle startRect( _current._rect );
0172             const bool animate( !_current.isValid() );
0173             _current.update( widget, rect );
0174             if( _current.isValid() )
0175             {
0176                 if( animate || !followMouse() ) _current._timeLine.start();
0177                 else startAnimation( startRect, _current._rect );
0178 
0179             }
0180 
0181             return true;
0182 
0183         } else if( (!state) && widget == _current._widget ) {
0184 
0185             // stop current animation if running
0186             if( _current._timeLine.isRunning() ) _current._timeLine.stop();
0187 
0188             // stop previous animation if running
0189             if( _previous._timeLine.isRunning() ) _previous._timeLine.stop();
0190 
0191             if( _previous.isValid() )
0192             { _dirtyRect = _previous._rect; }
0193 
0194             // move current to previous; clear current, and animate
0195             if( followMouse() && delayed ) {
0196 
0197                 // start delayed animation
0198                 if( !_timer.isRunning() )
0199                 { _timer.start( _timeOut, (GSourceFunc)delayedAnimate, this ); }
0200 
0201             } else {
0202 
0203                 if( _timer.isRunning() ) _timer.stop();
0204                 _previous.copy( _current );
0205                 _current.clear();
0206 
0207                 // start fade-out timeLine
0208                 if( _previous.isValid() ) _previous._timeLine.start();
0209 
0210             }
0211 
0212             return true;
0213 
0214         } else return false;
0215 
0216     }
0217 
0218     //_____________________________________________
0219     GdkRectangle ToolBarStateData::dirtyRect( void )
0220     {
0221 
0222         GdkRectangle rect( Gtk::gdk_rectangle() );
0223         Gtk::gdk_rectangle_union( &_previous._rect, &_current._rect, &rect );
0224 
0225         // add _dirtyRect
0226         if( Gtk::gdk_rectangle_is_valid( &_dirtyRect ) )
0227         {
0228             Gtk::gdk_rectangle_union( &_dirtyRect, &rect, &rect );
0229             _dirtyRect = Gtk::gdk_rectangle();
0230         }
0231 
0232         // add followMouse dirtyRect
0233         if( followMouse() )
0234         {
0235             const GdkRectangle followMouseRect( FollowMouseData::dirtyRect() );
0236             Gtk::gdk_rectangle_union( &followMouseRect, &rect, &rect );
0237         }
0238 
0239         // check validity
0240         if( !Gtk::gdk_rectangle_is_valid( &rect ) ) return rect;
0241 
0242         /* Add viewport offsets if any */
0243         if( GTK_IS_VIEWPORT( _target ) )
0244         {
0245             gint xOffset, yOffset;
0246             Gtk::gtk_viewport_get_position( GTK_VIEWPORT( _target ), &xOffset, &yOffset );
0247             rect.x -= xOffset;
0248             rect.y -= yOffset;
0249         }
0250 
0251         /* Add extra margin to rect. FIXME: this should be triggered from widget's metrics */
0252         rect.x -= 2;
0253         rect.y -= 2;
0254         rect.width += 4;
0255         rect.height += 4;
0256 
0257         return rect;
0258 
0259     }
0260 
0261     //____________________________________________________________________________________________
0262     gboolean ToolBarStateData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data )
0263     {
0264         #if OXYGEN_DEBUG
0265         std::cerr
0266             << "Oxygen::ToolBarStateData::childDestroyNotifyEvent -"
0267             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0268             << std::endl;
0269         #endif
0270         static_cast<ToolBarStateData*>(data)->unregisterChild( widget );
0271         return FALSE;
0272     }
0273 
0274     //________________________________________________________________________________
0275     gboolean ToolBarStateData::childEnterNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
0276     {
0277 
0278         #if OXYGEN_DEBUG
0279         std::cerr
0280             << "Oxygen::ToolBarStateData::childEnterNotifyEvent -"
0281             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0282             << std::endl;
0283         #endif
0284 
0285         static_cast<ToolBarStateData*>( data )->updateState( widget, true, false );
0286         return FALSE;
0287     }
0288 
0289     //________________________________________________________________________________
0290     gboolean ToolBarStateData::childLeaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
0291     {
0292 
0293         #if OXYGEN_DEBUG
0294         std::cerr
0295             << "Oxygen::ToolBarStateData::childLeaveNotifyEvent -"
0296             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0297             << std::endl;
0298         #endif
0299 
0300         static_cast<ToolBarStateData*>( data )->updateState( widget, false, true );
0301         return FALSE;
0302 
0303     }
0304 
0305     //________________________________________________________________________________
0306     gboolean ToolBarStateData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer pointer )
0307     {
0308         #if OXYGEN_DEBUG
0309         std::cerr
0310             << "Oxygen::ToolBarStateData::leaveNotifyEvent -"
0311             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0312             << std::endl;
0313         #endif
0314 
0315         ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) );
0316         if( data._current.isValid() ) data.updateState( data._current._widget, false, false );
0317         return FALSE;
0318     }
0319 
0320     //_____________________________________________
0321     gboolean ToolBarStateData::delayedUpdate( gpointer pointer )
0322     {
0323 
0324         ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) );
0325 
0326         if( data._target )
0327         {
0328             const GdkRectangle rect( data.dirtyRect() );
0329             Gtk::gtk_widget_queue_draw( data._target, &rect );
0330             if( data._previous._widget ) Gtk::gtk_widget_queue_draw( data._previous._widget );
0331             if( data._current._widget ) Gtk::gtk_widget_queue_draw( data._current._widget );
0332         }
0333 
0334         return FALSE;
0335 
0336     }
0337 
0338     //_____________________________________________
0339     gboolean ToolBarStateData::followMouseUpdate( gpointer pointer )
0340     {
0341 
0342         ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) );
0343 
0344         if( data._target && data.followMouse() )
0345         {
0346 
0347             data.updateAnimatedRect();
0348 
0349             GdkRectangle rect( data.dirtyRect() );
0350             Gtk::gtk_widget_queue_draw( data._target, &rect );
0351             if( data._previous._widget ) Gtk::gtk_widget_queue_draw( data._previous._widget );
0352             if( data._current._widget ) Gtk::gtk_widget_queue_draw( data._current._widget );
0353 
0354         }
0355 
0356         return FALSE;
0357 
0358     }
0359 
0360     //_____________________________________________
0361     gboolean ToolBarStateData::delayedAnimate( gpointer pointer )
0362     {
0363 
0364         ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) );
0365         data._previous.copy( data._current );
0366         data._current.clear();
0367 
0368         if( data._previous.isValid() )
0369         { data._previous._timeLine.start(); }
0370 
0371         return FALSE;
0372 
0373     }
0374 }