1 //=============================================================================
2 //
3 // File : libkvidialog.cpp
4 // Creation date : Sat Sep 15 2001 01:13:25 by Szymon Stefanek
5 //
6 // This file is part of the KVIrc IRC client distribution
7 // Copyright (C) 2001-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 // This program is FREE software. You can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the HOPE that it will be USEFUL,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 // See the GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, write to the Free Software Foundation,
21 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24
25 #include "libkvidialog.h"
26
27 #include "KviLocale.h"
28 #include "KviModule.h"
29 #include "KviModuleManager.h"
30 #include "KviError.h"
31 #include "KviApplication.h"
32 #include "KviIconManager.h"
33 #include "KviConsoleWindow.h"
34 #include "KviIconManager.h"
35 #include "KviKvsScript.h"
36 #include "KviMessageBox.h"
37 #include "KviTalHBox.h"
38
39 #include <QMessageBox>
40 #include <QLayout>
41 #include <QLineEdit>
42 #include <QLabel>
43 #include <QPushButton>
44 #include <QDesktopWidget>
45 #include <QEvent>
46 #include <QCloseEvent>
47
48 #include <QTextEdit>
49
50 static KviPointerList<QWidget> * g_pDialogModuleDialogList;
51
KviKvsCallbackMessageBox(const QString & szCaption,const QString & szText,const QString & szIcon,const QString & szButton0,const QString & szButton1,const QString & szButton2,const QString & szCode,KviKvsVariantList * pMagicParams,KviWindow * pWindow,bool modal)52 KviKvsCallbackMessageBox::KviKvsCallbackMessageBox(
53 const QString & szCaption,
54 const QString & szText,
55 const QString & szIcon,
56 const QString & szButton0,
57 const QString & szButton1,
58 const QString & szButton2,
59 const QString & szCode,
60 KviKvsVariantList * pMagicParams,
61 KviWindow * pWindow, bool modal)
62 : QMessageBox(nullptr),
63 KviKvsCallbackObject("dialog.message", pWindow, szCode, pMagicParams, 0)
64 {
65 setObjectName("dialog_message");
66 setWindowTitle(szCaption);
67 setText(szText);
68 setIcon(QMessageBox::NoIcon);
69 setModal(modal);
70 QMessageBox::StandardButtons buttons;
71 bool btn = false;
72 if(!szButton0.isEmpty())
73 {
74 btn = true;
75 buttons = QMessageBox::Yes;
76 }
77 if(!szButton1.isEmpty())
78 {
79 btn = true;
80 buttons |= QMessageBox::No;
81 }
82 if(!szButton2.isEmpty())
83 {
84 btn = true;
85 buttons |= QMessageBox::Cancel;
86 }
87
88 if(!btn)
89 buttons = QMessageBox::Ok;
90 setStandardButtons(buttons);
91 setDefaultButton(QMessageBox::Yes);
92 if(szButton2.isEmpty())
93 setEscapeButton(QMessageBox::No);
94 else
95 setEscapeButton(QMessageBox::Cancel);
96
97 g_pDialogModuleDialogList->append(this);
98
99 QPixmap * pix = g_pIconManager->getImage(szIcon);
100
101 if(pix)
102 setIconPixmap(*pix);
103 else
104 {
105 if(KviQString::equalCI(szIcon, "information"))
106 setIcon(QMessageBox::Information);
107 else if(KviQString::equalCI(szIcon, "warning"))
108 setIcon(QMessageBox::Warning);
109 else if(KviQString::equalCI(szIcon, "critical"))
110 setIcon(QMessageBox::Critical);
111 }
112 if(!szButton0.isEmpty())
113 setButtonText(QMessageBox::Yes, szButton0);
114 if(!szButton1.isEmpty())
115 setButtonText(QMessageBox::No, szButton1);
116 if(!szButton2.isEmpty())
117 setButtonText(QMessageBox::Cancel, szButton2);
118 }
119
~KviKvsCallbackMessageBox()120 KviKvsCallbackMessageBox::~KviKvsCallbackMessageBox()
121 {
122 g_pDialogModuleDialogList->removeRef(this);
123 }
124
done(int code)125 void KviKvsCallbackMessageBox::done(int code)
126 {
127 QMessageBox::done(code);
128
129 kvs_int_t iVal = 0;
130
131 switch(code)
132 {
133 case QMessageBox::No:
134 iVal = 1;
135 break;
136 case QMessageBox::Cancel:
137 iVal = 2;
138 break;
139 case 0:
140 // user closed the dialog, fake an "escape button" press
141 if(standardButtons() & QMessageBox::Cancel)
142 iVal = 2;
143 else
144 iVal = 1;
145 break;
146 }
147
148 KviKvsVariantList params;
149 params.append(new KviKvsVariant(iVal));
150
151 execute(¶ms);
152 deleteLater();
153 }
154
155 /*
156 @doc: dialog.message
157 @type:
158 command
159 @title:
160 dialog.message
161 @short:
162 Shows a message box
163 @syntax:
164 dialog.message [-b] (<caption>,<message_text>,<icon>,<button0>[,<button1>[,<button2>[,<magic1>[,<magic2>[...]]]]])
165 {
166 <callback_command>
167 }
168 @description:
169 Shows a message dialog box with the specified <caption>, <message_text>, <icon> and
170 buttons.[br]
171 <caption> is a text string that will appear in the caption of the dialog box.[br]
172 <message_text> is a text string that will appear in the dialog box and can contain HTML formatting.[br]
173 <icon> is an [doc:image_id]image identifier[/doc] that defines an icon to be placed in the dialog box.
174 <icon> can be a relative or absolute path to an image file, a signed number (in that case it defines
175 an internal KVIrc image) or one of the special strings [i]critical[/i], [i]information[/i] and [i]warning[/i].[br]
176 <button0> is the text of the first button (on the left).[br]
177 <button1> is the text of the second button (if empty or specified, only one button will appear in the dialog).[br]
178 <button2> is the text of the third button (if empty or specified, only two buttons will appear in the dialog).[br]
179 The first button is the default button - it is activated when the user presses the
180 enter key. The third (or the second if only two buttons are present) is treated as the escape button
181 and is activated when the user presses the Esc key or closes the dialog with the window manager close button.[br]
182 If one of the button text strings starts with a [i]default=[/i] prefix then that button is assumed
183 to be the default button of the dialog.[br]
184 If one of the button text strings starts with a [i]escape=[/i] prefix then that button is assumed
185 to be the escape button of the dialog.[br]
186 <magic1>, <magic2>... are the magic parameters - evaluated at dialog.message call time and passed
187 to the <callback_command> as positional parameters.[br]
188 If the -b or -modal switch is specified the dialog will have blocking modal behaviour -
189 it will appear above its parent widget and block its input until the dialog is closed.[br]
190 Once the dialog is displayed, the user will click one of the buttons. At this point the dialog
191 is hidden and the <callback_command> is executed, passing the number of the button clicked
192 as $0 and the magic parameters as positional parameters $1, $2, $3.[br]
193 @examples:
194 [example]
195 [comment]# Just a warning dialog[/comment]
196 dialog.message("Warning","You're being <b>warned</b>",warning,"OK"){ echo The user clicked OK; }
197 [comment]# A question[/comment]
198 dialog.message("And now?","What do you want to do?",information,"Go home","Watch TV","Scream")
199 {
200 if($0 == 0)echo "The user wants to go home"
201 else if($0 == 1)echo "The user wants to watch TV"
202 else echo "The user wants to scream!"
203 }
204 [/example]
205 */
206
dialog_kvs_cmd_message(KviKvsModuleCallbackCommandCall * c)207 static bool dialog_kvs_cmd_message(KviKvsModuleCallbackCommandCall * c)
208 {
209 QString szCaption, szMessage, szIcon, szButton0, szButton1, szButton2;
210 KviKvsVariantList params;
211
212 KVSM_PARAMETERS_BEGIN(c)
213 KVSM_PARAMETER("caption", KVS_PT_STRING, 0, szCaption)
214 KVSM_PARAMETER("message", KVS_PT_STRING, 0, szMessage)
215 KVSM_PARAMETER("icon", KVS_PT_STRING, 0, szIcon)
216 KVSM_PARAMETER("button0", KVS_PT_STRING, KVS_PF_OPTIONAL, szButton0)
217 KVSM_PARAMETER("button1", KVS_PT_STRING, KVS_PF_OPTIONAL, szButton1)
218 KVSM_PARAMETER("button2", KVS_PT_STRING, KVS_PF_OPTIONAL, szButton2)
219 KVSM_PARAMETER("magic", KVS_PT_VARIANTLIST, KVS_PF_OPTIONAL, params)
220 KVSM_PARAMETERS_END(c)
221 bool modal;
222 if(c->hasSwitch('b', "modal"))
223 modal = true;
224 else
225 modal = false;
226 QString szCmd = c->callback()->code();
227
228 KviKvsCallbackMessageBox * box = new KviKvsCallbackMessageBox(
229 szCaption, szMessage, szIcon, szButton0, szButton1, szButton2, szCmd, ¶ms, c->window(), modal);
230 box->show();
231
232 return true;
233 }
234
KviKvsCallbackTextInput(const QString & szCaption,const QString & szLabel,const QString & szDefaultText,const QString & szIcon,bool bMultiLine,bool bPassword,const QString & szButton0,const QString & szButton1,const QString & szButton2,const QString & szCode,KviKvsVariantList * pMagicParams,KviWindow * pWindow,bool modal)235 KviKvsCallbackTextInput::KviKvsCallbackTextInput(
236 const QString & szCaption,
237 const QString & szLabel,
238 const QString & szDefaultText,
239 const QString & szIcon,
240 bool bMultiLine,
241 bool bPassword,
242 const QString & szButton0,
243 const QString & szButton1,
244 const QString & szButton2,
245 const QString & szCode,
246 KviKvsVariantList * pMagicParams,
247 KviWindow * pWindow, bool modal)
248 : QDialog(), KviKvsCallbackObject("dialog.textinput", pWindow, szCode, pMagicParams, 0)
249 {
250 setObjectName("dialog_textinput");
251 g_pDialogModuleDialogList->append(this);
252 setWindowIcon(*(g_pIconManager->getSmallIcon(KviIconManager::KVIrc)));
253 setModal(modal);
254 setWindowTitle(szCaption);
255
256 QGridLayout * g = new QGridLayout(this);
257
258 QPixmap * pix = g_pIconManager->getImage(szIcon);
259
260 if(pix)
261 {
262 QLabel * il = new QLabel(this);
263 il->setPixmap(*pix);
264 il->setAlignment(Qt::AlignCenter);
265 g->addWidget(il, 0, 0);
266 QLabel * tl = new QLabel(szLabel, this);
267 g->addWidget(tl, 0, 1);
268 }
269 else
270 {
271 QLabel * tl = new QLabel(szLabel, this);
272 g->addWidget(tl, 0, 0, 1, 2);
273 }
274
275 g->setColumnStretch(1, 1);
276
277 m_bMultiLine = bMultiLine;
278 m_bPassword = bPassword;
279
280 if(m_bMultiLine)
281 {
282 m_pEdit = new QTextEdit(this);
283 ((QTextEdit *)m_pEdit)->setPlainText(szDefaultText);
284 ((QTextEdit *)m_pEdit)->selectAll();
285 }
286 else
287 {
288 m_pEdit = new QLineEdit(this);
289 if(m_bPassword)
290 ((QLineEdit *)m_pEdit)->setEchoMode(QLineEdit::Password);
291 ((QLineEdit *)m_pEdit)->setText(szDefaultText);
292 ((QLineEdit *)m_pEdit)->selectAll();
293 }
294
295 g->addWidget(m_pEdit, 1, 1, 1, 1);
296
297 KviTalHBox * box = new KviTalHBox(this);
298 g->addWidget(box, 2, 1, 1, 2);
299
300 m_iEscapeButton = -1;
301 m_iDefaultButton = 0;
302
303 if(!szButton0.isEmpty())
304 {
305 QString szB = szButton0;
306 bool bDef = false;
307 if(KviQString::equalCIN(szB, "default=", 8))
308 {
309 bDef = true;
310 szB.remove(0, 8);
311 m_iDefaultButton = 0;
312 }
313 else if(KviQString::equalCIN(szB, "escape=", 7))
314 {
315 szB.remove(0, 7);
316 m_iEscapeButton = 0;
317 }
318 QPushButton * pb1 = new QPushButton(szB, box);
319 if(bDef)
320 pb1->setDefault(true);
321 connect(pb1, SIGNAL(clicked()), this, SLOT(b0Clicked()));
322 }
323
324 if(!szButton1.isEmpty())
325 {
326 QString szB = szButton1;
327 bool bDef = false;
328 if(KviQString::equalCIN(szB, "default=", 8))
329 {
330 bDef = true;
331 szB.remove(0, 8);
332 m_iDefaultButton = 1;
333 }
334 else if(KviQString::equalCIN(szB, "escape=", 7))
335 {
336 szB.remove(0, 7);
337 m_iEscapeButton = 1;
338 }
339 QPushButton * pb2 = new QPushButton(szB, box);
340 if(bDef)
341 pb2->setDefault(true);
342 connect(pb2, SIGNAL(clicked()), this, SLOT(b1Clicked()));
343 }
344
345 if(!szButton2.isEmpty())
346 {
347 QString szB = szButton2;
348 bool bDef = false;
349 if(KviQString::equalCIN(szB, "default=", 8))
350 {
351 bDef = true;
352 szB.remove(0, 8);
353 m_iDefaultButton = 2;
354 }
355 else if(KviQString::equalCIN(szB, "escape=", 7))
356 {
357 szB.remove(0, 7);
358 m_iEscapeButton = 2;
359 }
360 QPushButton * pb3 = new QPushButton(szB, box);
361 if(bDef)
362 pb3->setDefault(true);
363 connect(pb3, SIGNAL(clicked()), this, SLOT(b2Clicked()));
364 }
365
366 if(m_iEscapeButton < 0)
367 {
368 //no escape button explicitly set, search for one:
369 if(!szButton2.isEmpty())
370 m_iEscapeButton = 2;
371 else if(!szButton1.isEmpty())
372 m_iEscapeButton = 1;
373 else
374 m_iEscapeButton = 0;
375 }
376 }
377
~KviKvsCallbackTextInput()378 KviKvsCallbackTextInput::~KviKvsCallbackTextInput()
379 {
380 g_pDialogModuleDialogList->removeRef(this);
381 }
382
b0Clicked()383 void KviKvsCallbackTextInput::b0Clicked()
384 {
385 done(0 + 10);
386 }
387
b1Clicked()388 void KviKvsCallbackTextInput::b1Clicked()
389 {
390 done(1 + 10);
391 }
392
b2Clicked()393 void KviKvsCallbackTextInput::b2Clicked()
394 {
395 done(2 + 10);
396 }
397
closeEvent(QCloseEvent * e)398 void KviKvsCallbackTextInput::closeEvent(QCloseEvent * e)
399 {
400 e->ignore();
401 done(m_iEscapeButton + 10);
402 }
403
done(int code)404 void KviKvsCallbackTextInput::done(int code)
405 {
406 if(code >= 10)
407 {
408 code -= 10;
409 }
410 else
411 {
412 switch(code)
413 {
414 case QDialog::Accepted:
415 code = m_iDefaultButton;
416 break;
417 default:
418 code = m_iEscapeButton;
419 break;
420 }
421 }
422
423 QString txt;
424
425 if(m_bMultiLine)
426 {
427 txt = ((QTextEdit *)m_pEdit)->toPlainText();
428 }
429 else
430 {
431 txt = ((QLineEdit *)m_pEdit)->text();
432 }
433
434 KviKvsVariantList params;
435 params.append(new KviKvsVariant((kvs_int_t)code));
436 params.append(new KviKvsVariant(txt));
437
438 execute(¶ms);
439
440 //QDialog::done(code);
441
442 deleteLater();
443 }
444
showEvent(QShowEvent * e)445 void KviKvsCallbackTextInput::showEvent(QShowEvent * e)
446 {
447 QRect rect = g_pApp->desktop()->screenGeometry(g_pApp->desktop()->primaryScreen());
448 move((rect.width() - width()) / 2, (rect.height() - height()) / 2);
449
450 QDialog::showEvent(e);
451 }
452
453 /*
454 @doc: dialog.textinput
455 @type:
456 command
457 @title:
458 dialog.textinput
459 @short:
460 Shows a dialog that accepts user input as text
461 @syntax:
462 dialog.textinput [-d=<default text>] [-i=<icon>] [-m] [-b] [-p] (<caption>,<info_text>,<button0>[,<button1>[,<button2>[,<magic1>[,<magic2>[...]]]]])
463 {
464 <callback_command>
465 }
466 @switches:
467 !sw: -d=<default_text> | --default=<default_text>
468 Set the initial text input value to <default_text>
469 !sw: -i=<icon> | --icon=<icon>
470 Display the specified icon, to the left of the informational text
471 !sw: -m | --multiline
472 Input multi-line text instead of single line
473 !sw: -p | --password
474 Display asterisks instead of the characters actually entered
475 @description:
476 Shows a text input dialog box with the specified <caption>, <info_text>, <icon> and
477 buttons.[br]
478 <caption> is a text string that will appear in the caption of the dialog box.[br]
479 <info_text> is a fixed text string that will appear in the dialog box and can contain HTML formatting.[br]
480 <button0> is the text of the first button (on the left).[br]
481 <button1> is the text of the second button (if empty or not given at all, only one button will appear in the dialog).[br]
482 <button2> is the text of the third button (if empty or not given, only two buttons will appear in the dialog).[br]
483 The first button is the default button - it is activated when the user presses the
484 enter key. The third (or the second if only two buttons are present) is treated as the escape button
485 and is activated when the user presses the Esc key or closes the dialog with the window manager close button.[br]
486 If one of the button text strings starts with a [i]default=[/i] prefix then that button is assumed
487 to be the default button of the dialog.[br]
488 If one of the button text strings starts with a [i]escape=[/i] prefix then that button is assumed
489 to be the escape button of the dialog.[br]
490 If the -m switch is used, the dialog will be a multi-line text input, otherwise the user will be able to
491 input only a single line of text.[br]
492 If the -p switch is used, the text will be show as asterisks, useful for sensitive data (passwords).[br]
493 If the -d switch is used, the initial text input value is set to <default text>.[br]
494 If the -i switch is used, the dialog displays also the icon <icon> to the left of <info_text>.
495 <icon> is an image identifier (a relative or absolute path to an image file, or a signed number that maps to an internal KVIrc image). [br]
496 If the -b or -modal switch is specified the dialog will have blocking modal behaviour:
497 it will appear above its parent widget and block its input until it's closed.[br]
498 In that case <icon> is an [doc:image_id]image identifier[/doc] (can be a relative or absolute
499 path to an image file or a signed number (in that case it defines an internal KVIrc image).[br]
500 <magic1>,<magic2>... are the magic parameters: evaluated at dialog.textinput call time and passed
501 to the <callback_command> as positional parameters.[br]
502 Once the dialog has been shown, the user will click one of the buttons. At this point the dialog
503 is hidden and the <callback_command> is executed passing the text input value in $1, the number of the button clicked
504 as $0, and the magic parameters as positional parameters $2, $3, $4....[br]
505 @examples:
506 [example]
507 [comment]# We need a single line reason[/comment]
508 dialog.textinput -d="Working!" (Away,Please enter the away message,"OK","Cancel")
509 {
510 switch($0)
511 {
512 case(0):
513 away $1-
514 break;
515 default:
516 # Cancelled
517 break;
518 }
519 }
520 [/example]
521 */
522
dialog_kvs_cmd_textinput(KviKvsModuleCallbackCommandCall * c)523 static bool dialog_kvs_cmd_textinput(KviKvsModuleCallbackCommandCall * c)
524 {
525 QString szCaption, szInfoText, szIcon, szDefaultText, szButton0, szButton1, szButton2;
526 KviKvsVariantList params;
527
528 KVSM_PARAMETERS_BEGIN(c)
529 KVSM_PARAMETER("caption", KVS_PT_STRING, 0, szCaption)
530 KVSM_PARAMETER("info_text", KVS_PT_STRING, 0, szInfoText)
531 KVSM_PARAMETER("button0", KVS_PT_STRING, KVS_PF_OPTIONAL, szButton0)
532 KVSM_PARAMETER("button1", KVS_PT_STRING, KVS_PF_OPTIONAL, szButton1)
533 KVSM_PARAMETER("button2", KVS_PT_STRING, KVS_PF_OPTIONAL, szButton2)
534 KVSM_PARAMETER("magic", KVS_PT_VARIANTLIST, KVS_PF_OPTIONAL, params)
535 KVSM_PARAMETERS_END(c)
536
537 QString szCmd = c->callback()->code();
538
539 c->switches()->getAsStringIfExisting('i', "icon", szIcon);
540 c->switches()->getAsStringIfExisting('d', "default", szDefaultText);
541 bool modal;
542 if(c->hasSwitch('b', "modal"))
543 modal = true;
544 else
545 modal = false;
546 KviKvsCallbackTextInput * box = new KviKvsCallbackTextInput(
547 szCaption, szInfoText, szDefaultText, szIcon, c->switches()->find('m', "multiline"), c->switches()->find('p', "password"),
548 szButton0, szButton1, szButton2, szCmd, ¶ms, c->window(), modal);
549 box->show();
550
551 return true;
552 }
553
KviKvsCallbackFileDialog(const QString & szCaption,const QString & szInitialSelection,const QString & szFilter,const QString & szCode,KviKvsVariantList * pMagicParams,KviWindow * pWindow,bool modal)554 KviKvsCallbackFileDialog::KviKvsCallbackFileDialog(
555 const QString & szCaption,
556 const QString & szInitialSelection,
557 const QString & szFilter,
558 const QString & szCode,
559 KviKvsVariantList * pMagicParams,
560 KviWindow * pWindow, bool modal)
561 : KviFileDialog(
562 szInitialSelection,
563 szFilter,
564 nullptr, // parent
565 nullptr,
566 modal),
567 KviKvsCallbackObject("dialog.file", pWindow, szCode, pMagicParams, 0)
568 {
569 g_pDialogModuleDialogList->append(this);
570 setWindowTitle(szCaption);
571 setObjectName("dialog_file");
572 }
573
~KviKvsCallbackFileDialog()574 KviKvsCallbackFileDialog::~KviKvsCallbackFileDialog()
575 {
576 g_pDialogModuleDialogList->removeRef(this);
577 }
578
done(int code)579 void KviKvsCallbackFileDialog::done(int code)
580 {
581 KviFileDialog::done(code);
582 KviKvsVariantList params;
583
584 if(code == QDialog::Accepted)
585 {
586 #ifdef COMPILE_KDE4_SUPPORT
587 if(mode() == KFile::ExistingOnly)
588 #else
589 if(fileMode() == QFileDialog::ExistingFiles)
590 #endif
591 {
592 KviKvsArray * a = new KviKvsArray();
593 QStringList sl = selectedFiles();
594 int idx = 0;
595 for(auto & it : sl)
596 {
597 a->set(idx, new KviKvsVariant(it));
598 idx++;
599 }
600 params.append(new KviKvsVariant(a));
601 }
602 else
603 {
604 params.append(new KviKvsVariant(selectedFiles().at(0)));
605 }
606 }
607 else
608 {
609 params.append(new KviKvsVariant(QString("")));
610 }
611
612 hide(); // ensure we're hidden
613
614 execute(¶ms);
615 deleteLater();
616 }
617
618 /*
619 @doc: dialog.file
620 @type:
621 command
622 @title:
623 dialog.file
624 @short:
625 Shows a file dialog
626 @syntax:
627 dialog.file [-b] (<mode>,<caption>[,<initial_selection[,<file_filter>[,<magic1>[,<magic2>[...]]]]]])
628 {
629 <callback_command>
630 }
631 @description:
632 Shows an open file dialog box with the specified <caption>, <initial_selection>, and <file_filter>.[br]
633 <mode> can be [i]open[/i], [i]openm[/i], [i]save[/i] or [i]dir[/i]:[br]
634 [b]open[/b] causes the dialog to return an existing file[br]
635 [b]openm[/b] is similar to open but allows returning multiple files as a comma separated list[br]
636 [b]save[/b] causes the dialog to return any file name (no overwrite confirmation is built in the dialog!)[br]
637 [b]dir[/b] causes the dialog to return an existing directory name[br]
638 <mode> defaults to [i]open[/i].[br]
639 <caption> is a text string that will appear in the caption of the dialog box.[br]
640 <initial_selection> can be a directory or filename that will be initially selected in the dialog.[br]
641 Only files matching <file_filter> are selectable. If filter is an empty string, all files are selectable.[br]
642 In the filter string multiple filters can be specified separated by either two semicolons next to each
643 other or separated by newlines. To add two filters, one to show all C++ files and one to show all
644 header files, the filter string could look like [i]C++ Files (*.cpp *.cc *.C *.cxx *.c++);;Header Files (*.h *.hxx *.h++)[/i]
645 <magic1>, <magic2>... are the magic parameters: evaluated at dialog.message call time and passed
646 to the <callback_command> as positional parameters.[br]
647 If the -b or -modal switch is specified the dialog will have non-blocking modal behaviour:
648 it will appear above its parent widget and block its input until it's closed.[br]
649 Once the dialog has been shown, the user will select an EXISTING file and click either
650 Ok or Cancel. At this point the dialog is hidden and the <callback_command> is executed passing the selected file(s) as $0
651 and the magic parameters as positional parameters $1, $2, $3....[br]
652 If the user clicks [i]Cancel[/i] or does not select any file the positional parameter $0 will be empty.[br]
653 @examples:
654 [example]
655 dialog.file(open,Choose an audio file,/home/pragma/TheAudio.au,"Audio files (*.au *.wav *.snd)")
656 {
657 if("$0" != "")run play $0
658 }
659 [/example]
660 */
661
662 //#warning "Examples for these dialogs!"
663
dialog_kvs_cmd_file(KviKvsModuleCallbackCommandCall * c)664 static bool dialog_kvs_cmd_file(KviKvsModuleCallbackCommandCall * c)
665 {
666 QString szMode, szCaption, szInitialSelection, szFilter;
667 KviKvsVariantList params;
668
669 KVSM_PARAMETERS_BEGIN(c)
670 KVSM_PARAMETER("mode", KVS_PT_STRING, 0, szMode)
671 KVSM_PARAMETER("caption", KVS_PT_STRING, 0, szCaption)
672 KVSM_PARAMETER("initial_selection", KVS_PT_STRING, KVS_PF_OPTIONAL, szInitialSelection)
673 KVSM_PARAMETER("filter", KVS_PT_STRING, KVS_PF_OPTIONAL, szFilter)
674 KVSM_PARAMETER("magic", KVS_PT_VARIANTLIST, KVS_PF_OPTIONAL, params)
675 KVSM_PARAMETERS_END(c)
676
677 bool modal = c->hasSwitch('b', "modal");
678
679 QString szCmd = c->callback()->code();
680
681 KviKvsCallbackFileDialog * box = new KviKvsCallbackFileDialog(szCaption, szInitialSelection, szFilter, szCmd, ¶ms, c->window(), modal);
682
683 KviFileDialog::FileMode md = KviFileDialog::ExistingFile;
684
685 if(KviQString::equalCI(szMode, "open"))
686 md = KviFileDialog::ExistingFiles;
687 else if(KviQString::equalCI(szMode, "save"))
688 md = KviFileDialog::AnyFile;
689 else if(KviQString::equalCI(szMode, "dir"))
690 md = KviFileDialog::DirectoryOnly;
691
692 box->setFileMode(md);
693
694 box->show();
695
696 return true;
697 }
698
KviKvsCallbackImageDialog(const QString & szCaption,const QString & szInitialSelection,int iType,int iMaxSize,const QString & szCode,KviKvsVariantList * pMagicParams,KviWindow * pWindow,bool modal)699 KviKvsCallbackImageDialog::KviKvsCallbackImageDialog(
700 const QString & szCaption,
701 const QString & szInitialSelection,
702 int iType,
703 int iMaxSize,
704 const QString & szCode,
705 KviKvsVariantList * pMagicParams,
706 KviWindow * pWindow, bool modal)
707 : KviImageDialog(nullptr, szCaption, iType, 0, szInitialSelection, iMaxSize, modal), KviKvsCallbackObject("dialog.image", pWindow, szCode, pMagicParams, 0)
708 {
709 g_pDialogModuleDialogList->append(this);
710 setObjectName("dialog_image");
711 }
712
~KviKvsCallbackImageDialog()713 KviKvsCallbackImageDialog::~KviKvsCallbackImageDialog()
714 {
715 g_pDialogModuleDialogList->removeRef(this);
716 }
717
done(int code)718 void KviKvsCallbackImageDialog::done(int code)
719 {
720 KviImageDialog::done(code);
721 KviKvsVariantList params;
722
723 if(code == QDialog::Accepted)
724 {
725 params.append(new KviKvsVariant(selectedImage()));
726 }
727 else
728 {
729 params.append(new KviKvsVariant(QString("")));
730 }
731
732 hide(); // ensure we're hidden
733
734 execute(¶ms);
735 deleteLater();
736 }
737
738 /*
739 @doc: dialog.image
740 @type:
741 command
742 @title:
743 dialog.image
744 @short:
745 Shows a image dialog
746 @syntax:
747 dialog.image [-b] (<type>,<caption>,<initial_directory>,[<maxsize>,[,<magic1>[,<magic2>[...]]]]]])
748 {
749 <callback_command>
750 }
751 @description:
752 Shows a dialog that allows selecting an [doc:image_id]image_id[doc].
753 The <type> parameter must be a combination of the following flags:<br>
754 [b]s[/b] : allow selecting from the KVIrc builtin small icons<br>
755 [b]f[/b] : allow browsing the local directories<br>
756 [b]a[/b] : all of the above<br>
757 The default for <type> is 'a'.<br>
758 <caption> is the caption string for the dialog.<br>
759 <initial_directory> makes sense only if 'f' is specified (if <initial_directory> is empty
760 then the last path used by the image dialog will be used).<br>
761 <maxsize> is the maximum size of the images for that the preview will be generated:
762 this is 256000 bytes by default (if unspecified). Don't make it a lot bigger: it can take a lot to make
763 the thumbnails for bigger images (and it can eat a considerable amount of memory).<br>
764 <magic1>, <magic2>... are the magic parameters: evaluated at dialog.image call time and passed
765 to the <callback_command> as positional parameters.[br]
766 If the -b or -modal switch is specified the dialog will have non-blocking modal behaviour:
767 it will appear above its parent widget and block its input until it's closed.[br]
768 Once the dialog has been shown, the user will select an [b]existing[/b] file and click either
769 OK or Cancel. At this point the dialog is hidden and the <callback_command> is executed passing the selected file(s) as $0
770 and the magic parameters as positional parameters $1, $2, $3....[br]
771 If the user clicks [i]Cancel[/i] or does not select any image the positional parameter $0 will be empty.[br]
772 @examples:
773 [example]
774 dialog.image(f,Choose an image file,/home/pragma/,"256000")
775 {
776 if("$0" != "")run okular $0
777 }
778 [/example]
779 */
780
781 //#warning "Examples for these dialogs!"
782
dialog_kvs_cmd_image(KviKvsModuleCallbackCommandCall * c)783 static bool dialog_kvs_cmd_image(KviKvsModuleCallbackCommandCall * c)
784 {
785 QString szType, szCaption, szInitialSelection;
786 kvs_uint_t iMaxSize;
787 KviKvsVariantList params;
788
789 KVSM_PARAMETERS_BEGIN(c)
790 KVSM_PARAMETER("mode", KVS_PT_STRING, 0, szType)
791 KVSM_PARAMETER("caption", KVS_PT_STRING, 0, szCaption)
792 KVSM_PARAMETER("initial_directory", KVS_PT_STRING, 0, szInitialSelection)
793 KVSM_PARAMETER("maxsize", KVS_PT_UINT, KVS_PF_OPTIONAL, iMaxSize)
794 KVSM_PARAMETER("magic", KVS_PT_VARIANTLIST, KVS_PF_OPTIONAL, params)
795 KVSM_PARAMETERS_END(c)
796 bool modal;
797 if(c->hasSwitch('b', "modal"))
798 modal = true;
799 else
800 modal = false;
801 QString szCmd = c->callback()->code();
802
803 int iType = 0;
804
805 if(szType.contains('s'))
806 iType |= KID_TYPE_BUILTIN_IMAGES_SMALL;
807 if(szType.contains('f'))
808 iType |= KID_TYPE_FULL_PATH;
809 if(szType.isEmpty())
810 iType = KID_TYPE_ALL;
811
812 if(iMaxSize < 1)
813 iMaxSize = 256000;
814
815 KviKvsCallbackImageDialog * box = new KviKvsCallbackImageDialog(szCaption, szInitialSelection, iType, iMaxSize, szCmd, ¶ms, c->window(), modal);
816
817 box->show();
818
819 return true;
820 }
821
822 /*
823 @doc: dialog.yesno
824 @type:
825 function
826 @title:
827 $dialog.yesno
828 @short:
829 Shows a simple yes/no dialog
830 @syntax:
831 $dialog.yesno(<caption:string>,<szText:string>)
832 @description:
833 Shows a simple yes/no dialog. Returns 1 if user clicks [i]Yes[/i] and 0 if (s)he clicks [i]No[/i].
834 Please note that this dialog is [b]blocking[/b]: it blocks execution of the script
835 until the user has selected either YES or NO.
836 @examples:
837 @seealso:
838 */
839
dialog_kvs_fnc_yesno(KviKvsModuleFunctionCall * c)840 static bool dialog_kvs_fnc_yesno(KviKvsModuleFunctionCall * c)
841 {
842 QString szCaption;
843 QString szText;
844 KVSM_PARAMETERS_BEGIN(c)
845 KVSM_PARAMETER("caption", KVS_PT_STRING, 0, szCaption)
846 KVSM_PARAMETER("text", KVS_PT_STRING, 0, szText)
847 KVSM_PARAMETERS_END(c)
848
849 c->enterBlockingSection();
850 bool yes = KviMessageBox::yesNo(szCaption, szText); // this will happily crash on quit ?
851 if(!c->leaveBlockingSection())
852 return true; // just die
853 c->returnValue()->setBoolean(yes);
854 return true;
855 }
856
857 /*
858 @doc: noblockingdialogs
859 @type:
860 generic
861 @title:
862 Why are there no blocking dialogs in KVIrc?
863 @short:
864 Technical answer
865 @description:
866 Why are there no blocking dialogs in KVIrc?[br]
867 The answer is simple: because they're more confusing and tricky than it seems.[br]
868 Blocking the entire program control flow while showing a dialog is
869 rather a bad idea since we have to deal with external entities (servers and other users)
870 that are [b]not[/b] blocked. This means that the blocking dialogs must block only the
871 script control-flow but let the rest of the application running.
872 Such blocking dialogs actually seem to simplify scripting because
873 the programmer [i]feels[/i] that the control is always left in the script snippet that he is writing.
874 This is actually confusing: the control IS in the script snippet but while the dialog
875 is open the whole world can change: you can return from the dialog call and discover
876 that the server connection no longer exists and the application is about to quit.[br]
877 This may happen even with non-blocking dialogs,but in non-blocking mode you have
878 a way to handle this event. Consider the following snippet of code:
879 [example]
880 echo My name is $?
881 [/example]
882 Where $? stands for a blocking input dialog that asks the user for some text.[br]
883 When the input dialog returns the window that the echo was directed to no longer
884 exists and you have no way to stop the echo! (Well... I could add extra code
885 in the executable to handle all these situations but that would be really too expensive).[br]
886 With object scripting this is actually dangerous: you might use a blocking dialog
887 in an object signal handler and when returning discover that this object has been deleted!
888 (The example refers to a simple object, but think about a complex hierarchy of objects
889 where one random gets deleted...).[br]
890 This is why the dialogs in KVIrc are non-blocking :)[br]
891 That's REAL programming.
892 */
893
dialog_module_init(KviModule * m)894 static bool dialog_module_init(KviModule * m)
895 {
896 g_pDialogModuleDialogList = new KviPointerList<QWidget>;
897 g_pDialogModuleDialogList->setAutoDelete(false);
898
899 KVSM_REGISTER_CALLBACK_COMMAND(m, "message", dialog_kvs_cmd_message);
900 KVSM_REGISTER_CALLBACK_COMMAND(m, "textinput", dialog_kvs_cmd_textinput);
901 KVSM_REGISTER_CALLBACK_COMMAND(m, "file", dialog_kvs_cmd_file);
902 KVSM_REGISTER_CALLBACK_COMMAND(m, "image", dialog_kvs_cmd_image);
903 KVSM_REGISTER_FUNCTION(m, "yesno", dialog_kvs_fnc_yesno);
904
905 return true;
906 }
907
dialog_module_cleanup(KviModule *)908 static bool dialog_module_cleanup(KviModule *)
909 {
910 // Here we get a tragedy if g_iLocalEventLoops > 0!
911 while(g_pDialogModuleDialogList->first())
912 delete g_pDialogModuleDialogList->first();
913 delete g_pDialogModuleDialogList;
914 g_pDialogModuleDialogList = nullptr;
915 return true;
916 }
917
dialog_module_can_unload(KviModule *)918 static bool dialog_module_can_unload(KviModule *)
919 {
920 return g_pDialogModuleDialogList->isEmpty();
921 }
922
923 KVIRC_MODULE(
924 "KVIrc script dialogs",
925 "4.0.0",
926 "Szymon Stefanek <pragma at kvirc dot net>",
927 "Adds the /dialog.* commands functionality\n",
928 dialog_module_init,
929 dialog_module_can_unload,
930 0,
931 dialog_module_cleanup,
932 0)
933