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

Last change on this file since 51 was 50, checked in by dmik, 19 years ago

Psi: Added recognition of the "PM Default Browser/Mail" setting for opening links (the HINI_USER_PROFILE\WPURLDEFAULTSETTINGS registry key is used for that purpose).

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