diff options
Diffstat (limited to 'src/tools.cpp')
-rw-r--r-- | src/tools.cpp | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/src/tools.cpp b/src/tools.cpp new file mode 100644 index 0000000..9acbda7 --- /dev/null +++ b/src/tools.cpp @@ -0,0 +1,446 @@ +/*************************************************************************** + * Copyright (C) 2003 by S�astien Laot * + * slaout@linux62.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. * + * * + * 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 <kdebug.h> +#include <qstring.h> +#include <qpixmap.h> +#include <qimage.h> +#include <qstylesheet.h> +#include <qregexp.h> +#include <qvaluestack.h> +#include <qfileinfo.h> +#include <qdir.h> +#include <qmime.h> +#include <qfont.h> +#include <qfontinfo.h> +#include <qobjectlist.h> + +#include "tools.h" + +QMemArray<QTime> StopWatch::starts; +QMemArray<double> StopWatch::totals; +QMemArray<uint> StopWatch::counts; + +void StopWatch::start(uint id) +{ + if (id >= starts.size()) { + totals.resize(id + 1); + counts.resize(id + 1); + for (uint i = starts.size(); i <= id; i++) { + totals[i] = 0; + counts[i] = 0; + } + starts.resize(id + 1); + } + starts[id] = QTime::currentTime(); +} + +void StopWatch::check(uint id) +{ + if (id >= starts.size()) + return; + double time = starts[id].msecsTo(QTime::currentTime()) / 1000.0; + totals[id] += time; + counts[id]++; + kdDebug() << k_funcinfo << "Timer_" << id << ": " << time << " s [" << counts[id] << " times, total: " << totals[id] << " s, average: " << totals[id] / counts[id] << " s]" << endl; +} + +QString Tools::textToHTML(const QString &text) +{ + if (text.isEmpty()) + return "<p></p>"; + if (/*text.isEmpty() ||*/ text == " " || text == " ") + return "<p> </p>"; + + // convertFromPlainText() replace "\n\n" by "</p>\n<p>": we don't want that + QString htmlString = QStyleSheet::convertFromPlainText(text, QStyleSheetItem::WhiteSpaceNormal); + return htmlString.replace("</p>\n", "<br>\n<br>\n").replace("\n<p>", "\n"); // Don't replace first and last tags +} + +QString Tools::textToHTMLWithoutP(const QString &text) +{ + // textToHTML(text) return "<p>HTMLizedText</p>". We remove the strating "<p>" and ending </p>" + QString HTMLizedText = textToHTML(text); + return HTMLizedText.mid(3, HTMLizedText.length() - 3 - 4); +} + +QString Tools::htmlToParagraph(const QString &html) +{ + QString result = html; + bool startedBySpan = false; + + // Remove the <html> start tag, all the <head> and the <body> start + // Because <body> can contain style="..." parameter, we transform it to <span> + int pos = result.find("<body"); + if (pos != -1) { + result = "<span" + result.mid(pos + 5); + startedBySpan = true; + } + + // Remove the ending "</p>\n</body></html>", each tag can be separated by space characters (%s) + // "</p>" can be omitted (eg. if the HTML doesn't contain paragraph but tables), as well as "</body>" (optinal) + pos = result.find(QRegExp("(?:(?:</p>[\\s\\n\\r\\t]*)*</body>[\\s\\n\\r\\t]*)*</html>", false)); // Case unsensitive + if (pos != -1) + result = result.left(pos); + + if (startedBySpan) + result += "</span>"; + + return result.replace("</p>", "<br><br>").replace("<p>", ""); +} + +// The following is adapted from KStringHanlder::tagURLs +// The adaptation lies in the change to urlEx +// Thanks to Richard Heck +QString Tools::tagURLs(const QString &text) +{ + QRegExp urlEx("(www\\.(?!\\.)|([a-zA-z]+)://)[\\d\\w\\./,:_~\\?=&;#@\\-\\+\\%\\$]+[\\d\\w/]"); + + QString richText(text); + int urlPos = 0; + int urlLen; + while ((urlPos = urlEx.search(richText, urlPos)) >= 0) { + urlLen = urlEx.matchedLength(); + QString href = richText.mid(urlPos, urlLen); + // Qt doesn't support (?<=pattern) so we do it here + if ((urlPos > 0) && richText[urlPos-1].isLetterOrNumber()) { + urlPos++; + continue; + } + QString anchor = "<a href=\"" + href + "\">" + href + "</a>"; + richText.replace(urlPos, urlLen, anchor); + urlPos += anchor.length(); + } + return richText; +} + +QString Tools::htmlToText(const QString &html) +{ + QString text = htmlToParagraph(html); + text.remove("\n"); + text.replace("</h1>", "\n"); + text.replace("</h2>", "\n"); + text.replace("</h3>", "\n"); + text.replace("</h4>", "\n"); + text.replace("</h5>", "\n"); + text.replace("</h6>", "\n"); + text.replace("</li>", "\n"); + text.replace("</dt>", "\n"); + text.replace("</dd>", "\n"); + text.replace("<dd>", " "); + text.replace("</div>","\n"); + text.replace("</blockquote>","\n"); + text.replace("</caption>","\n"); + text.replace("</tr>", "\n"); + text.replace("</th>", " "); + text.replace("</td>", " "); + text.replace("<br>", "\n"); + text.replace("<br />","\n"); + // FIXME: Format <table> tags better, if possible + // TODO: Replace é and co. by theire equivalent! + + // To manage tags: + int pos = 0; + int pos2; + QString tag, tag3; + // To manage lists: + int deep = 0; // The deep of the current line in imbriqued lists + QValueStack<bool> ul; // true if current list is a <ul> one, false if it's an <ol> one + QValueStack<int> lines; // The line number if it is an <ol> list + // We're removing every other tags, or replace them in the case of li: + while ( (pos = text.find("<"), pos) != -1 ) { + // What is the current tag? + tag = text.mid(pos + 1, 2); + tag3 = text.mid(pos + 1, 3); + // Lists work: + if (tag == "ul") { + deep++; + ul.push(true); + lines.push(-1); + } else if (tag == "ol") { + deep++; + ul.push(false); + lines.push(0); + } else if (tag3 == "/ul" || tag3 == "/ol") { + deep--; + ul.pop(); + lines.pop(); + } + // Where the tag closes? + pos2 = text.find(">"); + if (pos2 != -1) { + // Remove the tag: + text.remove(pos, pos2 - pos + 1); + // And replace li with "* ", "x. "... without forbidding to indent that: + if (tag == "li") { + // How many spaces before the line (indentation): + QString spaces = ""; + for (int i = 1; i < deep; i++) + spaces += " "; + // The bullet or number of the line: + QString bullet = "* "; + if (ul.top() == false) { + lines.push(lines.pop() + 1); + bullet = QString::number(lines.top()) + ". "; + } + // Insertion: + text.insert(pos, spaces + bullet); + } + if ( (tag3 == "/ul" || tag3 == "/ol") && deep == 0 ) + text.insert(pos, "\n"); // Empty line before and after a set of lists + } + ++pos; + } + + text.replace(">", ">"); + text.replace("<", "<"); + text.replace(""", "\""); + text.replace(" ", " "); + text.replace("&", "&"); // CONVERT IN LAST!! + + return text; +} + +QString Tools::cssFontDefinition(const QFont &font, bool onlyFontFamily) +{ + // The font definition: + QString definition = QString(font.italic() ? "italic " : "") + + QString(font.bold() ? "bold " : "") + + QString::number(QFontInfo(font).pixelSize()) + "px "; + + // Then, try to match the font name with a standard CSS font family: + QString genericFont = ""; + if (definition.contains("serif", false) || definition.contains("roman", false)) + genericFont = "serif"; + // No "else if" because "sans serif" must be counted as "sans". So, the order between "serif" and "sans" is important + if (definition.contains("sans", false) || definition.contains("arial", false) || definition.contains("helvetica", false)) + genericFont = "sans-serif"; + if (definition.contains("mono", false) || definition.contains("courier", false) || + definition.contains("typewriter", false) || definition.contains("console", false) || + definition.contains("terminal", false) || definition.contains("news", false)) + genericFont = "monospace"; + + // Eventually add the generic font family to the definition: + QString fontDefinition = "\"" + font.family() + "\""; + if (!genericFont.isEmpty()) + fontDefinition += ", " + genericFont; + + if (onlyFontFamily) + return fontDefinition; + + return definition + fontDefinition; +} + +QString Tools::stripEndWhiteSpaces(const QString &string) +{ + uint length = string.length(); + uint i; + for (i = length; i > 0; --i) + if (!string[i-1].isSpace()) + break; + if (i == 0) + return ""; + else + return string.left(i); +} + + + +bool Tools::isWebColor(const QColor &color) +{ + int r = color.red(); // The 216 web colors are those colors whose RGB (Red, Green, Blue) + int g = color.green(); // values are all in the set (0, 51, 102, 153, 204, 255). + int b = color.blue(); + + return ( ( r == 0 || r == 51 || r == 102 || + r == 153 || r == 204 || r == 255 ) && + ( g == 0 || g == 51 || g == 102 || + g == 153 || g == 204 || g == 255 ) && + ( b == 0 || b == 51 || b == 102 || + b == 153 || b == 204 || b == 255 ) ); +} + +QColor Tools::mixColor(const QColor &color1, const QColor &color2) +{ + QColor mixedColor; + mixedColor.setRgb( (color1.red() + color2.red()) / 2, + (color1.green() + color2.green()) / 2, + (color1.blue() + color2.blue()) / 2 ); + return mixedColor; +} + +bool Tools::tooDark(const QColor &color) +{ + int dontCare, value; + color.getHsv(/*hue:*/dontCare, /*saturation:*/dontCare, value); + return value < 175; +} + + +// TODO: Use it for all indentPixmap() +QPixmap Tools::normalizePixmap(const QPixmap &pixmap, int width, int height) +{ + if (height <= 0) + height = width; + + if (pixmap.isNull() || (pixmap.width() == width && pixmap.height() == height)) + return pixmap; + + return pixmap; +} + +QPixmap Tools::indentPixmap(const QPixmap &source, int depth, int deltaX) +{ + // Verify if it is possible: + if (depth <= 0 || source.isNull()) + return source; + + // Compute the number of pixels to indent: + if (deltaX <= 0) + deltaX = 2 * source.width() / 3; + int indent = depth * deltaX; + + // Create the images: + QImage resultImage(indent + source.width(), source.height(), 32); + QImage sourceImage = source.convertToImage(); + resultImage.setAlphaBuffer(true); + + // Clear the indent part (the left part) by making it fully transparent: + uint *p; + for (int row = 0; row < resultImage.height(); ++row) { + for (int column = 0; column < resultImage.width(); ++column) { + p = (uint *)resultImage.scanLine(row) + column; + *p = 0; // qRgba(0, 0, 0, 0) + } + } + + // Copy the source image byte per byte to the right part: + uint *q; + for (int row = 0; row < sourceImage.height(); ++row) { + for (int column = 0; column < sourceImage.width(); ++column) { + p = (uint *)resultImage.scanLine(row) + indent + column; + q = (uint *)sourceImage.scanLine(row) + column; + *p = *q; + } + } + + // And return the result: + QPixmap result; + result.convertFromImage(resultImage); + return result; +} + +#include <iostream> + +void Tools::deleteRecursively(const QString &folderOrFile) +{ + if (folderOrFile.isEmpty()) + return; + + QFileInfo fileInfo(folderOrFile); + if (fileInfo.isDir()) { + // Delete the child files: + QDir dir(folderOrFile, QString::null, QDir::Name | QDir::IgnoreCase, QDir::All | QDir::Hidden); + QStringList list = dir.entryList(); + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) + if ( *it != "." && *it != ".." ) + deleteRecursively(folderOrFile + "/" + *it); + // And then delete the folder: + dir.rmdir(folderOrFile); + } else + // Delete the file: + QFile::remove(folderOrFile); +} + +QString Tools::fileNameForNewFile(const QString &wantedName, const QString &destFolder) +{ + QString fileName = wantedName; + QString fullName = destFolder + fileName; + QString extension = ""; + int number = 2; + QDir dir; + + // First check if the file do not exists yet (simplier and more often case) + dir = QDir(fullName); + if ( ! dir.exists(fullName) ) + return fileName; + + // Find the file extension, if it exists : Split fileName in fileName and extension + // Example : fileName == "note5-3.txt" => fileName = "note5-3" and extension = ".txt" + int extIndex = fileName.findRev('.'); + if (extIndex != -1 && extIndex != int(fileName.length()-1)) { // Extension found and fileName do not ends with '.' ! + extension = fileName.mid(extIndex); + fileName.truncate(extIndex); + } // else fileName = fileName and extension = "" + + // Find the file number, if it exists : Split fileName in fileName and number + // Example : fileName == "note5-3" => fileName = "note5" and number = 3 + int extNumber = fileName.findRev('-'); + if (extNumber != -1 && extNumber != int(fileName.length()-1)) { // Number found and fileName do not ends with '-' ! + bool isANumber; + int theNumber = fileName.mid(extNumber + 1).toInt(&isANumber); + if (isANumber) { + number = theNumber; + fileName.truncate(extNumber); + } // else : + } // else fileName = fileName and number = 2 (because if the file already exists, the genereated name is at last the 2nd) + + QString finalName; + for (/*int number = 2*/; ; ++number) { // TODO: FIXME: If overflow ??? + finalName = fileName + "-" + QString::number(number) + extension; + fullName = destFolder + finalName; + dir = QDir(fullName); + if ( ! dir.exists(fullName) ) + break; + } + + return finalName; +} + + +// TODO: Move it from NoteFactory +/*QString NoteFactory::iconForURL(const KURL &url) +{ + QString icon = KMimeType::iconForURL(url.url()); + if ( url.protocol() == "mailto" ) + icon = "message"; + return icon; +}*/ + +bool Tools::isAFileCut(QMimeSource *source) +{ + if (source->provides("application/x-kde-cutselection")) { + QByteArray array = source->encodedData("application/x-kde-cutselection"); + return !array.isEmpty() && QCString(array.data(), array.size() + 1).at(0) == '1'; + } else + return false; +} + +void Tools::printChildren(QObject* parent) +{ + const QObjectList* objs = parent->children(); + QObjectListIt it(*objs); + QObject *obj; + + while((obj = it.current())!= 0){ + ++it; + kdDebug() << k_funcinfo << obj->className() << ": " << obj->name() << endl; + } +} |