moved the old Changelog, which only contained log from the old centericq
[centerim.git] / src / icqface.cc
blob1cd436c6226fd6aae6b1a1e6a87cefd80233dac6
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);
1136 buf = "";
1137 if (bi.serverbased)
1138 buf += _("Server-based contact");
1140 if (bi.authawait) {
1141 if (!buf.empty())
1142 buf += ", ";
1143 buf += _("Awaiting authorization");
1146 if (!buf.empty())
1147 mainw.write(sizeWArea.x1+2, sizeWArea.y1+15, conf.getcolor(cp_main_highlight), buf);
1150 void icqface::infohome(dialogbox &db, icqcontact *c) {
1151 int i, x;
1153 icqcontact::basicinfo bi = c->getbasicinfo();
1154 icqcontact::moreinfo mi = c->getmoreinfo();
1156 workarealine(sizeWArea.y1+10);
1157 x = sizeWArea.x1+2;
1159 mainw.write(x, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Address"));
1160 mainw.write(x, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Location"));
1161 mainw.write(x, sizeWArea.y1+4, conf.getcolor(cp_main_highlight), _("Zip code"));
1162 mainw.write(x, sizeWArea.y1+5, conf.getcolor(cp_main_highlight), _("Phone"));
1163 mainw.write(x, sizeWArea.y1+6, conf.getcolor(cp_main_highlight), _("Fax"));
1164 mainw.write(x, sizeWArea.y1+7, conf.getcolor(cp_main_highlight), _("Cellular"));
1165 mainw.write(x, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("Timezone"));
1167 mainw.write(x, sizeWArea.y1+10, conf.getcolor(cp_main_highlight), _("Homepage"));
1169 if(!bi.state.empty()) {
1170 if(bi.city.size()) bi.city += ", ";
1171 bi.city += bi.state;
1174 if(bi.country) {
1175 if(bi.city.size()) bi.city += ", ";
1176 bi.city += abstracthook::getCountryIDtoString(bi.country);
1179 x += 10;
1180 mainw.write(x, sizeWArea.y1+2, conf.getcolor(cp_main_text), bi.street);
1181 mainw.write(x, sizeWArea.y1+3, conf.getcolor(cp_main_text), bi.city);
1182 mainw.write(x, sizeWArea.y1+4, conf.getcolor(cp_main_text), bi.zip);
1183 mainw.write(x, sizeWArea.y1+5, conf.getcolor(cp_main_text), bi.phone);
1184 mainw.write(x, sizeWArea.y1+6, conf.getcolor(cp_main_text), bi.fax);
1185 mainw.write(x, sizeWArea.y1+7, conf.getcolor(cp_main_text), bi.cellular);
1186 mainw.write(x, sizeWArea.y1+8, conf.getcolor(cp_main_text), mi.strtimezone());
1188 for(i = 0; !mi.homepage.empty(); i++) {
1189 mainw.write(x, sizeWArea.y1+10+i, conf.getcolor(cp_main_text),
1190 mi.homepage.substr(0, sizeWArea.x2-sizeWArea.x1-12));
1191 mi.homepage.erase(0, sizeWArea.x2-sizeWArea.x1-12);
1195 void icqface::infowork(dialogbox &db, icqcontact *c) {
1196 int i;
1197 icqcontact::workinfo wi = c->getworkinfo();
1199 workarealine(sizeWArea.y1+6);
1200 workarealine(sizeWArea.y1+11);
1202 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Address"));
1203 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Location"));
1204 mainw.write(sizeWArea.x1+2, sizeWArea.y1+4, conf.getcolor(cp_main_highlight), _("Zip code"));
1206 mainw.write(sizeWArea.x1+2, sizeWArea.y1+6, conf.getcolor(cp_main_highlight), _("Company"));
1207 mainw.write(sizeWArea.x1+2, sizeWArea.y1+7, conf.getcolor(cp_main_highlight), _("Department"));
1208 mainw.write(sizeWArea.x1+2, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("Title"));
1210 mainw.write(sizeWArea.x1+2, sizeWArea.y1+11, conf.getcolor(cp_main_highlight), _("Phone"));
1211 mainw.write(sizeWArea.x1+2, sizeWArea.y1+12, conf.getcolor(cp_main_highlight), _("Fax"));
1212 mainw.write(sizeWArea.x1+2, sizeWArea.y1+13, conf.getcolor(cp_main_highlight), _("Homepage"));
1214 if(!wi.state.empty()) {
1215 if(wi.city.size()) wi.city += ", ";
1216 wi.city += wi.state;
1219 if(wi.country) {
1220 if(wi.city.size()) wi.city += ", ";
1221 wi.city += abstracthook::getCountryIDtoString(wi.country);
1224 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), wi.street);
1225 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), wi.city);
1226 mainw.write(sizeWArea.x1+14, sizeWArea.y1+4, conf.getcolor(cp_main_text), wi.zip);
1227 mainw.write(sizeWArea.x1+14, sizeWArea.y1+6, conf.getcolor(cp_main_text), wi.company);
1228 mainw.write(sizeWArea.x1+14, sizeWArea.y1+7, conf.getcolor(cp_main_text), wi.dept);
1229 mainw.write(sizeWArea.x1+14, sizeWArea.y1+8, conf.getcolor(cp_main_text), wi.position);
1230 mainw.write(sizeWArea.x1+14, sizeWArea.y1+11, conf.getcolor(cp_main_text), wi.phone);
1231 mainw.write(sizeWArea.x1+14, sizeWArea.y1+12, conf.getcolor(cp_main_text), wi.fax);
1233 for(i = 0; !wi.homepage.empty(); i++) {
1234 mainw.write(sizeWArea.x1+14, sizeWArea.y1+13+i, conf.getcolor(cp_main_text),
1235 wi.homepage.substr(0, sizeWArea.x2-sizeWArea.x1-14));
1236 wi.homepage.erase(0, sizeWArea.x2-sizeWArea.x1-14);
1240 void icqface::infoabout(dialogbox &db, icqcontact *c) {
1241 db.getbrowser()->setbuf(c->getabout());
1244 void icqface::infointerests(dialogbox &db, icqcontact *c) {
1245 string text;
1246 vector<string> data;
1247 vector<string>::iterator i;
1249 data = c->getinterests();
1251 if(!data.empty()) {
1252 text += (string) "* " + _("Interests") + "\n";
1254 for(i = data.begin(); i != data.end(); ++i) {
1255 text += " + " + *i + "\n";
1258 text += "\n";
1261 data = c->getbackground();
1263 if(!data.empty()) {
1264 text += (string) "* " + _("Background") + "\n";
1266 for(i = data.begin(); i != data.end(); ++i)
1267 text += " + " + *i + "\n";
1270 commaform(text);
1271 db.getbrowser()->setbuf(text);
1274 void icqface::inforss(dialogbox &db, icqcontact *c) {
1275 icqcontact::basicinfo bi = c->getbasicinfo();
1276 icqcontact::moreinfo mi = c->getmoreinfo();
1277 icqcontact::workinfo wi = c->getworkinfo();
1279 workarealine(sizeWArea.y1+7);
1280 workarealine(sizeWArea.y1+10);
1282 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Handle"));
1283 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("RSS doc"));
1284 mainw.write(sizeWArea.x1+2, sizeWArea.y1+4, conf.getcolor(cp_main_highlight), _("Version"));
1285 mainw.write(sizeWArea.x1+2, sizeWArea.y1+5, conf.getcolor(cp_main_highlight), _("Encoding"));
1287 mainw.write(sizeWArea.x1+2, sizeWArea.y1+7, conf.getcolor(cp_main_highlight), _("Title"));
1288 mainw.write(sizeWArea.x1+2, sizeWArea.y1+8, conf.getcolor(cp_main_highlight), _("Link"));
1290 mainw.write(sizeWArea.x1+2, sizeWArea.y1+10, conf.getcolor(cp_main_highlight), _("Frequency"));
1291 mainw.write(sizeWArea.x1+2, sizeWArea.y1+11, conf.getcolor(cp_main_highlight), _("Last check"));
1292 mainw.write(sizeWArea.x1+2, sizeWArea.y1+12, conf.getcolor(cp_main_highlight), _("Next check"));
1293 mainw.write(sizeWArea.x1+2, sizeWArea.y1+13, conf.getcolor(cp_main_highlight), _("Last result"));
1295 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), c->getnick());
1296 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), wi.homepage);
1297 mainw.write(sizeWArea.x1+14, sizeWArea.y1+4, conf.getcolor(cp_main_text), bi.city);
1298 mainw.write(sizeWArea.x1+14, sizeWArea.y1+5, conf.getcolor(cp_main_text), bi.state);
1300 mainw.write(sizeWArea.x1+14, sizeWArea.y1+7, conf.getcolor(cp_main_text), bi.fname);
1301 mainw.write(sizeWArea.x1+14, sizeWArea.y1+8, conf.getcolor(cp_main_text), mi.homepage);
1303 string freq, last, next;
1304 char buf[512];
1306 if(mi.checkfreq) {
1307 snprintf(buf, sizeof(buf), _("%lu minutes"), mi.checkfreq);
1308 freq = buf;
1309 if(mi.checklast) next = strdateandtime(mi.checklast+mi.checkfreq*60);
1310 else next = _("Now");
1311 } else {
1312 freq = next = _("Never");
1315 if(mi.checklast) last = strdateandtime(mi.checklast);
1316 else last = _("Never");
1318 mainw.write(sizeWArea.x1+14, sizeWArea.y1+10, conf.getcolor(cp_main_text), freq);
1319 mainw.write(sizeWArea.x1+14, sizeWArea.y1+11, conf.getcolor(cp_main_text), last);
1320 mainw.write(sizeWArea.x1+14, sizeWArea.y1+12, conf.getcolor(cp_main_text), next);
1321 mainw.write(sizeWArea.x1+14, sizeWArea.y1+13, conf.getcolor(cp_main_text), bi.lname);
1324 void icqface::infoljrss(dialogbox &db, icqcontact *c) {
1325 icqcontact::basicinfo bi = c->getbasicinfo();
1326 icqcontact::moreinfo mi = c->getmoreinfo();
1328 mainw.write(sizeWArea.x1+2, sizeWArea.y1+2, conf.getcolor(cp_main_highlight), _("Birthdate"));
1329 mainw.write(sizeWArea.x1+2, sizeWArea.y1+3, conf.getcolor(cp_main_highlight), _("Full name"));
1331 mainw.write(sizeWArea.x1+14, sizeWArea.y1+2, conf.getcolor(cp_main_text), mi.strbirthdate());
1332 mainw.write(sizeWArea.x1+14, sizeWArea.y1+3, conf.getcolor(cp_main_text), bi.email);
1335 void icqface::userinfo(const imcontact &cinfo, const imcontact &realinfo) {
1336 bool finished = false, showinfo;
1337 textbrowser tb(conf.getcolor(cp_main_text));
1338 dialogbox db;
1339 int k, lastb, b;
1341 imcontact savepassinfo = passinfo;
1342 icqcontact *c = clist.get(passinfo = realinfo);
1344 b = lastb = 0;
1345 saveworkarea();
1346 clearworkarea();
1348 status(_("%s to URLs, %s external actions, %s close"),
1349 getstatkey(key_show_urls, section_info).c_str(),
1350 getstatkey(key_user_external_action, section_info).c_str(),
1351 getstatkey(key_quit, section_editor).c_str());
1353 xtermtitle(_("user info for %s"), realinfo.totext().c_str());
1355 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
1356 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
1358 if(cinfo.pname == rss) {
1359 if(c->inlist() && realinfo != contactroot) {
1360 db.setbar(new horizontalbar(sizeWArea.x1+2, sizeWArea.y2-1,
1361 conf.getcolor(cp_main_highlight), conf.getcolor(cp_main_selected),
1362 _("Info"), _("About"), _("Check"), _("Edit"), islivejournal(c) ? _("LJ") : 0, (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"), _("About"), _("Retreive"), (char*)0));
1369 } else if(cinfo.pname == livejournal) {
1370 db.setbar(new horizontalbar(sizeWArea.x1+2, sizeWArea.y2-1,
1371 conf.getcolor(cp_main_highlight), conf.getcolor(cp_main_selected),
1372 _("Info"), _("Friend of"), (char*)0));
1375 } else {
1376 db.setbar(new horizontalbar(sizeWArea.x1+2, sizeWArea.y2-1,
1377 conf.getcolor(cp_main_highlight), conf.getcolor(cp_main_selected),
1378 _("Info"), _("Home"), _("Work"), _("More"), _("About"),
1379 cinfo.pname != infocard ? _("Retrieve") : _("Edit"), (char*)0));
1383 showinfo = true;
1384 db.addautokeys();
1385 db.idle = &dialogidle;
1386 db.otherkeys = &userinfokeys;
1388 while(!finished) {
1389 if(!showinfo) {
1390 if(cicq.idle(HIDL_SOCKEXIT)) {
1391 finished = !db.open(k, b);
1392 showinfo = !finished;
1396 if(c->updated()) {
1397 showinfo = true;
1398 c->unsetupdated();
1401 if(showinfo) {
1402 db.setbrowser(0);
1403 infoclear(db, c, cinfo);
1405 if(cinfo.pname == rss) switch(b) {
1406 case 0: inforss(db, c); break;
1407 case 1:
1408 db.setbrowser(&tb, false);
1409 infoabout(db, c);
1410 infoclear(db, c, cinfo);
1411 break;
1412 case 2:
1413 gethook(cinfo.pname).ping(c);
1414 b = lastb;
1415 continue;
1416 case 3:
1417 updatedetails(c, c->getdesc().pname);
1418 showinfo = true;
1419 b = lastb;
1420 continue;
1421 case 4: infoljrss(db, c); break;
1423 } else if(cinfo.pname == livejournal) switch(b) {
1424 case 0: infolivejournal(db, c); break;
1425 case 1:
1426 db.setbrowser(&tb, false);
1427 infofriends(db, c);
1428 infoclear(db, c, cinfo);
1429 break;
1431 } else switch(b) {
1432 case 0: infogeneral(db, c); break;
1433 case 1: infohome(db, c); break;
1434 case 2: infowork(db, c); break;
1435 case 3:
1436 db.setbrowser(&tb, false);
1437 infointerests(db, c);
1438 infoclear(db, c, cinfo);
1439 break;
1440 case 4:
1441 db.setbrowser(&tb, false);
1442 infoabout(db, c);
1443 infoclear(db, c, cinfo);
1444 break;
1445 case 5:
1446 switch(cinfo.pname) {
1447 case infocard:
1448 updatedetails(c);
1449 showinfo = true;
1450 break;
1452 default:
1453 gethook(cinfo.pname).requestinfo(cinfo);
1454 break;
1457 b = lastb;
1458 continue;
1462 refresh();
1463 showinfo = false;
1464 lastb = b;
1468 db.close();
1469 restoreworkarea();
1470 status("@");
1471 passinfo = savepassinfo;
1474 void icqface::makeprotocolmenu(verticalmenu &m) {
1475 icqconf::imaccount ia;
1476 protocolname ipname;
1478 static const string pitems[protocolname_size] = {
1479 _(" [icq] ICQ network"),
1480 _(" [yahoo] YAIM"),
1481 _(" [msn] M$ Messenger"),
1482 _(" [aim] AOL TOC"),
1483 _(" [irc] IRC"),
1484 _(" [jab] Jabber"),
1486 _(" [lj] LiveJournal"),
1487 _(" [gg] Gadu-Gadu"),
1491 for(ipname = icq; ipname != protocolname_size; ipname++) {
1492 ia = conf.getourid(ipname);
1494 if(!ia.empty()) {
1495 m.additem(0, ipname, pitems[ipname]);
1500 bool icqface::changestatus(vector<protocolname> &pnames, imstatus &st) {
1501 int i, choice, protcount;
1502 bool r, alrlogged = false;
1503 verticalmenu m(conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
1505 vector<imstatus> mst;
1506 vector<imstatus>::iterator im;
1507 protocolname pname, onechoice;
1509 m.setwindow(textwindow(sizeWArea.x1, sizeWArea.y1, sizeWArea.x1+27,
1510 sizeWArea.y1+9, conf.getcolor(cp_main_text)));
1512 m.idle = &menuidle;
1514 for(protcount = 0, pname = icq; pname != protocolname_size; pname++) {
1515 if(!conf.getourid(pname).empty()) {
1516 protcount++;
1517 onechoice = pname;
1520 alrlogged = alrlogged || gethook(pname).getstatus() != offline;
1523 if(protcount < 2) {
1524 i = 1;
1525 choice = onechoice;
1527 } else {
1528 m.additem(0, -1, _(" All protocols"));
1529 if(alrlogged) m.additem(0, -2, _(" Already logged in only"));
1530 m.addline();
1531 makeprotocolmenu(m);
1532 m.scale();
1534 i = m.open();
1535 choice = (intptr_t) m.getref(i-1);
1537 m.close();
1540 if(r = i) {
1541 switch(choice) {
1542 case -1:
1543 for(pname = icq; pname != protocolname_size; pname++)
1544 if(!conf.getourid(pname).empty())
1545 pnames.push_back(pname);
1546 break;
1547 case -2:
1548 for(pname = icq; pname != protocolname_size; pname++)
1549 if(!conf.getourid(pname).empty())
1550 if(gethook(pname).getstatus() != offline)
1551 pnames.push_back(pname);
1552 break;
1553 default:
1554 pnames.push_back((protocolname) choice);
1555 break;
1558 m.clear();
1560 mst.push_back(available);
1561 mst.push_back(offline);
1562 mst.push_back(away);
1563 mst.push_back(dontdisturb);
1564 mst.push_back(notavail);
1565 mst.push_back(outforlunch);
1566 mst.push_back(occupied);
1567 mst.push_back(freeforchat);
1568 mst.push_back(invisible);
1570 for(im = mst.begin(); im != mst.end(); ++im) {
1571 string sst = (string) " [" + imstatus2char[*im] + "] " + imstatus2str(*im);
1572 m.additem(0, *im, sst);
1574 if(pnames.size() == 1)
1575 if(gethook(pnames.front()).getstatus() == *im)
1576 m.setpos(m.getcount()-1);
1579 if(pnames.size() > 1) {
1580 m.setpos(0);
1583 m.otherkeys = &statuskeys;
1585 m.scale();
1587 i = m.open();
1588 m.close();
1590 if(r = i) {
1591 st = (imstatus) ((intptr_t) m.getref(i-1));
1595 return r;
1598 #define INPUT_POS LINES-2
1600 string icqface::inputstr(const string &q, const string &defl, char passwdchar) {
1601 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1603 attrset(conf.getcolor(cp_status));
1604 mvhline(INPUT_POS, 0, ' ', COLS);
1605 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1607 input.setpasswordchar(passwdchar);
1608 input.removeselector();
1609 input.setvalue(defl);
1610 input.setcoords(q.size(), INPUT_POS, COLS-q.size());
1611 input.exec();
1613 sa.restore();
1614 return input.getvalue();
1617 string icqface::inputfile(const string &q, const string &defl) {
1618 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1619 string r;
1621 mvhline(INPUT_POS, 0, ' ', COLS);
1622 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1623 kwriteatf(COLS-8, INPUT_POS, conf.getcolor(cp_status), "[Ctrl-T]");
1625 selector.setoptions(0);
1626 selector.setwindow(textwindow(0, 0, sizeDlg.width, sizeDlg.height,
1627 conf.getcolor(cp_dialog_frame), TW_CENTERED,
1628 conf.getcolor(cp_dialog_highlight),
1629 _(" <Space> or <Enter> confirm, <Esc> cancel ")));
1631 input.connectselector(selector);
1633 input.setcoords(q.size(), INPUT_POS, COLS-q.size()-8);
1634 input.setvalue(defl);
1635 input.exec();
1636 r = input.getvalue();
1638 if(r.rfind("/") != -1) {
1639 selector.setstartpoint(r.substr(0, r.find("/")));
1642 sa.restore();
1643 return r;
1646 string icqface::inputdir(const string &q, const string &defl) {
1647 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1648 string r;
1650 mvhline(INPUT_POS, 0, ' ', COLS);
1651 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1652 kwriteatf(COLS-8, INPUT_POS, conf.getcolor(cp_status), "[Ctrl-T]");
1654 selector.setoptions(FSEL_DIRSELECT);
1655 selector.setwindow(textwindow(0, 0, sizeDlg.width, sizeDlg.height,
1656 conf.getcolor(cp_dialog_frame), TW_CENTERED,
1657 conf.getcolor(cp_dialog_highlight),
1658 _(" <Space> confirm, <Esc> cancel ")));
1660 input.connectselector(selector);
1662 input.setcoords(q.size(), INPUT_POS, COLS-q.size()-8);
1663 input.setvalue(defl);
1664 input.exec();
1665 r = input.getvalue();
1667 if(r.rfind("/") != -1) {
1668 selector.setstartpoint(r.substr(0, r.find("/")));
1671 sa.restore();
1672 return r;
1675 int icqface::getlastinputkey() const {
1676 return input.getlastkey();
1679 void catqstr(string &q, int option, int defl) {
1680 char c = q[q.size()-1];
1681 if(!strchr("(/", c)) q += '/';
1683 switch(option) {
1684 case ASK_YES: q += 'y'; break;
1685 case ASK_NO: q += 'n'; break;
1686 case ASK_CANCEL: q += 'c'; break;
1689 if(defl == option) q[q.size()-1] = toupper(q[q.size()-1]);
1692 int icqface::ask(string q, int options, int deflt ) {
1693 int ret;
1694 screenarea sa(0, INPUT_POS, COLS, INPUT_POS);
1696 q += " (";
1697 if(options & ASK_YES) catqstr(q, ASK_YES, deflt);
1698 if(options & ASK_NO) catqstr(q, ASK_NO, deflt);
1699 if(options & ASK_CANCEL) catqstr(q, ASK_CANCEL, deflt);
1700 q += ") ";
1702 attrset(conf.getcolor(cp_status));
1703 mvhline(INPUT_POS, 0, ' ', COLS);
1704 kwriteatf(0, INPUT_POS, conf.getcolor(cp_status), "%s", q.c_str());
1706 for(ret = -1; ret == -1; ) {
1707 switch(getkey()) {
1708 case 'y':
1709 case 'Y':
1710 if(options & ASK_YES) ret = ASK_YES;
1711 break;
1712 case 'n':
1713 case 'N':
1714 if(options & ASK_NO) ret = ASK_NO;
1715 break;
1716 case 'c':
1717 case 'C':
1718 if(options & ASK_CANCEL) ret = ASK_CANCEL;
1719 break;
1720 case '\r':
1721 if(deflt != -1) ret = deflt;
1722 break;
1726 sa.restore();
1727 return ret;
1730 void icqface::saveworkarea() {
1731 int i;
1732 chtype **workareabuf = (chtype **) malloc(sizeof(chtype *) * (sizeWArea.y2-sizeWArea.y1+1));
1734 for(i = 0; i <= sizeWArea.y2-sizeWArea.y1; i++) {
1735 workareabuf[i] = (chtype *) malloc(sizeof(chtype) * (sizeWArea.x2-sizeWArea.x1+2));
1736 mvinchnstr(sizeWArea.y1+i, sizeWArea.x1, workareabuf[i], sizeWArea.x2-sizeWArea.x1+1);
1739 workareas.add(workareabuf);
1742 void icqface::restoreworkarea() {
1743 int i;
1744 chtype **workareabuf = (chtype **) workareas.at(workareas.count-1);
1746 if(workareabuf && !dotermresize) {
1747 for(i = 0; i <= sizeWArea.y2-sizeWArea.y1; i++) {
1748 mvaddchnstr(sizeWArea.y1+i, sizeWArea.x1,
1749 workareabuf[i], sizeWArea.x2-sizeWArea.x1+1);
1752 refresh();
1755 workareas.remove(workareas.count-1);
1758 void icqface::clearworkarea() {
1759 int i;
1761 attrset(conf.getcolor(cp_main_text));
1762 for(i = sizeWArea.y1+1; i < sizeWArea.y2; i++) {
1763 mvhline(i, sizeWArea.x1+1, ' ', sizeWArea.x2-sizeWArea.x1-1);
1764 refresh();
1768 void icqface::freeworkareabuf(void *p) {
1769 chtype **workareabuf = (chtype **) p;
1770 if(workareabuf) {
1771 for(int i = 0; i <= face.sizeWArea.y2-face.sizeWArea.y1; i++) {
1772 free((chtype *) workareabuf[i]);
1775 free(workareabuf);
1779 void icqface::workarealine(int l, chtype c ) {
1780 attrset(conf.getcolor(cp_main_frame));
1781 mvhline(l, sizeWArea.x1+1, c, sizeWArea.x2-sizeWArea.x1-1);
1784 void icqface::modelist(contactstatus cs) {
1785 int i, b;
1786 icqcontact *c;
1787 dialogbox db;
1788 modelistitem it;
1789 vector<imcontact>::iterator ic;
1791 saveworkarea();
1792 clearworkarea();
1794 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
1795 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
1797 db.setmenu(new verticalmenu(conf.getcolor(cp_main_text),
1798 conf.getcolor(cp_main_selected)));
1800 db.setbar(new horizontalbar(conf.getcolor(cp_main_highlight),
1801 conf.getcolor(cp_main_selected),
1802 _("Details"), _("Add"), _("Remove"), _("Move to contacts"), (char*)0));
1804 db.addautokeys();
1805 db.idle = &dialogidle;
1806 db.addkey(KEY_IC, 1);
1807 db.addkey(KEY_DC, 2);
1809 db.redraw();
1810 workarealine(sizeWArea.y1+2);
1811 workarealine(sizeWArea.y2-2);
1813 mainw.write(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight),
1814 cs == csignore ? _("Ignore list") :
1815 cs == csvisible ? _("Visible list") :
1816 cs == csinvisible ? _("Invisible list") : "");
1818 set<protocolname> ps;
1819 if(cs == csvisible || cs == csinvisible) {
1820 for(protocolname pname = icq; pname != protocolname_size; pname++)
1821 if(gethook(pname).getCapabs().count(hookcapab::visibility))
1822 ps.insert(pname);
1825 lst.fillmenu(db.getmenu(), cs);
1827 while(db.open(i, b)) {
1828 if(!db.getmenu()->getcount() && b != 1) continue;
1829 if(b != 1) it = lst.menuat(i-1);
1831 switch(b) {
1832 case 0:
1833 cicq.userinfo(it.getdesc());
1834 break;
1835 case 1:
1836 muins.clear();
1838 if(multicontacts(_("Select contacts to add to the list"), ps, cs)) {
1839 bool removecl;
1841 if(cs == csignore) {
1842 char buf[512];
1843 snprintf(buf, sizeof(buf), _("Remove the %d contacts from the contact list as well?"), muins.size());
1844 removecl = face.ask(buf, ASK_YES | ASK_NO, ASK_NO) == ASK_YES;
1847 for(ic = muins.begin(); ic != muins.end(); ++ic) {
1848 lst.push_back(modelistitem(clist.get(*ic)->getdispnick(), *ic, cs));
1850 if(removecl && cs == csignore)
1851 clist.remove(*ic);
1854 lst.fillmenu(db.getmenu(), cs);
1855 db.getmenu()->redraw();
1857 break;
1858 case 2:
1859 lst.del(it.getdesc(), cs);
1860 lst.fillmenu(db.getmenu(), cs);
1861 db.getmenu()->redraw();
1862 break;
1863 case 3:
1864 if(c = cicq.addcontact(it.getdesc())) {
1865 c->setdispnick(it.getnick());
1866 lst.del(it.getdesc(), cs);
1867 lst.fillmenu(db.getmenu(), cs);
1868 db.getmenu()->redraw();
1870 break;
1873 face.relaxedupdate();
1876 db.close();
1877 restoreworkarea();
1880 bool icqface::multicontacts(const string &ahead,
1881 const set<protocolname> &protos, contactstatus cs) {
1883 int i, savefirst, saveelem, prevgid;
1884 bool ret = true, finished = false;
1885 string head = ahead;
1887 imcontact ic;
1888 vector<imcontact>::iterator c, cc;
1889 vector<imcontact> mlst;
1891 map<int, bool> groupcheck;
1892 icqcontact *cont;
1894 verticalmenu m(sizeWArea.x1+1, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2,
1895 conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
1897 saveworkarea();
1898 clearworkarea();
1900 workarealine(sizeWArea.y1+2);
1902 if(!head.size()) head = _("Event recipients");
1903 mainw.write(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight), head);
1905 vector<icqgroup>::iterator ig = groups.begin();
1907 while(ig != groups.end()) {
1908 for(i = 0; i < clist.count; i++) {
1909 icqcontact *c = (icqcontact *) clist.at(i);
1910 imcontact desc = c->getdesc();
1912 if(!desc.empty())
1913 if(c->getgroupid() == ig->getid() || conf.getgroupmode() == icqconf::nogroups)
1914 if(protos.empty() || protos.count(desc.pname))
1915 if(!lst.inlist(desc, cs))
1916 mlst.push_back(desc);
1919 ig++;
1922 m.idle = &menuidle;
1923 m.otherkeys = &multiplekeys;
1925 while(!finished) {
1926 m.getpos(saveelem, savefirst);
1927 m.clear();
1929 int prevgid = -1;
1931 for(c = mlst.begin(); c != mlst.end(); ++c) {
1932 cont = (icqcontact *) clist.get(*c);
1934 if(conf.getgroupmode() != icqconf::nogroups)
1935 if(cont->getgroupid() != prevgid) {
1936 prevgid = cont->getgroupid();
1938 if(groupcheck.find(prevgid) == groupcheck.end())
1939 groupcheck[prevgid] = false;
1941 m.additemf(conf.getcolor(cp_main_highlight), 0, "\002[%c]\002\002 %s",
1942 groupcheck[prevgid] ? 'x' : ' ',
1943 groups.getname(prevgid).c_str());
1946 m.additemf(conf.getprotcolor(c->pname), cont, " [%c] %s",
1947 find(muins.begin(), muins.end(), *c) != muins.end() ? 'x' : ' ',
1948 cont->getdispnick().c_str());
1951 m.setpos(saveelem, savefirst);
1953 switch(m.open()) {
1954 case -2:
1955 if(m.getref(m.getpos())) {
1956 ic = ((icqcontact *) m.getref(m.getpos()))->getdesc();
1957 c = find(muins.begin(), muins.end(), ic);
1959 if(c != muins.end()) muins.erase(c);
1960 else muins.push_back(ic);
1962 cont = clist.get(ic);
1963 groupcheck[cont->getgroupid()] = false;
1965 } else {
1966 bool been = false;
1967 int gid = ((icqcontact *) m.getref(m.getpos()+1))->getgroupid();
1968 groupcheck[gid] = !groupcheck[gid];
1970 for(c = mlst.begin(); c != mlst.end(); ++c) {
1971 cont = (icqcontact *) clist.get(*c);
1973 if(cont->getgroupid() == gid) {
1974 been = true;
1975 cc = find(muins.begin(), muins.end(), *c);
1977 if(groupcheck[gid] && cc == muins.end()) {
1978 muins.push_back(*c);
1979 } else if(!groupcheck[gid] && cc != muins.end()) {
1980 muins.erase(cc);
1982 } else if(been) break;
1985 break;
1987 case -3:
1988 quickfind(&m);
1989 break;
1991 case 0:
1992 ret = false;
1994 default:
1995 finished = true;
1996 break;
2000 restoreworkarea();
2001 return ret;
2004 void icqface::log(const char *fmt, ...) {
2005 va_list ap;
2006 char buf[1024];
2008 va_start(ap, fmt);
2009 vsnprintf(buf, sizeof(buf), fmt, ap);
2010 log((string) buf);
2011 va_end(ap);
2014 void icqface::log(const string &atext) {
2015 int i;
2016 string text = atext;
2018 if(conf.getdebug())
2019 if(flog.is_open())
2020 flog << text << endl;
2022 /*! Add a timestamp if needed
2025 bool lts, lo;
2026 conf.getlogoptions(lts, lo);
2028 if(lts)
2029 if(text.size() > 3)
2030 if(ispunct(text[0]) && isspace(text[1]) && !isspace(text[2])) {
2031 time_t t;
2032 char stime[64];
2033 time(&t);
2034 strftime(stime, 64, conf.getlogtimestampformat(), localtime(&t));
2035 text.insert(2, stime);
2038 while((i = text.find("\n")) != -1) text[i] = ' ';
2039 while((i = text.find("\r")) != -1) text[i] = ' ';
2041 while((lastlog.size() > LINES-sizeWArea.y2-2) && !lastlog.empty())
2042 lastlog.erase(lastlog.begin());
2044 lastlog.push_back(text);
2046 if(!mainscreenblock && (sizeWArea.x2-sizeWArea.x1 > 0)) {
2047 chtype *logline = new chtype[sizeWArea.x2-sizeWArea.x1+2];
2048 attrset(conf.getcolor(cp_main_text));
2050 for(i = sizeWArea.y2+2; i < LINES-2; i++) {
2051 mvinchnstr(i, sizeWArea.x1+1, logline, sizeWArea.x2-sizeWArea.x1);
2052 mvaddchnstr(i-1, sizeWArea.x1+1, logline, sizeWArea.x2-sizeWArea.x1);
2055 delete[] logline;
2057 if(text.size() > sizeWArea.x2-sizeWArea.x1-2) text.resize(sizeWArea.x2-sizeWArea.x1-2);
2058 mvhline(LINES-3, sizeWArea.x1+2, ' ', sizeWArea.x2-sizeWArea.x1-2);
2059 kwriteatf(sizeWArea.x1+2, LINES-3, conf.getcolor(cp_main_text), "%s", text.c_str());
2063 void icqface::status(const string &text) {
2064 string t(text);
2066 if(t == "@") {
2067 if(!fstatus.empty()) fstatus.pop_back();
2068 if(!fstatus.empty()) t = fstatus.back();
2070 } else if(t == "#") {
2071 if(!fstatus.empty()) t = fstatus.back();
2073 } else {
2074 fstatus.push_back(t);
2075 while(fstatus.size() > 5)
2076 fstatus.erase(fstatus.begin());
2080 attrset(conf.getcolor(cp_status));
2081 mvhline(LINES-1, 0, ' ', COLS);
2082 kwriteatf(0, LINES-1, conf.getcolor(cp_status), "%s", t.c_str());
2085 void icqface::status(const char *fmt, ...) {
2086 va_list ap;
2087 char buf[1024];
2089 va_start(ap, fmt);
2090 vsnprintf(buf, sizeof(buf), fmt, ap);
2091 status((string) buf);
2092 va_end(ap);
2095 void icqface::blockmainscreen() {
2096 mainscreenblock = true;
2099 void icqface::unblockmainscreen() {
2100 mainscreenblock = false;
2101 update();
2104 void icqface::quickfind(verticalmenu *multi) {
2105 bool fin;
2106 string nick, disp, upnick, upcurrent;
2107 string::iterator is;
2108 int k, i, len, lx, ly;
2109 bool found;
2110 icqcontact *c;
2111 verticalmenu *cm = (multi ? multi : &mcontacts->menu);
2113 status(_("QuickSearch: type to find, %s find again, Enter finish"),
2114 getstatkey(key_quickfind, section_contact).c_str());
2116 xtermtitle(_("contact list quick search"));
2118 if(multi) {
2119 lx = sizeWArea.x1+2;
2120 ly = sizeWArea.y2;
2121 } else {
2122 lx = 2;
2123 ly = LINES-2;
2126 for(fin = false; !fin; ) {
2127 attrset(conf.getcolor(cp_main_frame));
2128 mvhline(ly, lx, HLINE, 23);
2129 disp = nick;
2130 if(disp.size() > 18) disp.replace(0, disp.size()-18, "");
2131 kwriteatf(lx, ly, conf.getcolor(cp_main_highlight), "[ %s ]", disp.c_str());
2132 kgotoxy(lx+2+disp.size(), ly);
2133 refresh();
2135 if(cicq.idle())
2136 switch(k = getkey()) {
2137 case KEY_ESC:
2138 case '\r':
2139 fin = true;
2140 break;
2142 case KEY_BACKSPACE:
2143 case CTRL('h'):
2144 if(!nick.empty()) nick.resize(nick.size()-1);
2145 else fin = true;
2146 break;
2148 default:
2149 if(isprint(k) || (key2action(k, section_contact) == key_quickfind)) {
2150 i = cm->getpos() + (multi ? 1 : 2);
2152 if(isprint(k)) {
2153 i--;
2154 nick += k;
2157 for(is = nick.begin(), upnick = ""; is != nick.end(); ++is)
2158 upnick += toupper(*is);
2160 bool fin = false;
2161 bool fpass = true;
2163 for(; !fin; i++) {
2164 c = 0;
2166 if(i > cm->getcount()) {
2167 if(fpass) {
2168 i = 0;
2169 fpass = false;
2170 } else {
2171 fin = true;
2172 break;
2176 if(!multi && mcontacts->isnode(i)) {
2177 c = 0;
2178 } else if(!multi) {
2179 c = (icqcontact *) mcontacts->getref(i);
2180 } else {
2181 c = (icqcontact *) cm->getref(i);
2184 if((intptr_t) c > 100) {
2185 string current = c->getdispnick();
2186 len = current.size();
2187 if(len > nick.size()) len = nick.size();
2188 current.erase(len);
2190 for(is = current.begin(), upcurrent = "";
2191 is != current.end(); ++is)
2192 upcurrent += toupper(*is);
2194 if(upnick == upcurrent) {
2195 cm->setpos(i - (multi ? 0 : 1));
2196 break;
2201 if(!multi) mcontacts->redraw();
2202 else cm->redraw();
2204 break;
2208 attrset(conf.getcolor(cp_main_frame));
2209 mvhline(ly, lx, HLINE, 23);
2212 void icqface::extracturls(const string &buf) {
2213 int pos = 0;
2214 regex_t r;
2215 regmatch_t rm[1];
2216 const char *pp = buf.c_str();
2218 extractedurls.clear();
2219 if(!regcomp(&r, "(http://[^ \t\n]+|https://[^ \t\n]+|ftp://[^ \t\n]+|www\\.[^ \t\n]+)", REG_EXTENDED)) {
2220 while(!regexec(&r, buf.substr(pos).c_str(), 1, rm, 0)) {
2221 extractedurls.push_back(buf.substr(pos+rm[0].rm_so, rm[0].rm_eo-rm[0].rm_so));
2222 pos += rm[0].rm_eo;
2224 regfree(&r);
2228 void icqface::showextractedurls() {
2229 if(extractedurls.empty()) {
2230 log(_("+ no URLs within the current context"));
2231 } else {
2232 int n;
2233 vector<string>::iterator i;
2234 verticalmenu m(sizeWArea.x1+1, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2,
2235 conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
2237 saveworkarea();
2238 clearworkarea();
2239 workarealine(sizeWArea.y1+2);
2241 mainw.writef(sizeWArea.x1+2, sizeWArea.y1,
2242 conf.getcolor(cp_main_highlight),
2243 _("URLs within the current context"));
2245 for(i = extractedurls.begin(); i != extractedurls.end(); ++i)
2246 m.additem(" " + *i);
2248 if(n = m.open())
2249 conf.execaction("openurl", extractedurls[n-1]);
2251 restoreworkarea();
2255 void icqface::userinfoexternal(const imcontact &ic) {
2256 vector<pair<int, string> > r = external.getlist(imexternal::aomanual, ic.pname);
2258 if(r.empty()) {
2259 log(_("+ no external actions defined for %s"), conf.getprotocolname(ic.pname).c_str());
2261 } else {
2262 verticalmenu m(conf.getcolor(cp_main_text), conf.getcolor(cp_main_selected));
2264 m.setwindow(textwindow(sizeWArea.x1, sizeWArea.y1, sizeWArea.x1+27, sizeWArea.y1+9, conf.getcolor(cp_main_text)));
2265 m.idle = &menuidle;
2267 vector<pair<int, string> >::const_iterator ir = r.begin();
2268 while(ir != r.end()) {
2269 m.additem((string(" ") + ir->second).c_str());
2270 ++ir;
2273 m.scale();
2274 int i = m.open();
2275 m.close();
2277 if(i) {
2278 char cbuf[512];
2279 snprintf(cbuf, sizeof(cbuf), _("Result of the external action %s:"),
2280 (r.begin()+i-1)->second.c_str());
2282 string buf;
2283 external.execmanual(ic, i-1, buf);
2285 if(!buf.empty()) {
2286 imnotification ev(ic, (string) cbuf + "\n\n" + buf);
2287 saveworkarea();
2288 eventview(&ev, vector<eventviewresult>(), true);
2289 restoreworkarea();
2295 void icqface::showeventbottom(const imcontact &ic) {
2296 const char *text = ischannel(ic) ?
2297 _("%s send, %s multi, %s/%s pr/nxt chat, %s hist, %s URLs, %s expand, %s memb") :
2298 _("%s send, %s multi, %s/%s pr/nxt chat, %s hist, %s URLs, %s expand, %s info");
2300 status(text,
2301 getstatkey(key_send_message, section_editor).c_str(),
2302 getstatkey(key_multiple_recipients, section_editor).c_str(),
2303 getstatkey(key_prev_chat, section_editor).c_str(),
2304 getstatkey(key_next_chat, section_editor).c_str(),
2305 getstatkey(key_history, section_editor).c_str(),
2306 getstatkey(key_show_urls, section_editor).c_str(),
2307 getstatkey(key_fullscreen, section_editor).c_str(),
2308 getstatkey(key_info, section_editor).c_str());
2311 bool icqface::eventedit(imevent &ev) {
2312 bool r;
2313 texteditor editor;
2314 icqcontact *c;
2315 string msg;
2317 editdone = r = false;
2318 passinfo = ev.getcontact();
2320 editor.addscheme(cp_main_text, cp_main_text, 0, 0);
2321 editor.otherkeys = &editmsgkeys;
2322 editor.idle = &editidle;
2323 editor.wrap = true;
2324 editor.smarttab = false;
2325 editor.emacs = conf.getemacs();
2327 saveworkarea();
2328 clearworkarea();
2330 peerinfo(2, ev.getcontact());
2332 mainw.writef(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight),
2333 _("Outgoing %s"), streventname(ev.gettype()));
2335 showeventbottom(ev.getcontact());
2337 if(ev.gettype() == imevent::message) {
2338 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2340 immessage *m = static_cast<immessage *>(&ev);
2341 editor.load(msg = m->gettext(), "");
2343 editdone = false;
2344 editor.open();
2345 r = editdone;
2347 /* TODO, should we relly use a pointer here ? */
2348 char *p = editor.save("\r\n");
2349 *m = immessage(ev.getcontact(), imevent::outgoing, p);
2351 if(c = clist.get(ev.getcontact()))
2352 c->setpostponed(r ? "" : p);
2353 free (p);
2355 } else if(ev.gettype() == imevent::xml) {
2356 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2358 while(1) {
2359 imxmlevent *m = static_cast<imxmlevent *>(&ev);
2360 editor.load(msg = m->gettext(), "");
2362 editdone = false;
2363 editor.open();
2364 r = editdone;
2366 char *p = editor.save("\r\n");
2367 m->setfield("text", p);
2369 if(r)
2370 if(ev.getcontact().pname == livejournal)
2371 if(!setljparams(m)){
2372 free(p);
2373 continue;
2376 if(c = clist.get(ev.getcontact()))
2377 c->setpostponed(r ? "" : p);
2379 free(p);
2380 break;
2383 } else if(ev.gettype() == imevent::url) {
2384 static textinputline urlinp;
2385 imurl *m = static_cast<imurl *>(&ev);
2387 urlinp.setvalue(m->geturl());
2388 urlinp.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2-sizeWArea.x1-2);
2389 urlinp.setcolor(conf.getcolor(cp_main_highlight));
2390 urlinp.idle = &textinputidle;
2392 workarealine(sizeWArea.y1+4);
2394 urlinp.exec();
2396 string url = urlinp.getlastkey() != KEY_ESC ? urlinp.getvalue() : "";
2398 if(!url.empty()) {
2399 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+5, sizeWArea.x2, sizeWArea.y2);
2400 editor.load(m->getdescription(), "");
2401 editor.open();
2403 r = editdone;
2405 char *p = editor.save("\r\n");
2406 *m = imurl(ev.getcontact(), imevent::outgoing, url, p);
2407 free (p);
2410 } else if(ev.gettype() == imevent::sms) {
2411 imsms *m = static_cast<imsms *>(&ev);
2413 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2414 editor.load(msg = m->getmessage(), "");
2415 editor.open();
2417 r = editdone;
2419 auto_ptr<char> p(editor.save("\r\n"));
2420 *m = imsms(ev.getcontact(), imevent::outgoing, p.get(), m->getphone());
2422 if(c = clist.get(ev.getcontact()))
2423 c->setpostponed(r ? "" : p.get());
2425 } else if(ev.gettype() == imevent::authorization) {
2426 imauthorization *m = static_cast<imauthorization *>(&ev);
2428 if (gethook(ev.getcontact().pname).getCapabs().count(hookcapab::authreqwithmessages)) {
2429 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+3, sizeWArea.x2, sizeWArea.y2);
2430 editor.load(msg = m->getmessage(), "");
2431 editor.open();
2432 char *p = editor.save("\r\n");
2433 *m = imauthorization(ev.getcontact(), imevent::outgoing, imauthorization::Request, p);
2434 r = editdone;
2435 free(p);
2436 } else {
2437 *m = imauthorization(ev.getcontact(), imevent::outgoing, imauthorization::Request, "");
2438 r = true;
2442 } else if(ev.gettype() == imevent::contacts) {
2443 imcontacts *m = static_cast<imcontacts *>(&ev);
2444 imcontact cont;
2446 vector<imcontact> smuins = muins;
2447 vector<imcontact>::iterator imc;
2449 vector< pair<unsigned int, string> > clst;
2450 vector< pair<unsigned int, string> >::const_iterator icc;
2452 muins.clear();
2454 for(icc = m->getcontacts().begin(); icc != m->getcontacts().end(); ++icc) {
2455 cont = imcontact();
2456 cont.pname = ev.getcontact().pname;
2458 if(icc->first) cont.uin = icc->first; else
2459 cont.nickname = icc->second;
2461 muins.push_back(cont);
2464 set<protocolname> ps;
2465 ps.insert(ev.getcontact().pname);
2466 r = multicontacts(_("Send contacts.."), ps);
2468 for(imc = muins.begin(); imc != muins.end(); ++imc) {
2469 if(imc->uin) {
2470 icqcontact *cc = clist.get(*imc);
2471 if(cc) imc->nickname = cc->getnick();
2474 clst.push_back(make_pair(imc->uin, imc->nickname));
2477 muins = smuins;
2479 *m = imcontacts(ev.getcontact(), imevent::outgoing, clst);
2481 } else if(ev.gettype() == imevent::file) {
2482 dialogbox db;
2483 int baritem, mitem;
2484 bool finished = false, floop = true;
2485 vector<imfile::record>::const_iterator ir;
2487 imfile *m = static_cast<imfile *>(&ev);
2489 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
2490 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
2491 db.setmenu(new verticalmenu(conf.getcolor(cp_main_menu),
2492 conf.getcolor(cp_main_selected)));
2493 db.setbar(new horizontalbar(conf.getcolor(cp_main_highlight),
2494 conf.getcolor(cp_main_selected),
2495 _("Add"), _("Remove"), _("Send"), (char*)0));
2497 db.addkey(KEY_IC, 0);
2498 db.addkey(KEY_DC, 1);
2500 db.addautokeys();
2501 db.idle = &dialogidle;
2503 db.redraw();
2505 workarealine(sizeWArea.y1+2);
2506 workarealine(sizeWArea.y2-2);
2508 while(!finished && !r) {
2509 db.getmenu()->clear();
2511 for(ir = m->getfiles().begin(); ir != m->getfiles().end(); ++ir)
2512 db.getmenu()->additemf(" %s", ir->fname.c_str());
2514 if(floop) {
2515 baritem = 0;
2516 floop = false;
2517 } else {
2518 finished = !db.open(mitem, baritem);
2521 if(!finished) {
2522 vector<imfile::record> files = m->getfiles();
2524 switch(baritem) {
2525 case 0:
2526 if((msg = inputfile(_("file name: "))).size()) {
2527 struct stat st;
2529 if(!stat(msg.c_str(), &st)) {
2530 imfile::record fr;
2531 fr.fname = msg;
2532 fr.size = st.st_size;
2533 files.push_back(fr);
2536 break;
2537 case 1:
2538 if(mitem > 0 && mitem <= m->getfiles().size())
2539 files.erase(files.begin()+mitem-1);
2540 break;
2541 case 2:
2542 r = true;
2543 break;
2546 m->setfiles(files);
2551 editdone = false;
2552 restoreworkarea();
2553 status("@");
2554 return r;
2557 string icqface::extracttime(const imevent &ev) {
2558 string ds1, ds2, r;
2559 int tdiff;
2560 time_t t, ts;
2561 char buf[64];
2563 ds1 = time2str(&(t = ev.gettimestamp()), conf.gettimestampformat(), buf);
2564 ds2 = time2str(&(ts = ev.getsenttimestamp()), conf.gettimestampformat(), buf);
2566 r = ds1 + " ";
2567 tdiff = ts-t;
2568 if(tdiff < 0) tdiff *= -1;
2570 if(tdiff > 5)
2571 if(ds1 != ds2) r += (string) "[" + ds2 + "] ";
2573 return r;
2576 void icqface::renderchathistory() {
2577 int count, chatmargin;
2578 string text;
2579 time_t lastread;
2580 struct stat st;
2582 vector<imevent *> events;
2583 typedef pair<imevent::imdirection, vector<string> > histentry;
2584 vector<histentry> toshow;
2585 vector<histentry>::iterator ir;
2586 vector<string>::reverse_iterator il;
2588 static imevent *lastev = 0;
2590 delete lastev;
2591 passevent = lastev = 0;
2593 histentry h;
2595 icqcontact *c = clist.get(passinfo);
2596 if(!c) return;
2598 count = 0;
2600 if(!stat((c->getdirname() + "history").c_str(), &st)) {
2601 count = st.st_size-chatlines*(sizeWArea.x2-sizeWArea.x1)*2;
2602 if(count < 0) count = 0;
2605 // Skip history items that won't be displayed
2606 c->sethistoffset(count);
2607 if (chatlastread == 0) chatlastread = 1;
2609 count = 0;
2610 events = em.getevents(passinfo, chatlastread);
2611 chatlastread = 0;
2612 lastread = c->getlastread();
2614 if(events.size()) {
2615 c->setlastread(events.back()->gettimestamp());
2618 while(events.size() > chatlines) {
2619 delete events.front();
2620 events.erase(events.begin());
2623 while(events.size()) {
2624 if(events.back()->getdirection() == imevent::incoming) {
2625 if(events.back()->gettimestamp() > lastread) {
2626 if(events.back()->gettype() == imevent::authorization
2627 || events.back()->gettype() == imevent::contacts
2628 || events.back()->gettype() == imevent::file) {
2629 bool fin, enough;
2630 fin = enough = false;
2631 saveworkarea();
2633 while(!fin && !enough)
2634 cicq.readevent(*events.back(), enough, fin);
2636 restoreworkarea();
2641 if(count < chatlines) {
2642 text = extracttime(*events.back());
2643 text += events.back()->gettext();
2644 chatlastread = events.back()->gettimestamp()-1;
2646 h = histentry();
2647 h.first = events.back()->getdirection();
2648 breakintolines(text, h.second, sizeWArea.x2-sizeWArea.x1-2);
2649 toshow.push_back(h);
2651 if(!lastev)
2652 if(h.first == imevent::incoming) {
2653 passevent = lastev = events.back()->getevent();
2654 extracturls(text);
2657 count += h.second.size();
2660 delete events.back();
2661 events.pop_back();
2664 chatmargin = sizeWArea.y1+chatlines;
2665 text = string(sizeWArea.x2-sizeWArea.x1-2, ' ');
2667 attrset(conf.getcolor(cp_main_text));
2668 for(count = 0; count < chatlines; count++)
2669 mvprintw(chatmargin-count, sizeWArea.x1+2, "%s", text.c_str());
2671 for(count = 0, ir = toshow.begin(); ir != toshow.end() && count < chatlines; ++ir) {
2672 switch(ir->first) {
2673 case imevent::incoming:
2674 attrset(conf.getcolor(cp_main_history_incoming));
2675 break;
2676 case imevent::outgoing:
2677 attrset(conf.getcolor(cp_main_history_outgoing));
2678 break;
2681 for(il = ir->second.rbegin(); il != ir->second.rend() && count < chatlines; ++il) {
2682 kgotoxy(sizeWArea.x1+2, chatmargin-count);
2683 printstring(*il);
2684 count++;
2688 peerinfo(chatlines+1, passinfo);
2691 void icqface::peerinfo(int line, const imcontact &ic) {
2692 workarealine(sizeWArea.y1+line);
2694 icqcontact *c = clist.get(ic);
2695 if(!c) return;
2697 string text = conf.getprotocolname(passinfo.pname) + " " + c->getdispnick();
2698 int maxsize = sizeWArea.x2-sizeWArea.x1-10;
2699 bool pgpon = false;
2701 if(text.size() > maxsize) {
2702 text.erase(maxsize);
2703 text += "..";
2706 #ifdef HAVE_GPGME
2707 if(pgpon = pgp.enabled(passinfo))
2708 text += string(4, ' ');
2709 #endif
2711 #ifdef HAVE_LIBOTR
2712 text += " OTR: " + otr.is_verified(ic.pname, c->getnick()) + " " + otr.get_msg_state(ic.pname, c->getnick());
2713 #endif
2715 text = (string) "[ " + text + " ]";
2717 kwriteatf(sizeWArea.x2-text.size()-1, sizeWArea.y1+line,
2718 conf.getcolor(cp_main_text), "%s", text.c_str());
2720 if(pgpon) {
2721 kwriteatf(sizeWArea.x2-6, sizeWArea.y1+line,
2722 conf.getcolor(cp_main_highlight), "PGP");
2726 bool icqface::chat(const imcontact &ic) {
2727 texteditor editor;
2728 imevent *sendev;
2729 vector<imcontact>::iterator i;
2731 int chatlines_diff = 0; // could get rid of?
2732 bool return_status = false ;
2733 face.stay_in_chat = false ;
2735 icqcontact *c = clist.get(ic);
2736 if(!c) return false;
2738 saveworkarea();
2739 clearworkarea();
2741 chatlastread = 0;
2742 inchat = true;
2743 passinfo = ic;
2744 c->setopenedforchat(true);
2746 muins.clear();
2747 muins.push_back(passinfo);
2749 // resizing doesn't change the effect, so we work from the bottom, not the top
2750 chatlines_diff = sizeWArea.y2 - sizeWArea.y1 ;
2751 chatlines = conf.getchatpanelheight();
2752 if( chatlines == 0 ) { // allow disabling reszing by setting it to zero
2753 chatlines = (int) (chatlines_diff*0.75);
2754 } else {
2755 chatlines = chatlines_diff - chatlines; // changes where the guide line is
2757 if( chatlines < MinPanelHeight ) // bottom
2758 chatlines = (int) (MinPanelHeight);
2760 if( chatlines > (chatlines_diff - MinPanelHeight)) // top
2761 chatlines = (int) (chatlines_diff - MinPanelHeight) ;
2764 conf.setchatpanelheight(chatlines_diff - chatlines);
2766 // workarealine(sizeWArea.y1+chatlines+1);
2768 editor.addscheme(cp_main_text, cp_main_text, 0, 0);
2769 editor.otherkeys = &editmsgkeys;
2770 editor.idle = &editchatidle;
2771 editor.wrap = true;
2772 editor.smarttab = false;
2773 editor.emacs = conf.getemacs();
2774 editor.setcoords(sizeWArea.x1+2, sizeWArea.y1+chatlines+2, sizeWArea.x2, sizeWArea.y2);
2775 editor.load(c->getpostponed(), "");
2777 showeventbottom(ic);
2779 for(bool finished = false; !finished && clist.get(ic); ) {
2780 renderchathistory();
2782 editdone = false;
2783 editor.open();
2784 auto_ptr<char> p(editor.save("\r\n"));
2786 editor.close();
2787 editor.load("", "");
2789 if(editdone) {
2790 auto_ptr<imevent> ev(new immessage(ic, imevent::outgoing, p.get()));
2792 for(i = muins.begin(); i != muins.end(); ++i) {
2793 ev.get()->setcontact(*i);
2794 em.store(*ev.get());
2797 muins.clear();
2798 muins.push_back(passinfo);
2800 } else {
2801 finished = true;
2804 c->setpostponed(editdone ? "" : p.get());
2807 c->toggleopenedforchat();
2808 c->save();
2809 restoreworkarea();
2810 status("@");
2811 inchat = false;
2812 update();
2814 if ( face.stay_in_chat == true ) {
2815 return_status = true;
2816 face.stay_in_chat = false;
2819 return return_status;
2822 icqface::eventviewresult icqface::eventview(const imevent *ev,
2823 vector<eventviewresult> abuttons, bool nobuttons) {
2825 string title_event, title_timestamp, text;
2826 horizontalbar *bar = 0;
2827 dialogbox db;
2828 int mitem, baritem;
2829 eventviewresult r;
2830 static int elem = 0;
2832 vector<eventviewresult> actions;
2833 vector<eventviewresult>::iterator ia;
2835 if(!nobuttons) {
2836 if(ev->gettype() == imevent::message
2837 || ev->gettype() == imevent::notification
2838 || ev->gettype() == imevent::xml) {
2839 actions.push_back(forward);
2841 if(ev->getdirection() == imevent::incoming)
2842 if(ev->getcontact().pname != rss /*|| islivejournal(ev->getcontact())*/)
2843 actions.push_back(reply);
2845 } else if(ev->gettype() == imevent::url) {
2846 actions.push_back(forward);
2847 actions.push_back(open);
2849 if(ev->getdirection() == imevent::incoming) {
2850 actions.push_back(reply);
2853 } else if(ev->gettype() == imevent::sms) {
2854 if(ev->getdirection() == imevent::incoming) {
2855 actions.push_back(reply);
2858 } else if(ev->gettype() == imevent::authorization) {
2859 if(ev->getdirection() == imevent::incoming) {
2860 actions.push_back(info);
2861 actions.push_back(accept);
2862 actions.push_back(reject);
2865 } else if(ev->gettype() == imevent::contacts) {
2866 actions.push_back(info);
2867 actions.push_back(add);
2869 } else if(ev->gettype() == imevent::email) {
2870 actions.push_back(forward);
2872 } else if(ev->gettype() == imevent::file) {
2873 actions.push_back(info);
2875 if(gethook(ev->getcontact().pname).knowntransfer(*static_cast<const imfile *>(ev))) {
2876 actions.push_back(accept);
2877 actions.push_back(reject);
2883 actions.push_back(ok);
2884 copy(abuttons.begin(), abuttons.end(), back_inserter(actions));
2886 switch(ev->getdirection()) {
2887 case imevent::outgoing:
2888 title_event = _("Outgoing %s to %s");
2889 title_timestamp = _("Sent on %s");
2890 break;
2891 case imevent::incoming:
2892 title_event = _("Incoming %s from %s");
2893 title_timestamp = _("Received on %s");
2894 break;
2897 clearworkarea();
2899 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+3, sizeWArea.x2,
2900 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
2902 db.setbar(bar = new horizontalbar(conf.getcolor(cp_main_highlight),
2903 conf.getcolor(cp_main_selected), 0));
2905 bar->item = actions.size()-1;
2907 for(ia = actions.begin(); ia != actions.end(); ++ia)
2908 bar->items.push_back(geteventviewresult(*ia));
2910 mainw.writef(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight),
2911 title_event.c_str(), streventname(ev->gettype()), ev->getcontact().totext().c_str());
2913 mainw.writef(sizeWArea.x1+2, sizeWArea.y1+1, conf.getcolor(cp_main_highlight),
2914 title_timestamp.c_str(), extracttime(*ev).c_str());
2916 db.addautokeys();
2918 if(ev->gettype() == imevent::contacts) {
2919 const imcontacts *m = static_cast<const imcontacts *>(ev);
2921 db.setmenu(new verticalmenu(conf.getcolor(cp_main_menu),
2922 conf.getcolor(cp_main_selected)));
2924 vector< pair<unsigned int, string> > lst = m->getcontacts();
2925 vector< pair<unsigned int, string> >::const_iterator il;
2927 for(il = lst.begin(); il != lst.end(); ++il) {
2928 db.getmenu()->additemf(" %s", il->second.c_str());
2931 db.getmenu()->setpos(elem-1);
2933 } else {
2934 text = ev->gettext();
2935 db.setbrowser(new textbrowser(conf.getcolor(cp_main_text)));
2936 db.getbrowser()->setbuf(text);
2937 extracturls(text);
2939 status(_("%s to URLs, %s to full-screenize, %s close"),
2940 getstatkey(key_show_urls, section_history).c_str(),
2941 getstatkey(key_fullscreen, section_history).c_str(),
2942 getstatkey(key_quit, section_editor).c_str());
2946 db.redraw();
2948 workarealine(sizeWArea.y1+3);
2949 workarealine(sizeWArea.y2-2);
2951 db.otherkeys = &eventviewkeys;
2952 db.idle = &dialogidle;
2954 passevent = ev;
2956 if(db.open(mitem, baritem)) {
2957 r = actions[baritem];
2958 } else {
2959 r = cancel;
2962 elem = mitem;
2964 switch(r) {
2965 case add:
2966 case info:
2967 extk = elem;
2968 break;
2971 passevent = 0;
2972 return r;
2975 void icqface::fullscreenize(const imevent *ev) {
2976 textwindow w(0, 1, COLS, LINES-1, conf.getcolor(cp_main_text), TW_NOBORDER);
2977 textbrowser tb(0, 4, COLS, LINES-1, conf.getcolor(cp_main_text));
2979 int k;
2980 char buf[512], *fmt = 0;
2981 vector<string> lines;
2982 vector<string>::const_iterator il;
2984 w.open();
2986 switch(ev->getdirection()) {
2987 case imevent::incoming: fmt = _("%s from %s, received on %s"); break;
2988 case imevent::outgoing: fmt = _("%s to %s, sent on %s"); break;
2991 snprintf(buf, sizeof(buf), fmt, streventname(ev->gettype()),
2992 ev->getcontact().totext().c_str(),
2993 strdateandtime(ev->gettimestamp()).c_str());
2995 w.write(0, 1, conf.getcolor(cp_main_highlight), buf);
2997 xtermtitle((string) _("full-screen view") + " " + buf);
2999 tb.setbuf(ev->gettext());
3001 blockmainscreen();
3003 status(_("%s or %s to close, Up/Down and PgUp/PgDn to scroll"),
3004 getstatkey(key_quit, section_editor).c_str(),
3005 getstatkey(key_fullscreen, section_editor).c_str());
3007 tb.idle = &textbrowseridle;
3008 tb.otherkeys = &fullscreenkeys;
3009 tb.open();
3011 unblockmainscreen();
3012 status("@");
3014 w.close();
3017 void icqface::histmake(const vector<imevent *> &hist) {
3018 vector<imevent *>::const_reverse_iterator i;
3019 string text;
3020 time_t t, ts;
3021 char buf[64];
3022 int color;
3024 mhist.clear();
3025 mhist.setpos(0);
3027 for(i = hist.rbegin(); i != hist.rend(); ++i) {
3028 imevent &ev = **i;
3030 color = 0;
3032 t = ev.gettimestamp();
3033 ts = ev.getsenttimestamp();
3034 text = (string) + " " + time2str(&t, conf.gettimestampformat(), buf) + " ";
3035 if ((t - ts) > 0)
3036 text += (string) + "[" + time2str(&ts, conf.gettimestampformat(), buf) + "] ";
3037 text += ev.gettext();
3039 if(ev.getdirection() == imevent::incoming) {
3040 color = conf.getcolor(cp_main_history_incoming);
3041 } else if(ev.getdirection() == imevent::outgoing) {
3042 color = conf.getcolor(cp_main_history_outgoing);
3045 mhist.additem(color, (void *) *i, text);
3049 bool icqface::histexec(imevent *&im) {
3050 dialogbox db;
3051 bool r, fin;
3052 int k;
3053 static string sub;
3054 char buf[512];
3056 r = fin = false;
3058 if(!mhist.empty()) {
3059 db.setwindow(new textwindow(sizeWArea.x1+1, sizeWArea.y1+2, sizeWArea.x2,
3060 sizeWArea.y2, conf.getcolor(cp_main_text), TW_NOBORDER));
3062 db.setmenu(&mhist, false);
3064 im = static_cast<imevent *> (mhist.getref(0));
3068 * Now set the menu position
3072 for(k = 0; k < mhist.getcount(); k++) {
3073 imevent *rim = static_cast<imevent *> (mhist.getref(k));
3074 if(rim == im) {
3075 mhist.setpos(k);
3076 break;
3080 db.idle = &dialogidle;
3081 db.otherkeys = &historykeys;
3083 saveworkarea();
3084 clearworkarea();
3086 db.redraw();
3087 workarealine(sizeWArea.y1+2);
3090 snprintf(buf, sizeof(buf), _("History for %s, %d events total"),
3091 im->getcontact().totext().c_str(),
3092 mhist.getcount());
3094 mainw.write(sizeWArea.x1+2, sizeWArea.y1, conf.getcolor(cp_main_highlight), buf);
3096 status(_("%s search, %s again, %s cancel"),
3097 getstatkey(key_search, section_history).c_str(),
3098 getstatkey(key_search_again, section_history).c_str(),
3099 getstatkey(key_quit, section_editor).c_str());
3101 while(!fin) {
3102 if(db.open(k)) {
3103 switch(k) {
3104 case -2:
3105 sub = face.inputstr(_("search for: "), sub);
3106 if(input.getlastkey() == KEY_ESC) break;
3107 case -3:
3108 if(!sub.empty())
3109 for(k = mhist.getpos()+1; k < mhist.getcount(); k++) {
3110 im = static_cast<imevent *> (mhist.getref(k));
3111 if(im)
3112 if(im->contains(sub)) {
3113 mhist.setpos(k);
3114 break;
3117 break;
3118 case -4:
3119 r = false;
3120 fin = true;
3121 break;
3123 default:
3124 if(mhist.getref(k-1)) {
3125 im = static_cast<imevent *> (mhist.getref(k-1));
3126 r = fin = true;
3128 break;
3130 } else {
3131 r = false;
3132 fin = true;
3136 db.close();
3137 restoreworkarea();
3140 return r;
3143 // ----------------------------------------------------------------------------
3145 void icqface::menuidle(verticalmenu &m) {
3146 cicq.idle();
3148 if(face.dotermresize) {
3149 if(&m == &face.mcontacts->menu) {
3150 face.redraw();
3151 face.dotermresize = false;
3157 void icqface::dialogidle(dialogbox &caller) {
3158 cicq.idle();
3161 void icqface::textbrowseridle(textbrowser &b) {
3162 cicq.idle();
3165 void icqface::transferidle(dialogbox &b) {
3166 time_t tstart = time(0);
3168 while(!cicq.idle(HIDL_SOCKEXIT)) {
3169 if(time(0)-tstart > 3) {
3170 b.gettree()->menu.abort();
3171 break;
3176 string icqface::getstatkey(int key, int section) const {
3177 int i = 2;
3178 string key2;
3179 string keys = action2key(key, section);
3181 while((key2 = action2key(key, section, i)) != "") {
3182 keys += "/" + key2;
3183 i++;
3186 return keys;
3189 string icqface::getmenuitem(string mtext, int width, int key, int section) {
3190 int i = 2;
3191 string text = " " + mtext + " ";
3192 string keyname = action2key(key, section);
3193 string key2;
3194 int s = keyname.size()+text.size();
3196 if (s > width)
3197 return text;
3199 while ((key2 = action2key(key, section, i)) != "" && (s += key2.size()+1) < width) {
3200 keyname += "/" + key2;
3201 i++;
3204 for (s = keyname.size()+text.size(); s < width; s++)
3205 text += " ";
3207 text += keyname;
3209 return text;
3212 int icqface::key2action(int k, int s) {
3213 for(vector<icqconf::keybinding>::size_type i = 0; i < icqconf::keys.size(); i++)
3214 if(k == icqconf::keys[i].key && s == icqconf::keys[i].section)
3215 return icqconf::keys[i].action;
3217 return -1;
3220 string icqface::action2key(int a, int s, int n) const {
3221 string key;
3222 #ifdef HAVE_SSTREAM
3223 std::ostringstream o;
3224 #else
3225 std::ostrstream o;
3226 #endif
3227 vector<icqconf::keybinding>::size_type i = 0;
3229 for( ; i < icqconf::keys.size(); i++)
3230 if(a == icqconf::keys[i].action && s == icqconf::keys[i].section) {
3231 if (n <= 1)
3232 break;
3233 n--;
3236 if (i == icqconf::keys.size())
3237 return "";
3238 else if(icqconf::keys[i].key == '\r')
3239 return "enter";
3240 else if(icqconf::keys[i].key == ' ')
3241 return "space";
3242 else if(icqconf::keys[i].key == '/')
3243 return "\'/\'";
3244 else if(icqconf::keys[i].key == ALT('\r'))
3245 return "alt-enter";
3246 else if(icqconf::keys[i].key == CTRL(' '))
3247 return "^space";
3248 else if(icqconf::keys[i].key == ALT(' '))
3249 return "alt-space";
3250 else if(icqconf::keys[i].key == 339)
3251 return "pageup";
3252 else if(icqconf::keys[i].key == 338)
3253 return "pagedown";
3254 else if(icqconf::keys[i].key == 9)
3255 return "tab";
3256 else if(icqconf::keys[i].key == 27)
3257 return "esc-esc";
3258 else if(icqconf::keys[i].key == 331)
3259 return "insert";
3260 else if(icqconf::keys[i].key == KEY_DC)
3261 return "del";
3262 else if(icqconf::keys[i].alt && icqconf::keys[i].ctrl)
3263 o << "F" << (icqconf::keys[i].key-KEY_F0);
3264 else if(icqconf::keys[i].alt)
3265 o << "alt-" << (char)icqconf::keys[i].key;
3266 else if(icqconf::keys[i].ctrl)
3267 o << "^" << (char)(icqconf::keys[i].key+64);
3268 else
3269 o << (char)icqconf::keys[i].key;
3271 string ret;
3272 #ifdef HAVE_SSTREAM
3273 ret = o.str();
3274 #else
3275 ret = string(o.str(), o.pcount());
3276 o.freeze(false);
3277 #endif
3279 return ret;
3282 int icqface::contactskeys(verticalmenu &m, int k) {
3283 icqcontact *c = NULL;
3285 int id = face.mcontacts->getid(face.mcontacts->menu.getpos());
3286 if(id != -1 && ! face.mcontacts->isnode(id))
3287 c = (icqcontact *) face.mcontacts->getref(m.getpos()+1);
3289 set<hookcapab::enumeration> capab;
3291 face.extk = 0;
3293 if(c)
3294 if(c->getdesc() != contactroot) {
3295 capab = gethook(c->getdesc().pname).getCapabs();
3298 switch(face.key2action(k, section_contact)) {
3299 case key_info:
3300 face.extk = ACT_INFO;
3301 break;
3303 case key_remove:
3304 face.extk = ACT_REMOVE;
3305 break;
3307 case key_quit:
3308 if (conf.getaskquit())
3309 if(face.ask("Really Quit?", ASK_YES | ASK_NO, ASK_NO) == ASK_NO)
3310 break;
3311 face.extk = ACT_QUIT;
3312 break;
3314 case key_send_url:
3315 if(capab.count(hookcapab::urls))
3316 face.extk = ACT_URL;
3317 break;
3319 case key_change_status:
3320 face.extk = ACT_STATUS;
3321 break;
3323 case key_history:
3324 face.extk = ACT_HISTORY;
3325 break;
3327 case key_next_chat:
3328 face.extk = ACT_DUMMY;
3329 face.next_chat(true);
3330 break;
3332 case key_prev_chat:
3333 face.extk = ACT_DUMMY;
3334 face.next_chat(false);
3335 break;
3337 case key_add: face.extk = ACT_ADD; break;
3339 case key_send_contact:
3340 if(capab.count(hookcapab::contacts))
3341 face.extk = ACT_CONTACT;
3342 break;
3343 case key_rss_check:
3344 if(c && c->getdesc().pname == rss)
3345 face.extk = ACT_PING;
3346 break;
3348 case key_fetch_away_message:
3349 if(capab.count(hookcapab::fetchaway))
3350 face.extk = ACT_FETCHAWAY;
3351 break;
3353 case key_user_menu:
3354 face.extk = ACT_MENU;
3355 break;
3357 case key_general_menu:
3358 face.extk = ACT_GMENU;
3359 break;
3361 case key_hide_offline:
3362 face.extk = ACT_HIDEOFFLINE;
3363 break;
3365 case key_contact_external_action:
3366 if(!ischannel(c) && c)
3367 face.extk = ACT_EXTERN;
3368 break;
3370 case key_rename:
3371 if(!ischannel(c) && c)
3372 face.extk = ACT_RENAME;
3373 break;
3375 case key_version:
3376 if(capab.count(hookcapab::version))
3377 face.extk = ACT_VERSION;
3378 break;
3380 case key_edit:
3381 if(!ischannel(c) && c)
3382 face.extk = ACT_EDITUSER;
3383 break;
3385 case key_ignore:
3386 if(c) face.extk = ACT_IGNORE;
3387 break;
3389 case key_left_panel_move_right:
3390 face.leftpanelwidth_inc(1);
3391 face.redraw();
3392 break;
3394 case key_left_panel_move_left:
3395 face.leftpanelwidth_inc(-1);
3396 face.redraw();
3397 break;
3399 case key_log_panel_move_up:
3400 face.logpanelheight_inc(1);
3401 face.redraw();
3402 break;
3404 case key_log_panel_move_down:
3405 face.logpanelheight_inc(-1);
3406 face.redraw();
3407 break;
3409 case key_quickfind: face.extk = ACT_QUICKFIND; break;
3412 if(face.extk && face.key2action(k, section_contact) != -1) {
3413 return m.getpos()+1;
3414 } else {
3415 return -1;
3419 int icqface::multiplekeys(verticalmenu &m, int k) {
3420 switch(k) {
3421 case ' ':
3422 case 'x':
3423 case 'X':
3424 return -2;
3425 case '/':
3426 return -3;
3429 if(face.key2action(k, section_contact) == key_quickfind)
3430 return -3;
3432 return -1;
3435 int icqface::findpgpkeys(dialogbox &db, int k) {
3436 int r;
3438 r = face.key2action(k, section_contact);
3439 switch(r) {
3440 case key_quickfind:
3441 return -3;
3444 return -1;
3447 string icqface::getprotocolchar(protocolname pname) const {
3448 static const string pprefixes[protocolname_size] = {
3449 "i", "y", "m", "a", "#", "j", "r", "l", "g", "n"
3452 if(pname > protocolname_size)
3453 return "x";
3455 return pprefixes[pname];
3458 int icqface::historykeys(dialogbox &db, int k) {
3459 static string sub;
3461 switch(face.key2action(k, section_history)) {
3462 case key_search:
3463 return -2;
3464 case key_search_again:
3465 return -3;
3466 case key_quit:
3467 return -4;
3470 return -1;
3473 int icqface::editmsgkeys(texteditor &e, int k) {
3474 char *p;
3476 if(k == '\r' && conf.getentersends(face.passinfo.pname)) {
3477 p = e.save("");
3478 face.editdone = strlen(p);
3479 free(p);
3480 if(face.editdone) return -1; else return 0;
3483 switch(face.key2action(k, section_editor)) {
3484 case key_send_message:
3485 p = e.save("");
3486 face.editdone = strlen(p);
3487 free(p);
3488 if(face.editdone) return -1; else break;
3489 case key_multiple_recipients:
3490 face.multicontacts("");
3491 break;
3492 case key_history:
3493 cicq.history(face.passinfo);
3494 break;
3495 case key_prev_chat:
3496 face.editdone = false;
3497 face.next_chat(false);
3498 return -1;
3499 case key_next_chat:
3500 face.editdone = false;
3501 face.next_chat(true);
3502 return -1;
3503 case key_out_chat:
3504 face.editdone = false;
3505 face.next_chat(false);
3506 face.last_selected = (icqcontact* ) clist.at(0);
3507 return -1;
3508 case key_info:
3509 cicq.userinfo(face.passinfo);
3510 break;
3511 case key_show_urls:
3512 face.showextractedurls(); break;
3513 case key_fullscreen:
3514 if(face.passevent)
3515 face.fullscreenize(face.passevent);
3516 break;
3517 case key_chat_panel_move_up:
3518 face.chatpanelheight_inc(1);
3519 face.stay_in_chat = true;
3520 return -1;
3521 break;
3522 case key_chat_panel_move_down:
3523 face.chatpanelheight_inc(-1);
3524 face.stay_in_chat = true;
3525 return -1;
3526 break;
3528 #ifdef HAVE_LIBOTR
3529 case key_otr_start_session:
3530 otr.start_session(face.last_selected);
3531 face.stay_in_chat = true;
3532 return -1;
3533 break;
3534 case key_otr_end_session:
3535 otr.end_session(face.last_selected);
3536 face.stay_in_chat = true;
3537 return -1;
3538 break;
3539 #endif
3541 case key_quit:
3542 return -1;
3545 return 0;
3548 int icqface::userinfokeys(dialogbox &db, int k) {
3549 switch(face.key2action(k, section_info)) {
3550 case key_show_urls: face.showextractedurls(); break;
3551 case key_user_external_action: face.userinfoexternal(face.passinfo); break;
3554 return -1;
3557 int icqface::eventviewkeys(dialogbox &db, int k) {
3558 switch(face.key2action(k, section_history)) {
3559 case key_show_urls:
3560 face.showextractedurls();
3561 break;
3562 case key_fullscreen:
3563 face.fullscreenize(face.passevent);
3564 break;
3565 case key_quit:
3566 return 0;
3569 return -1;
3572 int icqface::findreskeys(dialogbox &db, int k) {
3573 if(face.key2action(k, section_contact) == key_quickfind)
3574 return -3;
3576 return -1;
3579 int icqface::statuskeys(verticalmenu &m, int k) {
3580 char *status_order = "o_adnlcfi"; // Shortcuts for status
3581 char *p = strchr(status_order, k);
3582 if (p)
3583 return (1 + p - status_order);
3584 return -1;
3587 int icqface::fullscreenkeys(textbrowser &m, int k) {
3588 switch(face.key2action(k, section_editor)) {
3589 case key_fullscreen:
3590 return 0;
3593 return -1;
3596 void icqface::editidle(texteditor &e) {
3597 cicq.idle();
3600 void icqface::editchatidle(texteditor &e) {
3601 icqcontact *c;
3603 cicq.idle(HIDL_SOCKEXIT);
3605 if(c = clist.get(face.passinfo))
3606 if(c->hasevents()) {
3607 face.renderchathistory();
3611 void icqface::textinputidle(textinputline &il) {
3612 cicq.idle();
3615 void icqface::termresize(void) {
3616 face.dotermresize = true;
3619 void icqface::relaxedupdate() {
3620 fneedupdate = true;
3623 bool icqface::updaterequested() {
3624 return fneedupdate;
3627 void icqface::redraw() {
3628 if(!mainscreenblock) {
3629 done();
3630 init();
3631 draw();
3634 * Terminal resize specific
3637 vector<string> flog;
3638 vector<string>::iterator il;
3640 face.status("#");
3642 flog = face.lastlog;
3643 face.lastlog.clear();
3645 bool lts, lo;
3646 conf.getlogoptions(lts, lo);
3647 conf.setlogoptions(false, lo);
3649 for(il = flog.begin() ; il != flog.end(); ++il)
3650 face.log(*il);
3652 conf.setlogoptions(lts, lo);
3654 doredraw = fneedupdate = false;
3655 } else {
3656 doredraw = fneedupdate = true;
3660 void icqface::xtermtitle(const string &text) {
3661 if(conf.getxtitles()) {
3662 string term = getenv("TERM") ? getenv("TERM") : "";
3664 if(term == "xterm" || term == "Eterm" || term == "aterm"
3665 || term == "rxvt" || term.substr(0, 6) == "screen")
3666 cout << "\x1b]1;\x07\x1b]0;" << "centerim" << (text.empty() ? "" : (string) ": " + text) << "\x07" << flush;
3670 void icqface::xtermtitle(const char *fmt, ...) {
3671 if(conf.getxtitles()) {
3672 va_list ap;
3673 char buf[1024];
3675 va_start(ap, fmt);
3676 vsnprintf(buf, sizeof(buf), fmt, ap);
3677 xtermtitle((string) buf);
3678 va_end(ap);
3682 void icqface::xtermtitlereset() {
3683 if(conf.getxtitles()) {
3684 const char *p = getenv("TERM");
3685 if(p) xtermtitle((string) p);
3689 // ----------------------------------------------------------------------------
3691 icqface::icqprogress::icqprogress() {
3692 w = 0;
3695 icqface::icqprogress::~icqprogress() {
3696 delete w;
3699 void icqface::icqprogress::log(const char *fmt, ...) {
3700 va_list ap;
3701 char buf[1024];
3703 va_start(ap, fmt);
3704 vsnprintf(buf, sizeof(buf), fmt, ap);
3705 va_end(ap);
3707 if(curline >= face.sizeDlg.height-1) {
3708 w->redraw();
3709 curline = 0;
3712 w->write(2, 1+curline++, buf);
3715 void icqface::icqprogress::show(const string &title ) {
3716 if(!w) {
3717 w = new textwindow(0, 0, face.sizeDlg.width, face.sizeDlg.height,
3718 conf.getcolor(cp_dialog_frame), TW_CENTERED);
3721 w->set_title(conf.getcolor(cp_dialog_highlight), title);
3722 w->open();
3724 curline = 0;
3727 void icqface::icqprogress::hide() {
3728 w->close();
3731 void icqface::leftpanelwidth_inc(const int inc) {
3732 int i = conf.getleftpanelwidth();
3733 if ( ((i+inc) >= MinPanelWidth) && ((i+inc) <= (COLS - MinPanelWidth))) {
3734 conf.setleftpanelwidth(i+inc);
3735 dotermresize = true ;
3739 void icqface::logpanelheight_inc(const int inc) {
3740 int i = conf.getlogpanelheight();
3741 if (((i+inc) >= MinPanelHeight) && ((i+inc) <= (LINES - MinPanelHeight))) {
3742 conf.setlogpanelheight(i+inc);
3743 dotermresize = true ;
3747 void icqface::chatpanelheight_inc(const int inc ) {
3748 chatlines = conf.getchatpanelheight()+inc;
3749 conf.setchatpanelheight(chatlines);