Module: qcedit.cpp

class QCEditInfo
.h

constructorQCEditInfo()
destructor~QCEditInfo()

class QCEdit
.h

constructorQCEdit(QWindow *parent,QRect *r)

: QEdit(parent,r,MAX_DOCUMENT_LENGTH,0,QEdit::MULTILINE|QEdit::SCROLLING)

destructor~QCEdit()
GetFileNamecstring GetFileName()

Returns filename of current file (or empty string if none exists)

Loadbool Load(cstring fname,int slot)

Load a text file into a slot
If 'slot'==-1, the next available slot is used

Savebool Save(cstring fname)

Save the text
If 'fname'==0, the current filename is used
Checks modification time of the underlying file before saving,
to avoid overwrites of old versions in memory (!)
Returns FALSE if it did not save (no text, no file or file error)

Selectbool Select(int n,bool refresh)

Selects a file to use
Returns FALSE in case of any error

DoFindvoid DoFind()

Put up a dialog and find text

DoFindNextvoid DoFindNext()

Find next occurrence of 'findBuffer'

DoFindPrevvoid DoFindPrev()

Find previous occurrence of 'findBuffer'

EvKeyPressbool EvKeyPress(int key,int x,int y)

Supersedes QEdit's EvKeyPress() functionality
Specialized behavior



/*
 * QCEdit - Edit control for C(++) code; fancy stuff
 * 07-09-1999: Created!
 * NOTES:
 * - Should not be in QLib; is too high for that
 * (C) MG/RVG
 */

#include <qlib/cedit.h>
#include <qlib/keys.h>
#include <qlib/time.h>
#include <qlib/dialog.h>
#include <sys/stat.h>
#include <time.h>
#include <qlib/debug.h>
DEBUG_ENABLE

// Options
// Always save document, even though it is marked unchanged?
//#define ALWAYS_SAVE

// Max chars in a document
#define MAX_DOCUMENT_LENGTH	100000

/***
  QCEditInfo helper class
***/
QCEditInfo::QCEditInfo()
{
  cx=cy=0;
  maxChar=MAX_DOCUMENT_LENGTH;
  text=(char*)qcalloc(maxChar);
  textTop=text;
  rangeStart=rangeEnd=0;
  rangeDirection=0;
  marker=0;
  eFlags=QEdit::AUTOINDENT;
}
QCEditInfo::~QCEditInfo()
{
}


QCEdit::QCEdit(QWindow *parent,QRect *r)
  : QEdit(parent,r,MAX_DOCUMENT_LENGTH,0,QEdit::MULTILINE|QEdit::SCROLLING)
{
  int i;

  curFile=-1;
  for(i=0;i<MAX_FILES;i++)
    ei[i]=new QCEditInfo();

  findBuffer[0]=0;
  replaceBuffer[0]=0;
}
QCEdit::~QCEdit()
{
  int i;
  for(i=0;i<MAX_FILES;i++)
    delete ei[i];
}

cstring QCEdit::GetFileName()
// Returns filename of current file (or empty string if none exists)
{
  if(curFile==-1)return "";
  if(ei[curFile]->fileName.IsEmpty())
    return "";
  return ei[curFile]->fileName;
}

/***********
* FILE I/O *
***********/
bool QCEdit::Load(cstring fname,int slot)
// Load a text file into a slot
// If 'slot'==-1, the next available slot is used
{
  int i;

  if(slot==-1)
  { for(i=0;i<MAX_FILES;i++)
      if(ei[i]->fileName.IsEmpty())
      { slot=i; goto found; }
    return FALSE;			// Out of slot space
  }
 found:
  if(QFileSize(fname)>MAX_DOCUMENT_LENGTH)
  { qwarn("QCEdit: file '%s' is too large to edit",fname);
    return FALSE;
  }
//qdbg("QCEdit:Load(%s) in slot %d\n",fname,slot);
  QQuickLoad(fname,ei[slot]->text,ei[slot]->maxChar);
  ei[slot]->fileName=fname;
  // Get time of last modification to compare when saving
  stat(QExpandFilename(fname),&ei[slot]->lastStat);
  Select(slot,FALSE);
  return TRUE;
}
bool QCEdit::Save(cstring fname)
// Save the text
// If 'fname'==0, the current filename is used
// Checks modification time of the underlying file before saving,
// to avoid overwrites of old versions in memory (!)
// Returns FALSE if it did not save (no text, no file or file error)
{
  bool r;
  struct stat curStat;

//qdbg("QCEdit::Save()\n");
  if(curFile==-1)return FALSE;

  if(!fname)fname=ei[curFile]->fileName;
#ifdef ALWAYS_SAVE
  // Don't do unnecessary save
  if(!IsChanged())return TRUE;
#endif

  // Check if current modification time of file is other than
  // what we think it should be. If they don't match, someone
  // else has modified (the contents) of the file, so it is worth a good
  // warning before you actually overwrite the file.
  // Only works with seconds, but hey, safety only goes so far.
  stat(QExpandFilename(fname),&curStat);
  if(curStat.st_mtim.tv_sec!=ei[curFile]->lastStat.st_mtim.tv_sec)
  { // Warn!
    if(QMessageBox("Warning",
      "The file has been modified\nby some other program.\n"
      "Are you sure you want to overwrite the file?")!=0)
    { return FALSE;
    }
  }

  //if(ei[curFile]->fileName.IsEmpty())return FALSE;
  r=QQuickSave(fname,text,strlen(text));
  // Only set unchanged mode if save worked
  if(r)
  { eFlags&=~CHANGED;
    // Update expected modification time
    stat(QExpandFilename(fname),&ei[curFile]->lastStat);
  }
  return r;
}

bool QCEdit::Select(int n,bool refresh)
// Selects a file to use
// Returns FALSE in case of any error
{
  QCEditInfo *p;

  if(n<0||n>MAX_FILES)return FALSE;
  if(ei[n]==0)return FALSE;

  // Any change?
  if(curFile==n)return TRUE;

  // Store current state
  if(curFile!=-1)
  {
    p=ei[curFile];
    p->cx=cx; p->cy=cy;
    p->marker=marker;
    p->text=text;
    p->textTop=textTop;
    p->maxChar=maxChar;
    p->rangeStart=rangeStart;
    p->rangeEnd=rangeEnd;
    p->rangeDirection=rangeDirection;
    p->eFlags=eFlags;
  }

  // Copy in variables
  curFile=n;
  p=ei[curFile];
  cx=p->cx; cy=p->cy;
  marker=p->marker;
  text=p->text;
  textTop=p->textTop;
  maxChar=p->maxChar;
  rangeStart=p->rangeStart;
  rangeEnd=p->rangeEnd;
  rangeDirection=p->rangeDirection;
  eFlags=p->eFlags;

//qdbg("QCEdit:Select() '%s'\n",text);
  if(refresh)Paint();
  return TRUE;
}

void QCEdit::DoFind()
// Put up a dialog and find text
{
  char buf[MAX_FIND_LEN];
  QRect r(40,40,400,150);

//qdbg("QCEdit::DoFind\n");

  sprintf(buf,findBuffer);
  if(QDlgString("Find","Enter text to find:",buf,sizeof(buf),1,&r))
  {
    strcpy(findBuffer,buf);
    if(!findBuffer[0])return;
    if(!Find(findBuffer,0))
      QMessageBox("Find","String not found.",0,&r);
  }
}
void QCEdit::DoFindNext()
// Find next occurrence of 'findBuffer'
{
//qdbg("QCEdit::DoFindNext\n");
  QRect r(40,40,400,150);
  if(!findBuffer[0])return;
  if(!Find(findBuffer,0))
    QMessageBox("Find next","String not found.",0,&r);
}
void QCEdit::DoFindPrev()
// Find previous occurrence of 'findBuffer'
{
//qdbg("QCEdit::DoFindPrev\n");
  QRect r(40,40,400,150);
  if(!findBuffer[0])return;
  if(!Find(findBuffer,FIND_BACKWARDS))
    QMessageBox("Find previous","String not found.",0,&r);
}

/***************
* KEY HANDLING *
***************/
bool QCEdit::EvKeyPress(int key,int x,int y)
// Supersedes QEdit's EvKeyPress() functionality
// Specialized behavior
{ bool  refresh=FALSE;
  char  c;
  int   i,len;
  int   nBuf;
  char  buf[256],buf2[256];
  QRect r(40,40,400,150);


//qdbg("QCEdit:EvKeyPress\n");
  if(state==INACTIVE)return FALSE;

  if(key==(QK_ALT|QK_1))
  { nBuf=0;
   do_buf:
//qdbg("select buffer %d\n",nBuf);
    Select(nBuf);
    RefreshView();
    RefreshSmart();
    return TRUE;
  } else if(key==(QK_ALT|QK_2)){ nBuf=1; goto do_buf; }
  else if(key==(QK_ALT|QK_3)){ nBuf=2; goto do_buf; }
  else if(key==(QK_ALT|QK_4)){ nBuf=3; goto do_buf; }
  else if(key==(QK_ALT|QK_5)){ nBuf=4; goto do_buf; }
  else if(key==(QK_ALT|QK_6)){ nBuf=5; goto do_buf; }
  else if(key==(QK_ALT|QK_7)){ nBuf=6; goto do_buf; }
  else if(key==(QK_ALT|QK_8)){ nBuf=7; goto do_buf; }
  else if(key==(QK_ALT|QK_9)){ nBuf=8; goto do_buf; }
  else if(key==(QK_ALT|QK_0)){ nBuf=9; goto do_buf; }
  // Macros
  else if(key==(QK_ALT|QK_E))
  { // Function entry
    strcpy(buf,"");
    if(QDlgString("Function entry debugging","Enter name of function",
       buf,sizeof(buf),1,&r))
    {
      // Indent
      if(cx==0)InsertString("  ");
      else if(cx==1)InsertString(" ");
      InsertString("DBG_C(\"");
      InsertString(buf);
      InsertString("\")\n");
    }
    return TRUE;
  } else if(key==(QK_ALT|QK_C))
  { // Class debug
    strcpy(buf,"");
    if(QDlgString("Class debugging","Enter name of class",
      buf,sizeof(buf),1,&r))
    {
      InsertString("#undef  DBG_CLASS\n");
      InsertString("#define DBG_CLASS \"");
      InsertString(buf);
      InsertString("\"\n");
    }
    return TRUE;
  } else if(key==(QK_ALT|QK_O))
  { // Macro OBSoletize
    // BUGS: works correctly only on fully selected lines
    // BUGS: doesn't work at bottom of view
    if(RangeActive())
    { string s,e;
      s=rangeStart; e=rangeEnd;
      Goto(s); InsertString("#ifdef OBS\n");
      Goto(e+11); FormatView(); RefreshView(); InsertString("#endif\n");
      RangeClear();
      FormatView(); RefreshView();
      RefreshSmart();
    }
    return TRUE;
  } else if(key==(QK_ALT|QK_F))
  { // Macro FOR loop; relatively intelligent
    strcpy(buf,"");
    if(QDlgString("FOR loop","Enter FOR expression (omit ; for simple loop)",
      buf,sizeof(buf),1,&r))
    { int indent;
      char *ptr,var;
      bool fExpandExpr;

      indent=IndentPoint(lineStart[cy]);
      if(!strchr(buf,';'))
      { // Expand expression into default: i<MAX => i=0;i<MAX;i++
        var=buf[0];
        strcpy(buf2,buf);
        sprintf(buf,"%c=0;%s;%c++",var,buf2,var);
      }
      InsertString("for(");
      InsertString(buf);
      InsertString(")\n");
      for(i=0;i<indent;i++){ InsertChar(' '); CursorRight(); }
      InsertString("{\n");
      for(i=0;i<indent+2;i++){ InsertChar(' '); CursorRight(); }
      ptr=CurrentCharPtr();
      InsertString("\n");
      for(i=0;i<indent;i++){ InsertChar(' '); CursorRight(); }
      InsertString("}");
      Goto(ptr);
    }
    return TRUE;
  } else if(key==(QK_ALT|QK_W))
  { // Macro WHILE loop
    strcpy(buf,"");
    if(QDlgString("WHILE loop","Enter WHILE expression",
      buf,sizeof(buf),1,&r))
    { int indent;
      char *ptr;

      indent=IndentPoint(lineStart[cy]);
      InsertString("while(");
      InsertString(buf);
      InsertString(")\n");
      for(i=0;i<indent;i++){ InsertChar(' '); CursorRight(); }
      InsertString("{\n");
      for(i=0;i<indent+2;i++){ InsertChar(' '); CursorRight(); }
      ptr=CurrentCharPtr();
      InsertString("\n");
      for(i=0;i<indent;i++){ InsertChar(' '); CursorRight(); }
      InsertString("}");
      Goto(ptr);
    }
    return TRUE;
  } else if(key==(QK_ALT|QK_H))
  { // Macro HEADER generation
    strcpy(buf,"");
    if(QDlgString("Header generation","Enter header text",
      buf,sizeof(buf),1,&r))
    { int indent;
      char *ptr;

      indent=strlen(buf);
      InsertChar('/'); CursorRight();
      for(i=0;i<indent+3;i++){ InsertChar('*'); CursorRight(); }
      InsertString("\n* ");
      InsertString(buf);
      InsertString(" *\n");
      for(i=0;i<indent+3;i++){ InsertChar('*'); CursorRight(); }
      InsertString("/\n");
      //Goto(ptr);
    }
    return TRUE;
  } else if(key==(QK_ALT|QK_I))
  { // Macro INTRO generation
    strcpy(buf,"");
    if(QDlgString("Intro generation","Enter intro module header",
      buf,sizeof(buf),1,&r))
    {
      string sDate,sTime;

      InsertString("/*\n");
      InsertString(" * "); InsertString(buf); InsertString("\n");
      sDate=QCurrentDate(); sTime=QCurrentTime();
      InsertString(" * "); InsertString(sDate);
      InsertString(": Created! ("); InsertString(sTime);
      InsertString(")\n");
      InsertString(" * NOTES:\n");
      InsertString(" * (C) MarketGraph/RvG\n");
      InsertString(" */\n");
      InsertString("\n");
      InsertString("#include <qlib/debug.h>\n");
      InsertString("DEBUG_ENABLE\n");
      InsertString("\n");
    }
    return TRUE;
  } else if(key==(QK_CTRL|QK_F))
  { // Find
    DoFind();
    return TRUE;
  } else if(key==(QK_CTRL|QK_G))
  { // Find next
    DoFindNext();
    return TRUE;
  } else if(key==(QK_CTRL|QK_D))
  { // Find previous
    DoFindPrev();
    return TRUE;
  } else if(key==(QK_CTRL|QK_M))
  { // Mark place
    marker=CurrentCharPtr();
    return TRUE;
  } else if(key==QK_F4)
  { // Goto mark (toggle)
    cstring oldMarker=marker;
    marker=CurrentCharPtr();
    Goto(oldMarker);
    FormatView(); RefreshView(); RefreshSmart();
    return TRUE;
  }
#define ND_FOR_APP
#ifdef ND_FOR_APP
  else if(key==(QK_CTRL|QK_S))
  { if(curFile!=-1&&ei[curFile]->fileName.IsEmpty()==FALSE)
    { if(!Save(ei[curFile]->fileName))
      { // Error
        qwarn("Can't save file.");
      }
    }
    return TRUE;
  }
#endif
  else
  { // Dispatch to QEdit
    return QEdit::EvKeyPress(key,x,y);
  }
  return FALSE;
}