Andreas Neustifter wrote:
On 03.07.2007, at 08:20, Abdelrazak Younes wrote:
Bennett Helm wrote:
OK -- I did this. I still don't see any speedup with captions or
equations, but then I didn't have a problem on my Intel Mac
previously. Even watching the CPU usage of LyX as I drummed my
fingers on the keyboard as fast as I could revealed no difference
with your branch.
By the way, could you try with the attached file? If you could compile
a Mac/PPC LyX and ask some user to test that would be really great.
Abdel.
<LyXMathDWoes.lyx>
I have a PPC Mac and I'm compiling the latest SVN-Trunk (is that the
right version?)
No. This is in my mvc branch, three possibilities:
1) svn://svn.lyx.org/lyx/lyx-devel/branches/personal/younes/mvc
2) Or you can as well copy the attached two C++ files in the
frontend/qt4 directory.
3) Or you can use the attached patch.
and I will test it later on when the build has finished.
Good, thanks.
Abdel.
Index: frontends/qt4/GuiApplication.cpp
===================================================================
--- frontends/qt4/GuiApplication.cpp (revision 18971)
+++ frontends/qt4/GuiApplication.cpp (working copy)
@@ -41,6 +41,7 @@
#include <QFileOpenEvent>
#include <QLocale>
#include <QLibraryInfo>
+#include <QPixmapCache>
#include <QTextCodec>
#include <QTimer>
#include <QTranslator>
@@ -137,6 +138,10 @@
LoaderQueue::setPriority(10,100);
guiApp = this;
+
+ // Set the cache to 5120 kilobytes which corresponds to screen size of
+ // 1280 by 1024 pixels with a color depth of 32 bits.
+ QPixmapCache::setCacheLimit(5120);
}
Index: frontends/qt4/QLPainter.cpp
===================================================================
--- frontends/qt4/QLPainter.cpp (revision 18971)
+++ frontends/qt4/QLPainter.cpp (working copy)
@@ -11,8 +11,6 @@
#include <config.h>
-#include <QTextLayout>
-
#include "QLPainter.h"
#include "GuiApplication.h"
@@ -28,12 +26,29 @@
#include "support/unicode.h"
+#include <QPixmapCache>
+#include <QTextLayout>
+
using std::endl;
using std::string;
namespace lyx {
namespace frontend {
+namespace {
+
+QString generateStringSignature(QString const & str, Font const & f)
+{
+ QString sig = str;
+ sig.append(QChar(static_cast<short>(f.family())));
+ sig.append(QChar(static_cast<short>(f.series())));
+ sig.append(QChar(static_cast<short>(f.realShape())));
+ sig.append(QChar(static_cast<short>(f.size())));
+ return sig;
+}
+
+} // anon namespace
+
QLPainter::QLPainter(QPaintDevice * device)
: QPainter(device), Painter()
{
@@ -244,14 +259,12 @@
int textwidth;
if (f.realShape() != Font::SMALLCAPS_SHAPE) {
- setQPainterPen(f.realColor());
- if (font() != fi.font)
- setFont(fi.font);
- // We need to draw the text as LTR as we use our own bidi code.
- setLayoutDirection(Qt::LeftToRight);
+ // Here we use the font width cache instead of
+ // textwidth = fontMetrics().width(str);
+ // because the above is awfully expensive on MacOSX
+ textwidth = fi.metrics->width(s);
+
if (isDrawingEnabled()) {
- LYXERR(Debug::PAINTING) << "draw " <<
std::string(str.toUtf8())
- << " at " << x << "," << y << std::endl;
// Qt4 does not display a glyph whose codepoint is the
// same as that of a soft-hyphen (0x00ad), unless it
// occurs at a line-break. As a kludge, we force Qt to
@@ -265,13 +278,43 @@
line.setPosition(QPointF(0, -line.ascent()));
adsymbol.endLayout();
line.draw(this, QPointF(x, y));
- } else
- drawText(x, y, str);
+ } else {
+ QPixmap pm;
+ QString key = generateStringSignature(str, f);
+ // Warning: Left bearing is in general
negative! Only the case
+ // where left bearing is negative is of
interest WRT the the
+ // pixmap width and the text x-position.
+ // Only the left bearing of the first character
is important as we
+ // always write from left to right, even for
right-to-left languages.
+ int const lb =
std::min(fi.metrics->lbearing(s[0]), 0);
+ int const mA = fi.metrics->maxAscent();
+ if (!QPixmapCache::find(key, pm)) {
+ // Only the right bearing of the last
character is important as we
+ // always write from left to right,
even for right-to-left languages.
+ int const rb =
fi.metrics->rbearing(s[s.size()-1]);
+ int const w = textwidth + rb - lb;
+ int const mD = fi.metrics->maxDescent();
+ int const h = mA + mD;
+ pm = QPixmap(w, h);
+ pm.fill(Qt::transparent);
+ QLPainter p(&pm);
+ p.setQPainterPen(f.realColor());
+ if (p.font() != fi.font)
+ p.setFont(fi.font);
+ // We need to draw the text as LTR as
we use our own bidi code.
+ p.setLayoutDirection(Qt::LeftToRight);
+ p.drawText(-lb, mA, str);
+ QPixmapCache::insert(key, pm);
+
+ LYXERR(Debug::PAINTING) << "draw " <<
std::string(str.toUtf8())
+ << " at " << x << "," << y <<
std::endl;
+ LYXERR(Debug::PAINTING) << "h=" << h <<
" mA=" << mA << " mD=" << mD
+ << " w=" << w << " lb=" << lb
<< " tw=" << textwidth
+ << " rb=" << rb << endl;
+ }
+ drawPixmap(x + lb, y - mA, pm);
+ }
}
- // Here we use the font width cache instead of
- // textwidth = fontMetrics().width(str);
- // because the above is awfully expensive on MacOSX
- textwidth = fi.metrics->width(str);
} else {
textwidth = smallCapsText(x, y, str, f);
}
/**
* \file QLPainter.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author John Levon
* \author Abdelrazak Younes
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "QLPainter.h"
#include "GuiApplication.h"
#include "GuiFontMetrics.h"
#include "QLImage.h"
#include "GuiApplication.h"
#include "qt_helpers.h"
#include "debug.h"
#include "Language.h"
#include "Color.h"
#include "support/unicode.h"
#include <QPixmapCache>
#include <QTextLayout>
using std::endl;
using std::string;
namespace lyx {
namespace frontend {
namespace {
QString generateStringSignature(QString const & str, Font const & f)
{
QString sig = str;
sig.append(QChar(static_cast<short>(f.family())));
sig.append(QChar(static_cast<short>(f.series())));
sig.append(QChar(static_cast<short>(f.realShape())));
sig.append(QChar(static_cast<short>(f.size())));
return sig;
}
} // anon namespace
QLPainter::QLPainter(QPaintDevice * device)
: QPainter(device), Painter()
{
// new QPainter has default QPen:
current_color_ = Color::black;
current_ls_ = line_solid;
current_lw_ = line_thin;
}
QLPainter::~QLPainter()
{
QPainter::end();
//lyxerr << "QLPainter::end()" << endl;
}
void QLPainter::setQPainterPen(Color_color col,
Painter::line_style ls, Painter::line_width lw)
{
if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
return;
current_color_ = col;
current_ls_ = ls;
current_lw_ = lw;
QPen pen = QPainter::pen();
pen.setColor(guiApp->colorCache().get(col));
switch (ls) {
case line_solid: pen.setStyle(Qt::SolidLine); break;
case line_onoffdash: pen.setStyle(Qt::DotLine); break;
}
switch (lw) {
case line_thin: pen.setWidth(0); break;
case line_thick: pen.setWidth(3); break;
}
setPen(pen);
}
void QLPainter::point(int x, int y, Color_color col)
{
if (!isDrawingEnabled())
return;
setQPainterPen(col);
drawPoint(x, y);
}
void QLPainter::line(int x1, int y1, int x2, int y2,
Color_color col,
line_style ls,
line_width lw)
{
if (!isDrawingEnabled())
return;
setQPainterPen(col, ls, lw);
bool const do_antialiasing = renderHints() & TextAntialiasing
&& x1 != x2 && y1 != y2;
setRenderHint(Antialiasing, do_antialiasing);
drawLine(x1, y1, x2, y2);
setRenderHint(Antialiasing, false);
}
void QLPainter::lines(int const * xp, int const * yp, int np,
Color_color col,
line_style ls,
line_width lw)
{
if (!isDrawingEnabled())
return;
// double the size if needed
static QVector<QPoint> points(32);
if (np > points.size())
points.resize(2 * np);
bool antialias = false;
for (int i = 0; i < np; ++i) {
points[i].setX(xp[i]);
points[i].setY(yp[i]);
if (i != 0)
antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
}
setQPainterPen(col, ls, lw);
bool const text_is_antialiased = renderHints() & TextAntialiasing;
setRenderHint(Antialiasing, antialias && text_is_antialiased);
drawPolyline(points.data(), np);
setRenderHint(Antialiasing, false);
}
void QLPainter::rectangle(int x, int y, int w, int h,
Color_color col,
line_style ls,
line_width lw)
{
if (!isDrawingEnabled())
return;
setQPainterPen(col, ls, lw);
drawRect(x, y, w, h);
}
void QLPainter::fillRectangle(int x, int y, int w, int h, Color_color col)
{
fillRect(x, y, w, h, guiApp->colorCache().get(col));
}
void QLPainter::arc(int x, int y, unsigned int w, unsigned int h,
int a1, int a2, Color_color col)
{
if (!isDrawingEnabled())
return;
// LyX usings 1/64ths degree, Qt usings 1/16th
setQPainterPen(col);
bool const do_antialiasing = renderHints() & TextAntialiasing;
setRenderHint(Antialiasing, do_antialiasing);
drawArc(x, y, w, h, a1 / 4, a2 / 4);
setRenderHint(Antialiasing, false);
}
void QLPainter::image(int x, int y, int w, int h, graphics::Image const & i)
{
graphics::QLImage const & qlimage =
static_cast<graphics::QLImage const &>(i);
fillRectangle(x, y, w, h, Color::graphicsbg);
if (!isDrawingEnabled())
return;
drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
}
int QLPainter::text(int x, int y, char_type c, Font const & f)
{
docstring s(1, c);
return text(x, y, s, f);
}
int QLPainter::smallCapsText(int x, int y,
QString const & s, Font const & f)
{
Font smallfont(f);
smallfont.decSize().decSize().setShape(Font::UP_SHAPE);
QFont const & qfont = guiApp->guiFontLoader().get(f);
QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
setQPainterPen(f.realColor());
int textwidth = 0;
size_t const ls = s.length();
for (unsigned int i = 0; i < ls; ++i) {
QChar const c = s[i].toUpper();
if (c != s.at(i)) {
setFont(qsmallfont);
} else {
setFont(qfont);
}
if (isDrawingEnabled())
drawText(x + textwidth, y, c);
textwidth += fontMetrics().width(c);
}
return textwidth;
}
int QLPainter::text(int x, int y, docstring const & s,
Font const & f)
{
/* Caution: The following ucs4 to QString conversions work for symbol
fonts
only because they are no real conversions but simple casts in reality.
When we want to draw a symbol or calculate the metrics we pass the
position
of the symbol in the font (as given in lib/symbols) as a char_type to
the
frontend. This is just wrong, because the symbol is no UCS4 character at
all. You can think of this number as the code point of the symbol in a
custom symbol encoding. It works because this char_type is lateron again
interpreted as a position in the font again.
The correct solution would be to have extra functions for symbols, but
that
would require to duplicate a lot of frontend and mathed support code.
*/
QString str = toqstr(s);
#if 0
// HACK: QT3 refuses to show single compose characters
// Still needed with Qt4?
if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
str = ' ' + str;
#endif
QLFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
int textwidth;
if (f.realShape() != Font::SMALLCAPS_SHAPE) {
// Here we use the font width cache instead of
// textwidth = fontMetrics().width(str);
// because the above is awfully expensive on MacOSX
textwidth = fi.metrics->width(s);
if (isDrawingEnabled()) {
// Qt4 does not display a glyph whose codepoint is the
// same as that of a soft-hyphen (0x00ad), unless it
// occurs at a line-break. As a kludge, we force Qt to
// render this glyph using a one-column line.
if (s.size() == 1 && str[0].unicode() == 0x00ad) {
QTextLayout adsymbol(str);
adsymbol.setFont(fi.font);
adsymbol.beginLayout();
QTextLine line = adsymbol.createLine();
line.setNumColumns(1);
line.setPosition(QPointF(0, -line.ascent()));
adsymbol.endLayout();
line.draw(this, QPointF(x, y));
} else {
QPixmap pm;
QString key = generateStringSignature(str, f);
// Warning: Left bearing is in general
negative! Only the case
// where left bearing is negative is of
interest WRT the the
// pixmap width and the text x-position.
// Only the left bearing of the first character
is important as we
// always write from left to right, even for
right-to-left languages.
int const lb =
std::min(fi.metrics->lbearing(s[0]), 0);
int const mA = fi.metrics->maxAscent();
if (!QPixmapCache::find(key, pm)) {
// Only the right bearing of the last
character is important as we
// always write from left to right,
even for right-to-left languages.
int const rb =
fi.metrics->rbearing(s[s.size()-1]);
int const w = textwidth + rb - lb;
int const mD = fi.metrics->maxDescent();
int const h = mA + mD;
pm = QPixmap(w, h);
pm.fill(Qt::transparent);
QLPainter p(&pm);
p.setQPainterPen(f.realColor());
if (p.font() != fi.font)
p.setFont(fi.font);
// We need to draw the text as LTR as
we use our own bidi code.
p.setLayoutDirection(Qt::LeftToRight);
p.drawText(-lb, mA, str);
QPixmapCache::insert(key, pm);
LYXERR(Debug::PAINTING) << "draw " <<
std::string(str.toUtf8())
<< " at " << x << "," << y <<
std::endl;
LYXERR(Debug::PAINTING) << "h=" << h <<
" mA=" << mA << " mD=" << mD
<< " w=" << w << " lb=" << lb
<< " tw=" << textwidth
<< " rb=" << rb << endl;
}
drawPixmap(x + lb, y - mA, pm);
}
}
} else {
textwidth = smallCapsText(x, y, str, f);
}
if (f.underbar() == Font::ON) {
underline(f, x, y, textwidth);
}
return textwidth;
}
} // namespace frontend
} // namespace lyx
/**
* \file qt4/GuiApplication.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author unknown
* \author John Levon
* \author Abdelrazak Younes
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "GuiApplication.h"
#include "qt_helpers.h"
#include "QLImage.h"
#include "socket_callback.h"
#include "frontends/LyXView.h"
#include "graphics/LoaderQueue.h"
#include "support/FileName.h"
#include "support/lstrings.h"
#include "support/os.h"
#include "support/Package.h"
#include "BufferView.h"
#include "Color.h"
#include "debug.h"
#include "FuncRequest.h"
#include "LyX.h"
#include "LyXFunc.h"
#include "LyXRC.h"
#include <QApplication>
#include <QClipboard>
#include <QEventLoop>
#include <QFileOpenEvent>
#include <QLocale>
#include <QLibraryInfo>
#include <QPixmapCache>
#include <QTextCodec>
#include <QTimer>
#include <QTranslator>
#include <QWidget>
#ifdef Q_WS_X11
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#endif
#include <boost/bind.hpp>
#include <exception>
using std::string;
using std::endl;
///////////////////////////////////////////////////////////////
// You can find other X11 specific stuff
// at the end of this file...
///////////////////////////////////////////////////////////////
namespace {
int getDPI()
{
QWidget w;
return int(0.5 * (w.logicalDpiX() + w.logicalDpiY()));
}
} // namespace anon
namespace lyx {
using support::FileName;
frontend::Application * createApplication(int & argc, char * argv[])
{
return new frontend::GuiApplication(argc, argv);
}
namespace frontend {
GuiApplication * guiApp;
GuiApplication::~GuiApplication()
{
socket_callbacks_.clear();
}
GuiApplication::GuiApplication(int & argc, char ** argv)
: QApplication(argc, argv), Application(argc, argv)
{
// Qt bug? setQuitOnLastWindowClosed(true); does not work
setQuitOnLastWindowClosed(false);
#ifdef Q_WS_X11
// doubleClickInterval() is 400 ms on X11 which is just too long.
// On Windows and Mac OS X, the operating system's value is used.
// On Microsoft Windows, calling this function sets the double
// click interval for all applications. So we don't!
QApplication::setDoubleClickInterval(300);
#endif
// install translation file for Qt built-in dialogs
QString language_name = QString("qt_") + QLocale::system().name();
language_name.truncate(5);
if (qt_trans_.load(language_name,
QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
{
qApp->installTranslator(&qt_trans_);
// even if the language calls for RtL, don't do that
qApp->setLayoutDirection(Qt::LeftToRight);
LYXERR(Debug::GUI)
<< "Successfully installed Qt translations for locale "
<< fromqstr(language_name) << std::endl;
} else
LYXERR(Debug::GUI)
<< "Could not find Qt translations for locale "
<< fromqstr(language_name) << std::endl;
using namespace lyx::graphics;
Image::newImage = boost::bind(&QLImage::newImage);
Image::loadableFormats = boost::bind(&QLImage::loadableFormats);
// needs to be done before reading lyxrc
lyxrc.dpi = getDPI();
LoaderQueue::setPriority(10,100);
guiApp = this;
// Set the cache to 5120 kilobytes which corresponds to screen size of
// 1280 by 1024 pixels with a color depth of 32 bits.
QPixmapCache::setCacheLimit(5120);
}
Clipboard& GuiApplication::clipboard()
{
return clipboard_;
}
Selection& GuiApplication::selection()
{
return selection_;
}
int const GuiApplication::exec()
{
QTimer::singleShot(1, this, SLOT(execBatchCommands()));
return QApplication::exec();
}
void GuiApplication::exit(int status)
{
QApplication::exit(status);
}
void GuiApplication::execBatchCommands()
{
LyX::ref().execBatchCommands();
}
string const GuiApplication::romanFontName()
{
QFont font;
font.setKerning(false);
font.setStyleHint(QFont::Serif);
font.setFamily("serif");
return fromqstr(QFontInfo(font).family());
}
string const GuiApplication::sansFontName()
{
QFont font;
font.setKerning(false);
font.setStyleHint(QFont::SansSerif);
font.setFamily("sans");
return fromqstr(QFontInfo(font).family());
}
string const GuiApplication::typewriterFontName()
{
QFont font;
font.setKerning(false);
font.setStyleHint(QFont::TypeWriter);
font.setFamily("monospace");
return fromqstr(QFontInfo(font).family());
}
bool GuiApplication::event(QEvent * e)
{
switch(e->type()) {
case QEvent::FileOpen: {
// Open a file; this happens only on Mac OS X for now
QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
if (!currentView() || !currentView()->view())
// The application is not properly initialized yet.
// So we acknowledge the event and delay the file
opening
// until LyX is ready.
// FIXME UNICODE: FileName accept an utf8 encoded
string.
LyX::ref().addFileToLoad(FileName(fromqstr(foe->file())));
else
lyx::dispatch(FuncRequest(LFUN_FILE_OPEN,
qstring_to_ucs4(foe->file())));
e->accept();
return true;
}
default:
return QApplication::event(e);
}
}
bool GuiApplication::notify(QObject * receiver, QEvent * event)
{
bool return_value;
try {
return_value = QApplication::notify(receiver, event);
}
catch (std::exception const & e) {
lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
LyX::cref().emergencyCleanup();
abort();
}
catch (...) {
lyxerr << "Caught some really weird exception..." << endl;
LyX::cref().emergencyCleanup();
abort();
}
return return_value;
}
void GuiApplication::syncEvents()
{
// This is the ONLY place where processEvents may be called.
// During screen update/ redraw, this method is disabled to
// prevent keyboard events being handed to the LyX core, where
// they could cause re-entrant calls to screen update.
processEvents(QEventLoop::ExcludeUserInputEvents);
}
bool GuiApplication::getRgbColor(Color_color col,
RGBColor & rgbcol)
{
QColor const & qcol = color_cache_.get(col);
if (!qcol.isValid()) {
rgbcol.r = 0;
rgbcol.g = 0;
rgbcol.b = 0;
return false;
}
rgbcol.r = qcol.red();
rgbcol.g = qcol.green();
rgbcol.b = qcol.blue();
return true;
}
string const GuiApplication::hexName(Color_color col)
{
return lyx::support::ltrim(fromqstr(color_cache_.get(col).name()), "#");
}
void GuiApplication::updateColor(Color_color)
{
// FIXME: Bleh, can't we just clear them all at once ?
color_cache_.clear();
}
void GuiApplication::registerSocketCallback(int fd, boost::function<void()>
func)
{
socket_callbacks_[fd] =
boost::shared_ptr<socket_callback>(new socket_callback(fd,
func));
}
void GuiApplication::unregisterSocketCallback(int fd)
{
socket_callbacks_.erase(fd);
}
////////////////////////////////////////////////////////////////////////
// X11 specific stuff goes here...
#ifdef Q_WS_X11
bool GuiApplication::x11EventFilter(XEvent * xev)
{
if (!currentView())
return false;
switch (xev->type) {
case SelectionRequest: {
if (xev->xselectionrequest.selection != XA_PRIMARY)
break;
LYXERR(Debug::GUI) << "X requested selection." << endl;
BufferView * bv = currentView()->view();
if (bv) {
docstring const sel = bv->requestSelection();
if (!sel.empty())
selection_.put(sel);
}
break;
}
case SelectionClear: {
if (xev->xselectionclear.selection != XA_PRIMARY)
break;
LYXERR(Debug::GUI) << "Lost selection." << endl;
BufferView * bv = currentView()->view();
if (bv)
bv->clearSelection();
break;
}
}
return false;
}
#endif
} // namespace frontend
} // namespace lyx
#include "GuiApplication_moc.cpp"