source: psi/trunk/src/common.cpp@ 58

Last change on this file since 58 was 55, checked in by dmik, 19 years ago

Psi: General: Changed the !beep format to specify sequences of beeps: !beep[:freq[,dur]]...

File size: 46.1 KB
Line 
1/*
2 * common.cpp - contains all the common variables and functions for Psi
3 * Copyright (C) 2001-2003 Justin Karneges
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include"common.h"
22
23#include"profiles.h"
24#include"rtparse.h"
25
26#include"psievent.h"
27
28QString PROG_NAME = "Psi";
29QString PROG_VERSION = "0.10-os2-b2-dev (" __DATE__ ")";
30
31#ifdef HAVE_CONFIG
32#include "config.h"
33#endif
34
35#ifndef PSI_DATADIR
36#define PSI_DATADIR "/usr/local/share/psi"
37#endif
38
39#include<qregexp.h>
40#include<qdir.h>
41#include<qfile.h>
42#include<qapplication.h>
43#include<qsound.h>
44#include<qobjectlist.h>
45#include<qlibrary.h>
46#include<qstylesheet.h>
47
48#include<qthread.h>
49#include<qmutex.h>
50#include<qsemaphore.h>
51#include<qvaluelist.h>
52
53
54#include<stdio.h>
55
56#ifdef Q_WS_X11
57#include<sys/types.h>
58#include<sys/stat.h>
59#include<sys/wait.h>
60#endif
61
62#ifdef Q_WS_WIN
63#include<windows.h>
64#endif
65
66#ifdef Q_WS_PM
67#include<sys/types.h>
68#include<sys/stat.h>
69#endif
70
71#ifdef Q_WS_MAC
72#include<sys/types.h>
73#include<sys/stat.h>
74#include <Carbon/Carbon.h> // for HIToolbox/InternetConfig
75#include <CoreServices/CoreServices.h>
76#endif
77
78QString activeProfile;
79
80//QStringList dtcp_hostList;
81//int dtcp_port;
82//QString dtcp_proxy;
83//bool link_test = false;
84bool use_gpg = true;
85bool no_gpg_agent = false;
86uint psi_dialog_flags = (Qt::WStyle_SysMenu | Qt::WStyle_MinMax);
87//uint psi_dialog_flags = (Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WStyle_MinMax);
88
89char *pixdat_ft_back, *pixdat_ft_file, *pixdat_ft_folder;
90int pixlen_ft_back, pixlen_ft_file, pixlen_ft_folder;
91
92
93XMPP::Client *debug_jabber = 0;
94Options option;
95PsiGlobal g;
96PsiIconset *is;
97bool useSound;
98
99
100QString qstrlower(QString str)
101{
102 for(unsigned int n = 0; n < str.length(); ++n) {
103 str.at(n) = str.at(n).lower();
104 }
105
106 return str;
107}
108
109int qstrcasecmp(const QString &str1, const QString &str2)
110{
111 if(str1.length() != str2.length())
112 return 1;
113
114 for(unsigned int n = 0; n < str1.length(); ++n) {
115 if(str1.at(n).lower() != str2.at(n).lower())
116 return 1;
117 }
118
119 return 0;
120}
121
122int qstringlistmatch(QStringList &list, const QString &str)
123{
124 int n = 0;
125
126 for(QStringList::Iterator i = list.begin(); i != list.end(); ++i, ++n) {
127 if(*i == str)
128 return n;
129 }
130
131 return -1;
132}
133
134QString qstringlistlookup(QStringList &list, int x)
135{
136 int n = 0;
137 QStringList::Iterator i = list.begin();
138 for(;i != list.end() && n < x; ++i, ++n);
139 if(n != x)
140 return "";
141
142 return *i;
143}
144
145QString CAP(const QString &str)
146{
147 return QString("%1: %2").arg(PROG_NAME).arg(str);
148}
149
150QString eatptag(QString txt)
151{
152 if(txt.left(3) == "<p>")
153 txt = txt.mid(3, txt.length() - 7); // strlen("<p>") + strlen("</p>") = 7
154
155 return txt;
156}
157
158QString qstrquote(const QString &toquote, int width, bool quoteEmpty)
159{
160 int ql = 0, col = 0, atstart = 1, ls=0;
161
162 QString quoted = "> "+toquote; // quote first line
163 QString rxs = quoteEmpty ? "\n" : "\n(?!\\s*\n)";
164 QRegExp rx(rxs); // quote following lines
165 quoted.replace(rx, "\n> ");
166 rx.setPattern("> +>"); // compress > > > > quotes to >>>>
167 quoted.replace(rx, ">>");
168 quoted.replace(rx, ">>");
169 quoted.replace(QRegExp(" +\n"), "\n"); // remove trailing spaces
170
171 if (!quoteEmpty)
172 {
173 quoted.replace(QRegExp("^>+\n"), "\n\n"); // unquote empty lines
174 quoted.replace(QRegExp("\n>+\n"), "\n\n");
175 }
176
177
178 for (int i=0;i<(int) quoted.length();i++)
179 {
180 col++;
181 if (atstart && quoted[i] == '>') ql++; else atstart=0;
182
183 switch(quoted[i].latin1())
184 {
185 case '\n': ql = col = 0; atstart = 1; break;
186 case ' ':
187 case '\t': ls = i; break;
188
189 }
190 if (quoted[i]=='\n') { ql=0; atstart = 1; }
191
192 if (col > width)
193 {
194 if ((ls+width) < i)
195 {
196 ls = i; i = quoted.length();
197 while ((ls<i) && !quoted[ls].isSpace()) ls++;
198 i = ls;
199 }
200 if ((i<(int)quoted.length()) && (quoted[ls] != '\n'))
201 {
202 quoted.insert(ls, '\n');
203 ++ls;
204 quoted.insert(ls, QString().fill('>', ql));
205 i += ql+1;
206 col = 0;
207 }
208 }
209 }
210 quoted += "\n\n";// add two empty lines to quoted text - the cursor
211 // will be positioned at the end of those.
212 return quoted;
213}
214
215QString plain2rich(const QString &plain)
216{
217 QString rich;
218 int col = 0;
219
220 for(int i = 0; i < (int)plain.length(); ++i) {
221 if(plain[i] == '\n') {
222 rich += "<br>";
223 col = 0;
224 }
225 else if(plain[i] == '\t') {
226 rich += QChar::nbsp;
227 while(col % 4) {
228 rich += QChar::nbsp;
229 ++col;
230 }
231 }
232 else if(plain[i].isSpace()) {
233 if(i > 0 && plain[i-1] == ' ')
234 rich += QChar::nbsp;
235 else
236 rich += ' ';
237 }
238 else if(plain[i] == '<')
239 rich += "&lt;";
240 else if(plain[i] == '>')
241 rich += "&gt;";
242 else if(plain[i] == '\"')
243 rich += "&quot;";
244 else if(plain[i] == '\'')
245 rich += "&apos;";
246 else if(plain[i] == '&')
247 rich += "&amp;";
248 else
249 rich += plain[i];
250 ++col;
251 }
252
253 return rich;
254}
255
256QString rich2plain(const QString &in)
257{
258 QString out;
259
260 for(int i = 0; i < (int)in.length(); ++i) {
261 // tag?
262 if(in[i] == '<') {
263 // find end of tag
264 ++i;
265 int n = in.find('>', i);
266 if(n == -1)
267 break;
268 QString str = in.mid(i, (n-i));
269 i = n;
270
271 QString tagName;
272 n = str.find(' ');
273 if(n != -1)
274 tagName = str.mid(0, n);
275 else
276 tagName = str;
277
278 if(tagName == "br")
279 out += '\n';
280 }
281 // entity?
282 else if(in[i] == '&') {
283 // find a semicolon
284 ++i;
285 int n = in.find(';', i);
286 if(n == -1)
287 break;
288 QString type = in.mid(i, (n-i));
289 i = n; // should be n+1, but we'll let the loop increment do it
290
291 if(type == "amp")
292 out += '&';
293 else if(type == "lt")
294 out += '<';
295 else if(type == "gt")
296 out += '>';
297 else if(type == "quot")
298 out += '\"';
299 else if(type == "apos")
300 out += '\'';
301 }
302 else if(in[i].isSpace()) {
303 if(in[i] == QChar::nbsp)
304 out += ' ';
305 else if(in[i] != '\n') {
306 if(i == 0)
307 out += ' ';
308 else {
309 QChar last = out.at(out.length()-1);
310 bool ok = TRUE;
311 if(last.isSpace() && last != '\n')
312 ok = FALSE;
313 if(ok)
314 out += ' ';
315 }
316 }
317 }
318 else {
319 out += in[i];
320 }
321 }
322
323 return out;
324}
325
326
327// clips plain text
328QString clipStatus(const QString &str, int width, int height)
329{
330 QString out = "";
331 int at = 0;
332 int len = str.length();
333 if(len == 0)
334 return out;
335
336 // only take the first "height" lines
337 for(int n2 = 0; n2 < height; ++n2) {
338 // only take the first "width" chars
339 QString line;
340 bool hasNewline = false;
341 for(int n = 0; at < len; ++n, ++at) {
342 if(str.at(at) == '\n') {
343 hasNewline = true;
344 break;
345 }
346 line += str.at(at);
347 }
348 ++at;
349 if((int)line.length() > width) {
350 line.truncate(width-3);
351 line += "...";
352 }
353 out += line;
354 if(hasNewline)
355 out += '\n';
356
357 if(at >= len)
358 break;
359 }
360
361 return out;
362}
363
364QString expandEntities(const QString &in)
365{
366 return QStyleSheet::escape(in);
367}
368
369QString resolveEntities(const QString &in)
370{
371 QString out;
372
373 for(int i = 0; i < (int)in.length(); ++i) {
374 if(in[i] == '&') {
375 // find a semicolon
376 ++i;
377 int n = in.find(';', i);
378 if(n == -1)
379 break;
380 QString type = in.mid(i, (n-i));
381 i = n; // should be n+1, but we'll let the loop increment do it
382
383 if(type == "amp")
384 out += '&';
385 else if(type == "lt")
386 out += '<';
387 else if(type == "gt")
388 out += '>';
389 else if(type == "quot")
390 out += '\"';
391 else if(type == "apos")
392 out += '\'';
393 }
394 else {
395 out += in[i];
396 }
397 }
398
399 return out;
400}
401
402
403static bool linkify_pmatch(const QString &str1, int at, const QString &str2)
404{
405 if(str2.length() > (str1.length()-at))
406 return FALSE;
407
408 for(int n = 0; n < (int)str2.length(); ++n) {
409 if(str1.at(n+at).lower() != str2.at(n).lower())
410 return FALSE;
411 }
412
413 return TRUE;
414}
415
416static bool linkify_isOneOf(const QChar &c, const QString &charlist)
417{
418 for(int i = 0; i < (int)charlist.length(); ++i) {
419 if(c == charlist.at(i))
420 return TRUE;
421 }
422
423 return FALSE;
424}
425
426// encodes a few dangerous html characters
427static QString linkify_htmlsafe(const QString &in)
428{
429 QString out;
430
431 for(unsigned int n = 0; n < in.length(); ++n) {
432 if(linkify_isOneOf(in.at(n), "\"\'`<>")) {
433 // hex encode
434 QString hex;
435 hex.sprintf("%%%02X", in.at(n).latin1());
436 out.append(hex);
437 }
438 else {
439 out.append(in.at(n));
440 }
441 }
442
443 return out;
444}
445
446static bool linkify_okUrl(const QString &url)
447{
448 if(url.at(url.length()-1) == '.')
449 return FALSE;
450
451 return TRUE;
452}
453
454static bool linkify_okEmail(const QString &addy)
455{
456 // this makes sure that there is an '@' and a '.' after it, and that there is
457 // at least one char for each of the three sections
458 int n = addy.find('@');
459 if(n == -1 || n == 0)
460 return FALSE;
461 int d = addy.find('.', n+1);
462 if(d == -1 || d == 0)
463 return FALSE;
464 if((addy.length()-1) - d <= 0)
465 return FALSE;
466 if(addy.find("..") != -1)
467 return false;
468
469 return TRUE;
470}
471
472QString linkify(const QString &in)
473{
474 QString out = in;
475 int x1, x2;
476 bool isUrl, isEmail;
477 QString linked, link, href;
478
479 for(int n = 0; n < (int)out.length(); ++n) {
480 isUrl = FALSE;
481 isEmail = FALSE;
482 x1 = n;
483
484 if(linkify_pmatch(out, n, "http://")) {
485 n += 7;
486 isUrl = TRUE;
487 href = "";
488 }
489 else if(linkify_pmatch(out, n, "https://")) {
490 n += 8;
491 isUrl = TRUE;
492 href = "";
493 }
494 else if(linkify_pmatch(out, n, "ftp://")) {
495 n += 6;
496 isUrl = TRUE;
497 href = "";
498 }
499 else if(linkify_pmatch(out, n, "news://")) {
500 n += 7;
501 isUrl = TRUE;
502 href = "";
503 }
504 else if (linkify_pmatch(out, n, "ed2k://")) {
505 n += 7;
506 isUrl = TRUE;
507 href = "";
508 }
509 else if(linkify_pmatch(out, n, "www.")) {
510 isUrl = TRUE;
511 href = "http://";
512 }
513 else if(linkify_pmatch(out, n, "ftp.")) {
514 isUrl = TRUE;
515 href = "ftp://";
516 }
517 else if(linkify_pmatch(out, n, "@")) {
518 isEmail = TRUE;
519 href = "mailto:";
520 }
521
522 if(isUrl) {
523 // make sure the previous char is not alphanumeric
524 if(x1 > 0 && out.at(x1-1).isLetterOrNumber())
525 continue;
526
527 // find whitespace (or end)
528 for(x2 = n; x2 < (int)out.length(); ++x2) {
529 if(out.at(x2).isSpace() || out.at(x2) == '<')
530 break;
531 }
532 int len = x2-x1;
533 QString pre = resolveEntities(out.mid(x1, x2-x1));
534
535 // go backward hacking off unwanted punctuation
536 int cutoff;
537 for(cutoff = pre.length()-1; cutoff >= 0; --cutoff) {
538 if(!linkify_isOneOf(pre.at(cutoff), "!?,.()[]{}<>\""))
539 break;
540 }
541 ++cutoff;
542 //++x2;
543
544 link = pre.mid(0, cutoff);
545 if(!linkify_okUrl(link)) {
546 n = x1 + link.length();
547 continue;
548 }
549 href += link;
550 href = linkify_htmlsafe(href);
551 //printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
552 linked = QString("<a href=\"%1\">").arg(href) + expandEntities(link) + "</a>" + expandEntities(pre.mid(cutoff));
553 out.replace(x1, len, linked);
554 n = x1 + linked.length() - 1;
555 }
556 else if(isEmail) {
557 // go backward till we find the beginning
558 if(x1 == 0)
559 continue;
560 --x1;
561 for(; x1 >= 0; --x1) {
562 if(!linkify_isOneOf(out.at(x1), "_.-") && !out.at(x1).isLetterOrNumber())
563 break;
564 }
565 ++x1;
566
567 // go forward till we find the end
568 x2 = n + 1;
569 for(; x2 < (int)out.length(); ++x2) {
570 if(!linkify_isOneOf(out.at(x2), "_.-") && !out.at(x2).isLetterOrNumber())
571 break;
572 }
573
574 int len = x2-x1;
575 link = out.mid(x1, len);
576 //link = resolveEntities(link);
577
578 if(!linkify_okEmail(link)) {
579 n = x1 + link.length();
580 continue;
581 }
582
583 href += link;
584 //printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
585 linked = QString("<a href=\"%1\">").arg(href) + link + "</a>";
586 out.replace(x1, len, linked);
587 n = x1 + linked.length() - 1;
588 }
589 }
590
591 return out;
592}
593
594// sickening
595QString emoticonify(const QString &in)
596{
597 RTParse p(in);
598 while ( !p.atEnd() ) {
599 // returns us the first chunk as a plaintext string
600 QString str = p.next();
601
602 int i = 0;
603 while ( i >= 0 ) {
604 // find closest emoticon
605 int ePos = -1;
606 Icon *closest = 0;
607
608 int foundPos = -1, foundLen = -1;
609
610 QPtrListIterator<Iconset> iconsets(is->emoticons);
611 Iconset *iconset;
612 while ( (iconset = iconsets.current()) != 0 ) {
613 QPtrListIterator<Icon> it = iconset->iterator();
614 for ( ; it.current(); ++it) {
615 Icon *icon = it.current();
616 if ( icon->regExp().isEmpty() )
617 continue;
618
619 // some hackery
620 int iii = i;
621 bool searchAgain;
622
623 do {
624 searchAgain = false;
625
626 // find the closest match
627 const QRegExp &rx = icon->regExp();
628 int n = rx.search(str, iii);
629 if ( n == -1 )
630 continue;
631
632 if(ePos == -1 || n < ePos || (rx.matchedLength() > foundLen && n < ePos + foundLen)) {
633 // there must be whitespace at least on one side of the emoticon
634 if ( ( n == 0 ) ||
635 ( n+rx.matchedLength() == (int)str.length() ) ||
636 ( n > 0 && str[n-1].isSpace() ) ||
637 ( n+rx.matchedLength() < (int)str.length() && str[n+rx.matchedLength()].isSpace() ) )
638 {
639 ePos = n;
640 closest = icon;
641
642 foundPos = n;
643 foundLen = rx.matchedLength();
644 break;
645 }
646
647 searchAgain = true;
648 }
649
650 iii = n + rx.matchedLength();
651 } while ( searchAgain );
652 }
653
654 ++iconsets;
655 }
656
657 QString s;
658 if(ePos == -1)
659 s = str.mid(i);
660 else
661 s = str.mid(i, ePos-i);
662 p.putPlain(s);
663
664 if ( !closest )
665 break;
666
667 p.putRich( QString("<icon name=\"%1\" text=\"%2\">").arg(closest->name()).arg(str.mid(foundPos, foundLen)) );
668 i = foundPos + foundLen;
669 }
670 }
671
672 QString out = p.output();
673
674 //enable *bold* stuff
675 // //old code
676 //out=out.replace(QRegExp("(^[^<>\\s]*|\\s[^<>\\s]*)\\*(\\S+)\\*([^<>\\s]*\\s|[^<>\\s]*$)"),"\\1<b>*\\2*</b>\\3");
677 //out=out.replace(QRegExp("(^[^<>\\s\\/]*|\\s[^<>\\s\\/]*)\\/([^\\/\\s]+)\\/([^<>\\s\\/]*\\s|[^<>\\s\\/]*$)"),"\\1<i>/\\2/</i>\\3");
678 //out=out.replace(QRegExp("(^[^<>\\s]*|\\s[^<>\\s]*)_(\\S+)_([^<>\\s]*\\s|[^<>\\s]*$)"),"\\1<u>_\\2_</u>\\3");
679
680 out=out.replace(QRegExp("(^_|\\s_)(\\S+)(_\\s|_$)"),"\\1<u>\\2</u>\\3");
681 out=out.replace(QRegExp("(^\\*|\\s\\*)(\\S+)(\\*\\s|\\*$)"),"\\1<b>\\2</b>\\3");
682 out=out.replace(QRegExp("(^\\/|\\s\\/)(\\S+)(\\/\\s|\\/$)"),"\\1<i>\\2</i>\\3");
683
684 return out;
685}
686
687QString encodePassword(const QString &pass, const QString &key)
688{
689 QString result;
690 unsigned int n1, n2;
691
692 if(key.length() == 0)
693 return pass;
694
695 for(n1 = 0, n2 = 0; n1 < pass.length(); ++n1) {
696 ushort x = pass.at(n1).unicode() ^ key.at(n2++).unicode();
697 QString hex;
698 hex.sprintf("%04x", x);
699 result += hex;
700 if(n2 >= key.length())
701 n2 = 0;
702 }
703 return result;
704}
705
706QString decodePassword(const QString &pass, const QString &key)
707{
708 QString result;
709 unsigned int n1, n2;
710
711 if(key.length() == 0)
712 return pass;
713
714 for(n1 = 0, n2 = 0; n1 < pass.length(); n1 += 4) {
715 ushort x = 0;
716 if(n1 + 4 > pass.length())
717 break;
718 x += hexChar2int(pass.at(n1))*4096;
719 x += hexChar2int(pass.at(n1+1))*256;
720 x += hexChar2int(pass.at(n1+2))*16;
721 x += hexChar2int(pass.at(n1+3));
722 QChar c(x ^ key.at(n2++).unicode());
723 result += c;
724 if(n2 >= key.length())
725 n2 = 0;
726 }
727 return result;
728}
729
730QString status2txt(int status)
731{
732 switch(status) {
733 case STATUS_OFFLINE: return QObject::tr("Offline");
734 case STATUS_AWAY: return QObject::tr("Away");
735 case STATUS_XA: return QObject::tr("Not Available");
736 case STATUS_DND: return QObject::tr("Do not Disturb");
737 case STATUS_CHAT: return QObject::tr("Free for Chat");
738 case STATUS_INVISIBLE: return QObject::tr("Invisible");
739
740 case STATUS_ONLINE:
741 default: return QObject::tr("Online");
742 }
743}
744
745Icon category2icon(const QString &category, const QString &type)
746{
747 // TODO: update this to http://www.jabber.org/registrar/disco-categories.html#gateway
748
749 // still have to add more options...
750 if ( category == "service" || category == "gateway" ) {
751 QString trans;
752
753 if (type == "aim")
754 trans = "aim";
755 else if (type == "icq")
756 trans = "icq";
757 else if (type == "msn")
758 trans = "msn";
759 else if (type == "yahoo")
760 trans = "yahoo";
761 else if (type == "gadu-gadu" || type == "x-gadugadu")
762 trans = "gadugadu";
763 else if (type == "sms")
764 trans = "sms";
765 else
766 trans = "transport";
767
768 return is->transportStatus(trans, STATUS_ONLINE);
769
770 // irc
771 // jud
772 // pager
773 // jabber
774 // serverlist
775 // smtp
776 }
777 else if ( category == "conference" ) {
778 if (type == "public" || type == "private" || type == "text" || type == "irc")
779 return IconsetFactory::icon("psi/groupChat");
780 else if (type == "url")
781 return IconsetFactory::icon("psi/www");
782 // irc
783 // list
784 // topic
785 }
786 else if ( category == "validate" ) {
787 if (type == "xml")
788 return IconsetFactory::icon("psi/xml");
789 // grammar
790 // spell
791 }
792 else if ( category == "user" || category == "client" ) {
793 // client
794 // forward
795 // inbox
796 // portable
797 // voice
798 return is->status(STATUS_ONLINE);
799 }
800 // application
801 // bot
802 // calendar
803 // editor
804 // fileserver
805 // game
806 // whiteboard
807 // headline
808 // logger
809 // notice
810 // rss
811 // stock
812 // keyword
813 // dictionary
814 // dns
815 // software
816 // thesaurus
817 // web
818 // whois
819 // render
820 // en2ru
821 // ??2??
822 // tts
823 return Icon();
824}
825
826int hexChar2int(char c)
827{
828 if(c >= 'A' && c <= 'F')
829 return c - 'A' + 10;
830 else if(c >= 'a' && c <= 'f')
831 return c - 'a' + 10;
832 else if(c >= '0' && c <= '9')
833 return c - '0';
834
835 return 0;
836}
837
838char int2hexChar(int x)
839{
840 if(x < 10)
841 return (char)x + '0';
842 else
843 return (char)x - 10 + 'a';
844}
845
846QString jidEncode(const QString &jid)
847{
848 QString jid2;
849
850 for(unsigned int n = 0; n < jid.length(); ++n) {
851 if(jid.at(n) == '@') {
852 jid2.append("_at_");
853 }
854 else if(jid.at(n) == '.') {
855 jid2.append('.');
856 }
857 else if(!jid.at(n).isLetterOrNumber()) {
858 // hex encode
859 QString hex;
860 hex.sprintf("%%%02X", jid.at(n).latin1());
861 jid2.append(hex);
862 }
863 else {
864 jid2.append(jid.at(n));
865 }
866 }
867
868 return jid2;
869}
870
871QString jidDecode(const QString &jid)
872{
873 QString jid2;
874 int n;
875
876 for(n = 0; n < (int)jid.length(); ++n) {
877 if(jid.at(n) == '%' && (jid.length() - n - 1) >= 2) {
878 QString str = jid.mid(n+1,2);
879 bool ok;
880 char c = str.toInt(&ok, 16);
881 if(!ok)
882 continue;
883
884 QChar a(c);
885 jid2.append(a);
886 n += 2;
887 }
888 else {
889 jid2.append(jid.at(n));
890 }
891 }
892
893 // search for the _at_ backwards, just in case
894 for(n = (int)jid2.length(); n >= 3; --n) {
895 if(jid2.mid(n, 4) == "_at_") {
896 jid2.replace(n, 4, "@");
897 break;
898 }
899 }
900
901 return jid2;
902}
903
904QString jidnick(const QString &jid, const QString &nick)
905{
906 if(nick.isEmpty())
907 return jid;
908 else
909 return nick;
910}
911
912QString logencode(QString str)
913{
914 str.replace(QRegExp("\\\\"), "\\\\"); // backslash to double-backslash
915 str.replace(QRegExp("\\|"), "\\p"); // pipe to \p
916 str.replace(QRegExp("\n"), "\\n"); // newline to \n
917 return str;
918}
919
920QString logdecode(const QString &str)
921{
922 QString ret;
923
924 for(unsigned int n = 0; n < str.length(); ++n) {
925 if(str.at(n) == '\\') {
926 ++n;
927 if(n >= str.length())
928 break;
929
930 if(str.at(n) == 'n')
931 ret.append('\n');
932 if(str.at(n) == 'p')
933 ret.append('|');
934 if(str.at(n) == '\\')
935 ret.append('\\');
936 }
937 else {
938 ret.append(str.at(n));
939 }
940 }
941
942 return ret;
943}
944
945void qstringlistisort(QStringList &c)
946{
947 if ( c.count() <= 1 )
948 return;
949
950 QStringList::Iterator it;
951 uint size = c.count();
952
953 // first, make array that is easy (and quick) to manipulate
954 QString *heap = new QString[ size ];
955
956 int i = 0;
957 for (it = c.begin(); it != c.end(); ++it)
958 heap[i++] = *it;
959
960 // Insertion sort
961 for (uint tmp = 0; tmp < c.count(); tmp++) {
962 heap[tmp] = c[tmp];
963 size = tmp + 1;
964
965 for (uint j = 1; j < size; j++) {
966 QString k = heap[j].lower();
967 QString r = heap[j];
968
969 for (i = j - 1; i >= 0; i--) {
970 if ( QString::compare(k, heap[i].lower()) > 0 )
971 break;
972
973 heap[i+1] = heap[i];
974 }
975
976 heap[i+1] = r;
977 }
978 }
979
980 // now, copy sorted data back to QStringList
981 it = c.begin();
982 for (i = 0; i < (int)size; i++)
983 *it++ = heap[i];
984
985 delete[] heap;
986}
987
988#ifdef Q_WS_PM
989static QString getBrowserSetting (PCSZ key)
990{
991 static const PSZ app = "WPURLDEFAULTSETTINGS";
992
993 QString value;
994 ULONG keyLen = 0;
995
996 if ( PrfQueryProfileSize( HINI_USERPROFILE, app, key, &keyLen ) && keyLen ) {
997 char *buf = new char [keyLen];
998 ULONG realLen =
999 PrfQueryProfileString( HINI_USERPROFILE, app, key, NULL, buf, keyLen );
1000 if (realLen) {
1001 realLen --; // excude zero terminator
1002 buf[realLen] = 0;
1003 value = QString::fromLocal8Bit(buf);
1004 }
1005 delete[] buf;
1006 }
1007
1008 return value;
1009}
1010#endif
1011
1012void openURL(const QString &url)
1013{
1014 //fprintf(stderr, "openURL: [%s]\n", url.latin1());
1015 bool useCustom = TRUE;
1016
1017 int colon = url.find(':');
1018 if ( colon == -1 )
1019 colon = 0;
1020 QString service = url.left( colon );
1021 if ( service == "jabber" || service == "jid" ) {
1022 // TODO
1023 return;
1024 }
1025
1026#ifdef Q_WS_WIN
1027 if(option.browser == 0)
1028 useCustom = FALSE;
1029#endif
1030#ifdef Q_WS_PM
1031 if(option.browser == 0)
1032 useCustom = FALSE;
1033#endif
1034#ifdef Q_WS_X11
1035 if(option.browser == 0 || option.browser == 2)
1036 useCustom = FALSE;
1037#endif
1038#ifdef Q_WS_MAC
1039 useCustom = FALSE;
1040#endif
1041
1042 if(useCustom) {
1043 bool isMail = FALSE;
1044 QString s = url;
1045 if(url.left(7).lower() == "mailto:") {
1046#ifdef Q_WS_PM
1047 // mozilla/thunderbird doesn't understand e-mails w/o 'mailto:'
1048#else
1049 s.remove(0, 7);
1050#endif
1051 isMail = TRUE;
1052 }
1053
1054 QStringList args;
1055
1056 if(isMail) {
1057 if(option.customMailer.isEmpty()) {
1058 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a mailer (see Options)."));
1059 return;
1060 }
1061 args += QStringList::split(' ', option.customMailer);
1062 }
1063 else {
1064 if(option.customBrowser.isEmpty()) {
1065 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a browser (see Options)."));
1066 return;
1067 }
1068 args += QStringList::split(' ', option.customBrowser);
1069 }
1070
1071 args += s;
1072 QProcess cmd(args);
1073 if(!cmd.start()) {
1074 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that your custom browser/mailer exists (see Options)."));
1075 }
1076 }
1077 else {
1078#ifdef Q_WS_WIN
1079 QCString cs = url.local8Bit();
1080 if ((unsigned int)::ShellExecuteA(NULL,NULL,cs.data(),NULL,NULL,SW_SHOW) <= 32) {
1081 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have a web browser installed."));
1082 }
1083#endif
1084#ifdef Q_WS_PM
1085 QString exe, params, workDir;
1086
1087 if (url.left(7).lower() == "mailto:") {
1088 exe = getBrowserSetting("DefaultMailExe");
1089 params = getBrowserSetting("DefaultMailParameters");
1090 workDir = getBrowserSetting("DefaultMailWorkingDir");
1091 } else if (url.left(5).lower() == "news:") {
1092 exe = getBrowserSetting("DefaultNewsExe");
1093 params = getBrowserSetting("DefaultNewsParameters");
1094 workDir = getBrowserSetting("DefaultNewsWorkingDir");
1095 } else if (url.left(4).lower() == "ftp:") {
1096 exe = getBrowserSetting("DefaultFTPExe");
1097 params = getBrowserSetting("DefaultFTPParameters");
1098 workDir = getBrowserSetting("DefaultFTPWorkingDir");
1099 } else if (url.left(4).lower() == "irc:") {
1100 exe = getBrowserSetting("DefaultIRCExe");
1101 params = getBrowserSetting("DefaultIRCParameters");
1102 workDir = getBrowserSetting("DefaultIRCWorkingDir");
1103 }
1104 // if the specific app is not found, fallback to the general broswer
1105 if (exe.isEmpty()) {
1106 exe = getBrowserSetting("DefaultBrowserExe");
1107 params = getBrowserSetting("DefaultParameters");
1108 workDir = getBrowserSetting("DefaultWorkingDir");
1109 }
1110
1111 bool success = false;
1112
1113 if (!exe.isEmpty()) {
1114 QStringList args;
1115 args += exe;
1116 if (!params.isEmpty()) args += params;
1117 args += url;
1118 QProcess cmd(args);
1119 if (!workDir.isEmpty()) cmd.setWorkingDirectory(workDir);
1120 success = cmd.start();
1121 }
1122
1123 if (!success)
1124 QMessageBox::critical(0, CAP(QObject::tr("URL error")),
1125 QObject::tr("Unable to open the URL. Ensure that you have a web "
1126 "browser installed."));
1127#endif
1128#ifdef Q_WS_X11
1129 // KDE
1130 if(option.browser == 0) {
1131 QStringList args;
1132 args += "kfmclient";
1133 args += "exec";
1134 args += url;
1135 QProcess cmd(args);
1136 if(!cmd.start()) {
1137 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have KDE installed."));
1138 }
1139 }
1140 // GNOME 2
1141 else if(option.browser == 2) {
1142 QStringList args;
1143 args += "gnome-open";
1144 args += url;
1145 QProcess cmd(args);
1146 if(!cmd.start()) {
1147 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have GNOME 2 installed."));
1148 }
1149 }
1150#endif
1151#ifdef Q_WS_MAC
1152 // Use Internet Config to hand the URL to the appropriate application, as
1153 // set by the user in the Internet Preferences pane.
1154 // NOTE: ICStart could be called once at Psi startup, saving the
1155 // ICInstance in a global variable, as a minor optimization.
1156 // ICStop should then be called at Psi shutdown if ICStart succeeded.
1157 ICInstance icInstance;
1158 OSType psiSignature = 'psi ';
1159 OSStatus error = ::ICStart( &icInstance, psiSignature );
1160 if ( error == noErr ) {
1161 ConstStr255Param hint( 0x0 );
1162 QCString cs = url.local8Bit();
1163 const char* data = cs.data();
1164 long length = cs.length();
1165 long start( 0 );
1166 long end( length );
1167 // Don't bother testing return value (error); launched application will report problems.
1168 ::ICLaunchURL( icInstance, hint, data, length, &start, &end );
1169 ICStop( icInstance );
1170 }
1171#endif
1172 }
1173}
1174
1175static bool sysinfo_done = FALSE;
1176static int timezone_offset = 0;
1177static QString timezone_str = "N/A";
1178static QString os_str = "Unknown";
1179
1180#if defined(Q_WS_X11) || defined(Q_WS_MAC)
1181#include<time.h>
1182#include<stdlib.h>
1183#include<string.h>
1184#include<sys/utsname.h>
1185#endif
1186
1187static void getSysInfo()
1188{
1189#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_PM)
1190 time_t x;
1191 time(&x);
1192 char str[256];
1193 char fmt[32];
1194 strcpy(fmt, "%z");
1195 strftime(str, 256, fmt, localtime(&x));
1196 if(strcmp(fmt, str)) {
1197 QString s = str;
1198 if(s.at(0) == '+')
1199 s.remove(0,1);
1200 s.truncate(s.length()-2);
1201 timezone_offset = s.toInt();
1202 }
1203 strcpy(fmt, "%Z");
1204 strftime(str, 256, fmt, localtime(&x));
1205 if(strcmp(fmt, str))
1206 timezone_str = str;
1207#endif
1208#if defined(Q_WS_X11)
1209 struct utsname u;
1210 uname(&u);
1211 os_str.sprintf("%s", u.sysname);
1212
1213 // get description about os
1214 enum LinuxName {
1215 LinuxNone = 0,
1216
1217 LinuxMandrake,
1218 LinuxDebian,
1219 LinuxRedHat,
1220 LinuxGentoo,
1221 LinuxSlackware,
1222 LinuxSuSE,
1223 LinuxConectiva,
1224 LinuxCaldera,
1225 LinuxLFS,
1226
1227 LinuxASP, // Russian Linux distros
1228 LinuxALT,
1229
1230 LinuxPLD, // Polish Linux distros
1231 LinuxAurox,
1232 LinuxArch
1233 };
1234
1235 enum OsFlags {
1236 OsUseName = 0,
1237 OsUseFile,
1238 OsAppendFile
1239 };
1240
1241 struct OsInfo {
1242 LinuxName id;
1243 OsFlags flags;
1244 QString file;
1245 QString name;
1246 } osInfo[] = {
1247 { LinuxMandrake, OsUseFile, "/etc/mandrake-release", "Mandrake Linux" },
1248 { LinuxDebian, OsAppendFile, "/etc/debian_version", "Debian GNU/Linux" },
1249 { LinuxGentoo, OsUseFile, "/etc/gentoo-release", "Gentoo Linux" },
1250 { LinuxSlackware, OsAppendFile, "/etc/slackware-version", "Slackware Linux" },
1251 { LinuxPLD, OsUseFile, "/etc/pld-release", "PLD Linux" },
1252 { LinuxAurox, OsUseName, "/etc/aurox-release", "Aurox Linux" },
1253 { LinuxArch, OsUseFile, "/etc/arch-release", "Arch Linux" },
1254 { LinuxLFS, OsAppendFile, "/etc/lfs-release", "LFS Linux" },
1255
1256 // untested
1257 { LinuxSuSE, OsUseFile, "/etc/SuSE-release", "SuSE Linux" },
1258 { LinuxConectiva, OsUseFile, "/etc/conectiva-release", "Conectiva Linux" },
1259 { LinuxCaldera, OsUseFile, "/etc/.installed", "Caldera Linux" },
1260
1261 // many distros use the /etc/redhat-release for compatibility, so RedHat will be the last :)
1262 { LinuxRedHat, OsUseFile, "/etc/redhat-release", "RedHat Linux" },
1263
1264 { LinuxNone, OsUseName, "", "" }
1265 };
1266
1267 for (int i = 0; osInfo[i].id != LinuxNone; i++) {
1268 QFileInfo fi( osInfo[i].file );
1269 if ( fi.exists() ) {
1270 QString desc;
1271
1272 QFile f( osInfo[i].file );
1273 f.open( IO_ReadOnly );
1274 f.readLine( desc, 128 );
1275
1276 desc = desc.stripWhiteSpace ();
1277
1278 switch ( osInfo[i].flags ) {
1279 case OsUseFile:
1280 os_str = desc;
1281 break;
1282 case OsUseName:
1283 os_str = osInfo[i].name;
1284 break;
1285 case OsAppendFile:
1286 os_str = osInfo[i].name + " (" + desc + ")";
1287 break;
1288 }
1289
1290 break;
1291 }
1292 }
1293#elif defined(Q_WS_PM)
1294 os_str = "OS/2";
1295#elif defined(Q_WS_MAC)
1296 os_str = "Mac OS X";
1297#endif
1298
1299#if defined(Q_WS_WIN)
1300 TIME_ZONE_INFORMATION i;
1301 //GetTimeZoneInformation(&i);
1302 //timezone_offset = (-i.Bias) / 60;
1303 memset(&i, 0, sizeof(i));
1304 bool inDST = (GetTimeZoneInformation(&i) == TIME_ZONE_ID_DAYLIGHT);
1305 int bias = i.Bias;
1306 if(inDST)
1307 bias += i.DaylightBias;
1308 timezone_offset = (-bias) / 60;
1309 timezone_str = "";
1310 for(int n = 0; n < 32; ++n) {
1311 uint w = inDST ? i.DaylightName[n] : i.StandardName[n];
1312 if(w == 0)
1313 break;
1314 timezone_str += QChar(w);
1315 }
1316
1317 Qt::WindowsVersion v = QApplication::winVersion();
1318 if(v == Qt::WV_95)
1319 os_str = "Windows 95";
1320 else if(v == Qt::WV_98)
1321 os_str = "Windows 98";
1322 else if(v == Qt::WV_Me)
1323 os_str = "Windows Me";
1324 else if(v == Qt::WV_DOS_based)
1325 os_str = "Windows 9x/Me";
1326 else if(v == Qt::WV_NT)
1327 os_str = "Windows NT 4.x";
1328 else if(v == Qt::WV_2000)
1329 os_str = "Windows 2000";
1330 else if(v == Qt::WV_XP)
1331 os_str = "Windows XP";
1332#if QT_VERSION >= 0x030300
1333 else if(v == Qt::WV_2003)
1334 os_str = "Windows Server 2003";
1335#endif
1336 else if(v == Qt::WV_NT_based)
1337 os_str = "Windows NT";
1338#endif
1339 sysinfo_done = TRUE;
1340}
1341
1342QString getOSName()
1343{
1344 if(!sysinfo_done)
1345 getSysInfo();
1346
1347 return os_str;
1348}
1349
1350int getTZOffset()
1351{
1352 if(!sysinfo_done)
1353 getSysInfo();
1354
1355 return timezone_offset;
1356}
1357
1358QString getTZString()
1359{
1360 if(!sysinfo_done)
1361 getSysInfo();
1362
1363 return timezone_str;
1364}
1365
1366
1367#ifdef Q_WS_X11
1368QString getResourcesDir()
1369{
1370 return PSI_DATADIR;
1371}
1372
1373QString getHomeDir()
1374{
1375 QDir proghome(QDir::homeDirPath() + "/.psi");
1376 if(!proghome.exists()) {
1377 QDir home = QDir::home();
1378 home.mkdir(".psi");
1379 chmod(QFile::encodeName(proghome.path()), 0700);
1380 }
1381
1382 return proghome.path();
1383}
1384#endif
1385
1386#ifdef Q_WS_WIN
1387QString getResourcesDir()
1388{
1389#if QT_VERSION >= 0x030200
1390 return qApp->applicationDirPath();
1391#else
1392 char baseName[MAX_PATH];
1393 GetModuleFileNameA(GetModuleHandle(NULL), baseName, MAX_PATH);
1394 QString base(baseName);
1395 int idx = base.findRev('\\');
1396
1397 if (-1 == idx)
1398 return ".";
1399
1400 base.truncate(idx);
1401 QDir baseDir(base);
1402
1403 return baseDir.absPath();
1404#endif
1405}
1406
1407QString getHomeDir()
1408{
1409 QString base;
1410
1411 // Windows 9x
1412 if(QDir::homeDirPath() == QDir::rootDirPath())
1413 base = ".";
1414 // Windows NT/2K/XP variant
1415 else
1416 base = QDir::homeDirPath();
1417
1418 // no trailing slash
1419 if(base.at(base.length()-1) == '/')
1420 base.truncate(base.length()-1);
1421
1422 QDir proghome(base + "/PsiData");
1423 if(!proghome.exists()) {
1424 QDir home(base);
1425 home.mkdir("PsiData");
1426 }
1427
1428 return proghome.path();
1429}
1430#endif
1431
1432#ifdef Q_WS_PM
1433QString getResourcesDir()
1434{
1435#if QT_VERSION >= 0x030200
1436 return qApp->applicationDirPath();
1437#else
1438# error Not implemented!
1439#endif
1440}
1441
1442QString getHomeDir()
1443{
1444 QString base = QDir::homeDirPath();
1445
1446 // no trailing slash
1447 if(base.at(base.length()-1) == '/')
1448 base.truncate(base.length()-1);
1449
1450 QDir proghome(base + "/PsiData");
1451 if(!proghome.exists()) {
1452 QDir home(base);
1453 home.mkdir("PsiData");
1454 }
1455
1456 return proghome.path();
1457}
1458#endif
1459
1460#ifdef Q_WS_MAC
1461/******************************************************************************/
1462/* Get path to Resources directory as a string. */
1463/* Return an empty string if can't find it. */
1464/******************************************************************************/
1465QString getResourcesDir()
1466{
1467 // System routine locates resource files. We "know" that Psi.icns is
1468 // in the Resources directory.
1469 QString resourcePath;
1470 CFBundleRef mainBundle = CFBundleGetMainBundle();
1471 CFStringRef resourceCFStringRef
1472 = CFStringCreateWithCString( NULL, "application.icns",
1473 kCFStringEncodingASCII );
1474 CFURLRef resourceURLRef = CFBundleCopyResourceURL( mainBundle,
1475 resourceCFStringRef,
1476 NULL,
1477 NULL );
1478 if ( resourceURLRef ) {
1479 CFStringRef resourcePathStringRef =
1480 CFURLCopyFileSystemPath( resourceURLRef, kCFURLPOSIXPathStyle );
1481 const char* resourcePathCString =
1482 CFStringGetCStringPtr( resourcePathStringRef, kCFStringEncodingASCII );
1483 if ( resourcePathCString ) {
1484 resourcePath.setLatin1( resourcePathCString );
1485 } else { // CFStringGetCStringPtr failed; use fallback conversion
1486 CFIndex bufferLength = CFStringGetLength( resourcePathStringRef ) + 1;
1487 char* resourcePathCString = new char[ bufferLength ];
1488 Boolean conversionSuccess =
1489 CFStringGetCString( resourcePathStringRef,
1490 resourcePathCString, bufferLength,
1491 kCFStringEncodingASCII );
1492 if ( conversionSuccess ) {
1493 resourcePath = resourcePathCString;
1494 }
1495 delete [] resourcePathCString; // I own this
1496 }
1497 CFRelease( resourcePathStringRef ); // I own this
1498 }
1499 // Remove the tail component of the path
1500 if ( ! resourcePath.isNull() ) {
1501 QFileInfo fileInfo( resourcePath );
1502 resourcePath = fileInfo.dirPath( true );
1503 }
1504 return resourcePath;
1505}
1506
1507QString getHomeDir()
1508{
1509 QDir proghome(QDir::homeDirPath() + "/.psi");
1510 if(!proghome.exists()) {
1511 QDir home = QDir::home();
1512 home.mkdir(".psi");
1513 chmod(QFile::encodeName(proghome.path()), 0700);
1514 }
1515
1516 return proghome.path();
1517}
1518#endif
1519
1520
1521QString getHistoryDir()
1522{
1523 QDir history(pathToProfile(activeProfile) + "/history");
1524 if (!history.exists()) {
1525 QDir profile(pathToProfile(activeProfile));
1526 profile.mkdir("history");
1527 }
1528
1529 return history.path();
1530}
1531
1532QString getVCardDir()
1533{
1534 QDir vcard(pathToProfile(activeProfile) + "/vcard");
1535 if (!vcard.exists()) {
1536 QDir profile(pathToProfile(activeProfile));
1537 profile.mkdir("vcard");
1538 }
1539
1540 return vcard.path();
1541}
1542
1543bool fileCopy(const QString &src, const QString &dest)
1544{
1545 QFile in(src);
1546 QFile out(dest);
1547
1548 if(!in.open(IO_ReadOnly))
1549 return FALSE;
1550 if(!out.open(IO_WriteOnly))
1551 return FALSE;
1552
1553 char *dat = new char[16384];
1554 int n = 0;
1555 while(!in.atEnd()) {
1556 n = in.readBlock(dat, 16384);
1557 if(n == -1) {
1558 delete dat;
1559 return FALSE;
1560 }
1561 out.writeBlock(dat, n);
1562 }
1563 delete dat;
1564
1565 out.close();
1566 in.close();
1567
1568 return TRUE;
1569}
1570
1571class BeepThread : public QThread
1572{
1573public:
1574 BeepThread() : sem(1), terminate(false) {}
1575 ~BeepThread() { while (sem.available() != sem.total()) sem--; }
1576 void run() {
1577 lock.lock();
1578 while (!terminate) {
1579 while (!terminate && queue.count()) {
1580 Beep beep = queue.front();
1581 queue.pop_front();
1582 lock.unlock();
1583#if defined(Q_WS_WIN)
1584 if (beep.freq && beep.dur)
1585 Beep(beep.freq, beep.dur);
1586 else
1587#elif defined(Q_WS_PM)
1588 if (beep.freq && beep.dur)
1589 DosBeep(beep.freq, beep.dur);
1590 else
1591#endif
1592 QApplication::beep();
1593 lock.lock();
1594 }
1595 lock.unlock();
1596 sem++;
1597 lock.lock();
1598 }
1599 lock.unlock();
1600 }
1601 void beginSequence() {
1602 lock.lock();
1603 queue.clear(); // stop the previous sequence
1604 }
1605 void addNote(ulong freq, ulong dur) {
1606 queue.push_back(Beep(freq, dur));
1607 }
1608 void endSequence() {
1609 lock.unlock();
1610 if (!sem.available())
1611 sem--;
1612 }
1613 void stop() {
1614 lock.lock();
1615 terminate = true;
1616 lock.unlock();
1617 if (!sem.available())
1618 sem--;
1619 wait();
1620 }
1621private:
1622 struct Beep {
1623 Beep() : freq(0), dur(0) {}
1624 Beep(ulong f, ulong d) : freq(f), dur(d) {}
1625 const ulong freq;
1626 const ulong dur;
1627 };
1628 QMutex lock;
1629 QSemaphore sem; // not QWaitCond cause we need level-triggered semantics
1630 QValueList<Beep> queue;
1631 bool terminate;
1632};
1633
1634static BeepThread *beepThread = 0;
1635
1636static void cleanupBeepThread()
1637{
1638 if (beepThread) {
1639 beepThread->stop();
1640 delete beepThread;
1641 beepThread = 0;
1642 }
1643}
1644
1645void soundPlay(const QString &str)
1646{
1647 if (str.startsWith("!beep")) {
1648 QStringList seq = QStringList::split(':',str);
1649 if (seq[0].length() == 5 /* strlen("!beep") */) {
1650 if (beepThread == 0) {
1651 beepThread = new BeepThread();
1652 if (beepThread) {
1653 qAddPostRoutine(cleanupBeepThread);
1654 beepThread->start();
1655 }
1656 }
1657 Q_ASSERT(beepThread);
1658 if (beepThread) {
1659 beepThread->beginSequence();
1660#if defined(Q_WS_WIN) || defined(Q_WS_PM)
1661 ulong lastDur = 100;
1662 size_t i = 1;
1663 for (; i < seq.count(); ++i) {
1664 QStringList note = QStringList::split(',',seq[i]);
1665 if (note.count() < 1) break;
1666 ulong freq = note[0].toULong();
1667 if (freq < 0x25 || freq > 0x7FFF) break;
1668 ulong dur = lastDur;
1669 if (note.count() > 2) break;
1670 if (note.count() == 2) {
1671 dur = note[1].toULong();
1672 // 3s is the reasonable maximum for the duration
1673 if (dur == 0 || dur > 3000) break;
1674 lastDur = dur;
1675 }
1676 beepThread->addNote(freq, dur);
1677 }
1678 if (i == 1) // didn't find a single note
1679#endif
1680 beepThread->addNote(0, 0);
1681 beepThread->endSequence();
1682 }
1683 return;
1684 }
1685 }
1686
1687 if(!QFile::exists(str))
1688 return;
1689
1690#if defined(Q_WS_WIN) || defined(Q_WS_PM) || defined(Q_WS_MAC)
1691 QSound::play(str);
1692#else
1693 if(!option.player.isEmpty()) {
1694 QStringList args;
1695 args = QStringList::split(' ', option.player);
1696 args += str;
1697 QProcess cmd(args);
1698 if(!cmd.start())
1699 wait3(NULL,WNOHANG,NULL);
1700 }
1701#endif
1702}
1703
1704XMPP::Status makeStatus(int x, const QString &str)
1705{
1706 XMPP::Status s;
1707 s.setStatus(str);
1708 if(x == STATUS_OFFLINE)
1709 s.setIsAvailable(false);
1710 else if(x == STATUS_INVISIBLE)
1711 s.setIsInvisible(true);
1712 else {
1713 if(x == STATUS_AWAY)
1714 s.setShow("away");
1715 else if(x == STATUS_XA)
1716 s.setShow("xa");
1717 else if(x == STATUS_DND)
1718 s.setShow("dnd");
1719 else if(x == STATUS_CHAT)
1720 s.setShow("chat");
1721 }
1722
1723 return s;
1724}
1725
1726int makeSTATUS(const XMPP::Status &s)
1727{
1728 int type = STATUS_ONLINE;
1729 if(!s.isAvailable())
1730 type = STATUS_OFFLINE;
1731 else if(s.isInvisible())
1732 type= STATUS_INVISIBLE;
1733 else {
1734 if(s.show() == "away")
1735 type = STATUS_AWAY;
1736 else if(s.show() == "xa")
1737 type = STATUS_XA;
1738 else if(s.show() == "dnd")
1739 type = STATUS_DND;
1740 else if(s.show() == "chat")
1741 type = STATUS_CHAT;
1742 }
1743 return type;
1744}
1745
1746#include<qlayout.h>
1747QLayout *rw_recurseFindLayout(QLayout *lo, QWidget *w)
1748{
1749 //printf("scanning layout: %p\n", lo);
1750 QLayoutIterator it = lo->iterator();
1751 for(QLayoutItem *i; (i = it.current()); ++it) {
1752 //printf("found: %p,%p\n", i->layout(), i->widget());
1753 QLayout *slo = i->layout();
1754 if(slo) {
1755 QLayout *tlo = rw_recurseFindLayout(slo, w);
1756 if(tlo)
1757 return tlo;
1758 }
1759 else if(i->widget() == w)
1760 return lo;
1761 }
1762 return 0;
1763}
1764
1765QLayout *rw_findLayoutOf(QWidget *w)
1766{
1767 return rw_recurseFindLayout(w->parentWidget()->layout(), w);
1768}
1769
1770void replaceWidget(QWidget *a, QWidget *b)
1771{
1772 if(!a)
1773 return;
1774
1775 QLayout *lo = rw_findLayoutOf(a);
1776 if(!lo)
1777 return;
1778 //printf("decided on this: %p\n", lo);
1779
1780 if(lo->inherits("QBoxLayout")) {
1781 QBoxLayout *bo = (QBoxLayout *)lo;
1782 int n = bo->findWidget(a);
1783 bo->insertWidget(n+1, b);
1784 delete a;
1785 }
1786}
1787
1788void closeDialogs(QWidget *w)
1789{
1790 // close qmessagebox?
1791 const QObjectList *list = w->children();
1792 if(list) {
1793 QObjectListIt it(*list);
1794 QPtrList<QDialog> dialogs;
1795 for(QObject *o; (o = it.current()); ++it) {
1796 if(o->inherits("QDialog"))
1797 dialogs.append((QDialog *)o);
1798 }
1799 QPtrListIterator<QDialog> dit(dialogs);
1800 for(QDialog *w; (w = dit.current()); ++dit)
1801 w->close();
1802 }
1803}
1804
1805QString enc822jid(const QString &s)
1806{
1807 QString out;
1808 for(int n = 0; n < (int)s.length(); ++n) {
1809 if(s[n] == '\\' || s[n] == '<' || s[n] == '>') {
1810 QString hex;
1811 hex.sprintf("\\x%02X", (unsigned char )s[n]);
1812 out.append(hex);
1813 }
1814 else
1815 out += s[n];
1816 }
1817 return out;
1818}
1819
1820QString dec822jid(const QString &s)
1821{
1822 QString out;
1823 for(int n = 0; n < (int)s.length(); ++n) {
1824 if(s[n] == '\\' && n + 3 < (int)s.length()) {
1825 int x = n + 1;
1826 n += 3;
1827 if(s[x] != 'x')
1828 continue;
1829 ushort val = 0;
1830 val += hexChar2int(s[x+1])*16;
1831 val += hexChar2int(s[x+2]);
1832 QChar c(val);
1833 out += c;
1834 }
1835 else
1836 out += s[n];
1837 }
1838 return out;
1839}
1840
1841#ifdef Q_WS_X11
1842#include<X11/Xlib.h>
1843#include<X11/Xutil.h> // needed for WM_CLASS hinting
1844
1845void x11wmClass(Display *dsp, WId wid, QString resName)
1846{
1847 char app_name[] = "psi";
1848
1849 //Display *dsp = x11Display(); // get the display
1850 //WId win = winId(); // get the window
1851 XClassHint classhint; // class hints
1852 classhint.res_name = (char *)resName.latin1(); // res_name
1853 classhint.res_class = app_name; // res_class
1854 XSetClassHint(dsp, wid, &classhint); // set the class hints
1855}
1856
1857//>>>-- Nathaniel Gray -- Caltech Computer Science ------>
1858//>>>-- Mojave Project -- http://mojave.cs.caltech.edu -->
1859// Copied from http://www.nedit.org/archives/discuss/2002-Aug/0386.html
1860
1861// Helper function
1862bool getCardinal32Prop(Display *display, Window win, char *propName, long *value)
1863{
1864 Atom nameAtom, typeAtom, actual_type_return;
1865 int actual_format_return, result;
1866 unsigned long nitems_return, bytes_after_return;
1867 long *result_array=NULL;
1868
1869 nameAtom = XInternAtom(display, propName, False);
1870 typeAtom = XInternAtom(display, "CARDINAL", False);
1871 if (nameAtom == None || typeAtom == None) {
1872 //qDebug("Atoms not interned!");
1873 return false;
1874 }
1875
1876
1877 // Try to get the property
1878 result = XGetWindowProperty(display, win, nameAtom, 0, 1, False,
1879 typeAtom, &actual_type_return, &actual_format_return,
1880 &nitems_return, &bytes_after_return,
1881 (unsigned char **)&result_array);
1882
1883 if( result != Success ) {
1884 //qDebug("not Success");
1885 return false;
1886 }
1887 if( actual_type_return == None || actual_format_return == 0 ) {
1888 //qDebug("Prop not found");
1889 return false;
1890 }
1891 if( actual_type_return != typeAtom ) {
1892 //qDebug("Wrong type atom");
1893 }
1894 *value = result_array[0];
1895 XFree(result_array);
1896 return true;
1897}
1898
1899
1900// Get the desktop number that a window is on
1901bool desktopOfWindow(Window *window, long *desktop)
1902{
1903 Display *display = qt_xdisplay();
1904 bool result = getCardinal32Prop(display, *window, (char *)"_NET_WM_DESKTOP", desktop);
1905 //if( result )
1906 // qDebug("Desktop: " + QString::number(*desktop));
1907 return result;
1908}
1909
1910
1911// Get the current desktop the WM is displaying
1912bool currentDesktop(long *desktop)
1913{
1914 Window rootWin;
1915 Display *display = qt_xdisplay();;
1916 bool result;
1917
1918 rootWin = RootWindow(qt_xdisplay(), XDefaultScreen(qt_xdisplay()));
1919 result = getCardinal32Prop(display, rootWin, (char *)"_NET_CURRENT_DESKTOP", desktop);
1920 //if( result )
1921 // qDebug("Current Desktop: " + QString::number(*desktop));
1922 return result;
1923}
1924#endif
1925
1926void bringToFront(QWidget *w, bool grabFocus /* = true */)
1927{
1928#if defined(Q_WS_X11)
1929 // If we're not on the current desktop, do the hide/show trick
1930 long dsk, curr_dsk;
1931 Window win = w->winId();
1932 if(desktopOfWindow(&win, &dsk) && currentDesktop(&curr_dsk)) {
1933 if(dsk != curr_dsk) {
1934 w->hide();
1935 //qApp->processEvents();
1936 }
1937 }
1938#else
1939 // FIXME: multi-desktop hacks for Win and Mac required
1940#endif
1941
1942 w->show();
1943 if(w->isMinimized()) {
1944 //w->hide();
1945 if(w->isMaximized())
1946 w->showMaximized();
1947 else
1948 w->showNormal();
1949 }
1950
1951#ifdef Q_WS_WIN
1952 w->raise();
1953 if (grabFocus)
1954 w->setActiveWindow();
1955#else
1956 if(grabFocus)
1957 w->setActiveWindow();
1958 else
1959 w->raise();
1960#endif
1961
1962#if defined(Q_WS_PM)
1963 // When the widget is totally outside the screen bounds after making it shown,
1964 // we assume it's on a different (invisible) XPager's page. The assumption is
1965 // not so safe, but is there any other way to determine XPager is in action?..
1966 QRect geo = w->frameGeometry();
1967 QRect scrGeo = qApp->desktop()->screenGeometry();
1968 if (!w->isMinimized() && !scrGeo.contains(geo)) {
1969 // We assume that XPager stores invisible pages in the coordinate space
1970 // beyond (0,0)x(screen_width,screen_height) wiht a 8 px gap between
1971 // pages. Move it to the visible page.
1972 int sw = scrGeo.width() + 8;
1973 int sh = scrGeo.height() + 8;
1974 int x = geo.x() % sw;
1975 if (x < 0) x = sw + x;
1976 int y = geo.y() % sh;
1977 if (y < 0) y = sh + y;
1978 w->move (x, y);
1979 }
1980#endif
1981}
1982
1983bool operator!=(const QMap<QString, QString> &m1, const QMap<QString, QString> &m2)
1984{
1985 if ( m1.size() != m2.size() )
1986 return true;
1987
1988 QMap<QString, QString>::ConstIterator it = m1.begin(), it2;
1989 for ( ; it != m1.end(); ++it) {
1990 it2 = m2.find( it.key() );
1991 if ( it2 == m2.end() )
1992 return true;
1993 if ( it.data() != it2.data() )
1994 return true;
1995 }
1996
1997 return false;
1998}
1999
Note: See TracBrowser for help on using the repository browser.