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

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

UI: Misc changes:

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