Fixed bug #40
[centerim.git] / src / icqface.cc
blobccea5dc69c03b9df5d756456e7748a6159bf338b
1 /*
3 * centerim user interface class
4 * $Id: icqface.cc,v 1.249 2005/08/28 01:33:21 konst Exp $
6 * Copyright (C) 2001-2004 by Konstantin Klyagin <k@thekonst.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
25 #include <stdint.h> /* for intptr_t */
27 #include "icqface.h"
28 #include "icqconf.h"
29 #include "centerim.h"
30 #include "icqcontact.h"
31 #include "icqcontacts.h"
32 #include "icqmlist.h"
33 #include "icqgroups.h"
34 #include "abstracthook.h"
35 #include "imlogger.h"
36 #include "irchook.h"
37 #include "imexternal.h"
38 #include "impgp.h"
39 #ifdef HAVE_LIBOTR
40 #include "imotr.h"
41 #endif
43 const char *stryesno(bool b) {
44 return b ? _("yes") : _("no");
47 const char *strgender(imgender g) {
48 switch(g) {
49 case genderMale: return _("Male");
50 case genderFemale: return _("Female");
53 return _("Not specified");
56 const char *geteventviewresult(icqface::eventviewresult r) {
57 switch(r) {
58 case icqface::ok: return _("Ok");
59 case icqface::next: return _("Next");
60 case icqface::forward: return _("Fwd");
61 case icqface::reply: return _("Reply");
62 case icqface::open: return _("Open");
63 case icqface::accept: return _("Accept");
64 case icqface::reject: return _("Reject");
65 case icqface::info: return _("User info");
66 case icqface::add: return _("Add");
67 case icqface::prev: return _("Prev");
70 return "";
73 const char *strregsound(icqconf::regsound s) {
74 switch(s) {
75 case icqconf::rscard: return _("sound card");
76 case icqconf::rsspeaker: return _("speaker");
77 case icqconf::rsdisable: return _("disable");
80 return _("don't change");
83 const char *strregcolor(icqconf::regcolor c) {
84 switch(c) {
85 case icqconf::rcdark: return _("dark");
86 case icqconf::rcblue: return _("blue");
89 return _("don't change");
92 const char *strcolormode(icqconf::colormode cm) {
93 switch(cm) {
94 case icqconf::cmproto : return _("protocol");
95 case icqconf::cmstatus : return _("status");
98 return _("don't change");
101 const char *strint(unsigned int i) {
102 static char buf[64];
104 if(i) {
105 snprintf(buf, sizeof(buf), "%lu", i);
106 } else {
107 buf[0] = 0;
110 return buf;
113 const char *strgroupmode(icqconf::groupmode gmode) {
114 switch(gmode) {
115 case icqconf::group1: return _("mode 1");
116 case icqconf::group2: return _("mode 2");
119 return _("no");
122 icqface::icqface() {
123 workareas.freeitem = &freeworkareabuf;
125 mainscreenblock = inited = onlinefolder = dotermresize =
126 inchat = doredraw = false;
128 mcontacts = 0;
131 icqface::~icqface() {
132 kendinterface();
134 if(inited) {
135 for(int i = 0; i < LINES; i++) printf("\n");
138 if(conf.getdebug())
139 if(flog.is_open())
140 flog.close();
143 void icqface::init() {
144 if(conf.getdebug())
145 if(!flog.is_open()) {
146 time_t logtime = time(0);
148 flog.open((conf.getdirname() + "debug").c_str(), ios::app);
149 if(flog.is_open())
150 flog << endl << "-- centerim debug log started on " << ctime(&logtime);
153 /* Calculate various sizes and coordinates */
155 sizeDlg.width = COLS-20;
156 sizeDlg.height = LINES-10;
158 sizeBigDlg.width = COLS-10;
159 sizeBigDlg.height = LINES-7;
161 sizeWArea.x1 = conf.getleftpanelwidth();
162 if(sizeWArea.x1 < MinPanelWidth || sizeWArea.x1 > (COLS - MinPanelWidth)) {
163 sizeWArea.x1 = (int) (COLS*0.32);
164 conf.setleftpanelwidth(sizeWArea.x1);
167 sizeWArea.x2 = COLS-1;
168 sizeWArea.y1 = 1;
170 sizeWArea.y2 = conf.getlogpanelheight();
171 if(sizeWArea.y2 < MinPanelHeight || sizeWArea.y2 > (LINES - MinPanelHeight)) {
172 sizeWArea.y2 = (int) LINES / 4;
173 conf.setlogpanelheight(sizeWArea.y2);
175 sizeWArea.y2 = LINES - sizeWArea.y2;
177 if(!mcontacts) {
178 mcontacts = new treeview(conf.getcolor(cp_main_menu),
179 conf.getcolor(cp_main_selected),
180 conf.getcolor(cp_main_menu),
181 conf.getcolor(cp_main_menu));
184 mcontacts->setcoords(1, 2, sizeWArea.x1, LINES-2);
185 mcontacts->menu.idle = &menuidle;
186 mcontacts->menu.otherkeys = &contactskeys;
188 input.setcolor(conf.getcolor(cp_status));
189 input.idle = &textinputidle;
191 mainw = textwindow(0, 1, COLS-1, LINES-2, conf.getcolor(cp_main_frame));
193 selector.setcolor(conf.getcolor(cp_dialog_menu),
194 conf.getcolor(cp_dialog_highlight),
195 conf.getcolor(cp_dialog_selected),
196 conf.getcolor(cp_dialog_frame));
198 mhist = verticalmenu(conf.getcolor(cp_main_text),
199 conf.getcolor(cp_main_selected));
201 attrset(conf.getcolor(cp_status));
202 mvhline(0, 0, ' ', COLS);
203 mvhline(LINES, 0, ' ', COLS);
205 inited = true;
206 kt_resize_event = &termresize;
209 void icqface::done() {
210 kt_resize_event = 0;
211 workareas.empty();
212 xtermtitlereset();
215 void icqface::draw() {
216 mainw.open();
217 mainw.separatex(sizeWArea.x1);
218 workarealine(sizeWArea.y2);
219 mvhline(sizeWArea.y2, sizeWArea.x1, LTEE, 1);
220 mvhline(sizeWArea.y2, sizeWArea.x2, RTEE, 1);
222 update();
225 void icqface::showtopbar() {
226 string buf;
227 protocolname pname;
228 icqconf::imaccount ia;
230 for(pname = icq; pname != protocolname_size; pname++) {
231 ia = conf.getourid(pname);
233 if(!ia.empty()) {
234 buf += " ";
235 buf += conf.getprotocolname(pname) + ":";
236 buf += imstatus2char[gethook(pname).getstatus()];
237 // buf += ">";
241 attrset(conf.getcolor(cp_status));
242 mvhline(0, 0, ' ', COLS);
243 mvprintw(0, 0, _(" CENTERIM %s UNSENT: %lu"), centerim::version, em.getunsentcount());
244 mvprintw(0, COLS-buf.size()-1, "%s", buf.c_str());
247 void icqface::update() {
248 showtopbar();
249 fillcontactlist();
250 fneedupdate = false;
251 if(doredraw) redraw();
254 int icqface::contextmenu(icqcontact *c) {
255 int ret = 0, i;
256 static int lastr = 0;
257 imcontact cont;
258 char buf[64];
260 if(!c) return 0;
262 verticalmenu m(conf.getcolor(cp_main_text),
263 conf.getcolor(cp_main_selected));
265 static map<int, string> actnames;
267 if(actnames.empty()) {
268 actnames[ACT_URL] = getmenuitem(_("Send an URL"), 25, key_send_url, section_contact);
269 actnames[ACT_SMS] = _(" Send an SMS");
270 actnames[ACT_CONTACT] = getmenuitem(_("Send contacts"), 25, key_send_contact, section_contact);
271 actnames[ACT_AUTH] = _(" Request authorization");
272 actnames[ACT_EDITUSER] = getmenuitem(_("Edit details"), 25, key_edit, section_contact);
273 actnames[ACT_FETCHAWAY] = getmenuitem(_("Fetch away message"), 25, key_fetch_away_message, section_contact);
274 actnames[ACT_ADD] = getmenuitem(_("Add to list"), 25, key_add, section_contact);
275 actnames[ACT_RENAME] = getmenuitem(_("Rename contact"), 25, key_rename, section_contact);
276 actnames[ACT_GROUPMOVE] = _(" Move to group..");
277 actnames[ACT_PING] = _(" Ping");
278 actnames[ACT_VERSION] = getmenuitem(_("Fetch version info"), 25, key_rename, section_contact);
279 actnames[ACT_FILE] = _(" Send file(s)");
280 actnames[ACT_CONFER] = _(" Invite to conference..");
283 if(ischannel(c)) {
284 m.setwindow(textwindow(sizeWArea.x1, sizeWArea.y1, sizeWArea.x1+33,
285 sizeWArea.y1+6, conf.getcolor(cp_main_text)));
287 actnames[ACT_MSG] = _(" Send a channel message enter");
288 actnames[ACT_HISTORY] = getmenuitem(_("Channel chat history"), 25, key_history, section_contact);
289 actnames[ACT_REMOVE] = _(" Remove channel del");
290 actnames[ACT_JOIN] = _(" Join channel");
291 actnames[ACT_LEAVE] = _(" Leave channel");
292 actnames[ACT_INFO] = getmenuitem(_("List nicknames"), 25, key_info, section_contact);
294 if(lst.inlist(c, csignore))
295 actnames[ACT_IGNORE] = getmenuitem(_("UnBlock channel messages"), 25, key_ignore, section_contact);
296 else
297 actnames[ACT_IGNORE] = getmenuitem(_("Block channel messages"), 25, key_ignore, section_contact);
299 } else {
300 m.setwindow(textwindow(sizeWArea.x1, sizeWArea.y1, sizeWArea.x1+27,
301 sizeWArea.y1+6, conf.getcolor(cp_main_text)));
303 if(c->getdesc().pname != rss)
304 actnames[ACT_MSG] = getmenuitem(_("Send a message"), 25, key_compose, section_contact);
306 actnames[ACT_HISTORY] = getmenuitem(_("Event history"), 25, key_history, section_contact);
307 actnames[ACT_REMOVE] = getmenuitem(_("Remove user"), 25, key_remove, section_contact);
308 actnames[ACT_INFO] = getmenuitem(_("User's details"), 25, key_info, section_contact);
309 actnames[ACT_EXTERN] = getmenuitem(_("External actions.."), 25, key_contact_external_action, section_contact);
311 actnames[ACT_IGNORE] = getmenuitem(lst.inlist(c, csignore) ?
312 _("Unset ignore user") : _("Ignore user"), 25, key_ignore,
313 section_contact);
315 actnames[ACT_PGPKEY] = c->getpgpkey().empty() ?
316 _(" Assign PGP key..") : _(" Unassign PGP key");
318 snprintf(buf, sizeof(buf), _(" Turn PGP encryption %s"), c->getusepgpkey() ? "OFF" : "ON");
319 actnames[ACT_PGPSWITCH] = buf;
321 if(c->getdesc().pname == rss)
322 actnames[ACT_PING] = getmenuitem(_("Force check"), 25,
323 key_rss_check, section_contact);
327 cont = c->getdesc();
329 set<hookcapab::enumeration> capab = gethook(cont.pname).getCapabs();
331 vector<int> actions;
332 vector<int>::const_iterator ia;
334 if(ischannel(cont)) {
335 actions.push_back(ACT_MSG);
336 actions.push_back(ACT_HISTORY);
337 actions.push_back(ACT_REMOVE);
338 actions.push_back(ACT_IGNORE);
340 if(gethook(cont.pname).logged()) {
341 actions.push_back(ACT_INFO);
342 actions.push_back((c->getstatus() != offline) ? ACT_LEAVE : ACT_JOIN);
345 if(conf.getgroupmode() != icqconf::nogroups)
346 actions.push_back(ACT_GROUPMOVE);
348 } else if(c->getdesc().pname != rss) {
349 if(cont.pname != infocard) actions.push_back(ACT_MSG);
350 if(capab.count(hookcapab::urls)) actions.push_back(ACT_URL);
351 if(!conf.getourid(icq).empty()) actions.push_back(ACT_SMS);
352 if(capab.count(hookcapab::contacts)) actions.push_back(ACT_CONTACT);
353 if(capab.count(hookcapab::authrequests)) actions.push_back(ACT_AUTH);
354 if(capab.count(hookcapab::files)) actions.push_back(ACT_FILE);
356 #ifdef HAVE_GPGME
357 if(capab.count(hookcapab::pgp)) {
358 actions.push_back(ACT_PGPKEY);
359 if(pgp.havekey(c->getpgpkey()))
360 actions.push_back(ACT_PGPSWITCH);
362 #endif
364 if(!actions.empty())
365 actions.push_back(0);
367 actions.push_back(ACT_INFO);
368 actions.push_back(ACT_EDITUSER);
370 if(c->getstatus() != offline) {
371 if(capab.count(hookcapab::fetchaway)) actions.push_back(ACT_FETCHAWAY);
372 if(capab.count(hookcapab::version)) actions.push_back(ACT_VERSION);
373 if(capab.count(hookcapab::ping)) actions.push_back(ACT_PING);
374 if(capab.count(hookcapab::conferencesaretemporary)) actions.push_back(ACT_CONFER);
377 actions.push_back(ACT_HISTORY);
378 actions.push_back(ACT_EXTERN);
380 actions.push_back(0);
381 actions.push_back(ACT_IGNORE);
382 if(!c->inlist()) actions.push_back(ACT_ADD);
384 actions.push_back(ACT_REMOVE);
385 actions.push_back(ACT_RENAME);
387 if(conf.getgroupmode() != icqconf::nogroups && c->inlist())
388 actions.push_back(ACT_GROUPMOVE);
390 } else {
391 actions.push_back(ACT_HISTORY);
392 actions.push_back(ACT_REMOVE);
393 actions.push_back(ACT_IGNORE);
394 actions.push_back(ACT_INFO);
395 actions.push_back(ACT_EDITUSER);
396 actions.push_back(ACT_PING);
398 if(conf.getgroupmode() != icqconf::nogroups)
399 actions.push_back(ACT_GROUPMOVE);
403 for(ia = actions.begin(); ia != actions.end(); ++ia) {
404 if(*ia) {
405 m.additem(0, *ia, actnames[*ia].c_str());
406 if(*ia == lastr) m.setpos(m.getcount()-1);
407 } else {
408 m.addline();
412 m.scale();
413 m.idle = &menuidle;
414 i = (intptr_t) m.getref(m.open()-1);
415 m.close();
417 if(i) lastr = i;
418 return i;
421 int icqface::generalmenu() {
422 int i, r = 0;
423 static int lastitem = 0;
425 verticalmenu m(conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
426 m.setwindow(textwindow(sizeWArea.x1, sizeWArea.y1, sizeWArea.x1+40, sizeWArea.y1+11, conf.getcolor(cp_main_text)));
428 m.idle = &menuidle;
429 m.additem(0, ACT_STATUS, getmenuitem(_("Change status"), 38, key_change_status, section_contact));
430 m.additem(0, ACT_QUICKFIND, getmenuitem(_("Go to contact.."), 38, key_quickfind, section_contact));
431 m.additem(0, ACT_DETAILS, _(" Accounts.."));
432 m.additem(0, ACT_CONF, _(" CenterIM config options"));
433 #ifdef HAVE_LIBOTR
434 m.additem(0, ACT_OTR, _(" OTR options and fingerprints"));
435 #endif
436 m.additem(0, ACT_TRANSFERS, _(" File transfers monitor"));
437 m.addline();
438 m.additem(0, ACT_FIND, _(" Find/add users"));
439 m.additem(0, ACT_JOINDIALOG, _(" Join channel/conference"));
440 #ifdef BUILD_RSS
441 m.additem(0, ACT_RSS, _(" Link an RSS feed"));
442 #endif
443 m.addline();
444 m.additem(0, ACT_IGNORELIST, _(" View/edit ignore list"));
445 m.additem(0, ACT_INVISLIST, _(" View/edit invisible list"));
446 m.additem(0, ACT_VISIBLELIST, _(" View/edit visible list"));
447 m.addline();
448 if(conf.gethideoffline())
449 m.additem(0, ACT_HIDEOFFLINE, getmenuitem(_("Show offline users"), 38, key_hide_offline, section_contact));
450 else
451 m.additem(0, ACT_HIDEOFFLINE, getmenuitem(_("Hide offline users"), 38, key_hide_offline, section_contact));
453 if(conf.getgroupmode() != icqconf::nogroups) {
454 m.additem(0, ACT_ORG_GROUPS, _(" Organize contact groups"));
455 m.additem(0, ACT_MASS_MOVE, _(" Mass group move.."));
458 m.setpos(lastitem);
459 m.scale();
461 i = m.open();
462 m.close();
464 if(i) {
465 lastitem = i-1;
466 r = (intptr_t) m.getref(lastitem);
469 return r;
472 /* called to prepare the next chat contact, returns true if it found one, false otherwise
473 parameter is true if its next chat contact or false if is the previous
475 bool icqface::next_chat(bool next) {
476 find_next_action = (next ? 1:-1);
477 if (last_selected) last_selected->setopenedforchat(false);
478 return true;
481 icqcontact *icqface::find_next_chat() {
482 if (find_next_action == 0) return 0;
483 int i;
484 if (last_selected){
485 for(i = 0; i < clist.count && clist.at(i) != last_selected; i++)
487 i += find_next_action;
489 else i = find_next_action == 1 ? 0 : clist.count -1;
491 icqcontact *c;
492 for (; i < clist.count && i >= 0; i += find_next_action){
493 c = (icqcontact *) clist.at(i);
494 if (c->isopenedforchat() || c->hasevents()) {
495 find_next_action = 0;
496 extk = ACT_MSG;
497 return c;
501 find_next_action = 0;
502 return 0;
505 icqcontact *icqface::mainloop(int &action) {
506 int i, curid;
507 icqcontact *c = 0, *c1 = 0; icqgroup *g;
508 bool fin;
510 for(fin = false; !fin; ) {
511 extk = ACT_MSG;
513 /* Obtain the (icqcontact *) from the treeview. If a node is
514 selected, throw out the contact and obtain the correct (icqgroup *). */
516 if (!(c = c1 = find_next_chat())){ //check if next_chat was called from inside a chat window
517 last_selected = 0; // next_chat (if called) was called from contacts menu so there was no last selected contact
518 c = (icqcontact *) mcontacts->open(&i);
519 if ((c1 = find_next_chat())) c = c1; //check if next_chat was called from contacts menu
522 if(!c1 && c && mcontacts->isnode(i)) {
523 c = 0;
524 g = (icqgroup *) mcontacts->getref(mcontacts->getid(mcontacts->menu.getpos()));
525 } else {
526 g = 0;
529 if((intptr_t) c < 100) c = 0;
531 if(i) {
532 switch(action = extk) {
533 case ACT_GMENU:
534 if(!(action = generalmenu())) {
535 continue;
536 } else {
537 fin = true;
539 break;
542 if(c) {
543 if(action == ACT_MSG && !c->hasevents()) {
544 switch(c->getdesc().pname) {
545 case infocard: action = ACT_SMS; break;
546 case livejournal: action = ACT_LJ; break;
550 if(action == ACT_MENU)
551 if(!(action = contextmenu(c))) continue;
552 } else {
553 switch(action) {
554 case ACT_MSG:
555 curid = mcontacts->getid(mcontacts->menu.getpos());
557 /* kkconsui::treeview has collapsing built in, but when doing it
558 this way contacts with pending messages get moved to the top
559 even if their group is colapsed. */
561 if(mcontacts->isnodeopen(curid)) {
562 mcontacts->closenode(curid);
563 } else {
564 mcontacts->opennode(curid);
567 /* Handling of collapse events should happen in centerim::
568 mainloop, but as it stands this method doesn't handle
569 icqgroups, only icqcontacts, so we'll deal with collapsing
570 here. */
572 if(g) {
573 g->changecollapsed();
574 update();
576 break;
577 case ACT_QUIT:
578 case ACT_STATUS:
579 case ACT_FIND:
580 case ACT_QUICKFIND:
581 case ACT_HIDEOFFLINE:
582 break;
583 default:
584 continue;
588 break;
591 if (c) last_selected = c;
592 return c;
596 #define ADDGROUP(nn) { \
597 if(groupchange && !strchr("!", sc)) \
598 ngroup = mcontacts->addnode(nn, conf.getcolor(cp_main_highlight), \
599 g, " " + g->getname() + " " \
600 + (g->iscollapsed() ? "(" + string(i2str(g->getcount(c->getstatus() != offline, !conf.gethideoffline() && !(c->getstatus() != offline && conf.getgroupmode() == icqconf::group2)))) + ") " : "")); \
603 void icqface::fillcontactlist() {
604 int i, id, nnode, ngroup, prevgid, n, nonline;
605 string dnick;
606 icqcontact *c;
607 void *savec;
608 char prevsc = 'x', sc;
609 icqgroup *g = NULL;
610 vector<icqgroup>::iterator ig;
612 bool online_added, prevoffline, grouponline, groupchange,
613 ontop, iscurrentnode, birthday;
615 time_t timer = time(0);
617 grouponline = true;
618 online_added = prevoffline = false;
619 nnode = ngroup = prevgid = 0;
621 iscurrentnode = mcontacts->isnode(mcontacts->getid(mcontacts->menu.getpos()));
622 ontop = !mcontacts->menu.getpos() && iscurrentnode;
624 savec = mcontacts->getref(mcontacts->getid(mcontacts->menu.getpos()));
626 int ccolor = conf.getcolor(cp_main_text);
628 if(!iscurrentnode && savec) {
629 ig = find(groups.begin(), groups.end(), ((icqcontact *) savec)->getgroupid());
631 if(ig != groups.end()) {
632 g = &(*ig);
633 if(g->iscollapsed() && !((icqcontact *) savec)->hasevents())
634 savec = g;
638 /* if savec is a group, we need to figure out of it's the top or bottom
639 one when we're in groupmode mode 2. */
641 if(savec && iscurrentnode && conf.getgroupmode() == icqconf::group2) {
642 for(i = 0; i < mcontacts->getcount(); i++) {
643 if(mcontacts->getref(mcontacts->getid(i)) == savec) {
644 if(mcontacts->getid(i) != mcontacts->getid(mcontacts->menu.getpos()))
645 grouponline = false;
646 break;
651 mcontacts->clear();
652 clist.order();
654 for(i = 0; i < clist.count; i++) {
655 c = (icqcontact *) clist.at(i);
657 ig = find(groups.begin(), groups.end(), c->getgroupid());
658 if(ig != groups.end()) {
659 g = &(*ig);
662 if(c->getdesc() == contactroot)
663 continue;
665 c->remindbirthday(birthday = c->isbirthday());
667 abstracthook &h = gethook(c->getdesc().pname);
669 if(c->getstatus() == offline)
670 if(conf.gethideoffline())
671 if(!c->hasevents())
672 if(c->inlist() || !h.logged() || h.getCapabs().count(hookcapab::cltemporary)) {
673 continue;
676 sc = icqcontacts::sort_order->sortstatus(*c);
678 groupchange =
679 (conf.getgroupmode() != icqconf::nogroups) &&
680 ((c->getgroupid() != prevgid) ||
681 ((conf.getgroupmode() == icqconf::group2) &&
682 (prevoffline == false) && (c->getstatus() == offline))) &&
683 (sc != '#');
685 if(conf.getgroupmode() == icqconf::group1)
686 ADDGROUP(0);
688 if(groupchange || (sc != '#')) {
689 if(strchr("candilfo", sc)) sc = 'O';
691 if((sc != prevsc) || groupchange) {
692 switch(conf.getgroupmode()) {
693 case icqconf::group1:
694 n = ngroup;
695 break;
696 case icqconf::group2:
697 if(prevsc == sc) {
698 n = -1;
699 } else {
700 n = 0;
702 break;
703 default:
704 n = 0;
705 break;
708 /* Hide 'offline' and 'online' items when in group mode 1 and the current
709 group is collapsed */
711 if(n != -1) {
712 switch(sc) {
713 case 'O':
714 nnode = (conf.getgroupmode() == icqconf::group1 && g->iscollapsed()) ||
715 (conf.gethideoffline() && (conf.getgroupmode() != icqconf::nogroups)) ?
716 n : mcontacts->addnode(n,
717 conf.getcolor(cp_main_highlight), 0, " Online ");
719 online_added = true;
720 break;
721 case '_':
722 nnode = (conf.getgroupmode() == icqconf::group1 && g->iscollapsed()) ?
723 n : mcontacts->addnode(n, conf.getcolor(cp_main_highlight), 0, " Offline ");
724 break;
725 case '!':
726 ngroup = 0;
727 nnode = mcontacts->addnode(ngroup,
728 conf.getcolor(cp_main_highlight), 0, " Not in list ");
729 break;
732 nonline = nnode;
735 if(conf.getgroupmode() == icqconf::group2)
736 if(groupchange && (sc != '!')) {
737 ADDGROUP(nonline);
738 nnode = ngroup;
742 if(groupchange) prevgid = c->getgroupid();
743 prevoffline = (c->getstatus() == offline) ? true : false;
744 prevsc = sc;
747 dnick = c->getdispnick();
749 if(birthday) dnick += " :)";
751 if(conf.getgroupmode() != icqconf::nogroups && g->iscollapsed() &&
752 !c->hasevents() && sc != '!')
753 continue;
755 if(conf.getcolormode() == icqconf::cmstatus) {
756 ccolor = conf.getstatuscolor(c->getstatus());
757 } else {
758 ccolor = conf.getprotcolor(c->getdesc().pname);
761 if(c->getstatus() == offline) {
762 mcontacts->addleaff(nnode,
763 c->hasevents() ? conf.getcolor(cp_main_highlight) : ccolor,
764 c, "%s%s ", c->hasevents() ? "#" : c->getpostponed().empty() ? (c->isopenedforchat() ? "*" : " ") : ">", dnick.c_str());
766 } else {
768 if(conf.getcolormode() == icqconf::cmstatus) {
769 // When: 'Color contacts according to: status' is selected; protocol is not visible. Ordered by status
770 char *fmt = "%s[%c]%s%s ";
772 if(lst.inlist(c->getdesc(), csvisible)) fmt = "%s<%c>%s%s "; else
773 if(lst.inlist(c->getdesc(), csinvisible)) fmt = "%s{%c}%s%s ";
775 char shortstatus = c->getshortstatus();
777 if(c->getlasttyping()) {
778 if(timer-c->getlasttyping() > PERIOD_TYPING) c->setlasttyping(0);
779 else shortstatus = 'T';
782 mcontacts->addleaff(nnode,
783 c->hasevents() ? conf.getcolor(cp_main_highlight) : ccolor,
784 c, fmt, c->hasevents() ? "#" : (c->isopenedforchat() ? "*" : " "), shortstatus,
785 c->getpostponed().empty() ? " " : ">", dnick.c_str());
787 } else {
788 // Color contact by protocol. Ordered by status
789 char *fmt = "%s[%c]%s%s ";
791 if(lst.inlist(c->getdesc(), csvisible)) fmt = "%s<%c>%s%s "; else
792 if(lst.inlist(c->getdesc(), csinvisible)) fmt = "%s{%c}%s%s ";
794 char shortstatus = c->getshortstatus();
796 if(c->getlasttyping()) {
797 if(timer-c->getlasttyping() > PERIOD_TYPING) c->setlasttyping(0);
798 else shortstatus = 'T';
801 mcontacts->addleaff(nnode,
802 c->hasevents() ? conf.getcolor(cp_main_highlight) : ccolor,
803 c, fmt, c->hasevents() ? "#" : (c->isopenedforchat() ? "*" : " "), shortstatus,
804 c->getpostponed().empty() ? " " : ">", dnick.c_str());
810 if(!mainscreenblock) mcontacts->redraw();
812 if(!savec || ontop || (!onlinefolder && online_added && conf.gethideoffline())) {
813 mcontacts->menu.setpos(0);
815 } else if(savec) {
816 for(i = 0; i < mcontacts->menu.getcount(); i++)
817 if(mcontacts->getref(mcontacts->getid(i)) == savec) {
818 if(!grouponline) grouponline = true; else {
819 mcontacts->menu.setpos(i);
820 break;
826 onlinefolder = online_added;
828 if(!mainscreenblock) {
829 mcontacts->menu.redraw();
833 bool icqface::findresults(const imsearchparams &sp, bool fauto) {
834 bool finished = false, ret = false;
835 unsigned int ffuin;
836 icqcontact *c;
837 dialogbox db;
838 int r, b;
839 char *nick;
841 saveworkarea();
842 clearworkarea();
844 abstracthook &h = gethook(sp.pname);
846 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
847 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
848 db.setmenu(new verticalmenu(conf.getcolor(cp_main_menu),
849 conf.getcolor(cp_main_selected)));
851 if(!h.getCapabs().count(hookcapab::nochat)) {
852 db.setbar(new horizontalbar(conf.getcolor(cp_main_highlight),
853 conf.getcolor(cp_main_selected), _("Details"), _("Message"),
854 _("Add"), fauto ? 0 : _("New search"), (char*)0));
855 } else {
856 db.setbar(new horizontalbar(conf.getcolor(cp_main_highlight),
857 conf.getcolor(cp_main_selected), _("Details"), _("Add"),
858 fauto ? 0 : _("New search"), (char*)0));
861 db.addautokeys();
862 db.redraw();
864 if(!sp.nick.empty() && h.getCapabs().count(hookcapab::directadd)) {
865 imcontact ic(sp.nick, sp.pname);
866 static icqcontact *singlec = 0, *c;
868 delete singlec;
869 singlec = 0;
871 mainw.write(sizeWArea.x1+2, sizeWArea.y1,
872 conf.getcolor(cp_main_highlight), _("Contact to add"));
874 c = clist.get(ic);
875 if(!c) c = singlec = new icqcontact(ic);
877 db.getmenu()->additem(conf.getprotcolor(sp.pname),
878 c, (string) " " + sp.nick);
880 } else {
881 mainw.write(sizeWArea.x1+2, sizeWArea.y1,
882 conf.getcolor(cp_main_highlight), _("Searching contacts.."));
884 h.lookup(sp, *db.getmenu());
888 db.idle = &dialogidle;
889 db.otherkeys = &findreskeys;
891 workarealine(sizeWArea.y1+2);
892 workarealine(sizeWArea.y2-2);
894 while(!finished) {
895 finished = !db.open(r, b);
896 h.stoplookup();
898 if(h.getCapabs().count(hookcapab::nochat) && b > 0)
899 b++;
901 if(!finished) {
902 if(r == -3) {
903 quickfind(db.getmenu());
905 } else switch(b) {
906 case 0:
907 if(r) {
908 c = (icqcontact *) db.getmenu()->getref(r-1);
909 if(c) cicq.userinfo(c->getdesc());
911 break;
913 case 1:
914 if(r) {
915 bool existed;
916 if(c = (icqcontact *) db.getmenu()->getref(r-1)) {
917 imcontact cont = c->getdesc();
918 existed = (c = clist.get(cont));
919 if(!existed) c = clist.addnew(cont);
921 if(c)
922 if(!cicq.sendevent(immessage(cont, imevent::outgoing, ""), icqface::ok))
923 if(!existed)
924 clist.remove(cont);
927 break;
929 case 2:
930 if(r) {
931 c = (icqcontact *) db.getmenu()->getref(r-1);
932 if(c) cicq.addcontact(c->getdesc(), c->getbasicinfo().requiresauth);
934 break;
936 case 3:
937 ret = finished = true;
938 break;
944 db.close();
945 restoreworkarea();
947 return ret;
950 void icqface::findready() {
951 mvhline(sizeWArea.x1+2, sizeWArea.y1, ' ', sizeWArea.x2-sizeWArea.x1-2);
952 mainw.write(sizeWArea.x1+2, sizeWArea.y1,
953 conf.getcolor(cp_main_highlight), _("Search results [done]"));
956 void icqface::infoclear(dialogbox &db, icqcontact *c, const imcontact realdesc) {
957 for(int i = sizeWArea.y1+1; i < sizeWArea.y2; i++) {
958 workarealine(i, ' ');
961 db.redraw();
963 mainw.writef(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight),
964 _("Information about %s"), realdesc.totext().c_str());
966 workarealine(sizeWArea.y1+2);
967 workarealine(sizeWArea.y2-2);
969 icqcontact::basicinfo bi = c->getbasicinfo();
970 icqcontact::moreinfo mi = c->getmoreinfo();
971 icqcontact::workinfo wi = c->getworkinfo();
973 extracturls(bi.city + " " + bi.state + " " + bi.street + " " +
974 wi.city + " " + wi.state + " " + wi.street + " " +
975 wi.company + " " + wi.dept + " " + wi.position + " " +
976 c->getabout());
978 if(!mi.homepage.empty())
979 extractedurls.push_back(mi.homepage);
980 if(!wi.homepage.empty())
981 extractedurls.push_back(wi.homepage);
984 void commacat(string &text, string sub, bool nocomma = false) {
985 if(!sub.empty()) {
986 if(!nocomma)
987 if(text[text.size()-1] != ',') text += ",";
989 text += sub;
993 void commaform(string &text) {
994 int pos = 0, ipos;
996 while((pos = text.find(",", pos)) != -1) {
997 if(text.substr(pos+1, 1) != " ") {
998 text.insert(pos+1, " ");
1001 ipos = pos-1;
1002 while(ipos > 0) {
1003 if(text.substr(ipos, 1) == " ") {
1004 text.erase(ipos--, 1);
1005 } else {
1006 break;
1010 pos++;
1014 void icqface::infolivejournal(dialogbox &db, icqcontact *c) {
1015 icqcontact::basicinfo bi = c->getbasicinfo();
1016 icqcontact::moreinfo mi = c->getmoreinfo();
1018 workarealine(sizeWArea.y1+5);
1019 workarealine(sizeWArea.y1+7);
1021 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Nickname"));
1022 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Name"));
1023 mainw.write(sizeWArea.x1+2, sizeWArea.y1+5, conf.getcolor(cp_main_highlight), _("URL"));
1025 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), c->getnick());
1026 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), bi.fname + " " + bi.lname);
1027 mainw.write(sizeWArea.x1+14, sizeWArea.y1+5, conf.getcolor(cp_main_text), mi.homepage);
1029 mainw.write(sizeWArea.x1+2, sizeWArea.y1+7, conf.getcolor(cp_main_highlight), _("This is your LiveJournal account. Use it to"));
1030 mainw.write(sizeWArea.x1+2, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("post new entries to your journal."));
1033 void icqface::infofriends(dialogbox &db, icqcontact *c) {
1034 string text, buf;
1035 icqcontact::basicinfo bi = c->getbasicinfo();
1037 text += (string) "* " + _("The following users have you listed as their friend:") + " ";
1038 while(!(buf = getword(bi.zip)).empty()) {
1039 text += buf;
1040 if(!bi.zip.empty()) text += ", ";
1043 db.getbrowser()->setbuf(text);
1046 void icqface::infogeneral(dialogbox &db, icqcontact *c) {
1047 string langs, options, buf;
1048 icqcontact::basicinfo bi = c->getbasicinfo();
1049 icqcontact::moreinfo mi = c->getmoreinfo();
1051 workarealine(sizeWArea.y1+8);
1052 workarealine(sizeWArea.y1+12);
1054 #ifdef HAVE_GPGME
1055 if(gethook(c->getdesc().pname).getCapabs().count(hookcapab::pgp))
1056 if(!c->getpgpkey().empty()) {
1057 buf = (string) "[" + string(5, ' ') + c->getpgpkey() + ": ";
1059 if(pgp.havekey(c->getpgpkey())) {
1060 buf += _("found");
1061 buf += ", ";
1062 buf += (pgp.enabled(c) ? _("used") : _("not used"));
1063 } else {
1064 buf += _("not found");
1067 buf += " ]";
1069 mainw.write(sizeWArea.x2-buf.size()-1, sizeWArea.y1+11,
1070 conf.getcolor(cp_main_text), buf);
1072 mainw.write(sizeWArea.x2-buf.size()+1, sizeWArea.y1+11,
1073 conf.getcolor(cp_main_highlight), "PGP");
1075 #endif
1077 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Nickname"));
1078 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Name"));
1079 mainw.write(sizeWArea.x1+2, sizeWArea.y1+4, conf.getcolor(cp_main_highlight), _("E-mail"));
1080 mainw.write(sizeWArea.x1+2, sizeWArea.y1+5, conf.getcolor(cp_main_highlight), _("2nd e-mail"));
1081 mainw.write(sizeWArea.x1+2, sizeWArea.y1+6, conf.getcolor(cp_main_highlight), _("Old e-mail"));
1082 mainw.write(sizeWArea.x1+2, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("Gender"));
1083 mainw.write(sizeWArea.x1+2, sizeWArea.y1+9, conf.getcolor(cp_main_highlight), _("Birthdate"));
1084 mainw.write(sizeWArea.x1+2, sizeWArea.y1+10, conf.getcolor(cp_main_highlight), _("Age"));
1085 mainw.write(sizeWArea.x1+2, sizeWArea.y1+12, conf.getcolor(cp_main_highlight), _("Languages"));
1086 mainw.write(sizeWArea.x1+2, sizeWArea.y1+13, conf.getcolor(cp_main_highlight), _("Last IP"));
1087 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), c->getnick());
1088 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), bi.fname + " " + bi.lname);
1089 mainw.write(sizeWArea.x1+14, sizeWArea.y1+4, conf.getcolor(cp_main_text), bi.email);
1091 mainw.write(sizeWArea.x1+14, sizeWArea.y1+8, conf.getcolor(cp_main_text), strgender(mi.gender));
1092 mainw.write(sizeWArea.x1+14, sizeWArea.y1+9, conf.getcolor(cp_main_text), mi.strbirthdate());
1093 mainw.write(sizeWArea.x1+14, sizeWArea.y1+10, conf.getcolor(cp_main_text), mi.age ? i2str(mi.age) : "");
1095 if(mi.lang1) commacat(langs, abstracthook::getLanguageIDtoString(mi.lang1), langs.empty());
1096 if(mi.lang2) commacat(langs, abstracthook::getLanguageIDtoString(mi.lang2), langs.empty());
1097 if(mi.lang3) commacat(langs, abstracthook::getLanguageIDtoString(mi.lang3), langs.empty());
1098 commaform(langs);
1100 mainw.write(sizeWArea.x1+14, sizeWArea.y1+12, conf.getcolor(cp_main_text), langs);
1101 mainw.write(sizeWArea.x1+14, sizeWArea.y1+13, conf.getcolor(cp_main_text), c->getlastip());
1103 time_t ls = c->getlastseen();
1104 buf = "";
1106 if(c->getstatus() == offline) {
1107 mainw.write(sizeWArea.x1+2, sizeWArea.y1+14, conf.getcolor(cp_main_highlight), _("Last seen"));
1108 mainw.write(sizeWArea.x1+14, sizeWArea.y1+14, conf.getcolor(cp_main_text),
1109 ls ? strdateandtime(ls) : _("Never"));
1111 } else if(c->getdesc() != contactroot) {
1112 int days, hours, minutes, tdiff = timer_current-ls;
1114 days = (int) (tdiff/86400);
1115 hours = (int) ((tdiff-days*86400)/3600);
1116 minutes = (int) ((tdiff-days*8600-hours*3600)/60);
1118 if(days) buf = i2str(days) + " " + _("days");
1120 if(hours) {
1121 if(!buf.empty()) buf += ", ";
1122 buf += i2str(hours) + " " + _("hours");
1125 if(minutes) {
1126 if(!buf.empty()) buf += ", ";
1127 buf += i2str(minutes) + " " + _("min");
1130 if(buf.empty())
1131 buf = i2str(tdiff) + " " + _("seconds");
1133 mainw.write(sizeWArea.x1+2, sizeWArea.y1+14, conf.getcolor(cp_main_highlight), _("Online"));
1134 mainw.write(sizeWArea.x1+14, sizeWArea.y1+14, conf.getcolor(cp_main_text), buf);
1138 void icqface::infohome(dialogbox &db, icqcontact *c) {
1139 int i, x;
1141 icqcontact::basicinfo bi = c->getbasicinfo();
1142 icqcontact::moreinfo mi = c->getmoreinfo();
1144 workarealine(sizeWArea.y1+10);
1145 x = sizeWArea.x1+2;
1147 mainw.write(x, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Address"));
1148 mainw.write(x, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Location"));
1149 mainw.write(x, sizeWArea.y1+4, conf.getcolor(cp_main_highlight), _("Zip code"));
1150 mainw.write(x, sizeWArea.y1+5, conf.getcolor(cp_main_highlight), _("Phone"));
1151 mainw.write(x, sizeWArea.y1+6, conf.getcolor(cp_main_highlight), _("Fax"));
1152 mainw.write(x, sizeWArea.y1+7, conf.getcolor(cp_main_highlight), _("Cellular"));
1153 mainw.write(x, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("Timezone"));
1155 mainw.write(x, sizeWArea.y1+10, conf.getcolor(cp_main_highlight), _("Homepage"));
1157 if(!bi.state.empty()) {
1158 if(bi.city.size()) bi.city += ", ";
1159 bi.city += bi.state;
1162 if(bi.country) {
1163 if(bi.city.size()) bi.city += ", ";
1164 bi.city += abstracthook::getCountryIDtoString(bi.country);
1167 x += 10;
1168 mainw.write(x, sizeWArea.y1+2, conf.getcolor(cp_main_text), bi.street);
1169 mainw.write(x, sizeWArea.y1+3, conf.getcolor(cp_main_text), bi.city);
1170 mainw.write(x, sizeWArea.y1+4, conf.getcolor(cp_main_text), bi.zip);
1171 mainw.write(x, sizeWArea.y1+5, conf.getcolor(cp_main_text), bi.phone);
1172 mainw.write(x, sizeWArea.y1+6, conf.getcolor(cp_main_text), bi.fax);
1173 mainw.write(x, sizeWArea.y1+7, conf.getcolor(cp_main_text), bi.cellular);
1174 mainw.write(x, sizeWArea.y1+8, conf.getcolor(cp_main_text), mi.strtimezone());
1176 for(i = 0; !mi.homepage.empty(); i++) {
1177 mainw.write(x, sizeWArea.y1+10+i, conf.getcolor(cp_main_text),
1178 mi.homepage.substr(0, sizeWArea.x2-sizeWArea.x1-12));
1179 mi.homepage.erase(0, sizeWArea.x2-sizeWArea.x1-12);
1183 void icqface::infowork(dialogbox &db, icqcontact *c) {
1184 int i;
1185 icqcontact::workinfo wi = c->getworkinfo();
1187 workarealine(sizeWArea.y1+6);
1188 workarealine(sizeWArea.y1+11);
1190 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Address"));
1191 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Location"));
1192 mainw.write(sizeWArea.x1+2, sizeWArea.y1+4, conf.getcolor(cp_main_highlight), _("Zip code"));
1194 mainw.write(sizeWArea.x1+2, sizeWArea.y1+6, conf.getcolor(cp_main_highlight), _("Company"));
1195 mainw.write(sizeWArea.x1+2, sizeWArea.y1+7, conf.getcolor(cp_main_highlight), _("Department"));
1196 mainw.write(sizeWArea.x1+2, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("Title"));
1198 mainw.write(sizeWArea.x1+2, sizeWArea.y1+11, conf.getcolor(cp_main_highlight), _("Phone"));
1199 mainw.write(sizeWArea.x1+2, sizeWArea.y1+12, conf.getcolor(cp_main_highlight), _("Fax"));
1200 mainw.write(sizeWArea.x1+2, sizeWArea.y1+13, conf.getcolor(cp_main_highlight), _("Homepage"));
1202 if(!wi.state.empty()) {
1203 if(wi.city.size()) wi.city += ", ";
1204 wi.city += wi.state;
1207 if(wi.country) {
1208 if(wi.city.size()) wi.city += ", ";
1209 wi.city += abstracthook::getCountryIDtoString(wi.country);
1212 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), wi.street);
1213 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), wi.city);
1214 mainw.write(sizeWArea.x1+14, sizeWArea.y1+4, conf.getcolor(cp_main_text), wi.zip);
1215 mainw.write(sizeWArea.x1+14, sizeWArea.y1+6, conf.getcolor(cp_main_text), wi.company);
1216 mainw.write(sizeWArea.x1+14, sizeWArea.y1+7, conf.getcolor(cp_main_text), wi.dept);
1217 mainw.write(sizeWArea.x1+14, sizeWArea.y1+8, conf.getcolor(cp_main_text), wi.position);
1218 mainw.write(sizeWArea.x1+14, sizeWArea.y1+11, conf.getcolor(cp_main_text), wi.phone);
1219 mainw.write(sizeWArea.x1+14, sizeWArea.y1+12, conf.getcolor(cp_main_text), wi.fax);
1221 for(i = 0; !wi.homepage.empty(); i++) {
1222 mainw.write(sizeWArea.x1+14, sizeWArea.y1+13+i, conf.getcolor(cp_main_text),
1223 wi.homepage.substr(0, sizeWArea.x2-sizeWArea.x1-14));
1224 wi.homepage.erase(0, sizeWArea.x2-sizeWArea.x1-14);
1228 void icqface::infoabout(dialogbox &db, icqcontact *c) {
1229 db.getbrowser()->setbuf(c->getabout());
1232 void icqface::infointerests(dialogbox &db, icqcontact *c) {
1233 string text;
1234 vector<string> data;
1235 vector<string>::iterator i;
1237 data = c->getinterests();
1239 if(!data.empty()) {
1240 text += (string) "* " + _("Interests") + "\n";
1242 for(i = data.begin(); i != data.end(); ++i) {
1243 text += " + " + *i + "\n";
1246 text += "\n";
1249 data = c->getbackground();
1251 if(!data.empty()) {
1252 text += (string) "* " + _("Background") + "\n";
1254 for(i = data.begin(); i != data.end(); ++i)
1255 text += " + " + *i + "\n";
1258 commaform(text);
1259 db.getbrowser()->setbuf(text);
1262 void icqface::inforss(dialogbox &db, icqcontact *c) {
1263 icqcontact::basicinfo bi = c->getbasicinfo();
1264 icqcontact::moreinfo mi = c->getmoreinfo();
1265 icqcontact::workinfo wi = c->getworkinfo();
1267 workarealine(sizeWArea.y1+7);
1268 workarealine(sizeWArea.y1+10);
1270 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Handle"));
1271 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("RSS doc"));
1272 mainw.write(sizeWArea.x1+2, sizeWArea.y1+4, conf.getcolor(cp_main_highlight), _("Version"));
1273 mainw.write(sizeWArea.x1+2, sizeWArea.y1+5, conf.getcolor(cp_main_highlight), _("Encoding"));
1275 mainw.write(sizeWArea.x1+2, sizeWArea.y1+7, conf.getcolor(cp_main_highlight), _("Title"));
1276 mainw.write(sizeWArea.x1+2, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("Link"));
1278 mainw.write(sizeWArea.x1+2, sizeWArea.y1+10, conf.getcolor(cp_main_highlight), _("Frequency"));
1279 mainw.write(sizeWArea.x1+2, sizeWArea.y1+11, conf.getcolor(cp_main_highlight), _("Last check"));
1280 mainw.write(sizeWArea.x1+2, sizeWArea.y1+12, conf.getcolor(cp_main_highlight), _("Next check"));
1281 mainw.write(sizeWArea.x1+2, sizeWArea.y1+13, conf.getcolor(cp_main_highlight), _("Last result"));
1283 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), c->getnick());
1284 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), wi.homepage);
1285 mainw.write(sizeWArea.x1+14, sizeWArea.y1+4, conf.getcolor(cp_main_text), bi.city);
1286 mainw.write(sizeWArea.x1+14, sizeWArea.y1+5, conf.getcolor(cp_main_text), bi.state);
1288 mainw.write(sizeWArea.x1+14, sizeWArea.y1+7, conf.getcolor(cp_main_text), bi.fname);
1289 mainw.write(sizeWArea.x1+14, sizeWArea.y1+8, conf.getcolor(cp_main_text), mi.homepage);
1291 string freq, last, next;
1292 char buf[512];
1294 if(mi.checkfreq) {
1295 snprintf(buf, sizeof(buf), _("%lu minutes"), mi.checkfreq);
1296 freq = buf;
1297 if(mi.checklast) next = strdateandtime(mi.checklast+mi.checkfreq*60);
1298 else next = _("Now");
1299 } else {
1300 freq = next = _("Never");
1303 if(mi.checklast) last = strdateandtime(mi.checklast);
1304 else last = _("Never");
1306 mainw.write(sizeWArea.x1+14, sizeWArea.y1+10, conf.getcolor(cp_main_text), freq);
1307 mainw.write(sizeWArea.x1+14, sizeWArea.y1+11, conf.getcolor(cp_main_text), last);
1308 mainw.write(sizeWArea.x1+14, sizeWArea.y1+12, conf.getcolor(cp_main_text), next);
1309 mainw.write(sizeWArea.x1+14, sizeWArea.y1+13, conf.getcolor(cp_main_text), bi.lname);
1312 void icqface::infoljrss(dialogbox &db, icqcontact *c) {
1313 icqcontact::basicinfo bi = c->getbasicinfo();
1314 icqcontact::moreinfo mi = c->getmoreinfo();
1316 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Birthdate"));
1317 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Full name"));
1319 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), mi.strbirthdate());
1320 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), bi.email);
1323 void icqface::userinfo(const imcontact &cinfo, const imcontact &realinfo) {
1324 bool finished = false, showinfo;
1325 textbrowser tb(conf.getcolor(cp_main_text));
1326 dialogbox db;
1327 int k, lastb, b;
1329 imcontact savepassinfo = passinfo;
1330 icqcontact *c = clist.get(passinfo = realinfo);
1332 b = lastb = 0;
1333 saveworkarea();
1334 clearworkarea();
1336 status(_("%s to URLs, %s external actions, %s close"),
1337 getstatkey(key_show_urls, section_info).c_str(),
1338 getstatkey(key_user_external_action, section_info).c_str(),
1339 getstatkey(key_quit, section_editor).c_str());
1341 xtermtitle(_("user info for %s"), realinfo.totext().c_str());
1343 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
1344 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
1346 if(cinfo.pname == rss) {
1347 if(c->inlist() && realinfo != contactroot) {
1348 db.setbar(new horizontalbar(sizeWArea.x1+2, sizeWArea.y2-1,
1349 conf.getcolor(cp_main_highlight), conf.getcolor(cp_main_selected),
1350 _("Info"), _("About"), _("Check"), _("Edit"), islivejournal(c) ? _("LJ") : 0, (char*)0));
1351 } else {
1352 db.setbar(new horizontalbar(sizeWArea.x1+2, sizeWArea.y2-1,
1353 conf.getcolor(cp_main_highlight), conf.getcolor(cp_main_selected),
1354 _("Info"), _("About"), _("Retreive"), (char*)0));
1357 } else if(cinfo.pname == livejournal) {
1358 db.setbar(new horizontalbar(sizeWArea.x1+2, sizeWArea.y2-1,
1359 conf.getcolor(cp_main_highlight), conf.getcolor(cp_main_selected),
1360 _("Info"), _("Friend of"), (char*)0));
1363 } else {
1364 db.setbar(new horizontalbar(sizeWArea.x1+2, sizeWArea.y2-1,
1365 conf.getcolor(cp_main_highlight), conf.getcolor(cp_main_selected),
1366 _("Info"), _("Home"), _("Work"), _("More"), _("About"),
1367 cinfo.pname != infocard ? _("Retrieve") : _("Edit"), (char*)0));
1371 showinfo = true;
1372 db.addautokeys();
1373 db.idle = &dialogidle;
1374 db.otherkeys = &userinfokeys;
1376 while(!finished) {
1377 if(!showinfo) {
1378 if(cicq.idle(HIDL_SOCKEXIT)) {
1379 finished = !db.open(k, b);
1380 showinfo = !finished;
1384 if(c->updated()) {
1385 showinfo = true;
1386 c->unsetupdated();
1389 if(showinfo) {
1390 db.setbrowser(0);
1391 infoclear(db, c, cinfo);
1393 if(cinfo.pname == rss) switch(b) {
1394 case 0: inforss(db, c); break;
1395 case 1:
1396 db.setbrowser(&tb, false);
1397 infoabout(db, c);
1398 infoclear(db, c, cinfo);
1399 break;
1400 case 2:
1401 gethook(cinfo.pname).ping(c);
1402 b = lastb;
1403 continue;
1404 case 3:
1405 updatedetails(c, c->getdesc().pname);
1406 showinfo = true;
1407 b = lastb;
1408 continue;
1409 case 4: infoljrss(db, c); break;
1411 } else if(cinfo.pname == livejournal) switch(b) {
1412 case 0: infolivejournal(db, c); break;
1413 case 1:
1414 db.setbrowser(&tb, false);
1415 infofriends(db, c);
1416 infoclear(db, c, cinfo);
1417 break;
1419 } else switch(b) {
1420 case 0: infogeneral(db, c); break;
1421 case 1: infohome(db, c); break;
1422 case 2: infowork(db, c); break;
1423 case 3:
1424 db.setbrowser(&tb, false);
1425 infointerests(db, c);
1426 infoclear(db, c, cinfo);
1427 break;
1428 case 4:
1429 db.setbrowser(&tb, false);
1430 infoabout(db, c);
1431 infoclear(db, c, cinfo);
1432 break;
1433 case 5:
1434 switch(cinfo.pname) {
1435 case infocard:
1436 updatedetails(c);
1437 showinfo = true;
1438 break;
1440 default:
1441 gethook(cinfo.pname).requestinfo(cinfo);
1442 break;
1445 b = lastb;
1446 continue;
1450 refresh();
1451 showinfo = false;
1452 lastb = b;
1456 db.close();
1457 restoreworkarea();
1458 status("@");
1459 passinfo = savepassinfo;
1462 void icqface::makeprotocolmenu(verticalmenu &m) {
1463 icqconf::imaccount ia;
1464 protocolname ipname;
1466 static const string pitems[protocolname_size] = {
1467 _(" [icq] ICQ network"),
1468 _(" [yahoo] YAIM"),
1469 _(" [msn] M$ Messenger"),
1470 _(" [aim] AOL TOC"),
1471 _(" [irc] IRC"),
1472 _(" [jab] Jabber"),
1474 _(" [lj] LiveJournal"),
1475 _(" [gg] Gadu-Gadu"),
1479 for(ipname = icq; ipname != protocolname_size; ipname++) {
1480 ia = conf.getourid(ipname);
1482 if(!ia.empty()) {
1483 m.additem(0, ipname, pitems[ipname]);
1488 bool icqface::changestatus(vector<protocolname> &pnames, imstatus &st) {
1489 int i, choice, protcount;
1490 bool r, alrlogged = false;
1491 verticalmenu m(conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
1493 vector<imstatus> mst;
1494 vector<imstatus>::iterator im;
1495 protocolname pname, onechoice;
1497 m.setwindow(textwindow(sizeWArea.x1, sizeWArea.y1, sizeWArea.x1+27,
1498 sizeWArea.y1+9, conf.getcolor(cp_main_text)));
1500 m.idle = &menuidle;
1502 for(protcount = 0, pname = icq; pname != protocolname_size; pname++) {
1503 if(!conf.getourid(pname).empty()) {
1504 protcount++;
1505 onechoice = pname;
1508 alrlogged = alrlogged || gethook(pname).getstatus() != offline;
1511 if(protcount < 2) {
1512 i = 1;
1513 choice = onechoice;
1515 } else {
1516 m.additem(0, -1, _(" All protocols"));
1517 if(alrlogged) m.additem(0, -2, _(" Already logged in only"));
1518 m.addline();
1519 makeprotocolmenu(m);
1520 m.scale();
1522 i = m.open();
1523 choice = (intptr_t) m.getref(i-1);
1525 m.close();
1528 if(r = i) {
1529 switch(choice) {
1530 case -1:
1531 for(pname = icq; pname != protocolname_size; pname++)
1532 if(!conf.getourid(pname).empty())
1533 pnames.push_back(pname);
1534 break;
1535 case -2:
1536 for(pname = icq; pname != protocolname_size; pname++)
1537 if(!conf.getourid(pname).empty())
1538 if(gethook(pname).getstatus() != offline)
1539 pnames.push_back(pname);
1540 break;
1541 default:
1542 pnames.push_back((protocolname) choice);
1543 break;
1546 m.clear();
1548 mst.push_back(available);
1549 mst.push_back(offline);
1550 mst.push_back(away);
1551 mst.push_back(dontdisturb);
1552 mst.push_back(notavail);
1553 mst.push_back(outforlunch);
1554 mst.push_back(occupied);
1555 mst.push_back(freeforchat);
1556 mst.push_back(invisible);
1558 for(im = mst.begin(); im != mst.end(); ++im) {
1559 string sst = (string) " [" + imstatus2char[*im] + "] " + imstatus2str(*im);
1560 m.additem(0, *im, sst);
1562 if(pnames.size() == 1)
1563 if(gethook(pnames.front()).getstatus() == *im)
1564 m.setpos(m.getcount()-1);
1567 if(pnames.size() > 1) {
1568 m.setpos(0);
1571 m.otherkeys = &statuskeys;
1573 m.scale();
1575 i = m.open();
1576 m.close();
1578 if(r = i) {
1579 st = (imstatus) ((intptr_t) m.getref(i-1));
1583 return r;
1586 #define INPUT_POS LINES-2
1588 string icqface::inputstr(const string &q, const string &defl, char passwdchar) {
1589 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1591 attrset(conf.getcolor(cp_status));
1592 mvhline(INPUT_POS, 0, ' ', COLS);
1593 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1595 input.setpasswordchar(passwdchar);
1596 input.removeselector();
1597 input.setvalue(defl);
1598 input.setcoords(q.size(), INPUT_POS, COLS-q.size());
1599 input.exec();
1601 sa.restore();
1602 return input.getvalue();
1605 string icqface::inputfile(const string &q, const string &defl) {
1606 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1607 string r;
1609 mvhline(INPUT_POS, 0, ' ', COLS);
1610 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1611 kwriteatf(COLS-8, INPUT_POS, conf.getcolor(cp_status), "[Ctrl-T]");
1613 selector.setoptions(0);
1614 selector.setwindow(textwindow(0, 0, sizeDlg.width, sizeDlg.height,
1615 conf.getcolor(cp_dialog_frame), TW_CENTERED,
1616 conf.getcolor(cp_dialog_highlight),
1617 _(" <Space> or <Enter> confirm, <Esc> cancel ")));
1619 input.connectselector(selector);
1621 input.setcoords(q.size(), INPUT_POS, COLS-q.size()-8);
1622 input.setvalue(defl);
1623 input.exec();
1624 r = input.getvalue();
1626 if(r.rfind("/") != -1) {
1627 selector.setstartpoint(r.substr(0, r.find("/")));
1630 sa.restore();
1631 return r;
1634 string icqface::inputdir(const string &q, const string &defl) {
1635 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1636 string r;
1638 mvhline(INPUT_POS, 0, ' ', COLS);
1639 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1640 kwriteatf(COLS-8, INPUT_POS, conf.getcolor(cp_status), "[Ctrl-T]");
1642 selector.setoptions(FSEL_DIRSELECT);
1643 selector.setwindow(textwindow(0, 0, sizeDlg.width, sizeDlg.height,
1644 conf.getcolor(cp_dialog_frame), TW_CENTERED,
1645 conf.getcolor(cp_dialog_highlight),
1646 _(" <Space> confirm, <Esc> cancel ")));
1648 input.connectselector(selector);
1650 input.setcoords(q.size(), INPUT_POS, COLS-q.size()-8);
1651 input.setvalue(defl);
1652 input.exec();
1653 r = input.getvalue();
1655 if(r.rfind("/") != -1) {
1656 selector.setstartpoint(r.substr(0, r.find("/")));
1659 sa.restore();
1660 return r;
1663 int icqface::getlastinputkey() const {
1664 return input.getlastkey();
1667 void catqstr(string &q, int option, int defl) {
1668 char c = q[q.size()-1];
1669 if(!strchr("(/", c)) q += '/';
1671 switch(option) {
1672 case ASK_YES: q += 'y'; break;
1673 case ASK_NO: q += 'n'; break;
1674 case ASK_CANCEL: q += 'c'; break;
1677 if(defl == option) q[q.size()-1] = toupper(q[q.size()-1]);
1680 int icqface::ask(string q, int options, int deflt ) {
1681 int ret;
1682 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1684 q += " (";
1685 if(options & ASK_YES) catqstr(q, ASK_YES, deflt);
1686 if(options & ASK_NO) catqstr(q, ASK_NO, deflt);
1687 if(options & ASK_CANCEL) catqstr(q, ASK_CANCEL, deflt);
1688 q += ") ";
1690 attrset(conf.getcolor(cp_status));
1691 mvhline(INPUT_POS, 0, ' ', COLS);
1692 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1694 for(ret = -1; ret == -1; ) {
1695 switch(getkey()) {
1696 case 'y':
1697 case 'Y':
1698 if(options & ASK_YES) ret = ASK_YES;
1699 break;
1700 case 'n':
1701 case 'N':
1702 if(options & ASK_NO) ret = ASK_NO;
1703 break;
1704 case 'c':
1705 case 'C':
1706 if(options & ASK_CANCEL) ret = ASK_CANCEL;
1707 break;
1708 case '\r':
1709 if(deflt != -1) ret = deflt;
1710 break;
1714 sa.restore();
1715 return ret;
1718 void icqface::saveworkarea() {
1719 int i;
1720 chtype **workareabuf = (chtype **) malloc(sizeof(chtype *) * (sizeWArea.y2-sizeWArea.y1+1));
1722 for(i = 0; i <= sizeWArea.y2-sizeWArea.y1; i++) {
1723 workareabuf[i] = (chtype *) malloc(sizeof(chtype) * (sizeWArea.x2-sizeWArea.x1+2));
1724 mvinchnstr(sizeWArea.y1+i, sizeWArea.x1, workareabuf[i], sizeWArea.x2-sizeWArea.x1+1);
1727 workareas.add(workareabuf);
1730 void icqface::restoreworkarea() {
1731 int i;
1732 chtype **workareabuf = (chtype **) workareas.at(workareas.count-1);
1734 if(workareabuf && !dotermresize) {
1735 for(i = 0; i <= sizeWArea.y2-sizeWArea.y1; i++) {
1736 mvaddchnstr(sizeWArea.y1+i, sizeWArea.x1,
1737 workareabuf[i], sizeWArea.x2-sizeWArea.x1+1);
1740 refresh();
1743 workareas.remove(workareas.count-1);
1746 void icqface::clearworkarea() {
1747 int i;
1749 attrset(conf.getcolor(cp_main_text));
1750 for(i = sizeWArea.y1+1; i < sizeWArea.y2; i++) {
1751 mvhline(i, sizeWArea.x1+1, ' ', sizeWArea.x2-sizeWArea.x1-1);
1752 refresh();
1756 void icqface::freeworkareabuf(void *p) {
1757 chtype **workareabuf = (chtype **) p;
1758 if(workareabuf) {
1759 for(int i = 0; i <= face.sizeWArea.y2-face.sizeWArea.y1; i++) {
1760 free((chtype *) workareabuf[i]);
1763 free(workareabuf);
1767 void icqface::workarealine(int l, chtype c ) {
1768 attrset(conf.getcolor(cp_main_frame));
1769 mvhline(l, sizeWArea.x1+1, c, sizeWArea.x2-sizeWArea.x1-1);
1772 void icqface::modelist(contactstatus cs) {
1773 int i, b;
1774 icqcontact *c;
1775 dialogbox db;
1776 modelistitem it;
1777 vector<imcontact>::iterator ic;
1779 saveworkarea();
1780 clearworkarea();
1782 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
1783 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
1785 db.setmenu(new verticalmenu(conf.getcolor(cp_main_text),
1786 conf.getcolor(cp_main_selected)));
1788 db.setbar(new horizontalbar(conf.getcolor(cp_main_highlight),
1789 conf.getcolor(cp_main_selected),
1790 _("Details"), _("Add"), _("Remove"), _("Move to contacts"), (char*)0));
1792 db.addautokeys();
1793 db.idle = &dialogidle;
1794 db.addkey(KEY_IC, 1);
1795 db.addkey(KEY_DC, 2);
1797 db.redraw();
1798 workarealine(sizeWArea.y1+2);
1799 workarealine(sizeWArea.y2-2);
1801 mainw.write(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight),
1802 cs == csignore ? _("Ignore list") :
1803 cs == csvisible ? _("Visible list") :
1804 cs == csinvisible ? _("Invisible list") : "");
1806 set<protocolname> ps;
1807 if(cs == csvisible || cs == csinvisible) {
1808 for(protocolname pname = icq; pname != protocolname_size; pname++)
1809 if(gethook(pname).getCapabs().count(hookcapab::visibility))
1810 ps.insert(pname);
1813 lst.fillmenu(db.getmenu(), cs);
1815 while(db.open(i, b)) {
1816 if(!db.getmenu()->getcount() && b != 1) continue;
1817 if(b != 1) it = lst.menuat(i-1);
1819 switch(b) {
1820 case 0:
1821 cicq.userinfo(it.getdesc());
1822 break;
1823 case 1:
1824 muins.clear();
1826 if(multicontacts(_("Select contacts to add to the list"), ps, cs)) {
1827 bool removecl;
1829 if(cs == csignore) {
1830 char buf[512];
1831 snprintf(buf, sizeof(buf), _("Remove the %d contacts from the contact list as well?"), muins.size());
1832 removecl = face.ask(buf, ASK_YES | ASK_NO, ASK_NO) == ASK_YES;
1835 for(ic = muins.begin(); ic != muins.end(); ++ic) {
1836 lst.push_back(modelistitem(clist.get(*ic)->getdispnick(), *ic, cs));
1838 if(removecl && cs == csignore)
1839 clist.remove(*ic);
1842 lst.fillmenu(db.getmenu(), cs);
1843 db.getmenu()->redraw();
1845 break;
1846 case 2:
1847 lst.del(it.getdesc(), cs);
1848 lst.fillmenu(db.getmenu(), cs);
1849 db.getmenu()->redraw();
1850 break;
1851 case 3:
1852 if(c = cicq.addcontact(it.getdesc())) {
1853 c->setdispnick(it.getnick());
1854 lst.del(it.getdesc(), cs);
1855 lst.fillmenu(db.getmenu(), cs);
1856 db.getmenu()->redraw();
1858 break;
1861 face.relaxedupdate();
1864 db.close();
1865 restoreworkarea();
1868 bool icqface::multicontacts(const string &ahead,
1869 const set<protocolname> &protos, contactstatus cs) {
1871 int i, savefirst, saveelem, prevgid;
1872 bool ret = true, finished = false;
1873 string head = ahead;
1875 imcontact ic;
1876 vector<imcontact>::iterator c, cc;
1877 vector<imcontact> mlst;
1879 map<int, bool> groupcheck;
1880 icqcontact *cont;
1882 verticalmenu m(sizeWArea.x1+1, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2,
1883 conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
1885 saveworkarea();
1886 clearworkarea();
1888 workarealine(sizeWArea.y1+2);
1890 if(!head.size()) head = _("Event recipients");
1891 mainw.write(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight), head);
1893 vector<icqgroup>::iterator ig = groups.begin();
1895 while(ig != groups.end()) {
1896 for(i = 0; i < clist.count; i++) {
1897 icqcontact *c = (icqcontact *) clist.at(i);
1898 imcontact desc = c->getdesc();
1900 if(!desc.empty())
1901 if(c->getgroupid() == ig->getid() || conf.getgroupmode() == icqconf::nogroups)
1902 if(protos.empty() || protos.count(desc.pname))
1903 if(!lst.inlist(desc, cs))
1904 mlst.push_back(desc);
1907 ig++;
1910 m.idle = &menuidle;
1911 m.otherkeys = &multiplekeys;
1913 while(!finished) {
1914 m.getpos(saveelem, savefirst);
1915 m.clear();
1917 int prevgid = -1;
1919 for(c = mlst.begin(); c != mlst.end(); ++c) {
1920 cont = (icqcontact *) clist.get(*c);
1922 if(conf.getgroupmode() != icqconf::nogroups)
1923 if(cont->getgroupid() != prevgid) {
1924 prevgid = cont->getgroupid();
1926 if(groupcheck.find(prevgid) == groupcheck.end())
1927 groupcheck[prevgid] = false;
1929 m.additemf(conf.getcolor(cp_main_highlight), 0, "\002[%c]\002\002 %s",
1930 groupcheck[prevgid] ? 'x' : ' ',
1931 groups.getname(prevgid).c_str());
1934 m.additemf(conf.getprotcolor(c->pname), cont, " [%c] %s",
1935 find(muins.begin(), muins.end(), *c) != muins.end() ? 'x' : ' ',
1936 cont->getdispnick().c_str());
1939 m.setpos(saveelem, savefirst);
1941 switch(m.open()) {
1942 case -2:
1943 if(m.getref(m.getpos())) {
1944 ic = ((icqcontact *) m.getref(m.getpos()))->getdesc();
1945 c = find(muins.begin(), muins.end(), ic);
1947 if(c != muins.end()) muins.erase(c);
1948 else muins.push_back(ic);
1950 cont = clist.get(ic);
1951 groupcheck[cont->getgroupid()] = false;
1953 } else {
1954 bool been = false;
1955 int gid = ((icqcontact *) m.getref(m.getpos()+1))->getgroupid();
1956 groupcheck[gid] = !groupcheck[gid];
1958 for(c = mlst.begin(); c != mlst.end(); ++c) {
1959 cont = (icqcontact *) clist.get(*c);
1961 if(cont->getgroupid() == gid) {
1962 been = true;
1963 cc = find(muins.begin(), muins.end(), *c);
1965 if(groupcheck[gid] && cc == muins.end()) {
1966 muins.push_back(*c);
1967 } else if(!groupcheck[gid] && cc != muins.end()) {
1968 muins.erase(cc);
1970 } else if(been) break;
1973 break;
1975 case -3:
1976 quickfind(&m);
1977 break;
1979 case 0:
1980 ret = false;
1982 default:
1983 finished = true;
1984 break;
1988 restoreworkarea();
1989 return ret;
1992 void icqface::log(const char *fmt, ...) {
1993 va_list ap;
1994 char buf[1024];
1996 va_start(ap, fmt);
1997 vsnprintf(buf, sizeof(buf), fmt, ap);
1998 log((string) buf);
1999 va_end(ap);
2002 void icqface::log(const string &atext) {
2003 int i;
2004 string text = atext;
2006 if(conf.getdebug())
2007 if(flog.is_open())
2008 flog << text << endl;
2010 /*! Add a timestamp if needed
2013 bool lts, lo;
2014 conf.getlogoptions(lts, lo);
2016 if(lts)
2017 if(text.size() > 3)
2018 if(ispunct(text[0]) && isspace(text[1]) && !isspace(text[2])) {
2019 time_t t;
2020 char stime[64];
2021 time(&t);
2022 strftime(stime, 64, conf.getlogtimestampformat(), localtime(&t));
2023 text.insert(2, stime);
2026 while((i = text.find("\n")) != -1) text[i] = ' ';
2027 while((i = text.find("\r")) != -1) text[i] = ' ';
2029 while((lastlog.size() > LINES-sizeWArea.y2-2) && !lastlog.empty())
2030 lastlog.erase(lastlog.begin());
2032 lastlog.push_back(text);
2034 if(!mainscreenblock && (sizeWArea.x2-sizeWArea.x1 > 0)) {
2035 chtype *logline = new chtype[sizeWArea.x2-sizeWArea.x1+2];
2036 attrset(conf.getcolor(cp_main_text));
2038 for(i = sizeWArea.y2+2; i < LINES-2; i++) {
2039 mvinchnstr(i, sizeWArea.x1+1, logline, sizeWArea.x2-sizeWArea.x1);
2040 mvaddchnstr(i-1, sizeWArea.x1+1, logline, sizeWArea.x2-sizeWArea.x1);
2043 delete[] logline;
2045 if(text.size() > sizeWArea.x2-sizeWArea.x1-2) text.resize(sizeWArea.x2-sizeWArea.x1-2);
2046 mvhline(LINES-3, sizeWArea.x1+2, ' ', sizeWArea.x2-sizeWArea.x1-2);
2047 kwriteatf(sizeWArea.x1+2, LINES-3, conf.getcolor(cp_main_text), "%s", text.c_str());
2051 void icqface::status(const string &text) {
2052 string t(text);
2054 if(t == "@") {
2055 if(!fstatus.empty()) fstatus.pop_back();
2056 if(!fstatus.empty()) t = fstatus.back();
2058 } else if(t == "#") {
2059 if(!fstatus.empty()) t = fstatus.back();
2061 } else {
2062 fstatus.push_back(t);
2063 while(fstatus.size() > 5)
2064 fstatus.erase(fstatus.begin());
2068 attrset(conf.getcolor(cp_status));
2069 mvhline(LINES-1, 0, ' ', COLS);
2070 kwriteatf(0, LINES-1, conf.getcolor(cp_status), "%s", t.c_str());
2073 void icqface::status(const char *fmt, ...) {
2074 va_list ap;
2075 char buf[1024];
2077 va_start(ap, fmt);
2078 vsnprintf(buf, sizeof(buf), fmt, ap);
2079 status((string) buf);
2080 va_end(ap);
2083 void icqface::blockmainscreen() {
2084 mainscreenblock = true;
2087 void icqface::unblockmainscreen() {
2088 mainscreenblock = false;
2089 update();
2092 void icqface::quickfind(verticalmenu *multi) {
2093 bool fin;
2094 string nick, disp, upnick, upcurrent;
2095 string::iterator is;
2096 int k, i, len, lx, ly;
2097 bool found;
2098 icqcontact *c;
2099 verticalmenu *cm = (multi ? multi : &mcontacts->menu);
2101 status(_("QuickSearch: type to find, %s find again, Enter finish"),
2102 getstatkey(key_quickfind, section_contact).c_str());
2104 xtermtitle(_("contact list quick search"));
2106 if(multi) {
2107 lx = sizeWArea.x1+2;
2108 ly = sizeWArea.y2;
2109 } else {
2110 lx = 2;
2111 ly = LINES-2;
2114 for(fin = false; !fin; ) {
2115 attrset(conf.getcolor(cp_main_frame));
2116 mvhline(ly, lx, HLINE, 23);
2117 disp = nick;
2118 if(disp.size() > 18) disp.replace(0, disp.size()-18, "");
2119 kwriteatf(lx, ly, conf.getcolor(cp_main_highlight), "[ %s ]", disp.c_str());
2120 kgotoxy(lx+2+disp.size(), ly);
2121 refresh();
2123 if(cicq.idle())
2124 switch(k = getkey()) {
2125 case KEY_ESC:
2126 case '\r':
2127 fin = true;
2128 break;
2130 case KEY_BACKSPACE:
2131 case CTRL('h'):
2132 if(!nick.empty()) nick.resize(nick.size()-1);
2133 else fin = true;
2134 break;
2136 default:
2137 if(isprint(k) || (key2action(k, section_contact) == key_quickfind)) {
2138 i = cm->getpos() + (multi ? 1 : 2);
2140 if(isprint(k)) {
2141 i--;
2142 nick += k;
2145 for(is = nick.begin(), upnick = ""; is != nick.end(); ++is)
2146 upnick += toupper(*is);
2148 bool fin = false;
2149 bool fpass = true;
2151 for(; !fin; i++) {
2152 c = 0;
2154 if(i > cm->getcount()) {
2155 if(fpass) {
2156 i = 0;
2157 fpass = false;
2158 } else {
2159 fin = true;
2160 break;
2164 if(!multi && mcontacts->isnode(i)) {
2165 c = 0;
2166 } else if(!multi) {
2167 c = (icqcontact *) mcontacts->getref(i);
2168 } else {
2169 c = (icqcontact *) cm->getref(i);
2172 if((intptr_t) c > 100) {
2173 string current = c->getdispnick();
2174 len = current.size();
2175 if(len > nick.size()) len = nick.size();
2176 current.erase(len);
2178 for(is = current.begin(), upcurrent = "";
2179 is != current.end(); ++is)
2180 upcurrent += toupper(*is);
2182 if(upnick == upcurrent) {
2183 cm->setpos(i - (multi ? 0 : 1));
2184 break;
2189 if(!multi) mcontacts->redraw();
2190 else cm->redraw();
2192 break;
2196 attrset(conf.getcolor(cp_main_frame));
2197 mvhline(ly, lx, HLINE, 23);
2200 void icqface::extracturls(const string &buf) {
2201 int pos = 0;
2202 regex_t r;
2203 regmatch_t rm[1];
2204 const char *pp = buf.c_str();
2206 extractedurls.clear();
2207 if(!regcomp(&r, "(http://[^ \t\n]+|https://[^ \t\n]+|ftp://[^ \t\n]+|www\\.[^ \t\n]+)", REG_EXTENDED)) {
2208 while(!regexec(&r, buf.substr(pos).c_str(), 1, rm, 0)) {
2209 extractedurls.push_back(buf.substr(pos+rm[0].rm_so, rm[0].rm_eo-rm[0].rm_so));
2210 pos += rm[0].rm_eo;
2212 regfree(&r);
2216 void icqface::showextractedurls() {
2217 if(extractedurls.empty()) {
2218 log(_("+ no URLs within the current context"));
2219 } else {
2220 int n;
2221 vector<string>::iterator i;
2222 verticalmenu m(sizeWArea.x1+1, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2,
2223 conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
2225 saveworkarea();
2226 clearworkarea();
2227 workarealine(sizeWArea.y1+2);
2229 mainw.writef(sizeWArea.x1+2, sizeWArea.y1,
2230 conf.getcolor(cp_main_highlight),
2231 _("URLs within the current context"));
2233 for(i = extractedurls.begin(); i != extractedurls.end(); ++i)
2234 m.additem(" " + *i);
2236 if(n = m.open())
2237 conf.execaction("openurl", extractedurls[n-1]);
2239 restoreworkarea();
2243 void icqface::userinfoexternal(const imcontact &ic) {
2244 vector<pair<int, string> > r = external.getlist(imexternal::aomanual, ic.pname);
2246 if(r.empty()) {
2247 log(_("+ no external actions defined for %s"), conf.getprotocolname(ic.pname).c_str());
2249 } else {
2250 verticalmenu m(conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
2252 m.setwindow(textwindow(sizeWArea.x1, sizeWArea.y1, sizeWArea.x1+27, sizeWArea.y1+9, conf.getcolor(cp_main_text)));
2253 m.idle = &menuidle;
2255 vector<pair<int, string> >::const_iterator ir = r.begin();
2256 while(ir != r.end()) {
2257 m.additem((string(" ") + ir->second).c_str());
2258 ++ir;
2261 m.scale();
2262 int i = m.open();
2263 m.close();
2265 if(i) {
2266 char cbuf[512];
2267 snprintf(cbuf, sizeof(cbuf), _("Result of the external action %s:"),
2268 (r.begin()+i-1)->second.c_str());
2270 string buf;
2271 external.execmanual(ic, i-1, buf);
2273 if(!buf.empty()) {
2274 imnotification ev(ic, (string) cbuf + "\n\n" + buf);
2275 saveworkarea();
2276 eventview(&ev, vector<eventviewresult>(), true);
2277 restoreworkarea();
2283 void icqface::showeventbottom(const imcontact &ic) {
2284 const char *text = ischannel(ic) ?
2285 _("%s send, %s multi, %s/%s pr/nxt chat, %s hist, %s URLs, %s expand, %s memb") :
2286 _("%s send, %s multi, %s/%s pr/nxt chat, %s hist, %s URLs, %s expand, %s info");
2288 status(text,
2289 getstatkey(key_send_message, section_editor).c_str(),
2290 getstatkey(key_multiple_recipients, section_editor).c_str(),
2291 getstatkey(key_prev_chat, section_editor).c_str(),
2292 getstatkey(key_next_chat, section_editor).c_str(),
2293 getstatkey(key_history, section_editor).c_str(),
2294 getstatkey(key_show_urls, section_editor).c_str(),
2295 getstatkey(key_fullscreen, section_editor).c_str(),
2296 getstatkey(key_info, section_editor).c_str());
2299 bool icqface::eventedit(imevent &ev) {
2300 bool r;
2301 texteditor editor;
2302 icqcontact *c;
2303 string msg;
2305 editdone = r = false;
2306 passinfo = ev.getcontact();
2308 editor.addscheme(cp_main_text, cp_main_text, 0, 0);
2309 editor.otherkeys = &editmsgkeys;
2310 editor.idle = &editidle;
2311 editor.wrap = true;
2312 editor.smarttab = false;
2313 editor.emacs = conf.getemacs();
2315 saveworkarea();
2316 clearworkarea();
2318 peerinfo(2, ev.getcontact());
2320 mainw.writef(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight),
2321 _("Outgoing %s"), streventname(ev.gettype()));
2323 showeventbottom(ev.getcontact());
2325 if(ev.gettype() == imevent::message) {
2326 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2328 immessage *m = static_cast<immessage *>(&ev);
2329 editor.load(msg = m->gettext(), "");
2331 editdone = false;
2332 editor.open();
2333 r = editdone;
2335 /* TODO, should we relly use a pointer here ? */
2336 char *p = editor.save("\r\n");
2337 *m = immessage(ev.getcontact(), imevent::outgoing, p);
2339 if(c = clist.get(ev.getcontact()))
2340 c->setpostponed(r ? "" : p);
2341 free (p);
2343 } else if(ev.gettype() == imevent::xml) {
2344 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2346 while(1) {
2347 imxmlevent *m = static_cast<imxmlevent *>(&ev);
2348 editor.load(msg = m->gettext(), "");
2350 editdone = false;
2351 editor.open();
2352 r = editdone;
2354 char *p = editor.save("\r\n");
2355 m->setfield("text", p);
2357 if(r)
2358 if(ev.getcontact().pname == livejournal)
2359 if(!setljparams(m)){
2360 free(p);
2361 continue;
2364 if(c = clist.get(ev.getcontact()))
2365 c->setpostponed(r ? "" : p);
2367 free(p);
2368 break;
2371 } else if(ev.gettype() == imevent::url) {
2372 static textinputline urlinp;
2373 imurl *m = static_cast<imurl *>(&ev);
2375 urlinp.setvalue(m->geturl());
2376 urlinp.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2-sizeWArea.x1-2);
2377 urlinp.setcolor(conf.getcolor(cp_main_highlight));
2378 urlinp.idle = &textinputidle;
2380 workarealine(sizeWArea.y1+4);
2382 urlinp.exec();
2384 string url = urlinp.getlastkey() != KEY_ESC ? urlinp.getvalue() : "";
2386 if(!url.empty()) {
2387 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+5, sizeWArea.x2, sizeWArea.y2);
2388 editor.load(m->getdescription(), "");
2389 editor.open();
2391 r = editdone;
2393 char *p = editor.save("\r\n");
2394 *m = imurl(ev.getcontact(), imevent::outgoing, url, p);
2395 free (p);
2398 } else if(ev.gettype() == imevent::sms) {
2399 imsms *m = static_cast<imsms *>(&ev);
2401 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2402 editor.load(msg = m->getmessage(), "");
2403 editor.open();
2405 r = editdone;
2407 auto_ptr<char> p(editor.save("\r\n"));
2408 *m = imsms(ev.getcontact(), imevent::outgoing, p.get(), m->getphone());
2410 if(c = clist.get(ev.getcontact()))
2411 c->setpostponed(r ? "" : p.get());
2413 } else if(ev.gettype() == imevent::authorization) {
2414 imauthorization *m = static_cast<imauthorization *>(&ev);
2416 if (gethook(ev.getcontact().pname).getCapabs().count(hookcapab::authreqwithmessages)) {
2417 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2418 editor.load(msg = m->getmessage(), "");
2419 editor.open();
2420 char *p = editor.save("\r\n");
2421 *m = imauthorization(ev.getcontact(), imevent::outgoing, imauthorization::Request, p);
2422 r = editdone;
2423 free(p);
2424 } else {
2425 *m = imauthorization(ev.getcontact(), imevent::outgoing, imauthorization::Request, "");
2426 r = true;
2430 } else if(ev.gettype() == imevent::contacts) {
2431 imcontacts *m = static_cast<imcontacts *>(&ev);
2432 imcontact cont;
2434 vector<imcontact> smuins = muins;
2435 vector<imcontact>::iterator imc;
2437 vector< pair<unsigned int, string> > clst;
2438 vector< pair<unsigned int, string> >::const_iterator icc;
2440 muins.clear();
2442 for(icc = m->getcontacts().begin(); icc != m->getcontacts().end(); ++icc) {
2443 cont = imcontact();
2444 cont.pname = ev.getcontact().pname;
2446 if(icc->first) cont.uin = icc->first; else
2447 cont.nickname = icc->second;
2449 muins.push_back(cont);
2452 set<protocolname> ps;
2453 ps.insert(ev.getcontact().pname);
2454 r = multicontacts(_("Send contacts.."), ps);
2456 for(imc = muins.begin(); imc != muins.end(); ++imc) {
2457 if(imc->uin) {
2458 icqcontact *cc = clist.get(*imc);
2459 if(cc) imc->nickname = cc->getnick();
2462 clst.push_back(make_pair(imc->uin, imc->nickname));
2465 muins = smuins;
2467 *m = imcontacts(ev.getcontact(), imevent::outgoing, clst);
2469 } else if(ev.gettype() == imevent::file) {
2470 dialogbox db;
2471 int baritem, mitem;
2472 bool finished = false, floop = true;
2473 vector<imfile::record>::const_iterator ir;
2475 imfile *m = static_cast<imfile *>(&ev);
2477 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
2478 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
2479 db.setmenu(new verticalmenu(conf.getcolor(cp_main_menu),
2480 conf.getcolor(cp_main_selected)));
2481 db.setbar(new horizontalbar(conf.getcolor(cp_main_highlight),
2482 conf.getcolor(cp_main_selected),
2483 _("Add"), _("Remove"), _("Send"), (char*)0));
2485 db.addkey(KEY_IC, 0);
2486 db.addkey(KEY_DC, 1);
2488 db.addautokeys();
2489 db.idle = &dialogidle;
2491 db.redraw();
2493 workarealine(sizeWArea.y1+2);
2494 workarealine(sizeWArea.y2-2);
2496 while(!finished && !r) {
2497 db.getmenu()->clear();
2499 for(ir = m->getfiles().begin(); ir != m->getfiles().end(); ++ir)
2500 db.getmenu()->additemf(" %s", ir->fname.c_str());
2502 if(floop) {
2503 baritem = 0;
2504 floop = false;
2505 } else {
2506 finished = !db.open(mitem, baritem);
2509 if(!finished) {
2510 vector<imfile::record> files = m->getfiles();
2512 switch(baritem) {
2513 case 0:
2514 if((msg = inputfile(_("file name: "))).size()) {
2515 struct stat st;
2517 if(!stat(msg.c_str(), &st)) {
2518 imfile::record fr;
2519 fr.fname = msg;
2520 fr.size = st.st_size;
2521 files.push_back(fr);
2524 break;
2525 case 1:
2526 if(mitem > 0 && mitem <= m->getfiles().size())
2527 files.erase(files.begin()+mitem-1);
2528 break;
2529 case 2:
2530 r = true;
2531 break;
2534 m->setfiles(files);
2539 editdone = false;
2540 restoreworkarea();
2541 status("@");
2542 return r;
2545 string icqface::extracttime(const imevent &ev) {
2546 string ds1, ds2, r;
2547 int tdiff;
2548 time_t t, ts;
2549 char buf[64];
2551 ds1 = time2str(&(t = ev.gettimestamp()), conf.gettimestampformat(), buf);
2552 ds2 = time2str(&(ts = ev.getsenttimestamp()), conf.gettimestampformat(), buf);
2554 r = ds1 + " ";
2555 tdiff = ts-t;
2556 if(tdiff < 0) tdiff *= -1;
2558 if(tdiff > 5)
2559 if(ds1 != ds2) r += (string) "[" + ds2 + "] ";
2561 return r;
2564 void icqface::renderchathistory() {
2565 int count, chatmargin;
2566 string text;
2567 time_t lastread;
2568 struct stat st;
2570 vector<imevent *> events;
2571 typedef pair<imevent::imdirection, vector<string> > histentry;
2572 vector<histentry> toshow;
2573 vector<histentry>::iterator ir;
2574 vector<string>::reverse_iterator il;
2576 static imevent *lastev = 0;
2578 delete lastev;
2579 passevent = lastev = 0;
2581 histentry h;
2583 icqcontact *c = clist.get(passinfo);
2584 if(!c) return;
2586 count = 0;
2588 if(!stat((c->getdirname() + "history").c_str(), &st)) {
2589 count = st.st_size-chatlines*(sizeWArea.x2-sizeWArea.x1)*2;
2590 if(count < 0) count = 0;
2593 // Skip history items that won't be displayed
2594 c->sethistoffset(count);
2595 if (chatlastread == 0) chatlastread = 1;
2597 count = 0;
2598 events = em.getevents(passinfo, chatlastread);
2599 chatlastread = 0;
2600 lastread = c->getlastread();
2602 if(events.size()) {
2603 c->setlastread(events.back()->gettimestamp());
2606 while(events.size() > chatlines) {
2607 delete events.front();
2608 events.erase(events.begin());
2611 while(events.size()) {
2612 if(events.back()->getdirection() == imevent::incoming) {
2613 if(events.back()->gettimestamp() > lastread) {
2614 if(events.back()->gettype() == imevent::authorization
2615 || events.back()->gettype() == imevent::contacts
2616 || events.back()->gettype() == imevent::file) {
2617 bool fin, enough;
2618 fin = enough = false;
2619 saveworkarea();
2621 while(!fin && !enough)
2622 cicq.readevent(*events.back(), enough, fin);
2624 restoreworkarea();
2629 if(count < chatlines) {
2630 text = extracttime(*events.back());
2631 text += events.back()->gettext();
2632 chatlastread = events.back()->gettimestamp()-1;
2634 h = histentry();
2635 h.first = events.back()->getdirection();
2636 breakintolines(text, h.second, sizeWArea.x2-sizeWArea.x1-2);
2637 toshow.push_back(h);
2639 if(!lastev)
2640 if(h.first == imevent::incoming) {
2641 passevent = lastev = events.back()->getevent();
2642 extracturls(text);
2645 count += h.second.size();
2648 delete events.back();
2649 events.pop_back();
2652 chatmargin = sizeWArea.y1+chatlines;
2653 text = string(sizeWArea.x2-sizeWArea.x1-2, ' ');
2655 attrset(conf.getcolor(cp_main_text));
2656 for(count = 0; count < chatlines; count++)
2657 mvprintw(chatmargin-count, sizeWArea.x1+2, "%s", text.c_str());
2659 for(count = 0, ir = toshow.begin(); ir != toshow.end() && count < chatlines; ++ir) {
2660 switch(ir->first) {
2661 case imevent::incoming:
2662 attrset(conf.getcolor(cp_main_history_incoming));
2663 break;
2664 case imevent::outgoing:
2665 attrset(conf.getcolor(cp_main_history_outgoing));
2666 break;
2669 for(il = ir->second.rbegin(); il != ir->second.rend() && count < chatlines; ++il) {
2670 kgotoxy(sizeWArea.x1+2, chatmargin-count);
2671 printstring(*il);
2672 count++;
2676 peerinfo(chatlines+1, passinfo);
2679 void icqface::peerinfo(int line, const imcontact &ic) {
2680 workarealine(sizeWArea.y1+line);
2682 icqcontact *c = clist.get(ic);
2683 if(!c) return;
2685 string text = conf.getprotocolname(passinfo.pname) + " " + c->getdispnick();
2686 int maxsize = sizeWArea.x2-sizeWArea.x1-10;
2687 bool pgpon = false;
2689 if(text.size() > maxsize) {
2690 text.erase(maxsize);
2691 text += "..";
2694 #ifdef HAVE_GPGME
2695 if(pgpon = pgp.enabled(passinfo))
2696 text += string(4, ' ');
2697 #endif
2699 #ifdef HAVE_LIBOTR
2700 text += " OTR: " + otr.is_verified(ic.pname, c->getnick()) + " " + otr.get_msg_state(ic.pname, c->getnick());
2701 #endif
2703 text = (string) "[ " + text + " ]";
2705 kwriteatf(sizeWArea.x2-text.size()-1, sizeWArea.y1+line,
2706 conf.getcolor(cp_main_text), "%s", text.c_str());
2708 if(pgpon) {
2709 kwriteatf(sizeWArea.x2-6, sizeWArea.y1+line,
2710 conf.getcolor(cp_main_highlight), "PGP");
2714 bool icqface::chat(const imcontact &ic) {
2715 texteditor editor;
2716 imevent *sendev;
2717 vector<imcontact>::iterator i;
2719 int chatlines_diff = 0; // could get rid of?
2720 bool return_status = false ;
2721 face.stay_in_chat = false ;
2723 icqcontact *c = clist.get(ic);
2724 if(!c) return false;
2726 saveworkarea();
2727 clearworkarea();
2729 chatlastread = 0;
2730 inchat = true;
2731 passinfo = ic;
2732 c->setopenedforchat(true);
2734 muins.clear();
2735 muins.push_back(passinfo);
2737 // resizing doesn't change the effect, so we work from the bottom, not the top
2738 chatlines_diff = sizeWArea.y2 - sizeWArea.y1 ;
2739 chatlines = conf.getchatpanelheight();
2740 if( chatlines == 0 ) { // allow disabling reszing by setting it to zero
2741 chatlines = (int) (chatlines_diff*0.75);
2742 } else {
2743 chatlines = chatlines_diff - chatlines; // changes where the guide line is
2745 if( chatlines < MinPanelHeight ) // bottom
2746 chatlines = (int) (MinPanelHeight);
2748 if( chatlines > (chatlines_diff - MinPanelHeight)) // top
2749 chatlines = (int) (chatlines_diff - MinPanelHeight) ;
2752 conf.setchatpanelheight(chatlines_diff - chatlines);
2754 // workarealine(sizeWArea.y1+chatlines+1);
2756 editor.addscheme(cp_main_text, cp_main_text, 0, 0);
2757 editor.otherkeys = &editmsgkeys;
2758 editor.idle = &editchatidle;
2759 editor.wrap = true;
2760 editor.smarttab = false;
2761 editor.emacs = conf.getemacs();
2762 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+chatlines+2, sizeWArea.x2, sizeWArea.y2);
2763 editor.load(c->getpostponed(), "");
2765 showeventbottom(ic);
2767 for(bool finished = false; !finished && clist.get(ic); ) {
2768 renderchathistory();
2770 editdone = false;
2771 editor.open();
2772 auto_ptr<char> p(editor.save("\r\n"));
2774 editor.close();
2775 editor.load("", "");
2777 if(editdone) {
2778 auto_ptr<imevent> ev(new immessage(ic, imevent::outgoing, p.get()));
2780 for(i = muins.begin(); i != muins.end(); ++i) {
2781 ev.get()->setcontact(*i);
2782 em.store(*ev.get());
2785 muins.clear();
2786 muins.push_back(passinfo);
2788 } else {
2789 finished = true;
2792 c->setpostponed(editdone ? "" : p.get());
2795 c->toggleopenedforchat();
2796 c->save();
2797 restoreworkarea();
2798 status("@");
2799 inchat = false;
2800 update();
2802 if ( face.stay_in_chat == true ) {
2803 return_status = true;
2804 face.stay_in_chat = false;
2807 return return_status;
2810 icqface::eventviewresult icqface::eventview(const imevent *ev,
2811 vector<eventviewresult> abuttons, bool nobuttons) {
2813 string title_event, title_timestamp, text;
2814 horizontalbar *bar = 0;
2815 dialogbox db;
2816 int mitem, baritem;
2817 eventviewresult r;
2818 static int elem = 0;
2820 vector<eventviewresult> actions;
2821 vector<eventviewresult>::iterator ia;
2823 if(!nobuttons) {
2824 if(ev->gettype() == imevent::message
2825 || ev->gettype() == imevent::notification
2826 || ev->gettype() == imevent::xml) {
2827 actions.push_back(forward);
2829 if(ev->getdirection() == imevent::incoming)
2830 if(ev->getcontact().pname != rss /*|| islivejournal(ev->getcontact())*/)
2831 actions.push_back(reply);
2833 } else if(ev->gettype() == imevent::url) {
2834 actions.push_back(forward);
2835 actions.push_back(open);
2837 if(ev->getdirection() == imevent::incoming) {
2838 actions.push_back(reply);
2841 } else if(ev->gettype() == imevent::sms) {
2842 if(ev->getdirection() == imevent::incoming) {
2843 actions.push_back(reply);
2846 } else if(ev->gettype() == imevent::authorization) {
2847 if(ev->getdirection() == imevent::incoming) {
2848 actions.push_back(info);
2849 actions.push_back(accept);
2850 actions.push_back(reject);
2853 } else if(ev->gettype() == imevent::contacts) {
2854 actions.push_back(info);
2855 actions.push_back(add);
2857 } else if(ev->gettype() == imevent::email) {
2858 actions.push_back(forward);
2860 } else if(ev->gettype() == imevent::file) {
2861 actions.push_back(info);
2863 if(gethook(ev->getcontact().pname).knowntransfer(*static_cast<const imfile *>(ev))) {
2864 actions.push_back(accept);
2865 actions.push_back(reject);
2871 actions.push_back(ok);
2872 copy(abuttons.begin(), abuttons.end(), back_inserter(actions));
2874 switch(ev->getdirection()) {
2875 case imevent::outgoing:
2876 title_event = _("Outgoing %s to %s");
2877 title_timestamp = _("Sent on %s");
2878 break;
2879 case imevent::incoming:
2880 title_event = _("Incoming %s from %s");
2881 title_timestamp = _("Received on %s");
2882 break;
2885 clearworkarea();
2887 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+3, sizeWArea.x2,
2888 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
2890 db.setbar(bar = new horizontalbar(conf.getcolor(cp_main_highlight),
2891 conf.getcolor(cp_main_selected), 0));
2893 bar->item = actions.size()-1;
2895 for(ia = actions.begin(); ia != actions.end(); ++ia)
2896 bar->items.push_back(geteventviewresult(*ia));
2898 mainw.writef(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight),
2899 title_event.c_str(), streventname(ev->gettype()), ev->getcontact().totext().c_str());
2901 mainw.writef(sizeWArea.x1+2, sizeWArea.y1+1, conf.getcolor(cp_main_highlight),
2902 title_timestamp.c_str(), extracttime(*ev).c_str());
2904 db.addautokeys();
2906 if(ev->gettype() == imevent::contacts) {
2907 const imcontacts *m = static_cast<const imcontacts *>(ev);
2909 db.setmenu(new verticalmenu(conf.getcolor(cp_main_menu),
2910 conf.getcolor(cp_main_selected)));
2912 vector< pair<unsigned int, string> > lst = m->getcontacts();
2913 vector< pair<unsigned int, string> >::const_iterator il;
2915 for(il = lst.begin(); il != lst.end(); ++il) {
2916 db.getmenu()->additemf(" %s", il->second.c_str());
2919 db.getmenu()->setpos(elem-1);
2921 } else {
2922 text = ev->gettext();
2923 db.setbrowser(new textbrowser(conf.getcolor(cp_main_text)));
2924 db.getbrowser()->setbuf(text);
2925 extracturls(text);
2927 status(_("%s to URLs, %s to full-screenize, %s close"),
2928 getstatkey(key_show_urls, section_history).c_str(),
2929 getstatkey(key_fullscreen, section_history).c_str(),
2930 getstatkey(key_quit, section_editor).c_str());
2934 db.redraw();
2936 workarealine(sizeWArea.y1+3);
2937 workarealine(sizeWArea.y2-2);
2939 db.otherkeys = &eventviewkeys;
2940 db.idle = &dialogidle;
2942 passevent = ev;
2944 if(db.open(mitem, baritem)) {
2945 r = actions[baritem];
2946 } else {
2947 r = cancel;
2950 elem = mitem;
2952 switch(r) {
2953 case add:
2954 case info:
2955 extk = elem;
2956 break;
2959 passevent = 0;
2960 return r;
2963 void icqface::fullscreenize(const imevent *ev) {
2964 textwindow w(0, 1, COLS, LINES-1, conf.getcolor(cp_main_text), TW_NOBORDER);
2965 textbrowser tb(0, 4, COLS, LINES-1, conf.getcolor(cp_main_text));
2967 int k;
2968 char buf[512], *fmt = 0;
2969 vector<string> lines;
2970 vector<string>::const_iterator il;
2972 w.open();
2974 switch(ev->getdirection()) {
2975 case imevent::incoming: fmt = _("%s from %s, received on %s"); break;
2976 case imevent::outgoing: fmt = _("%s to %s, sent on %s"); break;
2979 snprintf(buf, sizeof(buf), fmt, streventname(ev->gettype()),
2980 ev->getcontact().totext().c_str(),
2981 strdateandtime(ev->gettimestamp()).c_str());
2983 w.write(0, 1, conf.getcolor(cp_main_highlight), buf);
2985 xtermtitle((string) _("full-screen view") + " " + buf);
2987 tb.setbuf(ev->gettext());
2989 blockmainscreen();
2991 status(_("%s or %s to close, Up/Down and PgUp/PgDn to scroll"),
2992 getstatkey(key_quit, section_editor).c_str(),
2993 getstatkey(key_fullscreen, section_editor).c_str());
2995 tb.idle = &textbrowseridle;
2996 tb.otherkeys = &fullscreenkeys;
2997 tb.open();
2999 unblockmainscreen();
3000 status("@");
3002 w.close();
3005 void icqface::histmake(const vector<imevent *> &hist) {
3006 vector<imevent *>::const_reverse_iterator i;
3007 string text;
3008 time_t t, ts;
3009 char buf[64];
3010 int color;
3012 mhist.clear();
3013 mhist.setpos(0);
3015 for(i = hist.rbegin(); i != hist.rend(); ++i) {
3016 imevent &ev = **i;
3018 color = 0;
3020 t = ev.gettimestamp();
3021 ts = ev.getsenttimestamp();
3022 text = (string) + " " + time2str(&t, conf.gettimestampformat(), buf) + " ";
3023 if ((t - ts) > 0)
3024 text += (string) + "[" + time2str(&ts, conf.gettimestampformat(), buf) + "] ";
3025 text += ev.gettext();
3027 if(ev.getdirection() == imevent::incoming) {
3028 color = conf.getcolor(cp_main_history_incoming);
3029 } else if(ev.getdirection() == imevent::outgoing) {
3030 color = conf.getcolor(cp_main_history_outgoing);
3033 mhist.additem(color, (void *) *i, text);
3037 bool icqface::histexec(imevent *&im) {
3038 dialogbox db;
3039 bool r, fin;
3040 int k;
3041 static string sub;
3042 char buf[512];
3044 r = fin = false;
3046 if(!mhist.empty()) {
3047 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
3048 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
3050 db.setmenu(&mhist, false);
3054 * Now set the menu position
3058 for(k = 0; k < mhist.getcount(); k++) {
3059 imevent *rim = static_cast<imevent *> (mhist.getref(k));
3060 if(rim == im) {
3061 mhist.setpos(k);
3062 break;
3066 db.idle = &dialogidle;
3067 db.otherkeys = &historykeys;
3069 saveworkarea();
3070 clearworkarea();
3072 db.redraw();
3073 workarealine(sizeWArea.y1+2);
3075 im = static_cast<imevent *> (mhist.getref(0));
3077 snprintf(buf, sizeof(buf), _("History for %s, %d events total"),
3078 im->getcontact().totext().c_str(),
3079 mhist.getcount());
3081 mainw.write(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight), buf);
3083 status(_("%s search, %s again, %s cancel"),
3084 getstatkey(key_search, section_history).c_str(),
3085 getstatkey(key_search_again, section_history).c_str(),
3086 getstatkey(key_quit, section_editor).c_str());
3088 while(!fin) {
3089 if(db.open(k)) {
3090 switch(k) {
3091 case -2:
3092 sub = face.inputstr(_("search for: "), sub);
3093 if(input.getlastkey() == KEY_ESC) break;
3094 case -3:
3095 if(!sub.empty())
3096 for(k = mhist.getpos()+1; k < mhist.getcount(); k++) {
3097 im = static_cast<imevent *> (mhist.getref(k));
3098 if(im)
3099 if(im->contains(sub)) {
3100 mhist.setpos(k);
3101 break;
3104 break;
3105 case -4:
3106 r = false;
3107 fin = true;
3108 break;
3110 default:
3111 if(mhist.getref(k-1)) {
3112 im = static_cast<imevent *> (mhist.getref(k-1));
3113 r = fin = true;
3115 break;
3117 } else {
3118 r = false;
3119 fin = true;
3123 db.close();
3124 restoreworkarea();
3127 return r;
3130 // ----------------------------------------------------------------------------
3132 void icqface::menuidle(verticalmenu &m) {
3133 cicq.idle();
3135 if(face.dotermresize) {
3136 if(&m == &face.mcontacts->menu) {
3137 face.redraw();
3138 face.dotermresize = false;
3144 void icqface::dialogidle(dialogbox &caller) {
3145 cicq.idle();
3148 void icqface::textbrowseridle(textbrowser &b) {
3149 cicq.idle();
3152 void icqface::transferidle(dialogbox &b) {
3153 time_t tstart = time(0);
3155 while(!cicq.idle(HIDL_SOCKEXIT)) {
3156 if(time(0)-tstart > 3) {
3157 b.gettree()->menu.abort();
3158 break;
3163 string icqface::getstatkey(int key, int section) const {
3164 int i = 2;
3165 string key2;
3166 string keys = action2key(key, section);
3168 while((key2 = action2key(key, section, i)) != "") {
3169 keys += "/" + key2;
3170 i++;
3173 return keys;
3176 string icqface::getmenuitem(string mtext, int width, int key, int section) {
3177 int i = 2;
3178 string text = " " + mtext + " ";
3179 string keyname = action2key(key, section);
3180 string key2;
3181 int s = keyname.size()+text.size();
3183 if (s > width)
3184 return text;
3186 while ((key2 = action2key(key, section, i)) != "" && (s += key2.size()+1) < width) {
3187 keyname += "/" + key2;
3188 i++;
3191 for (s = keyname.size()+text.size(); s < width; s++)
3192 text += " ";
3194 text += keyname;
3196 return text;
3199 int icqface::key2action(int k, int s) {
3200 for(vector<icqconf::keybinding>::size_type i = 0; i < icqconf::keys.size(); i++)
3201 if(k == icqconf::keys[i].key && s == icqconf::keys[i].section)
3202 return icqconf::keys[i].action;
3204 return -1;
3207 string icqface::action2key(int a, int s, int n) const {
3208 string key;
3209 #ifdef HAVE_SSTREAM
3210 std::ostringstream o;
3211 #else
3212 std::ostrstream o;
3213 #endif
3214 vector<icqconf::keybinding>::size_type i = 0;
3216 for( ; i < icqconf::keys.size(); i++)
3217 if(a == icqconf::keys[i].action && s == icqconf::keys[i].section) {
3218 if (n <= 1)
3219 break;
3220 n--;
3223 if (i == icqconf::keys.size())
3224 return "";
3225 else if(icqconf::keys[i].key == '\r')
3226 return "enter";
3227 else if(icqconf::keys[i].key == ' ')
3228 return "space";
3229 else if(icqconf::keys[i].key == '/')
3230 return "\'/\'";
3231 else if(icqconf::keys[i].key == ALT('\r'))
3232 return "alt-enter";
3233 else if(icqconf::keys[i].key == CTRL(' '))
3234 return "^space";
3235 else if(icqconf::keys[i].key == ALT(' '))
3236 return "alt-space";
3237 else if(icqconf::keys[i].key == 339)
3238 return "pageup";
3239 else if(icqconf::keys[i].key == 338)
3240 return "pagedown";
3241 else if(icqconf::keys[i].key == 9)
3242 return "tab";
3243 else if(icqconf::keys[i].key == 27)
3244 return "esc-esc";
3245 else if(icqconf::keys[i].key == 331)
3246 return "insert";
3247 else if(icqconf::keys[i].key == KEY_DC)
3248 return "del";
3249 else if(icqconf::keys[i].alt && icqconf::keys[i].ctrl)
3250 o << "F" << (icqconf::keys[i].key-KEY_F0);
3251 else if(icqconf::keys[i].alt)
3252 o << "alt-" << (char)icqconf::keys[i].key;
3253 else if(icqconf::keys[i].ctrl)
3254 o << "^" << (char)(icqconf::keys[i].key+64);
3255 else
3256 o << (char)icqconf::keys[i].key;
3258 string ret;
3259 #ifdef HAVE_SSTREAM
3260 ret = o.str();
3261 #else
3262 ret = string(o.str(), o.pcount());
3263 o.freeze(false);
3264 #endif
3266 return ret;
3269 int icqface::contactskeys(verticalmenu &m, int k) {
3270 icqcontact *c = NULL;
3272 int id = face.mcontacts->getid(face.mcontacts->menu.getpos());
3273 if(id != -1 && ! face.mcontacts->isnode(id))
3274 c = (icqcontact *) face.mcontacts->getref(m.getpos()+1);
3276 set<hookcapab::enumeration> capab;
3278 face.extk = 0;
3280 if(c)
3281 if(c->getdesc() != contactroot) {
3282 capab = gethook(c->getdesc().pname).getCapabs();
3285 switch(face.key2action(k, section_contact)) {
3286 case key_info:
3287 face.extk = ACT_INFO;
3288 break;
3290 case key_remove:
3291 face.extk = ACT_REMOVE;
3292 break;
3294 case key_quit:
3295 if (conf.getaskquit())
3296 if(face.ask("Really Quit?", ASK_YES | ASK_NO, ASK_NO) == ASK_NO)
3297 break;
3298 face.extk = ACT_QUIT;
3299 break;
3301 case key_send_url:
3302 if(capab.count(hookcapab::urls))
3303 face.extk = ACT_URL;
3304 break;
3306 case key_change_status:
3307 face.extk = ACT_STATUS;
3308 break;
3310 case key_history:
3311 face.extk = ACT_HISTORY;
3312 break;
3314 case key_next_chat:
3315 face.extk = ACT_DUMMY;
3316 face.next_chat(true);
3317 break;
3319 case key_prev_chat:
3320 face.extk = ACT_DUMMY;
3321 face.next_chat(false);
3322 break;
3324 case key_add: face.extk = ACT_ADD; break;
3326 case key_send_contact:
3327 if(capab.count(hookcapab::contacts))
3328 face.extk = ACT_CONTACT;
3329 break;
3330 case key_rss_check:
3331 if(c && c->getdesc().pname == rss)
3332 face.extk = ACT_PING;
3333 break;
3335 case key_fetch_away_message:
3336 if(capab.count(hookcapab::fetchaway))
3337 face.extk = ACT_FETCHAWAY;
3338 break;
3340 case key_user_menu:
3341 face.extk = ACT_MENU;
3342 break;
3344 case key_general_menu:
3345 face.extk = ACT_GMENU;
3346 break;
3348 case key_hide_offline:
3349 face.extk = ACT_HIDEOFFLINE;
3350 break;
3352 case key_contact_external_action:
3353 if(!ischannel(c) && c)
3354 face.extk = ACT_EXTERN;
3355 break;
3357 case key_rename:
3358 if(!ischannel(c) && c)
3359 face.extk = ACT_RENAME;
3360 break;
3362 case key_version:
3363 if(capab.count(hookcapab::version))
3364 face.extk = ACT_VERSION;
3365 break;
3367 case key_edit:
3368 if(!ischannel(c) && c)
3369 face.extk = ACT_EDITUSER;
3370 break;
3372 case key_ignore:
3373 if(c) face.extk = ACT_IGNORE;
3374 break;
3376 case key_left_panel_move_right:
3377 face.leftpanelwidth_inc(1);
3378 face.redraw();
3379 break;
3381 case key_left_panel_move_left:
3382 face.leftpanelwidth_inc(-1);
3383 face.redraw();
3384 break;
3386 case key_log_panel_move_up:
3387 face.logpanelheight_inc(1);
3388 face.redraw();
3389 break;
3391 case key_log_panel_move_down:
3392 face.logpanelheight_inc(-1);
3393 face.redraw();
3394 break;
3396 case key_quickfind: face.extk = ACT_QUICKFIND; break;
3399 if(face.extk && face.key2action(k, section_contact) != -1) {
3400 return m.getpos()+1;
3401 } else {
3402 return -1;
3406 int icqface::multiplekeys(verticalmenu &m, int k) {
3407 switch(k) {
3408 case ' ':
3409 case 'x':
3410 case 'X':
3411 return -2;
3412 case '/':
3413 return -3;
3416 if(face.key2action(k, section_contact) == key_quickfind)
3417 return -3;
3419 return -1;
3422 int icqface::findpgpkeys(dialogbox &db, int k) {
3423 int r;
3425 r = face.key2action(k, section_contact);
3426 switch(r) {
3427 case key_quickfind:
3428 return -3;
3431 return -1;
3434 string icqface::getprotocolchar(protocolname pname) const {
3435 static const string pprefixes[protocolname_size] = {
3436 "i", "y", "m", "a", "#", "j", "r", "l", "g", "n"
3439 if(pname > protocolname_size)
3440 return "x";
3442 return pprefixes[pname];
3445 int icqface::historykeys(dialogbox &db, int k) {
3446 static string sub;
3448 switch(face.key2action(k, section_history)) {
3449 case key_search:
3450 return -2;
3451 case key_search_again:
3452 return -3;
3453 case key_quit:
3454 return -4;
3457 return -1;
3460 int icqface::editmsgkeys(texteditor &e, int k) {
3461 char *p;
3463 if(k == '\r' && conf.getentersends(face.passinfo.pname)) {
3464 p = e.save("");
3465 face.editdone = strlen(p);
3466 free(p);
3467 if(face.editdone) return -1; else return 0;
3470 switch(face.key2action(k, section_editor)) {
3471 case key_send_message:
3472 p = e.save("");
3473 face.editdone = strlen(p);
3474 free(p);
3475 if(face.editdone) return -1; else break;
3476 case key_multiple_recipients:
3477 face.multicontacts("");
3478 break;
3479 case key_history:
3480 cicq.history(face.passinfo);
3481 break;
3482 case key_prev_chat:
3483 face.editdone = false;
3484 face.next_chat(false);
3485 return -1;
3486 case key_next_chat:
3487 face.editdone = false;
3488 face.next_chat(true);
3489 return -1;
3490 case key_out_chat:
3491 face.editdone = false;
3492 face.next_chat(false);
3493 face.last_selected = (icqcontact* ) clist.at(0);
3494 return -1;
3495 case key_info:
3496 cicq.userinfo(face.passinfo);
3497 break;
3498 case key_show_urls:
3499 face.showextractedurls(); break;
3500 case key_fullscreen:
3501 if(face.passevent)
3502 face.fullscreenize(face.passevent);
3503 break;
3504 case key_chat_panel_move_up:
3505 face.chatpanelheight_inc(1);
3506 face.stay_in_chat = true;
3507 return -1;
3508 break;
3509 case key_chat_panel_move_down:
3510 face.chatpanelheight_inc(-1);
3511 face.stay_in_chat = true;
3512 return -1;
3513 break;
3515 #ifdef HAVE_LIBOTR
3516 case key_otr_start_session:
3517 otr.start_session(face.last_selected);
3518 face.stay_in_chat = true;
3519 return -1;
3520 break;
3521 case key_otr_end_session:
3522 otr.end_session(face.last_selected);
3523 face.stay_in_chat = true;
3524 return -1;
3525 break;
3526 #endif
3528 case key_quit:
3529 return -1;
3532 return 0;
3535 int icqface::userinfokeys(dialogbox &db, int k) {
3536 switch(face.key2action(k, section_info)) {
3537 case key_show_urls: face.showextractedurls(); break;
3538 case key_user_external_action: face.userinfoexternal(face.passinfo); break;
3541 return -1;
3544 int icqface::eventviewkeys(dialogbox &db, int k) {
3545 switch(face.key2action(k, section_history)) {
3546 case key_show_urls:
3547 face.showextractedurls();
3548 break;
3549 case key_fullscreen:
3550 face.fullscreenize(face.passevent);
3551 break;
3552 case key_quit:
3553 return 0;
3556 return -1;
3559 int icqface::findreskeys(dialogbox &db, int k) {
3560 if(face.key2action(k, section_contact) == key_quickfind)
3561 return -3;
3563 return -1;
3566 int icqface::statuskeys(verticalmenu &m, int k) {
3567 char *status_order = "o_adnlcfi"; // Shortcuts for status
3568 char *p = strchr(status_order, k);
3569 if (p)
3570 return (1 + p - status_order);
3571 return -1;
3574 int icqface::fullscreenkeys(textbrowser &m, int k) {
3575 switch(face.key2action(k, section_editor)) {
3576 case key_fullscreen:
3577 return 0;
3580 return -1;
3583 void icqface::editidle(texteditor &e) {
3584 cicq.idle();
3587 void icqface::editchatidle(texteditor &e) {
3588 icqcontact *c;
3590 cicq.idle(HIDL_SOCKEXIT);
3592 if(c = clist.get(face.passinfo))
3593 if(c->hasevents()) {
3594 face.renderchathistory();
3598 void icqface::textinputidle(textinputline &il) {
3599 cicq.idle();
3602 void icqface::termresize(void) {
3603 face.dotermresize = true;
3606 void icqface::relaxedupdate() {
3607 fneedupdate = true;
3610 bool icqface::updaterequested() {
3611 return fneedupdate;
3614 void icqface::redraw() {
3615 if(!mainscreenblock) {
3616 done();
3617 init();
3618 draw();
3621 * Terminal resize specific
3624 vector<string> flog;
3625 vector<string>::iterator il;
3627 face.status("#");
3629 flog = face.lastlog;
3630 face.lastlog.clear();
3632 bool lts, lo;
3633 conf.getlogoptions(lts, lo);
3634 conf.setlogoptions(false, lo);
3636 for(il = flog.begin() ; il != flog.end(); ++il)
3637 face.log(*il);
3639 conf.setlogoptions(lts, lo);
3641 doredraw = fneedupdate = false;
3642 } else {
3643 doredraw = fneedupdate = true;
3647 void icqface::xtermtitle(const string &text) {
3648 if(conf.getxtitles()) {
3649 string term = getenv("TERM") ? getenv("TERM") : "";
3651 if(term == "xterm" || term == "Eterm" || term == "aterm"
3652 || term == "rxvt" || term.substr(0, 6) == "screen")
3653 cout << "\x1b]1;\x07\x1b]0;" << "centerim" << (text.empty() ? "" : (string) ": " + text) << "\x07" << flush;
3657 void icqface::xtermtitle(const char *fmt, ...) {
3658 if(conf.getxtitles()) {
3659 va_list ap;
3660 char buf[1024];
3662 va_start(ap, fmt);
3663 vsnprintf(buf, sizeof(buf), fmt, ap);
3664 xtermtitle((string) buf);
3665 va_end(ap);
3669 void icqface::xtermtitlereset() {
3670 if(conf.getxtitles()) {
3671 const char *p = getenv("TERM");
3672 if(p) xtermtitle((string) p);
3676 // ----------------------------------------------------------------------------
3678 icqface::icqprogress::icqprogress() {
3679 w = 0;
3682 icqface::icqprogress::~icqprogress() {
3683 delete w;
3686 void icqface::icqprogress::log(const char *fmt, ...) {
3687 va_list ap;
3688 char buf[1024];
3690 va_start(ap, fmt);
3691 vsnprintf(buf, sizeof(buf), fmt, ap);
3692 va_end(ap);
3694 if(curline >= face.sizeDlg.height-1) {
3695 w->redraw();
3696 curline = 0;
3699 w->write(2, 1+curline++, buf);
3702 void icqface::icqprogress::show(const string &title ) {
3703 if(!w) {
3704 w = new textwindow(0, 0, face.sizeDlg.width, face.sizeDlg.height,
3705 conf.getcolor(cp_dialog_frame), TW_CENTERED);
3708 w->set_title(conf.getcolor(cp_dialog_highlight), title);
3709 w->open();
3711 curline = 0;
3714 void icqface::icqprogress::hide() {
3715 w->close();
3718 void icqface::leftpanelwidth_inc(const int inc) {
3719 int i = conf.getleftpanelwidth();
3720 if ( ((i+inc) >= MinPanelWidth) && ((i+inc) <= (COLS - MinPanelWidth))) {
3721 conf.setleftpanelwidth(i+inc);
3722 dotermresize = true ;
3726 void icqface::logpanelheight_inc(const int inc) {
3727 int i = conf.getlogpanelheight();
3728 if (((i+inc) >= MinPanelHeight) && ((i+inc) <= (LINES - MinPanelHeight))) {
3729 conf.setlogpanelheight(i+inc);
3730 dotermresize = true ;
3734 void icqface::chatpanelheight_inc(const int inc ) {
3735 chatlines = conf.getchatpanelheight()+inc;
3736 conf.setchatpanelheight(chatlines);