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

Last change on this file since 7 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

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