Rubber: Das bessere Gummiband

Die Rubberbox hat sich längst im GEM-Alltag etabliert. Doch keine Routine ist so gut, daß man keine Verbesserungen anbringen könnte. Und so entstand das C-Modul RUBBER, mit dem das Gummiband noch vielseitiger wird.

Die im Listing 1 stehende Routine DoRubberbox ersetzt I nicht die normale Funktion graf_rubberbox auf Betriebssystemebene; vielmehr muß sie in eigene Programme eingebunden und über einen normalen Funktionsaufruf ausgeführt werden.

Ein Wunschzettel

Doch welche zusätzlichen Features hat die Routine? Das Wichtigste ist, daß man statt einer simplen Breiten- und Höhenbegrenzung der Rubberbox ein Rechteck zur Begrenzung übergeben kann, also z.B. den Arbeitsbereich eines Fensters. Indem man während der Funktion eine SHIFT-Taste drückt, wird aus dem Rechteck ein Quadrat, was bei einigen Anwendungen durchaus sinnvoll sein kann.

Die letzte Neuerung ist die, daß eine eigene Funktion angesprungen werden kann, während man das Gummiband aufzieht, so daß gleichzeitig Icons selektiert werden können.

Das Rechteck ...

..., mit dem man das Gummiband einschränken kann, wird der Funktion als GRECT-Struktur übergeben; da diese Struktur noch aus x/y-Koordinaten sowie Breite und Höhe besteht, werden diese Werte in zwei Koordinatenpaare umgewandelt, mit denen man besser arbeiten kann.

Aus diesem Begrenzungsrechteck resultieren dann auch die errechneten Werte max_1 und max_2, die für den Fall gebraucht werden, daß Sie SHIFT drücken. Der Parameter max_1 gibt dann nämlich die maximale Auslenkung in negativer Richtung, max_2 die in positiver Richtung an.

Funktion oder nicht...

Während man das Gummiband aufzieht, ist es möglich, eine eigene Funktion anspringen zu lassen, damit diese dann z.B. Icons oder Fenstereinträge, die „unter“ dem Gummiband liegen, selektieren kann. Möglich wird das durch einen Funktions-Pointer, der DoRubberbox übergeben wird. Die Funktion, auf die der Pointer zeigt, muß folgende Bedingungen erfüllen: Sie darf keinen Rückgabewert und muß einen Zeiger auf ein WORD als Eingangsparameter haben. Sie könnte also heißen:

VOID CallFunc( WORD *pxy )

Die Variable pxy zeigt dabei auf ein Feld, das die Koordinaten der aktuellen Rubberbox enthält. Wichtig ist, daß es sich nicht um GRECT-\Nerte, sondern um zwei Koordinatenpaare handelt.

Die Möglichkeit, eine eigene Funktion aufrufen zu lassen, wird natürlich nicht immer benötigt; wollen Sie, daß keine Funktion angesprungen wird, übergeben Sie an dieser Stelle einfach 0L oder NULL.

Der Aufruf

Um das Gummiband auf den Bildschirm zu bringen, müssen Sie der Funktion DoRubberbox folgende Parameter mit auf den Weg geben:

  1. v_handle: Das eigene VDI-Handle, das für die Ausgabe des Gummibandes benötigt wird.

  2. start_x, start_y: Die auslösenden Mauskoordinaten, also die Koordinaten des Punktes, der bei der Rubberbox unbeweglich ist.

  3. rect: Dieser Parameter enthält das oben besprochene Begrenzungsrechteck.

  4. res_x, res_y: Nach Beendigung der Funktion enthalten diese zwei WORDS die Breite und Höhe des Gummibandes.

  5. call_func: der Funktions-Pointer

Wenn Sie der Funktion also alle diese Parameter übergeben haben, wird das Gummiband solange dargestellt und verändert, wie Sie die linke Maustaste gedrückt halten. Die einzelnen Parameter und ihre Bedeutung sind in Bild 1 zu sehen.

Wie funktioniert’s?

Das Wichtigste an der Funktion ist ein evnt_multi, der auf eine Mausbewegung und das Loslassen der linken Maustaste wartet. Bei einer Mausbewegung werden aus der neuen Mausposition die Koordinaten des Gummibandes errechnet. Danach wird geprüft, ob die Rubberbox außerhalb des Begrenzungsrechteckes liegen würde, oder ob die SHIFT-Tasten gedrückt sind, so daß das Rechteck angepaßt werden müßte. Danach wird das Gummiband durch die Funktion DrawBox gemalt; da das Rechteck mit dem Schreibmodus MD_XOR auf den Bildschirm gebracht wird, verschwindet das Gummiband wieder, wenn man DrawBox mit den gleichen Koordinaten aufruft, was auch direkt hinter dem evnt_multi geschieht.

Nachdem die Box zum ersten Mal gemalt wurde, wird die durch den Funktions-Pointer angegebene Routine aufgerufen, wenn dieser Zeiger nicht NULL ist.

Die Bedeutung der einzelnen Parameter von DoRubberbox

/* RUBBER.H */

#ifndef __NEWRUBBER
#define __NEWRUBBER

# include <portab.h>

/* Verwaltet die Rubberbox */
WORD DoRubberbox( WORD v_handle,
                  WORD start_x, WORD start_y, 
                  GRECT *rect,
                  WORD *res_width, WORD *res_height,

VOID (*call_func)(WORD * new_rect) );

VOID DrawBox( WORD v_handle, WORD xl, WORD yl, WORD x2, WORD y2 );
/********************************************/ 
/*   MODUL : RUBBER.C                       */
/* AUFGABE : Verbesserte Rubberbox-Routine  */
/*   AUTOR : Markus Hövener                 */
/*           (C) MAXON Computer GmbH        */
/********************************************/

# include <aes.h>
# include <portab.h>
# include <vdi.h>

# include "rubber.h"

#define FALSE   0
#define TRUE    !FALSE

#define Min(a,b) ((a < b) ? a : b )
#define Max(a,b) ((a > b) ? a : b )

/********************************************/
/* Verwaltet die Rubberbox                  */
/********************************************/
WORD DoRubberbox( WORD  v_handle,
                  WORD  start_x, WORD start_y, 
                  GRECT *rect,
                  WORD  *res_width,
                  WORD  *res_height,
                  VOID  (*call_func)
                        (WORD *new_rect) )
{
WORD    events,         /* Eventtyp */
        m_x, m_y,       /* Mausposition */
        rect_xy[4],     /* Umrandung */
        pxy[4],         /* Die Box */
        spec_state,     /* Sondertasten */
        max_1, max_2,   /* Für SHIFT... */
        dif,            /* dito */
        _void;          /* Unwichtiges */

/* 'rect' umrechnen */ 
rect_xy[0] = rect->g_x; 
rect_xy[1] = rect->g_y;
rect_xy[2] = rect->g_x + rect->g_w - 1; 
rect_xy[3] = rect->g_y + rect->g_h - 1;

/* Maus nicht im Rechteck ?? */ 
if( (start_x < rect xy[0]) || (start_x > rect_xy[2]) ||
    (start_y < rect_xy[1]) || (start y > rect_xy[3]) ) 
        return( FALSE );

pxy[0] = start_x;
pxy[1] = start, y;

/* Werte für SHIFT-Benutzung */ 
max_1 = Min( start_x - rect_xy[0], start_y - rect_xy[1] );
max_2 = Min( rect_xy[2] - start_x, rect_xy[3] - start_y );

/* Grafikmodi setzen */ 
vswr_mode( v_handle, MD_XOR ); 
vsl_type( v_handle, 7 ); 
vsl_color( v_handle, 1 ); 
vsl_width( v_handle, 1 );

/* Maus als Zeiger */
graf_mouse( POINT_HAND, 0L );

wind_update( BEG_UPDATE ); 
wind_update( BEG_MCTRL );

/* Mauskoordinaten *./
graf_mkstate( &m_x, &m_y, &spec_state, &_void );

do
{
        /* Neue Koordinaten */
        pxy[2] = Max( m_x, rect_xy[0] );
        pxy[2] = Min( pxy[2], rect_xy[2] );

        pxy[3] = Max( m_y, rect_xy[1] ); 
        pxy[3] = Min( pxy[3], rect_xy[3] );

        /*******************/
        /* SHIFT gedrückt  */
        /*******************/
        if( spec_state & (K_LSHIFT | K_RSHIFT) )
        {
            /* Abstand errechnen */ 
            dif = pxy[2] - pxy[0];

            /* Einschränken */
            dif = (dif >0)? Min(dif, max_2) : Max (dif, -max_1);

            /* Neue Koordinaten */ 
            pxy[2] = pxy[0] + dif; 
            pxy[3] = pxy[1] + dif;
        }

        /* Rahmen zeichnen lassen */
        DrawBox( v_handle, pxy[0], pxy[1], pxy[2], pxy[3] );

        /* Eigene Punktion aufrufen */ 
        if( call_func )
            call_func( pxy );


        /*************/
        /* Event     */
        /*************/ 
        events = evnt_multi(
        /* Maus- und Rechteckevents */
                            MU_BUTTON|MU_M1,
                            /* Mausklick *7
                            1, 1, 0,
                            /* Das Rechteck */
                            1, m_x, m_y, 1, 1,
                            0, 0, 0, 0, 0,
                            /* Message-Buffer */ 
                            0L,
                            /* Kein Timer */
                            0, 0,
                            /* Endparameter */ 
                            &m_x, &m_y, &_void, 
                            &spec_state, &_void, &_void );

        /* Wieder drüber malen */
        DrawBox( v_handle, pxy[0], pxy[1], pxy[2], pxy[3] );
}
while( !(events & MU_BUTTON ) );

wind_update( END_MCTRL ); 
wind_update( END_UPDATE );

graf_mouse( ARROW, 0L );

/* Ergebnis eintragen */
*res_width  = pxy[2] - pxy[0];
*res_height = pxy[3] - pxy[1];

return( TRUE );
}


/*********************************************/ 
/* Malt die Rubberbox                        */
/*********************************************/

VOID DrawBox( WORD v_handle, WORD x1, WORD y1, WORD x2, WORD y2 )
{
    WORD xy[4];

    graf_mouse ( M_OFF, 0L ) ;

    xy[0] = xy[2] = x1; 
    xy[1] = y1; 
    xy[3] = y2;
    vsl_udsty( v_handle, (xy[0] % 2) == (xy[1] % 2) ? 21845 : (WORD)43690L ); 
    v_pline ( v_handle, 2, xy );


    xy[0] = xy[2] = x2;
    vsl_udsty( v_handle, (xy[0] % 2) == (xy[1] % 2) ? 21845 : (WORD)43690L ); 
    v_pline ( v_handle, 2, xy );


    xy[0] = x1;
    xy[2] = x2;
    xy[l] = xy[3] = y1;
    vsl_udsty( v_handle, (xy[1] % 2) ? (WORD)43690L : 21845 ); 
    v_pline ( v_handle, 2, xy );

    xy[1] = xy[3] = y2;
    vsl_udsty( v_handle, (xy[1] % 2) ? (WORD)43690L : 21845 ); 
    v_pline ( v_handle, 2, xy );

    graf_mouse( M_ON, 0L );

Anm: Das Listing wurde im Heft nicht vollständig abgedruckt!


Markus Hövener
Aus: ST-Computer 12 / 1992, Seite 94

Links

Copyright-Bestimmungen: siehe Über diese Seite