1 /***************************************************************************
2 * Copyright (C) 2003 by S�astien Laot *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
25 #include <q3stylesheet.h>
27 #include <q3valuestack.h>
28 #include <qfileinfo.h>
32 #include <qfontinfo.h>
40 Q3MemArray
<QTime
> StopWatch::starts
;
41 Q3MemArray
<double> StopWatch::totals
;
42 Q3MemArray
<uint
> StopWatch::counts
;
44 void StopWatch::start(uint id
)
46 if (id
>= starts
.size()) {
47 totals
.resize(id
+ 1);
48 counts
.resize(id
+ 1);
49 for (uint i
= starts
.size(); i
<= id
; i
++) {
53 starts
.resize(id
+ 1);
55 starts
[id
] = QTime::currentTime();
58 void StopWatch::check(uint id
)
60 if (id
>= starts
.size())
62 double time
= starts
[id
].msecsTo(QTime::currentTime()) / 1000.0;
65 kdDebug() << k_funcinfo
<< "Timer_" << id
<< ": " << time
<< " s [" << counts
[id
] << " times, total: " << totals
[id
] << " s, average: " << totals
[id
] / counts
[id
] << " s]" << endl
;
68 QString
Tools::textToHTML(const QString
&text
)
72 if (/*text.isEmpty() ||*/ text
== " " || text
== " ")
73 return "<p> </p>";
75 // convertFromPlainText() replace "\n\n" by "</p>\n<p>": we don't want that
76 QString htmlString
= Q3StyleSheet::convertFromPlainText(text
, Q3StyleSheetItem::WhiteSpaceNormal
);
77 return htmlString
.replace("</p>\n", "<br>\n<br>\n").replace("\n<p>", "\n"); // Don't replace first and last tags
80 QString
Tools::textToHTMLWithoutP(const QString
&text
)
82 // textToHTML(text) return "<p>HTMLizedText</p>". We remove the strating "<p>" and ending </p>"
83 QString HTMLizedText
= textToHTML(text
);
84 return HTMLizedText
.mid(3, HTMLizedText
.length() - 3 - 4);
87 QString
Tools::htmlToParagraph(const QString
&html
)
89 QString result
= html
;
90 bool startedBySpan
= false;
92 // Remove the <html> start tag, all the <head> and the <body> start
93 // Because <body> can contain style="..." parameter, we transform it to <span>
94 int pos
= result
.find("<body");
96 result
= "<span" + result
.mid(pos
+ 5);
100 // Remove the ending "</p>\n</body></html>", each tag can be separated by space characters (%s)
101 // "</p>" can be omitted (eg. if the HTML doesn't contain paragraph but tables), as well as "</body>" (optinal)
102 pos
= result
.find(QRegExp("(?:(?:</p>[\\s\\n\\r\\t]*)*</body>[\\s\\n\\r\\t]*)*</html>", false)); // Case unsensitive
104 result
= result
.left(pos
);
109 return result
.replace("</p>", "<br><br>").replace("<p>", "");
112 // The following is adapted from KStringHanlder::tagURLs
113 // The adaptation lies in the change to urlEx
114 // Thanks to Richard Heck
115 QString
Tools::tagURLs(const QString
&text
)
117 QRegExp
urlEx("(www\\.(?!\\.)|([a-zA-z]+)://)[\\d\\w\\./,:_~\\?=&;#@\\-\\+\\%\\$]+[\\d\\w/]");
119 QString
richText(text
);
122 while ((urlPos
= urlEx
.search(richText
, urlPos
)) >= 0) {
123 urlLen
= urlEx
.matchedLength();
124 QString href
= richText
.mid(urlPos
, urlLen
);
125 // Qt doesn't support (?<=pattern) so we do it here
126 if ((urlPos
> 0) && richText
[urlPos
-1].isLetterOrNumber()) {
130 QString anchor
= "<a href=\"" + href
+ "\">" + href
+ "</a>";
131 richText
.replace(urlPos
, urlLen
, anchor
);
132 urlPos
+= anchor
.length();
137 QString
Tools::htmlToText(const QString
&html
)
139 QString text
= htmlToParagraph(html
);
141 text
.replace("</h1>", "\n");
142 text
.replace("</h2>", "\n");
143 text
.replace("</h3>", "\n");
144 text
.replace("</h4>", "\n");
145 text
.replace("</h5>", "\n");
146 text
.replace("</h6>", "\n");
147 text
.replace("</li>", "\n");
148 text
.replace("</dt>", "\n");
149 text
.replace("</dd>", "\n");
150 text
.replace("<dd>", " ");
151 text
.replace("</div>","\n");
152 text
.replace("</blockquote>","\n");
153 text
.replace("</caption>","\n");
154 text
.replace("</tr>", "\n");
155 text
.replace("</th>", " ");
156 text
.replace("</td>", " ");
157 text
.replace("<br>", "\n");
158 text
.replace("<br />","\n");
159 // FIXME: Format <table> tags better, if possible
160 // TODO: Replace é and co. by theire equivalent!
167 int deep
= 0; // The deep of the current line in imbriqued lists
168 Q3ValueStack
<bool> ul
; // true if current list is a <ul> one, false if it's an <ol> one
169 Q3ValueStack
<int> lines
; // The line number if it is an <ol> list
170 // We're removing every other tags, or replace them in the case of li:
171 while ( (pos
= text
.find("<"), pos
) != -1 ) {
172 // What is the current tag?
173 tag
= text
.mid(pos
+ 1, 2);
174 tag3
= text
.mid(pos
+ 1, 3);
180 } else if (tag
== "ol") {
184 } else if (tag3
== "/ul" || tag3
== "/ol") {
189 // Where the tag closes?
190 pos2
= text
.find(">");
193 text
.remove(pos
, pos2
- pos
+ 1);
194 // And replace li with "* ", "x. "... without forbidding to indent that:
196 // How many spaces before the line (indentation):
198 for (int i
= 1; i
< deep
; i
++)
200 // The bullet or number of the line:
201 QString bullet
= "* ";
202 if (ul
.top() == false) {
203 lines
.push(lines
.pop() + 1);
204 bullet
= QString::number(lines
.top()) + ". ";
207 text
.insert(pos
, spaces
+ bullet
);
209 if ( (tag3
== "/ul" || tag3
== "/ol") && deep
== 0 )
210 text
.insert(pos
, "\n"); // Empty line before and after a set of lists
215 text
.replace(">", ">");
216 text
.replace("<", "<");
217 text
.replace(""", "\"");
218 text
.replace(" ", " ");
219 text
.replace("&", "&"); // CONVERT IN LAST!!
224 QString
Tools::cssFontDefinition(const QFont
&font
, bool onlyFontFamily
)
226 // The font definition:
227 QString definition
= QString(font
.italic() ? "italic " : "") +
228 QString(font
.bold() ? "bold " : "") +
229 QString::number(QFontInfo(font
).pixelSize()) + "px ";
231 // Then, try to match the font name with a standard CSS font family:
232 QString genericFont
= "";
233 if (definition
.contains("serif", false) || definition
.contains("roman", false))
234 genericFont
= "serif";
235 // No "else if" because "sans serif" must be counted as "sans". So, the order between "serif" and "sans" is important
236 if (definition
.contains("sans", false) || definition
.contains("arial", false) || definition
.contains("helvetica", false))
237 genericFont
= "sans-serif";
238 if (definition
.contains("mono", false) || definition
.contains("courier", false) ||
239 definition
.contains("typewriter", false) || definition
.contains("console", false) ||
240 definition
.contains("terminal", false) || definition
.contains("news", false))
241 genericFont
= "monospace";
243 // Eventually add the generic font family to the definition:
244 QString fontDefinition
= "\"" + font
.family() + "\"";
245 if (!genericFont
.isEmpty())
246 fontDefinition
+= ", " + genericFont
;
249 return fontDefinition
;
251 return definition
+ fontDefinition
;
254 QString
Tools::stripEndWhiteSpaces(const QString
&string
)
256 uint length
= string
.length();
258 for (i
= length
; i
> 0; --i
)
259 if (!string
[i
-1].isSpace())
264 return string
.left(i
);
269 bool Tools::isWebColor(const QColor
&color
)
271 int r
= color
.red(); // The 216 web colors are those colors whose RGB (Red, Green, Blue)
272 int g
= color
.green(); // values are all in the set (0, 51, 102, 153, 204, 255).
273 int b
= color
.blue();
275 return ( ( r
== 0 || r
== 51 || r
== 102 ||
276 r
== 153 || r
== 204 || r
== 255 ) &&
277 ( g
== 0 || g
== 51 || g
== 102 ||
278 g
== 153 || g
== 204 || g
== 255 ) &&
279 ( b
== 0 || b
== 51 || b
== 102 ||
280 b
== 153 || b
== 204 || b
== 255 ) );
283 QColor
Tools::mixColor(const QColor
&color1
, const QColor
&color2
)
286 mixedColor
.setRgb( (color1
.red() + color2
.red()) / 2,
287 (color1
.green() + color2
.green()) / 2,
288 (color1
.blue() + color2
.blue()) / 2 );
292 bool Tools::tooDark(const QColor
&color
)
295 color
.getHsv(/*hue:*/dontCare
, /*saturation:*/dontCare
, value
);
300 // TODO: Use it for all indentPixmap()
301 QPixmap
Tools::normalizePixmap(const QPixmap
&pixmap
, int width
, int height
)
306 if (pixmap
.isNull() || (pixmap
.width() == width
&& pixmap
.height() == height
))
312 QPixmap
Tools::indentPixmap(const QPixmap
&source
, int depth
, int deltaX
)
314 // Verify if it is possible:
315 if (depth
<= 0 || source
.isNull())
318 // Compute the number of pixels to indent:
320 deltaX
= 2 * source
.width() / 3;
321 int indent
= depth
* deltaX
;
323 // Create the images:
324 QImage
resultImage(indent
+ source
.width(), source
.height(), 32);
325 QImage sourceImage
= source
.convertToImage();
326 resultImage
.setAlphaBuffer(true);
328 // Clear the indent part (the left part) by making it fully transparent:
330 for (int row
= 0; row
< resultImage
.height(); ++row
) {
331 for (int column
= 0; column
< resultImage
.width(); ++column
) {
332 p
= (uint
*)resultImage
.scanLine(row
) + column
;
333 *p
= 0; // qRgba(0, 0, 0, 0)
337 // Copy the source image byte per byte to the right part:
339 for (int row
= 0; row
< sourceImage
.height(); ++row
) {
340 for (int column
= 0; column
< sourceImage
.width(); ++column
) {
341 p
= (uint
*)resultImage
.scanLine(row
) + indent
+ column
;
342 q
= (uint
*)sourceImage
.scanLine(row
) + column
;
347 // And return the result:
349 result
.convertFromImage(resultImage
);
355 void Tools::deleteRecursively(const QString
&folderOrFile
)
357 if (folderOrFile
.isEmpty())
360 QFileInfo
fileInfo(folderOrFile
);
361 if (fileInfo
.isDir()) {
362 // Delete the child files:
363 QDir
dir(folderOrFile
, QString::null
, QDir::Name
| QDir::IgnoreCase
, QDir::All
| QDir::Hidden
);
364 QStringList list
= dir
.entryList();
365 for ( QStringList::Iterator it
= list
.begin(); it
!= list
.end(); ++it
)
366 if ( *it
!= "." && *it
!= ".." )
367 deleteRecursively(folderOrFile
+ "/" + *it
);
368 // And then delete the folder:
369 dir
.rmdir(folderOrFile
);
372 QFile::remove(folderOrFile
);
375 QString
Tools::fileNameForNewFile(const QString
&wantedName
, const QString
&destFolder
)
377 QString fileName
= wantedName
;
378 QString fullName
= destFolder
+ fileName
;
379 QString extension
= "";
383 // First check if the file do not exists yet (simplier and more often case)
384 dir
= QDir(fullName
);
385 if ( ! dir
.exists(fullName
) )
388 // Find the file extension, if it exists : Split fileName in fileName and extension
389 // Example : fileName == "note5-3.txt" => fileName = "note5-3" and extension = ".txt"
390 int extIndex
= fileName
.findRev('.');
391 if (extIndex
!= -1 && extIndex
!= int(fileName
.length()-1)) { // Extension found and fileName do not ends with '.' !
392 extension
= fileName
.mid(extIndex
);
393 fileName
.truncate(extIndex
);
394 } // else fileName = fileName and extension = ""
396 // Find the file number, if it exists : Split fileName in fileName and number
397 // Example : fileName == "note5-3" => fileName = "note5" and number = 3
398 int extNumber
= fileName
.findRev('-');
399 if (extNumber
!= -1 && extNumber
!= int(fileName
.length()-1)) { // Number found and fileName do not ends with '-' !
401 int theNumber
= fileName
.mid(extNumber
+ 1).toInt(&isANumber
);
404 fileName
.truncate(extNumber
);
406 } // else fileName = fileName and number = 2 (because if the file already exists, the genereated name is at last the 2nd)
409 for (/*int number = 2*/; ; ++number
) { // TODO: FIXME: If overflow ???
410 finalName
= fileName
+ "-" + QString::number(number
) + extension
;
411 fullName
= destFolder
+ finalName
;
412 dir
= QDir(fullName
);
413 if ( ! dir
.exists(fullName
) )
421 // TODO: Move it from NoteFactory
422 /*QString NoteFactory::iconForURL(const KURL &url)
424 QString icon = KMimeType::iconForURL(url.url());
425 if ( url.protocol() == "mailto" )
430 bool Tools::isAFileCut(QMimeSource
*source
)
432 if (source
->provides("application/x-kde-cutselection")) {
433 QByteArray array
= source
->encodedData("application/x-kde-cutselection");
434 return !array
.isEmpty() && Q3CString(array
.data(), array
.size() + 1).at(0) == '1';
439 void Tools::printChildren(QObject
* parent
)
441 const QObjectList
* objs
= parent
->children();
442 QObjectListIt
it(*objs
);
445 while((obj
= it
.current())!= 0){
447 kdDebug() << k_funcinfo
<< obj
->className() << ": " << obj
->name() << endl
;