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

Last change on this file since 49 was 48, checked in by dmik, 19 years ago

Psi: General: bringToFront() can now move a window from an inactive (invisible) XPager's page to the current (visible) one (using a bit hackish trick).

File size: 41.4 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
983void openURL(const QString &url)
984{
985 //fprintf(stderr, "openURL: [%s]\n", url.latin1());
986 bool useCustom = TRUE;
987
988 int colon = url.find(':');
989 if ( colon == -1 )
990 colon = 0;
991 QString service = url.left( colon );
992 if ( service == "jabber" || service == "jid" ) {
993 // TODO
994 return;
995 }
996
997#ifdef Q_WS_WIN
998 if(option.browser == 0)
999 useCustom = FALSE;
1000#endif
1001#ifdef Q_WS_PM
1002 /// @todo (dmik) use HINI_USER_PROFILE\WPURLDEFAULTSETTINGS\DefaultBrowserExe
1003#endif
1004#ifdef Q_WS_X11
1005 if(option.browser == 0 || option.browser == 2)
1006 useCustom = FALSE;
1007#endif
1008#ifdef Q_WS_MAC
1009 useCustom = FALSE;
1010#endif
1011
1012 if(useCustom) {
1013 bool isMail = FALSE;
1014 QString s = url;
1015 if(url.left(7) == "mailto:") {
1016 s.remove(0, 7);
1017 isMail = TRUE;
1018 }
1019
1020 QStringList args;
1021
1022 if(isMail) {
1023 if(option.customMailer.isEmpty()) {
1024 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a mailer (see Options)."));
1025 return;
1026 }
1027 args += QStringList::split(' ', option.customMailer);
1028 }
1029 else {
1030 if(option.customBrowser.isEmpty()) {
1031 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a browser (see Options)."));
1032 return;
1033 }
1034 args += QStringList::split(' ', option.customBrowser);
1035 }
1036
1037 args += s;
1038 QProcess cmd(args);
1039 if(!cmd.start()) {
1040 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that your custom browser/mailer exists (see Options)."));
1041 }
1042 }
1043 else {
1044#ifdef Q_WS_WIN
1045 QCString cs = url.local8Bit();
1046 if ((unsigned int)::ShellExecuteA(NULL,NULL,cs.data(),NULL,NULL,SW_SHOW) <= 32) {
1047 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have a web browser installed."));
1048 }
1049#endif
1050#ifdef Q_WS_PM
1051 /// @todo (dmik) use HINI_USER_PROFILE\WPURLDEFAULTSETTINGS\DefaultBrowserExe
1052#endif
1053#ifdef Q_WS_X11
1054 // KDE
1055 if(option.browser == 0) {
1056 QStringList args;
1057 args += "kfmclient";
1058 args += "exec";
1059 args += url;
1060 QProcess cmd(args);
1061 if(!cmd.start()) {
1062 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have KDE installed."));
1063 }
1064 }
1065 // GNOME 2
1066 else if(option.browser == 2) {
1067 QStringList args;
1068 args += "gnome-open";
1069 args += url;
1070 QProcess cmd(args);
1071 if(!cmd.start()) {
1072 QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have GNOME 2 installed."));
1073 }
1074 }
1075#endif
1076#ifdef Q_WS_MAC
1077 // Use Internet Config to hand the URL to the appropriate application, as
1078 // set by the user in the Internet Preferences pane.
1079 // NOTE: ICStart could be called once at Psi startup, saving the
1080 // ICInstance in a global variable, as a minor optimization.
1081 // ICStop should then be called at Psi shutdown if ICStart succeeded.
1082 ICInstance icInstance;
1083 OSType psiSignature = 'psi ';
1084 OSStatus error = ::ICStart( &icInstance, psiSignature );
1085 if ( error == noErr ) {
1086 ConstStr255Param hint( 0x0 );
1087 QCString cs = url.local8Bit();
1088 const char* data = cs.data();
1089 long length = cs.length();
1090 long start( 0 );
1091 long end( length );
1092 // Don't bother testing return value (error); launched application will report problems.
1093 ::ICLaunchURL( icInstance, hint, data, length, &start, &end );
1094 ICStop( icInstance );
1095 }
1096#endif
1097 }
1098}
1099
1100static bool sysinfo_done = FALSE;
1101static int timezone_offset = 0;
1102static QString timezone_str = "N/A";
1103static QString os_str = "Unknown";
1104
1105#if defined(Q_WS_X11) || defined(Q_WS_MAC)
1106#include<time.h>
1107#include<stdlib.h>
1108#include<string.h>
1109#include<sys/utsname.h>
1110#endif
1111
1112static void getSysInfo()
1113{
1114#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_PM)
1115 /// @todo (dmik) test it
1116 time_t x;
1117 time(&x);
1118 char str[256];
1119 char fmt[32];
1120 strcpy(fmt, "%z");
1121 strftime(str, 256, fmt, localtime(&x));
1122 if(strcmp(fmt, str)) {
1123 QString s = str;
1124 if(s.at(0) == '+')
1125 s.remove(0,1);
1126 s.truncate(s.length()-2);
1127 timezone_offset = s.toInt();
1128 }
1129 strcpy(fmt, "%Z");
1130 strftime(str, 256, fmt, localtime(&x));
1131 if(strcmp(fmt, str))
1132 timezone_str = str;
1133#endif
1134#if defined(Q_WS_X11)
1135 struct utsname u;
1136 uname(&u);
1137 os_str.sprintf("%s", u.sysname);
1138
1139 // get description about os
1140 enum LinuxName {
1141 LinuxNone = 0,
1142
1143 LinuxMandrake,
1144 LinuxDebian,
1145 LinuxRedHat,
1146 LinuxGentoo,
1147 LinuxSlackware,
1148 LinuxSuSE,
1149 LinuxConectiva,
1150 LinuxCaldera,
1151 LinuxLFS,
1152
1153 LinuxASP, // Russian Linux distros
1154 LinuxALT,
1155
1156 LinuxPLD, // Polish Linux distros
1157 LinuxAurox,
1158 LinuxArch
1159 };
1160
1161 enum OsFlags {
1162 OsUseName = 0,
1163 OsUseFile,
1164 OsAppendFile
1165 };
1166
1167 struct OsInfo {
1168 LinuxName id;
1169 OsFlags flags;
1170 QString file;
1171 QString name;
1172 } osInfo[] = {
1173 { LinuxMandrake, OsUseFile, "/etc/mandrake-release", "Mandrake Linux" },
1174 { LinuxDebian, OsAppendFile, "/etc/debian_version", "Debian GNU/Linux" },
1175 { LinuxGentoo, OsUseFile, "/etc/gentoo-release", "Gentoo Linux" },
1176 { LinuxSlackware, OsAppendFile, "/etc/slackware-version", "Slackware Linux" },
1177 { LinuxPLD, OsUseFile, "/etc/pld-release", "PLD Linux" },
1178 { LinuxAurox, OsUseName, "/etc/aurox-release", "Aurox Linux" },
1179 { LinuxArch, OsUseFile, "/etc/arch-release", "Arch Linux" },
1180 { LinuxLFS, OsAppendFile, "/etc/lfs-release", "LFS Linux" },
1181
1182 // untested
1183 { LinuxSuSE, OsUseFile, "/etc/SuSE-release", "SuSE Linux" },
1184 { LinuxConectiva, OsUseFile, "/etc/conectiva-release", "Conectiva Linux" },
1185 { LinuxCaldera, OsUseFile, "/etc/.installed", "Caldera Linux" },
1186
1187 // many distros use the /etc/redhat-release for compatibility, so RedHat will be the last :)
1188 { LinuxRedHat, OsUseFile, "/etc/redhat-release", "RedHat Linux" },
1189
1190 { LinuxNone, OsUseName, "", "" }
1191 };
1192
1193 for (int i = 0; osInfo[i].id != LinuxNone; i++) {
1194 QFileInfo fi( osInfo[i].file );
1195 if ( fi.exists() ) {
1196 QString desc;
1197
1198 QFile f( osInfo[i].file );
1199 f.open( IO_ReadOnly );
1200 f.readLine( desc, 128 );
1201
1202 desc = desc.stripWhiteSpace ();
1203
1204 switch ( osInfo[i].flags ) {
1205 case OsUseFile:
1206 os_str = desc;
1207 break;
1208 case OsUseName:
1209 os_str = osInfo[i].name;
1210 break;
1211 case OsAppendFile:
1212 os_str = osInfo[i].name + " (" + desc + ")";
1213 break;
1214 }
1215
1216 break;
1217 }
1218 }
1219#elif defined(Q_WS_PM)
1220 os_str = "OS/2";
1221#elif defined(Q_WS_MAC)
1222 os_str = "Mac OS X";
1223#endif
1224
1225#if defined(Q_WS_WIN)
1226 TIME_ZONE_INFORMATION i;
1227 //GetTimeZoneInformation(&i);
1228 //timezone_offset = (-i.Bias) / 60;
1229 memset(&i, 0, sizeof(i));
1230 bool inDST = (GetTimeZoneInformation(&i) == TIME_ZONE_ID_DAYLIGHT);
1231 int bias = i.Bias;
1232 if(inDST)
1233 bias += i.DaylightBias;
1234 timezone_offset = (-bias) / 60;
1235 timezone_str = "";
1236 for(int n = 0; n < 32; ++n) {
1237 uint w = inDST ? i.DaylightName[n] : i.StandardName[n];
1238 if(w == 0)
1239 break;
1240 timezone_str += QChar(w);
1241 }
1242
1243 Qt::WindowsVersion v = QApplication::winVersion();
1244 if(v == Qt::WV_95)
1245 os_str = "Windows 95";
1246 else if(v == Qt::WV_98)
1247 os_str = "Windows 98";
1248 else if(v == Qt::WV_Me)
1249 os_str = "Windows Me";
1250 else if(v == Qt::WV_DOS_based)
1251 os_str = "Windows 9x/Me";
1252 else if(v == Qt::WV_NT)
1253 os_str = "Windows NT 4.x";
1254 else if(v == Qt::WV_2000)
1255 os_str = "Windows 2000";
1256 else if(v == Qt::WV_XP)
1257 os_str = "Windows XP";
1258#if QT_VERSION >= 0x030300
1259 else if(v == Qt::WV_2003)
1260 os_str = "Windows Server 2003";
1261#endif
1262 else if(v == Qt::WV_NT_based)
1263 os_str = "Windows NT";
1264#endif
1265 sysinfo_done = TRUE;
1266}
1267
1268QString getOSName()
1269{
1270 if(!sysinfo_done)
1271 getSysInfo();
1272
1273 return os_str;
1274}
1275
1276int getTZOffset()
1277{
1278 if(!sysinfo_done)
1279 getSysInfo();
1280
1281 return timezone_offset;
1282}
1283
1284QString getTZString()
1285{
1286 if(!sysinfo_done)
1287 getSysInfo();
1288
1289 return timezone_str;
1290}
1291
1292
1293#ifdef Q_WS_X11
1294QString getResourcesDir()
1295{
1296 return PSI_DATADIR;
1297}
1298
1299QString getHomeDir()
1300{
1301 QDir proghome(QDir::homeDirPath() + "/.psi");
1302 if(!proghome.exists()) {
1303 QDir home = QDir::home();
1304 home.mkdir(".psi");
1305 chmod(QFile::encodeName(proghome.path()), 0700);
1306 }
1307
1308 return proghome.path();
1309}
1310#endif
1311
1312#ifdef Q_WS_WIN
1313QString getResourcesDir()
1314{
1315#if QT_VERSION >= 0x030200
1316 return qApp->applicationDirPath();
1317#else
1318 char baseName[MAX_PATH];
1319 GetModuleFileNameA(GetModuleHandle(NULL), baseName, MAX_PATH);
1320 QString base(baseName);
1321 int idx = base.findRev('\\');
1322
1323 if (-1 == idx)
1324 return ".";
1325
1326 base.truncate(idx);
1327 QDir baseDir(base);
1328
1329 return baseDir.absPath();
1330#endif
1331}
1332
1333QString getHomeDir()
1334{
1335 QString base;
1336
1337 // Windows 9x
1338 if(QDir::homeDirPath() == QDir::rootDirPath())
1339 base = ".";
1340 // Windows NT/2K/XP variant
1341 else
1342 base = QDir::homeDirPath();
1343
1344 // no trailing slash
1345 if(base.at(base.length()-1) == '/')
1346 base.truncate(base.length()-1);
1347
1348 QDir proghome(base + "/PsiData");
1349 if(!proghome.exists()) {
1350 QDir home(base);
1351 home.mkdir("PsiData");
1352 }
1353
1354 return proghome.path();
1355}
1356#endif
1357
1358#ifdef Q_WS_PM
1359QString getResourcesDir()
1360{
1361#if QT_VERSION >= 0x030200
1362 return qApp->applicationDirPath();
1363#else
1364# error Not implemented!
1365#endif
1366}
1367
1368QString getHomeDir()
1369{
1370 QString base = QDir::homeDirPath();
1371
1372 // no trailing slash
1373 if(base.at(base.length()-1) == '/')
1374 base.truncate(base.length()-1);
1375
1376 QDir proghome(base + "/PsiData");
1377 if(!proghome.exists()) {
1378 QDir home(base);
1379 home.mkdir("PsiData");
1380 }
1381
1382 return proghome.path();
1383}
1384#endif
1385
1386#ifdef Q_WS_MAC
1387/******************************************************************************/
1388/* Get path to Resources directory as a string. */
1389/* Return an empty string if can't find it. */
1390/******************************************************************************/
1391QString getResourcesDir()
1392{
1393 // System routine locates resource files. We "know" that Psi.icns is
1394 // in the Resources directory.
1395 QString resourcePath;
1396 CFBundleRef mainBundle = CFBundleGetMainBundle();
1397 CFStringRef resourceCFStringRef
1398 = CFStringCreateWithCString( NULL, "application.icns",
1399 kCFStringEncodingASCII );
1400 CFURLRef resourceURLRef = CFBundleCopyResourceURL( mainBundle,
1401 resourceCFStringRef,
1402 NULL,
1403 NULL );
1404 if ( resourceURLRef ) {
1405 CFStringRef resourcePathStringRef =
1406 CFURLCopyFileSystemPath( resourceURLRef, kCFURLPOSIXPathStyle );
1407 const char* resourcePathCString =
1408 CFStringGetCStringPtr( resourcePathStringRef, kCFStringEncodingASCII );
1409 if ( resourcePathCString ) {
1410 resourcePath.setLatin1( resourcePathCString );
1411 } else { // CFStringGetCStringPtr failed; use fallback conversion
1412 CFIndex bufferLength = CFStringGetLength( resourcePathStringRef ) + 1;
1413 char* resourcePathCString = new char[ bufferLength ];
1414 Boolean conversionSuccess =
1415 CFStringGetCString( resourcePathStringRef,
1416 resourcePathCString, bufferLength,
1417 kCFStringEncodingASCII );
1418 if ( conversionSuccess ) {
1419 resourcePath = resourcePathCString;
1420 }
1421 delete [] resourcePathCString; // I own this
1422 }
1423 CFRelease( resourcePathStringRef ); // I own this
1424 }
1425 // Remove the tail component of the path
1426 if ( ! resourcePath.isNull() ) {
1427 QFileInfo fileInfo( resourcePath );
1428 resourcePath = fileInfo.dirPath( true );
1429 }
1430 return resourcePath;
1431}
1432
1433QString getHomeDir()
1434{
1435 QDir proghome(QDir::homeDirPath() + "/.psi");
1436 if(!proghome.exists()) {
1437 QDir home = QDir::home();
1438 home.mkdir(".psi");
1439 chmod(QFile::encodeName(proghome.path()), 0700);
1440 }
1441
1442 return proghome.path();
1443}
1444#endif
1445
1446
1447QString getHistoryDir()
1448{
1449 QDir history(pathToProfile(activeProfile) + "/history");
1450 if (!history.exists()) {
1451 QDir profile(pathToProfile(activeProfile));
1452 profile.mkdir("history");
1453 }
1454
1455 return history.path();
1456}
1457
1458QString getVCardDir()
1459{
1460 QDir vcard(pathToProfile(activeProfile) + "/vcard");
1461 if (!vcard.exists()) {
1462 QDir profile(pathToProfile(activeProfile));
1463 profile.mkdir("vcard");
1464 }
1465
1466 return vcard.path();
1467}
1468
1469bool fileCopy(const QString &src, const QString &dest)
1470{
1471 QFile in(src);
1472 QFile out(dest);
1473
1474 if(!in.open(IO_ReadOnly))
1475 return FALSE;
1476 if(!out.open(IO_WriteOnly))
1477 return FALSE;
1478
1479 char *dat = new char[16384];
1480 int n = 0;
1481 while(!in.atEnd()) {
1482 n = in.readBlock(dat, 16384);
1483 if(n == -1) {
1484 delete dat;
1485 return FALSE;
1486 }
1487 out.writeBlock(dat, n);
1488 }
1489 delete dat;
1490
1491 out.close();
1492 in.close();
1493
1494 return TRUE;
1495}
1496
1497
1498void soundPlay(const QString &str)
1499{
1500 if(str == "!beep") {
1501 QApplication::beep();
1502 return;
1503 }
1504
1505 if(!QFile::exists(str))
1506 return;
1507
1508#if defined(Q_WS_WIN) || defined(Q_WS_PM) || defined(Q_WS_MAC)
1509 QSound::play(str);
1510#else
1511 if(!option.player.isEmpty()) {
1512 QStringList args;
1513 args = QStringList::split(' ', option.player);
1514 args += str;
1515 QProcess cmd(args);
1516 if(!cmd.start())
1517 wait3(NULL,WNOHANG,NULL);
1518 }
1519#endif
1520}
1521
1522XMPP::Status makeStatus(int x, const QString &str)
1523{
1524 XMPP::Status s;
1525 s.setStatus(str);
1526 if(x == STATUS_OFFLINE)
1527 s.setIsAvailable(false);
1528 else if(x == STATUS_INVISIBLE)
1529 s.setIsInvisible(true);
1530 else {
1531 if(x == STATUS_AWAY)
1532 s.setShow("away");
1533 else if(x == STATUS_XA)
1534 s.setShow("xa");
1535 else if(x == STATUS_DND)
1536 s.setShow("dnd");
1537 else if(x == STATUS_CHAT)
1538 s.setShow("chat");
1539 }
1540
1541 return s;
1542}
1543
1544int makeSTATUS(const XMPP::Status &s)
1545{
1546 int type = STATUS_ONLINE;
1547 if(!s.isAvailable())
1548 type = STATUS_OFFLINE;
1549 else if(s.isInvisible())
1550 type= STATUS_INVISIBLE;
1551 else {
1552 if(s.show() == "away")
1553 type = STATUS_AWAY;
1554 else if(s.show() == "xa")
1555 type = STATUS_XA;
1556 else if(s.show() == "dnd")
1557 type = STATUS_DND;
1558 else if(s.show() == "chat")
1559 type = STATUS_CHAT;
1560 }
1561 return type;
1562}
1563
1564#include<qlayout.h>
1565QLayout *rw_recurseFindLayout(QLayout *lo, QWidget *w)
1566{
1567 //printf("scanning layout: %p\n", lo);
1568 QLayoutIterator it = lo->iterator();
1569 for(QLayoutItem *i; (i = it.current()); ++it) {
1570 //printf("found: %p,%p\n", i->layout(), i->widget());
1571 QLayout *slo = i->layout();
1572 if(slo) {
1573 QLayout *tlo = rw_recurseFindLayout(slo, w);
1574 if(tlo)
1575 return tlo;
1576 }
1577 else if(i->widget() == w)
1578 return lo;
1579 }
1580 return 0;
1581}
1582
1583QLayout *rw_findLayoutOf(QWidget *w)
1584{
1585 return rw_recurseFindLayout(w->parentWidget()->layout(), w);
1586}
1587
1588void replaceWidget(QWidget *a, QWidget *b)
1589{
1590 if(!a)
1591 return;
1592
1593 QLayout *lo = rw_findLayoutOf(a);
1594 if(!lo)
1595 return;
1596 //printf("decided on this: %p\n", lo);
1597
1598 if(lo->inherits("QBoxLayout")) {
1599 QBoxLayout *bo = (QBoxLayout *)lo;
1600 int n = bo->findWidget(a);
1601 bo->insertWidget(n+1, b);
1602 delete a;
1603 }
1604}
1605
1606void closeDialogs(QWidget *w)
1607{
1608 // close qmessagebox?
1609 const QObjectList *list = w->children();
1610 if(list) {
1611 QObjectListIt it(*list);
1612 QPtrList<QDialog> dialogs;
1613 for(QObject *o; (o = it.current()); ++it) {
1614 if(o->inherits("QDialog"))
1615 dialogs.append((QDialog *)o);
1616 }
1617 QPtrListIterator<QDialog> dit(dialogs);
1618 for(QDialog *w; (w = dit.current()); ++dit)
1619 w->close();
1620 }
1621}
1622
1623QString enc822jid(const QString &s)
1624{
1625 QString out;
1626 for(int n = 0; n < (int)s.length(); ++n) {
1627 if(s[n] == '\\' || s[n] == '<' || s[n] == '>') {
1628 QString hex;
1629 hex.sprintf("\\x%02X", (unsigned char )s[n]);
1630 out.append(hex);
1631 }
1632 else
1633 out += s[n];
1634 }
1635 return out;
1636}
1637
1638QString dec822jid(const QString &s)
1639{
1640 QString out;
1641 for(int n = 0; n < (int)s.length(); ++n) {
1642 if(s[n] == '\\' && n + 3 < (int)s.length()) {
1643 int x = n + 1;
1644 n += 3;
1645 if(s[x] != 'x')
1646 continue;
1647 ushort val = 0;
1648 val += hexChar2int(s[x+1])*16;
1649 val += hexChar2int(s[x+2]);
1650 QChar c(val);
1651 out += c;
1652 }
1653 else
1654 out += s[n];
1655 }
1656 return out;
1657}
1658
1659#ifdef Q_WS_X11
1660#include<X11/Xlib.h>
1661#include<X11/Xutil.h> // needed for WM_CLASS hinting
1662
1663void x11wmClass(Display *dsp, WId wid, QString resName)
1664{
1665 char app_name[] = "psi";
1666
1667 //Display *dsp = x11Display(); // get the display
1668 //WId win = winId(); // get the window
1669 XClassHint classhint; // class hints
1670 classhint.res_name = (char *)resName.latin1(); // res_name
1671 classhint.res_class = app_name; // res_class
1672 XSetClassHint(dsp, wid, &classhint); // set the class hints
1673}
1674
1675//>>>-- Nathaniel Gray -- Caltech Computer Science ------>
1676//>>>-- Mojave Project -- http://mojave.cs.caltech.edu -->
1677// Copied from http://www.nedit.org/archives/discuss/2002-Aug/0386.html
1678
1679// Helper function
1680bool getCardinal32Prop(Display *display, Window win, char *propName, long *value)
1681{
1682 Atom nameAtom, typeAtom, actual_type_return;
1683 int actual_format_return, result;
1684 unsigned long nitems_return, bytes_after_return;
1685 long *result_array=NULL;
1686
1687 nameAtom = XInternAtom(display, propName, False);
1688 typeAtom = XInternAtom(display, "CARDINAL", False);
1689 if (nameAtom == None || typeAtom == None) {
1690 //qDebug("Atoms not interned!");
1691 return false;
1692 }
1693
1694
1695 // Try to get the property
1696 result = XGetWindowProperty(display, win, nameAtom, 0, 1, False,
1697 typeAtom, &actual_type_return, &actual_format_return,
1698 &nitems_return, &bytes_after_return,
1699 (unsigned char **)&result_array);
1700
1701 if( result != Success ) {
1702 //qDebug("not Success");
1703 return false;
1704 }
1705 if( actual_type_return == None || actual_format_return == 0 ) {
1706 //qDebug("Prop not found");
1707 return false;
1708 }
1709 if( actual_type_return != typeAtom ) {
1710 //qDebug("Wrong type atom");
1711 }
1712 *value = result_array[0];
1713 XFree(result_array);
1714 return true;
1715}
1716
1717
1718// Get the desktop number that a window is on
1719bool desktopOfWindow(Window *window, long *desktop)
1720{
1721 Display *display = qt_xdisplay();
1722 bool result = getCardinal32Prop(display, *window, (char *)"_NET_WM_DESKTOP", desktop);
1723 //if( result )
1724 // qDebug("Desktop: " + QString::number(*desktop));
1725 return result;
1726}
1727
1728
1729// Get the current desktop the WM is displaying
1730bool currentDesktop(long *desktop)
1731{
1732 Window rootWin;
1733 Display *display = qt_xdisplay();;
1734 bool result;
1735
1736 rootWin = RootWindow(qt_xdisplay(), XDefaultScreen(qt_xdisplay()));
1737 result = getCardinal32Prop(display, rootWin, (char *)"_NET_CURRENT_DESKTOP", desktop);
1738 //if( result )
1739 // qDebug("Current Desktop: " + QString::number(*desktop));
1740 return result;
1741}
1742#endif
1743
1744void bringToFront(QWidget *w, bool grabFocus /* = true */)
1745{
1746#if defined(Q_WS_X11)
1747 // If we're not on the current desktop, do the hide/show trick
1748 long dsk, curr_dsk;
1749 Window win = w->winId();
1750 if(desktopOfWindow(&win, &dsk) && currentDesktop(&curr_dsk)) {
1751 if(dsk != curr_dsk) {
1752 w->hide();
1753 //qApp->processEvents();
1754 }
1755 }
1756#else
1757 // FIXME: multi-desktop hacks for Win and Mac required
1758#endif
1759
1760 w->show();
1761 if(w->isMinimized()) {
1762 //w->hide();
1763 if(w->isMaximized())
1764 w->showMaximized();
1765 else
1766 w->showNormal();
1767 }
1768
1769#ifdef Q_WS_WIN
1770 w->raise();
1771 if (grabFocus)
1772 w->setActiveWindow();
1773#else
1774 if(grabFocus)
1775 w->setActiveWindow();
1776 else
1777 w->raise();
1778#endif
1779
1780#if defined(Q_WS_PM)
1781 // When the widget is totally outside the screen bounds after making it shown,
1782 // we assume it's on a different (invisible) XPager's page. The assumption is
1783 // not so safe, but is there any other way to determine XPager is in action?..
1784 QRect geo = w->frameGeometry();
1785 QRect scrGeo = qApp->desktop()->screenGeometry();
1786 if (!w->isMinimized() && !scrGeo.contains(geo)) {
1787 // We assume that XPager stores invisible pages in the coordinate space
1788 // beyond (0,0)x(screen_width,screen_height) wiht a 8 px gap between
1789 // pages. Move it to the visible page.
1790 int sw = scrGeo.width() + 8;
1791 int sh = scrGeo.height() + 8;
1792 int x = geo.x() % sw;
1793 if (x < 0) x = sw + x;
1794 int y = geo.y() % sh;
1795 if (y < 0) y = sh + y;
1796 w->move (x, y);
1797 }
1798#endif
1799}
1800
1801bool operator!=(const QMap<QString, QString> &m1, const QMap<QString, QString> &m2)
1802{
1803 if ( m1.size() != m2.size() )
1804 return true;
1805
1806 QMap<QString, QString>::ConstIterator it = m1.begin(), it2;
1807 for ( ; it != m1.end(); ++it) {
1808 it2 = m2.find( it.key() );
1809 if ( it2 == m2.end() )
1810 return true;
1811 if ( it.data() != it2.data() )
1812 return true;
1813 }
1814
1815 return false;
1816}
1817
Note: See TracBrowser for help on using the repository browser.