Submenüs unter GEM - Der Nachschlag

In der letzten Ausgabe der ST Computer stellten wir Ihnen eine Möglichkeit vor, mit "offiziellen” GEM-Mitteln Submenüs auf dem ATARI ST zu programmieren. Bis jetzt konnte man so etwas nur auf dem Apple Macintosh, dem AMIGA und ähnlichen Rechnern bewundern. Durch einen massiven Einsatz des berüchtigten Fehlerteufels, haben sich aber die beiden dazugehörigen Listings selbstständig gemacht, wodurch wir uns gezwungen sehen, noch einen Nachschlag zum Thema Submenüs zu geben. Hier sind sie nun, die beiden verschollenen Listings SUBMENU.C und MENUDEMO.C.

/************************************************/ 
/*                 Submenu V1.15                */
/*     - Einsatz von Submenus unter GEM -       */
/*     - lauffähig unter allen TOS-Versionen    */
/*       und in allen Auflösungen -             */
/*       (in dieser Fassung für Megamax-C)      */
/*       by Uwe Hax (c) MAXON Computer GmbH     */
/************************************************/ 

#include <gemdefs.h>
#include <obdefs.h>
#include <osbind.h>
#include <portab.h>

WORD subnum; /* Rückgabewert: Index des Submenüeintrags */

/* Alle folgenden globalen Variablen werden nur in den 
   folgenden Routinen benutzt und dürfen auf keinen Fall 
   von anderen Routinen verändert werden! */

struct ext_appl_blk { /* erweiterten APPLBLK definieren */
                        WORD (*ub_code)();
                      /* Zeiger auf Zeichenroutine */
                        LONG ub_parm;
                      /* Index der Submenübox */ 
                        LONG regA4;
                      /* Register A4 (Megamax-C) */ 
                        char *text; /* Menütext */
                    } *block;

WORD *menu_buffer;   /* Hintergrundspeicher für Submenüs */
WORD copy_array[8];  /* Hintergrundkoordinaten */
WORD fill_array[8];  /* Boxkoordinaten */
OBJECT *sub_menu;    /* Adresse der Submenüs */ 
FDB memory;          /* zum Kopieren */
FDB screen;          /* zum Kopieren */
WORD device;         /* VDI-Handle */
WORD pxyarray[4];    /* zum Zeichnen */
WORD mbutton,mx,my;  /* Maus */
WORD sm_index;       /* Submenüindex */

init_submenus(handle,menu,submenu,count,menu_index)
WORD handle;            /* VDI-Handle */
OBJECT *menu,*submenu;  /* Adressen der beiden Menüzeilen */
WORD count;             /* Anzahl der Elemente in der Liste */
WORD *menu_index;       /* Liste mit Indizes aller Menüeinträge, 
                           die Titel f.Submenüs sein sollen */
{
   WORD i;
   WORD draw_submenu();
   WORD box2;
   WORD *size;
   WORD max_size=0;
   WORD submenu_index;
   WORD menubar;

   /* Höhe der Menüleiste ist abhängig von der Auflösung */ 
   menubar=(Getrez()==2) ? 19 : 11;

   /* Speicher reservieren für alle APPLBLKs */ 
   size=(WORD *)Malloc((LONG)count*2L); 
   block=(struct ext_appl_blk *)Malloc((LONG)(sizeof(struct ext_appl_blk)*count));

   /* Index d. G_BOX f.d. erste Submenü holen */ 
   submenu_index=submenu[submenu[submenu[0].ob_tail].ob_head].ob_next;

   for (i=0; i<count; i++)
   {
      /* Text retten */
      block[i].text=menu[menu_index[i]].ob_spec;

      /* eigene Zeichenroutine installieren */ 
      menu[menu_index[i]].ob_type=G_PROGDEF;

      /* Zeiger auf zugehörigen APPLBLK setzen */ 
      menu[menu_index[i]].ob_spec=(char * )&block[i];

      /* Adresse der neuen Zeichenroutine eintragen */ 
      block[i].ub_code=draw_submenu;

      /* Parameterübergabe: Index der G_BOX des zugehörigen Submenüs */ 
      block[i].ub_parm=(LONG)submenu_index;

      /* bei Eintritt in die Zeichenroutine sind alle Register verändert;
         Register A4 wird bei Megamax-C aber zur Adressierung der globalen 
         Variablen benötigt */ 
      asm
      {
         move.l   block(A4),A0
         clr.l    D0
         move     i(A6),D0 
         muls     #16,D0 
         adda.l   D0,A0
         move.l   A4,8(AO)    /* A4 —> block[i].regA4 */
      }

      /* Index der übergeordneten G_BOX für das Menü suchen */ 
      box2=menu_index[i]; 
      while (menu[box2].ob_type!=G_BOX) 
         box2=menu[box2].ob_next;

      /* Koordinaten des Submenüs errechnen */ 
      submenu[submenu_index].ob_x=menu[box2].ob_x+menu[menu_index[i]].ob_width+1; 
      submenu[submenu_index].ob_y=menubar+menu[menu_index[i]].ob_y;

      /* Größe des benötigten Hintergrundspeichers merken */
      size[i]=(submenu[submenu_index].ob_width+16)/8*submenu[submenu_index].ob_height; 
      /* nächstes Submenü */
      submenu_index=submenu[submenu_index].ob_next;
   }

   /* Speicher für das größte Menü belegen */ 
   for (i=0; i<count; i++) 
      if (max_size<size[i]) 
         max_size=size[i]; 
   menu_buffer=(WORD *)Malloc((LONG)max_size);

   /* globale Variablen setzen */ 
   sub_menu=submenu; 
   device=handle;
}

WORD draw_submenu(parmblock)
PARMBLK *parmblock;
{
   WORD index; 
   char *sm_text;
   struct ext_appl_blk *appl_pointer;
   WORD tx,ty;
   WORD attrib[5];
   WORD state;
   WORD resolution;
   WORD correct;

   /* Register retten */ 
   asm
   {
      movem.l D0-A6,-(A7)
   }

   /* Zeiger auf den angesprochenen APPLBLK */ 
   appl_pointer=(struct ext_appl_blk *)parmblock->pb_tree[parmblock->pb_obj].ob_spec;
   /* Text des Menüeintrags */ 
   sm_text=appl_pointer->text;

   /* Register A4 für die Adressierung der 
      globalen Variablen bei Megamax-C holen */
   asm
   {
      move.l appl_pointer(A6),A0 
      move.l 8(A0),A4
   }

   /* aktuelle Füllattribute merken */ 
   vqf_attributes(device,attrib);

   /* Index der Submenübox */ 
   sm_index=parmblock->pb_parm;

   /* kein Submenü angewählt */ 
   subnum=0;

   /* Korrekturfaktor für die Ausgabe von Texten 
      (abhängig von der Auflösung) */ 
   resolution=Getrez(); 
   correct=(resolution==2) ? 3 : 2;

   /* Auf keinen Fall Clipping durchführen; 
      Clip-Koordinaten sind 0!!! */

   /* Ist der Eintrag NORMAL? */
   if (!parmblock->pb_currstate)    /* ja */
   {
      /* war der Eintrag vorher SELECTED? */ 
      if (parmblock->pb_prevstate)  /* ja */
      {
         /* Maus im Submenü? */
         vq_mouse(device,&mbutton,&mx,&my);

         /* wenn ja, Submenükontrolle */ 
         if ((mx>=sub_menu[sm_index].ob_x-1) && 
            (mx<=sub_menu[sm_index].ob_x+sub_menu[sm_index].ob_width) && 
            (my>=sub_menu[sm_index].ob_y) && (my<=sub_menu[sm_index].ob_y+ sub_menu[sm_index].ob_height+1)) 
            do_submenu();

         /* Hintergrund restaurieren */ 
         redraw_bg();

         /* Menüeintrag neu zeichnen */ 
         pxyarray[0]=parmblock->pb_x; 
         pxyarray[1]=parmblock->pb_y; 
         pxyarray[2]=parmblock->pb_x+parmblock->pb_w-1;
         pxyarray[3]=parmblock->pb_y+parmblock->pb_h-1; 
         switch_entry(FALSE);
      }
      else
      {
         /* Text ausgeben */
         v_gtext(device,parmblock->pb_x,parmblock->pb_y+parmblock->pb_h-correct, sm_text);
      }
      state=NORMAL;
   }
   else     /* SELECTED */
   {
      /* Menüeintrag selektieren */ 
      pxyarray[0]=parmblock->pb_x; 
      pxyarray[1]=parmblock->pb_y; 
      pxyarray[2]=parmblock->pb_x+parmblock->pb_w-1;
      pxyarray[3]=parmblock->pb_y+parmblock->pb_h-1; 
      switch_entry(FALSE);

      /* Submenü zeichnen */
      /* ================ */
      /* Hintergrund retten */ 
      screen.fd_addr=0L;
      memory.fd_addr=(LONG)menu_buffer; 
      memory.fd_wdwidth=sub_menu[sm_index].ob_width/16+1;
      memory.fd_stand=0; 
      memory.fd_nplanes=resolution ? 2/resolution : 4;
      copy_array[0]=sub_menu[sm_index].ob_x; 
      copy_array[1]=sub_menu[sm_index].ob_y-1; 
      copy_array[2]=sub_menu[sm_index].ob_x+sub_menu[sm_index].ob_width+1; 
      copy_array[3]=sub_menu[sm_index].ob_y+sub_menu[sm_index].ob_height; 
      copy_array[4]=0;
      copy_array[5]=0;
      copy_array[6]=sub_menu[sm_index].ob_width+1; 
      copy_array[7]=sub_menu[sm_index].ob_height+1; 
      vro_cpyfm(device,3,copy_array,(screen, (memory);

      /* Box zeichnen */
      fill_array[0]=copy_array[0];
      fill_array[1]=copy_array[1];
      fill_array[2]=copy_array[2];
      fill_array[3]=copy_array[1];
      fill_array[4]=copy_array[2];
      fill_array[5]=copy_array[3];
      fill_array[6]=copy_array[0];
      fill_array[7]=copy_array[3]; 
      vsf_interior(device,0);
      vswr_mode(device,1);
      v_fillarea(device,4,fill_array);

      /* Texte ausgeben */
      index=sub_menu[sm_index].ob_head;
      while (sub_menu[index].ob_type==G_STRING)
      {
         if (sub_menu[index].ob_state & DISABLED) 
            vst_effects(device,2); 
         v_gtext(device,tx=sub_menu[sm_index].ob_x+sub_menu[index].ob_x+1,
                        ty=sub_menu[sm_index].ob_y+sub_menu[index].ob_y+ 
                        sub_menu[index].ob_height-correct,sub_menu[index].ob_spec);
         if (sub_menu[index].ob_state & CHECKED) 
            v_gtext(device,tx+2,ty,"\10"); 
         vst_effects(device,0); 
         index=sub_menu[index].ob_next;
      }
      state=SELECTED;
   }

   /* alte Füllattribute wieder hersteilen */ 
   vsf_interior(device,attrib[0]); 
   vsf_color(device,attrib[1]); 
   vsf_style(device,attrib[2]); 
   vswr_mode(device,attrib[3]); 
   vsf_perimeter(device,attrib[4]);

   /* Register zurückholen */ 
   asm 
   {
      movem.l (A7)+,D0-A6
   }

   /* Zustand des angewählten Menüeintrags zurückgeben */ 
   return (state);
}

redraw_bg()
{
   v_hide_c(device); 

   copy_array[0]=0;
   copy_array[1]=0;
   copy_array[2]=sub_menu[sm_index].ob_width+1; 
   copy_array[3]=sub_menu[sm_index].ob_height+1; 
   copy_array[4]=sub_menu[sm_index].ob_x; 
   copy_array[5]=sub_menu[sm_index].ob_y-1; 
   copy_array[6]=sub_menu[sm_index].ob_x+sub_menu[sm_index].ob_width+1; 
     copy_array[7]=sub_menu[sm_index].ob_y+sub_menu[sm_index].ob_height;

   /* alle and. Parameter sind bereits gesetzt */ 
   vro_cpyfm(device,3,copy_array,&memory,&screen);

   v_show_c(device,1);
}

switch_entry(disabled)
WORD disabled;
{
   /* je nach übergebenem Parameter Menüeintrag 
      entweder normal oder selektiert zeichnen */ 
   if (!disabled)
   {
      vsf_interior(device,1); 
      vswr_mode(device,3); 
      vr_recfl(device,pxyarray);
   }
}

do_submenu()
{
   WORD index;
   WORD prev_obj=0;

   /* Cursor einschalten */ 
   v_show_c(device,1);

   /* Mauszeiger innerhalb des Submenüs? */ 
   while ((mx>=sub_menu[sm_index].ob_x-1) && 
          (mx<=sub_menu[sm_index].ob_x+sub_menu[sm_index].ob_width) && 
          (my>=sub_menu[sm_index].ob_y) &&
          (my<=sub_menu[sm_index].ob_y+sub_menu[sm_index].ob_height+1))
   {
      /* Eintrag unter Mauszeiger suchen */ 
      index=sub_menu[sm_index].ob_head; 
      while (sub_menu[index].ob_type==G_STRING) 
         if ((my>sub_menu[sm_index].ob_y+sub_menu[index].ob_y) &&
             (my<sub_menu[sm_index].ob_y+sub_menu[index].ob_y+
             sub_menu[index].ob_height)) 
             break;
         else
            index=sub_menu[index].ob_next;

      /* gefunden? */
      if (index!=sm_index) /* ja */
      {
         /* Eintrag ungleich letztem angewählten Eintrag? */ 
         if (index!=prev_obj) /* ja */
         {
            /* zum Zeichnen Mauszeiger ausschalten */ 
            v_hide_c(device);

            /* vorigen Eintrag normalisieren */ 
            if (prev_obj && !(sub_menu[prev_obj].ob_state & DISABLED)) 
               switch_entry(FALSE);
            /* neuen Eintrag selektieren, falls möglich */ 
            pxyarray[0]=sub_menu[sm_index].ob_x+1; 
            pxyarray[1]=sub_menu[sm_index].ob_y+sub_menu[index].ob_y; 
            pxyarray[2]=pxyarray[0]+sub_menu[index].ob_width-1; 
            pxyarray[3]=pxyarray[1]+sub_menu[index].ob_height-1; 
            switch_entry(sub_menu[index].ob_state & DISABLED);

            v_show_c(device,1); 
            prev_obj=index;
         }

         /* Maustaste gedrückt? */ 
         if (mbutton)   /* ja */
         {
            /* Menüeintrag anwählbar? */ 
            if (!(sub_menu[index].ob_state & DISABLED)) /* ja */
                  subnum=index; /* Index des Eintrags zurückgeben */
            break;
         }
      }
      /* aktuelle Mauskoordinaten holen */ 
      vq_mouse(device,&mbutton,&mx,&my);
   }
   /* Mauszeiger wieder ausschalten */ 
   v_hide_c(device);
}

SUBMENU.C

/***********************************************/ 
/*             Submenü-Demo V1.14              */
/*    - Einsatz von Submenüs unter GEM -       */
/*    - lauffähig unter allen TOS-Versionen    */
/*      und in allen Auflösungen -             */
/*      (in dieser Fassung für Megamax-C)      */
/*      by Uwe Hax (c) MAXON Computer GmbH     */
/***********************************************/

#include <gemdefs.h>
#include <obdefs.h>
#include <osbind.h>
#include <portab.h>

WORD contrl[12];
WORD intin[128];
WORD ptsin[128];
WORD intout[128];
WORD ptsout[128];
WORD int_in[11];
WORD int_out[57];

WORD handle;
WORD dummy;
EXTERN WORD global[];

/**********************************************/ 
#define MAX_SUBMENU  4  /* 4 Submenüs */

#include "menu.h"       /* RSC-Definitionen für Menüs */
#include "submenu.h"    /* RSC-Definitionen für Submenüs */

EXTERN WORD subnum;

/* Rückgabewert: angeklickter Submenüeintrag */
/**********************************************/ 

main ()
{
   WORD mesg_buff[8];
   OBJECT *menu,*submenu;
   WORD event;
   OBJECT **rcs_pointer;
   OBJECT *rcs_adr;
   WORD ende=FALSE; 
   char string[20];
   WORD m_index[MAX_SUBMENU];

   /* Programm anmelden */ 
   appl_init();
   handle=graf_handle(&dummy,&dummy,&dummy,&dummy);
   open_vwork();

   /* Resource-Files laden */
   /* 1. Resource-File mit den Submenüs */ 
   if (!rsrc_load("submenu.rsc"))
   {
      form_alert(1,"[3][SUBMENU.RSC nicht gefunden!][ Abbruch ]");
      exit();
   }
   rsrc_gaddr(R_TREE,MENU,&submenu);

   /* Adresse des Resource-Files merken */ 
   rcs_pointer=(OBJECT **)&global[5]; 
   rcs_adr=*rcs_pointer;

   /* 2. Resource-File mit allen anderen Definitionen: normale Menüs,
      Dialogboxen, ... laden */ 
   if (!rsrc_load("menu.rsc"))
   {
      form_alert(1,"[3][MENU.RSC nicht gefunden!][Abbruch ]");
      exit();
   }
   rsrc_gaddr(R_TREE,MENU,&menu);

   /* Submenüs installieren */
   m_index[0]=BILDTYP;  /* Indizes aller Menüeinträge, die Titel */ 
   m_index[1]=SYSTEM;   /* f.Submenüs sein */
   m_index[2]=COMPUTER; /* sollen,in fortlaufender Reihenfolge */
   m_index[3]=STIL;
   init_submenus(handle,menu,submenu,MAX_SUBMENU,m_index);

   graf_mouse(ARROW,&dummy); 
   menu_bar(menu,TRUE);

   do
   {
      event=evnt_multi(MU_MESAG | MU_TIMER,dummy, dummy,dummy,dummy,dummy, 
                       dummy,dummy,dummy,dummy, dummy,dummy,dummy,dummy, mesg_buff,100,
                       0,&dummy,&dummy,&dummy,&dummy,&dummy,&dummy);

      if (event & MU_MESAG)
      {
         if (mesg_buff[0]==MN_SELECTED)
         {
            switch (mesg_buff[4])
            {
               case BILDTYP: 
               case SYSTEM: 
               case COMPUTER:
               case STIL:     redraw_bg();
                              break;
               /* bei Anklicken der Submenütitel müssen auch 
                  die Submenüs einklappen */ 
               case ENDE:     ende=TRUE;
                              break;
            }
            menu_tnormal(menu,mesg_buff[3],TRUE);
         }
      }

      if (event & MU_TIMER) /* subnum muß regelmäßig abgefragt werden */
      {                     /* da kein MU_MESAG gesendet wird */
         if (subnum)        /* Submenü angewählt? */
         {
            /* Haken setzen bzw. löschen */ 
            if (submenu[subnum].ob_state & CHECKED) 
               menu_icheck(submenu,subnum,FALSE);
            else
               menu_icheck(submenu,subnum,TRUE);

            /* Indexnr. des angewählten Submenüs ausgeben */ 
            sprintf(string,"Submenu-Index: %d ", subnum); 
            v_gtext(handle,0,150,string); 
            subnum=0; /* anschließend zurücksetzen */
         }
      }
   }
   while (!ende);

   /* Resource-Files wieder freigeben */ 
   rsrc_free();
   *rcs_pointer=rcs_adr; 
   rsrc_free(); 
   v_clsvwk(handle); 
   appl_exit();
}

open_vwork()
{
   WORD i;

   for (i=1; i<10; i++) 
      int_in[i]=1; 
   int_in[10]=2;
   v_opnvwk(int_in,&handle,int_out);
}

MENUDEMO.C



Links

Copyright-Bestimmungen: siehe Über diese Seite