home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format 125 / af125a.adf / Altabber.lzx / Altabber / src / Altabber.c next >
Encoding:
C/C++ Source or Header  |  1999-04-24  |  24.7 KB  |  926 lines

  1. /*  Altabber, n. He who performs an `Alt-Tab' action.
  2. **
  3. **  Version 1.5 (1.4.99) © Mfc.
  4. **
  5. **  Compile with DICE:
  6. **      dcc -mi -mRR -pi Altabber.c NewReadArgs.c
  7. **
  8. **  The "fixed-keys" version of Altabber (without argument/tooltypes parsing)
  9. **  can be obtained with:
  10. **      dcc -mi -mRR -DFIXEDKEYS -pi Altabber.c
  11. **
  12. **  To enable debugging output, simply add "-DDEBUG"
  13. **
  14. */
  15.  
  16.  
  17. #include "Altabber.h"
  18. #ifndef FIXEDKEYS
  19. #include <NewReadArgs.h>
  20. #endif
  21.  
  22. /// Defines
  23.  
  24. /* Messages we will receive from the Cx filter: */
  25. #define WINTAB      1   /* Cycle thru windows */
  26. #define SCRTAB      2   /* Cycle thru screens */
  27. #define UPSTROKE    8   /* Select this item */
  28.  
  29. /* Max string lengths: */
  30. #define MAX_TITLE_LEN   80
  31. #define MAX_HOTKEY_LEN  40
  32.  
  33. /* Some macros: */
  34. #define CURRENTLY_ACTIVE_SCR    IntuitionBase->ActiveScreen
  35. #define CURRENTLY_ACTIVE_WIN    IntuitionBase->ActiveWindow
  36. #define FIRST_AVAILABLE_SCR     IntuitionBase->FirstScreen
  37. #define FIRST_AVAILABLE_WIN     IntuitionBase->ActiveScreen->FirstWindow
  38.  
  39. /* Pretty obvious :-) */
  40. #define JAM1 0
  41. #define JAM2 1
  42.  
  43. /* This is a clearly impossible hotkey */
  44. #define DUMMY_KEY   "Ctrl LAmiga RAmiga NumPad *"
  45.  
  46. /* A couple of largely used macros: */
  47. #define xalloc(s)   AllocVec(s, 0)
  48. #define xfree(a)    FreeVec(a)
  49.  
  50. #ifdef DEBUG
  51. #define D(x)    x
  52. #else
  53. #define D(x)
  54. #endif
  55.  
  56. #define TEMPLATE    "WQ=WIN_QUAL/K,WK=WIN_KEY/K," \
  57.                     "SQ=SCR_QUAL/K,SK=SCR_KEY/K," \
  58.                     "CX_PRIORITY/N/K,"      \
  59.                     "COLOR1/N/K,COLOR2/N/K,"\
  60.                     "AA=AUTOACTIVATE/S" /* Not yet fully functional! */
  61. ///
  62.  
  63. /// Globals
  64.  
  65. /* The options (either CLI parameters or icon ToolTypes) */
  66. /* are stored in the Opt struct. Some defaults are given here: */
  67. const int def_pri = -5;
  68. const int def_color1 = 1;
  69. const int def_color2 = 2;
  70.  
  71. struct {
  72.     STRPTR win_qual, win_key;
  73.     STRPTR scr_qual, scr_key;
  74.     int *pri;
  75.     int *color1, *color2;
  76.     int aa; /* Not yet fully functional! */
  77. } Opt = {
  78.     "LAmiga", "Tab",
  79.     "LAmiga", "Shift Tab",
  80.     &def_pri,
  81.     &def_color1, &def_color2,
  82.     0       /* Not yet fully functional! */
  83. };
  84.  
  85. /* These are extern since we let the compiler care for */
  86. /* opening library, replying WB, etc. */
  87. extern struct IntuitionBase *IntuitionBase;
  88. extern struct WBStartup *_WBMsg;
  89.  
  90. /* Has our window been opened? */
  91. int Win_open = 0;
  92. int Scr_open = 0;
  93.  
  94. /* Each item (either scr or win) is stored in a struct */
  95. /* like this. Multiple records are then linked together */
  96. /* in a single chain. */
  97.  
  98. struct record {
  99.     void            *id;
  100.     unsigned char   title[MAX_TITLE_LEN];
  101.     int             len;
  102.     int             width;
  103.     struct record   *next;
  104. };
  105.  
  106. struct record *First_item;      /* Head of the chain */
  107. struct record *Special_item;    /* Some magic stuff usad by the OMM */
  108. struct record *Displayed_item;  /* The currently displayed item */
  109. struct record *Active_item;     /* The item which was active when the user */
  110.                                 /* pressed the hotkey the first time */
  111.  
  112. /* Gfx Globals: all the global variable which are related to gfx */
  113. /* (window, rastport, fonts, etc.) are placed in a single global struct */
  114. struct {
  115.     struct Window   *win;
  116.     struct RastPort *rp;
  117.     struct TextFont *font;
  118.     int font_height;
  119.     int font_baseline;
  120.     int max_width;
  121.     int top_corner, left_corner;
  122. } GG = {NULL};
  123.  
  124. /* OMM (Olivier's Memory Method) */
  125. void *Last_active_win = NULL;
  126. void *Last_active_scr = NULL;
  127.  
  128. struct MsgPort  *mp;    /* the port where we will receive all the msgs */
  129. ULONG   Sigbits;        /* signals we will wait for */
  130.  
  131. /* Every Cx must have an Object called a broker: */
  132. CxObj           *Broker;
  133.  
  134. /* The special filters used to know when the user releases the qualifier key. */
  135. /* They can't be "normal" hotkeys as they imply "upstroke", which the Cx */
  136. /* interface doen't recognize. */
  137.             /*  magic       class        code  cmask qual qmask synonyms*/
  138. IX scr_ix = { IX_VERSION, IECLASS_RAWKEY,  0, 0xffff,  0,   0,   0 };
  139. IX win_ix = { IX_VERSION, IECLASS_RAWKEY,  0, 0xffff,  0,   0,   0 };
  140.  
  141.  
  142.  
  143. ///
  144.  
  145. /// Prototypes
  146.  
  147. int open_window(void);
  148. void close_window(void);
  149. void display_next(void);
  150.  
  151. int create_scrlist(void);
  152. int create_winlist(void);
  153. void raise_scr(void);
  154. void raise_win(void);
  155. void dispose_list(void);
  156.  
  157. void handle(void);
  158.  
  159. int copy_title(STRPTR, STRPTR);
  160. int qualcode(STRPTR);
  161.  
  162. CxObj *AttachFilter(STRPTR, ULONG);
  163. int ShowError(STRPTR);
  164. ///
  165.  
  166. #define BROKERVERSION "Altabber 1.5  ©1999 Mfc."
  167. static const STRPTR version = "$VER: Altabber 1.5 (1.4.99)";
  168.  
  169. /// main()
  170. /*  ¯¯¯¯¯¯
  171. */
  172.  
  173. #ifdef _DCC
  174. __stkargs
  175. #endif
  176. void _main()
  177. {
  178.     int error = 0;
  179.  
  180. #ifndef FIXEDKEYS
  181.     /* Read the arguments/tooltypes */
  182.     struct NewRDArgs nrda = {
  183.         TEMPLATE,
  184.         NULL,
  185.         NULL,
  186.         (LONG *) &Opt,
  187.         -1,
  188.         TRUE,
  189.         /* other fields = NULL */
  190.     };
  191.  
  192.     error = NewReadArgs(_WBMsg, &nrda);
  193. #endif
  194.  
  195.     if (error == 0)
  196.     {
  197.         unsigned char win_hotkey[MAX_HOTKEY_LEN];
  198.         unsigned char scr_hotkey[MAX_HOTKEY_LEN];
  199.  
  200. #ifdef DEBUG
  201.         Printf("win_qual=%s\n", Opt.win_qual);
  202.         Printf("win_key=%s\n", Opt.win_key);
  203.         Printf("scr_qual=%s\n", Opt.scr_qual);
  204.         Printf("scr_key=%s\n", Opt.scr_key);
  205.         Printf("pri=%ld\n", *Opt.pri);
  206.         Printf("color1,2=%ld,%ld\n", *Opt.color1, *Opt.color2);
  207.         Printf("aa=%ld\n", Opt.aa);
  208. #endif
  209.  
  210.         /* Try to interpret the options and to build an adequate */
  211.         /* IX filter for every qualifier */
  212.         win_ix.ix_Code = IECODE_UP_PREFIX | qualcode(Opt.win_qual);
  213.         scr_ix.ix_Code = IECODE_UP_PREFIX | qualcode(Opt.scr_qual);
  214.  
  215.         /* Build the win hotkey as the union of the "_QUAL" option */
  216.         /* and the "_KEY" option */
  217.         strcpy(win_hotkey, Opt.win_qual);
  218.         strcat(win_hotkey, " ");
  219.         strcat(win_hotkey, Opt.win_key);
  220.  
  221.         /* Build the scr hotkey as the union of the "_QUAL" option */
  222.         /* and the "_KEY" option */
  223.         strcpy(scr_hotkey, Opt.scr_qual);
  224.         strcat(scr_hotkey, " ");
  225.         strcat(scr_hotkey, Opt.scr_key);
  226.  
  227. #ifdef DEBUG
  228.         Printf("win_hotkey=|%s|\n", win_hotkey);
  229.         Printf("scr_hotkey=|%s|\n", scr_hotkey);
  230.         Printf("win_ix.ix_Code=%lx\n", win_ix.ix_Code);
  231.         Printf("scr_ix.ix_Code=%lx\n", scr_ix.ix_Code);
  232. #endif
  233.  
  234.         if (mp=CreateMsgPort())
  235.         {
  236.             /* Create the broker */
  237.             struct NewBroker nb = {
  238.                 NB_VERSION,
  239.                 "Altabber",
  240.                 BROKERVERSION,
  241.                 "Windoze-like Alt-Tab function",
  242.                 NBU_UNIQUE | NBU_NOTIFY,
  243.                 0, *Opt.pri, 0, 0
  244.             };
  245.  
  246.             nb.nb_Port = mp;
  247.             Sigbits = SIGBREAKF_CTRL_C | (1L << mp->mp_SigBit);
  248.  
  249.             if (Broker=CxBroker(&nb, &error))
  250.             {
  251.                 /* Now we start to attach things to the broker. */
  252.                 /* First of all the two filters which intercept */
  253.                 /* the Amiga+Tab and Amiga+Shift+Tab strokes */
  254.                 if (AttachFilter(win_hotkey, WINTAB) &&
  255.                     AttachFilter(scr_hotkey, SCRTAB))
  256.                 {
  257.                     /* Add the special filter which intercept */
  258.                     /* the release of the Amiga key. To do this, */
  259.                     /* first build a normal filetr with a dummy hotkey, */
  260.                     /* then replace the dummy hotkey with the actual IX */
  261.                     CxObj *x = AttachFilter(DUMMY_KEY, UPSTROKE);
  262.                     if (x)
  263.                     {
  264.                         SetFilterIX(x, &win_ix);
  265.  
  266.                         /* Do the same with the scr upstroke filter, */
  267.                         /* but only if it's different from the win one */
  268.                         if (scr_ix.ix_Code != win_ix.ix_Code)
  269.                         {
  270.                             x = AttachFilter(DUMMY_KEY, UPSTROKE);
  271.                             if (x)
  272.                                 SetFilterIX(x, &scr_ix);
  273.                         }
  274.                         if (x)
  275.                         {
  276.                             /* If everithing's ok, activate the broker */
  277.                             ActivateCxObj(Broker, 1);
  278.  
  279.                             handle();
  280.                         }
  281.                         else
  282.                             error = ShowError("couldn't create 2nd upstroke filter.");
  283.                     }
  284.                     else
  285.                         error = ShowError("Couldn't create upstroke filter.");
  286.                 }
  287.                 else
  288.                     error = ShowError("couldn't create filters.");
  289.  
  290.                 /* Delete the broker (all attached objects get freed as well) */
  291.                 DeleteCxObjAll(Broker);
  292.  
  293.                 /* Empty the msg queue by replying to all pending msgs */
  294.                 {
  295.                     struct Message *msg;
  296.                     while (msg=GetMsg(mp))
  297.                         ReplyMsg(msg);
  298.                 }
  299.             }
  300.             else
  301.             {
  302.                 if (error == CBERR_DUP)
  303.                     /* Not a real error: the prg was started twice */
  304.                     /* so the second instance tells the first one to quit */
  305.                     error = 0;
  306.                 else
  307.                     error = ShowError("couldn't create broker.");
  308.             }
  309.  
  310.             DeleteMsgPort(mp);
  311.         }
  312.         else
  313.             error = ShowError("couldn't create message port.");
  314.  
  315. #ifndef FIXEDKEYS
  316.         NewFreeArgs(&nrda);
  317. #endif
  318.     }
  319.     else
  320.         error = ShowError("invalid arguments.");
  321.  
  322.     _exit(error);
  323.  
  324.  
  325. #ifdef _DCC
  326.     /* Dummy reference (never executed) which forces Dice to import */
  327.     /* the WBMsg handling routines. */
  328.     void _waitwbmsg(void);
  329.     _waitwbmsg();
  330. #endif
  331. }
  332. ///
  333.  
  334. /// AttachFilter()
  335. /*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  336. ** This routine adds a filter to an existing broker.
  337. ** Each filter is triggered by the event ginven in the "key" string.
  338. ** The filter in turn has attached a Translator (which "eats" the event
  339. ** so that it doesn't propagate to lower-priority Cx's) an a Sender
  340. ** (which sends a "event" msg to the msg port "mp").
  341. **
  342. ** It returns the address of the created filter.
  343. */
  344.  
  345. CxObj *AttachFilter(STRPTR key, ULONG event)
  346. {
  347.     CxObj *filter, *sender, *translator;
  348.  
  349.     if (filter=CxFilter(key))
  350.     {
  351.         AttachCxObj(Broker, filter);
  352.  
  353.         if (sender=CxSender(mp, event))
  354.         {
  355.             AttachCxObj(filter, sender);
  356.  
  357.             if (translator=CxTranslate(NULL))
  358.             {
  359.                 AttachCxObj(filter, translator);
  360.  
  361.                 if (!CxObjError(filter))
  362.                     return filter;
  363.             }
  364.         }
  365.     }
  366.  
  367.     return NULL;
  368. }
  369. ///
  370.  
  371. /// handle()
  372. /*  ¯¯¯¯¯¯¯¯
  373. ** This is the kernel of the entire program. It basically consists
  374. ** of a classic Wait-GetMsg-ReplyMsg loop where the appropriate
  375. ** routines are called according to the type of message received.
  376. */
  377.  
  378. void handle(void)
  379. {
  380.     int error = 0;
  381.  
  382.     /* Main loop: the routines inside will set error to 1 */
  383.     /* if the main loop is to be quitted. */
  384.  
  385.     while (!error)
  386.     {
  387.         CxMsg *msg;
  388.         ULONG signal = Wait(Sigbits);
  389.  
  390.         /* We received a signal: either one or more msgs have arrived */
  391.         /* or someone has sent us a CTRL-C */
  392.  
  393.         while (msg = (CxMsg*) GetMsg(mp))
  394.         {
  395.             /* We received a msg, so keep its type & ID */
  396.             /* and then reply to it */
  397.             ULONG id = CxMsgID(msg);
  398.             ULONG type = CxMsgType(msg);
  399.             ReplyMsg((struct Message *) msg);
  400.  
  401.             /* Take the appropriate action */
  402.             /* Note that this part could be more optimized */
  403.             switch (type)
  404.             {
  405.                 case CXM_IEVENT: /* One of the hotkeys... */
  406.  
  407.                     switch(id)
  408.                     {
  409.                         case SCRTAB:
  410.                             if (Win_open)
  411.                             {
  412.                                 close_window();
  413.                                 dispose_list();
  414.                             }
  415.  
  416.                             if (!Scr_open)
  417.                             {
  418.                                 if (create_scrlist())
  419.                                     Scr_open = open_window();
  420.                             }
  421.  
  422.                             if (Scr_open)
  423.                             {
  424.                                 display_next();
  425.                             }
  426.                             break;
  427.  
  428.                         case WINTAB:
  429.                             if (Scr_open)
  430.                             {
  431.                                 close_window();
  432.                                 dispose_list();
  433.                             }
  434.  
  435.                             if (!Win_open)
  436.                             {
  437.                                 if (create_winlist())
  438.                                     Win_open = open_window();
  439.                             }
  440.  
  441.                             if (Win_open)
  442.                             {
  443.                                 display_next();
  444.                             }
  445.                             break;
  446.  
  447.                         case UPSTROKE:
  448.                             /* If alright, raise the selected item */
  449.                             /* then close the window */
  450.                             if (Scr_open)
  451.                             {
  452.                                 raise_scr();
  453.                                 close_window();
  454.                                 dispose_list();
  455.                             }
  456.                             if (Win_open)
  457.                             {
  458.                                 raise_win();
  459.                                 close_window();
  460.                                 dispose_list();
  461.                             }
  462.                             break;
  463.                     }
  464.  
  465.                     break;
  466.  
  467.                 case CXM_COMMAND: /* Standard CX stuff... */
  468.  
  469.                     switch (id)
  470.                     {
  471.                         case CXCMD_DISABLE:
  472.                             ActivateCxObj(Broker, 0);
  473.                             break;
  474.  
  475.                         case CXCMD_ENABLE:
  476.                             ActivateCxObj(Broker, 1);
  477.                             break;
  478.  
  479.                         case CXCMD_KILL:
  480.                         case CXCMD_UNIQUE:
  481.                             /* Quit... */
  482.                             error = 1;
  483.                             break;
  484.                     }
  485.                     break;
  486.             }
  487.         }
  488.  
  489.         /* If we received a CTRL-C, quit */
  490.         if (signal & SIGBREAKF_CTRL_C)
  491.             error = 1;
  492.     }
  493.  
  494.     /* Before quitting, close open windows and free allocated resources */
  495.     if (Win_open || Scr_open)
  496.     {
  497.         close_window();
  498.         dispose_list();
  499.     }
  500. }
  501. ///
  502.  
  503. /// create_winlist()
  504. /*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  505. ** Examine the list of open windows and build a chain of "items"
  506. ** or "records". The first win in the chain must be the Special One
  507. ** according to the OMM (Olivier's Memory Method). The last win
  508. ** should be the currently active one.
  509. ** Windows whithout printable title are skipped.
  510. */
  511.  
  512. int create_winlist(void)
  513. {
  514.     Active_item = First_item = Special_item = Displayed_item = NULL;
  515.  
  516.     /* Examine all the wins, starting with the currently active one */
  517.     struct Window *aw = CURRENTLY_ACTIVE_WIN;
  518.     struct Window *w = aw;
  519.  
  520.     do {
  521.         /* Has printable title? */
  522.         STRPTR title = w->Title;
  523.         if ( title && (*title != '\0') )
  524.         {
  525.             /* Yes: allocate space for a new record and fill it */
  526.             struct record *item = xalloc(sizeof(struct record));
  527.             if (item == NULL)
  528.                 break;
  529.  
  530.             item->len = copy_title(item->title, title);
  531.             item->id = w;
  532.  
  533.             /* Is this win the Special One according to OMM? */
  534.             if (w == Last_active_win && w != aw)
  535.             {
  536.                 /* Yes: keep it apart */
  537.                 Special_item = item;
  538.             }
  539.             else
  540.             {
  541.                 /* No: add it to the head of the chain */
  542.                 item->next = First_item;
  543.                 First_item = item;
  544.             }
  545.  
  546.             /* Is this win the currently active one? */
  547.             if (w == aw)
  548.                 Active_item = item;
  549.         }
  550.  
  551.         /* Examine the next win. If we reach the end of the list */
  552.         /* restart from the first win of this screen */
  553.  
  554.         w = w->NextWindow;
  555.         if (w == NULL)
  556.             w = FIRST_AVAILABLE_WIN;
  557.  
  558.     /* Stop when we cycled thru all available wins and we came back to */
  559.     /* the firstly examined one, which happens to be the currently active one */
  560.     } while (w != aw);
  561.  
  562.     /* If a Special Win was found, add it to the head of the chain, so that */
  563.     /* it will appear first (making Olivier happy) */
  564.     if (Special_item)
  565.     {
  566.         Special_item->next = First_item;
  567.         First_item = Special_item;
  568.     }
  569.  
  570.     /* Now, if we found only one win and that win is already selected */
  571.     /* (the currently active one) then Altabber is obviously of no use, */
  572.     /* so don't even open the window: discard the chain and return an error */
  573.     if (First_item == Active_item)
  574.     {
  575.         dispose_list();
  576.         return 0; /* means: couldn't correctly initiate the wins chain */
  577.     }
  578.  
  579.     return 1;   /* Ok */
  580. }
  581. ///
  582.  
  583. /// create_scrlist()
  584. /*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  585. ** Examine the list of open screens and build a chain of "items"
  586. ** or "records". The first scr in the chain must be the Special One
  587. ** according to the OMM (Olivier's Memory Method). The last scr
  588. ** should be the currently active one.
  589. **
  590. ** Please see the comments in the create_winlist() function.
  591. */
  592.  
  593. int create_scrlist(void)
  594. {
  595.     Active_item = First_item = Special_item = Displayed_item = NULL;
  596.  
  597.     struct Screen *as = CURRENTLY_ACTIVE_SCR;
  598.     struct Screen *s = as;
  599.  
  600.     do {
  601.         STRPTR title = s->Title;    /* Or DefaultTitle ?!? */
  602.  
  603.         /* Screen are never skipped, regardless of their title */
  604.         //if ( title && (*title != '\0') )
  605.         {
  606.             struct record *item = xalloc(sizeof(struct record));
  607.             if (item == NULL)
  608.                 break;
  609.  
  610.             /* If the scr has no printable title, a default one */
  611.             /* is provided */
  612.             if ( title && (*title != '\0') )
  613.                 item->len = copy_title(item->title, title);
  614.             else
  615.                 item->len = copy_title(item->title, "<no title>");
  616.  
  617.             item->id = s;
  618.  
  619.             if (s == Last_active_scr && s != as)
  620.             {
  621.                 Special_item = item;
  622.             }
  623.             else
  624.             {
  625.                 item->next = First_item;
  626.                 First_item = item;
  627.             }
  628.  
  629.             if (s == as)
  630.                 Active_item = item;
  631.         }
  632.  
  633.         s = s->NextScreen;
  634.         if (s == NULL)
  635.             s = FIRST_AVAILABLE_SCR;
  636.  
  637.     } while (s != as);
  638.  
  639.     if (Special_item)
  640.     {
  641.         Special_item->next = First_item;
  642.         First_item = Special_item;
  643.     }
  644.  
  645.     if (First_item == Active_item)
  646.     {
  647.         dispose_list();
  648.         return 0;
  649.     }
  650.  
  651.     return 1;   /* Ok */
  652.  
  653. }
  654. ///
  655.  
  656. /// open_window()
  657. /*  ¯¯¯¯¯¯¯¯¯¯¯¯¯
  658. */
  659.  
  660. int open_window(void)
  661. {
  662.     struct Screen *as = IntuitionBase->ActiveScreen;
  663.     struct RastPort *tmp_rp = &as->RastPort;
  664.  
  665.     /* Get the screen font's height */
  666.     GG.font_height = as->Font->ta_YSize;
  667.  
  668.     /* Try to determine the max window width required */
  669.     /* by the titles in the chain */
  670.     GG.max_width = 0;
  671.     struct record *item = First_item;
  672.     while (item)
  673.     {
  674.         int width = TextLength(tmp_rp, item->title, item->len);
  675.         item->width = width;
  676.         if (width > GG.max_width)
  677.             GG.max_width = width;
  678.  
  679.         item = item->next;
  680.     }
  681.  
  682.     /* Leave a margin of twice the font's height on both sides */
  683.     /* and above and below the print-area */
  684.     int winw = GG.max_width + 4 * GG.font_height;
  685.     int winh = 5 * GG.font_height;
  686.  
  687.     /* Open the window on the currently active screen */
  688.     /* with the calculated size and without any title */
  689.     struct Window  *win = OpenWindowTags(NULL,
  690.             WA_Left,        (as->Width - winw)/2,
  691.             WA_Top,         (as->Height - winh)/2,
  692.             WA_Width,       winw,
  693.             WA_Height,      winh,
  694.             WA_CustomScreen,(ULONG) as,
  695.             TAG_END,        0
  696.         );
  697.  
  698.     if (win == NULL)
  699.         return 0;
  700.  
  701.     /* Fill in the Global Gfx variables */
  702.     GG.win = win;
  703.     GG.rp = win->RPort;
  704.     /* These are the coordinates of the print-area */
  705.     /* inside the window */
  706.     GG.left_corner = 2 * GG.font_height;
  707.     GG.top_corner = 2 * GG.font_height;
  708.  
  709.     /* Set the same font as the screen's one */
  710.     GG.font = OpenFont(as->Font);
  711.     if (GG.font)
  712.     {
  713.         SetFont(GG.rp, GG.font);
  714.     }
  715.  
  716.     GG.font_baseline = GG.font->tf_Baseline;
  717.  
  718.     /* Be sure we are writing in JAM1 mode */
  719.     SetABPenDrMd(GG.rp, 1, 0, JAM1);
  720.  
  721.     return 1;   /* Ok */
  722. }
  723. ///
  724.  
  725. /// display_next()
  726. /*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  727. */
  728.  
  729. void display_next(void)
  730. {
  731.     /* Select the next item */
  732.     if (Displayed_item)
  733.         Displayed_item = Displayed_item->next;
  734.  
  735.     /* If at the end of the chain (or if called for the first time) */
  736.     /* restart from the head of the chain */
  737.     if (Displayed_item == NULL)
  738.         Displayed_item = First_item;
  739.  
  740.     /* Clear the print-area */
  741.     EraseRect(GG.rp,
  742.         GG.left_corner,
  743.         GG.top_corner,
  744.         GG.left_corner + GG.max_width,
  745.         GG.top_corner + GG.font_height
  746.     );
  747.  
  748.     /* Center the title in the print-area */
  749.     Move(GG.rp,
  750.         GG.left_corner + (GG.max_width - Displayed_item->width)/2,
  751.         GG.top_corner + GG.font_baseline
  752.     );
  753.  
  754.     /* Use color2 (white) if we are showing the currently active win/scr, */
  755.     /* color1 (black) otherwise */
  756.     if (Displayed_item == Active_item)
  757.         SetAPen(GG.rp, *Opt.color2);
  758.     else
  759.         SetAPen(GG.rp, *Opt.color1);
  760.  
  761.     Text(GG.rp, Displayed_item->title, Displayed_item->len);
  762. }
  763. ///
  764.  
  765. /// close_window()
  766. /*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  767. ** Close the window and the font (if opened),
  768. ** then free all the items of the chain.
  769. */
  770.  
  771. void close_window(void)
  772. {
  773.     if (GG.font)
  774.     {
  775.         CloseFont(GG.font);
  776.         GG.font = NULL;
  777.     }
  778.  
  779.     if (GG.win)
  780.     {
  781.         CloseWindow(GG.win);
  782.         GG.win = NULL;
  783.     }
  784.  
  785.     Scr_open = Win_open = 0;
  786. }
  787. ///
  788.  
  789. /// dispose_list()
  790. /*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  791. ** This routine frees all items of the chain.
  792. */
  793.  
  794. void dispose_list(void)
  795. {
  796.     struct record *item = First_item;
  797.     while(item)
  798.     {
  799.         struct record *next = item->next;
  800.         xfree(item);
  801.         item = next;
  802.     }
  803. }
  804. ///
  805.  
  806. /// raise_win()
  807. /*  ¯¯¯¯¯¯¯¯¯¯¯
  808. ** Bring the selected win to the foreground, but only if it's not
  809. ** the currently active one.
  810. */
  811.  
  812. void raise_win(void)
  813. {
  814.     if (Displayed_item && Displayed_item != Active_item)
  815.     {
  816.         Last_active_win = CURRENTLY_ACTIVE_WIN; /* OMM! */
  817.         WindowToFront(Displayed_item->id);
  818.         ActivateWindow(Displayed_item->id);       
  819.     }
  820. }
  821. ///
  822.  
  823. /// raise_scr()
  824. /*  ¯¯¯¯¯¯¯¯¯¯¯
  825. ** Bring the selected scr to the foreground, but only if it's not
  826. ** the currently active one. If the AUTOACTIVE flag is on, then
  827. ** activate the first window of the newly raised screen.
  828. */
  829.  
  830. void raise_scr(void)
  831. {
  832.     if (Displayed_item && Displayed_item != Active_item)
  833.     {
  834.         Last_active_scr = CURRENTLY_ACTIVE_SCR; /* OMM! */
  835.         ScreenToFront(Displayed_item->id);
  836.         if (Opt.aa)
  837.             /* Not yet fully functional! */
  838.             ActivateWindow(FIRST_AVAILABLE_WIN);
  839.     }
  840. }
  841. ///
  842.  
  843. /// copy_title()
  844. /*  ¯¯¯¯¯¯¯¯¯¯¯¯
  845. ** Same as strcpy(), but ignores multiple spaces and copies
  846. ** only up to MAX_TITLE_LEN-1 chars (including trailing '\0').
  847. **
  848. ** Returns the number of chars copied, same as strlen(dst).
  849. */
  850.  
  851. int copy_title(STRPTR dst, STRPTR src)
  852. {
  853.     int i = 0;
  854.  
  855.     while ( *src && i < MAX_TITLE_LEN-1 )
  856.     {
  857.         if (*src == ' ')
  858.         {
  859.             while (*src == ' ')
  860.                 ++src;
  861.             if (i)
  862.                 dst[i++] = ' ';
  863.         }
  864.         else
  865.         {
  866.             dst[i++] = *(src++);
  867.         }
  868.     }
  869.  
  870.     dst[i] = '\0';
  871.  
  872.     return i;
  873. }
  874. ///
  875.  
  876. /// ShowError()
  877. /*  ¯¯¯¯¯¯¯¯¯¯¯
  878. ** Prints an error message in a requester.
  879. */
  880.  
  881. int ShowError(STRPTR err)
  882. {
  883.     struct EasyStruct errmsg =
  884.     {
  885.         sizeof(struct EasyStruct),
  886.         0,
  887.         "Altabber",
  888.         "An error has occurred:\n%s\nQuitting.",
  889.         "Ok"
  890.     };
  891.  
  892.     EasyRequest(NULL, &errmsg, NULL, err);
  893.  
  894.     return 10;
  895. }
  896. ///
  897.  
  898. /// qualcode()
  899. /*  ¯¯¯¯¯¯¯¯¯¯
  900. ** Every "qualifier" key has a raw code associated, as any other
  901. ** "normal" key. Unfortunately there's no way to access to these keys
  902. ** as normal keys using the standard "input expression" method.
  903. ** So to get the correct raw code we must work it out manually
  904. */
  905.  
  906. int qualcode(STRPTR key)
  907. {
  908.     if (!stricmp(key, "ctrl"))      return 0x63;
  909.     if (!stricmp(key, "control"))   return 0x63;
  910.     if (!stricmp(key, "lalt"))      return 0x64;
  911.     if (!stricmp(key, "ralt"))      return 0x65;
  912.     if (!stricmp(key, "lamiga"))    return 0x66;
  913.     if (!stricmp(key, "ramiga"))    return 0x67;
  914.     if (!stricmp(key, "lcommand"))  return 0x66;
  915.     if (!stricmp(key, "rcommand"))  return 0x67;
  916.     if (!stricmp(key, "lshift"))    return 0x60;
  917.     if (!stricmp(key, "rshift"))    return 0x61;
  918.     if (!stricmp(key, "caps"))      return 0x62;
  919.     if (!stricmp(key, "capslock"))  return 0x62;
  920.  
  921.     return 0;
  922. }
  923. ///
  924.  
  925.  
  926.