//*************************************************************************//
// Filename:    WindowList.c
// Autor:       Christian Taulien of Strange Intelligence
// Purpose:     WindowList-Function for Felix.api
// Creation:    24. März 1998
//*************************************************************************//
#include "WindowList.h"
#include "processcomm.h"
#include "freshlocale.h"

#include <string.h>
#include <stdio.h>

#include <exec/exec.h>
#include <utility/utility.h>
#include <diskfont/diskfont.h>
#include <graphics/gfx.h>
#include <dos/dos.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/utility_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/graphics_protos.h>
#include <clib/gadtools_protos.h>

#include <intuition/gadgetclass.h>

#define SIFC_MAX(a,b) ((a)>(b) ? (a) : (b))
#define SIFC_MIN(a,b) ((a)<(b) ? (a) : (b))

extern "C" void NewList(struct List *list);
extern FLXWatchListC *glob_poWatches;
extern WindowListC  *glob_poWindowList;
/*S*/ // Image-Zeug
#pragma chip
LONG Test;
UWORD modifiedData[] =
{
  /* Plane 0 */
  0x0080,0x0000,0x02C0,0x0000,0x0460,0x2000,0x08B0,0x2000,
  0x1158,0x2000,0x229C,0x2000,0x453E,0x2000,0x4A5A,0x2000,
  0x74BC,0x2000,0x3956,0x2000,0x1AAC,0xE000,0x0ED8,0x0000,
  0x0750,0x0000,0x03A0,0x2000,0x01C0,0x2000,0x0180,0xE000,
  /* Plane 1 */
  0x0100,0x0000,0x0381,0xC000,0x04C1,0x0000,0x09E1,0x0000,
  0x13F1,0x0000,0x27F9,0x0000,0x4FFD,0x0000,0x9FEF,0x0000,
  0xDFC7,0x0000,0x6F81,0x0000,0x3740,0x0000,0x1F60,0x0000,
  0x0FA1,0xC000,0x07C1,0x0000,0x0381,0x0000,0x0000,0x0000,
  /* Plane 2 */
  0x0000,0x0000,0x0200,0x0000,0x0400,0x0000,0x0880,0x0000,
  0x1140,0x0000,0x2280,0x0000,0x4500,0x0000,0x0A00,0x0000,
  0x1430,0x0000,0x0878,0x0000,0x00B8,0x0000,0x0010,0x0000,
  0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
  /* Plane 3 */
  0x0100,0x0000,0x0180,0x0000,0x03C0,0xC000,0x0660,0xC000,
  0x0C30,0xC000,0x1838,0xC000,0x3064,0xC000,0xE0CE,0xC000,
  0xE186,0xC000,0x7300,0xC000,0x3E00,0x0000,0x1E40,0x0000,
  0x0F00,0x0000,0x0680,0xC000,0x0380,0xC000,0x0000,0x0000
};
#pragma fast
struct Image oModifyImg =
{
  0, 0,   /* LeftEdge, TopEdge */
  19, 16, 4,  /* Width, Height, Depth */
  modifiedData, /* ImageData */
  0x000F, 0x0000, /* PlanePick, PlaneOnOff */
  NULL    /* NextImage */
};
/*E*/

char *createNameFromInstance(struct APIInstance *arg_poAPIInstance)
/*S*/
{
  TRACE("Entry");

  char *name = NULL;
  char *sDocName = ((struct EditConfig *) arg_poAPIInstance->api_Environment)->Name;
  ULONG sDocNameLen = strlen(sDocName);

  if (strlen(sDocName)>0)
  {
    name = new char [sDocNameLen+2];

    // if the speicher could be reserviert
    if (name)
    {
      strcpy(name, sDocName);
    } // if
  } // if
return name;
}
/*E*/

//******************************************************************//
//******************************************************************//
//
//  WNameNodeC
//
//******************************************************************//
//******************************************************************//
WNameNodeC::WNameNodeC(struct APIInstance *arg_poInstance, WNameListC *arg_poNameList)
/*S*/
{
  TRACE("Konstruktor");
  // init the baseclassmembers
  ln_Succ       = NULL;
  ln_Pred       = NULL;
  ln_Type       = NT_USER;
  ln_Pri        = 0;

  // now our members
  m_poAPIInstance = arg_poInstance;

  struct EditConfig *poEditConfig = (struct EditConfig *) arg_poInstance->api_Environment;
  m_bModified     = poEditConfig->DocModified || poEditConfig->LineModified;
  ln_Name         = createNameFromInstance(arg_poInstance);

} // WNameNodeC::WNameNodeC()
/*E*/
WNameNodeC::~WNameNodeC()
/*S*/
{
  TRACE("Destruktor");

  if (ln_Name)
  {
    delete ln_Name;
  } // if

  // if the node is in a list
  if (ln_Pred && ln_Succ)
  {
    Remove(this);
  } // if
}
/*E*/
WNameNodeC *WNameNodeC::getNext(void)
/*S*/
{
  TRACE("getNext()");
  if (ln_Succ && ln_Succ->ln_Succ)
  {
    return (WNameNodeC *) ln_Succ;
  } // if

return NULL;
}
/*E*/
WNameNodeC *WNameNodeC::getPrev(void)
/*S*/
{
  TRACE("getPrev()");
  if (ln_Pred && ln_Pred->ln_Pred)
  {
    return (WNameNodeC *) ln_Pred;
  } // if

return NULL;
}
/*E*/

//******************************************************************//
//******************************************************************//
//
//  WNameListC
//
//******************************************************************//
//******************************************************************//
WNameListC::WNameListC()
/*S*/
{
  TRACE("Konstruktor");
  NewList(this);
} // WNameListC::WNameListC()
/*E*/
WNameListC::~WNameListC()
/*S*/
{
  TRACE("Destruktor");
  removeAll();

} // WNameListC::~FLXWacthListC()
/*E*/
void WNameListC::removeAll()
/*S*/
{
  TRACE("Entry");
  // wenn die Liste nicht leer ist
  if (!IsListEmpty(this))
  {
    WNameNodeC *pNode;
    while (pNode = (WNameNodeC *) RemHead(this))
    {
      pNode->ln_Succ = NULL;
      pNode->ln_Pred = NULL;
      delete pNode;
    } // while
  } // if
  NewList(this);
}
/*E*/
BOOL WNameListC::addWName(struct APIInstance *arg_poInstance)
/*S*/
{
  TRACE("addWName()");
  TRACE(arg_poInstance->api_Name);
  // neuen Watch erzeugen
  WNameNodeC *poNode = new WNameNodeC(arg_poInstance, this);
  if (poNode)
  {
    // hänge diesen in die Liste ein
    AddTail(this, poNode);
  }
  else
  {
    TRACE("Kein Speicher");
    return FALSE;
  } // if
return TRUE;
}
/*E*/
int WNameListC::sortIn(struct APIInstance *arg_poInstance)
/*S*/
{
  TRACE("sortIn()");
  int iIndex = -1;

  // neue Node erzeugen
  WNameNodeC *poNode = new WNameNodeC(arg_poInstance, this);
  if (poNode)
  {
    iIndex = 0;
    // wenn die Watchliste nicht leer ist
    if (!IsListEmpty(this))
    {
      // hole letzen eintrag
      WNameNodeC *pCurNode = (WNameNodeC *) lh_TailPred;
      do
      {
        // wenn der neue eintrag kleiner ist, als der aktuelle
        if (Stricmp(pCurNode->ln_Name, poNode->ln_Name)<=0)
        {
          Insert(this, poNode, pCurNode);
          return iIndex;
        } // if

        iIndex++;
      } while (pCurNode = pCurNode->getPrev());
    } // if

    // hänge diesen vor die Liste.
    AddHead(this, poNode);
  }
  else
  {
    TRACE("Kein Speicher");
    return FALSE;
  } // if
return iIndex;
}
/*E*/
WNameNodeC *WNameListC::findWName(struct APIInstance *arg_poInstance)
/*S*/
{
  TRACE("Entry");
  WNameNodeC *pNode = NULL;

  // wenn die Watchliste nicht leer ist
  if (!IsListEmpty(this))
  {
    pNode = (WNameNodeC *) lh_Head;
    do
    {
      if (pNode->m_poAPIInstance == arg_poInstance)
      {
        return pNode;
      } // if
    } while (pNode = pNode->getNext());
  } // if

return pNode;
}
/*E*/
int WNameListC::indexOf(WNameNodeC *arg_pNode)
/*S*/
{
  TRACE("Entry");

  // wenn die Watchliste nicht leer ist
  if (arg_pNode && !IsListEmpty(this))
  {
    int iIndex = 0;

    // hole 1. eintrag
    WNameNodeC *pNode = (WNameNodeC *) lh_Head;
    do
    {
      // wenn gefunden
      if (pNode==arg_pNode)
      {
        return iIndex;
      } // if

      iIndex++;
    } while (pNode = pNode->getNext());
  } // if

return -1;
}
/*E*/
WNameNodeC *WNameListC::operator[](int arg_iIndex)
/*S*/
{ 
  TRACE("ENTRY");

  WNameNodeC *pNode = NULL;

  // wenn die Watchliste nicht leer ist
  if (!IsListEmpty(this))
  {
    int index = 0;

    // hole 1. eintrag
    pNode = (WNameNodeC *) lh_Head;
    do
    {
      // wenn i.ten eintrag erreicht
      if (index == arg_iIndex)
      {
        return pNode;
      } // if

      index++;
    } while (pNode = pNode->getNext());
  } // if

return pNode;
}
/*E*/
int WNameListC::getSize(void)
/*S*/
{
  TRACE("ENTRY");
  int iSize = 0;

  // wenn die Liste nicht leer ist
  if (!IsListEmpty(this))
  {
    // hole 1. Eintrag
    WNameNodeC *pNode = (WNameNodeC *) lh_Head;
    for (iSize=1; pNode = pNode->getNext(); iSize++);
  } // if

return iSize;
}
/*E*/

//******************************************************************//
//******************************************************************//
//
//  VisualInfoC
//
//******************************************************************//
//******************************************************************//
VisualInfoC::~VisualInfoC()
/*S*/
{
  TRACE("Entry");
  // wenn der VisualInfo-Member gültig ist.
  if (m_iVisualInfo)
  {
    FreeVisualInfo(m_iVisualInfo);
  } // if
}
/*E*/
void VisualInfoC::initVisualInfo(struct Screen *arg_poScreen)
/*S*/
{ 
  TRACE("Entry")
  m_iVisualInfo = NULL;
  // wenn screen-parameter gültig
  if (arg_poScreen)
  {
    // hole visual info
    m_iVisualInfo = GetVisualInfo(arg_poScreen, TAG_DONE);
  } // if
}
/*E*/


//******************************************************************//
// Tools
//******************************************************************//
static SAVEDS ULONG SIFC_HookEntry(register __a0 struct Hook *arg_poHook, register __a1 APTR arg_pObject, register __a2 APTR arg_pParms)
/*S*/
{
  TRACE("Entry");
  // Wenn Hook und Data vorhanden
  if (!arg_poHook || !arg_poHook->h_Data)
  {
    return NULL;
  } // if

  // Zeiger auf HookC-Objekt ermitteln und onHook() anspringen.
  TRACE("rufe onHook()");
  return ((HookC *) arg_poHook->h_Data)->onHook(arg_poHook, arg_pObject, arg_pParms);
}
extern "C"
{
  // Rumgetrickse um die statische Typprüfung zu verarschen.
  // Ja, jetzt fühl' ich mich besser.
  typedef ULONG (*SIFC_HookEntryType)();
  SIFC_HookEntryType SIFC_getHookEntry(void) { return (SIFC_HookEntryType) SIFC_HookEntry; }
}
/*E*/
void drawBox(struct Window *arg_poWindow, int arg_iX, int arg_iY, int arg_iWidth, int arg_iHeight, BOOL arg_bRecessed = FALSE)
/*S*/
{
  TRACE("Entry");
  if (!arg_poWindow) return;

  // VisualInfo erzeugen
  VisualInfoC oVI(arg_poWindow);
  if (oVI.isOk())
  {
    // BevelBox Zeichnen
    DrawBevelBox(arg_poWindow->RPort, arg_iX, arg_iY, arg_iWidth, arg_iHeight,
                 GT_VisualInfo, (APTR) oVI,
                 GTBB_Recessed, arg_bRecessed,
                 TAG_DONE);
  } // if
}
/*E*/
void TextAt(struct RastPort *arg_poRastPort, STRPTR arg_sText, int arg_iLen, int arg_iX, int arg_iY, int arg_iColor)
/*S*/
{
  TRACE("Entry");
  if (!arg_poRastPort || !arg_sText || !arg_iLen) return;
  // Grafik-Cursor positionieren
  Move(arg_poRastPort, arg_iX, arg_iY);

  // Farbe setzen
  SetAPen(arg_poRastPort, arg_iColor);

  // Text zeichnen
  Text(arg_poRastPort, arg_sText, arg_iLen);
}
/*E*/

//******************************************************************//
//******************************************************************//
//
//  WindowListC
//
//******************************************************************//
//******************************************************************//
int WindowListC::m_iLeft = -1;
int WindowListC::m_iTop = -1;

WindowListC::WindowListC(FLXWatchListC *arg_poWatches)
  : m_poWatches(arg_poWatches)
/*S*/
{
  TRACE("Entry");
  m_iWinHeight            = 30;
  m_iWinWidth             = 30;
  m_iMaxVisibleEntries    = 15;
  m_poFirstGadget         = NULL;
  m_apoGadgets[IDC_LISTE] = NULL;
  m_apoGadgets[IDC_OKAY]  = NULL;
  m_apoGadgets[IDC_CLOSE] = NULL;
  m_apoGadgets[IDC_CANCEL]= NULL;
  m_poWindow              = NULL;
  m_oLVRect.MinX = NULL;
  m_oLVRect.MinY = NULL;
  m_oLVRect.MaxX = NULL;
  m_oLVRect.MaxY = NULL;
};
/*E*/
WindowListC::~WindowListC()
/*S*/
{
  TRACE("Entry");
  // Das Fenster schließen, wenn nötig
  closeWindowList();
}
/*E*/

BOOL WindowListC::openWindowList(struct APIMessage *arg_poIMsg)
/*S*/
{
  TRACE("Entry");

BOOL erfolg = FALSE;
  // wenn das Fenster schon offen ist
  if (isOpen())
  {
    TRACE("schon offen");
    // dann nur aktivieren und zurückkehren
    ActivateWindow(m_poWindow);
    WindowToFront(m_poWindow);
    return TRUE;
  } // if

  // Wenn wir eine Watch-Liste haben.
  if (m_poWatches)
  {
    TRACE("m_poWatches ok");
    struct Screen *pScreen = arg_poIMsg->api_Instance->api_Screen;
    if (createGadgets(pScreen))
    {
      TRACE("Gadgets ok");
      validateWindowPosition(pScreen);
      if (m_poWindow = OpenWindowTags(NULL,
            WA_Width, m_iWinWidth,
            WA_Height, m_iWinHeight,
            WA_AutoAdjust, TRUE,
            WA_Top, m_iTop,
            WA_Left, m_iLeft,
            WA_PubScreen, pScreen,
            WA_Activate, TRUE,
            WA_DragBar, TRUE,
            WA_DepthGadget, TRUE,
            WA_CloseGadget, TRUE,
            WA_SizeGadget, FALSE,
            WA_SimpleRefresh, TRUE,
            WA_RMBTrap, TRUE,
            WA_IDCMP, CLOSEWINDOW
                    | REFRESHWINDOW
                    | BUTTONIDCMP
                    | LISTVIEWIDCMP
                    | IDCMP_VANILLAKEY
                    | IDCMP_RAWKEY,
            WA_Title, glob_poCatalog->getString(T_WINDOWLISTTITLE),
            TAG_DONE))
      {
        showGadgets();
        erfolg = TRUE;
      } // if
    } // if
  } // if
return erfolg;
}
/*E*/
void WindowListC::closeWindowList()
/*S*/
{
  TRACE("Entry");
  // wenn wir ein Fenster offen haben
  if (m_poWindow)
  {
    // schließen
    struct Message *poMsg;
    while (poMsg = GetMsg(m_poWindow->UserPort))
    {
      ReplyMsg(poMsg);
    } // while
    m_iLeft = m_poWindow->LeftEdge;
    m_iTop = m_poWindow->TopEdge;

    CloseWindow(m_poWindow);
    m_poWindow = NULL;
  } // if

  freeGadgets();

}
/*E*/
void WindowListC::updateWindowList(struct APIInstance *arg_poInstance)
/*S*/
{
  TRACE("Entry");

  // wenn die interne Liste gültig ist
  // wenn wir ein Listen-Gadget haben
  if (m_apoGadgets[IDC_LISTE])
  {
    // Lösche die Liste im Gadget
    GT_SetGadgetAttrs(m_apoGadgets[IDC_LISTE], NULL, NULL,
      GTLV_Labels, ~0,
      TAG_DONE);
  } // if

  // erstmal alle Einträge entfernen
  m_oWindowNameList.removeAll();

  // Fülle die Liste mit allen aktuellen Einträgen
  FLXNodeC *pFLXNode = m_poWatches->getFirst();
  while (pFLXNode)
  {
    m_oWindowNameList.sortIn(pFLXNode->m_poAPIInstance);
    pFLXNode = pFLXNode->getNext();
  } // if

  // Ermittle die Nummer des Eintrages, der für die arg_poInstance steht
  WNameNodeC *pNameNode = m_oWindowNameList.findWName(arg_poInstance);
  ULONG ulCurrWinEntry  = m_oWindowNameList.indexOf(pNameNode);
  if (ulCurrWinEntry == -1)
  {
    ulCurrWinEntry = 0;
  } // if

  // wenn wir ein Listen-Gadget haben
  if (m_apoGadgets[IDC_LISTE])
  {
    // aktualisiere die Liste des List-Gadgets
    GT_SetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
      GTLV_Selected, ulCurrWinEntry,
      GTLV_MakeVisible, ulCurrWinEntry,
      GTLV_Labels, &m_oWindowNameList,
      TAG_DONE);
    refreshDesign();
  } // if
}
/*E*/
void WindowListC::updateModifiedFlag(struct APIInstance *arg_poInstance)
/*S*/
{
  TRACE("Entry");
  // Nur wenn Fenster offen ist
  if (getWindow())
  {
    // Zeiger auf Node ermitteln
    struct WNameNodeC *poNode = m_oWindowNameList.findWName(arg_poInstance);
    if (!poNode) return;

    // Aktuelle Eintrag ermitteln
    ULONG ulCurrWinEntry  = m_oWindowNameList.indexOf(poNode);
    if (ulCurrWinEntry == -1)
    {
      ulCurrWinEntry = 0;
    } // if

    // wenn wir ein Listen-Gadget haben
    if (m_apoGadgets[IDC_LISTE])
    {
      // aktualisiere die Liste des List-Gadgets
      GT_SetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
        GTLV_Selected, ulCurrWinEntry,
        GTLV_MakeVisible, ulCurrWinEntry,
        TAG_DONE);
    } // if

    // Ermittle aktuellen modified-status der Instanz
    struct EditConfig *poEditConfig = (struct EditConfig *) arg_poInstance->api_Environment;
    BOOL bModified = poEditConfig->DocModified || poEditConfig->LineModified;

    // hat sich was verändert?
    if (bModified != poNode->m_bModified)
    {
      poNode->m_bModified = bModified;
      GT_RefreshWindow(m_poWindow, 0);
      refreshDesign();
    } // if
  } // if
}
/*E*/
char *WindowListC::getSelectedName()
/*S*/
{
  TRACE("Entry");
  // hole aktuelle position
  ULONG ulCurSel = 0;
  GT_GetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                    GTLV_Selected, &ulCurSel,
                    TAG_DONE);

  // hole den so-und-so-vielten eintrag
  WNameNodeC *pNameNode = m_oWindowNameList[ulCurSel];
  if (pNameNode)
  {
    return pNameNode->m_poAPIInstance->api_Name;
  } // if
return NULL;;
}
/*E*/
struct Gadget *WindowListC::createGadgets(struct Screen *arg_pScreen)
/*S*/
{
  TRACE("Entry");
  // Rückgabe
  struct Gadget *pGad = NULL;

  // parameter-check
  if (!arg_pScreen)
  {
    TRACE("Wrong createGadgets()-Parameter!");
    return pGad;
  } // if

  // die Text-Attribut struktur zunächst vom Screen nehmen
  struct TextAttr *pTAttr = arg_pScreen->Font;

  VisualInfoC oVI(arg_pScreen);
  if (!oVI)
  {
    TRACE("VisualInfo failed");
    return pGad;
  } // if

  // initialisiere Rastport
  struct RastPort TextRP;
  InitRastPort(&TextRP);

  // Öffne font
  struct TextFont *font = OpenFont(pTAttr);
  if (font)
  {
    TRACE("Font ok");
    // Font im RastPort setzen
    SetFont(&TextRP, font);

    int wTextBreiteMax = TextLength(&TextRP, glob_poCatalog->getString(T_WEITER),strlen(glob_poCatalog->getString(T_WEITER)));
    wTextBreiteMax = SIFC_MAX(wTextBreiteMax, TextLength(&TextRP, glob_poCatalog->getString(T_SCHLIESSEN),strlen(glob_poCatalog->getString(T_SCHLIESSEN))));
    wTextBreiteMax = SIFC_MAX(wTextBreiteMax, TextLength(&TextRP, glob_poCatalog->getString(T_ABBRECHEN),strlen(glob_poCatalog->getString(T_ABBRECHEN))));

    // Ermittle den breitesten Buttontext
    int  wXOffSet     = 10;   // Mindestabstand zw. den Buttons
    int  wInnerOffset = 5*2;  // Rand links und rechts vom Buttontext
    int  iYOffset     = 6;    // höhe der Gadgets
    WORD wButtonGesamtBreite;
    WORD wListViewBreite;

    m_iWinHeight = 30;
    m_iWinWidth = 30;

    int iNumberOfEntries = (arg_pScreen->Height-40-arg_pScreen->WBorTop-font->tf_YSize*2)/font->tf_YSize;
    if (iNumberOfEntries > m_iMaxVisibleEntries)
    {
      iNumberOfEntries = m_iMaxVisibleEntries;
    } // if

    // Berechne die Breite der Button-Gadgets...
    do
    {
      wButtonGesamtBreite = (wTextBreiteMax+wInnerOffset)*3+wXOffSet*2;

      wListViewBreite = SIFC_MAX(wButtonGesamtBreite, 300); // Mindestbreite
      wListViewBreite = SIFC_MIN(wListViewBreite, arg_pScreen->Width-30); // Maximalbreite

      // wenn die buttons noch breiter sind, als die liste
      if (wButtonGesamtBreite > wListViewBreite)
      {
        // solange vom x-offset noch was weg genommen werden kann, sonst innersoffset decrementieren
        wXOffSet>2 ? wXOffSet-- : wInnerOffset--;
      }
      // oder wenn wButtonGesamtBreite zu klein
      else if (wButtonGesamtBreite < wListViewBreite)
      {
        // wenn die buttons noch größer gemacht werden können, sonst die Abständer vergrößern
        wInnerOffset < 20 ? wInnerOffset++ : wXOffSet++;
      } // if

    // solange ungleich und innersoffset noch nicht zu klein
    } while ((wButtonGesamtBreite != wListViewBreite) && (wInnerOffset>2));


    // für Gadtools erforderlich
    pGad = CreateContext(&m_poFirstGadget);
    if (pGad)
    {
      TRACE("CreateContext ok. erstelle jetzt ListView-Gad ");
    } // if

    // Das ListView-Gadget
    struct NewGadget ng;
    m_oLVRect.MinX = ng.ng_LeftEdge = arg_pScreen->WBorLeft + 4;
    m_oLVRect.MinY = ng.ng_TopEdge  = arg_pScreen->WBorTop + arg_pScreen->BarHeight + font->tf_YSize + 5;
    m_oLVRect.MaxX = ng.ng_Width    = wButtonGesamtBreite;
    m_oLVRect.MaxY = ng.ng_Height   = (font->tf_YSize+3)*iNumberOfEntries+4;
    ng.ng_GadgetText = glob_poCatalog->getString(T_LISTE);
    ng.ng_TextAttr   = pTAttr;
    ng.ng_Flags      = NG_HIGHLABEL;
    ng.ng_GadgetID   = IDC_LISTE;
    ng.ng_UserData   = NULL;
    ng.ng_VisualInfo = oVI;
    m_apoGadgets[IDC_LISTE] = pGad = CreateGadget(LISTVIEW_KIND, pGad, &ng,
        GTLV_MakeVisible, 0,
        GTLV_Selected, 0,
        GTLV_ShowSelected, 0,
        GTLV_ItemHeight, ng.ng_TextAttr->ta_YSize+3,
        GTLV_CallBack, m_oListViewCallBack.getHook(),
        TAG_DONE);

    // Das Weiter-Gadget
    if (pGad)
    {
      TRACE("List-Gadget ok");
      ng.ng_TopEdge += pGad->Height   + 16;
      ng.ng_Width    = wTextBreiteMax + wInnerOffset;
      ng.ng_Height   = font->tf_YSize + iYOffset;
      m_iWinHeight   = ng.ng_TopEdge  + pGad->Height + 4;
    } // if
    ng.ng_GadgetText = glob_poCatalog->getString(T_WEITER);
    ng.ng_GadgetID   = IDC_OKAY;
    ng.ng_Flags      = 0;
    m_apoGadgets[IDC_OKAY] = pGad = CreateGadget(BUTTON_KIND, pGad, &ng,
        GT_Underscore, '_',
        TAG_DONE);

    // Das Schließen-Gadget
    if (pGad)
    {
      TRACE("Weiter-Gadget ok");
      ng.ng_LeftEdge += pGad->Width + wXOffSet;
      m_iWinHeight = ng.ng_TopEdge + pGad->Height + iYOffset;
    } // if
    ng.ng_GadgetText  = glob_poCatalog->getString(T_SCHLIESSEN);
    ng.ng_GadgetID    = IDC_CLOSE;
    m_apoGadgets[IDC_CLOSE] = pGad = CreateGadget(BUTTON_KIND, pGad, &ng,
        GT_Underscore, '_',
        TAG_DONE);

    // Das Abbrechen-Gadget
    if (pGad)
    {
      TRACE("Schließen-Gadget ok");
      ng.ng_LeftEdge += pGad->Width + wXOffSet;
      m_iWinHeight = ng.ng_TopEdge + pGad->Height + iYOffset;
    } // if
    ng.ng_GadgetText  = glob_poCatalog->getString(T_ABBRECHEN);
    ng.ng_GadgetID    = IDC_CANCEL;
    m_apoGadgets[IDC_CANCEL] = pGad = CreateGadget(BUTTON_KIND, pGad, &ng,
        GT_Underscore, '_',
        TAG_DONE);

    if (pGad)
    {
      TRACE("Abbrechen-Gadget ok");
      m_iWinHeight = ng.ng_TopEdge + pGad->Height + iYOffset;
    } // if
    m_iWinWidth = wButtonGesamtBreite+9+arg_pScreen->WBorRight+arg_pScreen->WBorLeft;

    // Font wieder schließen
    CloseFont(font);
  } // if

return pGad;
}
/*E*/
void WindowListC::freeGadgets()
/*S*/
{
  TRACE("Entry");
  // wenn das Fenster noch offen ist
  if (m_poWindow)
  {
    // die gadget vorher removen
    hideGadgets();
  } // if

  // wenn die gadget "befreit" werden müssen
  if (m_poFirstGadget)
  {
    FreeGadgets(m_poFirstGadget);
    m_poFirstGadget = NULL;
  } // if
}
/*E*/
BOOL SAVEDS WindowListC::handleWindowList()
/*S*/
{
//  TRACE("Entry");
  BOOL result = FALSE;

  if (!m_poWindow)
  {
//    TRACE("falsche params");
    return result;
  } // if

  struct IntuiMessage *poIMsg;
  while (poIMsg = GT_GetIMsg(m_poWindow->UserPort))
  {
//    TRACE("GT-IMessage geholt");
    ULONG ulIMsgClass = poIMsg->Class;
    APTR  pIAddress   = poIMsg->IAddress;
    ULONG ulQualifier = poIMsg->Qualifier;
    WORD  wICode      = poIMsg->Code;

    GT_ReplyIMsg(poIMsg);
    switch (ulIMsgClass)
    {
      case IDCMP_RAWKEY:
/*S*/ // IDCMP_RAWKEY
//          TRACE("Taste (IDCMP_RAWKEY)");
          switch (wICode)
          {
            ULONG ulPos;
            // Wenn Taste Cursor Up oder Left...
            case 0x4c:
            case 0x4f:
              // wenn shift
              if (ulQualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
              {
                ulPos=NULL;
                GT_SetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                    GTLV_Selected, ulPos,
                    GTLV_MakeVisible, ulPos,
                    TAG_DONE);
              }
              else
              {
                GT_GetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                    GTLV_Selected, &ulPos,
                    TAG_DONE);
                // wenn man noch nach oben kann
                if (ulPos > 0)
                {
                  ulPos--;
                  GT_SetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                      GTLV_Selected, ulPos,
                      GTLV_MakeVisible, ulPos,
                      TAG_DONE);
                } // if
              } // if
              break;
            // wenn Taste Cursor Down or Right
            case 0x4e:
            case 0x4d:
              // wenn mit shift
              if (ulQualifier&(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
              {
                // maximal-position ermitteln
                ulPos = m_oWindowNameList.getSize()-1;

                GT_SetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                    GTLV_Selected, ulPos,
                    GTLV_MakeVisible, ulPos,
                    TAG_DONE);
              }
              else
              {
                GT_GetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                    GTLV_Selected, &ulPos,
                    TAG_DONE);
                // wenn man noch nach unten kann
                if (ulPos < m_oWindowNameList.getSize())
                {
                  ulPos++;
                  GT_SetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                      GTLV_Selected, ulPos,
                      GTLV_MakeVisible, ulPos,
                      TAG_DONE);
                } // if
              } // if
              break;
          } // switch
        break;
/*E*/
      case IDCMP_VANILLAKEY:
/*S*/ // VANILLAKEY
//          TRACE("Taste (vanilla)");
          switch (wICode)
          {
            // wenn abbrechen
            case 27:
//              TRACE("Abbruch");
              result = TRUE;
              break;
            case 32:
//              TRACE("Fenster Aktivieren");
              sendMsgToGoldED("WINDOW USE=\"%s\"", getSelectedName(), FALSE);
              result = FALSE;
              break;
            case 10:
            case 13:
//              TRACE("Fenster Aktivieren");
              sendMsgToGoldED("WINDOW USE=\"%s\"", getSelectedName(), FALSE);
              result = TRUE;
              break;
            default:
              // in kleinbuchstaben wandeln
              wICode = ToLower(wICode);

              if (wICode == glob_poCatalog->getShortCutLowerCase(T_ABBRECHEN))
              {
//                TRACE("Abbruch");
                return TRUE;
              } //

              if (wICode == glob_poCatalog->getShortCutLowerCase(T_WEITER))
              {
//                TRACE("Fenster Aktivieren");
                sendMsgToGoldED("WINDOW USE=\"%s\"", getSelectedName(), FALSE);
                result = TRUE;
                break;
              } // if

              if (wICode == glob_poCatalog->getShortCutLowerCase(T_SCHLIESSEN))
              {
//                TRACE("Fenster schließen");
                sendMsgToGoldED("WINDOW QUIET USE=\"%s\"", getSelectedName(), FALSE);
                sendMsgToGoldED("QUIT", NULL, TRUE);
                result = FALSE;
              } // if
            break;
          } // switch
          break;
/*E*/
      case IDCMP_GADGETUP:
/*S*/ // IDCMP_GADGETUP
//            TRACE("GADGETUP-Msg");

            { // dummy scope
              struct Gadget *poGad = (struct Gadget *) pIAddress;
              if (poGad)
              {
                switch (poGad->GadgetID)
                {
                  case IDC_LISTE:
//                    TRACE("Auf die liste geklickt");
                    { // dummy scope
                      static ULONG ulOldSeconds   = NULL;
                      static ULONG ulOldMicros    = NULL;
                      static ULONG ulOldSelection = NULL;

                      // hole aktuelle selection
                      ULONG ulNewSelection = NULL;
                      GT_GetGadgetAttrs(m_apoGadgets[IDC_LISTE], m_poWindow, NULL,
                                        GTLV_Selected, &ulNewSelection,
                                        TAG_DONE);

                      // hole aktuelle Zeit der Message
                      ULONG ulNewSeconds = poIMsg->Seconds;
                      ULONG ulNewMicros  = poIMsg->Micros;

                      // wenn doppelgeklickt und der selbe eintrag angewählt
                      if ((ulOldSelection == ulNewSelection) &&
                          DoubleClick(ulOldSeconds, ulOldMicros,
                                      ulNewSeconds, ulNewMicros))
                      {
                        sendMsgToGoldED("WINDOW USE=\"%s\"", getSelectedName(), FALSE);
                        result = FALSE;
                      } // if

                      // merke die zeit als die jetzt alte
                      ulOldSelection = ulNewSelection;
                      ulOldSeconds   = ulNewSeconds;
                      ulOldMicros    = ulNewMicros;
                    } // dummy scope
                    break;
                  case IDC_OKAY:
//                    TRACE("Weiter");
                    sendMsgToGoldED("WINDOW USE=\"%s\"", getSelectedName(), FALSE);
                    result = TRUE;
                    break;
                  case IDC_CLOSE:
//                    TRACE("Schließen");
                    sendMsgToGoldED("WINDOW QUIET USE=\"%s\"", getSelectedName(), FALSE);
                    sendMsgToGoldED("QUIT", NULL, TRUE);
                    result = FALSE;
                    break;
                  case IDC_CANCEL:
//                    TRACE("Abbruch");
                    result = TRUE;
                    break;
                } // switch
              } // if
            } // dummy scope
          break;
/*E*/
      case IDCMP_CLOSEWINDOW:
/*S*/ // IDCMP_CLOSEWINDOW
//          TRACE("CloseGad");
          result = TRUE;
          break;
/*E*/
      case IDCMP_REFRESHWINDOW:
/*S*/ // IDCMP_REFRESHWINDOW
          GT_BeginRefresh(m_poWindow);
          drawBox(m_poWindow,                             // Fenster
                  m_poWindow->WScreen->WBorLeft + 4,      // X
                  m_apoGadgets[IDC_OKAY]->TopEdge - 8,    // Y
                  m_apoGadgets[IDC_LISTE]->Width,         // Breite
                  2);                                     // Höhe
          refreshDesign();
          GT_EndRefresh(m_poWindow, TRUE);
          break;
/*E*/
      default:
//        TRACE("unwichtige imsgclass");
        break;
    } // if
  } // while
//  TRACE("exit");
return result;
}
/*E*/
void WindowListC::validateWindowPosition(struct Screen *arg_poScreen)
/*S*/
{ 
  TRACE("Entry");
  // wenn der Screen ok ist
  if (arg_poScreen)
  {
    // wenn horizontal zentrieren
    if (m_iLeft == -1)
    {
      m_iLeft = (arg_poScreen->Width-m_iWinWidth)/2;
    } // if

    // wenn vertikal zentrieren
    if (m_iTop == -1)
    {
      m_iTop = (arg_poScreen->Height-m_iWinHeight)/2;
    } // if
  } // if
}
/*E*/
void WindowListC::setWindowPosition(int arg_iX /*= -1*/, int arg_iY /*= -1*/, BOOL arg_bNow /*= TRUE*/)
/*S*/
{                                   
  TRACE("Entry");
  m_iLeft = arg_iX;
  m_iTop = arg_iY;

  // wenn das Fenster offen ist
  if (isOpen())
  {
    validateWindowPosition(m_poWindow->WScreen);

    // und umstellen sofort gewünscht
    if (arg_bNow)
    {
      MoveWindow(m_poWindow, m_iLeft-m_poWindow->LeftEdge, m_iTop-m_poWindow->TopEdge);
    } // if
  } // if
}
/*E*/
void WindowListC::clearWindow()
/*S*/
{
  TRACE("Entry");
  // wenn Fenster offen
  if (isOpen())
  {
    EraseRect(m_poWindow->RPort,
              m_poWindow->BorderLeft,
              m_poWindow->BorderTop,
              m_poWindow->Width-m_poWindow->BorderRight-1,
              m_poWindow->Height-m_poWindow->BorderBottom-1);

  } // if
}
/*E*/
void WindowListC::hideGadgets()
/*S*/
{        
  TRACE("Entry");
  // validation check
  if (!m_poWindow || !m_poFirstGadget)
  {
    TRACE("Falsche Parameter");
    return;
  } // if

  if (!m_bGadgetsAttached)
  {
    TRACE("Gadgets garnicht attached");
    return;
  } // if
  RemoveGList(m_poWindow, m_poFirstGadget, ~0);

  m_bGadgetsAttached = FALSE;
}
/*E*/
void WindowListC::showGadgets()
/*S*/
{
  TRACE("Entry");
  // validation check
  if (!m_poWindow || !m_poFirstGadget)
  {
    TRACE("Falsche Parameter");
    return;
  } // if

  // wenn das Fenster bereits Gadgets hat
  if (m_bGadgetsAttached)
  {
    TRACE("Gadgets bereits vorhanden");
    return;
  }

  clearWindow();
  AddGList(m_poWindow, m_poFirstGadget, -1, -1, NULL);
  RefreshGList(m_poFirstGadget, m_poWindow, NULL, -1);

  drawBox(m_poWindow,                             // Fenster
          m_poWindow->WScreen->WBorLeft + 4,      // X
          m_apoGadgets[IDC_OKAY]->TopEdge - 8,    // Y
          m_apoGadgets[IDC_LISTE]->Width,         // Breite
          2);                                     // Höhe
  refreshDesign();
  m_bGadgetsAttached = TRUE;
}
/*E*/
void WindowListC::resizeWindow(int arg_iEntries /*= 0*/, int arg_iX /*= -1*/, int arg_iY /*= -1*/)
/*S*/
{
  TRACE("Entry");
  // wenn das Fenster nicht offen ist
  if (!isOpen())
  {
    return;
  } // if

  // gadgets entfernen
  freeGadgets();

  // wenn die Anzahl der Einträge gesetzt werden soll.
  if (arg_iEntries > 1)
  {      
    setMaxVisibleEntries(arg_iEntries);
  } // if

  // gadgets erstellen
  createGadgets(m_poWindow->WScreen);

  // Fensterposition berechnen
  setWindowPosition(arg_iX, arg_iY, FALSE);

  // Fenstergröße anpassen
  ChangeWindowBox(m_poWindow, m_iLeft, m_iTop, m_iWinWidth, m_iWinHeight);

  // gadgets anzeigen
  showGadgets();
}
/*E*/
void WindowListC::drawSeperator(int arg_iXOffset)
/*S*/
{
  TRACE("Entry");
  if (!m_poWindow) return;

  drawBox(m_poWindow,                     // Fenster
          m_oLVRect.MinX+arg_iXOffset+2,  // X
          m_oLVRect.MinY,                 // Y
          2,                              // Breite
          m_oLVRect.MaxY);                // Höhe

}
/*E*/
void WindowListC::refreshDesign()
/*S*/
{
  if (m_poWindow)
  {
    drawSeperator(oModifyImg.Width+3);
  } // if
}
/*E*/


//******************************************************************//
//******************************************************************//
//
//  HookC
//
//******************************************************************//
//******************************************************************//

HookC::HookC()
/*S*/
{
  TRACE("Entry");
  // Auf NULL initialisieren
  m_oHook.h_MinNode.mln_Succ = NULL;
  m_oHook.h_MinNode.mln_Pred = NULL;

  // our Jump-back-to-our-sifc-object-HookEntry
  m_oHook.h_Entry    = SIFC_getHookEntry();
  m_oHook.h_SubEntry = NULL;

    // Wenn kein Reciever angegeben, dann werden wir selbst benachrichtigt.
  m_oHook.h_Data = this;
}
/*E*/
ULONG HookC::onHook(struct Hook *arg_poHook, APTR arg_pObject, APTR arg_pParms)
/*S*/
{
  TRACE("Entry");

  // Parameter casten
  struct LVDrawMsg  *poDrawMsg = (struct LVDrawMsg *) arg_pObject;
  struct WNameNodeC *poNode = (struct WNameNodeC *) arg_pParms;

  // wenn DrawMsg blöd oder die falsche Method ID oder keine node oder keinen namen
  if (!poNode || !poNode->ln_Name || !poDrawMsg || (poDrawMsg->lvdm_MethodID != LV_DRAW))
  {
    return LVCB_UNKNOWN;
  } // if

  struct RastPort *rp = poDrawMsg->lvdm_RastPort;
  LONG bw = oModifyImg.Width+3;
  LONG bh = oModifyImg.Height;
  LONG x1 = poDrawMsg->lvdm_Bounds.MinX+bw+2;
  LONG y1 = poDrawMsg->lvdm_Bounds.MinY;
  LONG x2 = poDrawMsg->lvdm_Bounds.MaxX;
  LONG y2 = poDrawMsg->lvdm_Bounds.MaxY;

  // Anzahl der Druckbaren zeichen ermitteln
  char *sEntryName = poNode->ln_Name;
  int iEntryNameLen = strlen(sEntryName);

  struct TextExtent oTextExtent;
  iEntryNameLen = TextFit(rp, sEntryName, iEntryNameLen, &oTextExtent, NULL, 1, x2-x1, y2-y1);

  switch (poDrawMsg->lvdm_State)
  {
    case LVR_NORMAL:
      // Hintergrund löschen
      EraseRect(rp, x1, y1, x2, y2);
      EraseRect(rp, x1-bw-1,y1, x1-3, y2);

      // Text zeichnen
      TextAt(rp, sEntryName, iEntryNameLen, x1+2, y1+rp->TxHeight-3, poDrawMsg->lvdm_DrawInfo->dri_Pens[TEXTPEN]);

      // Wenn nötig die "Modified"-Marke anzeigen
      if (poNode->m_bModified)
      {      
        DrawImage(rp, &oModifyImg, x1-bw-1, (y1+y2)/2-bh/2);
      } // if
      break;

    case LVR_SELECTED:
      // Hintergrundfarbe setzen
      SetAPen(rp, poDrawMsg->lvdm_DrawInfo->dri_Pens[FILLPEN]);

      // Hintergrund füllen
//      RectFill(rp, x1,y1,x2,y2);
      EraseRect(rp, oTextExtent.te_Width+x1+2, y1, x2, y2);
      EraseRect(rp, x1-bw-1,y1, x1-3, y2);
      RectFill(rp, x1,y1,oTextExtent.te_Width+x1+2,y2);

      // Text zeichnen
      TextAt(rp, sEntryName, iEntryNameLen, x1+2, y1+rp->TxHeight-3, poDrawMsg->lvdm_DrawInfo->dri_Pens[HIGHLIGHTTEXTPEN]);

      // Wenn nötig die "Modified"-Marke anzeigen
      if (poNode->m_bModified)
      {
        DrawImage(rp, &oModifyImg, x1-bw-1, (y1+y2)/2-bh/2);
      } // if
      break;

    default:
      // Einfach alles löschen
      EraseRect(rp, x1, y1, x2, y2);
      break;
  } // switch

  return LVCB_OK;
}
/*E*/
