File indexing completed on 2024-04-28 05:32:21

0001 /*
0002     oxygenwindowmanager.cpp
0003     pass some window mouse press/release/move event actions to window manager
0004     -------------------
0005 
0006     SPDX-FileCopyrightText: 2010 Cédric Bellegarde <gnumdk@gmail.com>
0007     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0008 
0009     Largely inspired from Qtcurve style
0010     SPDX-FileCopyrightText: 2003-2010 Craig Drummond <craig.p.drummond@gmail.com>
0011 
0012     SPDX-License-Identifier: LGPL-2.0-or-later
0013 */
0014 
0015 #include "oxygenwindowmanager.h"
0016 #include "oxygenpropertynames.h"
0017 #include "oxygenstyle.h"
0018 #include "config.h"
0019 
0020 namespace Oxygen
0021 {
0022 
0023 
0024     //_________________________________________________
0025     WindowManager::WindowManager( void ):
0026         _useWMMoveResize( true ),
0027         _cursorLoaded( false ),
0028         _cursor( 0L ),
0029         _oldCursor( 0L ),
0030         _dragMode( Full ),
0031         _hooksInitialized( false ),
0032         _dragAboutToStart( false ),
0033         _dragInProgress( false ),
0034         _dragDistance( 4 ),
0035         _dragDelay( 500 ),
0036         _widget( 0L ),
0037         _lastRejectedEvent( 0L ),
0038         _x(-1),
0039         _y(-1),
0040         _globalX(-1),
0041         _globalY(-1),
0042         _time(0)
0043     {
0044         #if OXYGEN_DEBUG
0045         std::cerr << "Oxygen::WindowManager::WindowManager" << std::endl;
0046         #endif
0047 
0048         // black list
0049         initializeBlackList();
0050 
0051     }
0052 
0053     //_________________________________________________
0054     WindowManager::~WindowManager( void )
0055     {
0056 
0057         #if OXYGEN_DEBUG
0058         std::cerr << "Oxygen::WindowManager::~WindowManager" << std::endl;
0059         #endif
0060 
0061         _styleSetHook.disconnect();
0062         _buttonReleaseHook.disconnect();
0063         _map.disconnectAll();
0064         _map.clear();
0065 
0066         // cursor
0067         if( _cursor ) gdk_cursor_unref( _cursor );
0068 
0069     }
0070 
0071     //_________________________________________________
0072     void WindowManager::initializeHooks( void )
0073     {
0074 
0075         if( _hooksInitialized ) return;
0076         if( _dragMode!=Disabled )
0077         {
0078             _styleSetHook.connect( "style-set", (GSignalEmissionHook)styleSetHook, this );
0079             _buttonReleaseHook.connect( "button-release-event", (GSignalEmissionHook)buttonReleaseHook, this );
0080         }
0081         _hooksInitialized = true;
0082 
0083     }
0084 
0085     //_________________________________________________
0086     bool WindowManager::registerWidget( GtkWidget* widget )
0087     {
0088 
0089         // load cursor if needed
0090         if( !_cursorLoaded )
0091         {
0092             assert( !_cursor );
0093             GdkDisplay *display( gtk_widget_get_display( widget ) );
0094             _cursor = gdk_cursor_new_from_name( display, "all-scroll" );
0095             _cursorLoaded = true;
0096         }
0097 
0098         if( _map.contains( widget ) ) return false;
0099 
0100         // check against black listed typenames
0101         if( widgetIsBlackListed( widget ) )
0102         {
0103 
0104             #if OXYGEN_DEBUG
0105             std::cerr
0106                 << "Oxygen::WindowManager::registerWidget -"
0107                 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0108                 << " is black listed"
0109                 << std::endl;
0110             #endif
0111 
0112             registerBlackListWidget( widget );
0113             return false;
0114         }
0115 
0116         // check blocking property
0117         if( g_object_get_data( G_OBJECT( widget ), PropertyNames::noWindowGrab ) )
0118         {
0119 
0120             #if OXYGEN_DEBUG
0121             std::cerr
0122                 << "Oxygen::WindowManager::registerWidget -"
0123                 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0124                 << " requests no grab"
0125                 << std::endl;
0126             #endif
0127 
0128             registerBlackListWidget( widget );
0129             return false;
0130         }
0131 
0132         // Window with no decorations (set by app), let window manage it self
0133         if( GTK_IS_WINDOW( widget ) && !gtk_window_get_decorated( GTK_WINDOW( widget ) ) )
0134         {
0135 
0136             #if OXYGEN_DEBUG
0137             std::cerr
0138                 << "Oxygen::WindowManager::registerWidget -"
0139                 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0140                 << " is not decorated"
0141                 << std::endl;
0142             #endif
0143 
0144             registerBlackListWidget( widget );
0145             return false;
0146         }
0147 
0148         // widgets used in tabs also must be ignored (happens, unfortunately)
0149         GtkWidget* parent( gtk_widget_get_parent( widget ) );
0150         if( GTK_IS_NOTEBOOK( parent ) && Gtk::gtk_notebook_is_tab_label( GTK_NOTEBOOK( parent ), widget ) )
0151         { return false; }
0152 
0153         /*
0154         check event mask (for now we only need to do that for GtkWindow)
0155         The idea is that if the window has been set to receive button_press and button_release events
0156         (which is not done by default), it likely means that it does something with such events,
0157         in which case we should not use them for grabbing
0158         */
0159         if(
0160             ( GTK_IS_WINDOW( widget ) || GTK_IS_VIEWPORT( widget ) ) &&
0161             ( gtk_widget_get_events ( widget ) &
0162             ( GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK ) ) )
0163         {
0164             #if OXYGEN_DEBUG
0165             std::cerr
0166                 << "Oxygen::WindowManager::registerWidget -"
0167                 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0168                 << " has invalid event mask"
0169                 << std::endl;
0170             #endif
0171             registerBlackListWidget( widget );
0172             return false;
0173         }
0174 
0175         // check for blacklisted parent
0176         if( widgetHasBlackListedParent( widget ) )
0177         {
0178 
0179             #if OXYGEN_DEBUG
0180             std::cerr
0181                 << "Oxygen::WindowManager::registerWidget -"
0182                 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0183                 << " has black listed parent"
0184                 << std::endl;
0185             #endif
0186             return false;
0187         }
0188 
0189         #if OXYGEN_DEBUG
0190         std::cerr
0191             << "Oxygen::WindowManager::registerWidget -"
0192             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0193             << " " << gtk_widget_get_name( widget )
0194             << std::endl;
0195         #endif
0196 
0197         // Force widget to listen to relevant events
0198         gtk_widget_add_events( widget,
0199             GDK_BUTTON_RELEASE_MASK |
0200             GDK_BUTTON_PRESS_MASK   |
0201             GDK_LEAVE_NOTIFY_MASK   |
0202             GDK_BUTTON1_MOTION_MASK );
0203 
0204         // allocate new Data object
0205         Data& data( _map.registerWidget( widget ) );
0206 
0207         // connect signals
0208         if( _dragMode != Disabled ) connect( widget, data );
0209         return true;
0210 
0211     }
0212 
0213     //_________________________________________________
0214     void WindowManager::unregisterWidget( GtkWidget* widget )
0215     {
0216         if( !_map.contains( widget ) ) return;
0217 
0218         #if OXYGEN_DEBUG
0219         std::cerr << "Oxygen::WindowManager::unregisterWidget - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl;
0220         #endif
0221 
0222         _map.value( widget ).disconnect( widget );
0223         _map.erase( widget );
0224 
0225         // reset drag is the current drag target
0226         if( _widget == widget ) resetDrag();
0227 
0228     }
0229 
0230     //_________________________________________________
0231     bool WindowManager::registerBlackListWidget( GtkWidget* widget )
0232     {
0233 
0234         // make sure that widget is not already connected
0235         if( _blackListWidgets.find( widget ) != _blackListWidgets.end() ) return false;
0236 
0237         #if OXYGEN_DEBUG
0238         std::cerr << "Oxygen::WindowManager::registerBlackListWidget - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl;
0239         #endif
0240 
0241       // connect destroy signal and insert in map
0242         Signal destroyId;
0243         destroyId.connect( G_OBJECT( widget ), "destroy", G_CALLBACK( wmBlackListDestroy ), this );
0244         _blackListWidgets.insert( std::make_pair( widget, destroyId ) );
0245         return true;
0246 
0247     }
0248 
0249     //_________________________________________________
0250     void WindowManager::unregisterBlackListWidget( GtkWidget* widget )
0251     {
0252 
0253         WidgetMap::iterator iter( _blackListWidgets.find( widget ) );
0254         if( iter == _blackListWidgets.end() ) return;
0255 
0256         #if OXYGEN_DEBUG
0257         std::cerr << "Oxygen::WindowManager::unregisterBlackListWidget - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl;
0258         #endif
0259 
0260         iter->second.disconnect();
0261         _blackListWidgets.erase( widget );
0262 
0263     }
0264 
0265     //_________________________________________________
0266     void WindowManager::setDragMode( WindowManager::Mode mode )
0267     {
0268         if( mode == _dragMode ) return;
0269 
0270         // connect/disconnect all data in map, based on new and old mode
0271         if( mode == Disabled ) { _map.disconnectAll(); }
0272         else if( _dragMode == Disabled )
0273         {
0274             DataMap<Data>::Map& map( _map.map() );
0275             for( DataMap<Data>::Map::iterator iter = map.begin(); iter != map.end(); ++iter )
0276             { connect( iter->first, iter->second ); }
0277         }
0278 
0279         // assign new mode
0280         _dragMode = mode;
0281 
0282     }
0283 
0284     //_________________________________________________
0285     gboolean WindowManager::wmDestroy( GtkWidget* widget, gpointer data )
0286     {
0287         static_cast<WindowManager*>(data)->unregisterWidget( widget );
0288         return false;
0289     }
0290 
0291     //_________________________________________________
0292     gboolean WindowManager::wmBlackListDestroy( GtkWidget* widget, gpointer data )
0293     {
0294         static_cast<WindowManager*>(data)->unregisterBlackListWidget( widget );
0295         return false;
0296     }
0297 
0298     //_________________________________________________
0299     gboolean WindowManager::wmButtonPress(GtkWidget *widget, GdkEventButton* event, gpointer data )
0300     {
0301 
0302         if( event->type == GDK_BUTTON_PRESS && event->button == Gtk::LeftButton )
0303         {
0304 
0305             const bool accepted( static_cast<WindowManager*>(data)->canDrag( widget, event ) );
0306             #if OXYGEN_DEBUG
0307             std::cerr << "Oxygen::WindowManager::wmButtonPress -"
0308                 << " event: " << event
0309                 << " widget: " << widget
0310                 << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0311                 << " " << gtk_widget_get_name( widget )
0312                 << " accepted: " << accepted
0313                 << std::endl;
0314             #endif
0315 
0316             return accepted;
0317 
0318         } else return false;
0319 
0320     }
0321 
0322     //_________________________________________________
0323     gboolean WindowManager::wmLeave(GtkWidget*, GdkEventCrossing*, gpointer data )
0324     {
0325         WindowManager& manager( *static_cast<WindowManager*>( data ) );
0326         return (gboolean) ( manager.useWMMoveResize() && manager.resetDrag() );
0327     }
0328 
0329     //_________________________________________________
0330     gboolean WindowManager::wmMotion( GtkWidget *widget, GdkEventMotion* event, gpointer data )
0331     { return (gboolean) static_cast<WindowManager*>(data)->startDrag( widget, event ); }
0332 
0333     //_________________________________________________________________
0334     gboolean WindowManager::startDelayedDrag( gpointer data )
0335     {
0336         static_cast<WindowManager*>( data )->startDrag();
0337         return FALSE;
0338     }
0339 
0340     //_________________________________________________________________
0341     gboolean WindowManager::styleSetHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data )
0342     {
0343 
0344         // get widget from params
0345         GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) );
0346         if( !GTK_IS_WIDGET( widget ) ) return FALSE;
0347 
0348         // never register widgets that are possibly applets
0349         if( Gtk::gtk_widget_is_applet( widget ) ) return TRUE;
0350 
0351         // cast data to window manager
0352         WindowManager &manager( *static_cast<WindowManager*>(data ) );
0353 
0354         bool registered( false );
0355         if( GTK_IS_WINDOW( widget ) ||
0356             GTK_IS_VIEWPORT( widget ) ||
0357             GTK_IS_TOOLBAR( widget ) ||
0358             GTK_IS_MENU_BAR( widget ) ||
0359             GTK_IS_NOTEBOOK( widget ) )
0360         {
0361 
0362             // top-level windows
0363             registered = manager.registerWidget( widget );
0364 
0365         } else if(
0366             Gtk::gtk_button_is_in_path_bar(widget) &&
0367             Gtk::g_object_is_a( G_OBJECT( gtk_widget_get_parent( widget ) ),  "GtkPathBar" ) ) {
0368 
0369             // path bar widgets
0370             registered = manager.registerWidget( widget );
0371 
0372         }
0373 
0374         #if OXYGEN_DEBUG
0375         if( registered )
0376         {
0377             std::cerr
0378                 << "Oxygen::WindowManager::styleSetHook -"
0379                 << " registering: " << widget
0380                 << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0381                 << std::endl;
0382         }
0383         #else
0384         // silence the compiler
0385         (void)registered;
0386         #endif
0387 
0388         return TRUE;
0389 
0390     }
0391 
0392     //_________________________________________________________________
0393     gboolean WindowManager::buttonReleaseHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data )
0394     {
0395 
0396         // get widget from params
0397         GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) );
0398         if( !GTK_IS_WIDGET( widget ) ) return FALSE;
0399 
0400         // cast data to window manager
0401         WindowManager &manager( *static_cast<WindowManager*>(data) );
0402 
0403         // check mode
0404         if( manager._dragMode == Disabled ) return TRUE;
0405 
0406         // check if drag is in progress, and reset if yes
0407         if( manager._dragAboutToStart || manager._dragInProgress )
0408         {
0409 
0410             #if OXYGEN_DEBUG
0411             std::cerr << "Oxygen::WindowManager::buttonReleaseHook -"
0412                 << " widget: " << widget
0413                 << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0414                 << " " << gtk_widget_get_name( widget )
0415                 << std::endl;
0416             #endif
0417 
0418             // reset cursor
0419             if( !manager._useWMMoveResize && manager._dragInProgress )
0420             { manager.unsetCursor( widget ); }
0421 
0422             manager.resetDrag();
0423 
0424         }
0425 
0426         return TRUE;
0427     }
0428 
0429     //_________________________________________________________________
0430     bool WindowManager::startDrag( GtkWidget* widget, GdkEventMotion* event )
0431     {
0432 
0433         // make sure drag is enabled
0434         if( !_dragAboutToStart ) return false;
0435 
0436         // check displacement with respect to drag start, as long as not already started
0437         if( !_dragInProgress )
0438         {
0439             const int distance( abs( _globalX - int(event->x_root) ) + abs( _globalY - int(event->y_root) ) );
0440 
0441             if( distance > 0 && _timer.isRunning() ) _timer.stop();
0442             if( distance < _dragDistance ) return false;
0443 
0444         }
0445 
0446         // start drag from current position
0447         if( _useWMMoveResize )
0448         {
0449 
0450             return startDrag( widget, int(event->x_root), int(event->y_root), event->time );
0451 
0452         } else {
0453 
0454             if( !_dragInProgress )
0455             {
0456                 setCursor( widget );
0457                 _dragInProgress = true;
0458             }
0459 
0460             GtkWindow* topLevel = GTK_WINDOW( gtk_widget_get_toplevel( widget ) );
0461             int wx, wy;
0462             gtk_window_get_position( topLevel, &wx, &wy );
0463             gtk_window_move( topLevel, wx + event->x - _x, wy + event->y - _y );
0464             return true;
0465 
0466         }
0467 
0468     }
0469 
0470     //_________________________________________________________________
0471     bool WindowManager::startDrag( GtkWidget* widget, int x, int y, guint32 time )
0472     {
0473 
0474         // create xevent and send.
0475         if( _useWMMoveResize )
0476         {
0477 
0478             _dragInProgress = true;
0479             GtkWindow* topLevel = GTK_WINDOW( gtk_widget_get_toplevel( widget ) );
0480             gtk_window_begin_move_drag( topLevel, Gtk::LeftButton, x, y, time );
0481             resetDrag();
0482 
0483         } else if( !_dragInProgress ) {
0484 
0485             _dragInProgress = true;
0486             setCursor( widget );
0487 
0488         }
0489 
0490         return true;
0491 
0492     }
0493 
0494     //_________________________________________________________________
0495     void WindowManager::setCursor( GtkWidget* widget )
0496     {
0497 
0498         GdkWindow* window( gtk_widget_get_window( gtk_widget_get_toplevel( widget ) ) );
0499         _oldCursor = gdk_window_get_cursor( window );
0500         gdk_window_set_cursor( window, _cursor );
0501     }
0502 
0503     //_________________________________________________________________
0504     void WindowManager::unsetCursor( GtkWidget* widget )
0505     {
0506         GdkWindow* window( gtk_widget_get_window( gtk_widget_get_toplevel( widget ) ) );
0507         gdk_window_set_cursor( window, _oldCursor );
0508     }
0509 
0510     //_________________________________________________
0511     bool WindowManager::resetDrag( void )
0512     {
0513 
0514         _widget = 0L;
0515         _lastRejectedEvent = 0L;
0516         _x = -1;
0517         _y = -1;
0518         _globalX = -1;
0519         _globalY = -1;
0520         _time = 0;
0521 
0522         // stop timer
0523         if( _timer.isRunning() ) _timer.stop();
0524 
0525         if( _dragAboutToStart || _dragInProgress )
0526         {
0527 
0528             _dragAboutToStart = false;
0529             _dragInProgress = false;
0530             return true;
0531 
0532         } else return false;
0533 
0534     }
0535 
0536     //_________________________________________________
0537     bool WindowManager::canDrag( GtkWidget* widget, GdkEventButton* event )
0538     {
0539 
0540         if( _dragMode == Disabled ) return false;
0541         else if( !_dragAboutToStart && checkCursor( event->window ) && withinWidget(widget, event ) && useEvent( widget, event ) )
0542         {
0543 
0544             // store widget and event position
0545             _widget = widget;
0546             _x = int(event->x);
0547             _y = int(event->y);
0548             _globalX = int(event->x_root);
0549             _globalY = int(event->y_root);
0550             _time = event->time;
0551 
0552             // start timer
0553             if( _timer.isRunning() ) _timer.stop();
0554             _timer.start( _dragDelay, (GSourceFunc)startDelayedDrag, this );
0555 
0556             // enable drag and accept
0557             _dragAboutToStart = true;
0558             return true;
0559 
0560         } else {
0561 
0562             // mark event as rejected
0563             _lastRejectedEvent = event;
0564             return false;
0565         }
0566 
0567     }
0568 
0569     //_________________________________________________
0570     bool WindowManager::checkCursor( GdkWindow* window ) const
0571     {
0572         if( !window ) return true;
0573         GdkCursor* cursor = gdk_window_get_cursor( window );
0574 
0575         // do not propagate if widget's cursor is currently modified,
0576         // since it is usually indicative of mouse action
0577         return !cursor || gdk_cursor_get_cursor_type( cursor ) == GDK_ARROW;
0578 
0579     }
0580 
0581     //_________________________________________________
0582     bool WindowManager::withinWidget( GtkWidget* widget, GdkEventButton* event ) const
0583     {
0584 
0585         // get top level widget
0586         GtkWidget* topLevel( gtk_widget_get_toplevel( widget ) );
0587         if( !topLevel ) return true;
0588 
0589         // get top level window;
0590         GdkWindow *window( gtk_widget_get_window( topLevel ) );
0591         if( !window ) return true;
0592 
0593         // translate widget position to topLevel
0594         int wx(0);
0595         int wy(0);
0596         gtk_widget_translate_coordinates( widget, topLevel, wx, wy, &wx, &wy );
0597 
0598         // translate to absolute coordinates
0599         int nx(0);
0600         int ny(0);
0601         gdk_window_get_origin( window, &nx, &ny );
0602         wx += nx;
0603         wy += ny;
0604 
0605         // get widget size.
0606         GtkAllocation allocation( Gtk::gtk_widget_get_allocation( widget ) );
0607 
0608         // translate event position in 'local' coordinates with respect to widget's window
0609         const int xLocal  = int(event->x_root) - wx + allocation.x;
0610         const int yLocal  = int(event->y_root) - wy + allocation.y;
0611 
0612         if( GTK_IS_NOTEBOOK( widget ) )
0613         {
0614 
0615             GtkAllocation tabbarRect;
0616             Gtk::gtk_notebook_get_tabbar_rect( GTK_NOTEBOOK( widget ), &tabbarRect );
0617 
0618             // compare to event local position
0619             if( !Gtk::gdk_rectangle_contains( &tabbarRect, xLocal, yLocal ) ) return false;
0620             else if( !Style::instance().animations().tabWidgetEngine().contains( widget ) ) return false;
0621             else return !Style::instance().animations().tabWidgetEngine().isInTab( widget, xLocal, yLocal );
0622 
0623         } else {
0624 
0625             // compare to event position
0626             return Gtk::gdk_rectangle_contains( &allocation, xLocal, yLocal );
0627 
0628         }
0629 
0630     }
0631 
0632     //_________________________________________________
0633     bool WindowManager::useEvent( GtkWidget* widget, GdkEventButton* event )
0634     {
0635 
0636         // check against mode
0637         if( _dragMode == Disabled ) return false;
0638         else if( _dragMode == Minimal && !( GTK_IS_TOOLBAR( widget ) || GTK_IS_MENU_BAR( widget ) ) ) return false;
0639         else if( _lastRejectedEvent && event == _lastRejectedEvent ) return false;
0640         else {
0641 
0642             const DragStatus status( childrenUseEvent( widget, event, false ) );
0643 
0644             #if OXYGEN_DEBUG
0645             std::cerr << "Oxygen::WindowManager::useEvent -"
0646                 << " event: " << event
0647                 << " widget: " << widget
0648                 << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
0649                 << " " << gtk_widget_get_name( widget )
0650                 << " status: " << dragStatusString(status)
0651                 << std::endl;
0652             #endif
0653 
0654             return status == Accepted;
0655 
0656         }
0657 
0658     }
0659 
0660     //_________________________________________________
0661     std::string WindowManager::dragStatusString( DragStatus status ) const
0662     {
0663 
0664         switch( status )
0665         {
0666             case Accepted: return "accepted";
0667             case BlackListed: return "widget is black-listed";
0668             case WidgetIsPrelight: return "widget is prelit";
0669             case WidgetIsButton: return "widget is a button";
0670             case WidgetIsMenuItem: return "widget is a menu item";
0671             case WidgetIsScrolledWindow: return "widget is a scrolled window with focus";
0672             case WidgetIsTabLabel: return "widget is a notebook's tab label";
0673             case InvalidWindow: return "widget's window is hidden";
0674             case InvalidEventMask: return "invalid event mask";
0675             default: return "unknown";
0676         }
0677     }
0678 
0679     //_________________________________________________
0680     WindowManager::DragStatus WindowManager::childrenUseEvent( GtkWidget* widget, GdkEventButton* event, bool inNoteBook ) const
0681     {
0682 
0683         /*
0684         always reject if
0685         - widget is black listed
0686         - widget is prelit
0687         - widget is an active button
0688         - widget is a menu item
0689         - widget event mask is explicitly set to GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK
0690         */
0691         if( widgetIsBlackListed( widget ) ) return BlackListed;
0692         else if( gtk_widget_get_state( widget ) == GTK_STATE_PRELIGHT ) return WidgetIsPrelight;
0693         else if( GTK_IS_BUTTON( widget ) ) return WidgetIsButton;
0694         else if( GTK_IS_MENU_ITEM( widget ) ) return WidgetIsMenuItem;
0695         else if( GTK_IS_SCROLLED_WINDOW( widget ) && ( !inNoteBook || gtk_widget_is_focus( widget ) ) ) return WidgetIsScrolledWindow;
0696 
0697         // check window
0698         GdkWindow *window = gtk_widget_get_window( widget );
0699         if( !( window && gdk_window_is_visible( window ) ) ) return InvalidWindow;
0700 
0701         // accept if widget is not a container and we got this far,
0702         if( !GTK_IS_CONTAINER( widget ) ) return Accepted;
0703 
0704         // check notebook
0705         if( GTK_IS_NOTEBOOK( widget ) )
0706         {
0707 
0708             /*
0709             always reject if
0710             - Notebook has visible scroll arrows
0711             - there is an hovered tab
0712             - notebook is not registered
0713             */
0714             if( Gtk::gtk_notebook_has_visible_arrows( GTK_NOTEBOOK( widget ) ) ||
0715                 !Style::instance().animations().tabWidgetEngine().contains( widget ) ||
0716                 Style::instance().animations().tabWidgetEngine().hoveredTab( widget ) != -1 )
0717             { return WidgetIsPrelight; }
0718 
0719             // keep track on whether we are in a notebook
0720             inNoteBook = true;
0721         }
0722 
0723         // loop over children and check
0724         DragStatus status = Accepted;
0725         GList *children = gtk_container_get_children( GTK_CONTAINER( widget ) );
0726         for( GList *child = g_list_first( children ); child; child = g_list_next( child ) )
0727         {
0728 
0729             // cast child to GtkWidget
0730             if( !GTK_IS_WIDGET( child->data ) ) continue;
0731             GtkWidget* childWidget( GTK_WIDGET( child->data ) );
0732 
0733             // check that widget contains event
0734             if( withinWidget( childWidget, event ) )
0735             {
0736 
0737                 /*
0738                 following checks must not be moved upstream, as they are not to be applied
0739                 to the first parent widget (the one that recieved the mouse press event
0740                 */
0741 
0742                 // always reject if child explicitly accepts button press and button release events
0743                 if( gtk_widget_get_events( childWidget ) & (GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK) )
0744                 {
0745 
0746                     status = InvalidEventMask;
0747 
0748                 } else if( GTK_IS_NOTEBOOK( widget ) && Gtk::gtk_notebook_is_tab_label( GTK_NOTEBOOK( widget ), childWidget ) ) {
0749 
0750                     // always disable on tabbar label
0751                     status = WidgetIsTabLabel;
0752 
0753                 } else {
0754 
0755                     // recursive check
0756                     status = childrenUseEvent( childWidget, event, inNoteBook );
0757 
0758                 }
0759 
0760 
0761                 #if OXYGEN_DEBUG
0762                 std::cerr << "Oxygen::WindowManager::childrenUseEvent -"
0763                     << " event: " << event
0764                     << " widget: " << childWidget
0765                     << " (" << G_OBJECT_TYPE_NAME( childWidget ) << ")"
0766                     << " " << gtk_widget_get_name( childWidget )
0767                     << " status: " << dragStatusString(status)
0768                     << std::endl;
0769                 #endif
0770 
0771                 break;
0772 
0773             }
0774 
0775         }
0776 
0777         // free list
0778         if( children ) g_list_free( children );
0779         return status;
0780 
0781     }
0782 
0783     //_________________________________________________
0784     bool WindowManager::widgetIsBlackListed( GtkWidget* widget ) const
0785     {
0786         BlackList::const_iterator iter( std::find_if( _blackList.begin(), _blackList.end(), BlackListFTor( G_OBJECT( widget ) ) ) );
0787         #if OXYGEN_DEBUG
0788         if( iter == _blackList.end() ) return false;
0789         else {
0790 
0791             std::cerr << "Oxygen::WindowManager::widgetIsBlackListed - widget: " << widget << " type: " << *iter << std::endl;
0792             return true;
0793 
0794         }
0795         #else
0796         return iter != _blackList.end();
0797         #endif
0798     }
0799 
0800     //_________________________________________________
0801     bool WindowManager::widgetHasBlackListedParent( GtkWidget* widget ) const
0802     {
0803 
0804         #if OXYGEN_DEBUG
0805         std::cerr << "Oxygen::WindowManager::widgetHasBlackListedParent - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl;
0806         #endif
0807 
0808         // loop over widget parent
0809         for( GtkWidget* parent = gtk_widget_get_parent( widget ); parent; parent = gtk_widget_get_parent( parent ) )
0810         { if( _blackListWidgets.find( parent ) != _blackListWidgets.end() ) return true; }
0811 
0812         return false;
0813     }
0814 
0815     //_________________________________________________
0816     void WindowManager::initializeBlackList( void )
0817     {
0818         // clear list
0819         _blackList.clear();
0820         _blackList.push_back( "GtkScale" );
0821         _blackList.push_back( "GimpColorBar" );
0822         _blackList.push_back( "GladeDesignLayout" );
0823         _blackList.push_back( "GooCanvas" );
0824         _blackList.push_back( "GtkPizza" );
0825         _blackList.push_back( "MetaFrames" );
0826         _blackList.push_back( "SPHRuler" );
0827         _blackList.push_back( "SPVRuler" );
0828         _blackList.push_back( "GtkPlug" );
0829     }
0830 
0831     //________________________________________________________________________________
0832     void WindowManager::connect( GtkWidget* widget, WindowManager::Data& data )
0833     {
0834         data._destroyId.connect( G_OBJECT( widget ), "destroy", G_CALLBACK( wmDestroy ), this );
0835         data._pressId.connect( G_OBJECT( widget ), "button-press-event", G_CALLBACK( wmButtonPress ), this );
0836         data._leaveId.connect( G_OBJECT( widget ), "leave-notify-event", G_CALLBACK( wmLeave ), this );
0837         data._motionId.connect( G_OBJECT( widget ), "motion-notify-event", G_CALLBACK( wmMotion ), this );
0838     }
0839 
0840     //_________________________________________________
0841     void WindowManager::Data::disconnect( GtkWidget* )
0842     {
0843 
0844         _leaveId.disconnect();
0845         _destroyId.disconnect();
0846         _pressId.disconnect();
0847         _motionId.disconnect();
0848 
0849     }
0850 
0851 }