Skip to content

Commit

Permalink
add "Copy as HTML" menu item
Browse files Browse the repository at this point in the history
  • Loading branch information
bhaller committed Aug 29, 2024
1 parent 7d42260 commit 149a6ad
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 2 deletions.
15 changes: 15 additions & 0 deletions QtSLiM/QtSLiMAppDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,12 @@ void QtSLiMAppDelegate::addActionsForGlobalMenuItems(QWidget *window)
window->addAction(actionExecuteAll);
}

{
QAction *actionCopyAsHTML = new QAction("Copy as HTML", this);
actionCopyAsHTML->setShortcut(flagsAndKey(Qt::ControlModifier | Qt::AltModifier, Qt::Key_C));
connect(actionCopyAsHTML, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_copyAsHTML);
window->addAction(actionCopyAsHTML);
}
{
QAction *actionShiftLeft = new QAction("Shift Left", this);
actionShiftLeft->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_BracketLeft));
Expand Down Expand Up @@ -1377,6 +1383,15 @@ void QtSLiMAppDelegate::dispatch_close(void)
qApp->beep();
}

void QtSLiMAppDelegate::dispatch_copyAsHTML(void)
{
QWidget *focusWidget = QApplication::focusWidget();
QtSLiMScriptTextEdit *scriptEdit = dynamic_cast<QtSLiMScriptTextEdit*>(focusWidget);

if (scriptEdit && scriptEdit->isEnabled())
scriptEdit->copyAsHTML();
}

void QtSLiMAppDelegate::dispatch_shiftLeft(void)
{
QWidget *focusWidget = QApplication::focusWidget();
Expand Down
1 change: 1 addition & 0 deletions QtSLiM/QtSLiMAppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public slots:
void dispatch_open(void);
void dispatch_close(void);

void dispatch_copyAsHTML(void);
void dispatch_shiftLeft(void);
void dispatch_shiftRight(void);
void dispatch_commentUncomment(void);
Expand Down
89 changes: 88 additions & 1 deletion QtSLiM/QtSLiMScriptTextEdit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include <QTextDocument>
#include <QMenu>
#include <QToolTip>
#include <QClipboard>
#include <QMimeData>
#include <QDebug>

#include <utility>
Expand Down Expand Up @@ -2625,9 +2627,23 @@ QStringList QtSLiMScriptTextEdit::linesForRoundedSelection(QTextCursor &p_cursor
#endif
}

void QtSLiMScriptTextEdit::copyAsHTML(void)
{
if (isEnabled())
{
QString html = exportAsHtml();
QClipboard *clipboard = QGuiApplication::clipboard();
QMimeData *mimeData = new QMimeData;

mimeData->setHtml(html);
mimeData->setText(html);
clipboard->setMimeData(mimeData);
}
}

void QtSLiMScriptTextEdit::shiftSelectionLeft(void)
{
if (isEnabled() && !isReadOnly())
if (isEnabled() && !isReadOnly())
{
QTextCursor &&edit_cursor = textCursor();
bool movedBack;
Expand Down Expand Up @@ -3303,6 +3319,77 @@ void QtSLiMScriptTextEdit::addScriptBlockColoring(int startPos, int endPos, Spec
blockSpecies.emplace_back(species);
}

// this method courtesy of SO user Larswad, https://stackoverflow.com/a/15808889/2752221
QString QtSLiMScriptTextEdit::exportAsHtml(void)
{
// Create a new document from the entire document
QTextCursor cursor(document());
cursor.select(QTextCursor::Document);
QTextDocument *tempDocument = new QTextDocument;
QTextCursor tempCursor(tempDocument);

tempCursor.insertFragment(cursor.selection());
tempCursor.select(QTextCursor::Document);

// Set the default foreground for the inserted characters
QTextCharFormat textfmt = tempCursor.charFormat();
textfmt.setForeground(Qt::black);
tempCursor.setCharFormat(textfmt);

// Apply the additional formats set by the syntax highlighter
QTextBlock start = document()->findBlock(cursor.selectionStart());
QTextBlock end = document()->findBlock(cursor.selectionEnd());
end = end.next();
const int selectionStart = cursor.selectionStart();
const int endOfDocument = tempDocument->characterCount() - 1;
for (QTextBlock current = start; current.isValid() and current not_eq end; current = current.next()) {
const QTextLayout* layout(current.layout());

foreach (const QTextLayout::FormatRange &range, layout->formats()) {
const int formatStart = current.position() + range.start - selectionStart;
const int formatEnd = formatStart + range.length;
if(formatEnd <= 0 or formatStart >= endOfDocument)
continue;
tempCursor.setPosition(qMax(formatStart, 0));
tempCursor.setPosition(qMin(formatEnd, endOfDocument), QTextCursor::KeepAnchor);
tempCursor.setCharFormat(range.format);
}
}

// Reset the user states since they are not interesting
for (QTextBlock block = tempDocument->begin(); block.isValid(); block = block.next())
block.setUserState(-1);

// Make sure the text appears pre-formatted, and set the background we want
tempCursor.select(QTextCursor::Document);
QTextBlockFormat blockFormat = tempCursor.blockFormat();
blockFormat.setNonBreakableLines(true);
//blockFormat.setBackground(Qt::black);
tempCursor.setBlockFormat(blockFormat);

// Select the same range with tempCursor as is selected in the original document
QTextCursor selectedRange = textCursor();
tempCursor.setPosition(selectedRange.anchor(), QTextCursor::MoveAnchor);
tempCursor.setPosition(selectedRange.position(), QTextCursor::KeepAnchor);

// Retrieve the syntax highlighted and formatted html
QString html = tempCursor.selection().toHtml();
delete tempDocument;

// There is a bug where the first line uses <p> instead of <pre>, because
// it is a fragment, I guess. We can fix that with a regex.
QRegularExpression pToPreRegex("<p style=(.*)</p>");
pToPreRegex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
html.replace(pToPreRegex, "<pre style=\\1</pre>");

// Change new paragraphs to new lines, so we get one paragraph of text
// This doesn't work, you always get a new paragraph for each line; <BR> doesn't work either.
//QRegularExpression preRegex("</pre>.?.?<pre style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">");
//preRegex.setPatternOptions(QRegularExpression::DotMatchesEverythingOption | QRegularExpression::CaseInsensitiveOption);
//html.replace(preRegex, "\n");

return html;
}



Expand Down
3 changes: 3 additions & 0 deletions QtSLiM/QtSLiMScriptTextEdit.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ class QtSLiMScriptTextEdit : public QtSLiMTextEdit
void clearScriptBlockColoring(void);
void addScriptBlockColoring(int startPos, int endPos, Species *species);

QString exportAsHtml(void);

public slots:
void copyAsHTML(void);
void shiftSelectionLeft(void);
void shiftSelectionRight(void);
void commentUncommentSelection(void);
Expand Down
4 changes: 3 additions & 1 deletion QtSLiM/QtSLiMWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3090,8 +3090,10 @@ void QtSLiMWindow::updateMenuEnablingSHARED(QWidget *p_focusWidget)

// actions handled by QtSLiMScriptTextEdit only
QtSLiMScriptTextEdit *scriptEdit = dynamic_cast<QtSLiMScriptTextEdit*>(p_focusWidget);
bool isModifiableScriptTextEdit = (scriptEdit && !scriptEdit->isReadOnly());
bool isScriptTextEdit = (!!scriptEdit);
bool isModifiableScriptTextEdit = (isScriptTextEdit && !scriptEdit->isReadOnly());

ui->actionCopyAsHTML->setEnabled(isScriptTextEdit);
ui->actionShiftLeft->setEnabled(isModifiableScriptTextEdit);
ui->actionShiftRight->setEnabled(isModifiableScriptTextEdit);
ui->actionCommentUncomment->setEnabled(isModifiableScriptTextEdit);
Expand Down
12 changes: 12 additions & 0 deletions QtSLiM/QtSLiMWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,7 @@ QSlider::handle:horizontal:disabled {
<addaction name="separator"/>
<addaction name="actionCut"/>
<addaction name="actionCopy"/>
<addaction name="actionCopyAsHTML"/>
<addaction name="actionPaste"/>
<addaction name="actionDelete"/>
<addaction name="actionSelectAll"/>
Expand Down Expand Up @@ -2036,6 +2037,17 @@ QSlider::handle:horizontal:disabled {
<string>Show nonWF Tick Cycle (Multispecies)</string>
</property>
</action>
<action name="actionCopyAsHTML">
<property name="text">
<string>Copy as HTML</string>
</property>
<property name="toolTip">
<string>Copy as HTML</string>
</property>
<property name="shortcut">
<string>Ctrl+Alt+C</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
Expand Down
1 change: 1 addition & 0 deletions QtSLiM/QtSLiMWindow_glue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ void QtSLiMWindow::glueUI(void)
connect(ui->actionAboutStickSoftware, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_helpStickSoftware);

// connect custom menu items
connect(ui->actionCopyAsHTML, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_copyAsHTML);
connect(ui->actionShiftLeft, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_shiftLeft);
connect(ui->actionShiftRight, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_shiftRight);
connect(ui->actionCommentUncomment, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_commentUncomment);
Expand Down
1 change: 1 addition & 0 deletions VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ development head (in the master branch):
add a "Use OpenGL for fast display" checkbox in the Preferences panel, making it possible to disable OpenGL when it doesn't work well (#462)
add genomicElementForPosition() and hasGenomicElementForPosition() methods on Chromosome
policy change: genomicElements property is now in sorted order (not in order of declaration)
add a "Copy as HTML" command to copy script with syntax coloring and such


version 4.2.2 (Eidos version 3.2.2):
Expand Down

0 comments on commit 149a6ad

Please sign in to comment.