You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeutils/kjots/kjotsedit.cpp

624 lines
18 KiB
C++

//
// kjots
//
// Copyright (C) 1997 Christoph Neerfeld
// Copyright (C) 2002, 2003 Aaron J. Seigo
// Copyright (C) 2003 Stanislav Kljuhhin
//
// 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.
//
// 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <tqpainter.h>
#include <tqpaintdevicemetrics.h>
#include <tqcursor.h>
#include <tdepopupmenu.h>
#include <kfind.h>
#include <kfinddialog.h>
#include <kreplace.h>
#include <kreplacedialog.h>
#include <kstringhandler.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kopenwith.h>
#include <kprinter.h>
#include "kjotsedit.h"
#include "kjotsentry.h"
//----------------------------------------------------------------------
// MYMULTIEDIT
//----------------------------------------------------------------------
KJotsEdit::KJotsEdit (TQWidget* parent, const char* name)
: KTextEdit(parent, name)
, m_entry(nullptr)
, m_find(nullptr)
, m_findDialog(nullptr)
, m_replace(nullptr)
, m_replaceDialog(nullptr)
{
// no rich text until printing and other such issues are worked out
setTextFormat(TQt::PlainText);
setWordWrap(TQTextEdit::WidgetWidth);
setLinkUnderline(true);
web_menu = new TDEPopupMenu(this);;
web_menu->insertItem(i18n("Open URL"), this, TQ_SLOT(openUrl()) );
}
KJotsEdit::~KJotsEdit()
{
delete m_find;
delete m_replace;
}
void KJotsEdit::mousePressEvent( TQMouseEvent *e )
{
if(e->button() == TQt::RightButton &&
hasSelectedText())
{
KURL url(selectedText());
if(url.isValid())
{
web_menu->popup(TQCursor::pos());
return;
}
}
KTextEdit::mousePressEvent(e);
}
void KJotsEdit::openUrl()
{
if (hasSelectedText())
{
KURL url(selectedText());
if(url.isValid())
{
new KRun(url);
}
}
}
void KJotsEdit::print(TQString title)
{
KPrinter printer;
printer.setDocName(title);
printer.setFullPage(false);
printer.setCreator("KJots");
if (printer.setup(this))
{
TQFont printFont = font();
TQPainter painter( &printer );
TQPaintDeviceMetrics metrics( &printer );
int y = 0;
int maxWidth = metrics.width();
int maxHeight = metrics.height();
TQString currentParagraph;
for (int paragraphCount = 0; paragraphCount < paragraphs(); ++paragraphCount )
{
currentParagraph = text(paragraphCount);
TQRect r = painter.boundingRect(0, y, maxWidth, maxHeight,
TQPainter::ExpandTabs | TQPainter::WordBreak,
currentParagraph);
if ((y + r.height()) > maxHeight)
{
printer.newPage();
y = 0;
}
painter.drawText(0, y, maxWidth, maxHeight - y,
TQPainter::ExpandTabs | TQPainter::WordBreak,
currentParagraph);
y += r.height();
}
painter.end();
}
}
void KJotsEdit::slotFindHighlight(const TQString& text, int matchIndex, int matchLength)
{
// tqDebug("KJotsEdit::slotFindHighlight");
Q_UNUSED(text);
const bool inSelection = m_find->options() & KFindDialog::SelectedText;
// Ensure we are offsetting the selection for the first line of the selection
// so we don't highlight the wrong text.
if (inSelection && (m_findCursor.paragraph == m_selectionStart.paragraph))
{
setSelection(m_findCursor.paragraph, m_selectionStart.paragraphIndex + matchIndex, m_findCursor.paragraph,
m_selectionStart.paragraphIndex + matchIndex + matchLength);
}
else
{
setSelection(m_findCursor.paragraph, matchIndex, m_findCursor.paragraph, matchIndex + matchLength);
}
}
void KJotsEdit::slotReplaceHighlight(const TQString& text, int matchIndex, int matchLength)
{
// tqDebug("KJotsEdit::slotReplaceHighlight");
Q_UNUSED(text);
setSelection(m_replaceCursor.paragraph, matchIndex, m_replaceCursor.paragraph, matchIndex + matchLength);
}
void KJotsEdit::replace()
{
// tqDebug("KJotsEdit::replace");
if (length() == 0)
{
return;
}
if (m_replaceDialog)
{
m_replaceDialog->setActiveWindow();
}
else
{
m_replaceDialog = new KReplaceDialog(this, "m_replaceDialog", KReplaceDialog::PromptOnReplace);
connect(m_replaceDialog, TQ_SIGNAL(okClicked()), this, TQ_SLOT(slotDoReplace()));
}
if (hasSelectedText())
{
m_replaceDialog->setHasSelection(true);
m_replaceDialog->setOptions(m_replaceDialog->options() | KReplaceDialog::SelectedText);
}
else
{
m_replaceDialog->setHasSelection(false);
m_replaceDialog->setOptions(m_replaceDialog->options() & ~KReplaceDialog::SelectedText);
}
m_replaceDialog->show();
}
void KJotsEdit::slotDoReplace()
{
// tqDebug("KJotsEdit::slotDoReplace");
/* Performing a new replacement, so we need to remove the previous
* KReplace, set up a new one and the 'replaceNext' functionality. */
delete m_replace;
m_replace = nullptr;
if (!m_replaceDialog)
{
// tqDebug("KJotsEdit::slotDoReplace: no replaceDialog");
return;
}
m_replace = new KReplace(m_replaceDialog->pattern(), m_replaceDialog->replacement(), m_replaceDialog->options());
if ((m_replace->options() & KReplaceDialog::SelectedText))
{
const bool backwards = m_replace->options() & KFindDialog::FindBackwards;
getSelection(&m_selectionStart.paragraph, &m_selectionStart.paragraphIndex, &m_selectionEnd.paragraph,
&m_selectionEnd.paragraphIndex);
m_replaceCursor.paragraph = backwards ? m_selectionEnd.paragraph : m_selectionStart.paragraph;
m_replaceCursor.paragraphIndex = backwards ? m_selectionEnd.paragraphIndex : m_selectionStart.paragraphIndex;
}
else
{
setupCursor(&m_replaceCursor, m_replace);
}
connect(m_replace, TQ_SIGNAL(highlight(const TQString&, int, int)), this,
TQ_SLOT(slotReplaceHighlight(const TQString&, int, int)));
connect(m_replace, TQ_SIGNAL(findNext()), this, TQ_SLOT(slotReplaceNext()));
connect(m_replace, TQ_SIGNAL(replace(const TQString&, int, int, int)), this,
TQ_SLOT(slotReplace(const TQString&, int, int, int)));
m_replaceDialog->close();
slotReplaceNext();
}
void KJotsEdit::slotReplace(const TQString& replaceText, int replacementIndex, int replacementLength, int matchedLength)
{
// tqDebug("KJotsEdit::slotReplace");
// Ensure the selection only matches the replacement.
// Prevents an issue where all the text is selected before replacing (since we remove the selection below).
setSelection(m_replaceCursor.paragraph, replacementIndex, m_replaceCursor.paragraph,
replacementIndex + matchedLength);
const auto replacement = replaceText.mid(replacementIndex, replacementLength);
removeSelectedText();
insertAt(replacement, m_replaceCursor.paragraph, replacementIndex);
setModified(true);
}
void KJotsEdit::slotReplaceNext()
{
// tqDebug("KJotsEdit::slotReplaceNext");
if (!m_replace)
{
return;
}
const bool backwards = (m_replace->options() & KReplaceDialog::FindBackwards);
const bool useSelection = (m_replace->options() & KReplaceDialog::SelectedText);
if (m_replace->needData())
{
if (useSelection && backwards)
{
m_replace->setData(text(m_selectionEnd.paragraph), m_selectionEnd.paragraphIndex);
}
else if (useSelection)
{
m_replace->setData(text(m_selectionStart.paragraph), m_selectionStart.paragraphIndex);
}
else
{
m_replace->setData(text(m_replaceCursor.paragraph), m_replaceCursor.paragraphIndex);
}
}
KReplace::Result res = KReplace::NoMatch;
do
{
res = m_replace->replace();
if (res == KReplace::Match)
{
return;
}
m_replaceCursor.paragraph += (backwards ? -1 : 1);
m_replaceCursor.paragraphIndex = (backwards ? paragraphLength(m_replaceCursor.paragraph) : 0);
m_replace->setData(text(m_replaceCursor.paragraph), m_replaceCursor.paragraphIndex);
if (useSelection && m_replaceCursor.paragraph > m_selectionEnd.paragraph)
{
break;
}
if (useSelection && backwards && m_replaceCursor.paragraph < m_selectionStart.paragraph)
{
break;
}
} while (backwards ? (m_replaceCursor.paragraph >= 0) : (m_replaceCursor.paragraph < paragraphs()));
Q_ASSERT(res != KReplace::Match);
if ((m_replace->options() & KReplaceDialog::FromCursor) && m_replace->shouldRestart(true))
{
// tqDebug("KJotsEdit::slotReplaceNext restarting");
m_replaceCursor.paragraph = backwards ? (paragraphs() - 1) : 0;
m_replaceCursor.paragraphIndex = backwards ? (paragraphLength(m_replaceCursor.paragraph)) : 0;
m_replace->setData(text(m_replaceCursor.paragraph), m_replaceCursor.paragraphIndex);
m_replace->resetCounts();
slotReplaceNext();
return;
}
m_replace->displayFinalDialog();
m_replace->disconnect(this, TQ_SLOT(slotReplaceHighlight(const TQString&, int, int)));
m_replace->deleteLater();
m_replace = nullptr;
}
void KJotsEdit::find()
{
// tqDebug("KJotsEdit::find");
if (length() == 0)
{
return;
}
if (m_findDialog)
{
m_findDialog->setActiveWindow();
}
else
{
m_findDialog = new KFindDialog(this, "m_findDialog");
connect(m_findDialog, TQ_SIGNAL(okClicked()), this, TQ_SLOT(slotDoFind()));
}
if (hasSelectedText())
{
m_findDialog->setHasSelection(true);
m_findDialog->setOptions(m_findDialog->options() | KFindDialog::SelectedText);
}
else
{
m_findDialog->setHasSelection(false);
m_findDialog->setOptions(m_findDialog->options() & ~KFindDialog::SelectedText);
}
m_findDialog->show();
}
void KJotsEdit::findNext()
{
// tqDebug("KJotsEdit::findNext");
if (!m_find)
{
find();
return;
}
const bool backwards = (m_find->options() & KFindDialog::FindBackwards);
const bool fromCursor = (m_find->options() & KFindDialog::FromCursor);
const bool inSelection = (m_find->options() & KFindDialog::SelectedText);
if (m_find->needData())
{
if (inSelection)
{
if (m_selectionStart.paragraph == m_selectionEnd.paragraph)
{
// Same line, ensure we only inlcude the selection.
auto selectionLength = m_selectionEnd.paragraphIndex - m_selectionStart.paragraphIndex;
auto data = text(m_findCursor.paragraph).mid(m_selectionStart.paragraphIndex, selectionLength);
m_find->setData(data);
}
else if (backwards)
{
m_findCursor.paragraph = m_selectionEnd.paragraph;
m_findCursor.paragraphIndex = -1;
m_find->setData(text(m_findCursor.paragraph).left(m_selectionEnd.paragraphIndex));
}
else
{
m_findCursor.paragraph = m_selectionStart.paragraph;
m_findCursor.paragraphIndex = 0;
auto offset = (paragraphLength(m_findCursor.paragraph)) - m_selectionStart.paragraphIndex+1;
m_find->setData(text(m_findCursor.paragraph).right(offset), m_findCursor.paragraphIndex);
}
}
else
{
m_find->setData(text(m_findCursor.paragraph), m_findCursor.paragraphIndex);
}
}
KFind::Result res = KFind::NoMatch;
do
{
res = m_find->find();
if (res == KFind::Match)
{
return;
}
m_findCursor.paragraph += (backwards ? -1 : 1);
m_findCursor.paragraphIndex = -1; // SOL or EOL depending on `backwards`.
if (m_findCursor.paragraph == m_selectionStart.paragraph)
{
auto offset = (paragraphLength(m_findCursor.paragraph)) - m_selectionStart.paragraphIndex+1;
m_find->setData(text(m_findCursor.paragraph).right(offset), m_findCursor.paragraphIndex);
}
else if (m_findCursor.paragraph == m_selectionEnd.paragraph)
{
m_find->setData(text(m_findCursor.paragraph).left(m_selectionEnd.paragraphIndex), m_findCursor.paragraphIndex);
}
else
{
m_findCursor.paragraphIndex = -1;
m_find->setData(text(m_findCursor.paragraph), m_findCursor.paragraphIndex);
}
if (inSelection && backwards && m_findCursor.paragraph < m_selectionStart.paragraph)
{
break;
}
if (inSelection && m_findCursor.paragraph > m_selectionEnd.paragraph)
{
break;
}
} while (backwards ? (m_findCursor.paragraph >= 0) : (m_findCursor.paragraph < paragraphs()));
Q_ASSERT(res != KFind::Match);
// If there were no matches, and we were checking from the start of the text,
// then we do not want to prompt to search again, since we already know there
// is nothing.
if (m_find->numMatches() == 0 && !fromCursor)
{
KMessageBox::sorry(this,
i18n("Search string '%1' was not found!").arg(KStringHandler::csqueeze(m_find->pattern())));
// Reset the cursor in case more text is added between calls to findNext()
m_findCursor = m_selectionStart;
m_find->setData(text(m_selectionStart.paragraph)
.mid(m_selectionStart.paragraphIndex,
m_selectionEnd.paragraphIndex - m_selectionStart.paragraphIndex));
return;
}
if (m_find->shouldRestart(/* forceAsking */ true, /* showNumMatches */ false))
{
if (inSelection)
{
if (m_selectionStart.paragraph == m_selectionEnd.paragraph)
{
m_findCursor.paragraph = m_selectionStart.paragraph;
m_findCursor.paragraphIndex = m_selectionStart.paragraphIndex;
auto selectionLength = m_selectionEnd.paragraphIndex - m_selectionStart.paragraphIndex;
auto data = text(m_findCursor.paragraph).mid(m_findCursor.paragraphIndex, selectionLength);
m_find->setData(data);
}
else if (backwards)
{
m_findCursor = m_selectionEnd;
m_find->setData(text(m_findCursor.paragraph).left(m_findCursor.paragraphIndex));
}
else
{
m_findCursor.paragraph = m_selectionStart.paragraph;
m_findCursor.paragraphIndex = -1;
auto offset = (paragraphLength(m_findCursor.paragraph)) - m_selectionStart.paragraphIndex+1;
m_find->setData(text(m_findCursor.paragraph).right(offset), m_findCursor.paragraphIndex);
}
}
else
{
m_findCursor.paragraph = backwards ? (paragraphs() - 1) : 0;
m_findCursor.paragraphIndex = backwards ? (paragraphLength(m_findCursor.paragraph)) : 0;
m_find->setData(text(m_findCursor.paragraph), m_findCursor.paragraphIndex);
}
m_find->resetCounts();
findNext();
}
}
void KJotsEdit::findPrev()
{
if (!m_find)
{
find();
return;
}
m_find->setOptions(m_find->options() ^ KFindDialog::FindBackwards);
findNext();
// Check as pressing 'stop' will delete m_find.
if (m_find)
{
m_find->setOptions(m_find->options() ^ KFindDialog::FindBackwards);
}
}
void KJotsEdit::slotDoFind()
{
// tqDebug("KJotsEdit::slotDoFind");
/* Performing a new search, ensure the previous search is invalidated. */
delete m_find;
m_find = nullptr;
if (!m_findDialog)
{
// tqDebug("KJotsEdit::slotDoFind: find dialog not set up");
return;
}
if (m_findDialog->pattern().isEmpty())
{
// tqDebug("KJotsEdit::slotDoFind: empty pattern.");
return;
}
// tqDebug("findDialog->pattern = %s", m_findDialog->pattern().local8Bit().data());
m_find = new KFind(m_findDialog->pattern(), m_findDialog->options(), this);
if (m_find->options() & KFindDialog::SelectedText)
{
const bool backwards = m_find->options() & KFindDialog::FindBackwards;
getSelection(&m_selectionStart.paragraph, &m_selectionStart.paragraphIndex, &m_selectionEnd.paragraph,
&m_selectionEnd.paragraphIndex);
m_findCursor.paragraph = backwards ? m_selectionEnd.paragraph : m_selectionStart.paragraph;
m_findCursor.paragraphIndex = backwards ? m_selectionEnd.paragraphIndex : m_selectionStart.paragraphIndex;
}
else
{
setupCursor(&m_findCursor, m_find);
// Reset selection so slotFindHighlight works correctly.
m_selectionStart = {0, 0};
m_selectionEnd = {0, 0};
}
connect(m_find, TQ_SIGNAL(highlight(const TQString&, int, int)), this,
TQ_SLOT(slotFindHighlight(const TQString&, int, int)));
connect(m_find, TQ_SIGNAL(findNext()), this, TQ_SLOT(findNext()));
m_findDialog->close();
m_find->closeFindNextDialog();
findNext();
}
void KJotsEdit::setEntry (KJotsPage *entry)
{
//tell the old entry to take a hike
if ( m_entry )
{
m_entry->setEditor(0);
}
//load up the new entry (assuming there is one)
if ( entry )
{
m_entry = entry;
setText(entry->body());
removeSelection();
repaint();
setEnabled(true);
setFocus();
entry->setEditor(this);
} else {
clear();
}
m_entry = entry;
// Reset the find & replace dialog for the new entry.
delete m_find;
delete m_replace;
m_find = nullptr;
m_replace = nullptr;
}
void KJotsEdit::setupCursor(KJotsEdit::CursorPosition* cursor, const KFind* find)
{
if (!cursor)
{
tqWarning("WARNING: Attempting to setup a NULL cursor: %s (%d)", __FILE__, __LINE__);
return;
}
cursor->paragraph = 0;
cursor->paragraphIndex = 0;
if (find->options() & KFindDialog::FromCursor)
{
getCursorPosition(&cursor->paragraph, &cursor->paragraphIndex);
}
else if (find->options() & KFindDialog::FindBackwards)
{
cursor->paragraph = paragraphs();
cursor->paragraphIndex = paragraphLength(cursor->paragraph);
}
}
#include "kjotsedit.moc"
/* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */