marqueeTool.cpp

//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

//
// Marquee selection within a user defined context.
// Draws the marquee using OpenGL.
// Selection is done through the API (MGlobal).
//

#include <maya/MIOStream.h>
#include <math.h>
#include <stdlib.h>

#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MDagPath.h>
 
#include <maya/MItSelectionList.h>
#include <maya/MSelectionList.h>

#include <maya/MPxContextCommand.h>
#include <maya/MPxContext.h>
#include <maya/MEvent.h>

#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif

// Custom XOR Draw Class

//  Set this to 1 if you want to use the 'xorDraw' class for
//  customized XOR drawing rather than M3dView's methods.
#define CUSTOM_XOR_DRAW 0

#if CUSTOM_XOR_DRAW
// Example class code which basically does the same operatations
// as M3dView's beingXorDrawing() and endXorDrawing() methods.
// This class could be used in place these methods if desired.
//
class xorDraw
{
public:
    xorDraw(M3dView *view) { fView = view; };
    ~xorDraw() {};

    void beginXorDrawing();
    void endXorDrawing();

protected:
    xorDraw() { fView = NULL; }

    // Setup for XOR drawing
    GLboolean depthTest[1];
    GLboolean colorLogicOp[1];
    GLboolean lineStipple[1];

    M3dView *fView;


};

void xorDraw::beginXorDrawing()
{
    // Save the state of these 3 attribtes and restore them later.
    glGetBooleanv (GL_DEPTH_TEST, depthTest);
    glGetBooleanv (GL_COLOR_LOGIC_OP, colorLogicOp);
    glGetBooleanv (GL_LINE_STIPPLE, lineStipple);

    glDrawBuffer( GL_FRONT );

    // Turn Line stippling on.
    glLineStipple( 1, 0x5555 );
    glLineWidth( 1.0 );
    glEnable( GL_LINE_STIPPLE );

    // Save the state of the matrix on stack
    glMatrixMode (GL_MODELVIEW);
    glPushMatrix();

    // Setup the Orthographic projection Matrix.
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluOrtho2D(
                0.0, (GLdouble) fView->portWidth(),
                0.0, (GLdouble) fView->portHeight()
    );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    glTranslatef(0.375, 0.375, 0.0);

    // Set the draw color
    glColor3f(1.0f, 1.0f, 1.0f);

    // Draw the marquee in XOR mode
    
    glDisable (GL_DEPTH_TEST);

    // Enable XOR mode.
    glEnable(GL_COLOR_LOGIC_OP);
    glLogicOp (GL_XOR);
}

void xorDraw::endXorDrawing()
{
    glFlush();
    glDrawBuffer( GL_BACK );

    // Restore the state of the matrix from stack
    glMatrixMode( GL_MODELVIEW );
    glPopMatrix();

    // Restore the previous state of these attributes
    if (colorLogicOp[0])
        glEnable (GL_COLOR_LOGIC_OP);
    else
        glDisable (GL_COLOR_LOGIC_OP);

    if (depthTest[0])
        glEnable (GL_DEPTH_TEST);
    else
        glDisable (GL_DEPTH_TEST);

    if (lineStipple[0])
        glEnable( GL_LINE_STIPPLE );
    else
        glDisable( GL_LINE_STIPPLE );
}
#endif


// The user Context
const char helpString[] =
            "Click with left button or drag with middle button to select";

class marqueeContext : public MPxContext
{
public:
                    marqueeContext();
    virtual void    toolOnSetup( MEvent & event );
    virtual MStatus doPress( MEvent & event );
    virtual MStatus doDrag( MEvent & event );
    virtual MStatus doRelease( MEvent & event );
    virtual MStatus doEnterRegion( MEvent & event );

private:
    void                    drawMarquee();

    short                   start_x, start_y;
    short                   last_x, last_y;

    bool                    fsDrawn;

    MGlobal::ListAdjustment listAdjustment;
    M3dView                 view;
};

marqueeContext::marqueeContext()
{
    setTitleString ( "Marquee Tool" );

    // Tell the context which XPM to use so the tool can properly
    // be a candidate for the 6th position on the toolbar.
    setImage("marqueeTool.xpm", MPxContext::kImage1 );
}

void marqueeContext::toolOnSetup ( MEvent & )
{
    setHelpString( helpString );
}

MStatus marqueeContext::doPress( MEvent & event )
//
// Begin marquee drawing (using OpenGL)
// Get the start position of the marquee 
//
{
    // Figure out which modifier keys were pressed, and set up the
    // listAdjustment parameter to reflect what to do with the selected points.
    if (event.isModifierShift() || event.isModifierControl() ) {
        if ( event.isModifierShift() ) {
            if ( event.isModifierControl() ) {
                // both shift and control pressed, merge new selections
                listAdjustment = MGlobal::kAddToList;
            } else {
                // shift only, xor new selections with previous ones
                listAdjustment = MGlobal::kXORWithList;
            }
        } else if ( event.isModifierControl() ) {
            // control only, remove new selections from the previous list
            listAdjustment = MGlobal::kRemoveFromList; 
        }
    } else {
        listAdjustment = MGlobal::kReplaceList;
    }

    // Extract the event information
    //
    event.getPosition( start_x, start_y );

    view = M3dView::active3dView();
    fsDrawn = false;

    return MS::kSuccess;        
}

MStatus marqueeContext::doDrag( MEvent & event )
//
// Drag out the marquee (using OpenGL)
//
{
#if CUSTOM_XOR_DRAW
    xorDraw XORdraw(&view);
    XORdraw.beginXorDrawing();
#else
    view.beginXorDrawing();
#endif

    if (fsDrawn) {
        // Redraw the marquee at its old position to erase it.
        drawMarquee();
    }

    fsDrawn = true;

    //  Get the marquee's new end position.
    event.getPosition( last_x, last_y );

    // Draw the marquee at its new position.
    drawMarquee();

#if CUSTOM_XOR_DRAW
    XORdraw.endXorDrawing();
#else
    view.endXorDrawing();
#endif

    return MS::kSuccess;        
}

MStatus marqueeContext::doRelease( MEvent & event )
//
// Selects objects within the marquee box.
{
    MSelectionList          incomingList, marqueeList;

    if (fsDrawn) {
#if CUSTOM_XOR_DRAW
        xorDraw XORdraw(&view);
        XORdraw.beginXorDrawing();
#else
        view.beginXorDrawing();
#endif
        // Redraw the marquee at its old position to erase it.
        drawMarquee();

#if CUSTOM_XOR_DRAW
        XORdraw.endXorDrawing();
#else
        view.endXorDrawing();
#endif
    }

    // Get the end position of the marquee
    event.getPosition( last_x, last_y );

    // Save the state of the current selections.  The "selectFromSceen"
    // below will alter the active list, and we have to be able to put
    // it back.
    MGlobal::getActiveSelectionList(incomingList);

    // If we have a zero dimension box, just do a point pick
    //
    if ( abs(start_x - last_x) < 2 && abs(start_y - last_y) < 2 ) {
        // This will check to see if the active view is in wireframe or not.
        MGlobal::SelectionMethod selectionMethod = MGlobal::selectionMethod();

        MGlobal::selectFromScreen( start_x, start_y, MGlobal::kReplaceList, selectionMethod );
    } else {
        // The Maya select tool goes to wireframe select when doing a marquee, so
        // we will copy that behaviour.
        // Select all the objects or components within the marquee.
        MGlobal::selectFromScreen( start_x, start_y, last_x, last_y,
                                   MGlobal::kReplaceList, 
                                   MGlobal::kWireframeSelectMethod );
    }

    // Get the list of selected items
    MGlobal::getActiveSelectionList(marqueeList);

    // Restore the active selection list to what it was before
    // the "selectFromScreen"
    MGlobal::setActiveSelectionList(incomingList, MGlobal::kReplaceList);

    // Update the selection list as indicated by the modifier keys.
    MGlobal::selectCommand(marqueeList, listAdjustment);
    return MS::kSuccess;        
}

MStatus marqueeContext::doEnterRegion( MEvent & )
{
    return setHelpString( helpString );
}

void marqueeContext::drawMarquee()
{
    glBegin( GL_LINE_LOOP );
        glVertex2i( start_x, start_y );
        glVertex2i( last_x, start_y );
        glVertex2i( last_x, last_y );
        glVertex2i( start_x, last_y );
    glEnd();
}


// Command to create contexts

class marqueeContextCmd : public MPxContextCommand
{
public: 
                        marqueeContextCmd();
    virtual MPxContext* makeObj();
    static  void*       creator();
};

marqueeContextCmd::marqueeContextCmd() {}

MPxContext* marqueeContextCmd::makeObj()
{
    return new marqueeContext();
}

void* marqueeContextCmd::creator()
{
    return new marqueeContextCmd;
}

// plugin initialization
MStatus initializePlugin( MObject obj )
{
    MStatus     status;
    MFnPlugin   plugin( obj, PLUGIN_COMPANY, "12.0", "Any");

    status = plugin.registerContextCommand( "marqueeToolContext",
                                            marqueeContextCmd::creator );
    return status;
}

MStatus uninitializePlugin( MObject obj )
{
    MStatus     status;
    MFnPlugin   plugin( obj );

    status = plugin.deregisterContextCommand( "marqueeToolContext" );

    return status;
}