<commit_before>// kmfldsearch.cpp
// TODO: Add search in subfolder checkbox
// TODO: Use msgIdMD5 in MSGID_COLUMN

#include "kmfldsearch.h"
#include "kmglobal.h"
#include "kmfoldermgr.h"
#include "kmfolder.h"
#include "kmmessage.h"
#include "kmmainwin.h"

#include <qmessagebox.h>
#include <qpushbutton.h>
#include <qcombobox.h>
#include <qlineedit.h>
#include <qstring.h>
#include <qlayout.h>
#include <qlabel.h>
#include <assert.h>

#include <klocale.h>
#include <kdebug.h>
#include <kapp.h>
#include <kbuttonbox.h>
#include <qlistview.h>
#include <qheader.h>

#include <stdlib.h>

//-----------------------------------------------------------------------------
KMFldSearch::KMFldSearch(KMMainWin* w, const char* name,
  QString curFolder, bool modal, WFlags f):
  KMFldSearchInherited(NULL, name, modal, f | Qt::WDestructiveClose)
{
  KButtonBox* bbox;
  QLabel* lbl;
  KMFldSearchRule* rule;
  int i;

  mMainWin = w;

  mNumRules  = 2;
  mSearching = false;

  setCaption(i18n("Search in Folders"));
  mGrid = new QGridLayout(this, mNumRules+4, 5, 4, 4);

  mCbxFolders = createFolderCombo(curFolder);
  mCbxFolders->setMinimumSize(mCbxFolders->sizeHint());
  mCbxFolders->setMaximumSize(1024, mCbxFolders->sizeHint().height());
  mGrid->addMultiCellWidget(mCbxFolders, 0, 0, 1, 2);

  mRules = new KMFldSearchRule*[mNumRules];

  for (i=0; i<mNumRules; i++)
  {
    lbl = new QLabel(i ? i18n("and") : i18n("where"), this);
    lbl->setMinimumSize(lbl->sizeHint());
    lbl->setAlignment(AlignRight|AlignVCenter);
    mGrid->addWidget(lbl, i+1, 0);

    rule = new KMFldSearchRule(this, mGrid, i+1, 1);
    mRules[i] = rule;
  }

  lbl = new QLabel(i18n("Search in"), this);
  lbl->setFixedSize(lbl->sizeHint().width(), mCbxFolders->sizeHint().height());
  lbl->setAlignment(AlignRight|AlignVCenter);
  mGrid->addWidget(lbl, 0, 0);

  mLastFocus = new QWidget();	// to remeber the position of the focus

  mLbxMatches = new QListView(this, "Search in Folders");
  /* Default is to sort by date. TODO: Unfortunately this sorts *while*
     inserting, which looks rather strange - the user cannot read
     the results so far as they are constantly re-sorted --dnaber
  */
  mLbxMatches->setSorting(2, false);
  mLbxMatches->setShowSortIndicator(true);
  mLbxMatches->setAllColumnsShowFocus(true);
  mLbxMatches->addColumn(i18n("Subject"), 150);
  mLbxMatches->addColumn(i18n("Sender"), 120);
  mLbxMatches->addColumn(i18n("Date"), 120);
#define LOCATION_COLUMN 3
  mLbxMatches->addColumn(i18n("Folder"), 100);

#define MSGID_COLUMN 4
  //TODO: Use msgIDMD5, and create KMFolder::findIdMD5(QString) method
  mLbxMatches->addColumn("");		// should be hidden
  mLbxMatches->setColumnWidthMode( MSGID_COLUMN, QListView::Manual );
  mLbxMatches->header()->setResizeEnabled(false, MSGID_COLUMN);
  mLbxMatches->setColumnWidth( MSGID_COLUMN, 0 );

#define FOLDERID_COLUMN 5
  mLbxMatches->addColumn("");		// should be hidden
  mLbxMatches->setColumnWidthMode( FOLDERID_COLUMN, QListView::Manual );
  mLbxMatches->header()->setResizeEnabled(false, FOLDERID_COLUMN);
  mLbxMatches->setColumnWidth( FOLDERID_COLUMN, 0 );

  connect(mLbxMatches, SIGNAL(doubleClicked(QListViewItem *)),
	  this, SLOT(slotShowMsg(QListViewItem *)));
  //mLbxMatches->readConfig();
  mGrid->addMultiCellWidget(mLbxMatches, mNumRules+2, mNumRules+2, 0, 4);

  bbox = new KButtonBox(this, Vertical);
  mGrid->addMultiCellWidget(bbox, 0, mNumRules-1, 4, 4);
  mBtnSearch = bbox->addButton(i18n("Search"));
  mBtnSearch->setDefault(true);
  connect(mBtnSearch, SIGNAL(clicked()), SLOT(slotSearch()));
  mBtnClose  = bbox->addButton(i18n("Close"));
  connect(mBtnClose, SIGNAL(clicked()), SLOT(slotClose()));
  bbox->layout();
  bbox->setFixedWidth(bbox->width());

  mLblStatus = new QLabel(" ", this);
  mLblStatus->setFixedHeight(mLblStatus->sizeHint().height());
  mGrid->addMultiCellWidget(mLblStatus, mNumRules+3, mNumRules+3, 0, 3);

  mGrid->setColStretch(0, 0);
  mGrid->setColStretch(1, 2);
  mGrid->setColStretch(2, 1);
  mGrid->setColStretch(3, 100);
  mGrid->setColStretch(4, 0);

  mGrid->setRowStretch(mNumRules+2, 100);
  mGrid->setRowStretch(mNumRules+3, 0);

  mGrid->activate();
  resize(sizeHint());
}

//-----------------------------------------------------------------------------
KMFldSearch::~KMFldSearch()
{
  //TODO Save QListView layout
}

//-----------------------------------------------------------------------------
QComboBox* KMFldSearch::createFolderCombo(const QString curFolder)
{
 QValueList<QGuardedPtr<KMFolder> > folders;
 QComboBox* cbx = new QComboBox(false, this);
 QStringList str;

 kernel->folderMgr()->createI18nFolderList( &str, &folders );
 cbx->setFixedHeight(cbx->sizeHint().height());

 cbx->insertItem(i18n("<Search all folders>"));
 QStringList::Iterator st;
 int i = 1;
 for( st = str.begin(); st != str.end(); ++st, ++i) {
   cbx->insertItem(*st);
   QString fname = *st;
   if( fname.stripWhiteSpace() == curFolder ) {		// preselect current folder
     cbx->setCurrentItem(i);
   }
 }

 return cbx;
}

//-----------------------------------------------------------------------------
void KMFldSearch::updStatus(void)
{
  QString str;

  if (!mSearching) {
    if(!mStopped)
      str = i18n("Done, %1 matches (%2 messages processed)").arg(mNumMatches).arg(mCount);
    else
      str = i18n("Search cancelled, %1 matches so far (%3 messages processed)").arg(mNumMatches).arg(mCount);
  } else
    str = i18n("%1 matches, searching in %2 (message %3)")
		.arg(mNumMatches)
		.arg(mSearchFolder)
		.arg(mCount);
  mLblStatus->setText(str);
}

//-----------------------------------------------------------------------------
void KMFldSearch::keyPressEvent(QKeyEvent *evt)
{

  if(!evt)
    return;

  switch (evt->key()) {
      case Key_Return:
        KMFldSearchInherited::keyPressEvent(evt);
        break;
      case Key_Escape:
        if( mSearching )
          mSearching = false;
	else
          slotClose();
        break;
  }
}

//-----------------------------------------------------------------------------
bool KMFldSearch::searchInMessage(KMMessage* aMsg)
{
  int i;
  bool matches = true;

  assert(aMsg!=NULL);

  mCount++;
  updStatus();
  for(i=0; matches && i<mNumRules; i++)
    if (!mRules[i]->matches(aMsg))
      matches = false;

  return matches;
}

//-----------------------------------------------------------------------------
void KMFldSearch::searchInFolder(QGuardedPtr<KMFolder> aFld, int fldNum)
{
  KMMessage* msg;
  int i, num, upd;
  QString str;

  assert(!aFld.isNull());

  mBtnSearch->setText("Stop");
  if (aFld->isSystemFolder()) mSearchFolder = i18n(aFld->name());
    else mSearchFolder = aFld->name();
  kapp->processEvents();
  if (aFld->open() != 0)
  {
    kdDebug() << "Cannot open folder '" << (const char*)aFld->name() << "'" << endl;
    return;
  }

  num = aFld->count();
  for (i=0, upd=0; i<num && mSearching; i++, upd++)
  {
    msg = aFld->getMsg(i);
    if (msg && searchInMessage(msg))
    {
      (void)new QListViewItem(mLbxMatches,
			      msg->subject(),
			      msg->from(),
			      msg->dateIsoStr(),
			      mSearchFolder,
			      QString("%1").arg(i),
			      QString("%1").arg(fldNum)
			      );
      mNumMatches++;
      updStatus();
    }
    if (upd > 10)
    {
      kapp->processEvents();
      upd = 0;
      if (!aFld) // Folder deleted while searching!
	break;
    }
    aFld->unGetMsg(i);
  }

  if(!mSearching)
    // was stopped
    mStopped = true;

  updStatus();

  aFld->close();
  mBtnSearch->setText(i18n("Search"));
}

//-----------------------------------------------------------------------------
void KMFldSearch::searchInAllFolders(void)
{
  QValueList<QGuardedPtr<KMFolder> > folders;
  QGuardedPtr<KMFolder> folder;
  QStringList str;
  int i = 0;

  kernel->folderMgr()->createFolderList( &str, &folders );
  while (folders.at(i) != folders.end()) {
    folder = *folders.at(i);
    // Stop pressed?
    if(!mSearching)
      break;
    searchInFolder(folder,i);
    ++i;
  }
}

//-----------------------------------------------------------------------------
void KMFldSearch::slotSearch()
{
  int i;
  mLastFocus = focusWidget();
  mBtnSearch->setFocus();	// set focus so we don't miss key event

  if (mSearching)
  {
    mSearching = false;
    return;
  }

  mCount = 0;
  mStopped = false;
  mNumMatches = 0;
  mSearching  = true;

  mBtnSearch->setCaption(i18n("Stop"));
  enableGUI();
  mLbxMatches->clear();
  kapp->processEvents();

  for (i=0; i<mNumRules; i++)
    mRules[i]->prepare();

  if (mCbxFolders->currentItem() <= 0)
    searchInAllFolders();
  else {
    QValueList<QGuardedPtr<KMFolder> > folders;
    QStringList str;
    kernel->folderMgr()->createI18nFolderList( &str, &folders );
    if (str[mCbxFolders->currentItem()-1] == mCbxFolders->currentText()) {
      searchInFolder(*folders.at(mCbxFolders->currentItem()-1),
		     mCbxFolders->currentItem()-1);
    }
  }

  mSearching=false;
  updStatus();

  mBtnSearch->setCaption(i18n("Search"));
  enableGUI();
  if( mLastFocus )
    mLastFocus->setFocus();
}

//-----------------------------------------------------------------------------
void KMFldSearch::slotClose()
{
  accept();
}

//-----------------------------------------------------------------------------
void KMFldSearch::slotShowMsg(QListViewItem *item)
{
  QGuardedPtr<KMFolder> fld;
  KMMessage* msg;

  if(!item)
    return;

  QString fldName = item->text(LOCATION_COLUMN);
  QValueList<QGuardedPtr<KMFolder> > folders;
  QStringList str;
  int idx = item->text(FOLDERID_COLUMN).toInt();
  kernel->folderMgr()->createFolderList( &str, &folders );
  if (folders.at(idx) == folders.end())
    return;
  fld = *folders.at(idx);
  if (!fld)
    return;
  // This could goto the wrong folder if the folder list has been modified
  kdDebug() << "fld " << endl;

  mMainWin->slotSelectFolder(fld);
  msg = fld->getMsg(item->text(MSGID_COLUMN).toInt());
  if (!msg)
    return;

  mMainWin->slotSelectMessage(msg);
}

//-----------------------------------------------------------------------------
void KMFldSearch::enableGUI() {
  mBtnClose->setEnabled(!mSearching);
  mCbxFolders->setEnabled(!mSearching);

  for(int i = 0; i < mNumRules; i++)
    mRules[i]->setEnabled(!mSearching);
}

//=============================================================================
KMFldSearchRule::KMFldSearchRule(QWidget* aParent, QGridLayout* aGrid,
				 int aRow, int aCol)
{
  assert(aParent!=NULL);
  assert(aGrid!=NULL);

  mCbxField = new QComboBox(true, aParent);
  if( aRow > 1 ) {
    mCbxField->insertItem("");
  }
  mCbxField->insertItem("Subject");
  mCbxField->insertItem("From");
  mCbxField->insertItem("To");
  mCbxField->insertItem("Cc");
  mCbxField->insertItem("Organization");
  mCbxField->insertItem(i18n("<complete message>"));
  mCbxField->setMinimumSize(mCbxField->sizeHint());
  mCbxField->setMaximumSize(1024, mCbxField->sizeHint().height());

  mCbxFunc = new QComboBox(false, aParent);
  mCbxFunc->insertItem(i18n("contains"));
  mCbxFunc->insertItem(i18n("doesn't contain"));
  mCbxFunc->insertItem(i18n("equals"));
  mCbxFunc->insertItem(i18n("not equal"));
  mCbxFunc->insertItem(i18n("matches regular expr."));
  mCbxFunc->insertItem(i18n("doesn't match regular expr."));
  mCbxFunc->setMinimumSize(mCbxFunc->sizeHint());
  mCbxFunc->setMaximumSize(1024, mCbxFunc->sizeHint().height());

  mEdtValue = new QLineEdit(aParent);
  mEdtValue->setMinimumSize(mCbxFunc->sizeHint());
  mEdtValue->setMaximumSize(1024, mCbxFunc->sizeHint().height());
  if( aRow == 1 ) {
	mEdtValue->setFocus();
  }
  aGrid->addWidget(mCbxField, aRow, aCol);
  aGrid->addWidget(mCbxFunc,  aRow, aCol+1);
  aGrid->addWidget(mEdtValue, aRow, aCol+2);
}

//-----------------------------------------------------------------------------
KMFldSearchRule::~KMFldSearchRule()
{
}

//-----------------------------------------------------------------------------
void KMFldSearchRule::prepare(void)
{
  mFieldIdx = mCbxField->currentItem();
  mField = mCbxField->currentText();
  mFunc = mCbxFunc->currentItem();
  mValue = mEdtValue->text();
}

//-----------------------------------------------------------------------------
bool KMFldSearchRule::matches(const KMMessage* aMsg) const
{
  QString value;

  if (mField.isEmpty() || !aMsg) return true;
  if( mField == i18n("<complete message>") ) {
    value = aMsg->headerField(mField);
    value =+ aMsg->bodyDecoded();
  } else {
    value = aMsg->headerField(mField);
  }
  // also see KMFilterRule::matches() for a similar function:
  switch(mFunc)
  {
  case Equal:
    return (stricmp(value, mValue) == 0);
  case NotEqual:
    return (stricmp(value, mValue) != 0);
  case Contains:
    return value.contains(mValue, FALSE);
  case NotContains:
    return ( ! value.contains(mValue, FALSE) );
  case MatchesRegExp:
    return (value.find(QRegExp(mValue, FALSE)) >= 0);
  case NotMatchesRegExp:
    return (value.find(QRegExp(mValue, FALSE)) < 0);
  default:
    kdDebug() << "KMFldSearchRule::matches: wrong rule func #" << mFunc << endl;
    return false;
  }
}

//-----------------------------------------------------------------------------
void KMFldSearchRule::setEnabled(bool b) {
  mCbxField->setEnabled(b);
  mCbxFunc->setEnabled(b);
  mEdtValue->setEnabled(b);
}

//-----------------------------------------------------------------------------
#include "kmfldsearch.moc"
<commit_msg>searching "<complete message>" should search in body and header. (header was ignored before...)<commit_after>// kmfldsearch.cpp
// TODO: Add search in subfolder checkbox
// TODO: Use msgIdMD5 in MSGID_COLUMN

#include "kmfldsearch.h"
#include "kmglobal.h"
#include "kmfoldermgr.h"
#include "kmfolder.h"
#include "kmmessage.h"
#include "kmmainwin.h"

#include <qmessagebox.h>
#include <qpushbutton.h>
#include <qcombobox.h>
#include <qlineedit.h>
#include <qstring.h>
#include <qlayout.h>
#include <qlabel.h>
#include <assert.h>

#include <klocale.h>
#include <kdebug.h>
#include <kapp.h>
#include <kbuttonbox.h>
#include <qlistview.h>
#include <qheader.h>

#include <stdlib.h>

//-----------------------------------------------------------------------------
KMFldSearch::KMFldSearch(KMMainWin* w, const char* name,
  QString curFolder, bool modal, WFlags f):
  KMFldSearchInherited(NULL, name, modal, f | Qt::WDestructiveClose)
{
  KButtonBox* bbox;
  QLabel* lbl;
  KMFldSearchRule* rule;
  int i;

  mMainWin = w;

  mNumRules  = 2;
  mSearching = false;

  setCaption(i18n("Search in Folders"));
  mGrid = new QGridLayout(this, mNumRules+4, 5, 4, 4);

  mCbxFolders = createFolderCombo(curFolder);
  mCbxFolders->setMinimumSize(mCbxFolders->sizeHint());
  mCbxFolders->setMaximumSize(1024, mCbxFolders->sizeHint().height());
  mGrid->addMultiCellWidget(mCbxFolders, 0, 0, 1, 2);

  mRules = new KMFldSearchRule*[mNumRules];

  for (i=0; i<mNumRules; i++)
  {
    lbl = new QLabel(i ? i18n("and") : i18n("where"), this);
    lbl->setMinimumSize(lbl->sizeHint());
    lbl->setAlignment(AlignRight|AlignVCenter);
    mGrid->addWidget(lbl, i+1, 0);

    rule = new KMFldSearchRule(this, mGrid, i+1, 1);
    mRules[i] = rule;
  }

  lbl = new QLabel(i18n("Search in"), this);
  lbl->setFixedSize(lbl->sizeHint().width(), mCbxFolders->sizeHint().height());
  lbl->setAlignment(AlignRight|AlignVCenter);
  mGrid->addWidget(lbl, 0, 0);

  mLastFocus = new QWidget();	// to remeber the position of the focus

  mLbxMatches = new QListView(this, "Search in Folders");
  /* Default is to sort by date. TODO: Unfortunately this sorts *while*
     inserting, which looks rather strange - the user cannot read
     the results so far as they are constantly re-sorted --dnaber
  */
  mLbxMatches->setSorting(2, false);
  mLbxMatches->setShowSortIndicator(true);
  mLbxMatches->setAllColumnsShowFocus(true);
  mLbxMatches->addColumn(i18n("Subject"), 150);
  mLbxMatches->addColumn(i18n("Sender"), 120);
  mLbxMatches->addColumn(i18n("Date"), 120);
#define LOCATION_COLUMN 3
  mLbxMatches->addColumn(i18n("Folder"), 100);

#define MSGID_COLUMN 4
  //TODO: Use msgIDMD5, and create KMFolder::findIdMD5(QString) method
  mLbxMatches->addColumn("");		// should be hidden
  mLbxMatches->setColumnWidthMode( MSGID_COLUMN, QListView::Manual );
  mLbxMatches->header()->setResizeEnabled(false, MSGID_COLUMN);
  mLbxMatches->setColumnWidth( MSGID_COLUMN, 0 );

#define FOLDERID_COLUMN 5
  mLbxMatches->addColumn("");		// should be hidden
  mLbxMatches->setColumnWidthMode( FOLDERID_COLUMN, QListView::Manual );
  mLbxMatches->header()->setResizeEnabled(false, FOLDERID_COLUMN);
  mLbxMatches->setColumnWidth( FOLDERID_COLUMN, 0 );

  connect(mLbxMatches, SIGNAL(doubleClicked(QListViewItem *)),
	  this, SLOT(slotShowMsg(QListViewItem *)));
  //mLbxMatches->readConfig();
  mGrid->addMultiCellWidget(mLbxMatches, mNumRules+2, mNumRules+2, 0, 4);

  bbox = new KButtonBox(this, Vertical);
  mGrid->addMultiCellWidget(bbox, 0, mNumRules-1, 4, 4);
  mBtnSearch = bbox->addButton(i18n("Search"));
  mBtnSearch->setDefault(true);
  connect(mBtnSearch, SIGNAL(clicked()), SLOT(slotSearch()));
  mBtnClose  = bbox->addButton(i18n("Close"));
  connect(mBtnClose, SIGNAL(clicked()), SLOT(slotClose()));
  bbox->layout();
  bbox->setFixedWidth(bbox->width());

  mLblStatus = new QLabel(" ", this);
  mLblStatus->setFixedHeight(mLblStatus->sizeHint().height());
  mGrid->addMultiCellWidget(mLblStatus, mNumRules+3, mNumRules+3, 0, 3);

  mGrid->setColStretch(0, 0);
  mGrid->setColStretch(1, 2);
  mGrid->setColStretch(2, 1);
  mGrid->setColStretch(3, 100);
  mGrid->setColStretch(4, 0);

  mGrid->setRowStretch(mNumRules+2, 100);
  mGrid->setRowStretch(mNumRules+3, 0);

  mGrid->activate();
  resize(sizeHint());
}

//-----------------------------------------------------------------------------
KMFldSearch::~KMFldSearch()
{
  //TODO Save QListView layout
}

//-----------------------------------------------------------------------------
QComboBox* KMFldSearch::createFolderCombo(const QString curFolder)
{
 QValueList<QGuardedPtr<KMFolder> > folders;
 QComboBox* cbx = new QComboBox(false, this);
 QStringList str;

 kernel->folderMgr()->createI18nFolderList( &str, &folders );
 cbx->setFixedHeight(cbx->sizeHint().height());

 cbx->insertItem(i18n("<Search all folders>"));
 QStringList::Iterator st;
 int i = 1;
 for( st = str.begin(); st != str.end(); ++st, ++i) {
   cbx->insertItem(*st);
   QString fname = *st;
   if( fname.stripWhiteSpace() == curFolder ) {		// preselect current folder
     cbx->setCurrentItem(i);
   }
 }

 return cbx;
}

//-----------------------------------------------------------------------------
void KMFldSearch::updStatus(void)
{
  QString str;

  if (!mSearching) {
    if(!mStopped)
      str = i18n("Done, %1 matches (%2 messages processed)").arg(mNumMatches).arg(mCount);
    else
      str = i18n("Search cancelled, %1 matches so far (%3 messages processed)").arg(mNumMatches).arg(mCount);
  } else
    str = i18n("%1 matches, searching in %2 (message %3)")
		.arg(mNumMatches)
		.arg(mSearchFolder)
		.arg(mCount);
  mLblStatus->setText(str);
}

//-----------------------------------------------------------------------------
void KMFldSearch::keyPressEvent(QKeyEvent *evt)
{

  if(!evt)
    return;

  switch (evt->key()) {
      case Key_Return:
        KMFldSearchInherited::keyPressEvent(evt);
        break;
      case Key_Escape:
        if( mSearching )
          mSearching = false;
	else
          slotClose();
        break;
  }
}

//-----------------------------------------------------------------------------
bool KMFldSearch::searchInMessage(KMMessage* aMsg)
{
  int i;
  bool matches = true;

  assert(aMsg!=NULL);

  mCount++;
  updStatus();
  for(i=0; matches && i<mNumRules; i++)
    if (!mRules[i]->matches(aMsg))
      matches = false;

  return matches;
}

//-----------------------------------------------------------------------------
void KMFldSearch::searchInFolder(QGuardedPtr<KMFolder> aFld, int fldNum)
{
  KMMessage* msg;
  int i, num, upd;
  QString str;

  assert(!aFld.isNull());

  mBtnSearch->setText("Stop");
  if (aFld->isSystemFolder()) mSearchFolder = i18n(aFld->name());
    else mSearchFolder = aFld->name();
  kapp->processEvents();
  if (aFld->open() != 0)
  {
    kdDebug() << "Cannot open folder '" << (const char*)aFld->name() << "'" << endl;
    return;
  }

  num = aFld->count();
  for (i=0, upd=0; i<num && mSearching; i++, upd++)
  {
    msg = aFld->getMsg(i);
    if (msg && searchInMessage(msg))
    {
      (void)new QListViewItem(mLbxMatches,
			      msg->subject(),
			      msg->from(),
			      msg->dateIsoStr(),
			      mSearchFolder,
			      QString("%1").arg(i),
			      QString("%1").arg(fldNum)
			      );
      mNumMatches++;
      updStatus();
    }
    if (upd > 10)
    {
      kapp->processEvents();
      upd = 0;
      if (!aFld) // Folder deleted while searching!
	break;
    }
    aFld->unGetMsg(i);
  }

  if(!mSearching)
    // was stopped
    mStopped = true;

  updStatus();

  aFld->close();
  mBtnSearch->setText(i18n("Search"));
}

//-----------------------------------------------------------------------------
void KMFldSearch::searchInAllFolders(void)
{
  QValueList<QGuardedPtr<KMFolder> > folders;
  QGuardedPtr<KMFolder> folder;
  QStringList str;
  int i = 0;

  kernel->folderMgr()->createFolderList( &str, &folders );
  while (folders.at(i) != folders.end()) {
    folder = *folders.at(i);
    // Stop pressed?
    if(!mSearching)
      break;
    searchInFolder(folder,i);
    ++i;
  }
}

//-----------------------------------------------------------------------------
void KMFldSearch::slotSearch()
{
  int i;
  mLastFocus = focusWidget();
  mBtnSearch->setFocus();	// set focus so we don't miss key event

  if (mSearching)
  {
    mSearching = false;
    return;
  }

  mCount = 0;
  mStopped = false;
  mNumMatches = 0;
  mSearching  = true;

  mBtnSearch->setCaption(i18n("Stop"));
  enableGUI();
  mLbxMatches->clear();
  kapp->processEvents();

  for (i=0; i<mNumRules; i++)
    mRules[i]->prepare();

  if (mCbxFolders->currentItem() <= 0)
    searchInAllFolders();
  else {
    QValueList<QGuardedPtr<KMFolder> > folders;
    QStringList str;
    kernel->folderMgr()->createI18nFolderList( &str, &folders );
    if (str[mCbxFolders->currentItem()-1] == mCbxFolders->currentText()) {
      searchInFolder(*folders.at(mCbxFolders->currentItem()-1),
		     mCbxFolders->currentItem()-1);
    }
  }

  mSearching=false;
  updStatus();

  mBtnSearch->setCaption(i18n("Search"));
  enableGUI();
  if( mLastFocus )
    mLastFocus->setFocus();
}

//-----------------------------------------------------------------------------
void KMFldSearch::slotClose()
{
  accept();
}

//-----------------------------------------------------------------------------
void KMFldSearch::slotShowMsg(QListViewItem *item)
{
  QGuardedPtr<KMFolder> fld;
  KMMessage* msg;

  if(!item)
    return;

  QString fldName = item->text(LOCATION_COLUMN);
  QValueList<QGuardedPtr<KMFolder> > folders;
  QStringList str;
  int idx = item->text(FOLDERID_COLUMN).toInt();
  kernel->folderMgr()->createFolderList( &str, &folders );
  if (folders.at(idx) == folders.end())
    return;
  fld = *folders.at(idx);
  if (!fld)
    return;
  // This could goto the wrong folder if the folder list has been modified
  kdDebug() << "fld " << endl;

  mMainWin->slotSelectFolder(fld);
  msg = fld->getMsg(item->text(MSGID_COLUMN).toInt());
  if (!msg)
    return;

  mMainWin->slotSelectMessage(msg);
}

//-----------------------------------------------------------------------------
void KMFldSearch::enableGUI() {
  mBtnClose->setEnabled(!mSearching);
  mCbxFolders->setEnabled(!mSearching);

  for(int i = 0; i < mNumRules; i++)
    mRules[i]->setEnabled(!mSearching);
}

//=============================================================================
KMFldSearchRule::KMFldSearchRule(QWidget* aParent, QGridLayout* aGrid,
				 int aRow, int aCol)
{
  assert(aParent!=NULL);
  assert(aGrid!=NULL);

  mCbxField = new QComboBox(true, aParent);
  if( aRow > 1 ) {
    mCbxField->insertItem("");
  }
  mCbxField->insertItem("Subject");
  mCbxField->insertItem("From");
  mCbxField->insertItem("To");
  mCbxField->insertItem("Cc");
  mCbxField->insertItem("Organization");
  mCbxField->insertItem(i18n("<complete message>"));
  mCbxField->setMinimumSize(mCbxField->sizeHint());
  mCbxField->setMaximumSize(1024, mCbxField->sizeHint().height());

  mCbxFunc = new QComboBox(false, aParent);
  mCbxFunc->insertItem(i18n("contains"));
  mCbxFunc->insertItem(i18n("doesn't contain"));
  mCbxFunc->insertItem(i18n("equals"));
  mCbxFunc->insertItem(i18n("not equal"));
  mCbxFunc->insertItem(i18n("matches regular expr."));
  mCbxFunc->insertItem(i18n("doesn't match regular expr."));
  mCbxFunc->setMinimumSize(mCbxFunc->sizeHint());
  mCbxFunc->setMaximumSize(1024, mCbxFunc->sizeHint().height());

  mEdtValue = new QLineEdit(aParent);
  mEdtValue->setMinimumSize(mCbxFunc->sizeHint());
  mEdtValue->setMaximumSize(1024, mCbxFunc->sizeHint().height());
  if( aRow == 1 ) {
	mEdtValue->setFocus();
  }
  aGrid->addWidget(mCbxField, aRow, aCol);
  aGrid->addWidget(mCbxFunc,  aRow, aCol+1);
  aGrid->addWidget(mEdtValue, aRow, aCol+2);
}

//-----------------------------------------------------------------------------
KMFldSearchRule::~KMFldSearchRule()
{
}

//-----------------------------------------------------------------------------
void KMFldSearchRule::prepare(void)
{
  mFieldIdx = mCbxField->currentItem();
  mField = mCbxField->currentText();
  mFunc = mCbxFunc->currentItem();
  mValue = mEdtValue->text();
}

//-----------------------------------------------------------------------------
bool KMFldSearchRule::matches(const KMMessage* aMsg) const
{
  QString value;

  if (mField.isEmpty() || !aMsg) return true;
  if( mField == i18n("<complete message>") ) {
    value = aMsg->headerAsString();
    value += aMsg->bodyDecoded();
  } else {
    value = aMsg->headerField(mField);
  }
  // also see KMFilterRule::matches() for a similar function:
  switch(mFunc)
  {
  case Equal:
    return (stricmp(value, mValue) == 0);
  case NotEqual:
    return (stricmp(value, mValue) != 0);
  case Contains:
    return value.contains(mValue, FALSE);
  case NotContains:
    return ( ! value.contains(mValue, FALSE) );
  case MatchesRegExp:
    return (value.find(QRegExp(mValue, FALSE)) >= 0);
  case NotMatchesRegExp:
    return (value.find(QRegExp(mValue, FALSE)) < 0);
  default:
    kdDebug() << "KMFldSearchRule::matches: wrong rule func #" << mFunc << endl;
    return false;
  }
}

//-----------------------------------------------------------------------------
void KMFldSearchRule::setEnabled(bool b) {
  mCbxField->setEnabled(b);
  mCbxFunc->setEnabled(b);
  mEdtValue->setEnabled(b);
}

//-----------------------------------------------------------------------------
#include "kmfldsearch.moc"
<|endoftext|>


<commit_before>/***************************************************************************
                          kmailcvt2.cpp  -  description
                             -------------------
    begin                : Wed Aug  2 11:23:04 CEST 2000
    copyright            : (C) 2000 by Hans Dijkema
    email                : kmailcvt@hum.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kmailcvt.h"
#include <kaboutdialog.h>
#include <klocale.h>
#include <qgrid.h>
#include <qlayout.h>

//////////////////////////////////////////////////////////////////////////////
//
// Add filters here
//
//////////////////////////////////////////////////////////////////////////////

#include "filter_oe4.hxx"
#include "filter_oe5.hxx"
#include "filter_pmail.hxx"
#include "filter_pab.hxx"
#include "filter_eudora_ab.hxx"
#include "filter_ldif.hxx"

void Kmailcvt2::doFilters(void)
{
  imports->add(new filter_oe5);
  imports->add(new filter_pmail);
  imports->add(new filter_oe4);
  imports->add(new filter_pab);
  imports->add(new filter_ldif);
  imports->add(new filter_eudora_ab);
}

Kmailcvt2::Kmailcvt2(QWidget *parent, const char *name) : QWidget(parent, name)
{
  QGridLayout *grid1 = new QGridLayout(this,15,8,15,7);
  info=new filterInfo(this);

  grid1->addMultiCellWidget(info, 2,14,0,7);
  imports=new filters(info,this);
  grid1->addMultiCellWidget(imports, 0,0,0,5);
  doFilters();

  import=new QPushButton(i18n("&Import"),this);
  grid1->addWidget(import, 0,6);

  quit=new QPushButton(i18n("&Quit"),this);
  grid1->addWidget(quit, 0,7);

  about=new QPushButton(i18n("&About"),this);
  grid1->addWidget(about,1 ,7);

  connect(import,SIGNAL(clicked()),SLOT(startFilter()));
  connect(quit,SIGNAL(clicked()),SLOT(Quit()));
  connect(about,SIGNAL(clicked()),SLOT(About()));

  setCaption(KMAILCVT KMAILCVT_VERSION);
}

Kmailcvt2::~Kmailcvt2()
{
  delete imports;
  delete import;
  delete quit;
}

void Kmailcvt2::startFilter()
{
  import->setEnabled(false);
  imports->setEnabled(false);
  quit->setEnabled(false);
  about->setEnabled(false);

  imports->import();

  imports->setEnabled(true);
  import->setEnabled(true);
  quit->setEnabled(true);
  about->setEnabled(true);
}

void Kmailcvt2::Quit()
{
  close();
}

void Kmailcvt2::About()
{
QString cap=KMAILCVT KMAILCVT_VERSION;
/*
KAboutDialog dlg(KAboutDialog::AbtAppStandard,
                 cap,
                 KAboutDialog::Ok,
                 KAboutDialog::Ok,
                 this,
                 "kmailcvt2_about",
                 true
                );

  dlg.setTitle("Welcome to KMailCvt");
  dlg.setProduct(KMAILCVT,
                 KMAILCVT_VERSION,
                 "Hans Dijkema",
                 "2000"
                );
  dlg.setImage();
  dlg.addTextPage("Description",
                  "Converts various formats to Kmail and K Addressbook\n\n"
                  "Currently supported:\n\n"
                  "  - MS Outlook Express 5.0 .DBX format\n"
                  "  - MS Exchange Personal Addressbook .PAB format\n"
                  "\n"
                  "You are invited to contribute other import (or output)\n"
                  "formats. I you want to contribute, please contact me at\n"
                  "kmailcvt@hum.org\n"
                 );
*/
KAboutDialog dlg(this,"KmailCvt2");
  dlg.setLogo(KApplication::kApplication()->icon());
  {QString name="(c) 2000 Hans Dijkema",
           email="kmailcvt@hum.org",
           url="http://www.hum.org/kmailcvt.html",
           work=i18n("\n"
                "Converts various import formats to Kmail and K Addressbook  \n"
                "Currently supported formats:\n\n"
                );
           work+=imports->getFilters();
           work+=i18n("\n"
                "You are invited to contribute other import (or output)\n"
                "formats. I you want to contribute, please contact me.\n"
                "Translations are welcome too!"
                );
    dlg.setAuthor(name,email,url,work);
    dlg.adjust();
  }
  dlg.setVersion(KMAILCVT KMAILCVT_VERSION);
  {int X,Y;
     dlg.adjustSize();
     X=(width()-dlg.width())/2+x();
     Y=(height()-dlg.height())/2+y();
     dlg.move(X,Y);
  }
  dlg.show();
}

#include "kmailcvt.moc"
<commit_msg>Dix about dialogbox<commit_after>/***************************************************************************
                          kmailcvt2.cpp  -  description
                             -------------------
    begin                : Wed Aug  2 11:23:04 CEST 2000
    copyright            : (C) 2000 by Hans Dijkema
    email                : kmailcvt@hum.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kmailcvt.h"
#include <kaboutdialog.h>
#include <klocale.h>
#include <qgrid.h>
#include <qlayout.h>

//////////////////////////////////////////////////////////////////////////////
//
// Add filters here
//
//////////////////////////////////////////////////////////////////////////////

#include "filter_oe4.hxx"
#include "filter_oe5.hxx"
#include "filter_pmail.hxx"
#include "filter_pab.hxx"
#include "filter_eudora_ab.hxx"
#include "filter_ldif.hxx"

void Kmailcvt2::doFilters(void)
{
  imports->add(new filter_oe5);
  imports->add(new filter_pmail);
  imports->add(new filter_oe4);
  imports->add(new filter_pab);
  imports->add(new filter_ldif);
  imports->add(new filter_eudora_ab);
}

Kmailcvt2::Kmailcvt2(QWidget *parent, const char *name) : QWidget(parent, name)
{
  QGridLayout *grid1 = new QGridLayout(this,15,8,15,7);
  info=new filterInfo(this);

  grid1->addMultiCellWidget(info, 2,14,0,7);
  imports=new filters(info,this);
  grid1->addMultiCellWidget(imports, 0,0,0,5);
  doFilters();

  import=new QPushButton(i18n("&Import"),this);
  grid1->addWidget(import, 0,6);

  quit=new QPushButton(i18n("&Quit"),this);
  grid1->addWidget(quit, 0,7);

  about=new QPushButton(i18n("&About"),this);
  grid1->addWidget(about,1 ,7);

  connect(import,SIGNAL(clicked()),SLOT(startFilter()));
  connect(quit,SIGNAL(clicked()),SLOT(Quit()));
  connect(about,SIGNAL(clicked()),SLOT(About()));

  setCaption(KMAILCVT KMAILCVT_VERSION);
}

Kmailcvt2::~Kmailcvt2()
{
  delete imports;
  delete import;
  delete quit;
}

void Kmailcvt2::startFilter()
{
  import->setEnabled(false);
  imports->setEnabled(false);
  quit->setEnabled(false);
  about->setEnabled(false);

  imports->import();

  imports->setEnabled(true);
  import->setEnabled(true);
  quit->setEnabled(true);
  about->setEnabled(true);
}

void Kmailcvt2::Quit()
{
  close();
}

void Kmailcvt2::About()
{
QString cap=KMAILCVT KMAILCVT_VERSION;
/*
KAboutDialog dlg(KAboutDialog::AbtAppStandard,
                 cap,
                 KAboutDialog::Ok,
                 KAboutDialog::Ok,
                 this,
                 "kmailcvt2_about",
                 true
                );

  dlg.setTitle("Welcome to KMailCvt");
  dlg.setProduct(KMAILCVT,
                 KMAILCVT_VERSION,
                 "Hans Dijkema",
                 "2000"
                );
  dlg.setImage();
  dlg.addTextPage("Description",
                  "Converts various formats to Kmail and K Addressbook\n\n"
                  "Currently supported:\n\n"
                  "  - MS Outlook Express 5.0 .DBX format\n"
                  "  - MS Exchange Personal Addressbook .PAB format\n"
                  "\n"
                  "You are invited to contribute other import (or output)\n"
                  "formats. I you want to contribute, please contact me at\n"
                  "kmailcvt@hum.org\n"
                 );
*/
KAboutDialog dlg(this,"KmailCvt2");
  dlg.setLogo(KApplication::kApplication()->icon());
  {QString name="(c) 2000 Hans Dijkema",
           email="kmailcvt@hum.org",
           url="http://www.hum.org/kmailcvt.html",
           work=i18n("\n"
                "Converts various import formats to Kmail and K Addressbook  \n"
                "Currently supported formats:\n\n"
                );
           work+=imports->getFilters();
           work+=i18n("\n"
                "You are invited to contribute other import (or output)\n"
                "formats. I you want to contribute, please contact me.\n"
                "Translations are welcome too!"
                );
    dlg.setAuthor(name,email,url,work);
    dlg.adjust();
  }
  dlg.setVersion(KMAILCVT KMAILCVT_VERSION);
  {int X,Y;
     dlg.adjustSize();
     X=(width()-dlg.width())/2+x();
     Y=(height()-dlg.height())/2+y();
     dlg.move(X,Y);
  }
  dlg.exec();
}

#include "kmailcvt.moc"
<|endoftext|>


<commit_before>/*
 * producer_glaxnimate.cpp -- a Glaxnimate/Qt based producer for MLT
 * Copyright (C) 2022 Meltytech, LLC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>
 */

#include <framework/mlt.h>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <QApplication>

#include "io/io_registry.hpp"
using namespace glaxnimate;

class Glaxnimate
{
private:
    mlt_producer m_producer = nullptr;
    std::unique_ptr<model::Document> m_document;

public:
    mlt_profile m_profile = nullptr;

    void setProducer( mlt_producer producer )
        { m_producer = producer; }

    mlt_producer producer() const
        { return m_producer; }

    mlt_service service() const
        { return MLT_PRODUCER_SERVICE(m_producer); }

    mlt_properties properties() const
        { return MLT_PRODUCER_PROPERTIES(m_producer); }

    QSize size() const { return m_document->size(); }

    int duration() const {
        auto frames = m_document->main()->animation->last_frame.get() - m_document->main()->animation->first_frame.get() + 1.f;
        return toMltFps(frames);
    }

    int toMltFps(float frame) const {
        return qRound(frame / fps() * m_profile->frame_rate_num / m_profile->frame_rate_den);
    }

    float toGlaxnimateFps(float frame) const {
        return frame * fps() * m_profile->frame_rate_den / m_profile->frame_rate_num;
    }

    int firstFrame() const {
        return toMltFps(m_document->main()->animation->first_frame.get());
    }

    float fps() const {
        return m_document->main()->get_fps();
    }

    int getImage(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable)
    {
        int error = 0;
        auto pos = mlt_frame_original_position(frame);
        if (mlt_properties_get(properties(), "eof") && !::strcmp("loop", mlt_properties_get(properties(), "eof"))) {
            pos %= duration();
        }
        auto bg = mlt_properties_get_color(properties(), "background");
        auto background = QColor(bg.r, bg.g, bg.b, bg.a);
        pos += toMltFps(m_document->main()->animation->first_frame.get());
        auto image = m_document->render_image(toGlaxnimateFps(pos), {*width, *height}, background);

        *format = mlt_image_rgba;
        int size = mlt_image_format_size(*format, *width, *height, NULL);
        *buffer = static_cast<uint8_t*>(mlt_pool_alloc(size));
        memcpy(*buffer, image.constBits(), size);
        error = mlt_frame_set_image(frame, *buffer, size, mlt_pool_release);

        return error;
    }

    bool open(const char* fileName)
    {
        auto filename = QString::fromUtf8(fileName);
        auto importer = io::IoRegistry::instance().from_filename(filename, io::ImportExport::Import);
        if (!importer || !importer->can_open()) {
            mlt_log_error(service(), "Unknown importer\n");
            return false;
        }

        QFile file(filename);
        if (!file.open(QIODevice::ReadOnly)) {
            mlt_log_error(service(), "Could not open input file for reading\n");
            return false;
        }

        m_document.reset(new model::Document(filename));
        QVariantMap settings;
        if (!importer->open(file, filename, m_document.get(), settings)) {
            mlt_log_error(service(), "Error loading input file\n");
            return false;
        }

        return true;
    }
};

static bool createQApplicationIfNeeded(mlt_service service)
{
    if (!qApp) {
#if defined(Q_OS_WIN) && defined(NODEPLOY)
        QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR"))+QStringLiteral("/bin"));
        QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR"))+QStringLiteral("/plugins"));
#endif
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
        if (getenv("DISPLAY") == 0) {
            mlt_log_error(service,
                "The MLT Qt module requires a X11 environment.\n"
                "Please either run melt from an X session or use a fake X server like xvfb:\n"
                "xvfb-run -a melt (...)\n" );
            return false;
        }
#endif
        if (!mlt_properties_get(mlt_global_properties(), "qt_argv"))
            mlt_properties_set(mlt_global_properties(), "qt_argv", "MLT");
        static int argc = 1;
        static char* argv[] = { mlt_properties_get(mlt_global_properties(), "qt_argv") };
        new QApplication(argc, argv);
        const char *localename = mlt_properties_get_lcnumeric(MLT_SERVICE_PROPERTIES(service));
        QLocale::setDefault(QLocale(localename));
    }
    return true;
}

extern "C" {

static int get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable)
{
    auto producer = static_cast<mlt_producer>(mlt_frame_pop_service(frame));
    auto glax = static_cast<Glaxnimate*>(producer->child);

    if (mlt_properties_get_int(glax->properties(), "refresh")) {
        mlt_properties_clear(glax->properties(), "refresh");
        glax->open(mlt_properties_get(glax->properties(), "resource"));
        if (glax->duration() > mlt_properties_get_int(glax->properties(), "length")) {
            mlt_properties_set_int(glax->properties(), "length", glax->duration());
        }
    }

    return glax->getImage(frame, buffer, format, width, height, writable);
}

static int get_frame(mlt_producer producer, mlt_frame_ptr frame, int index)
{
    *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer));
    mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );

    // Set frame properties
    mlt_properties_set_int(frame_properties, "progressive", 1);
    double force_ratio = mlt_properties_get_double(MLT_PRODUCER_PROPERTIES(producer), "force_aspect_ratio");
    if (force_ratio > 0.0)
        mlt_properties_set_double(frame_properties, "aspect_ratio", force_ratio);
    else
        mlt_properties_set_double(frame_properties, "aspect_ratio", 1.0);

    mlt_frame_set_position(*frame, mlt_producer_position(producer));
    mlt_frame_push_service(*frame, producer);
    mlt_frame_push_get_image(*frame, get_image);
    mlt_producer_prepare_next(producer);
    return 0;
}

static void producer_close(mlt_producer producer)
{
    delete static_cast<Glaxnimate*>(producer->child);
    producer->close = nullptr;
    mlt_producer_close(producer);
}

mlt_producer producer_glaxnimate_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg)
{
    // Allocate the producer
    Glaxnimate* glax = new Glaxnimate();
    mlt_producer producer = (mlt_producer) calloc(1, sizeof( *producer ));

    createQApplicationIfNeeded(MLT_PRODUCER_SERVICE(producer));

    // If allocated and initializes
    if (glax && !mlt_producer_init(producer, glax) && glax->open(arg)) {
        glax->setProducer(producer);
        glax->m_profile = profile;
        producer->close = (mlt_destructor) producer_close;
        producer->get_frame = get_frame;

        auto properties = glax->properties();
        mlt_properties_set(properties, "resource", arg);
        mlt_properties_set(properties, "background", "#00000000");
        mlt_properties_set_int(properties, "aspect_ratio", 1);
        mlt_properties_set_int(properties, "progressive", 1);
        mlt_properties_set_int(properties, "seekable", 1);
        mlt_properties_set_int(properties, "meta.media.width", glax->size().width());
        mlt_properties_set_int(properties, "meta.media.height", glax->size().height());
        mlt_properties_set_int(properties, "meta.media.sample_aspect_num", 1);
        mlt_properties_set_int(properties, "meta.media.sample_aspect_den", 1);
        mlt_properties_set_double(properties, "meta.media.frame_rate", glax->fps());
        mlt_properties_set_int(properties, "out", glax->duration() - 1);
        mlt_properties_set_int(properties, "length", glax->duration());
        mlt_properties_set_int(properties, "first_frame", glax->firstFrame());
        mlt_properties_set(properties, "eof", "loop");
    }
    return producer;
}

static mlt_properties metadata(mlt_service_type type, const char *id, void *data)
{
    char file[PATH_MAX];
    const char *service_type = NULL;
    switch (type) {
        case mlt_service_producer_type:
            service_type = "producer";
            break;
        default:
            return NULL;
    }
    snprintf(file, PATH_MAX, "%s/glaxnimate/%s_%s.yml", mlt_environment("MLT_DATA"), service_type, id);
    return mlt_properties_parse_yaml(file);
}

MLT_REPOSITORY
{
    MLT_REGISTER(mlt_service_producer_type, "glaxnimate", producer_glaxnimate_init);
    MLT_REGISTER_METADATA(mlt_service_producer_type, "glaxnimate", metadata, NULL);
}

} // extern C
<commit_msg>glaxnimate: fix crash regression if not Windows<commit_after>/*
 * producer_glaxnimate.cpp -- a Glaxnimate/Qt based producer for MLT
 * Copyright (C) 2022 Meltytech, LLC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>
 */

#include <framework/mlt.h>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <QApplication>

#include "io/io_registry.hpp"
using namespace glaxnimate;

class Glaxnimate
{
private:
    mlt_producer m_producer = nullptr;
    std::unique_ptr<model::Document> m_document;

public:
    mlt_profile m_profile = nullptr;

    void setProducer( mlt_producer producer )
        { m_producer = producer; }

    mlt_producer producer() const
        { return m_producer; }

    mlt_service service() const
        { return MLT_PRODUCER_SERVICE(m_producer); }

    mlt_properties properties() const
        { return MLT_PRODUCER_PROPERTIES(m_producer); }

    QSize size() const { return m_document->size(); }

    int duration() const {
        auto frames = m_document->main()->animation->last_frame.get() - m_document->main()->animation->first_frame.get() + 1.f;
        return toMltFps(frames);
    }

    int toMltFps(float frame) const {
        return qRound(frame / fps() * m_profile->frame_rate_num / m_profile->frame_rate_den);
    }

    float toGlaxnimateFps(float frame) const {
        return frame * fps() * m_profile->frame_rate_den / m_profile->frame_rate_num;
    }

    int firstFrame() const {
        return toMltFps(m_document->main()->animation->first_frame.get());
    }

    float fps() const {
        return m_document->main()->get_fps();
    }

    int getImage(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable)
    {
        int error = 0;
        auto pos = mlt_frame_original_position(frame);
        if (mlt_properties_get(properties(), "eof") && !::strcmp("loop", mlt_properties_get(properties(), "eof"))) {
            pos %= duration();
        }
        auto bg = mlt_properties_get_color(properties(), "background");
        auto background = QColor(bg.r, bg.g, bg.b, bg.a);
        pos += toMltFps(m_document->main()->animation->first_frame.get());
        auto image = m_document->render_image(toGlaxnimateFps(pos), {*width, *height}, background);

        *format = mlt_image_rgba;
        int size = mlt_image_format_size(*format, *width, *height, NULL);
        *buffer = static_cast<uint8_t*>(mlt_pool_alloc(size));
        memcpy(*buffer, image.constBits(), size);
        error = mlt_frame_set_image(frame, *buffer, size, mlt_pool_release);

        return error;
    }

    bool open(const char* fileName)
    {
        auto filename = QString::fromUtf8(fileName);
        auto importer = io::IoRegistry::instance().from_filename(filename, io::ImportExport::Import);
        if (!importer || !importer->can_open()) {
            mlt_log_error(service(), "Unknown importer\n");
            return false;
        }

        QFile file(filename);
        if (!file.open(QIODevice::ReadOnly)) {
            mlt_log_error(service(), "Could not open input file for reading\n");
            return false;
        }

        m_document.reset(new model::Document(filename));
        QVariantMap settings;
        if (!importer->open(file, filename, m_document.get(), settings)) {
            mlt_log_error(service(), "Error loading input file\n");
            return false;
        }

        return true;
    }
};

static bool createQApplicationIfNeeded(mlt_service service)
{
    if (!qApp) {
#if defined(Q_OS_WIN) && defined(NODEPLOY)
        QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR"))+QStringLiteral("/bin"));
        QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR"))+QStringLiteral("/plugins"));
#endif
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
        if (getenv("DISPLAY") == 0) {
            mlt_log_error(service,
                "The MLT Qt module requires a X11 environment.\n"
                "Please either run melt from an X session or use a fake X server like xvfb:\n"
                "xvfb-run -a melt (...)\n" );
            return false;
        }
#endif
        if (!mlt_properties_get(mlt_global_properties(), "qt_argv"))
            mlt_properties_set(mlt_global_properties(), "qt_argv", "MLT");
        static int argc = 1;
        static char* argv[] = { mlt_properties_get(mlt_global_properties(), "qt_argv") };
        new QApplication(argc, argv);
        const char *localename = mlt_properties_get_lcnumeric(MLT_SERVICE_PROPERTIES(service));
        QLocale::setDefault(QLocale(localename));
    }
    return true;
}

extern "C" {

static int get_image(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable)
{
    auto producer = static_cast<mlt_producer>(mlt_frame_pop_service(frame));
    auto glax = static_cast<Glaxnimate*>(producer->child);

    if (mlt_properties_get_int(glax->properties(), "refresh")) {
        mlt_properties_clear(glax->properties(), "refresh");
        glax->open(mlt_properties_get(glax->properties(), "resource"));
        if (glax->duration() > mlt_properties_get_int(glax->properties(), "length")) {
            mlt_properties_set_int(glax->properties(), "length", glax->duration());
        }
    }

    return glax->getImage(frame, buffer, format, width, height, writable);
}

static int get_frame(mlt_producer producer, mlt_frame_ptr frame, int index)
{
    *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer));
    mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );

    // Set frame properties
    mlt_properties_set_int(frame_properties, "progressive", 1);
    double force_ratio = mlt_properties_get_double(MLT_PRODUCER_PROPERTIES(producer), "force_aspect_ratio");
    if (force_ratio > 0.0)
        mlt_properties_set_double(frame_properties, "aspect_ratio", force_ratio);
    else
        mlt_properties_set_double(frame_properties, "aspect_ratio", 1.0);

    mlt_frame_set_position(*frame, mlt_producer_position(producer));
    mlt_frame_push_service(*frame, producer);
    mlt_frame_push_get_image(*frame, get_image);
    mlt_producer_prepare_next(producer);
    return 0;
}

static void producer_close(mlt_producer producer)
{
    delete static_cast<Glaxnimate*>(producer->child);
    producer->close = nullptr;
    mlt_producer_close(producer);
}

mlt_producer producer_glaxnimate_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg)
{
    // Allocate the producer
    Glaxnimate* glax = new Glaxnimate();
    mlt_producer producer = (mlt_producer) calloc(1, sizeof( *producer ));

    if (!glax || mlt_producer_init(producer, glax)
            || !createQApplicationIfNeeded(MLT_PRODUCER_SERVICE(producer))) {
        mlt_producer_close(producer);
        return NULL;
    }
    // If allocated and initializes
    if (glax->open(arg)) {
        glax->setProducer(producer);
        glax->m_profile = profile;
        producer->close = (mlt_destructor) producer_close;
        producer->get_frame = get_frame;

        auto properties = glax->properties();
        mlt_properties_set(properties, "resource", arg);
        mlt_properties_set(properties, "background", "#00000000");
        mlt_properties_set_int(properties, "aspect_ratio", 1);
        mlt_properties_set_int(properties, "progressive", 1);
        mlt_properties_set_int(properties, "seekable", 1);
        mlt_properties_set_int(properties, "meta.media.width", glax->size().width());
        mlt_properties_set_int(properties, "meta.media.height", glax->size().height());
        mlt_properties_set_int(properties, "meta.media.sample_aspect_num", 1);
        mlt_properties_set_int(properties, "meta.media.sample_aspect_den", 1);
        mlt_properties_set_double(properties, "meta.media.frame_rate", glax->fps());
        mlt_properties_set_int(properties, "out", glax->duration() - 1);
        mlt_properties_set_int(properties, "length", glax->duration());
        mlt_properties_set_int(properties, "first_frame", glax->firstFrame());
        mlt_properties_set(properties, "eof", "loop");
    }
    return producer;
}

static mlt_properties metadata(mlt_service_type type, const char *id, void *data)
{
    char file[PATH_MAX];
    const char *service_type = NULL;
    switch (type) {
        case mlt_service_producer_type:
            service_type = "producer";
            break;
        default:
            return NULL;
    }
    snprintf(file, PATH_MAX, "%s/glaxnimate/%s_%s.yml", mlt_environment("MLT_DATA"), service_type, id);
    return mlt_properties_parse_yaml(file);
}

MLT_REPOSITORY
{
    MLT_REGISTER(mlt_service_producer_type, "glaxnimate", producer_glaxnimate_init);
    MLT_REGISTER_METADATA(mlt_service_producer_type, "glaxnimate", metadata, NULL);
}

} // extern C
<|endoftext|>


<commit_before>/**************************************************************************
**
** Copyright (c) 2014 Lorenz Haas
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "configurationeditor.h"

#include "abstractsettings.h"

#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <utils/qtcassert.h>

#include <QAbstractItemView>
#include <QCompleter>
#include <QScrollBar>
#include <QStringListModel>

namespace Beautifier {
namespace Internal {

ConfigurationSyntaxHighlighter::ConfigurationSyntaxHighlighter(QTextDocument *parent) :
    QSyntaxHighlighter(parent)
{
    const TextEditor::FontSettings fs = TextEditor::TextEditorSettings::instance()->fontSettings();
    m_formatKeyword = fs.toTextCharFormat(TextEditor::C_FIELD);
    m_formatComment = fs.toTextCharFormat(TextEditor::C_COMMENT);

    m_expressionComment.setPattern(QLatin1String("#[^\\n]*"));
    m_expressionComment.setMinimal(false);
}

void ConfigurationSyntaxHighlighter::setKeywords(const QStringList &keywords)
{
    if (keywords.isEmpty())
        return;

    QStringList pattern;
    for (int i = 0, total = keywords.count(); i < total; ++i)
        pattern << QRegExp::escape(keywords.at(i));

    m_expressionKeyword.setPattern(QLatin1String("(?:\\s|^)(") + pattern.join(QLatin1String("|"))
                                   + QLatin1String(")(?=\\s|\\:|\\=|\\,|$)"));
}

void ConfigurationSyntaxHighlighter::setCommentExpression(const QRegExp &rx)
{
    m_expressionComment = rx;
}

void ConfigurationSyntaxHighlighter::highlightBlock(const QString &text)
{
    int pos = 0;
    if (!m_expressionKeyword.isEmpty()) {
        while ((pos = m_expressionKeyword.indexIn(text, pos)) != -1) {
            const int length = m_expressionKeyword.matchedLength();
            setFormat(pos, length, m_formatKeyword);
            pos += length;
        }
    }

    if (!m_expressionComment.isEmpty()) {
        pos = 0;
        while ((pos = m_expressionComment.indexIn(text, pos)) != -1) {
            const int length = m_expressionComment.matchedLength();
            setFormat(pos, length, m_formatComment);
            pos += length;
        }
    }
}

ConfigurationEditor::ConfigurationEditor(QWidget *parent)
    : QPlainTextEdit(parent)
    , m_settings(0)
    , m_completer(new QCompleter(this))
    , m_model(new QStringListModel(QStringList(), m_completer))
    , m_highlighter(new ConfigurationSyntaxHighlighter(document()))
{
    m_completer->setModel(m_model);
    m_completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
    m_completer->setWrapAround(false);
    m_completer->setWidget(this);
    m_completer->setCompletionMode(QCompleter::PopupCompletion);
    m_completer->setCaseSensitivity(Qt::CaseInsensitive);
    m_completer->popup()->installEventFilter(this);

    connect(m_completer, SIGNAL(activated(QString)), this, SLOT(insertCompleterText(QString)));
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateDocumentation()));
}

void ConfigurationEditor::setSettings(AbstractSettings *settings)
{
    QTC_CHECK(settings);
    m_settings = settings;

    QStringList keywords = m_settings->options();
    m_highlighter->setKeywords(keywords);
    keywords << m_settings->completerWords();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    keywords.sort(Qt::CaseInsensitive);
#else //QT_VERSION_CHECK(5, 0, 0)
    keywords.sort();
#endif //QT_VERSION_CHECK(5, 0, 0)
    m_model->setStringList(keywords);
}

void ConfigurationEditor::setCommentExpression(const QRegExp &rx)
{
    m_highlighter->setCommentExpression(rx);
}

// Workaround for handling "ESC" right when popup is shown.
bool ConfigurationEditor::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::ShortcutOverride) {
        QKeyEvent *key = static_cast<QKeyEvent *>(event);
        if (key->key() == Qt::Key_Escape) {
            event->accept();
            m_completer->popup()->hide();
            return true;
        }
    }
    return QObject::eventFilter(object, event);
}

void ConfigurationEditor::keyPressEvent(QKeyEvent *event)
{
    const int key = event->key();

    if (key == Qt::Key_Escape) {
        event->ignore();
        return;
    }

    if (m_completer->popup()->isVisible()) {
        switch (key) {
        case Qt::Key_Backtab:
        case Qt::Key_Enter:
        case Qt::Key_Return:
        case Qt::Key_Tab:
            event->ignore();
            return;
        default:
            break;
        }
    }

    const bool isShortcut = ((event->modifiers() & Qt::ControlModifier) && key == Qt::Key_Space);
    if (!isShortcut)
        QPlainTextEdit::keyPressEvent(event);

    const int cursorPosition = textCursor().position();
    QTextCursor cursor = cursorForTextUnderCursor();
    const QString prefix = cursor.selectedText();

    if (!isShortcut && (prefix.length() < 2 || cursorPosition != cursor.position())) {
        m_completer->popup()->hide();
        return;
    }

    if (prefix != m_completer->completionPrefix()) {
        m_completer->setCompletionPrefix(prefix);
        m_completer->popup()->setCurrentIndex(m_completer->completionModel()->index(0, 0));
    }

    if (m_completer->completionCount() == 1 && prefix == m_completer->currentCompletion()) {
        m_completer->popup()->hide();
        return;
    }

    QRect popupRect = cursorRect();
    popupRect.setWidth(m_completer->popup()->sizeHintForColumn(0)
                       + m_completer->popup()->verticalScrollBar()->sizeHint().width());
    m_completer->complete(popupRect);
}

// Hack because '-' and '=' are treated as End/StartOfWord
QTextCursor ConfigurationEditor::cursorForTextUnderCursor(QTextCursor tc) const
{
    if (tc.isNull())
        tc = textCursor();

    tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
    QChar ch = document()->characterAt(tc.position() - 1);
    while (!(ch.isNull() || ch.isSpace() || ch == QLatin1Char(':') || ch == QLatin1Char(','))) {
        tc.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
        ch = document()->characterAt(tc.position() - 1);
    }
    tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
    ch = document()->characterAt(tc.position());
    while (!(ch.isNull() || ch.isSpace() || ch == QLatin1Char(':') || ch == QLatin1Char(','))) {
        tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
        ch = document()->characterAt(tc.position());
    }
    return tc;
}

void ConfigurationEditor::insertCompleterText(const QString &text)
{
   QTextCursor tc = textCursor();
   // Replace entire word to get case sensitivity right.
   tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
                   m_completer->completionPrefix().length());
   tc.insertText(text);
   setTextCursor(tc);
}

void ConfigurationEditor::updateDocumentation()
{
    QTC_CHECK(m_settings);
    QTextCursor cursor = textCursor();

    QString word = cursorForTextUnderCursor(cursor).selectedText();
    if (word == m_lastDocumentation)
        return;

    QString doc = m_settings->documentation(word);
    if (!doc.isEmpty()) {
        m_lastDocumentation = word;
        emit documentationChanged(word, doc);
        return;
    }

    // If the documentation was empty, then try to use the line's first word or the first word
    // in front of a colon for providing a documentation.
    cursor.movePosition(QTextCursor::PreviousWord);
    cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
    const int pos = cursor.selectedText().lastIndexOf(QLatin1Char(','));
    if (-1 != pos) {
        cursor.setPosition(cursor.position() + pos);
        cursor.movePosition(QTextCursor::NextWord);
    }
    word = cursorForTextUnderCursor(cursor).selectedText();

    if (word == m_lastDocumentation)
        return;

    doc = m_settings->documentation(word);
    if (doc.isEmpty())
        return;

    m_lastDocumentation = word;
    emit documentationChanged(word, doc);
}

} // namespace Internal
} // namespace Beautifier
<commit_msg>Beautifier: Fix possible endless loop<commit_after>/**************************************************************************
**
** Copyright (c) 2014 Lorenz Haas
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "configurationeditor.h"

#include "abstractsettings.h"

#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <utils/qtcassert.h>

#include <QAbstractItemView>
#include <QCompleter>
#include <QScrollBar>
#include <QStringListModel>

namespace Beautifier {
namespace Internal {

ConfigurationSyntaxHighlighter::ConfigurationSyntaxHighlighter(QTextDocument *parent) :
    QSyntaxHighlighter(parent)
{
    const TextEditor::FontSettings fs = TextEditor::TextEditorSettings::instance()->fontSettings();
    m_formatKeyword = fs.toTextCharFormat(TextEditor::C_FIELD);
    m_formatComment = fs.toTextCharFormat(TextEditor::C_COMMENT);

    m_expressionComment.setPattern(QLatin1String("#[^\\n]*"));
    m_expressionComment.setMinimal(false);
}

void ConfigurationSyntaxHighlighter::setKeywords(const QStringList &keywords)
{
    if (keywords.isEmpty())
        return;

    // Check for empty keywords since they can cause an endless loop in highlightBlock().
    QStringList pattern;
    foreach (const QString &word, keywords) {
        if (!word.isEmpty())
            pattern << QRegExp::escape(word);
    }

    m_expressionKeyword.setPattern(QLatin1String("(?:\\s|^)(") + pattern.join(QLatin1String("|"))
                                   + QLatin1String(")(?=\\s|\\:|\\=|\\,|$)"));
}

void ConfigurationSyntaxHighlighter::setCommentExpression(const QRegExp &rx)
{
    m_expressionComment = rx;
}

void ConfigurationSyntaxHighlighter::highlightBlock(const QString &text)
{
    int pos = 0;
    if (!m_expressionKeyword.isEmpty()) {
        while ((pos = m_expressionKeyword.indexIn(text, pos)) != -1) {
            const int length = m_expressionKeyword.matchedLength();
            setFormat(pos, length, m_formatKeyword);
            pos += length;
        }
    }

    if (!m_expressionComment.isEmpty()) {
        pos = 0;
        while ((pos = m_expressionComment.indexIn(text, pos)) != -1) {
            const int length = m_expressionComment.matchedLength();
            setFormat(pos, length, m_formatComment);
            pos += length;
        }
    }
}

ConfigurationEditor::ConfigurationEditor(QWidget *parent)
    : QPlainTextEdit(parent)
    , m_settings(0)
    , m_completer(new QCompleter(this))
    , m_model(new QStringListModel(QStringList(), m_completer))
    , m_highlighter(new ConfigurationSyntaxHighlighter(document()))
{
    m_completer->setModel(m_model);
    m_completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
    m_completer->setWrapAround(false);
    m_completer->setWidget(this);
    m_completer->setCompletionMode(QCompleter::PopupCompletion);
    m_completer->setCaseSensitivity(Qt::CaseInsensitive);
    m_completer->popup()->installEventFilter(this);

    connect(m_completer, SIGNAL(activated(QString)), this, SLOT(insertCompleterText(QString)));
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateDocumentation()));
}

void ConfigurationEditor::setSettings(AbstractSettings *settings)
{
    QTC_CHECK(settings);
    m_settings = settings;

    QStringList keywords = m_settings->options();
    m_highlighter->setKeywords(keywords);
    keywords << m_settings->completerWords();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    keywords.sort(Qt::CaseInsensitive);
#else //QT_VERSION_CHECK(5, 0, 0)
    keywords.sort();
#endif //QT_VERSION_CHECK(5, 0, 0)
    m_model->setStringList(keywords);
}

void ConfigurationEditor::setCommentExpression(const QRegExp &rx)
{
    m_highlighter->setCommentExpression(rx);
}

// Workaround for handling "ESC" right when popup is shown.
bool ConfigurationEditor::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::ShortcutOverride) {
        QKeyEvent *key = static_cast<QKeyEvent *>(event);
        if (key->key() == Qt::Key_Escape) {
            event->accept();
            m_completer->popup()->hide();
            return true;
        }
    }
    return QObject::eventFilter(object, event);
}

void ConfigurationEditor::keyPressEvent(QKeyEvent *event)
{
    const int key = event->key();

    if (key == Qt::Key_Escape) {
        event->ignore();
        return;
    }

    if (m_completer->popup()->isVisible()) {
        switch (key) {
        case Qt::Key_Backtab:
        case Qt::Key_Enter:
        case Qt::Key_Return:
        case Qt::Key_Tab:
            event->ignore();
            return;
        default:
            break;
        }
    }

    const bool isShortcut = ((event->modifiers() & Qt::ControlModifier) && key == Qt::Key_Space);
    if (!isShortcut)
        QPlainTextEdit::keyPressEvent(event);

    const int cursorPosition = textCursor().position();
    QTextCursor cursor = cursorForTextUnderCursor();
    const QString prefix = cursor.selectedText();

    if (!isShortcut && (prefix.length() < 2 || cursorPosition != cursor.position())) {
        m_completer->popup()->hide();
        return;
    }

    if (prefix != m_completer->completionPrefix()) {
        m_completer->setCompletionPrefix(prefix);
        m_completer->popup()->setCurrentIndex(m_completer->completionModel()->index(0, 0));
    }

    if (m_completer->completionCount() == 1 && prefix == m_completer->currentCompletion()) {
        m_completer->popup()->hide();
        return;
    }

    QRect popupRect = cursorRect();
    popupRect.setWidth(m_completer->popup()->sizeHintForColumn(0)
                       + m_completer->popup()->verticalScrollBar()->sizeHint().width());
    m_completer->complete(popupRect);
}

// Hack because '-' and '=' are treated as End/StartOfWord
QTextCursor ConfigurationEditor::cursorForTextUnderCursor(QTextCursor tc) const
{
    if (tc.isNull())
        tc = textCursor();

    tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
    QChar ch = document()->characterAt(tc.position() - 1);
    while (!(ch.isNull() || ch.isSpace() || ch == QLatin1Char(':') || ch == QLatin1Char(','))) {
        tc.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
        ch = document()->characterAt(tc.position() - 1);
    }
    tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
    ch = document()->characterAt(tc.position());
    while (!(ch.isNull() || ch.isSpace() || ch == QLatin1Char(':') || ch == QLatin1Char(','))) {
        tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
        ch = document()->characterAt(tc.position());
    }
    return tc;
}

void ConfigurationEditor::insertCompleterText(const QString &text)
{
   QTextCursor tc = textCursor();
   // Replace entire word to get case sensitivity right.
   tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
                   m_completer->completionPrefix().length());
   tc.insertText(text);
   setTextCursor(tc);
}

void ConfigurationEditor::updateDocumentation()
{
    QTC_CHECK(m_settings);
    QTextCursor cursor = textCursor();

    QString word = cursorForTextUnderCursor(cursor).selectedText();
    if (word == m_lastDocumentation)
        return;

    QString doc = m_settings->documentation(word);
    if (!doc.isEmpty()) {
        m_lastDocumentation = word;
        emit documentationChanged(word, doc);
        return;
    }

    // If the documentation was empty, then try to use the line's first word or the first word
    // in front of a colon for providing a documentation.
    cursor.movePosition(QTextCursor::PreviousWord);
    cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
    const int pos = cursor.selectedText().lastIndexOf(QLatin1Char(','));
    if (-1 != pos) {
        cursor.setPosition(cursor.position() + pos);
        cursor.movePosition(QTextCursor::NextWord);
    }
    word = cursorForTextUnderCursor(cursor).selectedText();

    if (word == m_lastDocumentation)
        return;

    doc = m_settings->documentation(word);
    if (doc.isEmpty())
        return;

    m_lastDocumentation = word;
    emit documentationChanged(word, doc);
}

} // namespace Internal
} // namespace Beautifier
<|endoftext|>


<commit_before>#include "pd_view.h"
#include "pd_backend.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Line {
    uint64_t address;
    const char* text;
    bool breakpoint;
    uint8_t addressSize;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const int BlockSize = 64;

struct Block {
    uint64_t id;
    uint64_t address;
    uint64_t addressEnd;
    std::vector<Line> lines;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct DissassemblyData {
    std::vector<Block*> blocks;
    uint64_t location;
    uint64_t pc;
    uint8_t locationSize;
    bool requestDisassembly;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void* createInstance(PDUI* uiFuncs, ServiceFunc* serviceFunc) {
    (void)serviceFunc;
    DissassemblyData* user_data = new DissassemblyData;
    user_data->location = 0;
    user_data->pc = 0;
    user_data->locationSize = 0;
    user_data->requestDisassembly = false;

    (void)uiFuncs;
    (void)serviceFunc;

    return user_data;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void destroyInstance(void* user_data) {
    delete (DissassemblyData*)user_data;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Block* findBlock(DissassemblyData* data, uint64_t blockId) {
    for (Block* b : data->blocks) {
        if (b->id == blockId)
            return b;
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Block* createBlock(DissassemblyData* data, uint64_t address, uint64_t blockId) {
    Block* block = new Block;

    (void)address;

    block->id = blockId;
    block->address = blockId * BlockSize;
    block->addressEnd = block->address + (uint64_t)BlockSize;

    data->blocks.push_back(block);

    return block;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void insertLineBlock(Block* block, uint64_t address, const char* text) {
    // So this isn't very smart but will do for now

    for (auto i = block->lines.begin(); i != block->lines.end(); ++i) {
        Line& line = *i;

        if (address < line.address) {
            Line newLine = { 0 };
            newLine.address = address;
            newLine.text = strdup(text);
            block->lines.insert(i, newLine);
            return;
        }

        // found matching address, update the disassembly

        if (line.address == address) {
            free((void*)line.text);
            line.text = (const char*)strdup(text);
            return;
        }

        // TODO: Handle the case if a new line is inbetween lines, meaning the code has been modified
        // so the disasssembly is out of data
    }

    // not find, insert the line
    Line newLine = { 0 };
    newLine.address = address;
    newLine.text = strdup(text);
    block->lines.push_back(newLine);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void insertLine(DissassemblyData* data, uint64_t address, const char* text) {
    Block* block = 0;

    // first find the block which this address should be in

    uint64_t blockId = address / BlockSize;

    if (!(block = findBlock(data, blockId)))
        block = createBlock(data, address, blockId);

    insertLineBlock(block, address, text);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void setDisassemblyCode(DissassemblyData* data, PDReader* reader) {
    PDReaderIterator it;

    if (PDRead_find_array(reader, &it, "disassembly", 0) == PDReadStatus_NotFound)
        return;

    while (PDRead_get_next_entry(reader, &it)) {
        uint64_t address;
        const char* text;

        PDRead_find_u64(reader, &address, "address", it);
        PDRead_find_string(reader, &text, "line", it);

        insertLine(data, address, text);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static Block* findBlockWithPC(DissassemblyData* data, uint64_t pc) {
    for (Block* b : data->blocks) {
        if (pc >= b->address && pc <= b->addressEnd)
            return b;
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void renderUI(DissassemblyData* data, PDUI* uiFuncs) {
    Block* block = 0;

    uiFuncs->text("");  // TODO: Temporary

    if (!(block = findBlockWithPC(data, data->pc)))
        return;

    PDVec2 size = uiFuncs->get_window_size();

    for (Line& line : block->lines) {
        if (line.address == data->pc) {
            PDRect rect;
            PDVec2 pos = uiFuncs->get_cursor_pos();
            rect.x = pos.x;
            rect.y = pos.y;
            rect.width = size.x;
            rect.height = 14;
            uiFuncs->fill_rect(rect, PDUI_COLOR(200, 0, 0, 127));
            uiFuncs->set_scroll_here(0.5f);
        }

        uiFuncs->text("0x%04x %s", (uint64_t)line.address, line.text);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void updateRegisters(DissassemblyData* data, PDReader* reader) {
    PDReaderIterator it;

    if (PDRead_find_array(reader, &it, "registers", 0) == PDReadStatus_NotFound)
        return;

    while (PDRead_get_next_entry(reader, &it)) {
        const char* name = "";

        PDRead_find_string(reader, &name, "name", it);

        if (!strcmp(name, "pc")) {
            PDRead_find_u64(reader, &data->pc, "register", it);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int update(void* user_data, PDUI* uiFuncs, PDReader* inEvents, PDWriter* writer) {
    uint32_t event;

    DissassemblyData* data = (DissassemblyData*)user_data;

    data->requestDisassembly = false;

    while ((event = PDRead_get_event(inEvents)) != 0) {
        switch (event) {
            case PDEventType_SetDisassembly:
            {
                setDisassemblyCode(data, inEvents);
                break;
            }

            case PDEventType_SetExceptionLocation:
            {
                uint64_t location = 0;

                PDRead_find_u64(inEvents, &location, "address", 0);

                if (location != data->location) {
                    data->location = location;
                    data->requestDisassembly = true;
                }

                PDRead_find_u8(inEvents, &data->locationSize, "address_size", 0);
                break;
            }

            case PDEventType_SetRegisters:
            {
                updateRegisters(data, inEvents);
                break;
            }

        }
    }

    renderUI(data, uiFuncs);

    if (data->requestDisassembly) {
        int pc = (int)(data->pc) & ~(BlockSize - 1);
        PDWrite_event_begin(writer, PDEventType_GetDisassembly);
        PDWrite_u64(writer, "address_start", (uint64_t)pc);
        PDWrite_u32(writer, "instruction_count", (uint32_t)BlockSize / 3);
        PDWrite_event_end(writer);
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static PDViewPlugin plugin =
{
    "Disassembly",
    createInstance,
    destroyInstance,
    update,
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

extern "C"
{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

PD_EXPORT void InitPlugin(RegisterPlugin* registerPlugin, void* private_data) {
	registerPlugin(PD_VIEW_API_VERSION, &plugin, private_data);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}

<commit_msg>Use default 4 bytes for addreses<commit_after>#include "pd_view.h"
#include "pd_backend.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Line {
    uint64_t address;
    const char* text;
    bool breakpoint;
    uint8_t addressSize;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const int BlockSize = 64;

struct Block {
    uint64_t id;
    uint64_t address;
    uint64_t addressEnd;
    std::vector<Line> lines;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct DissassemblyData {
    std::vector<Block*> blocks;
    uint64_t location;
    uint64_t pc;
    uint8_t locationSize;
    bool requestDisassembly;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void* createInstance(PDUI* uiFuncs, ServiceFunc* serviceFunc) {
    (void)serviceFunc;
    DissassemblyData* user_data = new DissassemblyData;
    user_data->location = 0;
    user_data->pc = 0;
    user_data->locationSize = 0;
    user_data->requestDisassembly = false;

    (void)uiFuncs;
    (void)serviceFunc;

    return user_data;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void destroyInstance(void* user_data) {
    delete (DissassemblyData*)user_data;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Block* findBlock(DissassemblyData* data, uint64_t blockId) {
    for (Block* b : data->blocks) {
        if (b->id == blockId)
            return b;
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Block* createBlock(DissassemblyData* data, uint64_t address, uint64_t blockId) {
    Block* block = new Block;

    (void)address;

    block->id = blockId;
    block->address = blockId * BlockSize;
    block->addressEnd = block->address + (uint64_t)BlockSize;

    data->blocks.push_back(block);

    return block;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void insertLineBlock(Block* block, uint64_t address, const char* text) {
    // So this isn't very smart but will do for now

    for (auto i = block->lines.begin(); i != block->lines.end(); ++i) {
        Line& line = *i;

        if (address < line.address) {
            Line newLine = { 0 };
            newLine.address = address;
            newLine.text = strdup(text);
            block->lines.insert(i, newLine);
            return;
        }

        // found matching address, update the disassembly

        if (line.address == address) {
            free((void*)line.text);
            line.text = (const char*)strdup(text);
            return;
        }

        // TODO: Handle the case if a new line is inbetween lines, meaning the code has been modified
        // so the disasssembly is out of data
    }

    // not find, insert the line
    Line newLine = { 0 };
    newLine.address = address;
    newLine.text = strdup(text);
    block->lines.push_back(newLine);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void insertLine(DissassemblyData* data, uint64_t address, const char* text) {
    Block* block = 0;

    // first find the block which this address should be in

    uint64_t blockId = address / BlockSize;

    if (!(block = findBlock(data, blockId)))
        block = createBlock(data, address, blockId);

    insertLineBlock(block, address, text);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void setDisassemblyCode(DissassemblyData* data, PDReader* reader) {
    PDReaderIterator it;

    if (PDRead_find_array(reader, &it, "disassembly", 0) == PDReadStatus_NotFound)
        return;

    while (PDRead_get_next_entry(reader, &it)) {
        uint64_t address;
        const char* text;

        PDRead_find_u64(reader, &address, "address", it);
        PDRead_find_string(reader, &text, "line", it);

        insertLine(data, address, text);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static Block* findBlockWithPC(DissassemblyData* data, uint64_t pc) {
    for (Block* b : data->blocks) {
        if (pc >= b->address && pc <= b->addressEnd)
            return b;
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void renderUI(DissassemblyData* data, PDUI* uiFuncs) {
    Block* block = 0;

    uiFuncs->text("");  // TODO: Temporary

    if (!(block = findBlockWithPC(data, data->pc)))
        return;

    PDVec2 size = uiFuncs->get_window_size();

    for (Line& line : block->lines) {
        if (line.address == data->pc) {
            PDRect rect;
            PDVec2 pos = uiFuncs->get_cursor_pos();
            rect.x = pos.x;
            rect.y = pos.y;
            rect.width = size.x;
            rect.height = 14;
            uiFuncs->fill_rect(rect, PDUI_COLOR(200, 0, 0, 127));
            uiFuncs->set_scroll_here(0.5f);
        }

        uiFuncs->text("0x%08x %s", (uint64_t)line.address, line.text);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void updateRegisters(DissassemblyData* data, PDReader* reader) {
    PDReaderIterator it;

    if (PDRead_find_array(reader, &it, "registers", 0) == PDReadStatus_NotFound)
        return;

    while (PDRead_get_next_entry(reader, &it)) {
        const char* name = "";

        PDRead_find_string(reader, &name, "name", it);

        if (!strcmp(name, "pc")) {
            PDRead_find_u64(reader, &data->pc, "register", it);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int update(void* user_data, PDUI* uiFuncs, PDReader* inEvents, PDWriter* writer) {
    uint32_t event;

    DissassemblyData* data = (DissassemblyData*)user_data;

    data->requestDisassembly = false;

    while ((event = PDRead_get_event(inEvents)) != 0) {
        switch (event) {
            case PDEventType_SetDisassembly:
            {
                setDisassemblyCode(data, inEvents);
                break;
            }

            case PDEventType_SetExceptionLocation:
            {
                uint64_t location = 0;

                PDRead_find_u64(inEvents, &location, "address", 0);

                if (location != data->location) {
                    data->location = location;
                    data->requestDisassembly = true;
                }

                PDRead_find_u8(inEvents, &data->locationSize, "address_size", 0);
                break;
            }

            case PDEventType_SetRegisters:
            {
                updateRegisters(data, inEvents);
                break;
            }

        }
    }

    renderUI(data, uiFuncs);

    if (data->requestDisassembly) {
        int pc = (int)(data->pc) & ~(BlockSize - 1);
        PDWrite_event_begin(writer, PDEventType_GetDisassembly);
        PDWrite_u64(writer, "address_start", (uint64_t)pc);
        PDWrite_u32(writer, "instruction_count", (uint32_t)BlockSize / 3);
        PDWrite_event_end(writer);
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static PDViewPlugin plugin =
{
    "Disassembly",
    createInstance,
    destroyInstance,
    update,
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

extern "C"
{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

PD_EXPORT void InitPlugin(RegisterPlugin* registerPlugin, void* private_data) {
	registerPlugin(PD_VIEW_API_VERSION, &plugin, private_data);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}

<|endoftext|>