1 //============================================================================= 2 // 3 // File : KviKvsCoreFunctions_af.cpp 4 // Creation date : Fri 31 Oct 2003 01:52:04 by Szymon Stefanek 5 // 6 // This file is part of the KVIrc IRC client distribution 7 // Copyright (C) 2003-2010 Szymon Stefanek <pragma at kvirc dot net> 8 // Copyright © 2010 Kai Wasserbäch <debian@carbon-project.org> 9 // 10 // This program is FREE software. You can redistribute it and/or 11 // modify it under the terms of the GNU General Public License 12 // as published by the Free Software Foundation; either version 2 13 // of the License, or (at your option) any later version. 14 // 15 // This program is distributed in the HOPE that it will be USEFUL, 16 // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 18 // See the GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with this program. If not, write to the Free Software Foundation, 22 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 // 24 //============================================================================= 25 26 #include "KviKvsCoreFunctions.h" 27 #include "KviKvsKernel.h" 28 #include "KviKvsObjectController.h" 29 #include "KviLocale.h" 30 #include "KviApplication.h" 31 #include "KviChannelWindow.h" 32 #include "KviConsoleWindow.h" 33 #include "KviIrcConnection.h" 34 #include "KviIrcConnectionUserInfo.h" 35 #include "KviControlCodes.h" 36 #include "KviAvatar.h" 37 #include "KviIrcUserDataBase.h" 38 #include "KviMainWindow.h" 39 #include "KviStatusBar.h" 40 #include "KviQString.h" 41 #include "KviSSLMaster.h" 42 #include "KviOptions.h" 43 #include "KviKvsAliasManager.h" 44 #include "KviKvsScript.h" 45 #include "KviBuildInfo.h" 46 47 #include <QChar> 48 #include <QDateTime> 49 #include <QString> 50 #include <QStringList> 51 52 namespace KviKvsCoreFunctions 53 { 54 /* 55 @doc: active 56 @type: 57 function 58 @title: 59 $active 60 @short: 61 Retrieves the window ID of the active window 62 @syntax: 63 <integer> $active[(<irc context id:integer>)] 64 @description: 65 Returns the [b]window ID[/b] of the active window 66 bound to the IRC context specified by <irc context id>. 67 If no window matches the specified IRC context, an invalid 68 window ID is returned (0).[br] 69 If no <irc context id> is specified, then 70 the application active window is returned (the window 71 that currently has the input focus).[br][br]Note that in this 72 case the returned window may also belong to another IRC 73 context or be not bound to any IRC context at all. 74 In some extreme cases you may even get a window that 75 has no output widget and thus has its output redirected. 76 Using the [i]global[/i] active window should be used only 77 for communicating something [b]really[/b] urgent (and maybe 78 unrelated to a specific IRC connection) to the user. 79 @seealso: 80 [fnc]$window[/fnc] 81 */ 82 KVSCF(active)83 KVSCF(active) 84 { 85 kvs_uint_t uContextId; 86 KVSCF_PARAMETERS_BEGIN 87 KVSCF_PARAMETER("context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uContextId) 88 KVSCF_PARAMETERS_END 89 90 KviWindow * wnd = nullptr; 91 if(KVSCF_pParams->count() > 0) 92 { 93 KviConsoleWindow * cons = g_pApp->findConsole(uContextId); 94 if(cons) 95 wnd = cons->activeWindow(); 96 } 97 else 98 { 99 wnd = g_pActiveWindow; 100 } 101 102 KVSCF_pRetBuffer->setInteger((kvs_int_t)(wnd ? wnd->numericId() : 0)); 103 return true; 104 } 105 106 /* 107 @doc: aliasBody 108 @type: 109 function 110 @title: 111 $aliasBody 112 @short: 113 Returns the body of an alias, if it exists 114 @syntax: 115 <string> $aliasBody(<alias_name:string>) 116 @description: 117 Returns the KVS body of the specified alias. 118 If the alias doesn't exist it just returns an empty string. 119 @seealso: 120 [cmd]alias[/cmd] 121 */ 122 KVSCF(aliasBody)123 KVSCF(aliasBody) 124 { 125 QString szAliasName; 126 KVSCF_PARAMETERS_BEGIN 127 KVSCF_PARAMETER("alias_name", KVS_PT_STRING, 0, szAliasName) 128 KVSCF_PARAMETERS_END 129 130 const KviKvsScript * pAlias = KviKvsAliasManager::instance()->lookup(szAliasName); 131 if(pAlias) 132 KVSCF_pRetBuffer->setString(pAlias->code()); 133 else 134 KVSCF_pRetBuffer->setString(QString()); 135 return true; 136 } 137 138 /* 139 @doc: ascii 140 @type: 141 function 142 @title: 143 $ascii 144 @short: 145 Returns the Unicode code of a character 146 @syntax: 147 <variant> $ascii(<char:string>) 148 @description: 149 This function has been renamed to $unicode and is present 150 only for backward compatibility. 151 @seealso: 152 [fnc]$cr[/fnc], [fnc]$lf[/fnc], [fnc]$char[/fnc] 153 */ 154 155 /* 156 @doc: asciiToBase64 157 @type: 158 function 159 @title: 160 $asciiToBase64 161 @short: 162 Returns an encoded base64 string 163 @syntax: 164 $asciiToBase64(<ascii_string>) 165 @description: 166 Encodes an ASCII string to its base64 encoded representation 167 Please note that since KVS is Unicode based, this function 168 will first encode the string in UTF-8 and then base64-encode. 169 This means that it is substantially only 7-bit safe (ASCII codes below 128). 170 @examples: 171 [example] 172 [cmd]echo[/cmd] $asciiToBase64("Hello!") 173 [/example] 174 @seealso: 175 [fnc]$base64toascii[/fnc] 176 */ 177 KVSCF(asciiToBase64)178 KVSCF(asciiToBase64) 179 { 180 QString szAscii; 181 KVSCF_PARAMETERS_BEGIN 182 KVSCF_PARAMETER("ascii_string", KVS_PT_STRING, 0, szAscii) 183 KVSCF_PARAMETERS_END 184 185 KviCString tmp1(szAscii); 186 if(tmp1.len() > 0) 187 { 188 KviCString tmp2; 189 tmp2.bufferToBase64(tmp1.ptr(), tmp1.len()); 190 KVSCF_pRetBuffer->setString(QString(tmp2.ptr())); 191 } 192 else 193 { 194 KVSCF_pRetBuffer->setString(QString()); 195 } 196 return true; 197 } 198 199 /* 200 @doc: asciiToHex 201 @type: 202 function 203 @title: 204 $asciiToHex 205 @short: 206 Returns an encoded hex string 207 @syntax: 208 <string> $asciiToHex(<ascii_string:string>) 209 @description: 210 Encodes an ASCII string to its hex encoded representation. 211 Please note that since KVS is Unicode based, this function 212 will first encode the string in UTF-8 and then hex-encode. 213 This means that it is substantially only 7bit safe (ASCII codes below 128). 214 @examples: 215 [example] 216 [cmd]echo[/cmd] $asciiToHex("Hello!") 217 [/example] 218 @seealso: 219 [fnc]$hextoascii[/fnc] 220 */ 221 KVSCF(asciiToHex)222 KVSCF(asciiToHex) 223 { 224 QString szAscii; 225 KVSCF_PARAMETERS_BEGIN 226 KVSCF_PARAMETER("ascii_string", KVS_PT_STRING, 0, szAscii) 227 KVSCF_PARAMETERS_END 228 229 KviCString tmp1(szAscii); 230 if(tmp1.len() > 0) 231 { 232 KviCString tmp2; 233 tmp2.bufferToHex(tmp1.ptr(), tmp1.len()); 234 KVSCF_pRetBuffer->setString(QString(tmp2.ptr())); 235 } 236 else 237 { 238 KVSCF_pRetBuffer->setString(QString()); 239 } 240 return true; 241 } 242 243 /* 244 @doc: array 245 @type: 246 function 247 @title: 248 $array 249 @short: 250 Explicitly creates an array 251 @syntax: 252 <array> $array(<item:variant>,<item:variant>,<item:variant>,....); 253 @description: 254 Returns an array with the specified items. The items are indexed starting from 0. 255 This is just an explicit way of creating an array with a defined set of items, 256 useful for increasing readability. 257 @examples: 258 [example] 259 [cmd]alias[/cmd](test) { 260 [cmd]return[/cmd] $array(1,2,3); 261 } 262 %x = $test(); 263 [cmd]foreach[/cmd](%y,%x) { 264 [cmd]echo[/cmd] %y; 265 } 266 [/example] 267 @seealso: 268 [fnc]$hash[/fnc] 269 */ 270 KVSCF(array)271 KVSCF(array) 272 { 273 KviKvsArray * a = new KviKvsArray(); 274 275 for(KviKvsVariant * v = KVSCF_pParams->first(); v; v = KVSCF_pParams->next()) 276 a->append(new KviKvsVariant(*v)); 277 278 KVSCF_pRetBuffer->setArray(a); 279 return true; 280 } 281 282 /* 283 @doc: away 284 @type: 285 function 286 @title: 287 $away 288 @short: 289 Returns true if the current user is away 290 @syntax: 291 <boolean> $away 292 @description: 293 Returns true if the current user is away, else false. 294 If the current IRC context is not connected at all, this function returns false. 295 */ 296 KVSCF(away)297 KVSCF(away) 298 { 299 kvs_uint_t uCntx; 300 301 KVSCF_PARAMETERS_BEGIN 302 KVSCF_PARAMETER("irc_context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uCntx) 303 KVSCF_PARAMETERS_END 304 305 KviConsoleWindow * cns = nullptr; 306 307 if(KVSCF_pParams->count() > 0) 308 { 309 cns = g_pApp->findConsole(uCntx); 310 if(cns && cns->context()->isConnected()) 311 KVSCF_pRetBuffer->setBoolean(cns->connection()->userInfo()->isAway()); 312 else 313 KVSCF_pRetBuffer->setNothing(); 314 } 315 else 316 { 317 cns = KVSCF_pContext->window()->console(); 318 if(cns) 319 { 320 if(cns->context()->isConnected()) 321 KVSCF_pRetBuffer->setBoolean(cns->connection()->userInfo()->isAway()); 322 else 323 KVSCF_pRetBuffer->setNothing(); 324 } 325 else 326 { 327 KVSCF_pContext->warning(__tr2qs_ctx("This window has no associated IRC context", "kvs")); 328 KVSCF_pRetBuffer->setNothing(); 329 } 330 } 331 return true; 332 } 333 334 /* 335 @doc: b 336 @type: 337 function 338 @title: 339 $b 340 @short: 341 Returns the BOLD mIRC control character 342 @syntax: 343 <string> $b 344 @description: 345 Returns the BOLD mIRC control character (Ctrl+B).[br] 346 @seealso: 347 [fnc]$k[/fnc], [fnc]$i[/fnc], [fnc]$u[/fnc], [fnc]$r[/fnc], [fnc]$o[/fnc] 348 */ 349 KVSCF(b)350 KVSCF(b) 351 { 352 KVSCF_pRetBuffer->setString(QString(QChar(KviControlCodes::Bold))); 353 return true; 354 } 355 356 /* 357 @doc: base64toAscii 358 @type: 359 function 360 @title: 361 $base64ToAscii 362 @short: 363 Returns a decoded base64 string 364 @syntax: 365 <string> $base64ToAscii(<base_64_encoded_string:string>) 366 @description: 367 Decodes a base64 encoded string. 368 @seealso: 369 [fnc]$asciiToBase64[/fnc] 370 */ 371 KVSCF(base64ToAscii)372 KVSCF(base64ToAscii) 373 { 374 QString szBase64; 375 KVSCF_PARAMETERS_BEGIN 376 KVSCF_PARAMETER("base64_encoded_string", KVS_PT_STRING, 0, szBase64) 377 KVSCF_PARAMETERS_END 378 379 KviCString tmp1(szBase64); 380 char * buf; 381 int len = tmp1.base64ToBuffer(&buf, true); 382 QString szRet(buf); 383 szRet.truncate(len); 384 KVSCF_pRetBuffer->setString(szRet); 385 KviCString::freeBuffer(buf); 386 return true; 387 } 388 389 /* 390 @doc: boolean 391 @type: 392 function 393 @title: 394 $boolean 395 @short: 396 Casts a variable to a boolean 397 @syntax: 398 <integer> $boolean(<data:variant>) 399 @description: 400 Forces <data> to be a boolean data type by first casting 401 to integer (see [fnc]$int[/fnc]()) and then comparing the result against zero. 402 A zero integer will result in a false value while a non-zero one 403 will result in a true value. 404 This function is similar to the C++ (bool) cast and is internally 405 aliased to [fnc]$bool[/fnc] too. 406 Note that since KVIrc does most of the casting work automatically 407 you shouldn't need to use this function. 408 @seealso: 409 [fnc]$real[/fnc] 410 [fnc]$integer[/fnc] 411 */ 412 413 /* 414 @doc: bool 415 @type: 416 function 417 @title: 418 $bool 419 @short: 420 Casts a variable to a boolean 421 @syntax: 422 <integer> $bool(<data:variant>) 423 @description: 424 This is an internal alias to [fnc]$boolean[/fnc](). 425 @seealso: 426 [fnc]$real[/fnc] 427 [fnc]$integer[/fnc] 428 */ 429 KVSCF(boolean)430 KVSCF(boolean) 431 { 432 KviKvsVariant * v; 433 KVSCF_PARAMETERS_BEGIN 434 KVSCF_PARAMETER("data", KVS_PT_VARIANT, 0, v) 435 KVSCF_PARAMETERS_END 436 437 kvs_int_t iVal; 438 v->castToInteger(iVal); 439 KVSCF_pRetBuffer->setBoolean(iVal); 440 return true; 441 } 442 443 /* 444 @doc: certificate 445 @type: 446 function 447 @title: 448 $certificate 449 @short: 450 Returns information about the local certificate 451 @syntax: 452 <string> $certificate(<query:string>[,<param1:string>]) 453 @description: 454 Returns the requested information about local certificate.[br] 455 Some queries can accept an optional parameter <param1>.[br] 456 Available query strings are: 457 [ul] 458 [li]signatureType[/li] 459 [li]signatureContents[/li] 460 [li]subjectCountry[/li] 461 [li]subjectStateOrProvince[/li] 462 [li]subjectLocality[/li] 463 [li]subjectOrganization[/li] 464 [li]subjectOrganizationalUnit[/li] 465 [li]subjectCommonName[/li] 466 [li]issuerCountry[/li] 467 [li]issuerStateOrProvince[/li] 468 [li]issuerLocality[/li] 469 [li]issuerOrganization[/li] 470 [li]issuerOrganizationalUnit[/li] 471 [li]issuerCommonName[/li] 472 [li]publicKeyBits[/li] 473 [li]publicKeyType[/li] 474 [li]serialNumber[/li] 475 [li]pemBase64[/li] 476 [li]version[/li] 477 [li]fingerprintIsValid[/li] 478 [li]fingerprintDigestId[/li] 479 [li]fingerprintDigestStr[/li] 480 [li]fingerprintContents * accepts parameter interpreted as [i]digest name[/i][/li] 481 [/ul] 482 @seealso: 483 [fnc]$str.evpSign[/fnc] 484 [fnc]$str.evpVerify[/fnc] 485 [fnc]$dcc.getSSLCertInfo[/fnc] 486 */ 487 KVSCF(certificate)488 KVSCF(certificate) 489 { 490 QString szQuery; 491 QString szParam1; 492 493 KVSCF_PARAMETERS_BEGIN 494 KVSCF_PARAMETER("query", KVS_PT_STRING, 0, szQuery) 495 KVSCF_PARAMETER("param1", KVS_PT_STRING, KVS_PF_OPTIONAL, szParam1) 496 KVSCF_PARAMETERS_END 497 498 #ifndef COMPILE_SSL_SUPPORT 499 KVSCF_pContext->warning(__tr2qs("This executable was built without SSL support")); 500 return true; 501 #else 502 KviSSL::globalSSLInit(); 503 X509 * cert = nullptr; 504 505 if(!KVI_OPTION_BOOL(KviOption_boolUseSSLCertificate)) 506 { 507 KVSCF_pContext->warning(__tr2qs("No public key certificate defined in KVIrc options.")); 508 KVSCF_pRetBuffer->setString(""); 509 return true; 510 } 511 512 FILE * f = fopen(KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data(), "r"); 513 if(!f) 514 { 515 KVSCF_pContext->warning(__tr2qs("File I/O error while trying to use the public key file %s"), KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data()); 516 KVSCF_pRetBuffer->setString(""); 517 return true; 518 } 519 520 QString szPass = KVI_OPTION_STRING(KviOption_stringSSLCertificatePass).toUtf8(); 521 PEM_read_X509(f, &cert, nullptr, szPass.data()); 522 523 fclose(f); 524 525 if(!cert) 526 { 527 KVSCF_pContext->warning(__tr2qs("Can't load certificate from public key file %s"), KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data()); 528 KVSCF_pRetBuffer->setString(""); 529 return true; 530 } 531 532 // KviSSLCertificate takes ownership of the X509 structure, no need to free it 533 KviSSLCertificate * pCert = new KviSSLCertificate(cert); 534 535 if(!pCert) 536 { 537 KVSCF_pContext->warning(__tr2qs_ctx("Error retrieving information from the local certificate", "dcc")); 538 KVSCF_pRetBuffer->setString(""); 539 return true; 540 } 541 542 if(KviSSLMaster::getSSLCertInfo(pCert, szQuery, szParam1, KVSCF_pRetBuffer)) 543 return true; 544 545 KVSCF_pContext->warning(__tr2qs_ctx("Unable to get certificate information: query not recognized", "dcc")); 546 KVSCF_pRetBuffer->setString(""); 547 return true; 548 #endif 549 } 550 551 /* 552 @doc: channel 553 @type: 554 function 555 @title: 556 $channel 557 @short: 558 Retrieves the window ID of a specified channel 559 @syntax: 560 $channel[(<channel name>[,<irc context id>])] 561 @description: 562 Returns the [b]window ID[/b] of channel matching the 563 <channel name> and bound to the connection specified by 564 <irc context id>[br] 565 If no window matches the specified name or connection, an invalid 566 window ID is returned [b]0[/b].[br] 567 If no <irc context id> is specified, this function looks for 568 the channel in the current connection context (if any).[br] 569 If no <channel name> is specified, this function returns the current 570 channel window ID, if executed in a channel, and [b]0[/b] otherwise.[br] 571 @seealso: 572 [fnc]$window[/fnc], 573 [fnc]$query[/fnc], 574 [fnc]$console[/fnc], 575 [doc:window_naming_conventions]Window naming conventions[/doc] 576 */ 577 KVSCF(channel)578 KVSCF(channel) 579 { 580 QString szName; 581 kvs_uint_t uContextId; 582 KVSCF_PARAMETERS_BEGIN 583 KVSCF_PARAMETER("channel_name", KVS_PT_NONEMPTYSTRING, KVS_PF_OPTIONAL, szName) 584 KVSCF_PARAMETER("context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uContextId) 585 KVSCF_PARAMETERS_END 586 587 KviWindow * wnd = nullptr; 588 if(KVSCF_pParams->count() > 1) 589 { 590 KviConsoleWindow * cns = g_pApp->findConsole(uContextId); 591 if(cns && cns->connection()) 592 wnd = cns->connection()->findChannel(szName); 593 else if (!cns) 594 KVSCF_pContext->warning(__tr2qs_ctx("No such IRC context (%u)", "kvs"), uContextId); 595 } 596 else if(KVSCF_pParams->count() == 1) 597 { 598 if(KVSCF_pContext->window()->connection()) 599 wnd = KVSCF_pContext->window()->connection()->findChannel(szName); 600 else 601 { 602 if(!KVSCF_pContext->window()->console()) 603 KVSCF_pContext->warning(__tr2qs_ctx("This window is not associated to an IRC context", "kvs")); 604 } 605 } 606 else 607 { 608 if(KVSCF_pContext->window()->type() == KviWindow::Channel) 609 wnd = KVSCF_pContext->window(); 610 } 611 612 KVSCF_pRetBuffer->setInteger((kvs_int_t)(wnd ? wnd->numericId() : 0)); 613 return true; 614 } 615 616 /* 617 @doc: char 618 @type: 619 function 620 @title: 621 $char 622 @short: 623 Returns a character specified by Unicode 624 @syntax: 625 <string> $char(<unicode_value:integer>) 626 @description: 627 Returns a character corresponding to the Unicode code <unicode_value>.[br] 628 This function can not return NUL character (Unicode 0). Basically 629 you should never need it: if you do, drop me a mail.[br] 630 If the <unicode_code> is not a valid Unicode code (or is 0), this function returns 631 an empty string.[br] 632 @seealso: 633 [fnc]$cr[/fnc], [fnc]$lf[/fnc], [fnc]$unicode[/fnc] 634 */ 635 KVSCF(charCKEYWORDWORKAROUND)636 KVSCF(charCKEYWORDWORKAROUND) 637 { 638 kvs_uint_t ac; 639 KVSCF_PARAMETERS_BEGIN 640 KVSCF_PARAMETER("unicode_value", KVS_PT_UINT, 0, ac) 641 KVSCF_PARAMETERS_END 642 643 if(ac != 0 && ac < 65536) 644 KVSCF_pRetBuffer->setString(QString(QChar((unsigned short)ac))); 645 else 646 KVSCF_pRetBuffer->setString(QString()); 647 return true; 648 } 649 650 /* 651 @doc: classdefined 652 @type: 653 function 654 @title: 655 $classdefined 656 @short: 657 Checks if a class is defined 658 @syntax: 659 $classdefined(<class_name>) 660 @description: 661 Returns [b]1[/b] if the class <class_name> is defined, and [b]0[/b] otherwise. 662 */ 663 KVSCF(classDefined)664 KVSCF(classDefined) 665 { 666 // prologue: parameter handling 667 QString szClassName; 668 669 KVSCF_PARAMETERS_BEGIN 670 KVSCF_PARAMETER("className", KVS_PT_NONEMPTYSTRING, 0, szClassName) 671 KVSCF_PARAMETERS_END 672 KVSCF_pRetBuffer->setBoolean(KviKvsKernel::instance()->objectController()->lookupClass(szClassName) != nullptr); 673 return true; 674 } 675 676 /* 677 @doc: console 678 @type: 679 function 680 @title: 681 $console 682 @short: 683 Retrieves the window ID of a specified console 684 @syntax: 685 $console[(<irc context id>)] 686 @description: 687 Returns the [b]window ID[/b] of the console bound 688 to the IRC context specified by <irc context id>. 689 If no window matches the specified IRC context, an invalid 690 window ID is returned (0).[br] 691 If no <irc context id> is specified, this function looks for 692 the console in the current IRC context (if any).[br] 693 @seealso: 694 [fnc]$window[/fnc], 695 [fnc]$channel[/fnc], 696 [fnc]$query[/fnc], 697 [doc:window_naming_conventions]Window naming conventions[/doc] 698 */ 699 KVSCF(console)700 KVSCF(console) 701 { 702 kvs_uint_t uContextId; 703 KVSCF_PARAMETERS_BEGIN 704 KVSCF_PARAMETER("context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uContextId) 705 KVSCF_PARAMETERS_END 706 707 KviConsoleWindow * cons; 708 if(KVSCF_pParams->count() > 0) 709 { 710 cons = g_pApp->findConsole(uContextId); 711 } 712 else 713 { 714 cons = KVSCF_pContext->window()->console(); 715 if(!cons) 716 KVSCF_pContext->warning(__tr2qs_ctx("This window is not associated to an IRC context", "kvs")); 717 } 718 719 KVSCF_pRetBuffer->setInteger((kvs_int_t)(cons ? cons->numericId() : 0)); 720 return true; 721 } 722 723 /* 724 @doc: context 725 @type: 726 function 727 @title: 728 $context 729 @short: 730 Retrieves the ID of the specified IRC context 731 @syntax: 732 $context[(<server>,<nickname>)] 733 @description: 734 Returns the [b]IRC context ID[/b] of the IRC context that uses 735 the specified <server> and local user's <nickname>.[br] This function can 736 find only connected IRC contexts. 737 If no context matches the server and nickname, an invalid 738 [b]IRC context ID 0[/b] is returned.[br] 739 If <server> is an empty string, the first context that matches 740 the specified nickname is returned. If <nickname> is an empty string, 741 the first context that uses the specified server is returned. 742 If both parameters are missing this function returns the 743 id of the current IRC context, or [b]0[/b] if the 744 window that this call is executed in is not bound to any IRC context. 745 Please note that in this last case you may find an [b]IRC context[/b] 746 that is [i]not connected[/i]. 747 This can only happen if the current window is a console that is 748 in the [i]idle[/i] state, with no connection established yet.[br] 749 It is a good idea to take a look at the 750 [doc:window_naming_conventions]window naming conventions[/doc]. 751 @seealso: 752 [fnc]$window.context[/fnc] 753 */ 754 KVSCF(context)755 KVSCF(context) 756 { 757 QString szServer, szNick; 758 KVSCF_PARAMETERS_BEGIN 759 KVSCF_PARAMETER("server", KVS_PT_STRING, KVS_PF_OPTIONAL, szServer) 760 KVSCF_PARAMETER("nick", KVS_PT_STRING, KVS_PF_OPTIONAL, szNick) 761 KVSCF_PARAMETERS_END 762 763 KviConsoleWindow * cons; 764 if(!szServer.isEmpty() || !szNick.isEmpty()) 765 cons = g_pApp->findConsole(szServer, szNick); 766 else 767 cons = KVSCF_pContext->window()->console(); 768 769 KVSCF_pRetBuffer->setInteger(cons ? cons->context()->id() : 0); 770 return true; 771 } 772 773 /* 774 @doc: countStatusBarItems 775 @type: 776 function 777 @title: 778 $countStatusBarItems 779 @short: 780 Returns the number of items in the statusbar 781 @syntax: 782 <int> $countStatusBarItems 783 @description: 784 Returns the number of items in the statusbar. 785 @seealso: 786 [class]widget[/class] 787 */ 788 KVSCF(countStatusBarItems)789 KVSCF(countStatusBarItems) 790 { 791 if(g_pMainWindow->mainStatusBar()) 792 { 793 QList<QWidget *> widgets = g_pMainWindow->mainStatusBar()->findChildren<QWidget *>(); 794 KVSCF_pRetBuffer->setInteger(widgets.count()); 795 } 796 else 797 KVSCF_pRetBuffer->setInteger(0); 798 return true; 799 } 800 801 /* 802 @doc: cr 803 @type: 804 function 805 @title: 806 $cr 807 @short: 808 Returns a carriage return character 809 @syntax: 810 <string> $cr 811 @description: 812 Returns a carriage return character 813 @seealso: 814 [fnc]$lf[/fnc], [fnc]$ascii[/fnc], [fnc]$char[/fnc] 815 */ 816 KVSCF(cr)817 KVSCF(cr) 818 { 819 KVSCF_pRetBuffer->setString(QString(QChar('\r'))); 820 return true; 821 } 822 823 // The formatting of the table contents has to remain this way (i.e. long lines), 824 // else it adds long spaces between words in same sentence which looks awful on screen + harder to read. 825 /* 826 @doc: date 827 @type: 828 function 829 @title: 830 $date 831 @short: 832 Returns a date/time string using a specified format 833 @syntax: 834 <string> $date(<format:string>[,<unixtime:integer>]) 835 @description: 836 Returns the string representation of <unixtime> or 837 of the current time if <unixtime> is not given, based on <format>.[br] 838 The <format string> should contain a set of characters 839 that will be transformed according to the following rules: 840 [table] 841 [tr][td][b]a[/b][/td][td]The abbreviated weekday name according to the current locale.[/td][/tr] 842 [tr][td][b]A[/b][/td][td]The full weekday name according to the current locale.[/td][/tr] 843 [tr][td][b]b[/b][/td][td]The abbreviated month name according to the current locale.[/td][/tr] 844 [tr][td][b]B[/b][/td][td]The full month name according to the current locale.[/td][/tr] 845 [tr][td][b]c[/b][/td][td]The preferred date and time representation for the current locale.[/td][/tr] 846 [tr][td][b]C[/b][/td][td]The century number (year/100) as a 2-digit integer. (SU)[/td][/tr] 847 [tr][td][b]d[/b][/td][td]The day of the month as a decimal number (range 01 to 31).[/td][/tr] 848 [tr][td][b]D[/b][/td][td]Equivalent to m/d/y.[/td][/tr] 849 [tr][td][b]e[/b][/td][td]Like d, the day of the month as a decimal number, but a leading zero is replaced by a space. (SU)[/td][/tr] 850 [tr][td][b]F[/b][/td][td]Equivalent to Y-m-d (the ISO 8601 date format). (C99)[/td][/tr] 851 [tr][td][b]h[/b][/td][td]Equivalent to b.[/td][/tr] 852 [tr][td][b]H[/b][/td][td]The hour as a decimal number using a 24-hour clock (range 00 to 23).[/td][/tr] 853 [tr][td][b]I[/b][/td][td]The hour as a decimal number using a 12-hour clock (range 01 to 12).[/td][/tr] 854 [tr][td][b]j[/b][/td][td]The day of the year as a decimal number (range 001 to 366).[/td][/tr] 855 [tr][td][b]k[/b][/td][td]The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. See also [b]H[/b].[/td][/tr] 856 [tr][td][b]l[/b][/td][td]The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. See also [b]I[/b].[/td][/tr] 857 [tr][td][b]m[/b][/td][td]The month as a decimal number (range 01 to 12).[/td][/tr] 858 [tr][td][b]M[/b][/td][td]The minute as a decimal number (range 00 to 59).[/td][/tr] 859 [tr][td][b]n[/b][/td][td]A newline character. (SU)[/td][/tr] 860 [tr][td][b]p[/b][/td][td]Either [b]AM[/b] or [b]PM[/b] according to the given time value, or the corresponding strings for the current locale. Noon is treated as [b]pm[/b] and midnight as [b]am[/b].[/td][/tr] 861 [tr][td][b]r[/b][/td][td]The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to [b]I:M:S p[/b].[/td][/tr] 862 [tr][td][b]s[/b][/td][td]The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC.[/td][/tr] 863 [tr][td][b]S[/b][/td][td]The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds.)[/td][/tr] 864 [tr][td][b]t[/b][/td][td]A tab character.[/td][/tr] 865 [tr][td][b]T[/b][/td][td]The time in 24-hour notation (H:M:S). (SU)[/td][/tr] 866 [tr][td][b]u[/b][/td][td]The day of the week as a decimal, range 1 to 7, Monday being 1. See also [b]w[/b].[/td][/tr] 867 [tr][td][b]V[/b][/td][td]The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also [b]U[/b] and [/b]W.[/td][/tr] 868 [tr][td][b]w[/b][/td][td]The day of the week as a decimal, range 0 to 6, Sunday being 0.[/td][/tr] 869 [tr][td][b]W[/b][/td][td]The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.[/td][/tr] 870 [tr][td][b]y[/b][/td][td]The year as a decimal number without a century (range 00 to 99).[/td][/tr] 871 [tr][td][b]Y[/b][/td][td]The year as a decimal number including the century.[/td][/tr] 872 [tr][td][b]z[/b][/td][td]The time-zone as hour offset from GMT. Required to emit RFC822-compliant dates (using [i]a, d b Y H:M:S z[/i]).[/td][/tr] 873 [tr][td][b]Z[/b][/td][td]The time zone or name or abbreviation (not implemented yet).[/td][/tr] 874 [/table] 875 @examples: 876 [example] 877 [cmd]echo[/cmd] $date("d/m/Y H:M:S") 878 [/example] 879 @seealso: 880 [fnc]$unixtime[/fnc], [fnc]$hptimestamp[/fnc] 881 @author: 882 Kai Wasserbäch <debian@carbon-project.org> 883 */ 884 KVSCF(date)885 KVSCF(date) 886 { 887 QString szFormat; 888 kvs_int_t iTime; 889 KVSCF_PARAMETERS_BEGIN 890 KVSCF_PARAMETER("format", KVS_PT_NONEMPTYSTRING, 0, szFormat) 891 KVSCF_PARAMETER("unixtime", KVS_PT_INT, KVS_PF_OPTIONAL, iTime) 892 KVSCF_PARAMETERS_END 893 894 // strftime() is not sufficient, as shown by #769, the following is 895 // derived from KDateTime (not a copy, but the structure is similar, 896 // which isn't surprising), but limited/extended to the required set of 897 // functionality. 898 // The reason for this is platform independent. 899 QDateTime qDt; 900 QString szFmtTime; 901 int iLength, iVal, iTemp, iLocalTzH, iLocalTzM, iUtcH, iUtcM, iTzOffset; 902 int iTzOffsetH, iTzOffsetM; 903 QChar cDiv; 904 905 if(KVSCF_pParams->count() > 1) 906 qDt.setTime_t(iTime); 907 else 908 qDt = QDateTime::currentDateTime(); 909 910 if(!qDt.isValid()) 911 { 912 KVSCF_pContext->warning(__tr2qs_ctx("Couldn't construct QDateTime object.", "kvs")); 913 goto leavenow; 914 } 915 916 for(auto i : szFormat) 917 { 918 ushort ch = i.unicode(); 919 iLength = 2; 920 iVal = 0x8000000; 921 cDiv = ' '; 922 923 switch(ch) 924 { 925 // FIXME: G, g, U, x, X, Z not implemented yet. 926 // 927 // E and O probably never will be implemented. 928 case 'a': // the abbreviated localized day name (e.g. 929 // 'Mon' to 'Sun'). Uses 930 // QDate::shortDayName(). 931 szFmtTime += qDt.toString("ddd"); 932 break; 933 case 'A': // the long localized day name (e.g. 934 // 'Monday' to 'Qt::Sunday'). Uses 935 // QDate::longDayName(). 936 szFmtTime += qDt.toString("dddd"); 937 break; 938 case 'b': // the abbreviated localized month name 939 case 'h': // (e.g. 'Jan' to 'Dec'). Uses 940 // QDate::shortMonthName(). 941 szFmtTime += qDt.toString("MMM"); 942 break; 943 case 'B': // the long localized month name (e.g. 944 // 'January' to 'December'). Uses 945 // QDate::longMonthName(). 946 szFmtTime += qDt.toString("MMMM"); 947 break; 948 case 'c': // If the format is Qt::TextDate, the string 949 // is formatted in the default way. 950 szFmtTime += qDt.toString(Qt::TextDate); 951 break; 952 case 'C': // 2-digit "century" (yyyy/100) 953 iVal = qDt.date().year() / 100; 954 break; 955 case 'd': // the day as number with a leading zero 956 // (01 to 31) 957 cDiv = '0'; 958 // break omitted on purpose, NEXT MUST BE 'e' 959 case 'e': // 2-character wide day representation, 960 // space padded 961 iVal = qDt.date().day(); 962 break; 963 case 'D': // American formatting 964 szFmtTime += qDt.toString("MM/dd/yy"); 965 break; 966 case 'F': // yyyy-MM-dd (ISO 8601) 967 szFmtTime += qDt.date().toString(Qt::ISODate); 968 break; 969 case 'H': // the hour with a leading zero (00 to 23) 970 cDiv = '0'; 971 // no break; on purpose (NEXT MUST BE case 'k') 972 case 'k': // same as H, only that single-character values 973 // are prefixed with a space 974 iVal = qDt.time().hour(); 975 break; 976 case 'I': // hour, 01 - 12 977 cDiv = '0'; 978 // fall through to 'l' 979 case 'l': // hour, 1 - 12 980 iVal = (qDt.time().hour() + 11) % 12 + 1; 981 break; 982 case 'j': // day of the year (001 to 365/366) 983 iVal = qDt.date().dayOfYear(); 984 iLength = 3; 985 cDiv = '0'; 986 break; 987 case 'M': // minutes (00 to 59) 988 szFmtTime += qDt.toString("mm"); 989 break; 990 case 'm': // month (01-12) 991 szFmtTime += qDt.toString("MM"); 992 break; 993 case 'n': // newline 994 szFmtTime += "\n"; 995 break; 996 case 'p': // AM/PM 997 // FIXME: l10n for the am/pm? Maybe we can 998 // draw it from a different source? 999 // Qt doesn't offer l10n strings 1000 // for this. 1001 if(qDt.time().hour() < 12) 1002 szFmtTime += "AM"; 1003 else 1004 szFmtTime += "PM"; 1005 break; 1006 case 'P': // am/pm, the same FIXME as for p applies. 1007 if(qDt.time().hour() < 12) 1008 szFmtTime += "am"; 1009 else 1010 szFmtTime += "pm"; 1011 break; 1012 case 'r': // "I:M:S p" 1013 szFmtTime += qDt.toString("hh:mm:ss AP"); 1014 break; 1015 case 'R': // "H:M" 1016 szFmtTime += qDt.toString("hh:mm"); 1017 break; 1018 case 's': // seconds since epoch (currently 1970-01-01 00:00:00 UTC) 1019 szFmtTime += QString::number(qDt.toTime_t()); 1020 break; 1021 case 'S': // seconds (00-60) 1022 cDiv = '0'; 1023 iVal = qDt.time().second(); 1024 break; 1025 case 't': // Tab 1026 szFmtTime += "\t"; 1027 break; 1028 case 'T': // H:M:S 1029 szFmtTime += qDt.toString("hh:mm:ss"); 1030 break; 1031 case 'u': // day of the week (1-7) 1032 iVal = qDt.date().dayOfWeek(); 1033 iLength = 1; 1034 break; 1035 case 'V': // week of the year (ISO 8601) 1036 case 'W': // W is not entirely correct, but that's a 1037 // lot easier this way. 1038 iVal = qDt.date().weekNumber(); 1039 cDiv = '0'; 1040 break; 1041 case 'w': // day of week (0-6, 0==Sunday) 1042 iTemp = qDt.date().dayOfWeek(); 1043 if(iTemp == Qt::Sunday) 1044 iTemp = 0; 1045 szFmtTime += QString::number(iTemp); 1046 break; 1047 case 'y': // year (2-character) 1048 szFmtTime += qDt.toString("yy"); 1049 break; 1050 case 'Y': // year (4-character) 1051 szFmtTime += qDt.toString("yyyy"); 1052 break; 1053 case 'z': // numerical timezone offset 1054 iLocalTzH = qDt.time().hour(); 1055 iLocalTzM = qDt.time().minute(); 1056 iUtcH = qDt.toUTC().time().hour(); 1057 iUtcM = qDt.toUTC().time().minute(); 1058 iTzOffsetH = iLocalTzH - iUtcH; 1059 iTzOffsetM = iLocalTzM - iUtcM; 1060 iTzOffset = iTzOffsetH * 100 + iTzOffsetM; 1061 1062 //clamp to -12:00 / +12:00 (ticket #924) 1063 if(iTzOffset < 1200) 1064 iTzOffset += 2400; 1065 1066 if(iTzOffset > 1200) 1067 iTzOffset -= 2400; 1068 1069 if(iTzOffset > 0) 1070 szFmtTime += "+"; 1071 1072 szFmtTime += QString("%1").arg(iTzOffset, 4, 10, QChar('0')); 1073 break; 1074 // FIXME 1075 // The abbrev. time zone name is a little bit trickier, 1076 // as I don't want to reimplement KTimeZone, but can't 1077 // use KTimeZone. I'll need to do some research on this. 1078 //case 'Z': // TZ abbrev. 1079 // szFmtTime += 1080 default: 1081 szFmtTime += i; 1082 } 1083 1084 if(iVal != 0x8000000) 1085 { 1086 if(iVal < 0) 1087 { 1088 iVal = -iVal; 1089 szFmtTime += "-"; 1090 } 1091 szFmtTime += QString("%1").arg(iVal, iLength, 10, cDiv); 1092 } 1093 } 1094 1095 KVSCF_pRetBuffer->setString(szFmtTime); 1096 1097 leavenow: 1098 return true; 1099 } 1100 1101 /* 1102 @doc: escape 1103 @type: 1104 function 1105 @title: 1106 $escape 1107 @short: 1108 Returns a kvs-escaped version of the string 1109 @syntax: 1110 <string> $escape(<text:string>) 1111 @description: 1112 In KVS some characters in a string have special meanings: [b]%[/b] marks the start of a variable name, [b]$[/b] the start of a function name, etc..[br] 1113 Sometimes you could need to escape them using a [b]\[/b] character to avoid KVIrc from interpreting the special meaning of these characters: 1114 this function will to the dirty job for you, returning a correctly kvs-escaped version of the string passed as a parameter.[br] 1115 @seealso: 1116 [cmd]eval[/cmd] 1117 */ 1118 KVSCF(escape)1119 KVSCF(escape) 1120 { 1121 QString szData; 1122 KVSCF_PARAMETERS_BEGIN 1123 KVSCF_PARAMETER("text", KVS_PT_STRING, KVS_PF_OPTIONAL, szData) 1124 KVSCF_PARAMETERS_END 1125 1126 if(!szData.isEmpty()) 1127 KviQString::escapeKvs(&szData); 1128 1129 KVSCF_pRetBuffer->setString(szData); 1130 return true; 1131 } 1132 1133 /* 1134 @doc: false 1135 @type: 1136 function 1137 @title: 1138 $false 1139 @short: 1140 The boolean false constant 1141 @syntax: 1142 <boolean> $false 1143 @description: 1144 Evaluates to the false boolean constant. False 1145 is equivalent to the integer [b]0[/b] too. This function/constant 1146 is useful to keep your code readable: when you 1147 have a variable that can assume boolean values it's 1148 nicer to use [fnc]$true[/fnc] and $false instead of 1149 the integer constants [b]1[/b] and [b]0[/b]. The reader will 1150 understand immediately that the variable simply can't 1151 assume any other value. 1152 @examples: 1153 [example] 1154 %a = $false 1155 [cmd]echo[/cmd] $typeof(%a) 1156 [cmd]echo[/cmd] $(%a + 1) 1157 [/example] 1158 @seealso: 1159 [fnc]$true[/fnc] 1160 */ 1161 KVSCF(falseCKEYWORDWORKAROUND)1162 KVSCF(falseCKEYWORDWORKAROUND) 1163 { 1164 KVSCF_pRetBuffer->setBoolean(false); 1165 return true; 1166 } 1167 1168 /* 1169 @doc: features 1170 @type: 1171 function 1172 @title: 1173 $features 1174 @short: 1175 Returns the features that KVIrc supports 1176 @syntax: 1177 <array> $features() 1178 <boolean> $features(<test_feature:string>) 1179 @description: 1180 The parameterless form returns an array of feature description strings that this KVIrc executable supports.[br] 1181 This function is useful when some part of your script depends on 1182 an optional KVIrc feature (like SSL support or IPv6 support).[br] 1183 The returned value may be assigned to a dictionary too: it will be used to simulate an array.[br] 1184 The form with the [test_feature] parameter returns true if and only if [test_feature] is available.[br] 1185 @examples: 1186 [example] 1187 %myfeats[] = $features 1188 [cmd]echo[/cmd] %myfeats[] 1189 %i = %myfeats[]# 1190 [cmd]while[/cmd](%i > 0) 1191 { 1192 %i--; 1193 [cmd]echo[/cmd] "Supporting feature %myfeats[%i]" 1194 } 1195 [/example] 1196 Nearly the same loop, just really shorter: 1197 [example] 1198 [cmd]foreach[/cmd](%f,$features) 1199 [cmd]echo[/cmd] "Supporting feature %myfeats[%i]" 1200 [/example] 1201 You can test for a specific feature in the following way: 1202 [example] 1203 [cmd]if[/cmd]($features("SSL"))[cmd]echo[/cmd] "Yes! SSL is available"; 1204 [/example] 1205 If used in [i]non-array[/i] context it returns just a comma separated list of entries: 1206 [example] 1207 [cmd]echo[/cmd] $features 1208 [/example] 1209 @seealso: 1210 [fnc]$version[/fnc] 1211 */ 1212 KVSCF(features)1213 KVSCF(features) 1214 { 1215 QString szFeature; 1216 KVSCF_PARAMETERS_BEGIN 1217 KVSCF_PARAMETER("test_feature", KVS_PT_STRING, KVS_PF_OPTIONAL, szFeature) 1218 KVSCF_PARAMETERS_END 1219 1220 if(!szFeature.isEmpty()) 1221 { 1222 KVSCF_pRetBuffer->setBoolean(feature_list.contains(szFeature, Qt::CaseInsensitive)); 1223 } 1224 else 1225 { 1226 KviKvsArray * a = new KviKvsArray(); 1227 for(auto&& f : feature_list) 1228 a->append(new KviKvsVariant(f)); 1229 KVSCF_pRetBuffer->setArray(a); 1230 } 1231 1232 return true; 1233 } 1234 1235 /* 1236 @doc: firstconnectedconsole 1237 @type: 1238 function 1239 @title: 1240 $firstConnectedConsole 1241 @short: 1242 Returns the window ID of the first connected console 1243 @syntax: 1244 <uint> $firstConnectedConsole() 1245 @description: 1246 Returns the window ID of the first connected console 1247 or 0 if no console is currently connected. 1248 */ 1249 KVSCF(firstConnectedConsole)1250 KVSCF(firstConnectedConsole) 1251 { 1252 KviConsoleWindow * c = g_pApp->topmostConnectedConsole(); 1253 KVSCF_pRetBuffer->setInteger(c ? c->numericId() : 0); 1254 return true; 1255 } 1256 1257 /* 1258 @doc: flatten 1259 @type: 1260 function 1261 @title: 1262 $flatten 1263 @short: 1264 Returns a flattened array of items 1265 @syntax: 1266 <array> $flatten(<data1:variant>[,<data2:variant>[,...]]) 1267 @description: 1268 Returns an array of items built from the passed arguments 1269 with the following rules: 1270 [ul] 1271 [li]If an argument is a scalar value then the argument itself is appended to the result.[/li] 1272 [li]If an argument is an array then each contained item is appended to the result.[/li] 1273 [li]If an argument is a hash then each contained value is appended to the result.[/li] 1274 [/ul] 1275 A simple example of usage is to merge N arrays into a new one. 1276 (Please note that for merging one array into another the 1277 [doc:arrayconcatenation]array concatenation operator[/doc] is more efficient). 1278 */ 1279 KVSCF(flatten)1280 KVSCF(flatten) 1281 { 1282 KviKvsArray * a = new KviKvsArray(); 1283 KVSCF_pRetBuffer->setArray(a); 1284 unsigned int uIdx = 0; 1285 for(KviKvsVariant * v = KVSCF_pParams->first(); v; v = KVSCF_pParams->next()) 1286 { 1287 switch(v->type()) 1288 { 1289 case KviKvsVariantData::Array: 1290 { 1291 KviKvsArray * z = v->array(); 1292 unsigned int uSize = z->size(); 1293 unsigned int uIdx2 = 0; 1294 while(uIdx2 < uSize) 1295 { 1296 KviKvsVariant * pInternal = z->at(uIdx2); 1297 if(pInternal) 1298 a->set(uIdx, new KviKvsVariant(*pInternal)); 1299 // else 1300 // don't set anything: just leave empty entry (nothing) 1301 uIdx++; 1302 uIdx2++; 1303 } 1304 } 1305 break; 1306 case KviKvsVariantData::Hash: 1307 { 1308 KviKvsHash * h = v->hash(); 1309 KviKvsHashIterator it(*(h->dict())); 1310 while(KviKvsVariant * pInternal = it.current()) 1311 { 1312 a->set(uIdx, new KviKvsVariant(*pInternal)); 1313 uIdx++; 1314 ++it; 1315 } 1316 } 1317 break; 1318 default: 1319 a->set(uIdx, new KviKvsVariant(*v)); 1320 uIdx++; 1321 break; 1322 } 1323 } 1324 return true; 1325 } 1326 1327 /* 1328 @doc: fmtlink 1329 @type: 1330 function 1331 @title: 1332 $fmtlink 1333 @short: 1334 Returns a formatted link buffer 1335 @syntax: 1336 <string> $fmtlink(<link_text:string>,<double_click_command:string>[,<tooltip_text:string>]) 1337 @description: 1338 Returns a link formatted for the [cmd]echo[/cmd] command.[br] 1339 If you pass the returned string to the echo command, the string will be displayed 1340 as a link and will be highlighted when the user moves the mouse over it.[br] 1341 If the user leaves the mouse for a few seconds over the link, the <tooltip_text> 1342 will be displayed in a small tooltip window. If <tooltip_text> is not given, 1343 then no tooltip will be shown.[br] 1344 The <double_click_command> will be executed when the user will double click on the link.[br] 1345 Please remember that if <double_click_command> contains identifiers 1346 that must be evaluated at double-click time, you MUST escape them in the $fmtlink() call 1347 to prevent the evaluation.[br] 1348 You might also take a look at [doc:escape_sequences]the escape sequences documentation[/doc] 1349 to learn more about how the links are implemented and how to create more powerful links (add 1350 right and middle button actions, use predefined KVIrc links etc...). Also take a look at [fnc]$link[/fnc] 1351 which has related functionality. 1352 @seealso: 1353 [cmd]echo[/cmd], [doc:escape_sequences]the escape sequences documentation[/doc], [fnc]$link[/fnc] 1354 */ 1355 KVSCF(fmtlink)1356 KVSCF(fmtlink) 1357 { 1358 QString szLinkText, szCmd, szToolTip; 1359 KVSCF_PARAMETERS_BEGIN 1360 KVSCF_PARAMETER("link_text", KVS_PT_NONEMPTYSTRING, 0, szLinkText) 1361 KVSCF_PARAMETER("double_click_command", KVS_PT_STRING, 0, szCmd) 1362 KVSCF_PARAMETER("tooltip_text", KVS_PT_STRING, KVS_PF_OPTIONAL, szToolTip) 1363 KVSCF_PARAMETERS_END 1364 1365 QString szPart = QString("[!dbl]%1").arg(szCmd); 1366 if(!szToolTip.isEmpty()) 1367 KviQString::appendFormatted(szPart, "[!txt]%Q", &szToolTip); 1368 QString szLink = QString("\r!%1\r%2\r").arg(szPart, szLinkText); 1369 1370 KVSCF_pRetBuffer->setString(szLink); 1371 return true; 1372 } 1373 }; 1374