home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 19 / AACD19.BIN / AACD / System / SnoopDos / SnoopDos_Src / SnoopDos_Source / mainwin.c < prev    next >
C/C++ Source or Header  |  2001-02-04  |  251KB  |  5,289 lines

  1. /*
  2.  *              MAINWIN.C                                                                                       vi:ts=4
  3.  *
  4.  *      Copyright (c) Eddy Carroll, September 1994.
  5.  *
  6.  *              This module handles the main SnoopDos window, outputting new events
  7.  *              to the window, menu options, etc.
  8.  */             
  9.  
  10. #include "system.h"
  11. #include "snoopdos.h"
  12. #include "gui.h"
  13. #include "patches.h"
  14.  
  15. #if 0
  16. #define DB(s)   KPrintF(s)
  17. #else
  18. #define DB(s)
  19. #endif
  20.  
  21. /*
  22.  *              In earlier versions, we used to lock layers whenever we rendered
  23.  *              event output to the SnoopDos main window buffer. This avoided
  24.  *              deadlocks with input.device. Now we use a different work around,
  25.  *              but for the sake of documentation, we leave the original calls
  26.  *              in (you never know when they might come in handy)
  27.  *
  28.  *              (The different workaround is to never monitor tasks that have
  29.  *              locked layers on the SnoopDos screen/window).
  30.  */
  31. #if 1
  32. #define LOCK_LAYERS                                                     \
  33.         if (MainWindow) {                                               \
  34.                 LockLayerInfo(&MainWindow->WScreen->LayerInfo);         \
  35.                 LockLayer(0, MainWindow->RPort->Layer);                 \
  36.         }
  37.  
  38. #define UNLOCK_LAYERS                                                   \
  39.         if (MainWindow) {                                               \
  40.                 UnlockLayer(MainWindow->RPort->Layer);                  \
  41.                 UnlockLayerInfo(&MainWindow->WScreen->LayerInfo);       \
  42.         }
  43.  
  44. #else
  45. #define LOCK_LAYERS
  46. #define UNLOCK_LAYERS
  47. #endif
  48.  
  49.  
  50. /*
  51.  *              Interface to RawPutChar() in exec.library
  52.  *              (It takes a single parameter in D0)
  53.  */
  54. #pragma libcall SysBase RawPutChar 204 001
  55. void RawPutChar(char);
  56.  
  57. /*
  58.  *              These two are imported from SNOOPDOS.C
  59.  */
  60. extern char     Version[];
  61. extern char SnoopDosTitle[];
  62. extern char SnoopDosTitleKey[];
  63.  
  64. /*
  65.  *              Global vars used by the main window
  66.  */
  67. struct Gadget           *MainGadList;           /* Main gadget list                     */
  68. struct Menu                     *MainWinMenu;           /* Main window menu                     */
  69. struct TextFont         *BufferFont;            /* Handle for buffer font       */
  70. struct TextAttr         *CurMainGadgetFA;       /* Current gadget font attr     */
  71. struct TextAttr         *CurMainBufferFA;       /* Current buffer font attr     */
  72.  
  73. struct RastPort         InvertRP;                       /* Used when sizing columns     */
  74.  
  75. int NumBufLines;                        /* Current number of lines in buffer    */
  76. int ChangedSpacing;                     /* 1 = edited spacing since last redraw */
  77. int ScrollDirection;            /* ID of current scroll gadget, or 0    */
  78. int ScrollCount;                        /* Used for pacing INTUITICKS in scroll */
  79. int RemovedGadgets;                     /* True if gadgets removed from window  */
  80. int AwaitingResize;                     /* True if waiting for window resize    */
  81.  
  82. #define RESIZE_DONE             0       /* Resize is now complete                               */
  83. #define RESIZE_MIDDLE   1       /* Resize happened in middle of buffer  */
  84. #define RESIZE_BOTTOM   2       /* Resize happened at end of buffer             */
  85.  
  86. #define MOVECOL_FREE    0       /* Reposition column freely                             */
  87. #define MOVECOL_FIXED   1       /* Reposition column fixed wrt RHS col  */
  88.  
  89. #define INVERT_HEADER   0       /* Invert only the header part of col   */
  90. #define INVERT_FULLBOX  1       /* Invert entire column                 */
  91.  
  92. int     BoxLeft;                                /* Pixel offset of left edge of box             */
  93. int     BoxTop;                                 /* Pixel offset of top edge of box              */
  94. int     BoxWidth;                               /* Width of box in pixels                               */
  95. int     BoxHeight;                              /* Height of box in pixels                              */
  96. int     BoxHeaderLine;                  /* Baseline for box header text                 */
  97. int     BoxInLeft;                              /* Pixel offset of inside top of box    */
  98. int     BoxInTop;                               /* Pixel offset of inside left of box   */
  99. int     BoxInWidth;                             /* Inside width of box                                  */
  100. int     BoxInHeight;                    /* Inside height of box                                 */
  101. int     BoxCharWidth;                   /* Width of a single character in box   */
  102. int BoxCharHeight;                      /* Height of a single character in box  */
  103. int     BoxCols;                                /* Number of columns of text in box             */
  104. int     BoxRows;                                /* Number of rows of text in box                */
  105. int     BoxSpacing;                             /* Height between font baselines in box */
  106. int     BoxLowest;                              /* Bottom point of box scroll arrow             */
  107. int     BoxBaseline;                    /* Baseline of text line 0 in box               */
  108. int     VBorderGap;                             /* Aspect-adjusted space betw hrz lines */
  109. int     LogButtonLeft;                  /* Left (X) Position of Logfile button  */
  110. int LogButtonID;                        /* Gadget ID of log button on view              */
  111.  
  112. int     LeftCol;                                /* Leftmost displayed column                    */
  113. int     RightCol;                               /* Rightmost displayed column                   */
  114.  
  115. int DraggingRow;                        /* 1 if dragging a row with mouse               */
  116. int SelectRow;                          /* Row to draw highlighted                              */
  117. int DraggingColumn;                     /* 1 if dragging a column with mouse    */
  118. int ClickStartCol;                      /* Col pos where mouse first clicked    */
  119. int ClickCurCol;                        /* Current column where mouse clicked   */
  120. int SelectStartCol;                     /* Start position of selected column    */
  121. int DragOrigColWidth;           /* Starting width of widening column    */
  122. int NextOrigColWidth;           /* Starting width of following column   */
  123. int OrigLeftCol;                        /* Starting position of left column             */
  124. int OrigBufWidth;                       /* Starting width of buffer                             */
  125. int MovedColumn;                        /* True if column position was altered  */
  126.  
  127. EventFormat *DragEvent;         /* Points to column being widened               */
  128. EventFormat *SelectEvent;       /* Points to column being highlighted   */
  129.  
  130. BPTR   LogFile;                         /* Handle of output log file                    */
  131. UBYTE *LogBuffer;                       /* Buffer for buffered i/o (or NULL)    */
  132. ULONG  LogBufferSize;           /* Size of logfile output buffer                */
  133. ULONG  LogBufferPos;            /* Current position in output buffer    */
  134.  
  135. UBYTE  MainKeyboard[KB_SHORTCUT_SIZE];  /* For keyboard equivs          */
  136.  
  137. /*
  138.  *              This table is used to select the appropriate row qualifier
  139.  *              for the row selection mechanism
  140.  */
  141. UWORD RowQualTable[] = {
  142.         0,                              /* ROWQUAL_ANY   -- ignore qualifiers                           */
  143.         0,                              /* ROWQUAL_NONE  -- don't allow any qualifiers          */
  144.         IE_SHIFT,               /* ROWQUAL_SHIFT -- only select if SHIFT is pressed     */
  145.         IE_ALT,                 /* ROWQUAL_ALT   -- only select if ALT is pressed       */
  146.         IE_CTRL,                /* ROWQUAL_CTRL  -- only select if CTRL is pressed      */
  147.         IE_ALL                  /* ROWQUAL_ALL   -- only select if any qual pressed     */
  148. };
  149.  
  150. /*
  151.  *              Now our font structures.
  152.  *
  153.  *              We do everything in terms of FontAttr structures, and whenever a
  154.  *              window needs a font, it OpenFont's it itself. This has several
  155.  *              advantages: it allows each window to dynamically select the
  156.  *              best-fitting font, and it also ensures no problems if we have a
  157.  *              window open with one font and the user than changes the font
  158.  *              to something else -- the first window will still have a valid
  159.  *              lock on the old font.
  160.  */
  161. char   BufferLine[MAX_BUFFER_WIDTH];  /* Storage area for buffer lines */
  162.  
  163. struct TextAttr TopazFontAttr  = {"topaz.font",   8, FS_NORMAL, FPB_ROMFONT };
  164. struct TextAttr SystemFontAttr = {SystemFontName, 0, FS_NORMAL, FPB_DISKFONT};
  165. struct TextAttr WindowFontAttr = {WindowFontName, 0, FS_NORMAL, FPB_DISKFONT};
  166. struct TextAttr BufferFontAttr = {BufferFontName, 0, FS_NORMAL, FPB_DISKFONT};
  167.  
  168. struct {
  169.         struct TextAttr *gadgetfa;
  170.         struct TextAttr *bufferfa;
  171. } MainWindowFontList[] = {
  172.         &WindowFontAttr,        &BufferFontAttr,
  173.         &SystemFontAttr,        &BufferFontAttr,
  174.         &WindowFontAttr,        &SystemFontAttr,
  175.         &SystemFontAttr,        &SystemFontAttr,
  176.         &TopazFontAttr,         &SystemFontAttr,
  177.         &TopazFontAttr,         &TopazFontAttr,
  178.         NULL,                           NULL
  179. };
  180.  
  181. /*
  182.  *              This is used by the boopsi proportional gadgets
  183.  */
  184. static struct TagItem RZ_MapTags[] = {
  185.         PGA_Top, ICSPECIAL_CODE,
  186.         TAG_END
  187. };
  188.  
  189. /*
  190.  *              This holds the image pointers for our BOOPSI scroll gadgets
  191.  */
  192. struct Image            *ScrollImage[4];
  193.  
  194. /*
  195.  *              This structure is used to define our four scroll gadgets. We
  196.  *              define the relative positions of each gadget in terms of the
  197.  *              formula (Scale * Width + offset) or (Scale * Height + offset)
  198.  *              where Width and Height are taken from the image structure.
  199.  */
  200. struct ScrollData {
  201.         int             gadgetid;                       /* ID of the gadget                                             */
  202.         int             imagetype;                      /* Which type of image we're creating   */
  203.         LONG    widthscale;                     /* Scale applied to width                               */
  204.         LONG    widthoffset;            /* Offset applied to width                              */
  205.         LONG    heightscale;            /* Scale applied to height                              */
  206.         LONG    heightoffset;           /* Offset applied to offset                             */
  207.         LONG    borderpos;                      /* Tag for border containing gadget             */
  208. } ScrollArrows[] = {
  209.         GID_LEFTARROW,  LEFTIMAGE,      -2, -17,        -1, 1,          GA_BottomBorder,
  210.         GID_RIGHTARROW, RIGHTIMAGE,     -1, -17,        -1, 1,          GA_BottomBorder,
  211.         GID_UPARROW,    UPIMAGE,        -1, 1,          -2, -9,         GA_RightBorder,
  212.         GID_DOWNARROW,  DOWNIMAGE,      -1, 1,          -1, -9,         GA_RightBorder
  213. };
  214.         
  215. /*
  216.  *              Now the gadgets for the main window
  217.  */
  218. struct MainGadgets {
  219.         UWORD   gadgid;                 /* Gadget ID                                    */
  220.         UWORD   stringid;               /* ID of label name             */
  221.         UBYTE   col4;                   /* Column in 4 x 2 layout               */
  222.         UBYTE   row4;                   /* Row in 4 x 2 layout                  */
  223.         UBYTE   col6;                   /* Column in 4 x 2 layout               */
  224.         UBYTE   row6;                   /* Row in 4 x 2 layout                  */
  225.         UBYTE   col8;                   /* Column in 4 x 2 layout               */
  226.         UBYTE   col12;                  /* Column in 12 x 1 layout              */
  227.         UBYTE   widthtype;              /* 0=narrow, 1=wide, 2=status   */
  228. };
  229.  
  230.  
  231. struct MainGadgets MainGadgs[] = {
  232.  GID_HIDE,              MSG_HIDE_GAD,           0, 2,    0,     1,      0,      4,      MAIN_NARROW,
  233.  GID_QUIT,              MSG_QUIT_GAD,           1, 2,    1,     1,      1,      5,      MAIN_NARROW,
  234.  GID_PAUSE,             MSG_PAUSE_GAD,          0, 1,    2,     1,      2,      6,      MAIN_NARROW_TOGGLE,
  235.  GID_DISABLE,   MSG_DISABLE_GAD,        1, 1,    3,     1,      3,      7,      MAIN_NARROW_TOGGLE,
  236.  GID_OPENLOG,   MSG_OPENLOG_GAD,        2, 1,    4, 0,  4,      8,  MAIN_WIDE,
  237.  GID_APPENDLOG, MSG_APPENDLOG_GAD,      2, 1,    4,     0,      4,      8,      MAIN_WIDE_INVIS,
  238.  GID_STARTLOG,  MSG_STARTLOG_GAD,       2, 1,    4,     0,      4,      8,      MAIN_WIDE_INVIS,
  239.  GID_SERIALLOG, MSG_SERIALLOG_GAD,      2, 1,    4, 0,  4,  8,  MAIN_WIDE_INVIS,
  240.  GID_CLOSELOG,  MSG_CLOSELOG_GAD,       2, 1,    4, 0,  4,      8,  MAIN_WIDE_INVIS,
  241.  GID_SETUP,             MSG_SETUP_GAD,          3, 1,    5,     0,      6,      10,     MAIN_WIDE,
  242.  GID_SAVESET,   MSG_SAVESET_GAD,        2, 2,    4,     1,      5,      9,      MAIN_WIDE,
  243.  GID_FUNCTION,  MSG_FUNCTION_GAD,       3, 2,    5,     1,      7,      11,     MAIN_WIDE,
  244.  GID_STATUS,    MSG_STATUS_GAD,         0, 0,    0,     0,      0,      0,      MAIN_STATUS,
  245.   0, 0, 0, 0, 0, 0, 0, 0, 0
  246. };
  247.  
  248.  
  249. /*
  250.  *              Names of gadgets in main window for calculating minimum width
  251.  */
  252. int MainWidthLeftText[] = {
  253.         MSG_PAUSE_GAD,
  254.         MSG_DISABLE_GAD,
  255.         MSG_HIDE_GAD,
  256.         MSG_QUIT_GAD,
  257.         0
  258. };
  259.  
  260. int MainWidthRightText[] = {
  261.         MSG_OPENLOG_GAD,
  262.         MSG_APPENDLOG_GAD,
  263.         MSG_STARTLOG_GAD,
  264.         MSG_SERIALLOG_GAD,
  265.         MSG_CLOSELOG_GAD,
  266.         MSG_SAVESET_GAD,
  267.         MSG_FUNCTION_GAD,
  268.         MSG_SETUP_GAD,
  269.         0
  270. };
  271.  
  272.  
  273. /*
  274.  *              SnoopDos Menus
  275.  *
  276.  *              -- Warning -- 
  277.  *
  278.  *              If you re-arrange the order of menu items or sub-items, you must
  279.  *              make sure to update the MENU_xxxx #define's at the end to reflect
  280.  *              the new ordering!
  281.  *
  282.  *              (Remember to count bars when working out item numbers, and that
  283.  *              menu/item numbers start from zero, not one.)
  284.  */
  285.  
  286. #define DIS                     NM_ITEMDISABLED
  287. #define TICK            (CHECKIT | MENUTOGGLE)
  288. #define TICKED          (CHECKIT | MENUTOGGLE | CHECKED)
  289. #define BAR                     NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL
  290. #define MENU_END        NM_END,  NULL, NULL, 0, 0, NULL
  291.  
  292. #define M(type,title_id,flags,mid) \
  293.         type,   (UBYTE *)(title_id), 0, flags, 0, (APTR)(mid)
  294.  
  295. #define MX(type,title_id,flags,ex,mid) \
  296.         type,   (UBYTE *)(title_id), 0, flags, ex,(APTR)(mid)
  297.  
  298. struct NewMenu MainMenu[] = {
  299.  
  300.         /*
  301.          *              Project menu
  302.          */
  303.         M(      NM_TITLE,       MSG_PROJECT_MENU,                       0,              0),
  304.         M(   NM_ITEM,   MSG_PROJECT_OPENLOG,            0,              MID_OPENLOG),
  305.         M(       NM_ITEM,       MSG_PROJECT_CLOSELOG,           DIS,    MID_CLOSELOG),
  306.              BAR,
  307.         MX(   NM_ITEM,  MSG_PROJECT_PAUSE,                      TICK,   ~8,             MID_PAUSE),
  308.         MX(   NM_ITEM,  MSG_PROJECT_DISABLE,            TICK,   ~16,    MID_DISABLE),
  309.              BAR,
  310.         M(    NM_ITEM,  MSG_PROJECT_STEP,           0,      MID_STEP),
  311.              BAR,
  312.         M(    NM_ITEM,  MSG_PROJECT_PRI,            0,      0),
  313.         MX(    NM_SUB,  MSG_PROJECT_PRI_A,                      TICK,   ~1,             MID_CHANGEPRI),
  314.         MX(    NM_SUB,  MSG_PROJECT_PRI_B,                      TICK,   ~2,             MID_CHANGEPRI),
  315.         MX(    NM_SUB,  MSG_PROJECT_PRI_C,                      TICK,   ~4,             MID_CHANGEPRI),
  316.         MX(    NM_SUB,  MSG_PROJECT_PRI_D,                      TICK,   ~8,             MID_CHANGEPRI),
  317.         MX(    NM_SUB,  MSG_PROJECT_PRI_E,                      TICK,   ~16,    MID_CHANGEPRI),
  318.         MX(    NM_SUB,  MSG_PROJECT_PRI_F,                      TICK,   ~32,    MID_CHANGEPRI),
  319.         MX(    NM_SUB,  MSG_PROJECT_PRI_G,                      TICK,   ~64,    MID_CHANGEPRI),
  320.                  BAR,
  321.         M(       NM_ITEM,       MSG_PROJECT_HELP,                       0,              MID_HELP),
  322.         M(       NM_ITEM,       MSG_PROJECT_ABOUT,                      0,              MID_ABOUT),
  323.              BAR,
  324.         M(       NM_ITEM,       MSG_PROJECT_HIDE,                       0,              MID_HIDE),
  325.              BAR,
  326.         M(       NM_ITEM,       MSG_PROJECT_QUIT,                       0,              MID_QUIT),
  327.  
  328.         /*
  329.          *              Windows menu
  330.          */
  331.         M(  NM_TITLE,   MSG_WINDOWS_MENU,                       0,              0),
  332.         M(   NM_ITEM,   MSG_WINDOWS_SETUP,                      0,              MID_SETUP),
  333.         M(   NM_ITEM,   MSG_WINDOWS_FUNCTION,           0,              MID_FUNCTION),
  334.         M(   NM_ITEM,   MSG_WINDOWS_FORMAT,                     0,              MID_FORMAT),
  335.              BAR,
  336.         M(   NM_ITEM,   MSG_WINDOWS_WIDTH,                      0,      0),
  337.         M(        NM_SUB,       MSG_WINDOWS_WIDTH_1,            0,              MID_SETWIDTH),
  338.         M(        NM_SUB,       MSG_WINDOWS_WIDTH_2,            0,              MID_SETWIDTH),
  339.         M(        NM_SUB,       MSG_WINDOWS_WIDTH_3,            0,              MID_SETWIDTH),
  340.         M(        NM_SUB,       MSG_WINDOWS_WIDTH_4,            0,              MID_SETWIDTH),
  341.         M(        NM_SUB,       MSG_WINDOWS_WIDTH_5,            0,              MID_SETWIDTH),
  342.              BAR,
  343.         M(       NM_ITEM,       MSG_WINDOWS_SPACING,            0,              0),
  344.         MX(       NM_SUB,       MSG_WINDOWS_SPACING_NONE,       TICKED, ~1,     MID_SPACE_NONE),
  345.         MX(       NM_SUB,       MSG_WINDOWS_SPACING_1P,         TICK,   ~2,     MID_SPACE_1P),
  346.         MX(       NM_SUB,       MSG_WINDOWS_SPACING_2P,         TICK,   ~4,     MID_SPACE_2P),
  347.  
  348.         M(       NM_ITEM,       MSG_WINDOWS_REFRESH,            0,              0),
  349.         MX(   NM_SUB,   MSG_WINDOWS_REF_SIMPLE,         TICK,   ~1, MID_REF_SIMPLE),
  350.         MX(   NM_SUB,   MSG_WINDOWS_REF_SMART,          TICKED, ~2, MID_REF_SMART),
  351.  
  352.         M(   NM_ITEM,   MSG_WINDOWS_ALIGN,                      0,              0),
  353.         MX(       NM_SUB,       MSG_WINDOWS_ALIGN_LEFT,         TICKED, ~1, MID_ALIGN_LEFT),
  354.         MX(       NM_SUB,       MSG_WINDOWS_ALIGN_RIGHT,        TICK,   ~2, MID_ALIGN_RIGHT),
  355.  
  356.         M(   NM_ITEM,   MSG_WINDOWS_ROW_QUAL,           0,      0),
  357.         MX(       NM_SUB,       MSG_WINDOWS_ROW_ANY,            TICKED, ~1,  MID_ROWQUAL),
  358.         MX(       NM_SUB,       MSG_WINDOWS_ROW_NONE,           TICK,   ~2,  MID_ROWQUAL),
  359.         MX(       NM_SUB,       MSG_WINDOWS_ROW_SHIFT,          TICK,   ~4,  MID_ROWQUAL),
  360.         MX(       NM_SUB,       MSG_WINDOWS_ROW_ALT,            TICK,   ~8,  MID_ROWQUAL),
  361.         MX(       NM_SUB,       MSG_WINDOWS_ROW_CTRL,           TICK,   ~16, MID_ROWQUAL),
  362.         MX(       NM_SUB,       MSG_WINDOWS_ROW_ALL,            TICK,   ~32, MID_ROWQUAL),
  363.  
  364.              BAR,
  365.         M(       NM_ITEM,       MSG_WINDOWS_SHOWSTATUS,         TICKED, MID_STATUS),
  366.         M(   NM_ITEM,   MSG_WINDOWS_SHOWGADGETS,    TICKED, MID_GADGETS),
  367.              BAR,
  368.         M(       NM_ITEM,       MSG_WINDOWS_AUTO_OPEN,          TICK,   MID_AUTO_OPEN),
  369.         M(   NM_ITEM,   MSG_WINDOWS_DISABLE_HIDDEN, TICK,   MID_DISABLE_HIDDEN),
  370.  
  371.         /*
  372.          *              Settings menu
  373.          */
  374.         M(      NM_TITLE,       MSG_SETTINGS_MENU,                      0,              0),
  375.         M(       NM_ITEM,       MSG_SETTINGS_LOAD,                      0,              MID_LOAD),
  376.         M(       NM_ITEM,       MSG_SETTINGS_SAVE,                      0,              MID_SAVE),
  377.         M(       NM_ITEM,       MSG_SETTINGS_SAVEAS,            0,              MID_SAVEAS),
  378.              BAR,
  379.         M(       NM_ITEM,       MSG_SETTINGS_RESETDEFAULTS,     0,              MID_RESET),
  380.         M(       NM_ITEM,       MSG_SETTINGS_LASTSAVED,         0,              MID_LASTSAVED),
  381.         M(       NM_ITEM,       MSG_SETTINGS_RESTORE,           0,              MID_RESTORE),
  382.              BAR,
  383.         M(       NM_ITEM,       MSG_SETTINGS_CREATEICONS,       TICK,   MID_ICONS),
  384.  
  385.         /*
  386.          *              Buffer menu
  387.          */
  388.         M(      NM_TITLE,       MSG_BUFFER_MENU,                        0,              0),
  389.         M(       NM_ITEM,       MSG_BUFFER_COPYWIN,                     0,              MID_COPYWIN),
  390.         M(       NM_ITEM,       MSG_BUFFER_COPYBUF,                     0,              MID_COPYBUF),
  391.              BAR,
  392.         M(   NM_ITEM,   MSG_BUFFER_SAVEWIN,                     0,              MID_SAVEWIN),
  393.         M(       NM_ITEM,       MSG_BUFFER_SAVEBUF,                     0,              MID_SAVEBUF),
  394.              BAR,
  395.         M(       NM_ITEM,       MSG_BUFFER_CLEARBUF,            0,              MID_CLEARBUF),
  396.  
  397.         MENU_END
  398. };
  399.  
  400. /*
  401.  *              These defines are used when adjusting the menu settings on the fly
  402.  */
  403.  
  404. #define MENU_OPENLOG            FULLMENUNUM(0,  0, NOSUB)
  405. #define MENU_CLOSELOG           FULLMENUNUM(0,  1, NOSUB)
  406. #define MENU_PAUSE                      FULLMENUNUM(0,  3, NOSUB)
  407. #define MENU_DISABLE            FULLMENUNUM(0,  4, NOSUB)
  408. #define MENU_CHANGEPRI          FULLMENUNUM(0,  8, 0)
  409. #define MENU_NUMPRI                     7                               /* Number of menu priorities */
  410. #define MENU_HIDE                       FULLMENUNUM(0,  13, NOSUB)
  411.  
  412. #define MENU_SPACING0           FULLMENUNUM(1,  6, 0)
  413. #define MENU_SPACING1           FULLMENUNUM(1,  6, 1)
  414. #define MENU_SPACING2           FULLMENUNUM(1,  6, 2)
  415. #define MENU_SIMPLE                     FULLMENUNUM(1,  7, 0)
  416. #define MENU_SMART                      FULLMENUNUM(1,  7, 1)
  417. #define MENU_AL_LEFT            FULLMENUNUM(1,  8, 0)
  418. #define MENU_AL_RIGHT           FULLMENUNUM(1,  8, 1)
  419. #define MENU_ROWQUAL_ANY        FULLMENUNUM(1,  9, 0)
  420. #define MENU_ROWQUAL_NONE       FULLMENUNUM(1,  9, 1)
  421. #define MENU_ROWQUAL_SHIFT      FULLMENUNUM(1,  9, 2)
  422. #define MENU_ROWQUAL_ALT        FULLMENUNUM(1,  9, 3)
  423. #define MENU_ROWQUAL_CTRL       FULLMENUNUM(1,  9, 4)
  424. #define MENU_ROWQUAL_ALL        FULLMENUNUM(1,  9, 5)
  425. #define MENU_STATUS                     FULLMENUNUM(1, 11, NOSUB)
  426. #define MENU_GADGETS            FULLMENUNUM(1, 12, NOSUB)
  427. #define MENU_AUTO_OPEN          FULLMENUNUM(1, 14, NOSUB)
  428. #define MENU_DIS_HIDDEN         FULLMENUNUM(1, 15, NOSUB)
  429.  
  430. #define MENU_ICONS                      FULLMENUNUM(2,  8, NOSUB)
  431.  
  432. /*****************************************************************************
  433.  *
  434.  *              Start of functions!
  435.  *
  436.  *****************************************************************************/
  437.  
  438. /*
  439.  *              GetTimeStr(datestamp)
  440.  *
  441.  *              Returns a pointer to a string giving the current time in
  442.  *              hours, mins, and seconds. The string remains valid until
  443.  *              the next time this function is called.
  444.  *
  445.  *              If the datestamp parameter is NULL, then the current date is
  446.  *              used instead.
  447.  */
  448. char *GetTimeStr(struct DateStamp *ds)
  449. {
  450.         static char timestr[20];
  451.         struct DateStamp ourds;
  452.         struct DateTime datetime;
  453.  
  454.         if (!ds) {
  455.                 ds = &ourds;
  456.                 DateStamp(ds);
  457.         }
  458.         datetime.dat_Stamp       = *ds;
  459.         datetime.dat_Format      = FORMAT_DOS;
  460.         datetime.dat_Flags       = 0;
  461.         datetime.dat_StrDay      = NULL;
  462.         datetime.dat_StrDate = NULL;
  463.         datetime.dat_StrTime = timestr;
  464.         DateToStr(&datetime);
  465.         return (timestr);
  466. }
  467.  
  468. /*
  469.  *              UpdateStatus(void)
  470.  *
  471.  *              Determines what the current status is (by examining various
  472.  *              variables) and sets the status line to display an appropriate
  473.  *              message. Call whenever the status may have changed.
  474.  *
  475.  *              Things which can cause a status change: pause/unpause, disable/enable,
  476.  *              open log/close log
  477.  */
  478. void UpdateStatus(void)
  479. {
  480.         if (Paused || Disabled) {
  481.                 char *timestr;
  482.                 char *merid;
  483.                 char *msg;
  484.                 int hour;
  485.                 int min;
  486.  
  487.                 if (Paused) {
  488.                         timestr = GetTimeStr(&PauseDateStamp);
  489.                         msg             = MSG(MSG_STATUS_PAUSED);
  490.                 } else {
  491.                         timestr = GetTimeStr(&DisableDateStamp);
  492.                         msg             = MSG(MSG_STATUS_DISABLED);
  493.                 }
  494.                 hour     = atoi(timestr);
  495.                 min      = atoi(timestr+3);
  496.                 merid    = "AM";
  497.                 if (hour >= 12) {
  498.                         hour -= 12;
  499.                         merid = "PM";
  500.                 }
  501.                 mysprintf(StatusLineText, msg, (hour ? hour : 12), min, merid);
  502.         } else if (LogActive) {
  503.                 int msgid;
  504.  
  505.                 switch (CurrentLogType) {
  506.                         case LT_FILE:                   msgid = MSG_STATUS_LOGFILE;             break;
  507.                         case LT_DEVICE:                 msgid = MSG_STATUS_LOGDEVICE;   break;
  508.                         case LT_DEBUG:                  msgid = MSG_STATUS_LOGDEBUG;    break;
  509.                 }
  510.                 mysprintf(StatusLineText, MSG(msgid), CurrentLogName);
  511.         } else
  512.                 strcpy(StatusLineText, MSG(MSG_STATUS_NORMAL));
  513.         
  514.         if (MainWindow) {
  515.                 GT_SetGadgetAttrs(Gadget[GID_STATUS], MainWindow, NULL,
  516.                                                   GTTX_Text, StatusLineText,
  517.                                                   TAG_DONE);
  518.         }
  519. }
  520.  
  521. /*
  522.  *              SetLogGadget(logmode, refresh)
  523.  *
  524.  *              Displays the appropriate gadget in the main window according to the
  525.  *              type of logging method chosen. If the logfile is currently open,
  526.  *              the "close log" gadget is displayed, otherwise the correct "open"
  527.  *              gadget is displayed according to the logmode: LOGMODE_PROMPT,
  528.  *              LOGMODE_APPEND, LOGMODE_OVERWRITE, or LOGMODE_SERIALPORT
  529.  *
  530.  *              If the refresh parameter is LG_REFRESH, then refresh gadgets.
  531.  *              LG_NOREFRESH means don't refresh gadgets (i.e. assume we're being
  532.  *              called from the gadget creation routine).
  533.  */
  534. void SetLogGadget(int logmode, int refresh)
  535. {
  536.         static int loggadgets[] = {
  537.                 GID_OPENLOG, GID_APPENDLOG, GID_STARTLOG, GID_SERIALLOG,
  538.                 GID_CLOSELOG, 0
  539.         };
  540.         int gadid;
  541.         int i;
  542.  
  543.         if (refresh == LG_REFRESH && !MainWindow)
  544.                 return;
  545.  
  546.         switch (logmode) {
  547.                 case LOGMODE_PROMPT:            gadid = GID_OPENLOG;            break;
  548.                 case LOGMODE_APPEND:            gadid = GID_APPENDLOG;          break;
  549.                 case LOGMODE_SERIALPORT:        gadid = GID_SERIALLOG;          break;
  550.                 case LOGMODE_OVERWRITE:         gadid = GID_STARTLOG;           break;
  551.         }
  552.         if (LogActive)
  553.                 gadid = GID_CLOSELOG;
  554.  
  555.         for (i = 0; loggadgets[i]; i++) {
  556.                 struct Gadget *gad = Gadget[loggadgets[i]];
  557.                 int gadpos;
  558.                 
  559.                 if (refresh == LG_REFRESH)
  560.                          gadpos = RemoveGadget(MainWindow, gad);
  561.  
  562.                 if (loggadgets[i] == gadid)
  563.                         gad->LeftEdge = LogButtonLeft;
  564.                 else
  565.                         gad->LeftEdge = INVIS_LEFT_EDGE;
  566.  
  567.                 if (refresh == LG_REFRESH) {
  568.                         AddGadget(MainWindow, gad, gadpos);
  569.                         RefreshGList(gad, MainWindow, NULL, 1);
  570.                 }
  571.         }
  572.         LogButtonID = gadid;
  573. }
  574.  
  575. /*
  576.  *              WriteLog(string)
  577.  *
  578.  *              Writes the specified string to the logfile, handling buffering
  579.  *              etc. as required.
  580.  *
  581.  *              If string is NULL, then flushes any buffers as necessary.
  582.  */
  583. void WriteLog(char *string)
  584. {
  585.         int len;
  586.  
  587.         if (!LogActive)
  588.                 return;
  589.         
  590.         if (string == NULL) {
  591.                 /*
  592.                  *              Flush output
  593.                  */
  594.                 if (LogFile && LogBuffer && LogBufferPos) {
  595.                         Write(LogFile, LogBuffer, LogBufferPos);
  596.                         LogBufferPos = 0;
  597.                 }
  598.                 return;
  599.         }
  600.  
  601.         if (CurrentLogType == LT_DEBUG) {
  602.                 /*
  603.                  *              Output string to serial terminal, expanding LF's to CR/LF's
  604.                  *              as we go.
  605.                  */
  606.                 while (*string) {
  607.                         if (*string == '\n')
  608.                                 RawPutChar('\r');
  609.                         RawPutChar(*string++);
  610.                 }
  611.                 return;
  612.         }
  613.         len = strlen(string);
  614.         if (LogBuffer) {
  615.                 /*
  616.                  *              Buffered i/o so add string to buffer; if necessary, flush
  617.                  *              buffer first.
  618.                  */
  619.                 if ((LogBufferPos + len) > LogBufferSize) {
  620.                         Write(LogFile, LogBuffer, LogBufferPos);
  621.                         LogBufferPos = 0;
  622.                 }
  623.                 memcpy(LogBuffer + LogBufferPos, string, len);
  624.                 LogBufferPos += len;
  625.         } else {
  626.                 /*
  627.                  *              Plain unbuffered i/o
  628.                  */
  629.                 Write(LogFile, string, strlen(string));
  630.         }
  631. }
  632.  
  633. /*
  634.  *              CloseLog()
  635.  *
  636.  *              Closes the currently open logfile (if any)
  637.  */
  638. void CloseLog(void)
  639. {
  640.         char msg[100];
  641.  
  642.         if (!LogActive)
  643.                 return;
  644.         
  645.         mysprintf(msg, MSG(MSG_LOG_STOP), GetTimeStr(NULL));
  646.         WriteLog(msg);
  647.         WriteLog(NULL); /* Flush output */
  648.         LogActive = 0;
  649.         SetLogGadget(DefaultLogMode, LG_REFRESH);
  650.         UpdateStatus();
  651.         SetMenuOptions();
  652.         if (LogFile) {
  653.                 int oldpaused = Paused;
  654.  
  655.                 Paused = 0;
  656.                 Close(LogFile);
  657.                 LogFile = NULL;
  658.                 Paused = oldpaused;
  659.         }
  660.         if (LogBuffer) {
  661.                 FreeMem(LogBuffer, LogBufferSize);
  662.                 LogBuffer = NULL;
  663.         }
  664. }
  665.  
  666. /*
  667.  *              WriteLogHeader()
  668.  *
  669.  *              Writes the standard SnoopDos header line to the logfile using the
  670.  *              current logfile format
  671.  */
  672. void WriteLogHeader(void)
  673. {
  674.         FormatEvent(LogEFormat, NULL, BufferLine+1, 0, LogWidth - 1);
  675.         BufferLine[0]              = ' ';
  676.         BufferLine[LogWidth+1] = '\n';
  677.         BufferLine[LogWidth+2] = 0;
  678.         WriteLog(BufferLine);
  679.         UnderlineTitles(LogEFormat, BufferLine+1, '-');
  680.         BufferLine[LogWidth+1] = '\n';
  681.         WriteLog(BufferLine);
  682. }
  683.  
  684. /*
  685.  *              OpenLog(mode, filename)
  686.  *
  687.  *              Opens the named file for logging. If a log file is already open,
  688.  *              then closes the existing file first.
  689.  *
  690.  *              Mode is set to:
  691.  *
  692.  *                      LOGMODE_PROMPT     - Prompt user if file exits for append/overwrite
  693.  *                      LOGMODE_APPEND     - Automatically append to file if it exists
  694.  *                      LOGMODE_OVERWRITE  - Automatically overwrite file it if exitsts
  695.  *                      LOGMODE_SERIALPORT - Direct output to debugger on serial port
  696.  *
  697.  *              Filename is ignored if mode is set to LOGMODE_SERIALPORT.
  698.  *
  699.  *              Returns 1 for success, 0 for failure (couldn't open file?)
  700.  */
  701. int OpenLog(int logmode, char *filename)
  702. {
  703.         APTR oldwinptr = *TaskWindowPtr;
  704.         char startmsg[100];
  705.         char timestr[20];
  706.         char datestr[20];
  707.         char weekday[20];
  708.         struct DateTime datetime;
  709.         int pausestate = Paused;
  710.  
  711.         Paused = 0;
  712.         if (LogActive)
  713.                 CloseLog();
  714.         
  715.         if (logmode == LOGMODE_SERIALPORT) {
  716.                 CurrentLogType = LT_DEBUG;
  717.         } else {
  718.                 int filemode;
  719.                 int buffer = 0;
  720.  
  721.                 strcpy(CurrentLogName, filename);
  722.                 *TaskWindowPtr = (APTR)-1;
  723.                 if (IsFileSystem(filename))
  724.                         CurrentLogType = LT_FILE;
  725.                 else
  726.                         CurrentLogType = LT_DEVICE;
  727.                 *TaskWindowPtr = oldwinptr;
  728.  
  729.                 if (logmode == LOGMODE_PROMPT && CurrentLogType == LT_FILE) {
  730.                         BPTR lock = Lock(filename, ACCESS_READ);
  731.  
  732.                         if (lock) {
  733.                                 UnLock(lock);
  734.                                 switch (GetResponse(MSG(MSG_REQ_APPEND_OVERWRITE_CANCEL),
  735.                                                                         MSG(MSG_REQ_FILE_EXISTS), filename)) {
  736.  
  737.                                         case 1:         filemode = MODE_READWRITE;              break;
  738.                                         case 2:         filemode = MODE_NEWFILE;                break;
  739.                                         default:        goto open_okay;
  740.                                 }
  741.                         } else
  742.                                 filemode = MODE_NEWFILE;
  743.                 } else if (logmode == LOGMODE_APPEND)
  744.                         filemode = MODE_READWRITE;
  745.                 else
  746.                         filemode = MODE_NEWFILE;
  747.  
  748.                 LogFile = Open(filename, filemode);
  749.                 if (!LogFile)
  750.                         goto open_failed;
  751.  
  752.                 if (filemode == MODE_READWRITE) {
  753.                         Seek(LogFile, 0, OFFSET_END);
  754.                         Write(LogFile, "\n", 1); /* Leave space between multiple logs */
  755.                 }
  756.  
  757.                 /*
  758.                  *              Now select buffer type for file according to gadget and
  759.                  *              file type.
  760.                  */
  761.                 switch (CurSettings.Setup.FileIOType) {
  762.                         case FILE_AUTOMATIC:
  763.                                 if (CurrentLogType == LT_FILE)
  764.                                         buffer = 1;
  765.                                 break;
  766.  
  767.                         case FILE_IMMEDIATE:            buffer = 0;     break;
  768.                         case FILE_BUFFERED:                     buffer = 1; break;
  769.                 }
  770.                 if (buffer) {
  771.                         /*
  772.                          *              Allocate a buffer for file i/o. Doesn't matter if
  773.                          *              it fails -- we just drop back to unbuffered file
  774.                          *              i/o instead.
  775.                          */
  776.                         LogBufferSize = LOGBUFFER_SIZE;
  777.                         LogBufferPos  = 0;
  778.                         LogBuffer     = AllocMem(LogBufferSize, MEMF_ANY);
  779.                 }
  780.         }
  781.         LogActive = 1;
  782.         SetLogGadget(DefaultLogMode, LG_REFRESH);
  783.         UpdateStatus();
  784.         SetMenuOptions();
  785.  
  786.         DateStamp(&datetime.dat_Stamp);
  787.         datetime.dat_Format      = FORMAT_DOS;
  788.         datetime.dat_Flags       = 0;
  789.         datetime.dat_StrDay      = weekday;
  790.         datetime.dat_StrDate = datestr;
  791.         datetime.dat_StrTime = timestr;
  792.         DateToStr(&datetime);
  793.         mysprintf(startmsg, MSG(MSG_LOG_START), weekday, datestr, timestr);
  794.  
  795.         /*
  796.          *              Now select appropriate output format for log
  797.          */
  798.         LogWidth = ParseFormatString(LogFormat, LogEFormat, MAX_FORM_LEN);
  799.         if (!LogWidth)
  800.                 LogWidth = ParseFormatString(BufFormat, LogEFormat, MAX_FORM_LEN);
  801.         WriteLog(startmsg);
  802.         WriteLogHeader();
  803. open_okay:
  804.         Paused = pausestate;
  805.         return (1);
  806.  
  807. open_failed:
  808.         Paused = pausestate;
  809.         return (0);
  810. }
  811.  
  812. /*
  813.  *              SaveBuffer(savetype, savename, overwrite)
  814.  *
  815.  *              This function saves the contents of some or all of the current
  816.  *              buffer to a disk file or to the clipboard.
  817.  *
  818.  *              savetype is SAVEBUF_WINDOW to save only that portion of the
  819.  *              buffer that is currently displayed in the main window, or
  820.  *              SAVEBUF_ALL to save the entire buffer.
  821.  *
  822.  *              savename is a pointer to a disk filename, or SAVEBUF_CLIPBOARD to
  823.  *              save to the clipboard.device.
  824.  *
  825.  *              overwrite is SAVEBUF_OVERWRITE to force an existing file to be
  826.  *              overwritten automatically or SAVEBUF_PROMPT to let the user
  827.  *              choose whether to overwrite using a requester. If the user
  828.  *              chooses not to overwrite, then success is returned.
  829.  *
  830.  *              Returns 1 for success or 0 for failure.
  831.  */
  832. int SaveBuffer(int savetype, char *savename, int overwrite)
  833. {
  834.         struct IOClipReq *clipreq;
  835.         struct MsgPort   *clipport;
  836.         BPTR  savefile;
  837.         UBYTE *savebuf;
  838.         Event *curev;
  839.         ULONG curpos;
  840.         int startseq;
  841.         int curseq;
  842.         int endseq;
  843.         int leftcol;
  844.         int rightcol;
  845.         int totalsize;
  846.         int width;
  847.         int numlines;
  848.  
  849.         if (savetype == SAVEBUF_WINDOW) {
  850.                 /*
  851.                  *              Save only visible portion of buffer
  852.                  */
  853.                 startseq        = TopSeq;
  854.                 curev           = TopEvent;
  855.                 endseq          = BottomSeq;
  856.                 leftcol         = LeftCol;
  857.                 rightcol        = RightCol;
  858.         } else {
  859.                 /*
  860.                  *              Save entire buffer
  861.                  */
  862.                 startseq        = RealFirstSeq;
  863.                 curev           = HeadNode(&EventList);
  864.                 endseq          = EndSeq;
  865.                 leftcol         = 0;
  866.                 rightcol        = BufferWidth - 1;
  867.         }
  868.  
  869.         savebuf = AllocMem(SAVEBUFFER_SIZE, MEMF_ANY);
  870.         if (!savebuf)
  871.                 return (0);
  872.  
  873.         curpos = 0;
  874.  
  875.         /*
  876.          *              Now try and open our output device
  877.          */
  878.         if (savename == SAVEBUF_CLIPBOARD) {
  879.                 /*
  880.                  *              Open clipboard
  881.                  */
  882.                 clipport = CreateMsgPort();
  883.                 if (!clipport) {
  884.                         FreeMem(savebuf, SAVEBUFFER_SIZE);
  885.                         return (0);
  886.                 }
  887.                 clipreq = (struct IOClipReq *)CreateIORequest(clipport, sizeof(*clipreq));
  888.                 if (!clipreq) {
  889.                         FreeMem(savebuf, SAVEBUFFER_SIZE);
  890.                         DeleteMsgPort(clipport);
  891.                         return (0);
  892.                 }
  893.                 if (OpenDevice("clipboard.device", 0, (void *)clipreq, 0)) {
  894.                         FreeMem(savebuf, SAVEBUFFER_SIZE);
  895.                         DeleteIORequest((struct IORequest *)clipreq);
  896.                         DeleteMsgPort(clipport);
  897.                         return (0);
  898.                 }
  899.                 clipreq->io_Error       = 0;
  900.                 clipreq->io_Offset      = 0;
  901.                 clipreq->io_ClipID      = 0;
  902.         } else {
  903.                 /*
  904.                  *              Open log file
  905.                  */
  906.                 int filemode = MODE_NEWFILE;;
  907.  
  908.                 if (overwrite == SAVEBUF_PROMPT) {
  909.                         BPTR lk = Lock(savename, ACCESS_READ);
  910.  
  911.                         if (lk) {
  912.                                 UnLock(lk);
  913.                                 switch (GetResponse(MSG(MSG_REQ_APPEND_OVERWRITE_CANCEL),
  914.                                                                     MSG(MSG_REQ_FILE_EXISTS), savename)) {
  915.  
  916.                                         case 1:         filemode = MODE_READWRITE;              break;
  917.                                         case 2:         filemode = MODE_NEWFILE;                break;
  918.                                         default:        FreeMem(savebuf, SAVEBUFFER_SIZE);
  919.                                                                 return (1);
  920.                                 }
  921.                         }
  922.                 }
  923.                 savefile = Open(savename, filemode);
  924.                 if (!savefile) {
  925.                         FreeMem(savebuf, SAVEBUFFER_SIZE);
  926.                         return (0);
  927.                 }
  928.                 if (filemode == MODE_READWRITE) {
  929.                         Seek(savefile, 0, OFFSET_END);
  930.                         Write(savefile, "\n", 1);       /* Leave blank line between appends */
  931.                 }
  932.         }
  933.  
  934.         /*
  935.          *              Now calculate how many lines there are to be saved in the
  936.          *              buffer. There will be as many lines as were selected from
  937.          *              startseq to endseq (inclusive) plus two additional lines for
  938.          *              the header and underline. Each line will have (rightcol-leftcol+1)
  939.          *              characters on it, plus one additional character for the newline.
  940.          *              We need to calculate all this in advance because the header
  941.          *              written for the clipboard includes the number of characters of
  942.          *              text to be written.
  943.          */
  944.         width = (rightcol - leftcol + 1);
  945.  
  946.         if (IsListEmpty(&EventList))
  947.                 numlines = 2;
  948.         else
  949.                 numlines = (endseq - startseq + 1) + 2;
  950.  
  951.         totalsize = (width + 1) * numlines;
  952.  
  953.         if (savename == SAVEBUF_CLIPBOARD) {
  954.                 /*
  955.                  *              Write the clipboard FTXT header into the buffer. We
  956.                  *              need to write the amount of text, but we also need
  957.                  *              to write the total size of the clip (including header)
  958.                  *              rounded to a word boundary.
  959.                  */
  960.                 int cliplen = (totalsize + 13) & ~1;    /* Includes header */
  961.  
  962.                 memcpy(savebuf, "FORM....FTXTCHRS....", 20);
  963.                 *((ULONG *)&savebuf[4])  = cliplen;
  964.                 *((ULONG *)&savebuf[16]) = totalsize;
  965.                 curpos += 20;
  966.         }
  967.         /*
  968.          *              Next, output the two header lines (the column headings plus
  969.          *              the underline)
  970.          */
  971.         FormatEvent(BufferEFormat, NULL, savebuf + curpos, leftcol, rightcol);
  972.         curpos += width;
  973.         savebuf[curpos++] = '\n';
  974.  
  975.         UnderlineTitles(BufferEFormat, BufferLine, '-');
  976.         memcpy(savebuf + curpos, BufferLine + leftcol, width);
  977.         curpos += width;
  978.         savebuf[curpos++] = '\n';
  979.  
  980.         /*
  981.          *              Now walk through the lines in the buffer, outputting each line
  982.          *              one at a time.
  983.          */
  984.         curseq     = startseq;
  985.         totalsize -= (width + width + 2);       /* Allow for header */
  986.  
  987.         while (totalsize > 0) {
  988.                 /*
  989.                  *              First check if we have enough room in the buffer for
  990.                  *              the next line -- if not, then flush the buffer
  991.                  */
  992.                 if ((curpos + width + 2) >= SAVEBUFFER_SIZE) {
  993.                         if (savename == SAVEBUF_CLIPBOARD) {
  994.                                 clipreq->io_Command = CMD_WRITE;
  995.                                 clipreq->io_Data        = savebuf;
  996.                                 clipreq->io_Length      = curpos;
  997.                                 DoIO((struct IORequest *)clipreq);
  998.                         } else
  999.                                 Write(savefile, savebuf, curpos);
  1000.                         curpos = 0;
  1001.                 }
  1002.                 /*
  1003.                  *              The next section must be completely enclosed in
  1004.                  *              ObtainSemaphore/ReleaseSemaphore to ensure the
  1005.                  *              buffer doesn't change under our feet
  1006.                  */
  1007.                 ObtainSemaphore(&BufSem);
  1008.                 if (curseq < RealFirstSeq) {
  1009.                         /*
  1010.                          *              Uhoh -- the list has scrolled past us while we
  1011.                          *              were outputting it; catch up as best we can.
  1012.                          */
  1013.                         curev = HeadNode(&EventList);
  1014.                         if (NextNode(curev))
  1015.                                  curseq = curev->seqnum;
  1016.                         else
  1017.                                  curseq = endseq + 1;
  1018.                 }
  1019.                 if (curseq > endseq) {
  1020.                         /*
  1021.                          *              We've somehow run out of lines -- this should never
  1022.                          *              happen, but you never know. For disk files, we can
  1023.                          *              just avoid outputting anything else. For the
  1024.                          *              clipboard, we need to output something, so we just
  1025.                          *              output a line of spaces.
  1026.                          */
  1027.                         if (savename != SAVEBUF_CLIPBOARD)
  1028.                                 break;
  1029.                         memset(savebuf + curpos, ' ', width);
  1030.                 } else {
  1031.                         FormatEvent(BufferEFormat, curev, savebuf + curpos,
  1032.                                                 leftcol, rightcol);
  1033.                         curev = NextNode(curev);
  1034.                         curseq++;
  1035.                 }
  1036.                 curpos += width;
  1037.                 savebuf[curpos++] = '\n';
  1038.                 totalsize -= (width + 1);
  1039.                 ReleaseSemaphore(&BufSem);
  1040.         }
  1041.  
  1042.         /*
  1043.          *              Now we've output the entire save region, so just flush anything
  1044.          *              left in the save buffer and we're done.
  1045.          */
  1046.         if (savename == SAVEBUF_CLIPBOARD) {
  1047.                 /*
  1048.                  *              Make sure out total output is word-aligned, otherwise
  1049.                  *              Bad Things will happen
  1050.                  */
  1051.                 if (totalsize & 1)
  1052.                         savebuf[curpos++] = '\0';
  1053.  
  1054.                 if (curpos > 0) {
  1055.                         clipreq->io_Command = CMD_WRITE;
  1056.                         clipreq->io_Data        = savebuf;
  1057.                         clipreq->io_Length      = curpos;
  1058.                         DoIO((struct IORequest *)clipreq);
  1059.                 }
  1060.                 clipreq->io_Command = CMD_UPDATE;
  1061.                 DoIO((struct IORequest *)clipreq); /* Make data publically available */
  1062.  
  1063.                 CloseDevice((struct IORequest *)clipreq);
  1064.                 DeleteIORequest((struct IORequest *)clipreq);
  1065.                 DeleteMsgPort(clipport);
  1066.         } else {
  1067.                 if (curpos)
  1068.                         Write(savefile, savebuf, curpos);
  1069.                 Close(savefile);
  1070.         }
  1071.         FreeMem(savebuf, SAVEBUFFER_SIZE);
  1072.         return (1);
  1073. }
  1074.  
  1075. /*
  1076.  *              SetMonitorMode(type)
  1077.  *
  1078.  *              Sets the current monitor mode to the specified type. This will
  1079.  *              be one of the following three:
  1080.  *
  1081.  *                      MONITOR_NORMAL
  1082.  *                      MONITOR_PAUSED
  1083.  *                      MONITOR_DISABLED
  1084.  *
  1085.  *              Automatically takes care of updating the gadget imagery and
  1086.  *              menu options to reflect the mode. Also ignores attempts to
  1087.  *              Pause() if the main window isn't currently open. Note that
  1088.  *              the three modes are mutually exclusive.
  1089.  *
  1090.  *              The status line is also updated to reflect the current mode.
  1091.  *              The global MonitorType reflects the currently selected mode
  1092.  *              after this call.
  1093.  *
  1094.  *              Important: any call which may possible spawn a subtask
  1095.  *              (e.g. showing an ASL file requester etc.) must force the Paused
  1096.  *              variable to 0 before hand and then restore it to its original
  1097.  *              setting afterwards. This ensures that any other tasks that were
  1098.  *              already paused remain paused, but any new tasks that make calls
  1099.  *              during the activity will not be paused.
  1100.  *
  1101.  *              This isn't perfect, but it gives about 99% certainty of avoiding
  1102.  *              deadlocks, while still ensuring that any existing paused tasks
  1103.  *              (normally just one or two) remain paused. Thus, if someone has
  1104.  *              paused some stuff so they can open a log file, turn on the
  1105.  *              printer, or whatever, it will remain paused. (The possibility of
  1106.  *              deadlock comes about if our external file i/o somehow causes
  1107.  *              another process to make a call to one of the monitored SnoopDos
  1108.  *              functions, e.g. OpenLibrary or OpenDevice).
  1109.  *
  1110.  *              The alternative is to completely disable pausing on such calls, in
  1111.  *              which case vast quantities of output might be generated while the
  1112.  *              person is messing around in the file requester oblivious.
  1113.  *
  1114.  *              NOTE: It is impossible to enter PAUSED mode unless the SnoopDos
  1115.  *              window is open -- this is to prevent situations where you freeze
  1116.  *              the system (e.g. it's waiting for you to unpause, but you can't
  1117.  *              because you can't call up the main window due to something else.)
  1118.  */
  1119. void SetMonitorMode(int modetype)
  1120. {
  1121.         int paused   = 0;
  1122.         int disabled = 0;
  1123.         int changed  = 0;
  1124.  
  1125.         if (!MainWindow && modetype == MONITOR_PAUSED)
  1126.                 return;
  1127.  
  1128.         if (modetype == MONITOR_DISABLED)
  1129.                 disabled = 1;
  1130.         else if (modetype == MONITOR_PAUSED)
  1131.                 paused = 1;
  1132.  
  1133.         /*
  1134.          *              Now update main window gadgets if necessary
  1135.          */
  1136.         if (MainWindow) {
  1137.                 if (paused != Paused) {
  1138.                         ShowGadget(MainWindow, Gadget[GID_PAUSE],
  1139.                                            (paused ? GADGET_DOWN : GADGET_UP));
  1140.                 }
  1141.                 if (disabled != Disabled) {
  1142.                         ShowGadget(MainWindow, Gadget[GID_DISABLE],
  1143.                                            (disabled ? GADGET_DOWN : GADGET_UP));
  1144.                 }
  1145.         }
  1146.  
  1147.         /*
  1148.          *              Next,  make the changes actually take effect.
  1149.          */
  1150.         if (paused != Paused) {
  1151.                 DateStamp(&PauseDateStamp);
  1152.                 if (paused)
  1153.                         ObtainSemaphore(&PauseSem);
  1154.                 else
  1155.                         ReleaseSemaphore(&PauseSem);
  1156.                 Paused  = paused;
  1157.                 changed = 1;
  1158.         }
  1159.         if (disabled != Disabled) {
  1160.                 DateStamp(&DisableDateStamp);
  1161.                 Disabled = disabled;
  1162.                 changed  = 1;
  1163.                 LoadFuncSettings(&CurSettings.Func);    /* Update patches state */
  1164.  
  1165.                 if (LogActive) {
  1166.                         char msg[100];
  1167.  
  1168.                         mysprintf(msg, MSG(Disabled ? MSG_LOG_DISABLED : MSG_LOG_ENABLED),
  1169.                                                    GetTimeStr(NULL));
  1170.                         WriteLog(msg);
  1171.                         WriteLog(NULL); /* Flush output to keep everything up-to-date */
  1172.                 }
  1173.         }
  1174.         if (changed) {
  1175.                 if (LogActive)          /* Flush log file when Paused */
  1176.                         WriteLog(NULL);
  1177.                 UpdateStatus();
  1178.                 SetMenuOptions();
  1179.         }
  1180.         MonitorType = modetype;
  1181. }
  1182.  
  1183. /*
  1184.  *              SingleStep()
  1185.  *
  1186.  *              Undoes Pause just long enough for all currently waiting tasks
  1187.  *              to execute a single monitored event. Usually, this will just
  1188.  *              produce a single event in the event buffer.
  1189.  *
  1190.  *              Why would you want to do this? Because it's a real handy way
  1191.  *              of single-stepping through a program's actions at startup,
  1192.  *              especially if it happens to be crashing after doing a particular
  1193.  *              action!
  1194.  *
  1195.  *              We implement the singlestep by freeing up the Pause semaphore
  1196.  *              and then immediately trying to recapture it. We rely on the
  1197.  *              fact that Exec schedules semaphore operations on a strictly
  1198.  *              first-come first-served basis, so as soon as we free it,
  1199.  *              all the waiting tasks are guaranteed to run before we can
  1200.  *              recapture it. We move ourselves to a high priority temporarily
  1201.  *              to ensure that nobody gets a chance to run two events before
  1202.  *              we can regain control again.
  1203.  *
  1204.  *              If we weren't in pause mode before, we will be when we exit.
  1205.  *
  1206.  */
  1207. void SingleStep(void)
  1208. {
  1209.         int oldpri;
  1210.  
  1211.         if (!MainWindow)
  1212.                 return;
  1213.  
  1214.         if (!Paused)
  1215.                 SetMonitorMode(MONITOR_PAUSED);
  1216.         
  1217.         WriteLog(NULL);         /* Make sure logfile is up to date! */
  1218.  
  1219.         /*
  1220.          *              To ensure that we get the semaphore back as soon as we 
  1221.          *              possibly can (i.e. let all the waiting tasks run but nobody
  1222.          *              else), we bump our priority up temporarily
  1223.          */
  1224.         oldpri = SetTaskPri(SysBase->ThisTask, 25);
  1225.         ReleaseSemaphore(&PauseSem);
  1226.         ObtainSemaphore(&PauseSem);
  1227.         SetTaskPri(SysBase->ThisTask, oldpri);
  1228. }
  1229.  
  1230. /*
  1231.  *              CheckForScreen()
  1232.  *
  1233.  *              Checks if we've already got a SnoopDos screen open -- if we do,
  1234.  *              then returns TRUE. If we don't, then tries to lock the current
  1235.  *              screen and returns TRUE. If we can't lock the screen, then
  1236.  *              displays an error message and returns FALSe.
  1237.  */
  1238. int CheckForScreen(void)
  1239. {
  1240.         if (!SnoopScreen) {
  1241.                 SetupScreen();
  1242.                 if (!SnoopScreen) {
  1243.                         ShowError(MSG(MSG_ERROR_NO_SCREEN));
  1244.                         return (FALSE);
  1245.                 }
  1246.         }
  1247.         return (TRUE);
  1248. }
  1249.  
  1250. /*
  1251.  *              ShowSnoopDos()
  1252.  *
  1253.  *              Attempts to open SnoopDos on the user's current preferred screen.
  1254.  *              If SnoopDos is already running, but on a different screen, then
  1255.  *              it is moved to this screen. Any open SnoopDos windows are moved,
  1256.  *              though any AmigaGuide help remains on the previous screen until
  1257.  *              the next time help is requested.
  1258.  *
  1259.  *              Fails if we can't lock the new screen for some reason (this
  1260.  *              should never happen!)
  1261.  */
  1262. int ShowSnoopDos(void)
  1263. {
  1264.         struct Screen *prevscr  = SnoopScreen;
  1265.         struct Window *prevfunc = FuncWindow;
  1266.         struct Window *prevform = FormWindow;
  1267.         struct Window *prevset  = SetWindow;
  1268.  
  1269.         if (!SetupScreen()) {
  1270.                 ShowError(MSG(MSG_ERROR_NO_SCREEN));
  1271.                 return (FALSE);
  1272.         }
  1273.         RemoveProgramFromWorkbench();
  1274.         ScreenToFront(SnoopScreen);
  1275.         if (prevscr == SnoopScreen) {
  1276.                 /*
  1277.                  *              We were already running, and we're on the
  1278.                  *              same screen, so nothing else to do. For good
  1279.                  *              measure, we bring the main window to the
  1280.                  *              foreground, however.
  1281.                  */
  1282.                 OpenMainWindow();
  1283.                 return (TRUE);
  1284.         }
  1285.         if (prevscr != NULL) {
  1286.                 /*
  1287.                  *              We're moving from a previous screen to this new screen,
  1288.                  *              so bring all the windows with us
  1289.                  */
  1290.                 CleanupSubWindows();
  1291.                 CloseMainWindow();
  1292.                 OpenMainWindow();
  1293.                 if (prevset)    OpenSettingsWindow();
  1294.                 if (prevfunc)   OpenFunctionWindow();
  1295.                 if (prevform)   OpenFormatWindow();
  1296.         } else {
  1297.                 /*
  1298.                  *              We're opening on an entirely new screen -- if
  1299.                  *              DisableOnHide is true, then we need to restore
  1300.                  *              the saved state of Disable/Enable (since it will
  1301.                  *              have been disabled while in the background)
  1302.                  */
  1303.                 if (DisableOnHide)
  1304.                         SetMonitorMode(LastKnownState);
  1305.                 OpenMainWindow();
  1306.         }
  1307.         return (TRUE);
  1308. }
  1309.  
  1310. /*
  1311.  *              HideSnoopDos()
  1312.  *
  1313.  *              Removes all SnoopDos windows from the screen. If DisableOnHide
  1314.  *              is true, then SnoopDos is put into a disabled state. Otherwise,
  1315.  *              if we're currently paused, then SnoopDos is unpaused (since
  1316.  *              otherwise, it would be easy to get into a situation where
  1317.  *              everything was frozen and you could do nothing to bring
  1318.  *              SnoopDos back to life).
  1319.  *
  1320.  *              If commodities library hasn't been opened, then we don't hide
  1321.  *              since that would make it difficult to resurrect SnoopDos again.
  1322.  *              However, if commodities library is open but the user has given
  1323.  *              an invalid hotkey, we do hide -- they can always use commodities
  1324.  *              exchange to reactivate it.
  1325.  */
  1326. void HideSnoopDos(void)
  1327. {
  1328.         LastKnownState = MonitorType;
  1329.  
  1330.         if (CurSettings.Setup.HideMethod == HIDE_NONE)
  1331.                 return;
  1332.  
  1333.         CleanupSubWindows();
  1334.         CloseMainWindow();
  1335.         CleanupScreen();
  1336.         if (DisableOnHide)
  1337.                 SetMonitorMode(MONITOR_DISABLED);
  1338.         else if (Paused)
  1339.                 SetMonitorMode(MONITOR_NORMAL);
  1340.         if (CurSettings.Setup.HideMethod != HIDE_INVIS)
  1341.                 AddProgramToWorkbench(CurSettings.Setup.HideMethod);
  1342. }
  1343.  
  1344. /*
  1345.  *              InitMenus()
  1346.  *
  1347.  *              Should be called once as soon as the language file has been
  1348.  *              read in. Walks down the menu structure and replaces the
  1349.  *              message ID's with actual pointers to the message.
  1350.  */
  1351. void InitMenus(void)
  1352. {
  1353.         struct NewMenu *menu;
  1354.  
  1355.         for (menu = &MainMenu[0]; menu->nm_Type != NM_END; menu++) {
  1356.                 if (menu->nm_Label && menu->nm_Label != NM_BARLABEL) {
  1357.                         char *msg = MSG((ULONG)(menu->nm_Label));
  1358.  
  1359.                         menu->nm_Label = msg + 2;
  1360.                         if (*msg && *msg != ' ')
  1361.                                 menu->nm_CommKey = msg;
  1362.                 }
  1363.         }
  1364. }
  1365.  
  1366. /*
  1367.  *              CalcMinMainSize(gadgetfa, bufferfa, &width, &height)
  1368.  *
  1369.  *              Fills in width and height with the minimum allowable dimensions
  1370.  *              the main window can obtain using the specified gadget and buffer
  1371.  *              fonts. Returns TRUE for success, FALSE for failure (e.g. window
  1372.  *              to big to fit on screen using current fonts).
  1373.  */
  1374. BOOL CalcMinMainSize(struct TextAttr *gadgetfa, struct TextAttr *bufferfa,
  1375.                                          int *pwidth, int *pheight)
  1376. {
  1377.         struct TextFont *gfont;
  1378.         struct TextFont *bfont;
  1379.         int widthleft;
  1380.         int widthright;
  1381.         int width;
  1382.         int height;
  1383.         int fonty;
  1384.         int bfonty;
  1385.         int bfontx;
  1386.         int bordright = SizeImage->Width;
  1387.         int bordbot   = SizeImage->Height;
  1388.  
  1389.         gfont = MyOpenFont(gadgetfa);
  1390.         if (!gfont)
  1391.                 return (FALSE);
  1392.  
  1393.         bfont = MyOpenFont(bufferfa);
  1394.         if (!bfont) {
  1395.                 CloseFont(gfont);
  1396.                 return (FALSE);
  1397.         }
  1398.         if (bfont->tf_Flags & FPF_PROPORTIONAL) {
  1399.                 CloseFont(bfont);
  1400.                 CloseFont(gfont);
  1401.                 return (FALSE);
  1402.         }
  1403.         fonty           = gfont->tf_YSize;
  1404.         bfonty          = bfont->tf_YSize;
  1405.         bfontx      = bfont->tf_XSize;
  1406.  
  1407.         widthleft   = MaxTextLen(gfont, MainWidthLeftText)  + 12;
  1408.         widthright  = MaxTextLen(gfont, MainWidthRightText) + 12;
  1409.  
  1410.         width  = (widthleft + widthright + MAIN_MARGIN) * 2 +
  1411.                          BorderLeft + bordright;
  1412.         height = TitlebarHeight + (bfonty + BoxInterGap) * 3 +
  1413.                          bordbot + 20 + (fonty + 10) * 3;
  1414.  
  1415.         CloseFont(bfont);
  1416.         CloseFont(gfont);
  1417.  
  1418.         if (width > ScreenWidth || height > ScreenHeight)
  1419.                 return (FALSE);
  1420.  
  1421.         *pwidth  = width;
  1422.         *pheight = height;
  1423.         return (TRUE);
  1424. }
  1425.  
  1426. /*
  1427.  *              SetupBufferRastPort()
  1428.  *
  1429.  *              Sets up the main window rastport to have the correct font and
  1430.  *              colours for rendering. Should always be called before OutputBufLine
  1431.  *              if gadget operations may have occurred since the last redraw.
  1432.  */
  1433. void SetupBufferRastPort(void)
  1434. {
  1435.         struct RastPort *rport = MainWindow->RPort;
  1436.  
  1437.         SetAPen(rport, 1);
  1438.         SetBPen(rport, 0);
  1439.         SetDrMd(rport, JAM2);
  1440.         SetFont(rport, BufferFont);
  1441. }
  1442.  
  1443. /*
  1444.  *              DrawHeaderLine()
  1445.  *
  1446.  *              Redraws the current event header line in the top part of the window
  1447.  *
  1448.  *              If a column is currently selected for dragging, then draws the
  1449.  *              column heading in white to indicate it's active.
  1450.  */
  1451. void DrawHeaderLine(void)
  1452. {
  1453.         struct RastPort *rport = MainWindow->RPort;
  1454.         int numcols = RightCol - LeftCol + 1;
  1455.  
  1456.         if (ClearMainRHS)
  1457.                 numcols = BoxCols;
  1458.  
  1459.         SetupBufferRastPort();
  1460.         FormatEvent(BufferEFormat, NULL, BufferLine, LeftCol, LeftCol+numcols-1);
  1461.  
  1462.         /*
  1463.          *              We mark the very last position on the header line with a single
  1464.          *              dot, which can then be grabbed by the user to resize the width
  1465.          *              of the last column. We don't overwrite the actual text of the
  1466.          *              final header, if it's narrow.
  1467.          *
  1468.          *              If the columns are currently being dragged, we display a | instead
  1469.          *              of a dot, to make it easier to spot, and we display it even if
  1470.          *              it would overwrite some of the final heading.
  1471.          */
  1472.         if (DraggingColumn) {
  1473.                 /*
  1474.                  *              Draw the currently selected column with a white heading,
  1475.                  *              and the other stuff with a black heading
  1476.                  */
  1477.                 int selectwidth = SelectEvent->width;
  1478.  
  1479.                 if ((SelectEvent+1)->type != EF_END) {
  1480.                         /*
  1481.                          *              If we're not highlighting the last column already
  1482.                          *              then indicate the end of the last column with a
  1483.                          *              little vertical bar
  1484.                          */
  1485.                         BufferLine[BufferWidth - LeftCol - 1] = '|';
  1486.                 }
  1487.                 if (SelectStartCol > LeftCol) {
  1488.                         /*
  1489.                          *              Draw black portion to left of selected column
  1490.                          */
  1491.                         int width = MIN(SelectStartCol - LeftCol, numcols);
  1492.  
  1493.                         Move(rport, BoxInLeft, BoxHeaderLine);
  1494.                         Text(rport, BufferLine, width);
  1495.                 }
  1496.                 if ((SelectStartCol + selectwidth) < (LeftCol + numcols)) {
  1497.                         /*
  1498.                          *              Draw black portion to right of selected column
  1499.                          */
  1500.                         int xoffset = SelectStartCol + selectwidth - LeftCol;
  1501.  
  1502.                         if (xoffset < 0)
  1503.                                 xoffset = 0;
  1504.  
  1505.                         Move(rport, BoxInLeft + BoxCharWidth * xoffset, BoxHeaderLine);
  1506.                         Text(rport, BufferLine + xoffset, numcols - MAX(xoffset,0));
  1507.                 }
  1508.                 if (SelectStartCol >= LeftCol && SelectStartCol < (LeftCol+numcols)) {
  1509.                         /*
  1510.                          *              Draw selected column heading in white
  1511.                          */
  1512.                         int xoffset = SelectStartCol - LeftCol;
  1513.                         int fwidth  = MIN(selectwidth, numcols - xoffset);
  1514.  
  1515.                         if (fwidth > 0) {
  1516.                                 SetAPen(rport, 2);
  1517.                                 Move(rport, BoxInLeft  + xoffset * BoxCharWidth,
  1518.                                          BoxHeaderLine);
  1519.                                 Text(rport, BufferLine + xoffset, fwidth);
  1520.                                 SetAPen(rport, 1);
  1521.                         }
  1522.                 } else if (SelectStartCol < LeftCol &&
  1523.                                    (SelectStartCol + selectwidth) >= LeftCol) {
  1524.                         /*
  1525.                          *              Draw portion of selected column heading in white
  1526.                          */
  1527.                         int colwidth = SelectStartCol + selectwidth - LeftCol;
  1528.  
  1529.                         SetAPen(rport, 2);
  1530.                         Move(rport, BoxInLeft, BoxHeaderLine);
  1531.                         Text(rport, BufferLine, MIN(colwidth, numcols));
  1532.                         SetAPen(rport, 1);
  1533.                 }
  1534.         } else {
  1535.                 /*
  1536.                  *              Not dragging a column, so draw entire line in black
  1537.                  */
  1538.                 Move(rport, BoxInLeft, BoxHeaderLine);
  1539.                 Text(rport, BufferLine, numcols);
  1540.         }
  1541. }
  1542.  
  1543. /*
  1544.  *              RedrawMainWindow()
  1545.  *
  1546.  *              Redraws all the main parts of the window, except gadgets.
  1547.  */
  1548. void RedrawMainWindow(void)
  1549. {
  1550.         struct RastPort *rport;
  1551.         
  1552.         if (!MainWindow)
  1553.                 return;
  1554.  
  1555.         rport = MainWindow->RPort;
  1556.         DrawBevelBox(rport, BoxLeft, MainWindow->BorderTop,
  1557.                                                 BoxWidth, BoxTop - MainWindow->BorderTop,
  1558.                                                 GT_VisualInfo,  MainVI,
  1559.                                                 TAG_DONE);
  1560.         DrawBevelBox(rport, BoxLeft, BoxTop, BoxWidth, BoxHeight,
  1561.                                                 GT_VisualInfo,  MainVI,
  1562.                                                 TAG_DONE);
  1563.         /*
  1564.          *              We only draw the third bevel box if either the status line or
  1565.          *              gadget line is enabled.
  1566.          */
  1567.         if (StatusLine || GadgetsLine) {
  1568.                 DrawBevelBox(rport, BoxLeft, BoxTop + BoxHeight, BoxWidth,
  1569.                                                         MainWindow->Height - MainWindow->BorderBottom -
  1570.                                                                                                  BoxTop - BoxHeight,
  1571.                                                         GT_VisualInfo,  MainVI,
  1572.                                                         TAG_DONE);
  1573.         }
  1574.         DrawHeaderLine();
  1575.         ShowBuffer(TopSeq, DISPLAY_ALL);
  1576.         if (ClearMainRHS) {
  1577.                 if ((RightCol-LeftCol+1) < BoxCols) {
  1578.                         /*
  1579.                          *              Erase strip to right of our rendered text
  1580.                          */
  1581.                         SetAPen(rport, 0);
  1582.                         SetDrMd(rport, JAM1);
  1583.                         RectFill(rport, BoxInLeft + (RightCol-LeftCol+1) * BoxCharWidth,
  1584.                                                         BoxInTop,
  1585.                                                         BoxInLeft + BoxInWidth - 1,
  1586.                                                         BoxInTop + BoxInHeight - 1);
  1587.                 }
  1588.                 ClearMainRHS = 0;
  1589.         }
  1590. }
  1591.  
  1592. /*
  1593.  *              SetTextSpacing(newspacing)
  1594.  *
  1595.  *              Updates the spacing of the main window to reflect the new spacing
  1596.  *              value passed in, and adjust all the global variables accordingly.
  1597.  */
  1598. void SetTextSpacing(int newspacing)
  1599. {
  1600.         int bfonty                = BufferFont->tf_YSize;
  1601.         int bfontbaseline = BufferFont->tf_Baseline;
  1602.         int maxheight;
  1603.  
  1604.         if (BoxInterGap == newspacing)
  1605.                 return;         /* No change needed */
  1606.         
  1607.         ChangedSpacing = 1;                     /* Flag for resize routine */
  1608.         BoxInterGap    = newspacing;
  1609.  
  1610.         if (!MainWindow)
  1611.                 return;
  1612.  
  1613.         SetAPen(MainWindow->RPort, 0);
  1614.         SetDrMd(MainWindow->RPort, JAM1);
  1615.         RectFill(MainWindow->RPort, BoxInLeft, BoxInTop,
  1616.                                                                 BoxInLeft + BoxInWidth - 1,
  1617.                                                                 BoxInTop + BoxInHeight - 1);
  1618.         
  1619.         maxheight       = BoxHeight - VBorderGap * 2 - 2;
  1620.         BoxSpacing      = bfonty + BoxInterGap;
  1621.         BoxRows         = (maxheight + BoxInterGap) / BoxSpacing;
  1622.         BoxInHeight     = BoxRows * BoxSpacing - BoxInterGap;
  1623.         BoxInTop        = BoxTop   + VBorderGap + 1;
  1624.         BoxBaseline     = BoxInTop + bfontbaseline;
  1625.  
  1626.         /*
  1627.          *              We now check to see if we're currently at the end of the buffer,
  1628.          *              and if so, make sure we stay that way by repositioning after the
  1629.          *              line change.
  1630.          */
  1631.         if (BottomSeq >= EndSeq)
  1632.                 ShowBuffer(EndSeq, DISPLAY_NONE);
  1633.                 
  1634.         LOCK_LAYERS;
  1635.         RedrawMainWindow();
  1636.         UNLOCK_LAYERS
  1637.         UpdateMainVScroll();
  1638. }
  1639.         
  1640. /*
  1641.  *              InitMainMargins()
  1642.  *
  1643.  *              Initialises the window margins according to the currently selected
  1644.  *              left offset and the current buffer width (as determined by the
  1645.  *              format string). Automatically ensures margins are kept within
  1646.  *              the bounds of the displayable area, adjusting them as necessary.
  1647.  *
  1648.  *              On entry, LeftCol should contain the current left margin, BoxCols
  1649.  *              should be initialised to the current width of the displayable
  1650.  *              buffer area, and BufferWidth should be the maximum width of the
  1651.  *              currently selected format string.
  1652.  */
  1653. void InitMainMargins(void)
  1654. {
  1655.         if ((LeftCol + BoxCols) > BufferWidth)
  1656.                 LeftCol = BufferWidth - BoxCols;
  1657.  
  1658.         if (LeftCol < 0)
  1659.                 LeftCol = 0;
  1660.  
  1661.         RightCol = LeftCol + BoxCols - 1;
  1662.         if (RightCol >= BufferWidth) {
  1663.                 RightCol = BufferWidth - 1;
  1664.                 if (RightCol < 0)
  1665.                         RightCol = 0;
  1666.         }
  1667. }
  1668.  
  1669. /*
  1670.  *              FreeScrollGadgets()
  1671.  *
  1672.  *              Frees all associated BOOPSI objects and images used by the
  1673.  *              scroll gadgets. Must only be called after the main window
  1674.  *              has closed.
  1675.  */
  1676. void FreeScrollGadgets(void)
  1677. {
  1678.         int i;
  1679.  
  1680.         for (i = GID_STARTSCROLL; i <= GID_ENDSCROLL; i++) {
  1681.                 if (Gadget[i]) {
  1682.                         DisposeObject(Gadget[i]);
  1683.                         Gadget[i] = NULL;
  1684.                 }
  1685.         }
  1686.         for (i = 0; i < 4; i++) {
  1687.                 if (ScrollImage[i]) {
  1688.                         DisposeObject(ScrollImage[i]);
  1689.                         ScrollImage[i] = NULL;
  1690.                 }
  1691.         }
  1692. }
  1693.  
  1694. /*
  1695.  *              FreeMainGadgets()
  1696.  *
  1697.  *              Frees all the gadgets allocated for the main window.
  1698.  */
  1699. void FreeMainGadgets(void)
  1700. {
  1701.         if (BufferFont) {
  1702.                 CloseFont(BufferFont);
  1703.                 BufferFont  = NULL;
  1704.         }
  1705.         if (MainGadList) {
  1706.                 FreeGadgets(MainGadList);
  1707.                 MainGadList = NULL;
  1708.         }
  1709. }
  1710.  
  1711. /*
  1712.  *              CreateScrollGadgets()
  1713.  *
  1714.  *              Creates the list of six gadgets that are used to give our window
  1715.  *              scroll bars and scroll arrows. These have to be created independently
  1716.  *              of the GadTools button gadgets, because since they are in the border,
  1717.  *              they must be added to the window at the time it is opened, and not
  1718.  *              afterwards.
  1719.  *
  1720.  *              The GadTools gadgets are removed and then re-added (with new
  1721.  *              size-sensitive positions) whenever the window is resized; if we
  1722.  *              try and do this with the BOOPSI border gadgets however, Intuition
  1723.  *              gets the positioning subtely wrong the second and subsequent times.
  1724.  *
  1725.  *              Hence you must call this function before opening the main window. All
  1726.  *              the gadgets are created relative to the current window dimensions, and
  1727.  *              so do not depend on specific hardcoded with/height values.
  1728.  *
  1729.  *              Returns a pointer to the gadget list, or NULL if the gadgets
  1730.  *              couldn't be created.
  1731.  */
  1732. struct Gadget *CreateScrollGadgets(void)
  1733. {
  1734.         struct Gadget *gadlist  = NULL;
  1735.         struct Gadget *gad              = (struct Gadget *)&gadlist;
  1736.         int sizew       = SizeImage->Width;
  1737.         int sizeh       = SizeImage->Height;
  1738.         int bw      = (ScreenResolution == SYSISIZE_LOWRES) ? 1 : 2;
  1739.         int i;
  1740.  
  1741.         /*
  1742.          *              First, create the arrows for the scroll bars. Gad starts off
  1743.          *              pointing to gadlist, which will receive the pointer to the
  1744.          *              first gadget we create.
  1745.          */
  1746.         ScrollArrows[0].widthoffset  =
  1747.         ScrollArrows[1].widthoffset  = 1 - sizew;
  1748.         ScrollArrows[2].heightoffset =
  1749.         ScrollArrows[3].heightoffset = 1 - sizeh;
  1750.  
  1751.         for (i = 0; i < 4; i++) {
  1752.                 struct ScrollData *scd = &ScrollArrows[i];
  1753.                 int gadid = scd->gadgetid;
  1754.                 struct Image *img;
  1755.  
  1756.                 img = (struct Image *)
  1757.                                 NewObject(NULL, "sysiclass",
  1758.                                                   SYSIA_DrawInfo,       ScreenDI,
  1759.                                                   SYSIA_Which,          scd->imagetype,
  1760.                                                   SYSIA_Size,           ScreenResolution,
  1761.                                                   TAG_END);
  1762.                 if (!img)
  1763.                         goto sgad_failed;
  1764.                 ScrollImage[i] = img;
  1765.  
  1766.                 gad = (struct Gadget *)
  1767.                                 NewObject(NULL,                         "buttongclass",
  1768.                                                   GA_ID,                        gadid,
  1769.                                                   GA_Immediate,         TRUE,
  1770.                                                   GA_Image,                     img,
  1771.                                                   GA_Width,                     img->Width,
  1772.                                                   GA_Height,            img->Height,
  1773.                                                   GA_Previous,          gad,
  1774.                                                   GA_RelVerify,         TRUE,
  1775.                                                   scd->borderpos,       TRUE,
  1776.                                                   GA_RelRight,          scd->widthscale * img->Width +
  1777.                                                                                         scd->widthoffset,
  1778.                                                   GA_RelBottom,         scd->heightscale * img->Height +
  1779.                                                                                         scd->heightoffset,
  1780.                                                   ICA_TARGET,           ICTARGET_IDCMP,
  1781.                                                   TAG_END);
  1782.                 if (!gad)
  1783.                         goto sgad_failed;
  1784.                 Gadget[gadid] = gad;
  1785.         }
  1786.         /*
  1787.          *              Now create our two BOOPSI scroll gadgets in the window border
  1788.          */
  1789.         gad = (struct Gadget *)NewObject(NULL,                          "propgclass",
  1790.                                                                          GA_ID,                         GID_HSCROLLER,
  1791.                                                                          PGA_Freedom,           FREEHORIZ,
  1792.                                                                          PGA_NewLook,           TRUE,
  1793.                                                                          PGA_Borderless,        TRUE,
  1794.                                                                          PGA_Top,                       LeftCol,
  1795.                                                                          PGA_Visible,           BoxCols,
  1796.                                                                          PGA_Total,                     BufferWidth,
  1797.                                                                          GA_Left,                       3,
  1798.                                                                          GA_RelBottom,          3 - sizeh,
  1799.                                                                          GA_RelWidth,           -sizew - 5 -
  1800.                                                                                                                 ScrollImage[0]->Width -
  1801.                                                                                                                 ScrollImage[1]->Width,
  1802.                                                                          GA_Height,                     sizeh - 4,
  1803.                                                                          GA_Previous,           gad,
  1804.                                                                          GA_BottomBorder,       TRUE,
  1805.                                                                          ICA_TARGET,            ICTARGET_IDCMP,
  1806.                                                                          ICA_MAP,                       RZ_MapTags,
  1807.                                                                          TAG_END);
  1808.         if (!gad)
  1809.                 goto sgad_failed;
  1810.         Gadget[GID_HSCROLLER] = gad;
  1811.  
  1812.         gad = (struct Gadget *)NewObject(NULL,                          "propgclass",
  1813.                                                                          GA_ID,                         GID_VSCROLLER,
  1814.                                                                          PGA_Freedom,           FREEVERT,
  1815.                                                                          PGA_NewLook,           TRUE,
  1816.                                                                          PGA_Borderless,        TRUE,
  1817.                                                                          PGA_Top,                       TopSeq - FirstSeq,
  1818.                                                                          PGA_Visible,           BoxRows,
  1819.                                                                          PGA_Total,                     EndSeq - FirstSeq + 1,
  1820.                                                                          GA_RelRight,           bw - sizew + 3,
  1821.                                                                          GA_Top,                        TitlebarHeight + 1,
  1822.                                                                          GA_Width,                      sizew - bw - bw - 4,
  1823.                                                                          GA_RelHeight,          -TitlebarHeight -
  1824.                                                                                                                 ScrollImage[2]->Height-
  1825.                                                                                                                 ScrollImage[3]->Height-
  1826.                                                                                                                 sizeh - 2,
  1827.                                                                          GA_RightBorder,        TRUE,
  1828.                                                                          GA_Previous,           gad,
  1829.                                                                          ICA_TARGET,            ICTARGET_IDCMP,
  1830.                                                                          ICA_MAP,                       RZ_MapTags,
  1831.                                                                          TAG_END);
  1832.         if (!gad)
  1833.                 goto sgad_failed;
  1834.         Gadget[GID_VSCROLLER] = gad;
  1835.         return (gadlist);
  1836.  
  1837. sgad_failed:
  1838.         FreeScrollGadgets();
  1839.         return (NULL);
  1840. }
  1841.  
  1842. /*
  1843.  *              CreateMainGadgets(width, height, statusline)
  1844.  *
  1845.  *              Creates the gadgets for the main SnoopDos window, assuming a window
  1846.  *              of size width x height. MainGadList will point to the list of
  1847.  *              gadgets when completed. Returns NULL if unsuccessful, or MainGadList
  1848.  *              if successful.
  1849.  *
  1850.  *              Statusline is a flag that says whether or not the status gadget is
  1851.  *              to be created.
  1852.  *
  1853.  *              There are basically seven possible gadget layouts:
  1854.  *
  1855.  *                      - 4 x 3 grid with status line on top
  1856.  *                      - 4 x 2 grid with no status line
  1857.  *                      - 6 x 2 grid with status line occupying the upper 4 left spaces
  1858.  *                      - 8 x 1 grid with no status line
  1859.  *                      - 12 x 1 grid with status line
  1860.  *          - 0 grid with no gadgets or status line
  1861.  *          - 1 line grid with status line but no gadgets
  1862.  *
  1863.  *              The first two are used when the window is narrow; the second two
  1864.  *              when the window is wider. We use colwidth[0] to hold the width
  1865.  *              of the narrow gadgets, colwidth[1] for the wide gadgets, and
  1866.  *              colwidth[2] for the status line gadget.
  1867.  *
  1868.  *              To allow us to conveniently replace one gadget definition with
  1869.  *              another, we use the concept of invisible buttons -- these are
  1870.  *              like normal buttons, but their X position is set way off to the
  1871.  *              left of the window where they won't be displayed; to reveal
  1872.  *              the buttons, we simply restore their X position and make the
  1873.  *              X position of a different button negative.
  1874.  *
  1875.  *              Finally, we have toggle buttons which flip flop between on and off.
  1876.  *              These are defined as MAIN_NARROW_TOGGLE.
  1877.  */
  1878. struct Gadget *CreateMainGadgets(struct TextAttr *gadgetfa,
  1879.                                                                  struct TextAttr *bufferfa,
  1880.                                                                  int width, int height, int statusline)
  1881. {
  1882.         struct MainGadgets *mg;
  1883.         struct NewGadget ng;
  1884.         struct TextFont *font;
  1885.         struct Gadget *gadlist;
  1886.         struct Gadget *gad;
  1887.         UWORD colwidth[MAIN_NUMWIDTHS];
  1888.         WORD  colpos[12];
  1889.         UWORD spacing;                          /* Spacing between gadget tops                          */
  1890.         UWORD yoffset;                          /* Offset of first gadget top in window         */
  1891.         int fonty;                                      /* Y height of gadget font                                      */
  1892.         int bfontx;                                     /* X height of buffer font                                      */
  1893.         int bfonty;                                     /* Y height of buffer font                                      */
  1894.         int bfontbaseline;                      /* Baseline of buffer font                                      */
  1895.         int innerwidth;                         /* Usable width of window                                       */
  1896.         int extraspace;                         /* Spare space inbetween buffer and gadgets     */
  1897.         int gheight;                            /* Height of a single gadget                            */
  1898.         int width4;                                     /* Minimum width needed for 4 x 3 gadgets       */
  1899.         int width6;                                     /* Minimum width needed for 6 x 2 gadgets       */
  1900.         int width8;                                     /* Minimum width needed for 8 x 1 gadgets       */
  1901.         int width12;                            /* Minimum width needed for 12 x 1 gadgets      */
  1902.         int gridtype;                           /* Grid type for gadget layout                          */
  1903.         int numrows;                            /* Number of rows of gadgets involved           */
  1904.         int gadg_gap;                           /* Aspect-corrected gap between gadget rows     */
  1905.         int margin = MAIN_MARGIN;       /* Gadget margin at left edge of window         */
  1906.         int boxmaxwidth;                        /* Temporary width of listview box                      */
  1907.         int boxheight;                          /* Temporary height of listview box                     */
  1908.  
  1909.         int bordright  = SizeImage->Width;
  1910.         int bordbot    = SizeImage->Height;
  1911.  
  1912. /*
  1913.  *              Quick defines for our internal grid arrangements
  1914.  */
  1915. #define FOUR_BY_THREE   0
  1916. #define SIX_BY_TWO              1
  1917. #define EIGHT_BY_ONE    2
  1918. #define TWELVE_BY_ONE   3
  1919.  
  1920.         if (MainGadList)
  1921.                 FreeMainGadgets();
  1922.  
  1923.         if (!MainVI) {
  1924.                 MainVI = GetVisualInfoA(SnoopScreen, NULL);
  1925.                 if (!MainVI)
  1926.                         return (NULL);
  1927.         }
  1928.         font = MyOpenFont(gadgetfa);
  1929.         if (!font)
  1930.                 return (NULL);
  1931.  
  1932.         BufferFont = MyOpenFont(bufferfa);
  1933.         if (!BufferFont) {
  1934.                 CloseFont(font);
  1935.                 return (NULL);
  1936.         }
  1937.         fonty                   = font->tf_YSize;
  1938.         bfontx                  = BufferFont->tf_XSize;
  1939.         bfonty                  = BufferFont->tf_YSize;
  1940.         bfontbaseline   = BufferFont->tf_Baseline;
  1941.  
  1942.         if (SquareAspect) {
  1943.                 VBorderGap   = 2;
  1944.                 gadg_gap         = 3;
  1945.         } else {
  1946.                 VBorderGap   = 1;
  1947.                 gadg_gap         = 2;
  1948.         }
  1949.  
  1950.         gheight         = fonty + GadgetHeight;
  1951.         spacing     = gheight + gadg_gap;
  1952.  
  1953.         colwidth[MAIN_NARROW]            = MaxTextLen(font, MainWidthLeftText)  + 12;
  1954.         colwidth[MAIN_WIDE]                      = MaxTextLen(font, MainWidthRightText) + 12;
  1955.  
  1956.         /*
  1957.          *              First, check if window is wide enough for gadgets. If not,
  1958.          *              then fail.
  1959.          */
  1960.         innerwidth = width - BorderLeft - bordright;
  1961.  
  1962.         if ((colwidth[MAIN_NARROW]+colwidth[MAIN_WIDE]+margin) * 2 > innerwidth) {
  1963.                 CloseFont(font);
  1964.                 CloseFont(BufferFont);
  1965.                 BufferFont = NULL;
  1966.                 return (NULL);
  1967.         }
  1968.  
  1969.         /*
  1970.          *              Now calculate how many columns our fake listview can hold using
  1971.          *              the current font, and use this to adjust our margins accordingly.
  1972.          *
  1973.          *              The '4' in boxmaxwidth comes from 2 pixels for the left and right
  1974.          *              of the box (the borders)
  1975.          */
  1976.         boxmaxwidth     = innerwidth - 2*BOX_LEFT_MARGIN - 4;
  1977.         BoxCols         = (boxmaxwidth / bfontx);
  1978.  
  1979.         width4          = colwidth[MAIN_NARROW] * 2 + colwidth[MAIN_WIDE] * 2;
  1980.         width6          = colwidth[MAIN_NARROW] * 4 + colwidth[MAIN_WIDE] * 2;
  1981.         width8          = colwidth[MAIN_NARROW] * 4 + colwidth[MAIN_WIDE] * 4;
  1982.         width12         = colwidth[MAIN_NARROW] * 8 + colwidth[MAIN_WIDE] * 4;
  1983.         colpos[0]       = BorderLeft + margin;
  1984.         innerwidth -= margin * 2;
  1985.  
  1986.         if (GadgetsLine) {
  1987.                 if (statusline && width12 <= innerwidth) {
  1988.                         /*
  1989.                          *              Setup layout for 12 x 1 grid
  1990.                          */
  1991.                         int totalspace = innerwidth - width12;
  1992.                         int i;
  1993.  
  1994.                         for (i = 1; i < 8; i++)
  1995.                                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW] * i
  1996.                                                                           + (totalspace*i)/11;
  1997.                         for (i = 8; i < 12; i++)
  1998.                                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW] * 8
  1999.                                                                           + colwidth[MAIN_WIDE]   * (i-8)
  2000.                                                                           + (totalspace*i)/11;
  2001.  
  2002.                         /*
  2003.                          *              We always ensure that there is at least two pixels
  2004.                          *              between the end of the status line and the beginning
  2005.                          *              of the next gadget, otherwise we get a nasty artifact
  2006.                          *              where the identically-coloured white borders meet.
  2007.                          */
  2008.                         colwidth[MAIN_STATUS] = colpos[3] - colpos[0] +
  2009.                                                                         colwidth[MAIN_NARROW];
  2010.                         if ((colpos[0] + colwidth[MAIN_STATUS] + 2) > colpos[4])
  2011.                                 colwidth[MAIN_STATUS] = colpos[4] - colpos[0] - 2;
  2012.  
  2013.                         gridtype = TWELVE_BY_ONE;
  2014.                         numrows  = 1;
  2015.                 } else if (statusline && width6 <= innerwidth) {
  2016.                         /*
  2017.                          *              Setup layout for 6 x 2 grid.
  2018.                          */
  2019.                         int totalspace = innerwidth - width6;
  2020.                         int i;
  2021.  
  2022.                         for (i = 1; i < 4; i++)
  2023.                                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW]*i +
  2024.                                                         (totalspace*i)/5;
  2025.  
  2026.                         colwidth[MAIN_STATUS] = colpos[3] - colpos[0] +
  2027.                                                                         colwidth[MAIN_NARROW];
  2028.  
  2029.                         colpos[5]       = width - bordright - margin - colwidth[MAIN_WIDE];
  2030.                         colpos[4]       = colpos[5] - colwidth[MAIN_WIDE] - totalspace/5;
  2031.  
  2032.                         /*
  2033.                          *              If there isn't at least 2 pixels between the status line
  2034.                          *              and the adjacent wide gadget, reduce the size of each
  2035.                          *              wide gadget by 1 pixel (2 pixels in total) to prevent
  2036.                          *              a nasty clash between the two gadget edges.
  2037.                          */
  2038.                         if ((colpos[0] + colwidth[MAIN_STATUS] + 2) > colpos[4]) {
  2039.                                 colpos[4] += 2;
  2040.                                 colpos[5]++;
  2041.                                 colwidth[MAIN_WIDE]--;
  2042.                         }
  2043.                         gridtype    = SIX_BY_TWO;
  2044.                         numrows         = 2;
  2045.  
  2046.                 } else if (!statusline && width8 <= innerwidth) {
  2047.                         /*
  2048.                          *              Setup layout for 8 x 1 grid
  2049.                          */
  2050.                         int totalspace = innerwidth - width8;
  2051.                         int i;
  2052.  
  2053.                         for (i = 1; i < 4; i++)
  2054.                                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW]*i +
  2055.                                                         (totalspace*i)/7;
  2056.  
  2057.                         for (i = 4; i < 8; i++)
  2058.                                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW] * 4
  2059.                                                                           + colwidth[MAIN_WIDE]   * (i-4)
  2060.                                                                           + (totalspace*i)/7;
  2061.                         gridtype = EIGHT_BY_ONE;
  2062.                         numrows  = 1;
  2063.  
  2064.                 } else {
  2065.                         /*
  2066.                          *              Setup layout for 4 x 3 grid
  2067.                          */
  2068.                         int totalspace = (innerwidth - width4);
  2069.  
  2070.                         colwidth[MAIN_STATUS] = width - bordright - margin - colpos[0];
  2071.                         colpos[3]       = width - bordright - margin - colwidth[MAIN_WIDE];
  2072.                         colpos[1]       = colpos[0] + colwidth[MAIN_NARROW] + totalspace/3;
  2073.                         colpos[2]       = colpos[3] - colwidth[MAIN_WIDE]   - totalspace/3;
  2074.                         gridtype        = FOUR_BY_THREE;
  2075.                         if (statusline)
  2076.                                 numrows  = 3;
  2077.                         else
  2078.                                 numrows  = 2;
  2079.                 }
  2080.         } else {
  2081.                 /*
  2082.                  *              No gadget line, so set all the gadget positions to a
  2083.                  *              negative value to ensure the gadgets stay hidden, and
  2084.                  *              create or don't create a status line as appropriate.
  2085.                  */
  2086.                 int i;
  2087.  
  2088.                 colwidth[MAIN_STATUS] = innerwidth;
  2089.                 gridtype              = TWELVE_BY_ONE;
  2090.                 numrows               = 1;
  2091.                 for (i = 1; i < 12; i++)
  2092.                         colpos[i] = INVIS_LEFT_EDGE;    /* Gadget off left-hand edge */
  2093.  
  2094.                 if (!statusline) {
  2095.                         numrows   = 0;
  2096.                         colpos[0] = INVIS_LEFT_EDGE;
  2097.                 }
  2098.         }
  2099.         colwidth[MAIN_NARROW_TOGGLE] = colwidth[MAIN_NARROW];
  2100.         colwidth[MAIN_WIDE_INVIS]        = colwidth[MAIN_WIDE];
  2101.  
  2102.         /*
  2103.          *              Next, calculate how many lines we can fit in the main buffer
  2104.          *              display. The total vertical margin is fonty + 8 + BarHeight
  2105.          *              pixels, which is distributed as follows:
  2106.          *
  2107.          *                                      ----titlebar----
  2108.          *                  <1 pixel border>
  2109.          *                                      4 pixel gap
  2110.          *                                      Column headings
  2111.          *                                      <VBorderGap>
  2112.          *                                      2 pixels of border
  2113.          *                                      <VBorderGap>
  2114.          *                                      Lines of text
  2115.          *                                      <VBorderGap>
  2116.          *                                      (1 pixel border)
  2117.          *
  2118.          *              And in addition, if we have gadgets enabled:
  2119.          *
  2120.          *                                      (1 pixel border)
  2121.          *                                      <gadg_gap>
  2122.          *                                      ---gadget area---
  2123.          *                                      <gadg_gap>
  2124.          *                                      (1 pixel border)
  2125.          *                                      ---bottom border---
  2126.          *
  2127.          *              The minimum spacing between gadgets is also gadg_gap.
  2128.          *              Each individual gadget has a height 6 pixels higher than
  2129.          *              the font size.
  2130.          *
  2131.          *              The catch is to take the <any additional space> and distribute
  2132.          *              it equally between the various gadget spaces etc. For N rows
  2133.          *              of gadgets, this implies that there are (N+1) "gaps" that can
  2134.          *              have vertical space added to them.
  2135.          */
  2136.         BoxLeft                 = BorderLeft;
  2137.         BoxWidth                = width - bordright - BoxLeft;
  2138.         BoxInLeft               = BoxLeft + BOX_LEFT_MARGIN + 2;
  2139.         BoxInWidth              = bfontx * BoxCols;
  2140.         BoxCharWidth    = bfontx;
  2141.         BoxCharHeight   = bfonty;
  2142.  
  2143.         yoffset                 = height - bordbot - spacing * numrows - gadg_gap;
  2144.         BoxSpacing      = bfonty + BoxInterGap;
  2145.         if (SquareAspect) {
  2146.                 BoxTop                  = TitlebarHeight + 6 + bfonty;
  2147.                 BoxHeaderLine   = TitlebarHeight + 4 + bfontbaseline;
  2148.         } else {
  2149.                 BoxTop                  = TitlebarHeight + 5 + bfonty;
  2150.                 BoxHeaderLine   = TitlebarHeight + 3 + bfontbaseline;
  2151.         }
  2152.         if (numrows)
  2153.                 boxheight   = yoffset - gadg_gap - VBorderGap*3 - BoxTop-2;
  2154.         else
  2155.                 boxheight       = height - bordbot - BoxTop - 2*VBorderGap - 2;
  2156.         BoxRows = (boxheight + BoxInterGap) / BoxSpacing;
  2157.         if (numrows)
  2158.                 BoxHeight       = BoxRows * BoxSpacing - BoxInterGap + 2*VBorderGap + 2;
  2159.         else
  2160.                 BoxHeight       = height - bordbot - BoxTop;
  2161.  
  2162.         BoxLowest       = BoxTop   + BoxHeight + VBorderGap;
  2163.         BoxInTop        = BoxTop   + VBorderGap + 1;
  2164.         BoxBaseline     = BoxInTop + bfontbaseline;
  2165.         BoxInHeight     = BoxRows * BoxSpacing - BoxInterGap;
  2166.  
  2167.         InitMainMargins();
  2168.  
  2169.         /*
  2170.          *              Now get ready to create the button and status line gadgets
  2171.          *              We always create the gadgets, even if they're not currently
  2172.          *              being displayed.
  2173.          */     
  2174.         ng.ng_TextAttr          = gadgetfa;
  2175.         ng.ng_VisualInfo        = MainVI;
  2176.         ng.ng_Flags         = PLACETEXT_IN;
  2177.  
  2178.         ng.ng_GadgetText        = "";
  2179.         ng.ng_GadgetID          = 0;
  2180.  
  2181.         gadlist = NULL;
  2182.         gad = CreateContext(&gadlist);
  2183.         if (!gad)
  2184.                 goto mgad_failed;
  2185.  
  2186.         /*
  2187.          *              Now, the buttons and status line gadget. At this point,
  2188.          *              we distribute any additional space that we have left between
  2189.          *              the lower scroll bar and the current start of the gadget
  2190.          *              grid (aka yoffset).
  2191.          */
  2192.         extraspace              = yoffset - (BoxTop + BoxHeight);
  2193.         yoffset            -= extraspace - extraspace/(numrows+1) -
  2194.                                           ((extraspace % (numrows+1)) + 1)/2;
  2195.         spacing            += extraspace / (numrows + 1);
  2196.         yoffset            += gadg_gap;
  2197.         ng.ng_Height    = gheight; 
  2198.  
  2199.         if (gridtype == FOUR_BY_THREE && !statusline)
  2200.                 yoffset -= spacing;     /* Bias to allow for invisible status bar */
  2201.  
  2202.         for (mg = &MainGadgs[0]; mg->gadgid; mg++) {
  2203.                 switch (gridtype) {
  2204.                         case FOUR_BY_THREE:
  2205.                                 ng.ng_LeftEdge  = colpos[mg->col4];
  2206.                                 ng.ng_TopEdge   = yoffset + spacing * mg->row4;
  2207.                                 ng.ng_Width             = colwidth[mg->widthtype];
  2208.                                 break;
  2209.  
  2210.                         case SIX_BY_TWO:
  2211.                                 ng.ng_LeftEdge  = colpos[mg->col6];
  2212.                                 ng.ng_TopEdge   = yoffset + spacing * mg->row6;
  2213.                                 ng.ng_Width             = colwidth[mg->widthtype];
  2214.                                 break;
  2215.  
  2216.                         case EIGHT_BY_ONE:
  2217.                                 ng.ng_LeftEdge  = colpos[mg->col8];
  2218.                                 ng.ng_TopEdge   = yoffset;
  2219.                                 ng.ng_Width             = colwidth[mg->widthtype];
  2220.                                 break;
  2221.                 
  2222.                         case TWELVE_BY_ONE:
  2223.                                 ng.ng_LeftEdge  = colpos[mg->col12];
  2224.                                 ng.ng_TopEdge   = yoffset;
  2225.                                 ng.ng_Width             = colwidth[mg->widthtype];
  2226.                                 break;
  2227.                 }
  2228.                 ng.ng_GadgetText        = MSG(mg->stringid);
  2229.                 ng.ng_GadgetID          = mg->gadgid;
  2230.  
  2231.                 if (mg->gadgid == GID_STATUS) {
  2232.                         if (statusline) {
  2233.                                 int len = GetTextLen(font, MSG(mg->stringid)) + 9;
  2234.  
  2235.                                 ng.ng_LeftEdge += len;
  2236.                                 ng.ng_Width        -= len;
  2237.                                 ng.ng_Flags             = PLACETEXT_LEFT;
  2238.                                 gad = CreateGadget(TEXT_KIND, gad, &ng,
  2239.                                                                    GT_Underscore,       '_',
  2240.                                                                    GTTX_Text,           StatusLineText,
  2241.                                                                    GTTX_Border,         TRUE,
  2242.                                                                    TAG_DONE);
  2243.                                 ng.ng_Flags                = PLACETEXT_IN;
  2244.                                 if (!gad)
  2245.                                         goto mgad_failed;
  2246.                                 Gadget[GID_STATUS] = gad;
  2247.                         } else {
  2248.                                 Gadget[GID_STATUS] = NULL;
  2249.                         }
  2250.                 } else {
  2251.                         gad = CreateGadget(BUTTON_KIND, gad, &ng,
  2252.                                                            GT_Underscore,       '_',
  2253.                                                            TAG_DONE);
  2254.                         if (!gad)
  2255.                                 goto mgad_failed;
  2256.  
  2257.                         if (mg->widthtype == MAIN_NARROW_TOGGLE)
  2258.                                 gad->Activation |= GACT_TOGGLESELECT;
  2259.  
  2260.                         if (mg->widthtype == MAIN_WIDE_INVIS)
  2261.                                 gad->LeftEdge = INVIS_LEFT_EDGE;
  2262.  
  2263.                         Gadget[mg->gadgid] = gad;
  2264.                 }
  2265.                 AddKeyShortcut(MainKeyboard, mg->gadgid, mg->stringid);
  2266.         }
  2267.         LogButtonLeft = Gadget[GID_OPENLOG]->LeftEdge;
  2268.         SetLogGadget(DefaultLogMode, LG_NOREFRESH);
  2269.  
  2270.         /*
  2271.          *              Now some additional initialisation for the Pause
  2272.          *              and Disable gadgets in case they're initially set
  2273.          */
  2274.         if (Paused)             Gadget[GID_PAUSE  ]->Flags |= GFLG_SELECTED;
  2275.         if (Disabled)   Gadget[GID_DISABLE]->Flags |= GFLG_SELECTED;
  2276.                 
  2277.         /*
  2278.          *              Now a quick check to see if we have opened commodities.library
  2279.          *              yet -- if we haven't, then disable the hide gadget.
  2280.          */
  2281.         if (CurSettings.Setup.HideMethod == HIDE_NONE)
  2282.                 Gadget[GID_HIDE]->Flags |= GFLG_DISABLED;
  2283.  
  2284.         return (gadlist);
  2285.  
  2286. mgad_failed:
  2287.         FreeMainGadgets();
  2288.         CloseFont(font);
  2289.         return (NULL);
  2290. }
  2291.  
  2292. /*
  2293.  *              RecalcMainWindow(width, height, refresh)
  2294.  *
  2295.  *              Updates the gadgets for the main window to reflect the new size,
  2296.  *              and renders the changes to the screen. If the refresh parameter
  2297.  *              is REDRAW_GADGETS, then the function will automatically redraw
  2298.  *              the gadgets in the window, otherwise this must be done by hand
  2299.  *              (or perhaps automatically if Intuition generates an IDCMP_REFRESH
  2300.  *              message after a resize event!)
  2301.  *
  2302.  *              Returns FALSE if something went wrong (in which case the caller
  2303.  *              is responsible for closing the main window) and displaying an
  2304.  *              error message.
  2305.  */
  2306. BOOL RecalcMainWindow(int width, int height, int dorefresh)
  2307. {
  2308.         struct RastPort *rport = MainWindow->RPort;
  2309.         int saveleft    = BoxLeft;
  2310.         int savewidth   = BoxWidth;
  2311.         int saveheight  = BoxHeight;
  2312.         int oldrows     = BoxRows;
  2313.         int startrow;
  2314.  
  2315.         if (!MainWindow)
  2316.                 return (TRUE);
  2317.  
  2318.         if (MainGadList && !RemovedGadgets)
  2319.                 RemoveGList(MainWindow, MainGadList, -1);
  2320.  
  2321.         MainGadList = CreateMainGadgets(CurMainGadgetFA, CurMainBufferFA,
  2322.                                                                         width, height, StatusLine);
  2323.         if (!MainGadList)
  2324.                 return (FALSE);
  2325.  
  2326.         UpdateMainHScroll();
  2327.         UpdateMainVScroll();
  2328.         if (BoxWidth == savewidth && BoxLeft == saveleft && !ChangedSpacing) {
  2329.                 if (BoxHeight == saveheight)
  2330.                         /*
  2331.                          *              Only erase from bottom of scroll box down since the
  2332.                          *              rest hasn't actually changed at all.
  2333.                          */
  2334.                         startrow = BoxLowest;
  2335.                 else {
  2336.                         startrow = BoxTop + MIN(BoxHeight, saveheight) - 1;
  2337.                         startrow = MAX(startrow, TitlebarHeight);
  2338.                 }
  2339.         } else
  2340.                 startrow = TitlebarHeight;
  2341.  
  2342.         SetDrMd(rport, JAM1);
  2343.         SetAPen(rport, 0);
  2344.         AddGList(MainWindow, MainGadList, -1, -1, NULL);
  2345.  
  2346.         if (startrow == BoxLowest) {
  2347.                 /*
  2348.                  *              If we are only refeshing the bottom part of the screen,
  2349.                  *              don't bother erasing the top half -- it looks much
  2350.                  *              cleaner.
  2351.                  */
  2352.                 if (StatusLine || GadgetsLine) {
  2353.                         // SetAPen(rport, 4);   /* Makes gadget bground gray (nice!) */
  2354.                         RectFill(rport, BoxLeft + 2, BoxTop + BoxHeight + 1,
  2355.                                                         BoxLeft + BoxWidth - 3,
  2356.                                                         MainWindow->Height - MainWindow->BorderBottom - 1);
  2357.                         DrawBevelBox(rport, BoxLeft, BoxTop + BoxHeight, BoxWidth,
  2358.                                                                 MainWindow->Height - MainWindow->BorderBottom -
  2359.                                                                                                          BoxTop - BoxHeight,
  2360.                                                                 GT_VisualInfo,  MainVI,
  2361.                                                                 TAG_DONE);
  2362.                 }
  2363.                 if (dorefresh)
  2364.                         RefreshGList(MainGadList, MainWindow, NULL, -1);
  2365.         } else {
  2366.                 startrow = MAX(TitlebarHeight, startrow - BoxSpacing - 3);
  2367.                 RectFill(rport, MainWindow->BorderLeft, startrow,
  2368.                                  width  - MainWindow->BorderRight  - 1,
  2369.                                  height - MainWindow->BorderBottom - 1);
  2370.                 if (dorefresh)
  2371.                         RefreshGList(MainGadList, MainWindow, NULL, -1);
  2372.                 /*
  2373.                  *              If we are currently at the end of the display, we want to
  2374.                  *              ensure that we scroll to the end after refreshing the window.
  2375.                  */
  2376.                 if (TopSeq + oldrows > EndSeq) {
  2377.                         /*
  2378.                          *              Force redraw from buffer end after resize
  2379.                          */
  2380.                         TopSeq   = EndSeq;
  2381.                         TopEvent = EndEvent;
  2382.                 }
  2383.                 RedrawMainWindow();
  2384.                 UpdateMainVScroll();
  2385.         }
  2386.         GT_RefreshWindow(MainWindow, NULL);
  2387.         CurWindowWidth  = MainWindow->Width;
  2388.         CurWindowHeight = MainWindow->Height;
  2389.         ChangedSpacing  = 0;
  2390.         return (TRUE);
  2391. }
  2392.  
  2393. /*
  2394.  *              SetMenuOptions()
  2395.  *
  2396.  *              Configures the main window's menu strip to match the currently
  2397.  *              selected options (e.g. Pause/Disable, Text Spacing, window type,
  2398.  *              icon type, etc.)
  2399.  *
  2400.  *              Call whenever settings change.
  2401.  *
  2402.  */
  2403. void SetMenuOptions(void)
  2404. {
  2405.         struct MenuItem *item;
  2406.  
  2407. #define SetMenuState(menunum, val) \
  2408.         item = ItemAddress(MainWinMenu, menunum); \
  2409.         item->Flags = (item->Flags & ~CHECKED) | ((val) ? CHECKED : 0)
  2410.  
  2411.         if (MainWindow && MainWinMenu) {
  2412.                 int curpri = SysBase->ThisTask->tc_Node.ln_Pri;
  2413.                 int i;
  2414.  
  2415.                 SetMenuState(MENU_PAUSE,          Paused);
  2416.                 SetMenuState(MENU_DISABLE,        Disabled);
  2417.                 SetMenuState(MENU_AUTO_OPEN,  AutoOpen);
  2418.                 SetMenuState(MENU_DIS_HIDDEN, DisableOnHide);
  2419.                 SetMenuState(MENU_STATUS,         StatusLine);
  2420.                 SetMenuState(MENU_GADGETS,        GadgetsLine);
  2421.                 SetMenuState(MENU_ICONS,          CreateIcons);
  2422.                 SetMenuState(MENU_SIMPLE,         CurSettings.SimpleRefresh);
  2423.                 SetMenuState(MENU_SMART,          !CurSettings.SimpleRefresh);
  2424.                 SetMenuState(MENU_AL_LEFT,        !RightAligned);
  2425.                 SetMenuState(MENU_AL_RIGHT,       RightAligned);
  2426.                 SetMenuState(MENU_SPACING0,       BoxInterGap == 0);
  2427.                 SetMenuState(MENU_SPACING1,       BoxInterGap == 1);
  2428.                 SetMenuState(MENU_SPACING2,       BoxInterGap == 2);
  2429.  
  2430.                 SetMenuState(MENU_ROWQUAL_ANY,    RowQual == ROWQUAL_ANY);
  2431.                 SetMenuState(MENU_ROWQUAL_NONE,   RowQual == ROWQUAL_NONE);
  2432.                 SetMenuState(MENU_ROWQUAL_SHIFT,  RowQual == ROWQUAL_SHIFT);
  2433.                 SetMenuState(MENU_ROWQUAL_ALT,    RowQual == ROWQUAL_ALT);
  2434.                 SetMenuState(MENU_ROWQUAL_CTRL,   RowQual == ROWQUAL_CTRL);
  2435.                 SetMenuState(MENU_ROWQUAL_ALL,    RowQual == ROWQUAL_ALL);
  2436.  
  2437.                 if (CurSettings.Setup.HideMethod == HIDE_NONE)
  2438.                         OffMenu(MainWindow, MENU_HIDE);
  2439.                 else
  2440.                         OnMenu(MainWindow,      MENU_HIDE);
  2441.  
  2442.                 if (LogActive) {
  2443.                         OffMenu(MainWindow,     MENU_OPENLOG);
  2444.                         OnMenu(MainWindow,      MENU_CLOSELOG);
  2445.                 } else {
  2446.                         OnMenu(MainWindow,      MENU_OPENLOG);
  2447.                         OffMenu(MainWindow,     MENU_CLOSELOG);
  2448.                 }
  2449.  
  2450.                 /*
  2451.                  *              We calculate our current priority on the fly. This
  2452.                  *              means we'll always be up-to-date, even if someone
  2453.                  *              changes our priority from outside the program.
  2454.                  */
  2455.                 for (i = 0; i < MENU_NUMPRI; i++) {
  2456.                         int subnum = MENU_CHANGEPRI | SHIFTSUB(i);
  2457.                         struct MenuItem  *item  = ItemAddress(MainWinMenu, subnum);
  2458.                         struct IntuiText *itext = (void *)(item->ItemFill);
  2459.  
  2460.                         if (atoi(itext->IText) == curpri)
  2461.                                 item->Flags |= CHECKED;
  2462.                         else
  2463.                                 item->Flags &= ~CHECKED;
  2464.                 }
  2465.         }
  2466. }
  2467.                 
  2468. /*
  2469.  *              SetMainHideState(state)
  2470.  *
  2471.  *              Updates the current main window titlebar, hide gadget, and
  2472.  *              menu HIDE option to reflect the given hide state.
  2473.  *
  2474.  *              It works as follows. If state is HIDE_NONE, then the titlebar
  2475.  *              is set to <none> and the gadget/menu item are ghosted. If
  2476.  *              state is not HIDE_NONE, the gadget/menu item are unghosted,
  2477.  *              and the titlebar is set to <key sequence>, unless HotKeyActive
  2478.  *              is set to 0, in which case it is set to <invalid>.
  2479.  */
  2480. void SetMainHideState(int hidestate)
  2481. {
  2482.         char title[200];
  2483.         char *keyname = CurSettings.Setup.HotKey;
  2484.  
  2485.         if (!HotKeyActive)
  2486.                 keyname = MSG(MSG_INVALID_HOTKEY);
  2487.         
  2488.         if (hidestate == HIDE_NONE)
  2489.                 strcpy(title, SnoopDosTitle);
  2490.         else
  2491.                 mysprintf(title, SnoopDosTitleKey, keyname);
  2492.  
  2493.         if (strcmp(CurrentTitle, title) != 0)
  2494.                 strcpy(CurrentTitle, title);
  2495.         
  2496.         if (MainWindow) {
  2497.                 struct Gadget *gad = Gadget[GID_HIDE];
  2498.                 int oldstate       = gad->Flags & GFLG_DISABLED;
  2499.                 int gadpos;
  2500.  
  2501.                 SetWindowTitles(MainWindow, CurrentTitle, (UBYTE *)-1);
  2502.  
  2503.                 gadpos = RemoveGadget(MainWindow, gad);
  2504.                 if (hidestate == HIDE_NONE)
  2505.                         gad->Flags |= GFLG_DISABLED;
  2506.                 else
  2507.                         gad->Flags &= ~GFLG_DISABLED;
  2508.  
  2509.                 AddGadget(MainWindow, gad, gadpos);
  2510.                 if ((gad->Flags & GFLG_DISABLED) != oldstate) {
  2511.                         /*
  2512.                          *              Only refresh the gadget if its state changed
  2513.                          */
  2514.                         RefreshGList(gad, MainWindow, NULL, 1);
  2515.                 }
  2516.                 if (MainWinMenu) {
  2517.                         if (CurSettings.Setup.HideMethod == HIDE_NONE)
  2518.                                 OffMenu(MainWindow, MENU_HIDE);
  2519.                         else
  2520.                                 OnMenu(MainWindow,      MENU_HIDE);
  2521.                 }
  2522.         }
  2523. }
  2524.  
  2525. /*
  2526.  *              OpenMainWindow()
  2527.  *
  2528.  *              Opens the main window with the scroll buffer display, arrow gadgets,
  2529.  *              and buttons. Also creates the status line gadget if necessary.
  2530.  *
  2531.  *              Returns true for success, false for failure.
  2532.  */
  2533. BOOL OpenMainWindow(void)
  2534. {
  2535.         static WORD initzoomdims[] = { 0, 0, -1, -1 };
  2536.         struct Window *win;
  2537.         struct Gadget *scrollgadlist;
  2538.         struct TextAttr *gadgetfa;
  2539.         struct TextAttr *bufferfa;
  2540.         int minx, miny;
  2541.         int menupen;
  2542.         int i;
  2543.         int width  = CurSettings.MainWinWidth;
  2544.         int height = CurSettings.MainWinHeight;
  2545.         int left   = CurSettings.MainWinLeft;
  2546.         int top    = CurSettings.MainWinTop;
  2547.  
  2548.         CheckSegTracker();
  2549.         if (MainWindow) {
  2550.                 WindowToFront(MainWindow);
  2551.                 ActivateWindow(MainWindow);
  2552.                 return (TRUE);
  2553.         }
  2554.         if (!CheckForScreen())
  2555.                 goto open_main_failed;
  2556.  
  2557.         for (i = 0; gadgetfa = MainWindowFontList[i].gadgetfa; i++) {
  2558.                 bufferfa = MainWindowFontList[i].bufferfa;
  2559.  
  2560.                 if (CalcMinMainSize(gadgetfa, bufferfa, &minx, &miny))
  2561.                         break;
  2562.         }
  2563.         if (!gadgetfa)
  2564.                 goto open_main_failed;
  2565.  
  2566.         if (width  == -1)                                       width  = DEF_WINDOW_WIDTH;
  2567.         if (height == -1)                                       height = DEF_WINDOW_HEIGHT;
  2568.         if (width  == 0)                                        width  = CurSettings.MainWinWidth;
  2569.         if (height == 0)                                        height = CurSettings.MainWinHeight;
  2570.         if (width  < minx)                                      width  = minx;
  2571.         if (height < miny)                                      height = miny;
  2572.         if (width  > ScreenWidth)                       width  = ScreenWidth;
  2573.         if (height > ScreenHeight)                      height = ScreenHeight;
  2574.         if (left   == -1)                                       left   = 0;
  2575.         if (top    == -1)                                       top    = SnoopScreen->BarHeight + 2;
  2576.  
  2577.         initzoomdims[2] = ScreenWidth;
  2578.         initzoomdims[3] = ScreenHeight;
  2579.                 
  2580.         ShowBuffer(TopSeq, DISPLAY_NONE);       /* Ensure scroll vars are up to date */
  2581.  
  2582.         MainGadList   = CreateMainGadgets(gadgetfa, bufferfa,
  2583.                                                                          width, height, StatusLine);
  2584.         scrollgadlist = CreateScrollGadgets();
  2585.         if (!scrollgadlist || !MainGadList)
  2586.                 goto open_main_failed;
  2587.  
  2588.         Gadget[GID_ENDSCROLL]->NextGadget = MainGadList;
  2589.  
  2590.         CurMainGadgetFA = gadgetfa;
  2591.         CurMainBufferFA = bufferfa;
  2592.  
  2593.         SetMainHideState(CurSettings.Setup.HideMethod); /* Make title up to date */
  2594.         win = OpenWindowTags(NULL,
  2595.                                                  WA_Title,                      CurrentTitle,
  2596.                                                  WA_IDCMP,                      IDCMP_CLOSEWINDOW        |
  2597.                                                                                         IDCMP_REFRESHWINDOW      |
  2598.                                                                                         IDCMP_NEWSIZE            |
  2599.                                                                                         IDCMP_CHANGEWINDOW       |
  2600.                                                                                         IDCMP_RAWKEY             |
  2601.                                                                                         IDCMP_MENUPICK           |
  2602.                                                                                         IDCMP_IDCMPUPDATE        |
  2603.                                                                                         IDCMP_GADGETDOWN         |
  2604.                                                                                         IDCMP_MOUSEBUTTONS       |
  2605.                                                                                         IDCMP_MOUSEMOVE          |
  2606.                                                                                         IDCMP_MENUHELP           |
  2607.                                                                                         IDCMP_SIZEVERIFY         |
  2608.                                                                                         IDCMP_INACTIVEWINDOW |
  2609.                                                                                         BUTTONIDCMP,
  2610.                                                  WA_Left,                       left,
  2611.                                                  WA_Top,                        top,
  2612.                                                  WA_Width,                      width,
  2613.                                                  WA_Height,                     height,
  2614.                                                  WA_Flags,                      WFLG_CLOSEGADGET         |
  2615.                                                                                         WFLG_DRAGBAR             |
  2616.                                                                                         WFLG_DEPTHGADGET         |
  2617.                                                                                         WFLG_ACTIVATE            |
  2618.                                                                                         WFLG_SIZEGADGET          |
  2619.                                                                                         WFLG_SIZEBBOTTOM         |
  2620.                                                                                         WFLG_SIZEBRIGHT          |
  2621.                                                                                         WFLG_NEWLOOKMENUS,
  2622.                                                  WA_MenuHelp,           TRUE,
  2623.                                                  RefreshTag,            TRUE,
  2624.                                                  WA_NoCareRefresh,      NoCareRefreshBool,
  2625.                                                  WA_PubScreen,          SnoopScreen,
  2626.                                                  WA_MinWidth,           minx,
  2627.                                                  WA_MinHeight,          miny,
  2628.                                                  WA_MaxWidth,           -1,
  2629.                                                  WA_MaxHeight,          -1,
  2630.                                                  WA_Zoom,                       initzoomdims,
  2631.                                                  WA_Gadgets,            scrollgadlist,
  2632.                                                  TAG_DONE);
  2633.         if (!win)
  2634.                 goto open_main_failed;
  2635.  
  2636.         MainWindow         = win;
  2637.         MainWindowPort = win->UserPort;
  2638.         MainWindowMask = 1 << MainWindowPort->mp_SigBit;
  2639.  
  2640.         if (DraggingColumn || DraggingRow) {
  2641.                 /*
  2642.                  *              If we were previously closed while a column was being dragged,
  2643.                  *              reset the drag flag and signal ourselves to ensure we catch
  2644.                  *              any events that arrived while output was suspended.
  2645.                  */
  2646.                 DraggingColumn = 0;
  2647.                 DraggingRow    = 0;
  2648.                 Signal(SysBase->ThisTask, NewEventMask);
  2649.         }
  2650.         RedrawMainWindow();
  2651.         // RefreshGList(MainGadList, MainWindow, NULL, -1);     /* Already done */
  2652.         GT_RefreshWindow(MainWindow, NULL);
  2653.         UpdateStatus();
  2654.  
  2655.         /*
  2656.          *              Now create menus for window
  2657.          */
  2658.         if (IntuitionBase->LibNode.lib_Version >= 39)
  2659.                 menupen = 1;
  2660.         else
  2661.                 menupen = 0;
  2662.  
  2663.         MainWinMenu = CreateMenus(MainMenu, GTMN_FrontPen, menupen, TAG_DONE);
  2664.  
  2665.         if (!MainWinMenu)
  2666.                 ShowError(MSG(MSG_ERROR_CREATEMENUS));
  2667.         else {
  2668.                 LayoutMenus(MainWinMenu, MainVI, GTMN_NewLookMenus, TRUE, TAG_DONE);
  2669.                 SetMenuStrip(MainWindow, MainWinMenu);
  2670.                 SetMenuOptions();
  2671.         }
  2672.         CurWindowWidth  = MainWindow->Width;
  2673.         CurWindowHeight = MainWindow->Height;
  2674.         AwaitingResize  = RESIZE_DONE;
  2675.         if ((SnoopScreen->Flags & SCREENTYPE) != WBENCHSCREEN) {
  2676.                 /*
  2677.                  *              We're running on a custom screen, so redirect system
  2678.                  *              requesters to appear on this screen instead of the
  2679.                  *              Workbench screen. We don't do it unconditionally, because
  2680.                  *              our window title is so long that it makes the requesters
  2681.                  *              look very lopsided -- still, if we're on a custom screen,
  2682.                  *              it's better than nothing.
  2683.                  */
  2684.                 *TaskWindowPtr = MainWindow;
  2685.         }
  2686.         if (DisableNestCount)
  2687.                 DisableWindow(MainWindow, &MainRequester);
  2688.  
  2689.         return (TRUE);
  2690.  
  2691. open_main_failed:
  2692.         ShowError(MSG(MSG_ERROR_OPENMAIN));
  2693.         return (FALSE);
  2694. }
  2695.  
  2696. /*
  2697.  *              CloseMainWindow(void)
  2698.  *
  2699.  *              Closes the main SnoopDos window
  2700.  */
  2701. void CloseMainWindow(void)
  2702. {
  2703.         *TaskWindowPtr = SaveWindowPtr;         /* Restore previous screen address */
  2704.         if (MainWindow) {
  2705.                 if (MainWinMenu) {
  2706.                         ClearMenuStrip(MainWindow);
  2707.                         FreeMenus(MainWinMenu);
  2708.                         MainWinMenu = NULL;
  2709.                 }
  2710.                 RecordWindowSizes();
  2711.                 {                               /* To avoid race conditions, we
  2712.                                                  * NULL the pointer here immediately so
  2713.                                                  * the patches will no longer test
  2714.                                                  * an invalid pointer.
  2715.                                                  * THOR, 4.8.2000
  2716.                                                  */
  2717.                         struct Window *win=MainWindow;
  2718.  
  2719.                         MainWindow      = NULL;
  2720.                         CloseWindow(win);
  2721.                 }
  2722.                 FreeMainGadgets();
  2723.                 MainWindow              = NULL;
  2724.                 MainWindowMask  = 0;
  2725.         }
  2726.         FreeScrollGadgets();
  2727.         if (MainVI) {
  2728.                 FreeVisualInfo(MainVI);
  2729.                 MainVI = NULL;
  2730.         }
  2731. }
  2732.  
  2733. /*
  2734.  *              ShowStatus(msg)
  2735.  *
  2736.  *              Displays the specified message in the status line (if enabled)
  2737.  */
  2738. void ShowStatus(char *msg)
  2739. {
  2740.         strcpy(StatusLineText, msg);
  2741.  
  2742.         if (MainWindow) {
  2743.                 GT_SetGadgetAttrs(Gadget[GID_STATUS], MainWindow, NULL,
  2744.                                                   GTTX_Text,    StatusLineText,
  2745.                                                   TAG_DONE);
  2746.         }
  2747. }
  2748.  
  2749. /*
  2750.  *              ReOpenMainWindow()
  2751.  *
  2752.  *              Closes and re-opens main window retaining current size but
  2753.  *              taking into account new font, spacing, refresh type, etc.
  2754.  *              If another window is specified, then that window is brought
  2755.  *              to the front after the main window is reopened.
  2756.  */
  2757. void ReOpenMainWindow(void)
  2758. {
  2759.         if (MainWindow) {
  2760.                 /*
  2761.                  *              If we've changed spacing but not yet updated the window to
  2762.                  *              reflect this, then if we are going from 0-spacing to 2-spacing,
  2763.                  *              the buffer may loose a few lines. To ensure we remain at the
  2764.                  *              end of the buffer if we were there to start with, we do an
  2765.                  *              invisible scroll to the _very_ end, just to be safe.
  2766.                  */
  2767.                 if (BottomSeq >= EndSeq) {
  2768.                         TopSeq   = EndSeq;
  2769.                         TopEvent = EndEvent;
  2770.                 }
  2771.                 CloseMainWindow();
  2772.                 OpenMainWindow();
  2773.         }
  2774. }
  2775.  
  2776. /*
  2777.  *              CheckForDirtyMainWindow()
  2778.  *
  2779.  *              Checks to see if the main Window's rastport is dirty (i.e. we did
  2780.  *              a scrollraster which was partially obscured by another window, and
  2781.  *              now we need to refresh) and redraw the window if necessary.
  2782.  *
  2783.  *              Typically called after ShowBuffer()
  2784.  */
  2785. void CheckForDirtyMainWindow(void)
  2786. {
  2787.         if (MainWindow && MainWindow->RPort->Layer->Flags & LAYERREFRESH) {
  2788.                 GT_BeginRefresh(MainWindow);
  2789.                 RedrawMainWindow();
  2790.                 GT_EndRefresh(MainWindow, TRUE);
  2791.         }
  2792. }
  2793.  
  2794. /*
  2795.  *              UpdateMainHScroll(void)
  2796.  *
  2797.  *              Updates the main horizontal scrollbar to reflect the current
  2798.  *              margin settings and buffer width.
  2799.  */
  2800. void UpdateMainHScroll(void)
  2801. {
  2802.         if (MainWindow)
  2803.                 SetGadgetAttrs(Gadget[GID_HSCROLLER], MainWindow, NULL,
  2804.                                            PGA_Top,             LeftCol,
  2805.                                            PGA_Total,   BufferWidth,
  2806.                                            PGA_Visible, BoxCols,
  2807.                                            TAG_DONE);
  2808. }
  2809.  
  2810. /*
  2811.  *              UpdateMainVScroll(void)
  2812.  *
  2813.  *              Updates the main scrollbar to reflect the current state of play.
  2814.  *              N.b. Depends on the parameters calculated during the last call
  2815.  *              to ShowBuffer.
  2816.  */
  2817. void UpdateMainVScroll(void)
  2818. {
  2819.         if (MainWindow)
  2820.                 SetGadgetAttrs(Gadget[GID_VSCROLLER], MainWindow, NULL,
  2821.                                            PGA_Top,             TopSeq - FirstSeq,
  2822.                                            PGA_Total,   EndSeq - FirstSeq + 1,
  2823.                                            PGA_Visible, BoxRows,
  2824.                                            TAG_DONE);
  2825. }
  2826.  
  2827. /*
  2828.  *              ScrollHorizontal(amount)
  2829.  *
  2830.  *              Scrolls the window horizontally to the left (-ve amount) or right
  2831.  *              (+ve amount) and does the minimum amount of refresh necessary to
  2832.  *              ensure the screen is correctly updated. i.e., we use ScrollRaster()
  2833.  *              to perform the bulk of the scroll.
  2834.  */
  2835. void ScrollHorizontal(int amount)
  2836. {
  2837.         struct RastPort *rport = MainWindow->RPort;
  2838.         int        oldpos = LeftCol;
  2839.         Event *firstevent;
  2840.         int x0, y0, x1, y1;
  2841.         int h0, h1;
  2842.         int dx;
  2843.         int dxwidth;
  2844.         int saveboxleft;
  2845.         int saveleftcol;
  2846.         int saverightcol;
  2847.         int saveboxcols;
  2848.  
  2849.         LeftCol += amount;
  2850.         InitMainMargins();
  2851.         if (!MainWindow || LeftCol == oldpos)
  2852.                 return;
  2853.  
  2854.         UpdateMainHScroll();
  2855.  
  2856.         /*
  2857.          *              Okay, we're scrolling left or right, so do a ScrollRaster for
  2858.          *              part of the amount and then use ShowBuffer() to fill in the
  2859.          *              refreshed area. If there are no events or the buffer display
  2860.          *              is now out of date, do a full refresh instead.
  2861.          *
  2862.          *              We lock semaphores for the duration to ensure that the buffer
  2863.          *              doesn't change under our feet which might lead to mismatched
  2864.          *              lines of output.
  2865.          */
  2866.  
  2867.         /*
  2868.          *
  2869.         LOCK_LAYERS;
  2870.         ObtainSemaphore(&BufSem);
  2871.          *
  2872.          */
  2873.  
  2874.         if (!AttemptSemaphoreHeavely(&BufSem)) /* Try non-blocking here, important! */
  2875.                 return;
  2876.  
  2877.         dx = LeftCol - oldpos;
  2878.         dxwidth = dx * BoxCharWidth;
  2879.  
  2880.         firstevent = HeadNode(&EventList);
  2881.         if (!firstevent || TopSeq < firstevent->seqnum || abs(dx) > (BoxCols/2)) {
  2882.                 RedrawMainWindow();
  2883.                 goto unlockall;
  2884.         }
  2885.  
  2886.         /*
  2887.          *              Calculate co-ordinates for scroll
  2888.          */
  2889.         h0 = BoxHeaderLine + BoxInTop - BoxBaseline;
  2890.         h1 = h0 + BoxCharHeight - 1;
  2891.         y0 = BoxInTop;
  2892.         y1 = BoxInTop + BoxInHeight - 1;
  2893.         x0 = BoxInLeft;
  2894.         x1 = BoxInLeft + (RightCol - LeftCol + 1) * BoxCharWidth - 1;
  2895.  
  2896.         if (GfxBase->LibNode.lib_Version >= 39) {
  2897.                 /*
  2898.                  *              Do optimised flicker-free scroll under V39
  2899.                  */
  2900.                 struct Hook *oldhook;
  2901.  
  2902.                 oldhook = InstallLayerHook(MainWindow->WLayer, LAYERS_NOBACKFILL);
  2903.                 ScrollRasterBF(rport, dxwidth, 0, x0, h0, x1, y1);
  2904.                 InstallLayerHook(MainWindow->WLayer, oldhook);
  2905.         } else {
  2906.                 /*
  2907.                  *              Do a somewhat flickering scroll under V37 -- unfortunately,
  2908.                  *              we have to do the scroll in two separate parts, to avoid
  2909.                  *              erasing part of the separator bar between the header and
  2910.                  *              the buffer text when it is scrolled
  2911.                  */
  2912.                 ScrollRaster(rport, dxwidth, 0, x0, h0, x1, h1);
  2913.                 ScrollRaster(rport, dxwidth, 0, x0, y0, x1, y1);
  2914.         }
  2915.         /*
  2916.          *              Finally, refresh the window section we just vacated by
  2917.          *              temporarily changing the margins
  2918.          */
  2919.         saveboxleft  = BoxInLeft;
  2920.         saveleftcol  = LeftCol;
  2921.         saverightcol = RightCol;
  2922.         saveboxcols  = BoxCols;
  2923.  
  2924.         if (dx < 0) {
  2925.                 /*
  2926.                  *              Scrolling to left so refresh left side of window
  2927.                  */
  2928.                 if (RightCol > -dx)
  2929.                         RightCol = LeftCol - dx - 1;
  2930.                 BoxCols = RightCol - LeftCol + 1;
  2931.         } else {
  2932.                 /*
  2933.                  *              Scrolling to right so refresh right side of window
  2934.                  */
  2935.                 int offset = RightCol - LeftCol + 1 - dx;
  2936.                 if (offset < 0)
  2937.                         goto done_scroll;
  2938.  
  2939.                 BoxInLeft += offset * BoxCharWidth;
  2940.                 LeftCol   += offset;
  2941.                 BoxCols   -= offset;
  2942.         }
  2943.         if (RightCol < LeftCol)
  2944.                 goto done_scroll;
  2945.         DrawHeaderLine();
  2946.         ShowBuffer(TopSeq, DISPLAY_ALL);
  2947.  
  2948. done_scroll:
  2949.         BoxInLeft = saveboxleft;
  2950.         LeftCol   = saveleftcol;
  2951.         RightCol  = saverightcol;
  2952.         BoxCols   = saveboxcols;
  2953.         
  2954.         /*
  2955.          *              All done, now we can release semaphores and exit
  2956.          */
  2957. unlockall:
  2958.         ReleaseSemaphore(&BufSem);
  2959.         /*
  2960.         UNLOCK_LAYERS
  2961.          */
  2962.         CheckForDirtyMainWindow();
  2963. }
  2964.  
  2965. /*
  2966.  *              DoArrowScrolling()
  2967.  *
  2968.  *              Scrolls window in the direction of the specified arrow
  2969.  *              (GID_UPARROW, GID_DOWNARROW, GID_LEFTARROW or GID_RIGHTARROW)
  2970.  *              and by the specified amount (usually 1 but sometimes more).
  2971.  */
  2972. void DoArrowScrolling(int arrowtype, int amount)
  2973. {
  2974.         int changedvert = 0;
  2975.         int horizadjust = 0;
  2976.         int newseq;
  2977.  
  2978.         /*
  2979.          *              If we're currently dragging columns, we ignore any attempts
  2980.          *              at scrolling since it can cause problems with the format
  2981.          *              being screwed up.
  2982.          */
  2983.         if (DraggingColumn || DraggingRow)
  2984.                 return;
  2985.  
  2986.         switch (arrowtype) {
  2987.                 case GID_UPARROW:
  2988.                         /*
  2989.                          *              Move up in a message
  2990.                          */
  2991.                         if (TopSeq > FirstSeq) {
  2992.                                 changedvert = 1;
  2993.                                 newseq          = TopSeq - amount;
  2994.                                 if (newseq < 1)
  2995.                                         newseq = 1;
  2996.                         }
  2997.                         break;
  2998.  
  2999.                 case GID_DOWNARROW:
  3000.                         /*              We do a clever optimisation here for power users:
  3001.                          *              if SnoopDos is Paused and is at the end of the
  3002.                          *              buffer, then clicking on the down arrow (normally
  3003.                          *              a no-op) will have the same effect as selecting
  3004.                          *              single step.
  3005.                          */
  3006.                         if (Paused && BottomSeq >= EndSeq)
  3007.                                 SingleStep();
  3008.  
  3009.                         changedvert = 1;
  3010.                         newseq          = TopSeq + amount;
  3011.                         break;
  3012.  
  3013.                 case GID_LEFTARROW:
  3014.                         if (LeftCol > 0)
  3015.                                 horizadjust =- amount;
  3016.                         break;
  3017.  
  3018.                 case GID_RIGHTARROW:
  3019.                         if (LeftCol + BoxCols <= BufferWidth)
  3020.                                 horizadjust = amount;
  3021.                         break;
  3022.         }
  3023.         if (MainWindow) {
  3024.                 if (horizadjust)
  3025.                         ScrollHorizontal(horizadjust);
  3026.  
  3027.                 if (changedvert) {
  3028.                         InitMainMargins();
  3029.                         ShowBuffer(newseq, DISPLAY_QUICK);
  3030.                         UpdateMainVScroll();
  3031.                         CheckForDirtyMainWindow();
  3032.                 }
  3033.         }
  3034. }
  3035.  
  3036. /*
  3037.  *              InvertColumn(pos)
  3038.  *
  3039.  *              Inverts the column drawn at the specified column position, relative
  3040.  *              to the left margin of the box (i.e. 0 ... BoxCols-1)
  3041.  *
  3042.  *              mode is INVERT_FULLBOX or INVERT_HEADER, depending on whether just
  3043.  *              the top of the column or the entire column is to be inverted. This
  3044.  *              allows for optimised updates if a column's position hasn't actually
  3045.  *              changed but the header still needs to be redrawn.
  3046.  */
  3047. void InvertColumn(int col, int mode)
  3048. {
  3049.         struct RastPort *rport = &InvertRP;
  3050.         int bfontbaseline          = BufferFont->tf_Baseline;
  3051.         int y1 = BoxHeaderLine - bfontbaseline;
  3052.         int y2 = (mode == INVERT_HEADER ? (BoxTop - 1) : (BoxInTop + BoxInHeight));
  3053.  
  3054.         if (col >= 0 && col < BoxCols) {
  3055.                 int x = BoxInLeft + col * BoxCharWidth + BoxCharWidth / 2;
  3056.  
  3057.                 RectFill(rport, x, y1, x+1, y2);
  3058.         }
  3059. }
  3060.  
  3061. /*
  3062.  *              StartDragCol(x)
  3063.  *
  3064.  *              Starts dragging a column displayed in the main window at absolute
  3065.  *              coordinate X.
  3066.  *
  3067.  *              Initialises all the variables associated with this and returns
  3068.  *              with dragging enabled.
  3069.  */
  3070. void StartDragCol(int x)
  3071. {
  3072.         EventFormat *ef;
  3073.         int clickcol;
  3074.  
  3075.         if (BufferEFormat[0].type == EF_END)    /* Skip empty event formats */
  3076.                 return;
  3077.  
  3078.         /*
  3079.          *              Now scan event buffer and figure out which heading we match
  3080.          *              Note that the user must click on the event heading itself,
  3081.          *              or it won't count (i.e. clicking on space between columns
  3082.          *              doesn't work) -- this is to prevent confusion.
  3083.          */
  3084.         clickcol = LeftCol + ((x - BoxInLeft) / BoxCharWidth);
  3085.         {
  3086.                 /*
  3087.                  *              Select the column to drag
  3088.                  */
  3089.                 int colpos = 0;
  3090.                 int len    = 0;
  3091.  
  3092.                 for (ef = BufferEFormat; ef->type != EF_END; ef++) {
  3093.                         if (clickcol >= colpos && clickcol < (colpos + ef->width + 1))
  3094.                                 break;
  3095.                         
  3096.                         colpos += ef->width + 1;
  3097.                 }
  3098.                 /*
  3099.                  *              Now check if the clickcol should affect the left or right
  3100.                  *              sides of the selected column. The leftmost column is a
  3101.                  *              special case (in that we always choose the righthand column
  3102.                  *              rather  the preceding lefthand, since you can't drag the
  3103.                  *              lefthand column any further to the left).
  3104.                  *
  3105.                  *              In the normal case, if there is any blank area to the right of
  3106.                  *              the column, then clicking in the blank area activates the
  3107.                  *              the right column adjustment, else the left column adjustment.
  3108.                  *              If there is no blank area, then clicking in the rightmost
  3109.                  *              column activates the right column, else the left column.
  3110.                  */
  3111.                 if (ef->type != EF_END)
  3112.                         len = strlen(MSG(ef->titlemsgid));
  3113.  
  3114.                 if (ef == BufferEFormat || ef->type == EF_END ||
  3115.                         clickcol >= (colpos + MIN(ef->width-1, len)))
  3116.                 {
  3117.                         /*
  3118.                          *              Adjusting the righthand side of the selected column
  3119.                          */
  3120.                         if (ef->type == EF_END) {
  3121.                                 DragEvent          = ef-1;
  3122.                                 SelectEvent        = ef-1;
  3123.                                 SelectStartCol = colpos - SelectEvent->width - 1;
  3124.                         } else {
  3125.                                 SelectEvent    = ef;
  3126.                                 SelectStartCol = colpos;
  3127.                                 DragEvent                = ef;
  3128.                         }
  3129.                         DragOrigColWidth = DragEvent->width;
  3130.                         NextOrigColWidth = (ef+1)->width;
  3131.                 } else {
  3132.                         /*
  3133.                          *              Adjusting the lefthand side of the selected column
  3134.                          */
  3135.                         SelectEvent      = ef;
  3136.                         SelectStartCol   = colpos;
  3137.                         DragEvent                = ef-1;
  3138.                         DragOrigColWidth = DragEvent->width;
  3139.                         NextOrigColWidth = ef->width;
  3140.                 }
  3141.         }
  3142.         OrigLeftCol             = LeftCol;
  3143.         OrigBufWidth    = BufferWidth;
  3144.         ClickStartCol   = clickcol;
  3145.         ClickCurCol             = clickcol;
  3146.         DraggingColumn  = 1;
  3147.         MovedColumn             = 0;
  3148.         DrawHeaderLine();
  3149.         /*
  3150.          *              Now initialise the rastport we use for the inverted column
  3151.          *              (we use a separate rastport for speed; otherwise, we have
  3152.          *              to keep initialising it, which is rather slow.)
  3153.          */
  3154.         InvertRP = *(MainWindow->RPort);
  3155.         if (GfxBase->LibNode.lib_Version >= 39)
  3156.                 SetWriteMask(&InvertRP, 1);
  3157.         else
  3158.                 InvertRP.Mask = 1;
  3159.         SetAPen(&InvertRP, 1);
  3160.         SetBPen(&InvertRP, 0);
  3161.         SetDrMd(&InvertRP, COMPLEMENT);
  3162.  
  3163.         InvertColumn(SelectStartCol-LeftCol-1,                                  INVERT_FULLBOX);
  3164.         InvertColumn(SelectStartCol-LeftCol+SelectEvent->width, INVERT_FULLBOX);
  3165.  
  3166.         ReportMouse(TRUE, MainWindow);
  3167.         MainWindow->IDCMPFlags |= IDCMP_INTUITICKS;
  3168.         MainWindow->Flags      |= WFLG_RMBTRAP; /* Need to detect RMB for cancel */
  3169. }
  3170.  
  3171. /*
  3172.  *              SizeColumn(eventptr, newwidth, movetype)
  3173.  *
  3174.  *              Adjusts the width of the specified event entry to the new
  3175.  *              value given, performing any scrolling and updating of the
  3176.  *              main window that may be required.
  3177.  *
  3178.  *              movetype is MOVECOL_FREE to allow free movement between the
  3179.  *              columns to the left and right, or MOVECOL_FIXED to force
  3180.  *              the columns to the right to move along with the column being
  3181.  *              sized.
  3182.  */
  3183. void SizeColumn(EventFormat *ef, int newwidth, int movetype)
  3184. {
  3185.         EventFormat *nextef = ef+1;
  3186.         int oldleftcol          = LeftCol;
  3187.         int dwidth                      = newwidth - ef->width;
  3188.         int oldbufwidth         = BufferWidth;
  3189.         int oldpos1                     = SelectStartCol - LeftCol - 1;
  3190.         int oldpos2                     = SelectStartCol - LeftCol + SelectEvent->width;
  3191.         int newpos1;
  3192.         int newpos2;
  3193.         int mode1;
  3194.         int mode2;
  3195.  
  3196.         if (dwidth == 0)
  3197.                 return;
  3198.  
  3199.         if (movetype == MOVECOL_FREE &&  nextef->type    != EF_END       &&
  3200.                                                                         (nextef+1)->type != EF_END) {
  3201.                 /*
  3202.                  *              Bound movement so that we don't make the column to the
  3203.                  *              right of the main column exceed its bounds (in either
  3204.                  *              direction).
  3205.                  */
  3206.                 int nextwidth = nextef->width - dwidth;
  3207.  
  3208.                 if (nextwidth < 1)                              nextwidth = 1;
  3209.                 if (nextwidth > MAX_FIELD_LEN)  nextwidth = MAX_FIELD_LEN;
  3210.  
  3211.                 if (nextwidth != (nextef->width - dwidth)) {
  3212.                         dwidth   = nextef->width - nextwidth;
  3213.                         newwidth = ef->width + dwidth;
  3214.                 }
  3215.                 nextef->width = nextwidth;
  3216.         } else {
  3217.                 /*
  3218.                  *              Dragging all the columns to the right of this one too so
  3219.                  *              update width to reflect the change
  3220.                  */
  3221.                 BufferWidth   += dwidth;
  3222.         }
  3223.         ef->width      = newwidth;
  3224.         if (ef < SelectEvent)
  3225.                 SelectStartCol += dwidth;
  3226.         ClickCurCol   += dwidth;
  3227.         ClearMainRHS   = 1;
  3228.         InitMainMargins();
  3229.  
  3230.         /*
  3231.          *              Check if the drag mouse position is off the right edge of the
  3232.          *              window; if it is, then scroll the window to keep it in view.
  3233.          */
  3234.         if (ClickCurCol >= (LeftCol + BoxCols)) {
  3235.                 LeftCol = ClickCurCol - BoxCols + 1;
  3236.                 InitMainMargins();
  3237.         }
  3238.         if (BufferWidth != oldbufwidth || LeftCol != oldleftcol)
  3239.                 UpdateMainHScroll();
  3240.  
  3241.         newpos1 = SelectStartCol - LeftCol - 1;
  3242.         newpos2 = SelectStartCol - LeftCol + SelectEvent->width;
  3243.  
  3244.         mode1 = mode2 = INVERT_FULLBOX;
  3245.  
  3246.         if (newpos1 == oldpos1) mode1 = INVERT_HEADER;
  3247.         if (newpos2 == oldpos2) mode2 = INVERT_HEADER;
  3248.  
  3249.         InvertColumn(oldpos1, mode1);
  3250.         InvertColumn(oldpos2, mode2);
  3251.         DrawHeaderLine();
  3252.         InvertColumn(newpos1, mode1);
  3253.         InvertColumn(newpos2, mode2);
  3254.  
  3255.         MovedColumn = 1;        /* Show we updated a column for FinishDragCol() */
  3256. }
  3257.  
  3258. /*
  3259.  *              MoveColumn(xpos, movetype)
  3260.  *
  3261.  *              Mini function used when dragging columns with the mouse --
  3262.  *              changes the width of the current column to pixel position xpos,
  3263.  *              as much as possible.
  3264.  *
  3265.  *              movetype is either MOVECOL_FREE or MOVECOL_FIXED -- see the
  3266.  *              description of SizeColumn() for more details.
  3267.  */
  3268. void MoveColumn(int xpos, int movetype)
  3269. {
  3270.         int colpos = LeftCol + ((xpos - BoxInLeft) / BoxCharWidth);
  3271.  
  3272.         if (DragEvent) {
  3273.                 int disp         = colpos - ClickStartCol;
  3274.                 int newwidth = DragOrigColWidth + disp;
  3275.                 int dwidth   = newwidth - DragEvent->width;
  3276.  
  3277.                 /*
  3278.                  *              Now check to see if the change we're making will
  3279.                  *              cause us to scroll off the left edge of the window.
  3280.                  *              If it will, then reduce the change so that we scroll
  3281.                  *              at a steady 1-character rate (this works out around
  3282.                  *              10 chars a second, which should be fast enough).
  3283.                  */
  3284.                 if (LeftCol > 0 && RightCol > (BufferWidth + dwidth))
  3285.                 {
  3286.                         dwidth   = RightCol - BufferWidth;
  3287.                         newwidth = DragEvent->width + dwidth;
  3288.                 }
  3289.                 if (newwidth < 1)                               newwidth = 1;
  3290.                 if (newwidth > MAX_FIELD_LEN)   newwidth = MAX_FIELD_LEN;
  3291.  
  3292.                 SizeColumn(DragEvent, newwidth, movetype);
  3293.         }
  3294. }
  3295.  
  3296. /*
  3297.  *              FinishDragCol()
  3298.  *
  3299.  *              Finishes up a drag operation (just resets a few variables)
  3300.  */
  3301. void FinishDragCol(void)
  3302. {
  3303.         if (!DraggingColumn)
  3304.                 return;
  3305.  
  3306.         ReportMouse(FALSE, MainWindow);
  3307.         MainWindow->IDCMPFlags &= ~IDCMP_INTUITICKS;
  3308.         MainWindow->Flags      &= ~WFLG_RMBTRAP;        /* Re-enable menus      */
  3309.         DraggingColumn = 0;
  3310.  
  3311.         /*
  3312.          *              We can optimise our update a bit by checking MovedColumn.
  3313.          *              This will only be true if the user actually resized one
  3314.          *              of the columns, so it saves us having to do a redraw
  3315.          *              if a column heading was accidentally clicked but not
  3316.          *              moved.
  3317.          */
  3318.         if (SelectEvent) {
  3319.                 InvertColumn(SelectStartCol-LeftCol-1,                             INVERT_FULLBOX);
  3320.                 InvertColumn(SelectStartCol-LeftCol+SelectEvent->width,INVERT_FULLBOX);
  3321.         }
  3322.         if (MovedColumn) {
  3323.                 /*
  3324.                  *              Now update the settings window and format window with the
  3325.                  *              new format. This will also redraw the main window and
  3326.                  *              header line for us.
  3327.                  */
  3328.                 BuildFormatString(BufferEFormat, BufFormat, MAX_FORM_LEN);
  3329.                 InstallNewFormat(NEW_STRING);
  3330.         } else {
  3331.                 /*
  3332.                  *              Nothing else to update, so just redraw the header line
  3333.                  *              (which will now be completely black again with no
  3334.                  *              highlighted item.)
  3335.                  */
  3336.                 ClearMainRHS = 1;
  3337.                 DrawHeaderLine();
  3338.         }
  3339.         /*
  3340.          *              Since we disable event scanning when columns are being dragged,
  3341.          *              we signal ourselves to check for new events in case any
  3342.          *              arrived while we were playing.
  3343.          */
  3344.         Signal(SysBase->ThisTask, NewEventMask);
  3345. }
  3346.  
  3347. /*
  3348.  *              StartDragRow(int y)
  3349.  *
  3350.  *              Starts dragging a highlight at y (pixel row) in the event output.
  3351.  *              This allows the user to use the mouse to highlight an entire line,
  3352.  *              making it easy to see all the information on the line related to
  3353.  *              the event. (This can sometimes be difficult to see in a normal
  3354.  *              window when there is a lot of white space between columns).
  3355.  *
  3356.  *              Initialises all the variables associated with this and returns
  3357.  *              with dragging enabled.
  3358.  */
  3359. void StartDragRow(int y)
  3360. {
  3361.         if (BufferEFormat[0].type == EF_END)    /* Skip empty event formats */
  3362.                 return;
  3363.  
  3364.         SelectRow = 0;
  3365.         if (y > BoxInTop)
  3366.                 SelectRow = (y - BoxInTop) / BoxSpacing;
  3367.  
  3368.         if (SelectRow >= BoxRows)
  3369.                 SelectRow = BoxRows - 1;
  3370.  
  3371.         if ((TopSeq + SelectRow) > BottomSeq)
  3372.                 SelectRow = BottomSeq - TopSeq;
  3373.  
  3374.         DraggingRow = 1;
  3375.         ReportMouse(TRUE, MainWindow);
  3376.         MainWindow->IDCMPFlags |= IDCMP_INTUITICKS;
  3377.         DrawSelectedLine(SelectRow, TRUE);
  3378. }
  3379.  
  3380. /*
  3381.  *              MoveToRow(y)
  3382.  *
  3383.  *              Moves the row highlight from its current position to a new position
  3384.  *              if necessary (doesn't render anything if the row position remains
  3385.  *              unchanged.)
  3386.  */
  3387. void MoveToRow(int y)
  3388. {
  3389.         int newrow = 0;
  3390.  
  3391.         if (y > BoxInTop)
  3392.                 newrow = (y - BoxInTop) / BoxSpacing;
  3393.  
  3394.         if (newrow >= BoxRows)
  3395.                 newrow = BoxRows - 1;
  3396.  
  3397.         if ((TopSeq + newrow) > BottomSeq)
  3398.                 newrow = BottomSeq - TopSeq;
  3399.  
  3400.         if (newrow != SelectRow) {
  3401.                 DrawSelectedLine(SelectRow, FALSE);
  3402.                 DrawSelectedLine(newrow,    TRUE);
  3403.                 SelectRow = newrow;
  3404.         }
  3405. }
  3406.  
  3407. /*
  3408.  *              FinishDragRow()
  3409.  *
  3410.  *              Stops highlighting the selected row and restores everything to normal
  3411.  */
  3412. void FinishDragRow(void)
  3413. {
  3414.         if (!DraggingRow)
  3415.                 return;
  3416.  
  3417.         DrawSelectedLine(SelectRow, FALSE);
  3418.         ReportMouse(FALSE, MainWindow);
  3419.         MainWindow->IDCMPFlags &= ~IDCMP_INTUITICKS;
  3420.         DraggingRow = 0;
  3421.  
  3422.         /*
  3423.          *              It's possible that while we were playing with the row highlight,
  3424.          *              the entire buffer may have been swept away under our feet,
  3425.          *              leading to a slightly corrupt display (events out of sequence).
  3426.          *              We don't mind this while we're dragging the highlight, but we want
  3427.          *              to ensure it's sorted out properly afterwards. So, we do a quick
  3428.          *              check to see if the current display is no longer valid, and if so,
  3429.          *              then we redraw it. We need to handle new events first, else lots
  3430.          *              of important internal variables will be out of sync.
  3431.          */
  3432.         if (TopSeq < RealFirstSeq) {
  3433.                 HandleNewEvents();
  3434.                 ShowBuffer(TopSeq, DISPLAY_ALL);
  3435.         }
  3436. }
  3437.  
  3438. /*
  3439.  *              BeginResize()
  3440.  *
  3441.  *              Removes gadget list from window and does a few other things
  3442.  *              in preparation for a window resize occurring in the very
  3443.  *              near future.
  3444.  */
  3445. void BeginResize(void)
  3446. {
  3447.         if (!RemovedGadgets)
  3448.                 RemoveGList(MainWindow, MainGadList, -1);
  3449.         RemovedGadgets = TRUE;
  3450.         if (BottomSeq >= EndSeq)
  3451.                 AwaitingResize = RESIZE_BOTTOM;
  3452.         else
  3453.                 AwaitingResize = RESIZE_MIDDLE;
  3454. }
  3455.  
  3456. /*
  3457.  *              EndResize(void)
  3458.  *
  3459.  *              Tiedies up after the window has been resized -- recalculates
  3460.  *              gadget list, refreshes display, etc.
  3461.  *
  3462.  *              Returns true for success, false for failure.
  3463.  */
  3464. int EndResize(void)
  3465. {
  3466.         if (MainWindow->Width  == CurWindowWidth &&
  3467.                 MainWindow->Height == CurWindowHeight)
  3468.         {
  3469.                 /*
  3470.                  *              Window didn't change size after all, so don't make any
  3471.                  *              changes, just restore the status quo
  3472.                  */
  3473.                 if (RemovedGadgets)
  3474.                         AddGList(MainWindow, MainGadList, ~0, -1, NULL);
  3475.                 AwaitingResize = RESIZE_DONE;
  3476.         } else {
  3477.                 /*
  3478.                  *              Window was resized. If necessary, reposition the buffer
  3479.                  *              at the end of the new window (otherwise, if we were at
  3480.                  *              the end of the buffer and the window got smaller, there
  3481.                  *              would now be a few lines below the bottom of the window.)
  3482.                  */
  3483.                 if (AwaitingResize == RESIZE_BOTTOM)
  3484.                         TopSeq = EndSeq;
  3485.                 AwaitingResize = RESIZE_DONE;
  3486.                 if (!RecalcMainWindow(MainWindow->Width,
  3487.                                                           MainWindow->Height,
  3488.                                                           REDRAW_GADGETS)) {
  3489.                         ShowError(MSG(MSG_ERROR_RESIZE));
  3490.                         return (0);
  3491.                 }
  3492.                 // RefreshWindowFrame(MainWindow);
  3493.         }
  3494.         RemovedGadgets = FALSE;
  3495.  
  3496.         /*
  3497.          *              Now, since we disabled monitoring of events during
  3498.          *              the resize, some may have accumulated that we missed.
  3499.          *              So we generate a fake new events signal to force a
  3500.          *              recheck in case anything did arrive.
  3501.          */
  3502.         Signal(SysBase->ThisTask, NewEventMask);
  3503.         return (1);
  3504. }
  3505.  
  3506. /*
  3507.  *              SetMainWindowWidth(int width)
  3508.  *
  3509.  *              Sets the width of the main window to the specified number of
  3510.  *              characters. Note that this call won't take effect immediately;
  3511.  *              it just tells Intuition to resize our window, which will generate
  3512.  *              a NEWSIZE event that we can trap.
  3513.  *
  3514.  *              Note: has no effect if main window isn't open.
  3515.  */
  3516. void SetMainWindowWidth(int colwidth)
  3517. {
  3518.         if (colwidth == 0)
  3519.                 colwidth = BufferWidth;
  3520.  
  3521.         if (MainWindow) {
  3522.                 int newleft       = MainWindow->LeftEdge;
  3523.                 int newtop        = MainWindow->TopEdge;
  3524.                 int newheight = MainWindow->Height;
  3525.                 int newwidth  = MainWindow->Width - BoxWidth +
  3526.                                                 colwidth*BoxCharWidth + 2 * BOX_LEFT_MARGIN + 5;
  3527.  
  3528.                 if (newwidth < MainWindow->MinWidth)
  3529.                         newwidth = MainWindow->MinWidth;
  3530.                 
  3531.                 if ((newleft + newwidth) > ScreenWidth)
  3532.                         newleft  = ScreenWidth - newwidth;
  3533.  
  3534.                 if (newleft < 0) {
  3535.                         newleft  = 0;
  3536.                         newwidth = ScreenWidth;
  3537.                 }
  3538.                 /*
  3539.                  *              Now check if we're so close to the edge of the screen that
  3540.                  *              a single additional character space would push us over the
  3541.                  *              edge .. if so, then expand the width to the full screen
  3542.                  *              width, since it looks neater when the window completely
  3543.                  *              fills the screen, instead of "almost" filling it.
  3544.                  */
  3545.                 if (newleft == 0 && (newwidth + BoxCharWidth) > ScreenWidth)
  3546.                         newwidth = ScreenWidth;
  3547.  
  3548.                 BeginResize();
  3549.                 ChangeWindowBox(MainWindow, newleft, newtop, newwidth, newheight);
  3550.         }
  3551. }
  3552.  
  3553. /*
  3554.  *              HandleMainMsgs()
  3555.  *
  3556.  *              Processes all outstanding messages associated with the main SnoopDos
  3557.  *              window. To be called whenever we get a main window event.
  3558.  */
  3559. void HandleMainMsgs(void)
  3560. {
  3561.         static int activegad;                   /* Non-zero if gadget is depressed   */
  3562.         static int activekey;                   /* Keycode shortcut of activegad     */
  3563.  
  3564.         struct IntuiMessage *imsg;
  3565.         Settings *newsettings = NULL;
  3566.         int loadlastsaved = 0;
  3567.         int donemain = 0;
  3568.         int origtopseq;
  3569.         int updateslider;
  3570.         int changedhoriz;
  3571.         int changedvert;
  3572.         int amount;
  3573.         ULONG newval;
  3574.         int refreshtype         = CurSettings.SimpleRefresh;
  3575.         int newspacing          = BoxInterGap;
  3576.         int newstatus           = StatusLine;
  3577.         int newgadgets          = GadgetsLine;
  3578.         int newmode         = MonitorType;
  3579.         int newalign            = RightAligned;
  3580.         int dohide          = 0;
  3581.  
  3582.         if (!MainWindow)
  3583.                 return;
  3584.  
  3585.         while ((imsg = GT_GetIMsg(MainWindowPort)) != NULL) {
  3586.                 ULONG code  = imsg->Code;
  3587.                 int shift   = imsg->Qualifier & IE_SHIFT;
  3588.                 int altctrl = imsg->Qualifier & (IE_ALT | IE_CTRL);
  3589.                 struct Gadget *gad;
  3590.  
  3591.                 switch (imsg->Class) {
  3592.                         
  3593.                         case IDCMP_CLOSEWINDOW:
  3594.                                 if (CurSettings.Setup.HideMethod == HIDE_NONE) {
  3595.                                         donemain = 1;
  3596.                                         QuitFlag = 1;
  3597.                                 } else {
  3598.                                         dohide = 1;
  3599.                                 }
  3600.                                 break;
  3601.  
  3602.                         case IDCMP_REFRESHWINDOW:
  3603.                                 /*
  3604.                                  *              Since the events currently displayed in our buffer
  3605.                                  *              window may have scrolled off the top of the buffer
  3606.                                  *              in the meantime, if we just blindly refresh, we
  3607.                                  *              might end up with the refreshed portions not matching
  3608.                                  *              the rest of the display. So, first try and redraw the
  3609.                                  *              buffer to ensure it contains current data. Normally,
  3610.                                  *              this will end up being a NOP.
  3611.                                  *
  3612.                                  *              See HandleNewEvents() for details as to why we need
  3613.                                  *              to lock our layers first.
  3614.                                  */
  3615.                                 DB("Begin Refresh Semapore\n");
  3616.                                 origtopseq = LastDrawnTopSeq;
  3617.                                 /*
  3618.                                 LOCK_LAYERS;
  3619.                                 ObtainSemaphore(&BufSem);
  3620.                                  */
  3621.                                 if (AttemptSemaphoreHeavely(&BufSem)) {
  3622.                                         if (TopSeq != origtopseq)
  3623.                                                 ShowBuffer(TopSeq, DISPLAY_ALL);
  3624.                                         GT_BeginRefresh(MainWindow);
  3625.                                         RedrawMainWindow();
  3626.                                         GT_EndRefresh(MainWindow, TRUE);
  3627.                                         DB("End refresh semaphore\n");
  3628.                                         ReleaseSemaphore(&BufSem);
  3629.                                 }
  3630.                                 /*
  3631.                                 UNLOCK_LAYERS
  3632.                                  */
  3633.                                 if (TopSeq != origtopseq)
  3634.                                         UpdateMainVScroll();
  3635.                                 break;
  3636.  
  3637.                         case IDCMP_MENUHELP:
  3638.                                 /*
  3639.                                  *              Display simple help for whatever menu was
  3640.                                  *              active when the HELP key was pressed. To decide
  3641.                                  *              which menu item to display help on, we simply
  3642.                                  *              build a description string from the menu name
  3643.                                  *              itself (replacing spaces with underscores and
  3644.                                  *              skipping over any dots).
  3645.                                  *
  3646.                                  *              We also strip out sub-menu items and just show
  3647.                                  *              the help for the parent item instead.
  3648.                                  */
  3649.                                 if (code != MENUNULL) {
  3650.                                         static int menunames[] = {
  3651.                                                 MSG_LINK_MENU_PROJECT,
  3652.                                                 MSG_LINK_MENU_WINDOW,
  3653.                                                 MSG_LINK_MENU_SETTINGS,
  3654.                                                 MSG_LINK_MENU_BUFFER
  3655.                                         };
  3656.                                         char msgname[50];
  3657.                                         char *msg;
  3658.                                         int menunum = MENUNUM(code);
  3659.                                         
  3660.                                         if (menunum == NOMENU)
  3661.                                                 msg = MSG(MSG_LINK_MAINWIN);
  3662.                                         else if (ITEMNUM(code) == NOITEM) {
  3663.                                                 msg = MSG(menunames[menunum]);
  3664.                                         } else {
  3665.                                                 struct MenuItem *item;
  3666.  
  3667.                                                 item = ItemAddress(MainWinMenu,code | SHIFTSUB(NOSUB));
  3668.                                                 if (!item || !(item->Flags & ITEMTEXT)) {
  3669.                                                         msg = MSG(menunames[menunum]);
  3670.                                                 } else {
  3671.                                                         char *p;
  3672.  
  3673.                                                         msg = ((struct IntuiText *)item->ItemFill)->IText;
  3674.                                                         strcpy(msgname, "LINK Menu_");
  3675.                                                         p = msgname + strlen(msgname);
  3676.  
  3677.                                                         while (*msg) {
  3678.                                                                 if (*msg == ' ' || *msg == '-')
  3679.                                                                         *p++ = '_';
  3680.                                                                 else if (*msg != '.' && *msg != '?')
  3681.                                                                         *p++ = *msg;
  3682.                                                                 msg++;
  3683.                                                         }
  3684.                                                         *p = '\0';
  3685.                                                         msg = msgname;
  3686.                                                 }
  3687.                                         }
  3688.                                         ShowAGuide(msg);
  3689.                                 }
  3690.                                 /*
  3691.                                  *              It's possible the user may have played with some
  3692.                                  *              menu toggles while the menu was displayed ... to
  3693.                                  *              avoid us missing any items that were changed, we
  3694.                                  *              simply reset them all back to the original state.
  3695.                                  */             
  3696.                                 SetMenuOptions();
  3697.                                 break;
  3698.  
  3699.                         case IDCMP_MENUPICK:
  3700.                                 while (code != MENUNULL) {
  3701.                                         struct MenuItem *item;
  3702.                                         int checked;
  3703.                                         
  3704.                                         item    = ItemAddress(MainWinMenu, code);
  3705.                                         checked = (item->Flags & CHECKED) == CHECKED;
  3706.  
  3707.                                         switch ((ULONG)GTMENUITEM_USERDATA(item)) {
  3708.  
  3709.                                                 case MID_OPENLOG:
  3710.                                                         /*
  3711.                                                          *              Open a new logfile
  3712.                                                          */
  3713.                                                         if (SelectFile(ChosenLogName, ChosenLogName,
  3714.                                                                                    MainWindow, FILESEL_NEWLOGNAME))
  3715.                                                         {
  3716.                                                                 if (!OpenLog(LOGMODE_PROMPT, ChosenLogName))
  3717.                                                                         ShowError(MSG(MSG_ERROR_STARTLOG),
  3718.                                                                                           DefaultLogName);
  3719.                                                         }
  3720.                                                         break;
  3721.  
  3722.                                                 case MID_CLOSELOG:
  3723.                                                         CloseLog();
  3724.                                                         break;
  3725.  
  3726.                                                 case MID_PAUSE:
  3727.                                                         if (checked)
  3728.                                                                 newmode = MONITOR_PAUSED;
  3729.                                                         else
  3730.                                                                 newmode = MONITOR_NORMAL;
  3731.                                                         break;
  3732.  
  3733.                                                 case MID_DISABLE:
  3734.                                                         if (checked)
  3735.                                                                 newmode = MONITOR_DISABLED;
  3736.                                                         else
  3737.                                                                 newmode = MONITOR_NORMAL;
  3738.                                                         break;
  3739.  
  3740.                                                 case MID_STEP:
  3741.                                                         SingleStep();
  3742.                                                         newmode = MonitorType;
  3743.                                                         break;
  3744.  
  3745.                                                 case MID_CHANGEPRI:
  3746.                                                         /*
  3747.                                                          *              New task priority selected -- let's
  3748.                                                          *              read the priority directly from the
  3749.                                                          *              menu item itself.
  3750.                                                          */
  3751.                                                         if (checked) {
  3752.                                                                 struct IntuiText *itext;
  3753.                                                                 int newpri;
  3754.                                                                 
  3755.                                                                 itext  = (void *)item->ItemFill;
  3756.                                                                 newpri = atoi(itext->IText);
  3757.                                                                 SetTaskPri(SysBase->ThisTask, newpri);
  3758.                                                         }
  3759.                                                         break;
  3760.  
  3761.                                                 case MID_HELP:
  3762.                                                         ShowAGuide(MSG(MSG_LINK_MAIN));
  3763.                                                         break;
  3764.  
  3765.                                                 case MID_ABOUT:
  3766.                                                         ShowError(MSG(MSG_ABOUT_SNOOPDOS), Version+6,
  3767.                                                                           PORT_NAME);
  3768.                                                         break;
  3769.  
  3770.                                                 case MID_HIDE:
  3771.                                                         dohide = 1;
  3772.                                                         break;
  3773.  
  3774.                                                 case MID_QUIT:
  3775.                                                         donemain = 1;
  3776.                                                         QuitFlag = 1;
  3777.                                                         break;
  3778.  
  3779.                                                 /*
  3780.                                                  *              Windows menu
  3781.                                                  */
  3782.                                                 case MID_SETUP:         
  3783.                                                         if (shift && SetWindow)
  3784.                                                                 CloseSettingsWindow();
  3785.                                                         else
  3786.                                                                 OpenSettingsWindow();
  3787.                                                         break;
  3788.  
  3789.                                                 case MID_FUNCTION:
  3790.                                                         if (shift && FuncWindow)
  3791.                                                                 CloseFunctionWindow();
  3792.                                                         else
  3793.                                                                 OpenFunctionWindow();
  3794.                                                         break;
  3795.  
  3796.                                                 case MID_FORMAT:
  3797.                                                         if (shift && FormWindow)
  3798.                                                                 CloseFormatWindow();
  3799.                                                         else
  3800.                                                                 OpenFormatWindow();
  3801.                                                         break;
  3802.  
  3803.                                                 case MID_SETWIDTH:
  3804.                                                 {
  3805.                                                         struct IntuiText *itext = (void *)item->ItemFill;
  3806.                                                         int newwidth                    = atoi(itext->IText);
  3807.                                                                 
  3808.                                                         SetMainWindowWidth(newwidth);
  3809.                                                         break;
  3810.                                                 }
  3811.                                                 case MID_ROWQUAL:
  3812.                                                 {
  3813.                                                         /*
  3814.                                                          *              Changing to a new row qualifier
  3815.                                                          */
  3816.                                                         int newqual = SUBNUM(code);
  3817.  
  3818.                                                         if (newqual != NOSUB)
  3819.                                                                 RowQual = newqual;
  3820.                                                         break;
  3821.                                                 }
  3822.                                                 case MID_STATUS:        newstatus  = checked;   break;
  3823.                                                 case MID_GADGETS:       newgadgets = checked;   break;
  3824.                                                 case MID_AUTO_OPEN:     AutoOpen   = checked;   break;
  3825.                                                 case MID_DISABLE_HIDDEN: DisableOnHide = checked;break;
  3826.  
  3827.                                                 case MID_SPACE_NONE:    if (checked) newspacing  = 0;
  3828.                                                                                                 break;
  3829.                                                 case MID_SPACE_1P:              if (checked) newspacing  = 1;
  3830.                                                                                                 break;
  3831.                                                 case MID_SPACE_2P:              if (checked) newspacing  = 2;
  3832.                                                                                                 break;
  3833.                                                 case MID_REF_SIMPLE:    if (checked) refreshtype = 1;
  3834.                                                                                                 break;
  3835.                                                 case MID_REF_SMART:             if (checked) refreshtype = 0;
  3836.                                                                                                 break;
  3837.                                                 case MID_ALIGN_LEFT:    if (checked) newalign    = 0;
  3838.                                                                                                 break;
  3839.                                                 case MID_ALIGN_RIGHT:   if (checked) newalign    = 1;
  3840.                                                                                                 break;
  3841.  
  3842.                                                 /*
  3843.                                                  *              Settings menu
  3844.                                                  */
  3845.                                                 case MID_LOAD:
  3846.                                                         if (SelectFile(ConfigFileName, ConfigFileName,
  3847.                                                                                    MainWindow, FILESEL_LOADCONFIG))
  3848.                                                         {
  3849.                                                                 /*
  3850.                                                                  *              This new settings file will becomoe
  3851.                                                                  *              the last saved file, so just load
  3852.                                                                  *              it in at the end instead
  3853.                                                                  */
  3854.                                                                 newsettings       = 0;
  3855.                                                                 loadlastsaved = 1;
  3856.                                                         }
  3857.                                                         break;
  3858.                                                         
  3859.                                                 case MID_SAVE:
  3860.                                                         if (!SaveConfig(DefaultConfigName, SAVE_NOICON))
  3861.                                                                 ShowError(MSG(MSG_ERROR_SAVING_SETTINGS),
  3862.                                                                                   DefaultConfigName);
  3863.                                                         break;
  3864.                                                 
  3865.                                                 case MID_SAVEAS:
  3866.                                                         if (SelectFile(ConfigFileName, ConfigFileName,
  3867.                                                                                    MainWindow, FILESEL_SAVECONFIG))
  3868.                                                         {
  3869.                                                                 if (!SaveConfig(ConfigFileName, SAVE_ICON)) {
  3870.                                                                         ShowError(MSG(MSG_ERROR_SAVING_SETTINGS),
  3871.                                                                                           ConfigFileName);
  3872.                                                                 }
  3873.                                                         }
  3874.                                                         break;
  3875.                                                         
  3876.                                                 case MID_RESET:
  3877.                                                         newsettings = &DefaultSettings;
  3878.                                                         break;
  3879.  
  3880.                                                 case MID_RESTORE:
  3881.                                                         newsettings = &RestoreSettings;
  3882.                                                         break;
  3883.  
  3884.                                                 case MID_LASTSAVED:
  3885.                                                         /*
  3886.                                                          *              If we successfully read in the defaults
  3887.                                                          *              file _or_ saved out a settings file, then
  3888.                                                          *              go ahead and read back in the last saved
  3889.                                                          *              file; otherwise, it's the very first
  3890.                                                          *              time we've ever been run by this user,
  3891.                                                          *              so just restore the factory settings
  3892.                                                          *              instead of producing an error message.
  3893.                                                          */
  3894.                                                         if (GotLastSaved) {
  3895.                                                                 newsettings   = 0;
  3896.                                                                 loadlastsaved = 1;
  3897.                                                         } else {
  3898.                                                                 newsettings   = &DefaultSettings;
  3899.                                                         }
  3900.                                                         break;
  3901.  
  3902.                                                 case MID_ICONS:
  3903.                                                         CreateIcons     = checked;
  3904.                                                         break;
  3905.  
  3906.                                                 /*
  3907.                                                  *              Buffer menu
  3908.                                                  */
  3909.                                                 case MID_COPYWIN:
  3910.                                                         /*
  3911.                                                          *              Copy current window to clipboard
  3912.                                                          */
  3913.                                                         DisableAllWindows();
  3914.                                                         if (!SaveBuffer(SAVEBUF_WINDOW, SAVEBUF_CLIPBOARD,
  3915.                                                                                         SAVEBUF_OVERWRITE))
  3916.                                                                 ShowError(MSG(MSG_ERROR_COPY_WIN_TO_CLIP));
  3917.                                                         EnableAllWindows();
  3918.                                                         break;
  3919.  
  3920.                                                 case MID_COPYBUF:
  3921.                                                         /*
  3922.                                                          *              Copy current buffer to clipboard
  3923.                                                          */
  3924.                                                         DisableAllWindows();
  3925.                                                         if (!SaveBuffer(SAVEBUF_ALL, SAVEBUF_CLIPBOARD,
  3926.                                                                                         SAVEBUF_OVERWRITE))
  3927.                                                                 ShowError(MSG(MSG_ERROR_COPY_ALL_TO_CLIP));
  3928.                                                         EnableAllWindows();
  3929.                                                         break;
  3930.  
  3931.                                                 case MID_SAVEWIN:
  3932.                                                         if (SelectFile(BufferFileName, BufferFileName,
  3933.                                                                                    MainWindow, FILESEL_SAVEWINDOW))
  3934.                                                         {
  3935.                                                                 DisableAllWindows();
  3936.                                                                 if (!SaveBuffer(SAVEBUF_WINDOW, BufferFileName,
  3937.                                                                                                 SAVEBUF_PROMPT))
  3938.                                                                 {
  3939.                                                                         ShowError(MSG(MSG_ERROR_SAVING_WINDOW),
  3940.                                                                                           BufferFileName);
  3941.                                                                 }
  3942.                                                                 EnableAllWindows();
  3943.                                                         }
  3944.                                                         break;
  3945.                                                         
  3946.                                                 case MID_SAVEBUF:
  3947.                                                         if (SelectFile(BufferFileName, BufferFileName,
  3948.                                                                                    MainWindow, FILESEL_SAVEBUFFER))
  3949.                                                         {
  3950.                                                                 DisableAllWindows();
  3951.                                                                 if (!SaveBuffer(SAVEBUF_ALL, BufferFileName,
  3952.                                                                                                 SAVEBUF_PROMPT))
  3953.                                                                 {
  3954.                                                                         ShowError(MSG(MSG_ERROR_SAVING_BUFFER),
  3955.                                                                                           BufferFileName);
  3956.                                                                 }
  3957.                                                                 EnableAllWindows();
  3958.                                                         }
  3959.                                                         break;
  3960.  
  3961.                                                 case MID_CLEARBUF:
  3962.                                                         ClearWindowBuffer();
  3963.                                                         break;
  3964.  
  3965.                                         }
  3966.                                         code = item->NextSelect;
  3967.                                 }
  3968.                                 break;
  3969.  
  3970.                         case IDCMP_IDCMPUPDATE:
  3971.                                 /*
  3972.                                  *              Handle BOOPSI slider movement. BOOPSI arrow 
  3973.                                  *              gadgets are no longer handled here but are
  3974.                                  *              treated as ordinary button gadgets since
  3975.                                  *              otherwise, we can't get the desired behaviour
  3976.                                  *              (with a delay after the first click before it
  3977.                                  *              starts repeating).
  3978.                                  */
  3979.                                 changedhoriz = 0;
  3980.                                 changedvert  = 0;
  3981.                                 updateslider = 0;
  3982.  
  3983.                                 GetAttr(PGA_Top, Gadget[GID_HSCROLLER], &newval);
  3984.                                 if (newval != LeftCol)
  3985.                                         ScrollHorizontal(newval - LeftCol);
  3986.  
  3987.                                 GetAttr(PGA_Top, Gadget[GID_VSCROLLER], &newval);
  3988.                                 if (newval != (TopSeq - FirstSeq)) {
  3989.                                         ShowBuffer(FirstSeq + newval, DISPLAY_QUICK);
  3990.                                         CheckForDirtyMainWindow();
  3991.                                 }
  3992.                                 break;
  3993.  
  3994.                         case IDCMP_INTUITICKS:
  3995.                                 if (ScrollDirection) {
  3996.                                         /*
  3997.                                          *              Handle possible arrow gadget scrolling
  3998.                                          */
  3999.                                         if (ScrollCount >= 3) {
  4000.                                                 if (Gadget[ScrollDirection]->Flags & GFLG_SELECTED)
  4001.                                                         DoArrowScrolling(ScrollDirection, 1);
  4002.                                         } else {
  4003.                                                 ScrollCount++;
  4004.                                         }
  4005.                                 }
  4006.                                 if (DraggingColumn) {
  4007.                                         /*
  4008.                                          *              Handle horizontal scrolling to left or right;
  4009.                                          *              we just treat it as a normal mouse move -- in
  4010.                                          *              the general case, the displacement will be
  4011.                                          *              zero, so no updating will take place.
  4012.                                          */
  4013.                                         MoveColumn(imsg->MouseX,
  4014.                                                            shift ? MOVECOL_FIXED : MOVECOL_FREE);
  4015.                                 }
  4016.                                 if (DraggingRow) {
  4017.                                         /*
  4018.                                          *              Dragging a highlight across rows, so we want to
  4019.                                          *              automatically scroll if we are off the top or
  4020.                                          *              bottom of the window. We also check to make sure
  4021.                                          *              we're not off the top or bottom of the buffer
  4022.                                          *              (no harm would come if we were, but it would
  4023.                                          *              lead to unnecessary flashing of the display
  4024.                                          *              highlight)
  4025.                                          */
  4026.                                         int scrolldir = 0;
  4027.  
  4028.                                         if (imsg->MouseY < BoxInTop && TopSeq > FirstSeq)
  4029.                                         {
  4030.                                                 scrolldir = GID_UPARROW;
  4031.                                         } else if (imsg->MouseY > (BoxInTop + BoxInHeight) &&
  4032.                                                            BottomSeq    < EndSeq)
  4033.                                         {
  4034.                                                 scrolldir = GID_DOWNARROW;
  4035.                                         }
  4036.                                         if (scrolldir) {
  4037.                                                 FinishDragRow();
  4038.                                                 DoArrowScrolling(scrolldir, 1);
  4039.                                                 StartDragRow(imsg->MouseY);
  4040.                                         }
  4041.                                 }
  4042.                                 break;
  4043.  
  4044.                         case IDCMP_MOUSEBUTTONS:
  4045.                         {
  4046.                                 static int lastrmb;
  4047.  
  4048.                                 int lmb = imsg->Qualifier & IEQUALIFIER_LEFTBUTTON;
  4049.                                 int rmb = imsg->Qualifier & IEQUALIFIER_RBUTTON;
  4050.  
  4051.                                 if (lmb && !rmb && !lastrmb) {
  4052.                                         static WORD lastx, lasty;
  4053.                                         static ULONG lastsecs, lastmicros;
  4054.                                         int curx = imsg->MouseX;
  4055.                                         int cury = imsg->MouseY;
  4056.  
  4057.                                         if (curx >= (lastx - 1) && curx <= (lastx + 1) &&
  4058.                                                 cury >= (lasty - 1) && cury <= (lasty + 1))
  4059.                                         {
  4060.                                                 if (DoubleClick(lastsecs, lastmicros,
  4061.                                                                                 imsg->Seconds, imsg->Micros)) {
  4062.                                                         /*
  4063.                                                          *              Got a double-click. Now check if it's
  4064.                                                          *              in the window header area. If so,
  4065.                                                          *              open or close the format window.
  4066.                                                          */
  4067.                                                         if (cury < BoxTop) {
  4068.                                                                 if (shift && FormWindow)
  4069.                                                                         CloseFormatWindow();
  4070.                                                                 else
  4071.                                                                         OpenFormatWindow();
  4072.                                                                 break;
  4073.                                                         }
  4074.                                                 }
  4075.                                         }
  4076.                                         lastx           = curx;
  4077.                                         lasty           = cury;
  4078.                                         lastsecs        = imsg->Seconds;
  4079.                                         lastmicros      = imsg->Micros;
  4080.                                 }
  4081.                                 if (DraggingColumn) {
  4082.                                         if (lmb == 0) {
  4083.                                                 /*
  4084.                                                  *              We've finished dragging our column
  4085.                                                  */
  4086.                                                 FinishDragCol();
  4087.                                         } else if (rmb) {
  4088.                                                 /*
  4089.                                                  *              User clicked right button to cancel the
  4090.                                                  *              drag operation, so restore column width
  4091.                                                  *              to its original position and cancel drag.
  4092.                                                  */
  4093. cancel_dragging_column:
  4094.                                                 /* Stop highlight flash when restoring orig header */
  4095.  
  4096.  
  4097.                                                 if (DraggingColumn) {
  4098.                                                         EventFormat *nextev = DragEvent + 1;
  4099.  
  4100.                                                         /*
  4101.                                                          *              We invert the columns now and then
  4102.                                                          *              set SelectEvent to NULL to let
  4103.                                                          *              FinishDragCol() know we have already
  4104.                                                          *              unhighlighted them (this is because
  4105.                                                          *              we're hacking the window format
  4106.                                                          *              directly).
  4107.                                                          */
  4108.                                                         InvertColumn(SelectStartCol - LeftCol - 1,
  4109.                                                                                  INVERT_FULLBOX);
  4110.                                                         InvertColumn(SelectStartCol - LeftCol +
  4111.                                                                                  SelectEvent->width, INVERT_FULLBOX);
  4112.                                                         SelectEvent = NULL;
  4113.                                                         DragEvent->width = DragOrigColWidth;
  4114.                                                         if (nextev->type != EF_END)
  4115.                                                                 nextev->width = NextOrigColWidth;
  4116.                                                         BufferWidth = OrigBufWidth;
  4117.                                                         if (LeftCol != OrigLeftCol) {
  4118.                                                                 LeftCol = OrigLeftCol;
  4119.                                                                 InitMainMargins();
  4120.                                                                 UpdateMainHScroll();
  4121.                                                         }
  4122.                                                         FinishDragCol();
  4123.                                                 }
  4124.                                                 lastrmb = 1;
  4125.                                                 break;
  4126.                                         }
  4127.                                 } else if (lmb && !lastrmb) {
  4128.                                         /*
  4129.                                          *              Could be starting to drag a new column or row!
  4130.                                          *              Check bounds to see if we are. If lastrmb was
  4131.                                          *              true, then user has just released right mouse
  4132.                                          *              button but hasn't yet released left mouse button,
  4133.                                          *              so we ignore this attempt..
  4134.                                          */
  4135.                                         int x = imsg->MouseX;
  4136.                                         int y = imsg->MouseY;
  4137.  
  4138.                                         if (y < BoxTop && x >= BoxInLeft &&
  4139.                                                                           x < (BoxInLeft + BoxInWidth)) {
  4140.                                                 StartDragCol(x);
  4141.                                         } else if (y > BoxInTop && y < (BoxInTop + BoxInHeight)) {
  4142.                                                 /*
  4143.                                                  *              Now check if correct qualifier is held down
  4144.                                                  *              to activate the row selection. We need to use
  4145.                                                  *              a slighty tricky boolean equation to handle
  4146.                                                  *              ROWQUAL_NONE, wher 
  4147.                                                  */
  4148.                                                 int qual = imsg->Qualifier & IE_ALL;
  4149.  
  4150.                                                 if (RowQual == ROWQUAL_ANY                                                       ||
  4151.                                                     (RowQual == ROWQUAL_NONE && !qual)                           ||
  4152.                                                     (qual && (qual & RowQualTable[RowQual]) == qual))
  4153.                                                 {
  4154.                                                         StartDragRow(y);
  4155.                                                 }
  4156.                                         }
  4157.                                 }
  4158.                                 if (DraggingRow && !lmb)
  4159.                                         FinishDragRow();
  4160.  
  4161.                                 lastrmb = rmb;
  4162.                                 break;
  4163.                         }
  4164.  
  4165.                         case IDCMP_MOUSEMOVE:
  4166.                                 if (DraggingColumn) {
  4167.                                         MoveColumn(imsg->MouseX,
  4168.                                                            shift ? MOVECOL_FIXED : MOVECOL_FREE);
  4169.                                 }
  4170.                                 if (DraggingRow)
  4171.                                         MoveToRow(imsg->MouseY);
  4172.                                 break;
  4173.  
  4174.                         case IDCMP_GADGETUP:
  4175.                         case IDCMP_GADGETDOWN:
  4176.                                 gad   = (struct Gadget *)imsg->IAddress;
  4177.  
  4178. handle_maingadgets:
  4179.                                 switch (gad->GadgetID) {
  4180.                                         case GID_UPARROW:
  4181.                                         case GID_DOWNARROW:
  4182.                                         case GID_LEFTARROW:
  4183.                                         case GID_RIGHTARROW:
  4184.                                                 if (imsg->Class == IDCMP_GADGETDOWN) {
  4185.                                                         /*
  4186.                                                          *              User clicked on a scroll button so
  4187.                                                          *              kick-off the scrolling.
  4188.                                                          */
  4189.                                                         ScrollDirection = gad->GadgetID;
  4190.                                                         DoArrowScrolling(ScrollDirection, 1);
  4191.                                                         MainWindow->IDCMPFlags |= IDCMP_INTUITICKS;
  4192.                                                         ScrollCount = 0;
  4193.                                                 } else {
  4194.                                                         /*
  4195.                                                          *              User let go of a scroll button so turn
  4196.                                                          *              off scrolling.
  4197.                                                          */
  4198.                                                         ScrollDirection = 0;
  4199.                                                         MainWindow->IDCMPFlags &= ~IDCMP_INTUITICKS;
  4200.                                                 }
  4201.                                                 break;
  4202.  
  4203.                                         case GID_SETUP:
  4204.                                                 if (shift && SetWindow)
  4205.                                                         CloseSettingsWindow();
  4206.                                                 else
  4207.                                                         OpenSettingsWindow();
  4208.                                                 break;
  4209.  
  4210.                                         case GID_FUNCTION:
  4211.                                                 if (shift && FuncWindow)
  4212.                                                         CloseFunctionWindow();
  4213.                                                 else
  4214.                                                         OpenFunctionWindow();
  4215.                                                 break;
  4216.  
  4217.                                         case GID_SAVESET:
  4218.                                                 /*
  4219.                                                  *              Save settings to configuration file
  4220.                                                  */
  4221.                                                 ShowStatus(MSG(MSG_STATUS_SAVESET));
  4222.                                                 DisableAllWindows();
  4223.                                                 if (!SaveConfig(DefaultConfigName, SAVE_NOICON))
  4224.                                                         ShowError(MSG(MSG_ERROR_SAVING_SETTINGS),
  4225.                                                                           DefaultConfigName);
  4226.                                                 EnableAllWindows();
  4227.                                                 UpdateStatus();
  4228.                                                 break;
  4229.  
  4230.                                         case GID_PAUSE:
  4231.                                                 if (gad->Flags & GFLG_SELECTED)
  4232.                                                         newmode = MONITOR_PAUSED;
  4233.                                                 else
  4234.                                                         newmode = MONITOR_NORMAL;
  4235.                                                 break;
  4236.  
  4237.                                         case GID_DISABLE:
  4238.                                                 if (gad->Flags & GFLG_SELECTED)
  4239.                                                         newmode = MONITOR_DISABLED;
  4240.                                                 else
  4241.                                                         newmode = MONITOR_NORMAL;
  4242.                                                 break;
  4243.  
  4244.                                         case GID_APPENDLOG:
  4245.                                         case GID_STARTLOG:
  4246.                                         case GID_SERIALLOG:
  4247.                                                 if (!OpenLog(DefaultLogMode, DefaultLogName))
  4248.                                                         ShowError(MSG(MSG_ERROR_STARTLOG), DefaultLogName);
  4249.                                                 break;
  4250.  
  4251.                                         case GID_OPENLOG:
  4252. #if 0
  4253.                                                 /*
  4254.                                                  *              Quick and dirty benchmark code to scroll
  4255.                                                  *              the buffer 10 times to check speed
  4256.                                                  */
  4257.                                                 { int i, l;
  4258.                                                 
  4259.                                                   for (i = 0; i < 10; i++)
  4260.                                                     for (l = FirstSeq; l < EndSeq; l += BoxRows)
  4261.                                                           ShowBuffer(l, DISPLAY_QUICK);
  4262.                                                   break;
  4263.                                                 }
  4264. #endif
  4265.                                                 /*
  4266.                                                  *              Back to normal: open a new log file
  4267.                                                  */
  4268.                                                 if (SelectFile(ChosenLogName, ChosenLogName,
  4269.                                                                            MainWindow, FILESEL_NEWLOGNAME))
  4270.                                                 {
  4271.                                                         OpenLog(LOGMODE_PROMPT, ChosenLogName);
  4272.                                                 }
  4273.                                                 break;
  4274.                                                 
  4275.                                         case GID_CLOSELOG:
  4276.                                                 CloseLog();
  4277.                                                 break;
  4278.  
  4279.                                         case GID_HIDE:
  4280.                                                 dohide = 1;
  4281.                                                 break;
  4282.  
  4283.                                         case GID_QUIT:
  4284.                                                 donemain = 1;
  4285.                                                 QuitFlag = 1;
  4286.                                                 break;
  4287.                                 }
  4288.                                 break;
  4289.  
  4290.                         case IDCMP_SIZEVERIFY:
  4291.                                 BeginResize();
  4292.                                 break;
  4293.  
  4294.                         case IDCMP_CHANGEWINDOW:
  4295.                         case IDCMP_NEWSIZE:
  4296.                                 if (!EndResize())
  4297.                                         donemain = 1;
  4298.                                 break;
  4299.                         
  4300.                         case IDCMP_INACTIVEWINDOW:
  4301.                                 /*
  4302.                                  *              If window becomes inactive, cancel any column
  4303.                                  *              drag currently in operation, since it could
  4304.                                  *              confuse things otherwise when the window is
  4305.                                  *              later re-activated.
  4306.                                  */
  4307.                                 if (activegad) {
  4308.                                         ShowGadget(MainWindow, Gadget[activegad], GADGET_UP);
  4309.                                         activegad = 0;
  4310.                                 }
  4311.                                 if (DraggingRow)
  4312.                                         FinishDragRow();
  4313.  
  4314.                                 if (DraggingColumn)
  4315.                                         goto cancel_dragging_column;
  4316.                                 break;
  4317.  
  4318.                         case IDCMP_RAWKEY:
  4319.                         {
  4320.                                 /*
  4321.                                  *              Handle all keyboard shortcuts
  4322.                                  */
  4323.                                 int upstroke = imsg->Code & 0x80;
  4324.                                 int keypress = ConvertIMsgToChar(imsg);
  4325.                                 UBYTE gadid  = MainKeyboard[keypress];
  4326.  
  4327.                                 if (activegad) {
  4328.                                         /*
  4329.                                          *              We're releasing a gadget that was pressed
  4330.                                          *              earlier, so handle it now
  4331.                                          */
  4332.                                         int samekey = (imsg->Code & 0x7f) == activekey;
  4333.  
  4334.                                         if (samekey && !upstroke)       /* Ignore repeated keys */
  4335.                                                 break;
  4336.  
  4337.                                         ShowGadget(MainWindow, Gadget[activegad], GADGET_UP);
  4338.                                         if (samekey) {
  4339.                                                 /*
  4340.                                                  *              Just released the key that was originally
  4341.                                                  *              pressed for this gadget, so now go and
  4342.                                                  *              handle the gadget action.
  4343.                                                  */
  4344.                                                 gadid     = activegad;
  4345.                                                 gad       = Gadget[gadid];
  4346.                                                 activegad = 0;
  4347.                                                 goto handle_maingadgets;
  4348.                                         }
  4349.                                         /*
  4350.                                          *              If the above check didn't kick in, it means
  4351.                                          *              we got a downpress of a different key, so
  4352.                                          *              disable the gadget for this key and continue
  4353.                                          *              to press the other key.
  4354.                                          */
  4355.                                         activegad = 0;
  4356.                                 }
  4357.  
  4358.                                 /*
  4359.                                  *              Handle simple keyboard input (i.e. vanilla keys)
  4360.                                  *
  4361.                                  *              In addition to the gadget shortcuts in MainKeyboard[]
  4362.                                  *              we also use TAB to unconditionally pause, and Space/
  4363.                                  *              Return to singlestep if we're already paused.
  4364.                                  *
  4365.                                  *              (If we made Space always single step, then if someone
  4366.                                  *              hit it by accident when the SnoopDos window was open,
  4367.                                  *              it would go into pause mode which would be annoying.)
  4368.                                  */
  4369.                                 if (gadid) {
  4370.                                         /*
  4371.                                          *              Handle a gadget shortcut
  4372.                                          */
  4373.                                         gad = Gadget[gadid];
  4374.                                         if (gadid == GID_PAUSE || gadid == GID_DISABLE) {
  4375.                                                 /*
  4376.                                                  *              PAUSE and DISABLE are toggle gadgets
  4377.                                                  *              which will highlight in the gadget code.
  4378.                                                  *              So, we handle them immediately, to give
  4379.                                                  *              instant feedback.
  4380.                                                  */
  4381.                                                 gad->Flags ^= GFLG_SELECTED;
  4382.                                                 goto handle_maingadgets;
  4383.                                         } else {
  4384.                                                 /*
  4385.                                                  *              Now a slight problem ... since our logfile
  4386.                                                  *              gadget can cycle between multiple values,
  4387.                                                  *              we had better make sure that the same
  4388.                                                  *              shortcut will activate whichever one is
  4389.                                                  *              currently displayed rather than the
  4390.                                                  *              default hotkey (since all three opens
  4391.                                                  *              may share the same hotkey). If the logfile
  4392.                                                  *              is already open, we do nothing.
  4393.                                                  */
  4394.                                                 if (gadid == GID_OPENLOG  || gadid == GID_APPENDLOG ||
  4395.                                                         gadid == GID_STARTLOG || gadid == GID_SERIALLOG)
  4396.                                                 {
  4397.                                                         if (LogActive)
  4398.                                                                 break;
  4399.  
  4400.                                                         gadid = LogButtonID;
  4401.                                                         gad   = Gadget[gadid];
  4402.                                                 }
  4403.  
  4404.                                                 /*
  4405.                                                  *              All these gadget are button gadgets so we
  4406.                                                  *              just highlight them now, and set the
  4407.                                                  *              activegad flag -- on the upstroke, they'll
  4408.                                                  *              be released.
  4409.                                                  */
  4410.                                                 ShowGadget(MainWindow, gad, GADGET_DOWN);
  4411.                                                 activegad = gadid;
  4412.                                                 activekey = imsg->Code;
  4413.                                         }
  4414.                                         break;
  4415.                                 }
  4416.  
  4417.                                 /*
  4418.                                  *              Didn't match one of the gadget hotkeys,
  4419.                                  *              now check for some other special keys
  4420.                                  */
  4421.                                 switch (keypress) {
  4422.                                         case 0x0D:              /* Return */
  4423.                                                 DoArrowScrolling(GID_DOWNARROW, 1);
  4424.                                                 break;
  4425.  
  4426.                                         case ' ':               /* Space */
  4427.                                                 if (DraggingColumn) {
  4428.                                                         /*
  4429.                                                          *              Redraw window with current format
  4430.                                                          *              to date so user can see how it looks
  4431.                                                          */
  4432.                                                         InvertColumn(SelectStartCol - LeftCol - 1,
  4433.                                                                                  INVERT_FULLBOX);
  4434.                                                         InvertColumn(SelectStartCol - LeftCol +
  4435.                                                                                  SelectEvent->width, INVERT_FULLBOX);
  4436.                                                         BuildFormatString(BufferEFormat,
  4437.                                                                                           BufFormat, MAX_FORM_LEN);
  4438.                                                         InstallNewFormat(NEW_STRING);
  4439.                                                         InvertColumn(SelectStartCol - LeftCol - 1,
  4440.                                                                                  INVERT_FULLBOX);
  4441.                                                         InvertColumn(SelectStartCol - LeftCol +
  4442.                                                                                  SelectEvent->width, INVERT_FULLBOX);
  4443.                                                 } else if (MonitorType == MONITOR_PAUSED) {
  4444.                                                         /*
  4445.                                                          *              Singlestep, but only if we were
  4446.                                                          *              already paused.
  4447.                                                          */
  4448.                                                         SingleStep();
  4449.                                                         newmode = MonitorType;
  4450.                                                 }
  4451.                                                 break;
  4452.  
  4453.                                         case 0x09:              /* Tab */
  4454.                                                 SingleStep();
  4455.                                                 newmode = MonitorType;
  4456.                                                 break;
  4457.  
  4458.                                         case 0x03:              /* CTRL-C -- quit               */
  4459.                                                 gadid = GID_QUIT;
  4460.                                                 ShowGadget(MainWindow, Gadget[gadid], GADGET_DOWN);
  4461.                                                 activegad = gadid;
  4462.                                                 activekey = imsg->Code;
  4463.                                                 break;
  4464.                                                 
  4465.                                         case 0x04:              /* CTRL-D -- disable    */
  4466.                                                 newmode = MONITOR_DISABLED;
  4467.                                                 break;
  4468.                                         
  4469.                                         case 0x05:              /* CTRL-E -- enable             */
  4470.                                                 newmode = MONITOR_NORMAL;
  4471.                                                 break;
  4472.  
  4473.                                         case 0x06:              /* CTRL-F -- to front   */
  4474.                                                 OpenMainWindow();
  4475.                                                 break;
  4476.  
  4477.                                         case '\033':    /* ESC -- cancel column drag */
  4478.                                                 if (DraggingColumn)
  4479.                                                         goto cancel_dragging_column;
  4480.                                                 break;
  4481.                                 }
  4482.  
  4483.                                 /*
  4484.                                  *              Finally, finally, we do a quick check to see
  4485.                                  *              if the keystroke matches the format editor
  4486.                                  *              menu shortcut; if it does, we open the format
  4487.                                  *              editor. (If this key has been used by one of
  4488.                                  *              the other gadgets, e.g. due to localisation,
  4489.                                  *              then we will never get here, so not a problem.)
  4490.                                  *
  4491.                                  *              As with the settings and functions windows, if
  4492.                                  *              the keypress is shifted, then we close the
  4493.                                  *              window if it was already open.
  4494.                                  */
  4495.                                 if ((keypress & 0x5F) == (*MSG(MSG_WINDOWS_FORMAT) & 0x5F)) {
  4496.                                         if (shift && FormWindow)
  4497.                                                 CloseFormatWindow();
  4498.                                         else
  4499.                                                 OpenFormatWindow();
  4500.                                         break;
  4501.                                 }
  4502.  
  4503.                                 /*
  4504.                                  *              Handle cursor keys etc. We allow scrolling by
  4505.                                  *              varying numbers of steps, depending on the
  4506.                                  *              qualifier key used.
  4507.                                  */
  4508.                                 amount  = 1;
  4509.                                 switch (imsg->Code) {
  4510.                                         case CURSORUP:
  4511.                                                 if (shift)   amount = BoxRows;
  4512.                                                 if (altctrl) amount = TopSeq - FirstSeq;
  4513.                                                 DoArrowScrolling(GID_UPARROW, amount);
  4514.                                                 break;
  4515.  
  4516.                                         case CURSORDOWN:
  4517.                                                 if (shift)   amount = BoxRows;
  4518.                                                 if (altctrl) amount = EndSeq - TopSeq;
  4519.                                                 DoArrowScrolling(GID_DOWNARROW, amount);
  4520.                                                 break;
  4521.  
  4522.                                         case CURSORLEFT:
  4523.                                                 if (shift)   amount = HSCROLL_SHIFT_JUMP;
  4524.                                                 if (altctrl) amount = LeftCol;
  4525.                                                 DoArrowScrolling(GID_LEFTARROW, amount);
  4526.                                                 break;
  4527.  
  4528.                                         case CURSORRIGHT:
  4529.                                                 if (shift)   amount = HSCROLL_SHIFT_JUMP;
  4530.                                                 if (altctrl) amount = BufferWidth - LeftCol;
  4531.                                                 DoArrowScrolling(GID_RIGHTARROW, amount);
  4532.                                                 break;
  4533.  
  4534.                                         case TABKEY:
  4535.                                                 if (shift)
  4536.                                                         newmode = MONITOR_NORMAL;
  4537.                                                 break;
  4538.  
  4539.                                         case HELPKEY:
  4540.                                                 ShowAGuide(MSG(MSG_LINK_MAINWIN));
  4541.                                                 break;
  4542.                                 }
  4543.                                 break;
  4544.                         }
  4545.                 }
  4546.                 GT_ReplyIMsg(imsg);
  4547.         }
  4548.         if (newmode != MonitorType)
  4549.                 SetMonitorMode(newmode);
  4550.  
  4551.         if (dohide)
  4552.                 HideSnoopDos();
  4553.         else if (donemain)
  4554.                 CloseMainWindow();
  4555.         else if (refreshtype != CurSettings.SimpleRefresh) {
  4556.                 BoxInterGap = newspacing;
  4557.                 StatusLine  = newstatus;
  4558.                 GadgetsLine = newgadgets;
  4559.                 CurSettings.SimpleRefresh = refreshtype;
  4560.                 ReOpenMainWindow();
  4561.         } else if (newsettings) {
  4562.                 InstallSettings(newsettings, SET_ALL);
  4563.         } else if (loadlastsaved) {
  4564.                 /*
  4565.                  *              We read the last-saved configuration from a file rather
  4566.                  *              than just from a memory copy so that the user can edit
  4567.                  *              the disk version by hand first if they so choose
  4568.                  */
  4569.                 if (!LoadConfig(ConfigFileName, MODE_INTERNAL, NULL))
  4570.                         ShowError(MSG(MSG_ERROR_LOADING_SETTINGS), ConfigFileName);
  4571.         } else {
  4572.                 if (newspacing != BoxInterGap)
  4573.                         SetTextSpacing(newspacing);
  4574.                 else if (newalign != RightAligned) {
  4575.                         RightAligned = newalign;
  4576.                         ShowBuffer(TopSeq, DISPLAY_ALL);
  4577.                 }
  4578.                 if (newgadgets != GadgetsLine || newstatus != StatusLine) {
  4579.                         StatusLine  = newstatus;
  4580.                         GadgetsLine = newgadgets;
  4581.  
  4582.                         if (!RecalcMainWindow(MainWindow->Width, MainWindow->Height,
  4583.                                                                   REDRAW_GADGETS))
  4584.                                 ShowError(MSG(MSG_ERROR_RESIZE));
  4585.                 }
  4586.         }
  4587. }
  4588.  
  4589. /*
  4590.  *              DrawSelectedLine(row, highlight)
  4591.  *
  4592.  *              Calculates what event is currently positioned at the specified row
  4593.  *              in the main window and redraws that row either highlighted or
  4594.  *              unhighlighted. If the event is now complete, its state is updated
  4595.  *              accordingly. If no event can be found at that line, then the nearest
  4596.  *              row is highlighted instead.
  4597.  *
  4598.  *              If highlight is true, the row is highlighted, else it is unhighlighted.
  4599.  */
  4600. void DrawSelectedLine(int row, int highlight)
  4601. {
  4602.         struct RastPort *rport = MainWindow->RPort;
  4603.         Event *ev;
  4604.         Event *firstevent;
  4605.         int   firstseq;
  4606.         int   fillpen = 0;
  4607.         int   textpen = 1;
  4608.         int   i;
  4609.  
  4610.         if (IsListEmpty(&EventList) || BufferEFormat[0].type == EF_END)
  4611.                 return;
  4612.  
  4613.         /*
  4614.          *              First, find the event
  4615.          */
  4616.          /*
  4617.         LOCK_LAYERS;
  4618.         ObtainSemaphore(&BufSem);
  4619.          */
  4620.         if (!AttemptSemaphoreHeavely(&BufSem))
  4621.                 return;
  4622.  
  4623.         firstevent = HeadNode(&EventList);
  4624.         firstseq   = firstevent->seqnum;
  4625.         ev         = TopEvent;
  4626.         if (TopSeq < firstseq)
  4627.                 ev = firstevent;
  4628.         
  4629.         for (i = 0; i < row && ev != TailNode(&EventList); i++, ev = NextNode(ev))
  4630.                 ;
  4631.         if (ev->status == ES_READY)
  4632.                 ev->status = ES_ACCEPTED;
  4633.  
  4634.         FormatEvent(BufferEFormat, ev, BufferLine, LeftCol, LeftCol + BoxCols - 1);
  4635.         /*
  4636.          *              Now setup rastport according to whether or not we want it
  4637.          *              highlighted
  4638.          */
  4639.         if (highlight) {
  4640.                 fillpen = ScreenDI->dri_Pens[FILLPEN];
  4641.                 textpen = ScreenDI->dri_Pens[FILLTEXTPEN];
  4642.         }
  4643.         SetAPen(rport, textpen);
  4644.         SetBPen(rport, fillpen);
  4645.         SetDrMd(rport, JAM2);
  4646.         Move(rport, BoxInLeft, BoxBaseline + i * BoxSpacing);
  4647.         Text(rport, BufferLine, BoxCols);
  4648.         ReleaseSemaphore(&BufSem);
  4649.         /*
  4650.         UNLOCK_LAYERS
  4651.         */
  4652. }
  4653.  
  4654. /*
  4655.  *              OutputBufLine(event, row)
  4656.  *
  4657.  *              Outputs the specified event to the buffer display at the given
  4658.  *              row, using the current format, left and right col indicators.
  4659.  *              See also DrawSelectedLine() above.
  4660.  */
  4661. void OutputBufLine(Event *event, int row)
  4662. {
  4663.         struct RastPort *rport = MainWindow->RPort;
  4664.  
  4665.         if (event->status == ES_READY)
  4666.                 event->status = ES_ACCEPTED;
  4667.  
  4668.         FormatEvent(BufferEFormat, event, BufferLine, LeftCol, RightCol);
  4669.         Move(rport, BoxInLeft, BoxBaseline + row*BoxSpacing);
  4670.         Text(rport, BufferLine, RightCol - LeftCol + 1);
  4671. }
  4672.  
  4673. /*
  4674.  *              ClearWindowBuffer()
  4675.  *
  4676.  *              Clears the buffer area on the screen, and empties the internal
  4677.  *              buffer as well (called by the Clear Buffer menu option).
  4678.  */
  4679. void ClearWindowBuffer(void)
  4680. {
  4681.         ClearBuffer();
  4682.         if (MainWindow) {
  4683.                 struct RastPort *rport = MainWindow->RPort;
  4684.  
  4685.                 /*
  4686.                  *              Erase strip to right of our rendered text
  4687.                  */
  4688.                 SetAPen(rport, 0);
  4689.                 SetDrMd(rport, JAM1);
  4690.                 RectFill(rport, BoxInLeft, BoxInTop,
  4691.                                                 BoxInLeft+BoxInWidth-1, BoxInTop+BoxInHeight-1);
  4692.  
  4693.                 UpdateMainVScroll();
  4694.         }
  4695.         ClearMainRHS = 0;
  4696. }
  4697.  
  4698. /*
  4699.  *              ShowBuffer(seqnum, displaytype)
  4700.  *
  4701.  *              Adjusts the buffer display so that it begins at event numbered
  4702.  *              seqnum. If more than half of the needed events are currently
  4703.  *              on display, the buffer is scrolled to put them in the correct
  4704.  *              position. Any lines not currently on display are redrawn.
  4705.  *
  4706.  *              Updates TopSeq and BottomSeq accordingly.
  4707.  *
  4708.  *              Also does validity checking to ensure that if our current set of
  4709.  *              events has scrolled off the top of the buffer, we will still be
  4710.  *              displayed.
  4711.  *
  4712.  *              Displaytype is DISPLAY_ALL or DISPLAY_QUICK, to force either
  4713.  *              the entire buffer to be redrawn dumbly at the new position, or
  4714.  *              to optimise it by scrolling, only drawing changed items, etc.
  4715.  *
  4716.  *              If displaytype is DISPLAY_NONE, then all the Seq variables are
  4717.  *              recalculated to take account of events scrolling off the top
  4718.  *              etc. but no new output is displayed. This allows us to update
  4719.  *              the scrollbar without interrupting the current screen display
  4720.  *              while the user is reading it.
  4721.  *
  4722.  *              Note: it is save to call ShowBuffer with DISPLAY_NONE when the
  4723.  *              main window is closed, but for all the others, it should be open.
  4724.  */
  4725. void ShowBuffer(LONG seqnum, int displaytype)
  4726. {
  4727.         struct RastPort *rport;
  4728.         LONG  oldtopseq = TopSeq;               /* Saved old top position                               */
  4729.         LONG  firstseq;                                 /* First sequence number on list                */
  4730.         Event *firstevent;                              /* First event on list                                  */
  4731.         Event *topev;                                   /* New top event displayed on screen    */
  4732.         int   row = 0;                                  /* Current output row in buffer                 */
  4733.         int   scrollthresh;                             /* If moving less than this, use scroll */
  4734.  
  4735.         /*
  4736.          *              If there is a lot of activity, then sometimes we can get
  4737.          *              output messages in between an IDCMP_VERIFY and an IDCMP_NEWSIZE
  4738.          *              which can lead to screen artefacts appearing in the border.
  4739.          *              One way to avoid these is to always refresh the borders when
  4740.          *              we get an IDCMP_NEWSIZE, but a neater way is to suspend output
  4741.          *              while we're between the two types of message -- this is less
  4742.          *              visually jarring on the user.
  4743.          */
  4744.         if (AwaitingResize != RESIZE_DONE)
  4745.                 displaytype = DISPLAY_NONE;
  4746.  
  4747.         /*
  4748.          *              Important that we lock our layers -- see HandleNewEvents()
  4749.          *              for a more detailed explanation of why.
  4750.          */
  4751.         DB("ShowBuffer-Semaphore\n");
  4752.         /*
  4753.         LOCK_LAYERS;
  4754.         ObtainSemaphore(&BufSem);
  4755.         */
  4756.         if (!AttemptSemaphoreHeavely(&BufSem))
  4757.                 return;
  4758.  
  4759.         if (IsListEmpty(&EventList) ||
  4760.             ((Event *)HeadNode(&EventList))->status == ES_CREATING)
  4761.                 goto done_show_buffer;
  4762.  
  4763.         firstevent = HeadNode(&EventList);
  4764.         firstseq   = firstevent->seqnum;
  4765.  
  4766.         if (TopSeq < firstseq) {
  4767.                 TopEvent    = firstevent;
  4768.                 TopSeq      = firstseq;
  4769.                 BottomSeq   = firstseq;
  4770.                 /*
  4771.                  *              The events currently on display in the window must have
  4772.                  *              scrolled off the top of the buffer, so we can't assume
  4773.                  *              any of them still exist. So, force a full redraw instead.
  4774.                  */
  4775.                 if (displaytype == DISPLAY_QUICK)
  4776.                         displaytype = DISPLAY_ALL;
  4777.         }
  4778.         if (EndSeq < firstseq) {
  4779.                 EndSeq          = firstseq;
  4780.                 EndEvent        = firstevent;
  4781.         }
  4782.         if (seqnum < firstseq)  seqnum = firstseq;
  4783.         if (seqnum > EndSeq)    seqnum = EndSeq;
  4784.         
  4785.         /*
  4786.          *              Work out if we need to scan forwards or backwards from the
  4787.          *              current event at the top of the screen
  4788.          */
  4789.         if (seqnum <= TopSeq) {
  4790.                 /*
  4791.                  *              Scanning backwards from current top of screen. Now see if
  4792.                  *              it would be faster to scan forward from the start of the
  4793.                  *              list.
  4794.                  */
  4795.                 if ((TopSeq - seqnum) < (seqnum - firstseq)) {
  4796.                         for (topev = TopEvent;
  4797.                                         topev != HeadNode(&EventList) && topev->seqnum > seqnum;
  4798.                                 topev = PrevNode(topev))
  4799.                                 ;
  4800.                 } else {
  4801.                         for (topev = firstevent;
  4802.                                         topev != TailNode(&EventList) && topev->seqnum < seqnum;
  4803.                                 topev = NextNode(topev))
  4804.                                 ;
  4805.                 }
  4806.         } else { /* seqnum > TopSeq */
  4807.                 /*
  4808.                  *              Scanning forwards from the current top of the screen. Now
  4809.                  *              see if it would be faster to scan backwards from the end of
  4810.                  *              the event list.
  4811.                  */
  4812.                 if ((EndSeq - seqnum) < (seqnum - TopSeq)) {
  4813.                         for (topev = EndEvent; topev != TopEvent && topev->seqnum > seqnum;
  4814.                                                                    topev = PrevNode(topev))
  4815.                                 ;
  4816.                 } else {
  4817.                         for (topev = TopEvent; topev != EndEvent && topev->seqnum < seqnum;
  4818.                                                                    topev = NextNode(topev))
  4819.                                 ;
  4820.                 }
  4821.         }
  4822.         
  4823.         /*
  4824.          *              Now topev is the new top event to display in the window.
  4825.          *              But wait! What if this topev is close to the bottom of our
  4826.          *              list? We want to always show a full window of text if
  4827.          *              possible. So, adjust it to ensure as many events as possible
  4828.          *              can fit in the screen.
  4829.          */
  4830.         while (topev != firstevent && seqnum > (EndSeq - BoxRows + 1)) {
  4831.                 topev = PrevNode(topev);
  4832.                 seqnum--;
  4833.         }
  4834.  
  4835.         /*
  4836.          *              Okay! Now we finally have topev pointing to our new top of
  4837.          *              screen node. Now update our display accordingly. We also
  4838.          *              take this opportunity to refresh our scroll bar, since it's
  4839.          *              relative position may well now be different.
  4840.          */
  4841.         FirstSeq  = firstseq;
  4842.         TopEvent  = topev;
  4843.         TopSeq    = seqnum;
  4844.  
  4845.         if ((displaytype & DISPLAY_NONE) || !MainWindow)
  4846.                 goto done_show_buffer;
  4847.  
  4848.         SetupBufferRastPort();
  4849.         rport = MainWindow->RPort;
  4850.  
  4851.         /*
  4852.          *              We record the top line that was last drawn into the buffer. This
  4853.          *              is useful when ShowBuffer(..., DISPLAY_NONE) is called since it
  4854.          *              let's us detect when the info inside the window is no longer valid
  4855.          *              due to having scrolled off the top of the buffer.
  4856.          */
  4857.         if (seqnum != oldtopseq)
  4858.                 LastDrawnTopSeq = TopSeq;
  4859.  
  4860.         if (displaytype & DISPLAY_ALL)
  4861.                 goto redrawall;
  4862.         
  4863.         /*
  4864.          *              Setup so we only render to bitplane 0 -- this greatly
  4865.          *              speeds up text output and rendering speed, especially
  4866.          *              on deep Workbenches.
  4867.          */
  4868.         if (GfxBase->LibNode.lib_Version >= 39)
  4869.                 SetWriteMask(rport, 1);         /* Speed up scrolling and rendering! */
  4870.         else
  4871.                 rport->Mask = 1;
  4872.  
  4873.         /* 
  4874.          *              Now setup our scroll threshold. If running on V37 (where
  4875.          *              when we scroll, we get backfilled with colour 0 before
  4876.          *              we can redraw), then our threshold is 1/2 the displayed
  4877.          *              number of rows. If running on V39 or above, we increase
  4878.          *              the threshold to 3/4 of the displayed rows.
  4879.          */
  4880.         scrollthresh = BoxRows / 2;
  4881.         if (GfxBase->LibNode.lib_Version >= 39)
  4882.                 scrollthresh = (BoxRows * 3) / 4;
  4883.  
  4884.         if (seqnum == oldtopseq) {
  4885.                 /*
  4886.                  *              We're already at the correct position in the buffer, so
  4887.                  *              just scan looking for new or updated entries (i.e. any
  4888.                  *              which are not yet ES_ACCEPTED)
  4889.                  */
  4890.                 while (row < BoxRows && seqnum <= EndSeq) {
  4891.                         if (topev->status != ES_ACCEPTED)
  4892.                                 OutputBufLine(topev, row);
  4893.                         topev = NextNode(topev);
  4894.                         seqnum++;
  4895.                         row++;
  4896.                 }
  4897.         } else if (seqnum > oldtopseq && (seqnum - oldtopseq) <= scrollthresh) {
  4898.                 /*
  4899.                  *              Scrolling forward, so move buffer up.
  4900.                  *
  4901.                  *              If running on V39 or above, we disable background fills
  4902.                  *              for our layer while we ScrollRaster() since we're going
  4903.                  *              to be redrawing the area anyway -- it speeds things up,
  4904.                  *              and is also less visually obtrusive.
  4905.                  */
  4906.                 int dy = seqnum - oldtopseq;
  4907.                 int scrollwidth = (RightCol - LeftCol + 1) * BoxCharWidth;
  4908.  
  4909.                 DB("ScrollRaster start\n");
  4910.                 /*
  4911.                  * THOR: ScrollRasterBF is buggy. Do not use it.
  4912.                  *
  4913.                 if (GfxBase->LibNode.lib_Version >= 39) {
  4914.                         struct Hook *oldhook;
  4915.  
  4916.                         oldhook = InstallLayerHook(MainWindow->WLayer, LAYERS_NOBACKFILL);
  4917.                         ScrollRasterBF(rport, 0, dy * BoxSpacing,
  4918.                                                    BoxInLeft, BoxInTop,
  4919.                                                    BoxInLeft + scrollwidth-1, BoxInTop + BoxInHeight-1);
  4920.                         InstallLayerHook(MainWindow->WLayer, oldhook);
  4921.                 } else
  4922.                  *
  4923.                  */
  4924.                        {
  4925.                         ScrollRaster(rport, 0, dy * BoxSpacing,
  4926.                                                  BoxInLeft, BoxInTop,
  4927.                                                  BoxInLeft + scrollwidth-1, BoxInTop + BoxInHeight-1);
  4928.                 }
  4929.                 DB("ScrollRaster end\n");
  4930.                                          
  4931.                 while (seqnum <= EndSeq && row < (BoxRows - dy)) {
  4932.                         /*
  4933.                          *              Even though these lines are already present,
  4934.                          *              we still have to re-output any lines that are
  4935.                          *              not ES_ACCEPTED since they may have been
  4936.                          *              updated since the last time they were displayed.
  4937.                          */
  4938.                         if (topev->status != ES_ACCEPTED)
  4939.                                 OutputBufLine(topev, row);
  4940.                         topev = NextNode(topev);
  4941.                         seqnum++;
  4942.                         row++;
  4943.                 }
  4944.                 while (seqnum <= EndSeq && row < BoxRows) {
  4945.                         OutputBufLine(topev, row);
  4946.                         topev = NextNode(topev);
  4947.                         seqnum++;
  4948.                         row++;
  4949.                 }
  4950.         } else if (oldtopseq > seqnum && (oldtopseq - seqnum) <= scrollthresh) {
  4951.                 /*
  4952.                  *              Scrolling backward, so move buffer down.
  4953.                  */
  4954.                 int dy = oldtopseq - seqnum;
  4955.                 int scrollwidth = (RightCol - LeftCol + 1) * BoxCharWidth;
  4956.  
  4957.                 DB("ScrollRaster start\n");
  4958.                 /*
  4959.                  * THOR: ScrollRasterBF is buggy...
  4960.                  *
  4961.                 if (GfxBase->LibNode.lib_Version >= 39) {
  4962.                         struct Hook *oldhook;
  4963.  
  4964.                         oldhook = InstallLayerHook(MainWindow->WLayer, LAYERS_NOBACKFILL);
  4965.                         ScrollRasterBF(rport, 0, -dy * BoxSpacing,
  4966.                                                    BoxInLeft, BoxInTop,
  4967.                                                    BoxInLeft + scrollwidth-1, BoxInTop+BoxInHeight-1);
  4968.                         InstallLayerHook(MainWindow->WLayer, oldhook);
  4969.                 } else
  4970.                  *
  4971.                  */
  4972.                         {
  4973.                         ScrollRaster(rport, 0, -dy * BoxSpacing,
  4974.                                                  BoxInLeft, BoxInTop,
  4975.                                                  BoxInLeft + scrollwidth-1, BoxInTop + BoxInHeight-1);
  4976.                 }
  4977.                 DB("ScrollRaster end\n");
  4978.                 /*
  4979.                  *              Update the new (blank) areas first
  4980.                  */
  4981.                 while (seqnum <= oldtopseq) {
  4982.                         OutputBufLine(topev, row);
  4983.                         topev = NextNode(topev);
  4984.                         seqnum++;
  4985.                         row++;
  4986.                 }
  4987.                 /*
  4988.                  *              Now can the remaining lines in the buffer (which are
  4989.                  *              already on the screen) and re-output any which are
  4990.                  *              not ES_ACCEPTED, since they may have changed since the
  4991.                  *              last update.
  4992.                  */
  4993.                 while (row < BoxRows && seqnum <= EndSeq) {
  4994.                         if (topev->status != ES_ACCEPTED)
  4995.                                 OutputBufLine(topev, row);
  4996.                         topev = NextNode(topev);
  4997.                         seqnum++;
  4998.                         row++;
  4999.                 }
  5000.         } else {
  5001.                 /*
  5002.                  *              The move is too big to handle by scrolling, so just
  5003.                  *              redraw the entire buffer instead.
  5004.                  */
  5005. redrawall:
  5006.                 while (row < BoxRows && seqnum <= EndSeq) {
  5007.                         OutputBufLine(topev, row);
  5008.                         topev = NextNode(topev);
  5009.                         seqnum++;
  5010.                         row++;
  5011.                 }
  5012.         }
  5013.  
  5014.         if ((displaytype & DISPLAY_ALL) == 0) {
  5015.                 /*
  5016.                  *              Undo the bitplane optimisation we did earlier, but only
  5017.                  *              if we're doing an optimised refresh.
  5018.                  */
  5019.                 if (GfxBase->LibNode.lib_Version >= 39)
  5020.                         SetWriteMask(rport, 0xFF);
  5021.                 else
  5022.                         rport->Mask = 0xff;
  5023.         }
  5024.         BottomSeq = seqnum - 1; /* Bottom most sequence number in window */
  5025.  
  5026. done_show_buffer:
  5027.         DB("ShowBuffer-Semaphore-done\n");
  5028.         ReleaseSemaphore(&BufSem);
  5029.         /*
  5030.         UNLOCK_LAYERS
  5031.          */
  5032. }
  5033.  
  5034. /*
  5035.  *              HandleNewEvents()
  5036.  *
  5037.  *              Called whenever we are signalled that a new event has arrived
  5038.  *              in the input queue.
  5039.  *
  5040.  *              We simply scan the queue from our last known position to our
  5041.  *              current position and, if necessary, update our output window
  5042.  *              to reflect any new events. We also output any new _complete_
  5043.  *              events to the logfile.
  5044.  */
  5045. void HandleNewEvents(void)
  5046. {
  5047.         Event *firstevent;
  5048.         Event *newev;
  5049.         Event *ev;
  5050.         int   curendcompleteseq = EndCompleteSeq;
  5051.         int   curendseq         = EndSeq;
  5052.         LONG  newseq;
  5053.         int   refresh = 0;
  5054.  
  5055.         DB("HandleNewEvent-Semaphore\n");
  5056.  
  5057.         if (IsListEmpty(&EventList) || AwaitingResize != RESIZE_DONE
  5058.             || DraggingColumn || DraggingRow)
  5059.                 return;
  5060.         
  5061.         /*
  5062.          *              It is vital that for the duration of the screen update,
  5063.          *              we retain complete access to our output rastport. Otherwise,
  5064.          *              we can run into a deadlock where a higher priority task
  5065.          *              (like input.device) locks the screen layer's while we
  5066.          *              are in the middle of our refresh, and then calls one of
  5067.          *              our patched functions (like OpenFont) -- instant hang
  5068.          *              (because we own the buffer semaphore and the patched
  5069.          *              function will sit waiting for us to free it).
  5070.          *
  5071.          *              By locking our layers, we ensure that nobody else can
  5072.          *              ever get in once we start our refresh. The downside is
  5073.          *              that this locks out menu rendering etc, but since we'll
  5074.          *              be quick at refreshing, that's okay.
  5075.          *
  5076.          *              Note that we lock the layers _before_ getting the buffer
  5077.          *              semaphore -- if we do it afterwards, there is a window of
  5078.          *              opportunity where we task switch to a higher priority task
  5079.          *              between the two calls which then proceeds to lock layers
  5080.          *              and try and lock the buffer semaphore -- deadlock again!
  5081.          *
  5082.          *              News Flash! We don't lock layers any more, since it lead
  5083.          *              to jerky performance when dragging the scrollbar etc.
  5084.          *              Instead, our patch code checks to see if our layers are
  5085.          *              locked by the calling task and if they are, bypasses
  5086.          *              outputting the event completely. Since the only things
  5087.          *              that ever call our patches when layers are locked are
  5088.          *              low-level Intuition functions that are re-rendering
  5089.          *              gadgets etc (and calling OpenFont()) this is actually
  5090.          *              a win-win since we're typically not interested in those
  5091.          *              events anyway.
  5092.          */             
  5093.  
  5094.         /*
  5095.         if (MainWindow) {
  5096.                 LOCK_LAYERS;            \* This is now a no-op *\
  5097.         }
  5098.         */
  5099.  
  5100.         /*
  5101.          *
  5102.         ObtainSemaphore(&BufSem);
  5103.          *
  5104.          */
  5105.         if (!AttemptSemaphoreHeavely(&BufSem))
  5106.                 return;
  5107.  
  5108.         if (EndSeq == BottomSeq)                        /* If at end of buf, show new o/p       */
  5109.                 refresh = 1;
  5110.  
  5111.         firstevent = HeadNode(&EventList);
  5112.         if (firstevent->seqnum > EndCompleteSeq)
  5113.                 newev = firstevent;
  5114.         else if (EndCompleteEvent == TailNode(&EventList)) {
  5115.                 newev = EndCompleteEvent;
  5116.         } else {
  5117.                 newev = NextNode(EndCompleteEvent);
  5118.         }
  5119.         newseq = newev->seqnum;
  5120.         
  5121.         /*
  5122.          *              First we scan all the new incoming entries to figure out
  5123.          *              our new EndEvent/EndSeq (for ShowBuffer)
  5124.          */
  5125.         for (ev = newev; NextNode(ev) != NULL && ev->status != ES_CREATING;
  5126.                                          ev = NextNode(ev))
  5127.                 ;
  5128.         EndEvent = PrevNode(ev);
  5129.         EndSeq   = EndEvent->seqnum;
  5130.  
  5131.         if (MainWindow) {
  5132.                 if (refresh) {
  5133.                         /*
  5134.                          *              We were already displaying the end of the buffer and we
  5135.                          *              got new info in, so move the display to the new end of
  5136.                          *              buffer.
  5137.                          */
  5138.                         ShowBuffer(EndSeq, DISPLAY_QUICK);
  5139.                 } else if (BottomSeq > EndCompleteSeq) {
  5140.                         /*
  5141.                          *              We weren't at the very end of the buffer, but there was an
  5142.                          *              unfinished event displayed somewhere on the screen, so
  5143.                          *              rescan the currently displayed bufferlines in case it
  5144.                          *              needs to be updated.
  5145.                          */
  5146.                         ShowBuffer(TopSeq, DISPLAY_QUICK);              
  5147.                 } else {
  5148.                         /*
  5149.                          *              We're too far up in the buffer to show any of the
  5150.                          *              new data, but update our internal variables anyway
  5151.                          *              so we can keep the scroll bar up to date.
  5152.                          */
  5153.                         ShowBuffer(TopSeq, DISPLAY_NONE);
  5154.                 }
  5155.         } else {
  5156.                 /*
  5157.                  *              Simply update TopSeq accordingly so it will be correct the
  5158.                  *              next time we open the window
  5159.                  */
  5160.                 TopSeq   = EndSeq;
  5161.                 TopEvent = EndEvent;
  5162.         }
  5163.  
  5164.         /*
  5165.          *              Now mark all new events which are finished as complete, so that
  5166.          *              we won't rescan them
  5167.          */
  5168.         for (ev = newev; NextNode(ev) != NULL && ev->status >= ES_READY;
  5169.                          ev = NextNode(ev)) {
  5170.                 EndCompleteEvent = ev;
  5171.                 EndCompleteSeq   = ev->seqnum;
  5172.         }
  5173.         DB("HandleNewEvent-Semaphore-done\n");
  5174.         ReleaseSemaphore(&BufSem);
  5175.  
  5176.         if (MainWindow) {
  5177.                 CheckForDirtyMainWindow();
  5178.                 /*
  5179.                 UNLOCK_LAYERS
  5180.                 */
  5181.                 UpdateMainVScroll();
  5182.         }
  5183.  
  5184.         if (LogActive && !Disabled) {
  5185.                 /*
  5186.                  *              Now output the new events to our logfile. We have to be
  5187.                  *              very careful doing this since we must not keep the buffer
  5188.                  *              semaphore locked while doing file output (it could lead
  5189.                  *              to deadlocks if the file output causes a requester to
  5190.                  *              appear on the screen, for example). Thus, we must lock
  5191.                  *              and unlock the semaphore as we scan each event. Sigh.
  5192.                  */
  5193.                 LONG latestready;               /* seqnum of highest event that's ES_READY */
  5194.  
  5195.                 /*
  5196.                  *              Output the new events to the logfile. We only output
  5197.                  *              events that are complete, or events that are incomplete
  5198.                  *              but which have a complete event after them. Any complete
  5199.                  *              events that have been output earlier are not output
  5200.                  *              again, unless they were only partially output the first
  5201.                  *              time.
  5202.                  */
  5203.                 ObtainSemaphore(&BufSem);
  5204.                 ev = HeadNode(&EventList);
  5205.                 if (ev->seqnum > newseq) {
  5206.                         newev  = ev;
  5207.                         newseq = ev->seqnum;
  5208.                 } else if (newseq == curendcompleteseq) {
  5209.                         /*
  5210.                          *              We couldn't advance past the endcompleteseq earlier on
  5211.                          *              because there was nothing beyond it, so step past now.
  5212.                          */
  5213.                         newev = NextNode(newev);
  5214.                         newseq++;
  5215.                 }
  5216.                 for (ev = TailNode(&EventList);
  5217.                         ev->seqnum > newseq && ev->status < ES_READY; ev = PrevNode(ev))
  5218.                         ;
  5219.                 latestready = ev->seqnum;
  5220.                 if (ev->status < ES_READY)
  5221.                         latestready--;          /* Just in case ev->seqnum == newseq */
  5222.  
  5223.                 for (ev = newev; NextNode(ev) != NULL && ev->seqnum <= latestready;
  5224.                                                  ev = NextNode(ev)) {
  5225.                         int firstchar = 0;
  5226.  
  5227.                         /*
  5228.                          *              For each event that is either complete, or partially
  5229.                          *              complete but followed by a complete event, output it
  5230.                          *              to the logfile (unless already output).
  5231.                          */
  5232.                         if (ev->status >= ES_READY) {
  5233.                                 if ((ev->flags & EFLG_LOGDONE) == 0) {
  5234.                                         if (ev->flags & EFLG_LOGPARTIAL)
  5235.                                                 firstchar = '\\';
  5236.                                         else
  5237.                                                 firstchar = ' ';
  5238.                                         ev->flags |= EFLG_LOGDONE;
  5239.                                 }
  5240.                         } else if ((ev->flags & EFLG_LOGPARTIAL) == 0) {
  5241.                                 /*
  5242.                                  *              Output partial event
  5243.                                  */
  5244.                                 ev->flags |= EFLG_LOGPARTIAL;
  5245.                                 firstchar = '/';
  5246.                         }
  5247.  
  5248.                         if (firstchar) {
  5249.                                 /*
  5250.                                  *              Output the line
  5251.                                  */
  5252.                                 FormatEvent(LogEFormat, ev, BufferLine+1, 0, LogWidth - 1);
  5253.                                 BufferLine[0]                     = firstchar;
  5254.                                 BufferLine[LogWidth+1] = '\n';
  5255.                                 BufferLine[LogWidth+2] = 0;
  5256.                                 ReleaseSemaphore(&BufSem);
  5257.                                 WriteLog(BufferLine);
  5258.                                 ObtainSemaphore(&BufSem);
  5259.                                 /*
  5260.                                  *              While we had the semaphore released, it's possible
  5261.                                  *              that the event we're on may have scrolled off the
  5262.                                  *              buffer. If it has, then give up right now (we'll
  5263.                                  *              catch the new ones the next time around).
  5264.                                  */
  5265.                                 if (((Event *)HeadNode(&EventList))->seqnum > latestready)
  5266.                                         break;
  5267.                         }
  5268.                 }
  5269.                 ReleaseSemaphore(&BufSem);
  5270.                 if (Paused)
  5271.                         WriteLog(NULL); /* Flush log if we're single-stepping */
  5272.         }
  5273.         if (AutoOpen && !MainWindow && curendseq < EndSeq)
  5274.                 ShowSnoopDos();
  5275. }
  5276.  
  5277. /*
  5278.  *              CleanupMainWindow()
  5279.  *
  5280.  *              Frees any resources associated with this module
  5281.  */
  5282. void CleanupMainWindow(void)
  5283. {
  5284.         CloseMainWindow();
  5285.         if (MainGadList)        FreeGadgets(MainGadList),       MainGadList             = NULL;
  5286.         if (BufferFont)         CloseFont(BufferFont),          BufferFont              = NULL;
  5287.         if (LogActive)          CloseLog();
  5288. }
  5289.