summaryrefslogtreecommitdiffstats
path: root/konversation
diff options
context:
space:
mode:
Diffstat (limited to 'konversation')
-rw-r--r--konversation/Makefile.am19
-rw-r--r--konversation/NEWS5
-rw-r--r--konversation/configure.files3
-rw-r--r--konversation/configure.in.in48
-rw-r--r--konversation/images/Makefile.am1
-rw-r--r--konversation/images/icons/Makefile.am1
-rw-r--r--konversation/images/icons/README-kimproxy5
-rw-r--r--konversation/images/icons/cr16-action-kimproxyaway.pngbin0 -> 521 bytes
-rw-r--r--konversation/images/icons/cr16-action-kimproxyoffline.pngbin0 -> 540 bytes
-rw-r--r--konversation/images/icons/cr16-action-kimproxyonline.pngbin0 -> 460 bytes
-rw-r--r--konversation/images/icons/cr22-action-char.pngbin0 -> 342 bytes
-rw-r--r--konversation/images/icons/cr22-action-kimproxyaway.pngbin0 -> 685 bytes
-rw-r--r--konversation/images/icons/cr22-action-kimproxyoffline.pngbin0 -> 711 bytes
-rw-r--r--konversation/images/icons/cr22-action-kimproxyonline.pngbin0 -> 590 bytes
-rw-r--r--konversation/images/icons/cr22-action-konv_message.pngbin0 -> 1091 bytes
-rw-r--r--konversation/images/icons/cr32-action-kimproxyaway.pngbin0 -> 1007 bytes
-rw-r--r--konversation/images/icons/cr32-action-kimproxyoffline.pngbin0 -> 1030 bytes
-rw-r--r--konversation/images/icons/cr32-action-kimproxyonline.pngbin0 -> 816 bytes
-rw-r--r--konversation/images/icons/crsc-action-kimproxyaway.svgzbin0 -> 1272 bytes
-rw-r--r--konversation/images/icons/crsc-action-kimproxyoffline.svgzbin0 -> 1284 bytes
-rw-r--r--konversation/images/icons/crsc-action-kimproxyonline.svgzbin0 -> 1305 bytes
-rw-r--r--konversation/images/icons/crsc-action-konv_message.svgzbin0 -> 2946 bytes
-rw-r--r--konversation/images/icons/hi128-app-konversation.pngbin0 -> 11281 bytes
-rw-r--r--konversation/images/icons/hi16-app-konversation.pngbin0 -> 827 bytes
-rw-r--r--konversation/images/icons/hi22-app-konversation.pngbin0 -> 1198 bytes
-rw-r--r--konversation/images/icons/hi32-app-konversation.pngbin0 -> 1806 bytes
-rw-r--r--konversation/images/icons/hi48-app-konversation.pngbin0 -> 3358 bytes
-rw-r--r--konversation/images/icons/hi64-app-konversation.pngbin0 -> 4757 bytes
-rw-r--r--konversation/images/icons/hisc-app-konversation.svgzbin0 -> 3051 bytes
-rw-r--r--konversation/images/nickicons/Makefile.am1
-rw-r--r--konversation/images/nickicons/alternative/Makefile.am3
-rw-r--r--konversation/images/nickicons/alternative/index.desktop57
-rw-r--r--konversation/images/nickicons/alternative/irc_admin.pngbin0 -> 797 bytes
-rw-r--r--konversation/images/nickicons/alternative/irc_away.pngbin0 -> 225 bytes
-rw-r--r--konversation/images/nickicons/alternative/irc_halfop.pngbin0 -> 784 bytes
-rw-r--r--konversation/images/nickicons/alternative/irc_normal.pngbin0 -> 746 bytes
-rw-r--r--konversation/images/nickicons/alternative/irc_op.pngbin0 -> 892 bytes
-rw-r--r--konversation/images/nickicons/alternative/irc_owner.pngbin0 -> 824 bytes
-rw-r--r--konversation/images/nickicons/alternative/irc_voice.pngbin0 -> 811 bytes
-rw-r--r--konversation/images/nickicons/christmas/Makefile.am3
-rw-r--r--konversation/images/nickicons/christmas/index.desktop68
-rw-r--r--konversation/images/nickicons/christmas/irc_admin.pngbin0 -> 777 bytes
-rw-r--r--konversation/images/nickicons/christmas/irc_away.pngbin0 -> 770 bytes
-rw-r--r--konversation/images/nickicons/christmas/irc_halfop.pngbin0 -> 859 bytes
-rw-r--r--konversation/images/nickicons/christmas/irc_normal.pngbin0 -> 871 bytes
-rw-r--r--konversation/images/nickicons/christmas/irc_op.pngbin0 -> 938 bytes
-rw-r--r--konversation/images/nickicons/christmas/irc_owner.pngbin0 -> 868 bytes
-rw-r--r--konversation/images/nickicons/christmas/irc_voice.pngbin0 -> 697 bytes
-rw-r--r--konversation/images/nickicons/classic/Makefile.am3
-rw-r--r--konversation/images/nickicons/classic/index.desktop70
-rw-r--r--konversation/images/nickicons/classic/irc_admin.pngbin0 -> 250 bytes
-rw-r--r--konversation/images/nickicons/classic/irc_away.pngbin0 -> 111 bytes
-rw-r--r--konversation/images/nickicons/classic/irc_halfop.pngbin0 -> 275 bytes
-rw-r--r--konversation/images/nickicons/classic/irc_normal.pngbin0 -> 246 bytes
-rw-r--r--konversation/images/nickicons/classic/irc_op.pngbin0 -> 234 bytes
-rw-r--r--konversation/images/nickicons/classic/irc_owner.pngbin0 -> 242 bytes
-rw-r--r--konversation/images/nickicons/classic/irc_voice.pngbin0 -> 246 bytes
-rw-r--r--konversation/images/nickicons/default/Makefile.am3
-rw-r--r--konversation/images/nickicons/default/index.desktop69
-rw-r--r--konversation/images/nickicons/default/irc_admin.pngbin0 -> 225 bytes
-rw-r--r--konversation/images/nickicons/default/irc_away.pngbin0 -> 193 bytes
-rw-r--r--konversation/images/nickicons/default/irc_halfop.pngbin0 -> 236 bytes
-rw-r--r--konversation/images/nickicons/default/irc_normal.pngbin0 -> 227 bytes
-rw-r--r--konversation/images/nickicons/default/irc_op.pngbin0 -> 238 bytes
-rw-r--r--konversation/images/nickicons/default/irc_owner.pngbin0 -> 238 bytes
-rw-r--r--konversation/images/nickicons/default/irc_voice.pngbin0 -> 238 bytes
-rw-r--r--konversation/images/nickicons/oxygen/Makefile.am3
-rw-r--r--konversation/images/nickicons/oxygen/index.desktop22
-rw-r--r--konversation/images/nickicons/oxygen/irc_admin.pngbin0 -> 754 bytes
-rw-r--r--konversation/images/nickicons/oxygen/irc_away.pngbin0 -> 472 bytes
-rw-r--r--konversation/images/nickicons/oxygen/irc_halfop.pngbin0 -> 750 bytes
-rw-r--r--konversation/images/nickicons/oxygen/irc_normal.pngbin0 -> 743 bytes
-rw-r--r--konversation/images/nickicons/oxygen/irc_op.pngbin0 -> 752 bytes
-rw-r--r--konversation/images/nickicons/oxygen/irc_owner.pngbin0 -> 739 bytes
-rw-r--r--konversation/images/nickicons/oxygen/irc_voice.pngbin0 -> 743 bytes
-rw-r--r--konversation/images/nickicons/smiling/Makefile.am3
-rw-r--r--konversation/images/nickicons/smiling/index.desktop70
-rw-r--r--konversation/images/nickicons/smiling/irc_admin.pngbin0 -> 813 bytes
-rw-r--r--konversation/images/nickicons/smiling/irc_away.pngbin0 -> 442 bytes
-rw-r--r--konversation/images/nickicons/smiling/irc_halfop.pngbin0 -> 972 bytes
-rw-r--r--konversation/images/nickicons/smiling/irc_normal.pngbin0 -> 921 bytes
-rw-r--r--konversation/images/nickicons/smiling/irc_op.pngbin0 -> 919 bytes
-rw-r--r--konversation/images/nickicons/smiling/irc_owner.pngbin0 -> 898 bytes
-rw-r--r--konversation/images/nickicons/smiling/irc_voice.pngbin0 -> 923 bytes
-rw-r--r--konversation/images/nickicons/square/Makefile.am3
-rw-r--r--konversation/images/nickicons/square/index.desktop69
-rw-r--r--konversation/images/nickicons/square/irc_admin.pngbin0 -> 570 bytes
-rw-r--r--konversation/images/nickicons/square/irc_away.pngbin0 -> 578 bytes
-rw-r--r--konversation/images/nickicons/square/irc_halfop.pngbin0 -> 631 bytes
-rw-r--r--konversation/images/nickicons/square/irc_normal.pngbin0 -> 331 bytes
-rw-r--r--konversation/images/nickicons/square/irc_op.pngbin0 -> 546 bytes
-rw-r--r--konversation/images/nickicons/square/irc_owner.pngbin0 -> 611 bytes
-rw-r--r--konversation/images/nickicons/square/irc_voice.pngbin0 -> 559 bytes
-rw-r--r--konversation/scripts/Makefile.am4
-rw-r--r--konversation/scripts/README55
-rwxr-xr-xkonversation/scripts/bug18
-rwxr-xr-xkonversation/scripts/cmd31
-rwxr-xr-xkonversation/scripts/fortune53
-rw-r--r--konversation/scripts/fortunes.dat680
-rwxr-xr-xkonversation/scripts/gauge71
-rwxr-xr-xkonversation/scripts/kdeversion9
-rwxr-xr-xkonversation/scripts/mail70
-rwxr-xr-xkonversation/scripts/media484
-rwxr-xr-xkonversation/scripts/sayclip19
-rw-r--r--konversation/scripts/sysinfo86
-rwxr-xr-xkonversation/scripts/tinyurl31
-rwxr-xr-xkonversation/scripts/uptime56
-rwxr-xr-xkonversation/scripts/weather70
-rw-r--r--konversation/src/Makefile.am73
-rw-r--r--konversation/src/alias_preferences.cpp223
-rw-r--r--konversation/src/alias_preferences.h51
-rw-r--r--konversation/src/alias_preferencesui.ui174
-rw-r--r--konversation/src/autoreplace_preferences.cpp403
-rw-r--r--konversation/src/autoreplace_preferences.h57
-rw-r--r--konversation/src/autoreplace_preferencesui.ui292
-rw-r--r--konversation/src/awaymanager.cpp372
-rw-r--r--konversation/src/awaymanager.h73
-rw-r--r--konversation/src/blowfish/BlowfishCbc.cpp654
-rw-r--r--konversation/src/blowfish/BlowfishCbc.h228
-rw-r--r--konversation/src/blowfish/Makefile.am7
-rw-r--r--konversation/src/blowfish/README3
-rw-r--r--konversation/src/blowfish/b64stuff.cpp218
-rw-r--r--konversation/src/blowfish/b64stuff.h53
-rw-r--r--konversation/src/blowfish/blowfish.cpp129
-rw-r--r--konversation/src/blowfish/blowfish.h30
-rw-r--r--konversation/src/blowfish/mc_blowfish.cpp63
-rw-r--r--konversation/src/blowfish/mc_blowfish.h34
-rw-r--r--konversation/src/blowfish/newblowfish.cpp208
-rw-r--r--konversation/src/blowfish/newblowfish.h32
-rw-r--r--konversation/src/blowfish/oldblowfish.cpp425
-rw-r--r--konversation/src/blowfish/oldblowfish1.h141
-rw-r--r--konversation/src/blowfish/oldblowfish2.h295
-rw-r--r--konversation/src/channel.cpp2960
-rw-r--r--konversation/src/channel.h375
-rw-r--r--konversation/src/channeldialog.cpp82
-rw-r--r--konversation/src/channeldialog.h46
-rw-r--r--konversation/src/channellistpanel.cpp597
-rw-r--r--konversation/src/channellistpanel.h147
-rw-r--r--konversation/src/channellistviewitem.cpp40
-rw-r--r--konversation/src/channellistviewitem.h31
-rw-r--r--konversation/src/channelnick.cpp259
-rw-r--r--konversation/src/channelnick.h95
-rw-r--r--konversation/src/channeloptionsdialog.cpp537
-rw-r--r--konversation/src/channeloptionsdialog.h102
-rw-r--r--konversation/src/channeloptionsui.ui559
-rw-r--r--konversation/src/chatwindow.cpp526
-rw-r--r--konversation/src/chatwindow.h205
-rw-r--r--konversation/src/chatwindowappearance_preferences.ui378
-rw-r--r--konversation/src/chatwindowbehaviour_preferences.ui374
-rw-r--r--konversation/src/colorsappearance_preferences.ui1395
-rw-r--r--konversation/src/commit.h4
-rw-r--r--konversation/src/common.cpp196
-rw-r--r--konversation/src/common.h56
-rw-r--r--konversation/src/config/Makefile.am8
-rw-r--r--konversation/src/config/konversation.kcfg996
-rw-r--r--konversation/src/config/preferences.cpp605
-rw-r--r--konversation/src/config/preferences.h169
-rw-r--r--konversation/src/config/preferences_base.kcfgc8
-rw-r--r--konversation/src/connectionbehavior_preferences.ui187
-rw-r--r--konversation/src/connectionmanager.cpp580
-rw-r--r--konversation/src/connectionmanager.h95
-rw-r--r--konversation/src/connectionsettings.cpp55
-rw-r--r--konversation/src/connectionsettings.h61
-rw-r--r--konversation/src/dcc_preferences.cpp54
-rw-r--r--konversation/src/dcc_preferences.h38
-rw-r--r--konversation/src/dcc_preferencesui.ui527
-rw-r--r--konversation/src/dccchat.cpp473
-rw-r--r--konversation/src/dccchat.h102
-rw-r--r--konversation/src/dcccommon.cpp118
-rw-r--r--konversation/src/dcccommon.h48
-rw-r--r--konversation/src/dccrecipientdialog.cpp100
-rw-r--r--konversation/src/dccrecipientdialog.h47
-rw-r--r--konversation/src/dccresumedialog.cpp198
-rw-r--r--konversation/src/dccresumedialog.h59
-rw-r--r--konversation/src/dcctransfer.cpp358
-rw-r--r--konversation/src/dcctransfer.h186
-rw-r--r--konversation/src/dcctransferdetailedinfopanel.cpp214
-rw-r--r--konversation/src/dcctransferdetailedinfopanel.h44
-rw-r--r--konversation/src/dcctransferdetailedinfopanelui.ui492
-rw-r--r--konversation/src/dcctransfermanager.cpp238
-rw-r--r--konversation/src/dcctransfermanager.h95
-rw-r--r--konversation/src/dcctransferpanel.cpp459
-rw-r--r--konversation/src/dcctransferpanel.h113
-rw-r--r--konversation/src/dcctransferpanelitem.cpp403
-rw-r--r--konversation/src/dcctransferpanelitem.h98
-rw-r--r--konversation/src/dcctransferrecv.cpp799
-rw-r--r--konversation/src/dcctransferrecv.h184
-rw-r--r--konversation/src/dcctransfersend.cpp509
-rw-r--r--konversation/src/dcctransfersend.h98
-rw-r--r--konversation/src/decoder.h113
-rw-r--r--konversation/src/editnotifydialog.cpp103
-rw-r--r--konversation/src/editnotifydialog.h50
-rw-r--r--konversation/src/emoticon.cpp209
-rw-r--r--konversation/src/emoticon.h47
-rw-r--r--konversation/src/eventsrc973
-rw-r--r--konversation/src/fontappearance_preferences.ui160
-rw-r--r--konversation/src/generalbehavior_preferences.ui360
-rw-r--r--konversation/src/guess_ja.cpp377
-rw-r--r--konversation/src/guess_ja.h116
-rw-r--r--konversation/src/highlight.cpp49
-rw-r--r--konversation/src/highlight.h63
-rw-r--r--konversation/src/highlight_preferences.cpp352
-rw-r--r--konversation/src/highlight_preferences.h63
-rw-r--r--konversation/src/highlight_preferencesui.ui453
-rw-r--r--konversation/src/highlightviewitem.cpp93
-rw-r--r--konversation/src/highlightviewitem.h72
-rw-r--r--konversation/src/identity.cpp177
-rw-r--r--konversation/src/identity.h133
-rw-r--r--konversation/src/identitydialog.cpp642
-rw-r--r--konversation/src/identitydialog.h99
-rw-r--r--konversation/src/ignore.cpp30
-rw-r--r--konversation/src/ignore.h46
-rw-r--r--konversation/src/ignore_preferences.cpp219
-rw-r--r--konversation/src/ignore_preferences.h58
-rw-r--r--konversation/src/ignore_preferencesui.ui219
-rw-r--r--konversation/src/ignorelistviewitem.cpp55
-rw-r--r--konversation/src/ignorelistviewitem.h39
-rw-r--r--konversation/src/images.cpp319
-rw-r--r--konversation/src/images.h102
-rw-r--r--konversation/src/inputfilter.cpp1985
-rw-r--r--konversation/src/inputfilter.h94
-rw-r--r--konversation/src/insertchardialog.cpp57
-rw-r--r--konversation/src/insertchardialog.h46
-rw-r--r--konversation/src/irccharsets.cpp177
-rw-r--r--konversation/src/irccharsets.h106
-rw-r--r--konversation/src/irccolorchooser.cpp83
-rw-r--r--konversation/src/irccolorchooser.h41
-rw-r--r--konversation/src/irccolorchooserui.ui148
-rw-r--r--konversation/src/ircinput.cpp551
-rw-r--r--konversation/src/ircinput.h91
-rw-r--r--konversation/src/ircqueue.cpp209
-rw-r--r--konversation/src/ircqueue.h137
-rw-r--r--konversation/src/ircview.cpp1791
-rw-r--r--konversation/src/ircview.h242
-rw-r--r--konversation/src/ircviewbox.cpp141
-rw-r--r--konversation/src/ircviewbox.h49
-rw-r--r--konversation/src/joinchanneldialog.cpp104
-rw-r--r--konversation/src/joinchanneldialog.h44
-rw-r--r--konversation/src/joinchannelui.ui110
-rw-r--r--konversation/src/konsolepanel.cpp75
-rw-r--r--konversation/src/konsolepanel.h47
-rw-r--r--konversation/src/konvdcop.cpp359
-rw-r--r--konversation/src/konvdcop.h110
-rwxr-xr-xkonversation/src/konversation-0.19-appearance.pl52
-rw-r--r--konversation/src/konversation-0.19-colorcodes.pl26
-rwxr-xr-xkonversation/src/konversation-0.19-colors.pl23
-rw-r--r--konversation/src/konversation-0.19-custombrowser.pl26
-rw-r--r--konversation/src/konversation-0.19-notifylists.pl43
-rwxr-xr-xkonversation/src/konversation-0.19-sortorder.pl69
-rwxr-xr-xkonversation/src/konversation-0.19-tabplacement.pl26
-rw-r--r--konversation/src/konversation-0.20-customfonts.pl21
-rw-r--r--konversation/src/konversation-0.20-quickbuttons.pl137
-rw-r--r--konversation/src/konversation.desktop47
-rw-r--r--konversation/src/konversation.upd144
-rw-r--r--konversation/src/konversationapplication.cpp888
-rw-r--r--konversation/src/konversationapplication.h169
-rw-r--r--konversation/src/konversationmainwindow.cpp654
-rw-r--r--konversation/src/konversationmainwindow.h148
-rw-r--r--konversation/src/konversationsound.cpp41
-rw-r--r--konversation/src/konversationsound.h41
-rw-r--r--konversation/src/konversationstatusbar.cpp242
-rw-r--r--konversation/src/konversationstatusbar.h67
-rw-r--r--konversation/src/konversationui.rc82
-rw-r--r--konversation/src/konvibookmarkhandler.cpp96
-rw-r--r--konversation/src/konvibookmarkhandler.h55
-rw-r--r--konversation/src/konvibookmarkmenu.cpp154
-rw-r--r--konversation/src/konvibookmarkmenu.h65
-rw-r--r--konversation/src/konviconfigdialog.cpp284
-rw-r--r--konversation/src/konviconfigdialog.h124
-rw-r--r--konversation/src/konviface.h89
-rw-r--r--konversation/src/konvirc.protocol11
-rw-r--r--konversation/src/konvirc6.protocol11
-rw-r--r--konversation/src/konvisettingsdialog.cpp316
-rw-r--r--konversation/src/konvisettingsdialog.h95
-rw-r--r--konversation/src/konvisettingspage.h27
-rw-r--r--konversation/src/linkaddressbook/Makefile.am13
-rw-r--r--konversation/src/linkaddressbook/addressbook.cpp339
-rw-r--r--konversation/src/linkaddressbook/addressbook.h99
-rw-r--r--konversation/src/linkaddressbook/addressbook_base.cpp516
-rw-r--r--konversation/src/linkaddressbook/addressbook_base.h128
-rw-r--r--konversation/src/linkaddressbook/addresseeitem.cpp63
-rw-r--r--konversation/src/linkaddressbook/addresseeitem.h66
-rw-r--r--konversation/src/linkaddressbook/kimiface.h191
-rw-r--r--konversation/src/linkaddressbook/linkaddressbookui.cpp191
-rw-r--r--konversation/src/linkaddressbook/linkaddressbookui.h68
-rw-r--r--konversation/src/linkaddressbook/linkaddressbookui_base.ui174
-rw-r--r--konversation/src/linkaddressbook/nicklisttooltip.cpp68
-rw-r--r--konversation/src/linkaddressbook/nicklisttooltip.h45
-rw-r--r--konversation/src/linkaddressbook/nicksonlinetooltip.cpp67
-rw-r--r--konversation/src/linkaddressbook/nicksonlinetooltip.h45
-rw-r--r--konversation/src/log_preferences.ui116
-rw-r--r--konversation/src/logfilereader.cpp187
-rw-r--r--konversation/src/logfilereader.h59
-rw-r--r--konversation/src/main.cpp103
-rw-r--r--konversation/src/modebutton.cpp44
-rw-r--r--konversation/src/modebutton.h40
-rw-r--r--konversation/src/multilineedit.cpp79
-rw-r--r--konversation/src/multilineedit.h40
-rw-r--r--konversation/src/multilinetextedit.cpp175
-rw-r--r--konversation/src/multilinetextedit.h41
-rw-r--r--konversation/src/nick.cpp262
-rw-r--r--konversation/src/nick.h55
-rw-r--r--konversation/src/nickinfo.cpp445
-rw-r--r--konversation/src/nickinfo.h185
-rw-r--r--konversation/src/nicklistbehavior_preferences.cpp108
-rw-r--r--konversation/src/nicklistbehavior_preferences.h46
-rw-r--r--konversation/src/nicklistbehavior_preferencesui.ui160
-rw-r--r--konversation/src/nicklistview.cpp336
-rw-r--r--konversation/src/nicklistview.h77
-rw-r--r--konversation/src/nicksonline.cpp975
-rw-r--r--konversation/src/nicksonline.h261
-rw-r--r--konversation/src/nicksonlineitem.cpp52
-rw-r--r--konversation/src/nicksonlineitem.h58
-rw-r--r--konversation/src/notificationhandler.cpp334
-rw-r--r--konversation/src/notificationhandler.h61
-rw-r--r--konversation/src/osd.cpp446
-rw-r--r--konversation/src/osd.h124
-rw-r--r--konversation/src/osd_preferences.cpp190
-rw-r--r--konversation/src/osd_preferences.h57
-rw-r--r--konversation/src/osd_preferencesui.ui424
-rw-r--r--konversation/src/outputfilter.cpp1790
-rw-r--r--konversation/src/outputfilter.h182
-rw-r--r--konversation/src/query.cpp673
-rw-r--r--konversation/src/query.h118
-rw-r--r--konversation/src/queuetuner.cpp256
-rw-r--r--konversation/src/queuetuner.h62
-rw-r--r--konversation/src/queuetunerbase.ui767
-rw-r--r--konversation/src/quickbutton.cpp38
-rw-r--r--konversation/src/quickbutton.h42
-rw-r--r--konversation/src/quickbuttons_preferences.cpp262
-rw-r--r--konversation/src/quickbuttons_preferences.h53
-rw-r--r--konversation/src/quickbuttons_preferencesui.ui222
-rw-r--r--konversation/src/quickconnectdialog.cpp108
-rw-r--r--konversation/src/quickconnectdialog.h54
-rw-r--r--konversation/src/rawlog.cpp79
-rw-r--r--konversation/src/rawlog.h40
-rw-r--r--konversation/src/replycodes.h160
-rw-r--r--konversation/src/scriptlauncher.cpp74
-rw-r--r--konversation/src/scriptlauncher.h39
-rw-r--r--konversation/src/searchbar.cpp245
-rw-r--r--konversation/src/searchbar.h83
-rw-r--r--konversation/src/searchbarbase.ui131
-rw-r--r--konversation/src/server.cpp3348
-rw-r--r--konversation/src/server.h717
-rw-r--r--konversation/src/serverdialog.cpp103
-rw-r--r--konversation/src/serverdialog.h48
-rw-r--r--konversation/src/servergroupdialog.cpp418
-rw-r--r--konversation/src/servergroupdialog.h85
-rw-r--r--konversation/src/servergroupdialogui.ui318
-rw-r--r--konversation/src/servergroupsettings.cpp229
-rw-r--r--konversation/src/servergroupsettings.h131
-rw-r--r--konversation/src/serverison.cpp242
-rw-r--r--konversation/src/serverison.h113
-rw-r--r--konversation/src/serverlistdialog.cpp564
-rw-r--r--konversation/src/serverlistdialog.h110
-rw-r--r--konversation/src/serverlistview.cpp114
-rw-r--r--konversation/src/serverlistview.h38
-rw-r--r--konversation/src/serversettings.cpp66
-rw-r--r--konversation/src/serversettings.h52
-rw-r--r--konversation/src/ssllabel.cpp29
-rw-r--r--konversation/src/ssllabel.h33
-rw-r--r--konversation/src/sslsocket.cpp358
-rw-r--r--konversation/src/sslsocket.h60
-rw-r--r--konversation/src/statuspanel.cpp398
-rw-r--r--konversation/src/statuspanel.h96
-rw-r--r--konversation/src/tabnotifications_preferences.ui407
-rw-r--r--konversation/src/tabs_preferences.cpp59
-rw-r--r--konversation/src/tabs_preferences.h33
-rw-r--r--konversation/src/tabs_preferencesui.ui192
-rw-r--r--konversation/src/theme_preferences.cpp307
-rw-r--r--konversation/src/theme_preferences.h56
-rw-r--r--konversation/src/theme_preferencesui.ui222
-rw-r--r--konversation/src/topiccombobox.cpp50
-rw-r--r--konversation/src/topiccombobox.h39
-rw-r--r--konversation/src/topiclabel.cpp385
-rw-r--r--konversation/src/topiclabel.h90
-rw-r--r--konversation/src/trayicon.cpp99
-rw-r--r--konversation/src/trayicon.h60
-rw-r--r--konversation/src/unicode.cpp145
-rw-r--r--konversation/src/urlcatcher.cpp233
-rw-r--r--konversation/src/urlcatcher.h66
-rw-r--r--konversation/src/valuelistviewitem.cpp40
-rw-r--r--konversation/src/valuelistviewitem.h33
-rw-r--r--konversation/src/version.h3
-rw-r--r--konversation/src/viewcontainer.cpp2454
-rw-r--r--konversation/src/viewcontainer.h229
-rw-r--r--konversation/src/viewtree.cpp973
-rw-r--r--konversation/src/viewtree.h112
-rw-r--r--konversation/src/viewtreeitem.cpp490
-rw-r--r--konversation/src/viewtreeitem.h92
-rw-r--r--konversation/src/warnings_preferences.cpp185
-rw-r--r--konversation/src/warnings_preferences.h48
-rw-r--r--konversation/src/warnings_preferencesui.ui50
-rw-r--r--konversation/src/watchednicknames_preferences.cpp420
-rw-r--r--konversation/src/watchednicknames_preferences.h59
-rw-r--r--konversation/src/watchednicknames_preferencesui.ui415
396 files changed, 69954 insertions, 0 deletions
diff --git a/konversation/Makefile.am b/konversation/Makefile.am
new file mode 100644
index 0000000..4641e7b
--- /dev/null
+++ b/konversation/Makefile.am
@@ -0,0 +1,19 @@
+SUBDIRS = src images scripts
+EXTRA_DIST = AUTHORS COPYING ChangeLog INSTALL README TODO
+
+messages: rc.cpp
+ $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui" -o -name "*.kcfg"` > rc.cpp
+# we haven't got a tips file atm, so there isn't a lot of point in making one...
+# maybe someone will document the process of writing tips files, or tell me that
+# it needs to be done
+# $(PREPARETIPS) > tips.cpp
+ LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name *.cxx -o -name \*.ecpp -o -name \*.C`; \
+ if test -n "$$LIST"; then \
+ $(XGETTEXT) $$LIST -o $(podir)/konversation.pot; \
+ fi
+# rm -f tips.cpp
+ rm -f rc.cpp
+
+
+include $(top_srcdir)/admin/Doxyfile.am
+
diff --git a/konversation/NEWS b/konversation/NEWS
new file mode 100644
index 0000000..1e542ef
--- /dev/null
+++ b/konversation/NEWS
@@ -0,0 +1,5 @@
+Mon Jul 14 08:40:00 CEST 2008
+- Konversation now depends on KDE 3.5
+
+Tue Jan 17 16:11:00 CEST 2006
+- Konversation now depends on KDE 3.4
diff --git a/konversation/configure.files b/konversation/configure.files
new file mode 100644
index 0000000..dc6867b
--- /dev/null
+++ b/konversation/configure.files
@@ -0,0 +1,3 @@
+./admin/configure.in.min
+configure.in.in
+
diff --git a/konversation/configure.in.in b/konversation/configure.in.in
new file mode 100644
index 0000000..d6d72da
--- /dev/null
+++ b/konversation/configure.in.in
@@ -0,0 +1,48 @@
+#MIN_CONFIG(3.2)
+CXXFLAGS="$CXXFLAGS $KDE_DEFAULT_CXXFLAGS"
+
+KDE_CHECK_HEADER(knotifyclient.h,
+ [
+ AC_DEFINE(USE_KNOTIFY, 1, [Define if you have KNotify] )
+ ])
+
+
+KDE_CHECK_HEADER(kimiface.h,
+ [
+ AC_DEFINE(HAVE_KIMIFACE, 1, [Define if you have Kimiface headers] )
+ LIB_KIMIFACE="-lkimproxy"
+ AC_SUBST(LIB_KIMIFACE)
+ ])
+
+# Check for XScreenSaver
+AC_CHECK_HEADERS(tgmath.h)xss_save_ldflags="$LDFLAGS"
+LDFLAGS="$X_LDFLAGS"
+
+LIB_XSS=
+
+KDE_CHECK_HEADER(X11/extensions/scrnsaver.h,
+ [
+ AC_CHECK_LIB(Xext,XScreenSaverQueryInfo,
+ [
+ AC_DEFINE(HAVE_XSCREENSAVER, 1, [Define if you have the XScreenSaver extension])
+ LIB_XSS="-lXext"
+ ],
+ [
+ ld_shared_flag=
+ KDE_CHECK_COMPILER_FLAG(shared, [ld_shared_flag="-shared"])
+ AC_CHECK_LIB(Xss,XScreenSaverQueryInfo,
+ [
+ AC_DEFINE(HAVE_XSCREENSAVER, 1, [Define if you have the XScreenSaver extension])
+ LIB_XSS="-lXss"
+ ],
+ [],
+ [ $ld_shared_flag $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS ])
+ ],
+ [ $X_PRE_LIBS -lX11 $X_EXTRA_LIBS ])
+ ], [],
+ [
+ #include <X11/Xlib.h>
+ ] )
+
+AC_SUBST(LIB_XSS)
+LDFLAGS="$xss_save_ldflags"
diff --git a/konversation/images/Makefile.am b/konversation/images/Makefile.am
new file mode 100644
index 0000000..e565828
--- /dev/null
+++ b/konversation/images/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = icons nickicons
diff --git a/konversation/images/icons/Makefile.am b/konversation/images/icons/Makefile.am
new file mode 100644
index 0000000..e5515a8
--- /dev/null
+++ b/konversation/images/icons/Makefile.am
@@ -0,0 +1 @@
+KDE_ICON = AUTO
diff --git a/konversation/images/icons/README-kimproxy b/konversation/images/icons/README-kimproxy
new file mode 100644
index 0000000..e0bf8eb
--- /dev/null
+++ b/konversation/images/icons/README-kimproxy
@@ -0,0 +1,5 @@
+These are the icons that are shown in kaddressbook, kmail etc for konversation contacts.
+This icons are provided via the kimiface dcop interface.
+
+The icons were created by Seba y Loretito (aka segfault2k).
+
diff --git a/konversation/images/icons/cr16-action-kimproxyaway.png b/konversation/images/icons/cr16-action-kimproxyaway.png
new file mode 100644
index 0000000..cf7d168
--- /dev/null
+++ b/konversation/images/icons/cr16-action-kimproxyaway.png
Binary files differ
diff --git a/konversation/images/icons/cr16-action-kimproxyoffline.png b/konversation/images/icons/cr16-action-kimproxyoffline.png
new file mode 100644
index 0000000..c35816e
--- /dev/null
+++ b/konversation/images/icons/cr16-action-kimproxyoffline.png
Binary files differ
diff --git a/konversation/images/icons/cr16-action-kimproxyonline.png b/konversation/images/icons/cr16-action-kimproxyonline.png
new file mode 100644
index 0000000..7252280
--- /dev/null
+++ b/konversation/images/icons/cr16-action-kimproxyonline.png
Binary files differ
diff --git a/konversation/images/icons/cr22-action-char.png b/konversation/images/icons/cr22-action-char.png
new file mode 100644
index 0000000..76917d5
--- /dev/null
+++ b/konversation/images/icons/cr22-action-char.png
Binary files differ
diff --git a/konversation/images/icons/cr22-action-kimproxyaway.png b/konversation/images/icons/cr22-action-kimproxyaway.png
new file mode 100644
index 0000000..dc1dc66
--- /dev/null
+++ b/konversation/images/icons/cr22-action-kimproxyaway.png
Binary files differ
diff --git a/konversation/images/icons/cr22-action-kimproxyoffline.png b/konversation/images/icons/cr22-action-kimproxyoffline.png
new file mode 100644
index 0000000..56c8270
--- /dev/null
+++ b/konversation/images/icons/cr22-action-kimproxyoffline.png
Binary files differ
diff --git a/konversation/images/icons/cr22-action-kimproxyonline.png b/konversation/images/icons/cr22-action-kimproxyonline.png
new file mode 100644
index 0000000..1c0ecd6
--- /dev/null
+++ b/konversation/images/icons/cr22-action-kimproxyonline.png
Binary files differ
diff --git a/konversation/images/icons/cr22-action-konv_message.png b/konversation/images/icons/cr22-action-konv_message.png
new file mode 100644
index 0000000..66b2771
--- /dev/null
+++ b/konversation/images/icons/cr22-action-konv_message.png
Binary files differ
diff --git a/konversation/images/icons/cr32-action-kimproxyaway.png b/konversation/images/icons/cr32-action-kimproxyaway.png
new file mode 100644
index 0000000..016c0fe
--- /dev/null
+++ b/konversation/images/icons/cr32-action-kimproxyaway.png
Binary files differ
diff --git a/konversation/images/icons/cr32-action-kimproxyoffline.png b/konversation/images/icons/cr32-action-kimproxyoffline.png
new file mode 100644
index 0000000..52f2c2d
--- /dev/null
+++ b/konversation/images/icons/cr32-action-kimproxyoffline.png
Binary files differ
diff --git a/konversation/images/icons/cr32-action-kimproxyonline.png b/konversation/images/icons/cr32-action-kimproxyonline.png
new file mode 100644
index 0000000..53308ff
--- /dev/null
+++ b/konversation/images/icons/cr32-action-kimproxyonline.png
Binary files differ
diff --git a/konversation/images/icons/crsc-action-kimproxyaway.svgz b/konversation/images/icons/crsc-action-kimproxyaway.svgz
new file mode 100644
index 0000000..473befc
--- /dev/null
+++ b/konversation/images/icons/crsc-action-kimproxyaway.svgz
Binary files differ
diff --git a/konversation/images/icons/crsc-action-kimproxyoffline.svgz b/konversation/images/icons/crsc-action-kimproxyoffline.svgz
new file mode 100644
index 0000000..b983517
--- /dev/null
+++ b/konversation/images/icons/crsc-action-kimproxyoffline.svgz
Binary files differ
diff --git a/konversation/images/icons/crsc-action-kimproxyonline.svgz b/konversation/images/icons/crsc-action-kimproxyonline.svgz
new file mode 100644
index 0000000..ffb4f42
--- /dev/null
+++ b/konversation/images/icons/crsc-action-kimproxyonline.svgz
Binary files differ
diff --git a/konversation/images/icons/crsc-action-konv_message.svgz b/konversation/images/icons/crsc-action-konv_message.svgz
new file mode 100644
index 0000000..fc42686
--- /dev/null
+++ b/konversation/images/icons/crsc-action-konv_message.svgz
Binary files differ
diff --git a/konversation/images/icons/hi128-app-konversation.png b/konversation/images/icons/hi128-app-konversation.png
new file mode 100644
index 0000000..3ca81d8
--- /dev/null
+++ b/konversation/images/icons/hi128-app-konversation.png
Binary files differ
diff --git a/konversation/images/icons/hi16-app-konversation.png b/konversation/images/icons/hi16-app-konversation.png
new file mode 100644
index 0000000..360ca30
--- /dev/null
+++ b/konversation/images/icons/hi16-app-konversation.png
Binary files differ
diff --git a/konversation/images/icons/hi22-app-konversation.png b/konversation/images/icons/hi22-app-konversation.png
new file mode 100644
index 0000000..6da9b6a
--- /dev/null
+++ b/konversation/images/icons/hi22-app-konversation.png
Binary files differ
diff --git a/konversation/images/icons/hi32-app-konversation.png b/konversation/images/icons/hi32-app-konversation.png
new file mode 100644
index 0000000..7a85d5f
--- /dev/null
+++ b/konversation/images/icons/hi32-app-konversation.png
Binary files differ
diff --git a/konversation/images/icons/hi48-app-konversation.png b/konversation/images/icons/hi48-app-konversation.png
new file mode 100644
index 0000000..4d0b066
--- /dev/null
+++ b/konversation/images/icons/hi48-app-konversation.png
Binary files differ
diff --git a/konversation/images/icons/hi64-app-konversation.png b/konversation/images/icons/hi64-app-konversation.png
new file mode 100644
index 0000000..b10e66f
--- /dev/null
+++ b/konversation/images/icons/hi64-app-konversation.png
Binary files differ
diff --git a/konversation/images/icons/hisc-app-konversation.svgz b/konversation/images/icons/hisc-app-konversation.svgz
new file mode 100644
index 0000000..27dcd1f
--- /dev/null
+++ b/konversation/images/icons/hisc-app-konversation.svgz
Binary files differ
diff --git a/konversation/images/nickicons/Makefile.am b/konversation/images/nickicons/Makefile.am
new file mode 100644
index 0000000..32c7af8
--- /dev/null
+++ b/konversation/images/nickicons/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = alternative christmas classic default smiling square oxygen
diff --git a/konversation/images/nickicons/alternative/Makefile.am b/konversation/images/nickicons/alternative/Makefile.am
new file mode 100644
index 0000000..78b04ec
--- /dev/null
+++ b/konversation/images/nickicons/alternative/Makefile.am
@@ -0,0 +1,3 @@
+konversationicondir = $(kde_datadir)/konversation/themes/alternative
+konversationicon_DATA = irc_admin.png irc_away.png irc_halfop.png irc_normal.png irc_op.png \
+ irc_owner.png irc_voice.png index.desktop
diff --git a/konversation/images/nickicons/alternative/index.desktop b/konversation/images/nickicons/alternative/index.desktop
new file mode 100644
index 0000000..cb7baa2
--- /dev/null
+++ b/konversation/images/nickicons/alternative/index.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Type=Theme
+Name=Big Bullets
+Name[bg]=Големи орнаменти
+Name[br]=Melloù bras
+Name[cs]=Velké odrážky
+Name[de]=Große Kugeln
+Name[el]=Μεγάλα Bullet
+Name[fi]=Isot luettelomerkit
+Name[fr]=Grosse balles
+Name[he]=עיגולים גדולים
+Name[hi]=बिग बुलेट्स
+Name[ja]=ビュレット
+Name[ka]=დიდი ტყვიები
+Name[lt]=Dideli ženkliukai
+Name[pa]=ਵੱਡੀਆਂ ਬਿੰਦੀਆਂ
+Name[pt_BR]=Bolas grandes
+Name[sr]=Велики предзнаци
+Name[sr@Latn]=Veliki predznaci
+Name[sv]=Stora punkter
+Name[ta]=பெரிய புள்ளிகள்
+Name[tr]=Büyük Mermiler
+Name[uk]=Великі крапки
+Name[xx]=xxBig Bulletsxx
+Name[zh_CN]=圆点
+Comment="Big Bullets" by Dario Abatianni
+Comment[ar]="Big Bullets" من Dario Abatianni
+Comment[bg]="Големи орнаменти" от Dario Abatianni
+Comment[ca]="Big Bullets" per Dario Abatianni
+Comment[cs]=Velké odrážky od Daria Abatianniho
+Comment[da]="Big Bullets" af Dario Abatianni
+Comment[de]="Big Bullets" von Dario Abatianni
+Comment[el]="Μεγάλα Bullet" από Dario Abatianni
+Comment[es]=«Big Bullets» de Dario Abatianni
+Comment[et]="Big Bullets" (autor Dario Abatianni)
+Comment[fi]="Isot luettelomerkit" (Dario Abatianni)
+Comment[fr]=« Grosses balles » par Dario Abatianni
+Comment[ga]="Big Bullets" le Dario Abatianni
+Comment[gl]="Big Bullets" por Dario Abatianni
+Comment[he]="עיגולים גדולים" של Dario Abatianni
+Comment[hi]="बिग बुलेट्स" दारियो अबातिआनी द्वारा
+Comment[it]=«Big Bullets» di Dario Abatianni
+Comment[ja]=Dario Abatianni 作のビュレットテーマ
+Comment[ka]="დიდი ტყვიები" დარიო აბატიანის მიერ
+Comment[lt]=„Dideli ženkliukai“, sukurti Dario Abatianni
+Comment[nl]="Big Bullets", door Dario Abatianni
+Comment[pt]="Big Bullets" por Dario Abatianni
+Comment[pt_BR]="Bolas grandes" de Dario Abatianni
+Comment[sr]=„Велики предзнаци“ од Дарија Абатијанија (Dario Abatianni)
+Comment[sr@Latn]=„Veliki predznaci“ od Darija Abatijanija (Dario Abatianni)
+Comment[sv]="Stora punkter" av Dario Abatianni
+Comment[ta]="Big Bullets" டாரியோ அபடையானி
+Comment[tr]=Dario Abatianni tarafından "Büyük Mermiler"
+Comment[uk]="Великі крапки" - Dario Abatianni
+Comment[xx]=xx"Big Bullets" by Dario Abatiannixx
+Comment[zh_CN]=“圆点”,由 Dario Abatianni 设计
+Comment[zh_TW]="Big Bullets",由 Dario Abatianni 撰寫
diff --git a/konversation/images/nickicons/alternative/irc_admin.png b/konversation/images/nickicons/alternative/irc_admin.png
new file mode 100644
index 0000000..d071f6f
--- /dev/null
+++ b/konversation/images/nickicons/alternative/irc_admin.png
Binary files differ
diff --git a/konversation/images/nickicons/alternative/irc_away.png b/konversation/images/nickicons/alternative/irc_away.png
new file mode 100644
index 0000000..381982e
--- /dev/null
+++ b/konversation/images/nickicons/alternative/irc_away.png
Binary files differ
diff --git a/konversation/images/nickicons/alternative/irc_halfop.png b/konversation/images/nickicons/alternative/irc_halfop.png
new file mode 100644
index 0000000..fab9b15
--- /dev/null
+++ b/konversation/images/nickicons/alternative/irc_halfop.png
Binary files differ
diff --git a/konversation/images/nickicons/alternative/irc_normal.png b/konversation/images/nickicons/alternative/irc_normal.png
new file mode 100644
index 0000000..89b9dd2
--- /dev/null
+++ b/konversation/images/nickicons/alternative/irc_normal.png
Binary files differ
diff --git a/konversation/images/nickicons/alternative/irc_op.png b/konversation/images/nickicons/alternative/irc_op.png
new file mode 100644
index 0000000..d8b849f
--- /dev/null
+++ b/konversation/images/nickicons/alternative/irc_op.png
Binary files differ
diff --git a/konversation/images/nickicons/alternative/irc_owner.png b/konversation/images/nickicons/alternative/irc_owner.png
new file mode 100644
index 0000000..b52668a
--- /dev/null
+++ b/konversation/images/nickicons/alternative/irc_owner.png
Binary files differ
diff --git a/konversation/images/nickicons/alternative/irc_voice.png b/konversation/images/nickicons/alternative/irc_voice.png
new file mode 100644
index 0000000..8090ad3
--- /dev/null
+++ b/konversation/images/nickicons/alternative/irc_voice.png
Binary files differ
diff --git a/konversation/images/nickicons/christmas/Makefile.am b/konversation/images/nickicons/christmas/Makefile.am
new file mode 100644
index 0000000..4c1aa5c
--- /dev/null
+++ b/konversation/images/nickicons/christmas/Makefile.am
@@ -0,0 +1,3 @@
+konversationicondir = $(kde_datadir)/konversation/themes/christmas
+konversationicon_DATA = irc_admin.png irc_away.png irc_halfop.png irc_normal.png irc_op.png \
+ irc_owner.png irc_voice.png index.desktop
diff --git a/konversation/images/nickicons/christmas/index.desktop b/konversation/images/nickicons/christmas/index.desktop
new file mode 100644
index 0000000..c3ae670
--- /dev/null
+++ b/konversation/images/nickicons/christmas/index.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Type=Theme
+Name=Christmas Theme
+Name[ar]=سمة Christmas
+Name[bg]=Коледна тема
+Name[br]=Giz Nedeleg
+Name[ca]=Tema nadalenc
+Name[cs]=Vánoční téma
+Name[da]=Juletema
+Name[de]=Weihnachtsdesign
+Name[el]=Χριστουγεννιάτικο θέμα
+Name[es]=Tema navideño
+Name[et]=Jõuluteema
+Name[fi]=Jouluteema
+Name[fr]=Thème de Noël
+Name[gl]=Tema de Nadal
+Name[he]=ערכת חג מולד
+Name[it]=Tema natalizio
+Name[ja]=クリスマス
+Name[ka]=საშობაო გაფორმება
+Name[lt]=Kalėdinė tema
+Name[nl]=Kerstthema
+Name[pa]=ਕਰਿਸਮਿਸ ਸਰੂਪ
+Name[pt]=Tema de Natal
+Name[pt_BR]=Tema de Natal
+Name[ru]=Рождественская
+Name[sr]=Божићна тема
+Name[sr@Latn]=Božićna tema
+Name[sv]=Jultema
+Name[ta]=கிறிஸ்துமஸ் பொருள்
+Name[tr]=Noel Teması
+Name[uk]=Різдвяна тема
+Name[xx]=xxChristmas Themexx
+Name[zh_CN]=耶诞节主题
+Name[zh_TW]=聖誕主題
+Comment="Christmas Theme" by Kenichiro Takahashi
+Comment[ar]=سمة "Christmas Theme" من Kenichiro Takahashi
+Comment[bg]="Коледна тема" от Kenichiro Takahashi
+Comment[br]=« Giz Nedeleg » gant Kenichiro Takahashi
+Comment[ca]="Tema nadalenc" per Kenichiro Takahashi
+Comment[cs]=Vánoční téma od Kenichiro Takahashi
+Comment[da]="Juletema" af Kenichiro Takahashi
+Comment[de]="Christmas Theme" von Kenichiro Takahashi
+Comment[el]="Χριστουγεννιάτικο θέμα" από Kenichiro Takahashi
+Comment[es]=«Tema navideño» de Kenichiro Takahashi
+Comment[et]="Jõuluteema" (autor Kenichiro Takahashi)
+Comment[fi]="Jouluteema" (Kenichiro Takahashi)
+Comment[fr]=« Thème de Noël » par Kenichiro Takahashi
+Comment[ga]="Christmas Theme" le Kenichiro Takahashi
+Comment[gl]="Tema de Nadal" por Kenichiro Takahashi
+Comment[he]="ערכת חג מולד " של Kenichiro Takahashi
+Comment[it]=«Tema natalizio» di Kenichiro Takahashi
+Comment[ja]=Kenichiro Takahashi 作のクリスマステーマ
+Comment[ka]="საშობაო გაფორმება" კენიჩირო ტაკაჰაშის მიერ
+Comment[lt]=„Kalėdinė tema“, sukurta Kenichiro Takahashi
+Comment[nl]="Christmas Theme" door Kenichiro Takahashi
+Comment[pt]="Tema de Natal" por Kenichiro Takahashi
+Comment[pt_BR]="Tema de Natal" de Kenichiro Takahashi
+Comment[ru]=Сделал Kenichiro Takahashi
+Comment[sr]=„Божићна тема“ од Кеничира Такахашија (Kenichiro Takahashi)
+Comment[sr@Latn]=„Božićna tema“ od Keničira Takahašija (Kenichiro Takahashi)
+Comment[sv]="Jultema" av Kenichiro Takahashi
+Comment[ta]="Christmas Theme" கெனிசிரோ டகாஹாஷி
+Comment[tr]=Kenichiro Takahashi tarafından "Noel Teması"
+Comment[uk]="Різдвяна тема" - Kenichiro Takahashi
+Comment[xx]=xx"Christmas Theme" by Kenichiro Takahashixx
+Comment[zh_CN]=“耶诞节主题”,由 Kenichiro Takahashi 设计
+Comment[zh_TW]="聖誕"主題,由 Kenichiro Takahashi 撰寫
diff --git a/konversation/images/nickicons/christmas/irc_admin.png b/konversation/images/nickicons/christmas/irc_admin.png
new file mode 100644
index 0000000..ef64b8d
--- /dev/null
+++ b/konversation/images/nickicons/christmas/irc_admin.png
Binary files differ
diff --git a/konversation/images/nickicons/christmas/irc_away.png b/konversation/images/nickicons/christmas/irc_away.png
new file mode 100644
index 0000000..e36e7ba
--- /dev/null
+++ b/konversation/images/nickicons/christmas/irc_away.png
Binary files differ
diff --git a/konversation/images/nickicons/christmas/irc_halfop.png b/konversation/images/nickicons/christmas/irc_halfop.png
new file mode 100644
index 0000000..130c552
--- /dev/null
+++ b/konversation/images/nickicons/christmas/irc_halfop.png
Binary files differ
diff --git a/konversation/images/nickicons/christmas/irc_normal.png b/konversation/images/nickicons/christmas/irc_normal.png
new file mode 100644
index 0000000..ce45e5c
--- /dev/null
+++ b/konversation/images/nickicons/christmas/irc_normal.png
Binary files differ
diff --git a/konversation/images/nickicons/christmas/irc_op.png b/konversation/images/nickicons/christmas/irc_op.png
new file mode 100644
index 0000000..c4608ff
--- /dev/null
+++ b/konversation/images/nickicons/christmas/irc_op.png
Binary files differ
diff --git a/konversation/images/nickicons/christmas/irc_owner.png b/konversation/images/nickicons/christmas/irc_owner.png
new file mode 100644
index 0000000..c18bff4
--- /dev/null
+++ b/konversation/images/nickicons/christmas/irc_owner.png
Binary files differ
diff --git a/konversation/images/nickicons/christmas/irc_voice.png b/konversation/images/nickicons/christmas/irc_voice.png
new file mode 100644
index 0000000..5043867
--- /dev/null
+++ b/konversation/images/nickicons/christmas/irc_voice.png
Binary files differ
diff --git a/konversation/images/nickicons/classic/Makefile.am b/konversation/images/nickicons/classic/Makefile.am
new file mode 100644
index 0000000..343835b
--- /dev/null
+++ b/konversation/images/nickicons/classic/Makefile.am
@@ -0,0 +1,3 @@
+konversationicondir = $(kde_datadir)/konversation/themes/classic
+konversationicon_DATA = irc_admin.png irc_away.png irc_halfop.png irc_normal.png irc_op.png \
+ irc_owner.png irc_voice.png index.desktop
diff --git a/konversation/images/nickicons/classic/index.desktop b/konversation/images/nickicons/classic/index.desktop
new file mode 100644
index 0000000..5191fd9
--- /dev/null
+++ b/konversation/images/nickicons/classic/index.desktop
@@ -0,0 +1,70 @@
+[Desktop Entry]
+Type=Theme
+Name=Classic Theme
+Name[ar]=سمة كلاسيكية
+Name[bg]=Класическа тема
+Name[br]=Giz da gustum
+Name[ca]=Tema clàssic
+Name[cs]=Klasické téma
+Name[da]=Klassisk tema
+Name[de]=Klassisches Design
+Name[el]=Κλασικό θέμα
+Name[es]=Tema clásico
+Name[et]=Klassikaline teema
+Name[fi]=Klassinen teema
+Name[fr]=Thème classique
+Name[ga]=Téama Clasaiceach
+Name[gl]=Tema Clásico
+Name[he]=הערכה הישנה והטובה
+Name[hi]=क्लासिक प्रसंग
+Name[it]=Tema classico
+Name[ja]=クラシック
+Name[ka]=კლასიკური გაფორმება
+Name[lt]=Klasikinė tema
+Name[nl]=Klassiek thema
+Name[pa]=ਟਕਸਾਲੀ ਸਰੂਪ
+Name[pt]=Tema Clássico
+Name[pt_BR]=Tema Clássico
+Name[ru]=Классическая
+Name[sr]=Класична тема
+Name[sr@Latn]=Klasična tema
+Name[sv]=Klassiskt tema
+Name[ta]=சிறந்த தலைப்பு
+Name[tr]=Klasik tema
+Name[uk]=Класична тема
+Name[xx]=xxClassic Themexx
+Name[zh_CN]=经典主题
+Name[zh_TW]=傳統式主題
+Comment="LED" by Dario Abatianni
+Comment[ar]="LED" من Dario Abatianni
+Comment[bg]="LED" от Dario Abatianni
+Comment[br]=« LED » gant Dario Abatianni
+Comment[ca]="LED" per Dario Abatianni
+Comment[cs]=LED od Daria Abatianniho
+Comment[da]="LED" af Dario Abatianni
+Comment[de]="LED" von Dario Abatianni
+Comment[el]="LED" από Dario Abatianni
+Comment[es]=«LED» de Dario Abatianni
+Comment[et]="LED" (autor Dario Abatianni)
+Comment[fi]="LEDit" (Dario Abatianni)
+Comment[fr]=« LED » par Dario Abatianni
+Comment[ga]="LED" le Dario Abatianni
+Comment[gl]="LED" led Dario Abatianni
+Comment[he]="LED" של Dario Abatianni
+Comment[hi]="एलईडी" दारियो अबातिआनी द्वारा
+Comment[it]=«LED» di Dario Abatianni
+Comment[ja]=Dario Abatianni 作の LED テーマ
+Comment[ka]="LED" დარიო აბატიანის მიერ
+Comment[lt]=„LED“, sukurta Dario Abatianni
+Comment[nl]="LED", door Dario Abatianni
+Comment[pt]="LED" por Dario Abatianni
+Comment[pt_BR]="LED" de Dario Abatianni
+Comment[sr]=„LED“ од Дарија Абатијанија (Dario Abatianni)
+Comment[sr@Latn]=„LED“ od Darija Abatijanija (Dario Abatianni)
+Comment[sv]="Lampor" av Dario Abatianni
+Comment[ta]="LED" டைரியோ அபடியானி
+Comment[tr]=Dario Abatianni tarafından "LED"
+Comment[uk]="LED" - Dario Abatianni
+Comment[xx]=xx"LED" by Dario Abatiannixx
+Comment[zh_CN]=“指示灯”,由 Dario Abatianni 设计
+Comment[zh_TW]="LED" 由 Dario Abatianni 撰寫
diff --git a/konversation/images/nickicons/classic/irc_admin.png b/konversation/images/nickicons/classic/irc_admin.png
new file mode 100644
index 0000000..6a04b69
--- /dev/null
+++ b/konversation/images/nickicons/classic/irc_admin.png
Binary files differ
diff --git a/konversation/images/nickicons/classic/irc_away.png b/konversation/images/nickicons/classic/irc_away.png
new file mode 100644
index 0000000..ba569b1
--- /dev/null
+++ b/konversation/images/nickicons/classic/irc_away.png
Binary files differ
diff --git a/konversation/images/nickicons/classic/irc_halfop.png b/konversation/images/nickicons/classic/irc_halfop.png
new file mode 100644
index 0000000..b4bc2ce
--- /dev/null
+++ b/konversation/images/nickicons/classic/irc_halfop.png
Binary files differ
diff --git a/konversation/images/nickicons/classic/irc_normal.png b/konversation/images/nickicons/classic/irc_normal.png
new file mode 100644
index 0000000..78b9be4
--- /dev/null
+++ b/konversation/images/nickicons/classic/irc_normal.png
Binary files differ
diff --git a/konversation/images/nickicons/classic/irc_op.png b/konversation/images/nickicons/classic/irc_op.png
new file mode 100644
index 0000000..539a97d
--- /dev/null
+++ b/konversation/images/nickicons/classic/irc_op.png
Binary files differ
diff --git a/konversation/images/nickicons/classic/irc_owner.png b/konversation/images/nickicons/classic/irc_owner.png
new file mode 100644
index 0000000..cdb0aa4
--- /dev/null
+++ b/konversation/images/nickicons/classic/irc_owner.png
Binary files differ
diff --git a/konversation/images/nickicons/classic/irc_voice.png b/konversation/images/nickicons/classic/irc_voice.png
new file mode 100644
index 0000000..dd103a2
--- /dev/null
+++ b/konversation/images/nickicons/classic/irc_voice.png
Binary files differ
diff --git a/konversation/images/nickicons/default/Makefile.am b/konversation/images/nickicons/default/Makefile.am
new file mode 100644
index 0000000..10886c0
--- /dev/null
+++ b/konversation/images/nickicons/default/Makefile.am
@@ -0,0 +1,3 @@
+konversationicondir = $(kde_datadir)/konversation/themes/default
+konversationicon_DATA = irc_admin.png irc_away.png irc_halfop.png irc_normal.png irc_op.png \
+ irc_owner.png irc_voice.png index.desktop
diff --git a/konversation/images/nickicons/default/index.desktop b/konversation/images/nickicons/default/index.desktop
new file mode 100644
index 0000000..d0c1222
--- /dev/null
+++ b/konversation/images/nickicons/default/index.desktop
@@ -0,0 +1,69 @@
+[Desktop Entry]
+Type=Theme
+Name=Default Theme
+Name[ar]=السِمة الإفتراضية
+Name[bg]=Стандартна тема
+Name[br]=Giz dre ziouer
+Name[ca]=Tema per omissió
+Name[cs]=Výchozí téma
+Name[da]=Standardtema
+Name[de]=Standard-Design
+Name[el]=Προκαθορισμένο θέμα
+Name[es]=Tema predeterminado
+Name[et]=Vaiketeema
+Name[fi]=Oletusteema
+Name[fr]=Thème par défaut
+Name[ga]=Téama Réamhshocraithe
+Name[gl]=Tema por omisión
+Name[he]=ערכת ברירת מחדל
+Name[hi]=डिफ़ॉल्ट प्रसंग
+Name[it]=Tema predefinito
+Name[ja]=標準テーマ
+Name[ka]=ნაგულისხმები გაფორმება
+Name[lt]=Numatyta tema
+Name[nl]=Standaardthema
+Name[pa]=ਮੂਲ ਸਰੂਪ
+Name[pt]=Tema Predefinido
+Name[pt_BR]=Tema Padrão
+Name[ru]=Стандартная
+Name[sr]=Подразумевана тема
+Name[sr@Latn]=Podrazumevana tema
+Name[sv]=Standardtema
+Name[ta]=முன்னிருப்பு தலைப்பு
+Name[tr]=Öntanımlı tema
+Name[uk]=Типова тема
+Name[xx]=xxDefault Themexx
+Name[zh_CN]=默认页面
+Name[zh_TW]=預設主題
+Comment="Simplistic" by Shintaro Matsuoka
+Comment[ar]="Simplistic" من Shintaro Matsuoka
+Comment[bg]="Опростена" от Shintaro Matsuoka
+Comment[ca]="Simplistic" per Shintaro Matsuoka
+Comment[cs]=Jednoduché téma od Shintara Matsuoky
+Comment[da]="Simplistic" af Shintaro Matsuoka
+Comment[de]="Simplistic" von Shintaro Matsuoka
+Comment[el]="Απλοϊκό" από Shintaro Matsuoka
+Comment[es]=«Simplistic» de Shintaro Matsuoka
+Comment[et]="Simplistic" (autor Shintaro Matsuoka)
+Comment[fi]="Yksinkertainen" (Shintaro Matsuoka)
+Comment[fr]=« Simpliste » par Shintaro Matsuoka
+Comment[ga]="Simplistic" le Shintaro Matsuoka
+Comment[gl]="Simples" por Shintaro Matsuoka
+Comment[he]="מראה פשוט" של Shintaro Matsuoka
+Comment[hi]="सिम्प्लिस्टिक" शिनतारो मात्सुओका द्वारा
+Comment[it]=«Simplistic» di Shintaro Matsuoka
+Comment[ja]=Shintaro Matsuoka 作のシンプルなテーマ
+Comment[ka]="Simplistic" შინტარო მაცუოკას მიერ
+Comment[lt]=„Supaprastinta“, sukurta Shintaro Matsuoka
+Comment[nl]="Simplistic", door Shintaro Matsuoka
+Comment[pt]="Simplistic" por Shintaro Matsuoka
+Comment[pt_BR]="Simplista" de Shintaro Matsuoka
+Comment[sr]=„Једноставно“ од Шинтара Мацуоке (Shintaro Matsuoka)
+Comment[sr@Latn]=„Jednostavno“ od Šintara Macuoke (Shintaro Matsuoka)
+Comment[sv]="Förenklat" av Shintaro Matsuoka
+Comment[ta]="Simplistic" ஷிண்டாரோ மாட்சொக்கா
+Comment[tr]=Shintaro Matsuoka tarafından "Basitlik"
+Comment[uk]="Проста" - Shintaro Matsuoka
+Comment[xx]=xx"Simplistic" by Shintaro Matsuokaxx
+Comment[zh_CN]=“简洁”,由 Shintaro Matsuoka 设计
+Comment[zh_TW]="Simplistic" 由 Shintaro Matsuoka 撰寫
diff --git a/konversation/images/nickicons/default/irc_admin.png b/konversation/images/nickicons/default/irc_admin.png
new file mode 100644
index 0000000..dc4cfc8
--- /dev/null
+++ b/konversation/images/nickicons/default/irc_admin.png
Binary files differ
diff --git a/konversation/images/nickicons/default/irc_away.png b/konversation/images/nickicons/default/irc_away.png
new file mode 100644
index 0000000..1a209e2
--- /dev/null
+++ b/konversation/images/nickicons/default/irc_away.png
Binary files differ
diff --git a/konversation/images/nickicons/default/irc_halfop.png b/konversation/images/nickicons/default/irc_halfop.png
new file mode 100644
index 0000000..3494353
--- /dev/null
+++ b/konversation/images/nickicons/default/irc_halfop.png
Binary files differ
diff --git a/konversation/images/nickicons/default/irc_normal.png b/konversation/images/nickicons/default/irc_normal.png
new file mode 100644
index 0000000..3402cf3
--- /dev/null
+++ b/konversation/images/nickicons/default/irc_normal.png
Binary files differ
diff --git a/konversation/images/nickicons/default/irc_op.png b/konversation/images/nickicons/default/irc_op.png
new file mode 100644
index 0000000..8dd62fb
--- /dev/null
+++ b/konversation/images/nickicons/default/irc_op.png
Binary files differ
diff --git a/konversation/images/nickicons/default/irc_owner.png b/konversation/images/nickicons/default/irc_owner.png
new file mode 100644
index 0000000..289ee21
--- /dev/null
+++ b/konversation/images/nickicons/default/irc_owner.png
Binary files differ
diff --git a/konversation/images/nickicons/default/irc_voice.png b/konversation/images/nickicons/default/irc_voice.png
new file mode 100644
index 0000000..2ea8713
--- /dev/null
+++ b/konversation/images/nickicons/default/irc_voice.png
Binary files differ
diff --git a/konversation/images/nickicons/oxygen/Makefile.am b/konversation/images/nickicons/oxygen/Makefile.am
new file mode 100644
index 0000000..66ef8e5
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/Makefile.am
@@ -0,0 +1,3 @@
+konversationicondir = $(kde_datadir)/konversation/themes/oxygen
+konversationicon_DATA = irc_admin.png irc_away.png irc_halfop.png irc_normal.png irc_op.png \
+ irc_owner.png irc_voice.png index.desktop
diff --git a/konversation/images/nickicons/oxygen/index.desktop b/konversation/images/nickicons/oxygen/index.desktop
new file mode 100644
index 0000000..07796da
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/index.desktop
@@ -0,0 +1,22 @@
+[Desktop Entry]
+Type=Theme
+Name=Oxygen Theme
+Name[da]=Oxygen-tema
+Name[de]=Oxygen-Design
+Name[et]=Oxygeni teema
+Name[it]=Tema Oxygen
+Name[ja]=Oxygen テーマ
+Name[sr]=Кисеоник тема
+Name[sr@Latn]=Kiseonik tema
+Name[sv]=Oxygen-tema
+Name[zh_TW]=Oxygen 主題
+Comment="Oxygen" by Nuno Pinheiro
+Comment[da]="Oxygen" af Nuno Pinheiro
+Comment[de]="Oxygen" von Nuno Pinheiro
+Comment[et]="Oxygen" (autor Nuno Pinheiro)
+Comment[it]=«Oxygen» di Nuno Pinheiro
+Comment[ja]=Nuno Pinheiro 作の Oxygen テーマ
+Comment[sr]=„Кисеоник“ од Нуна Пињеира
+Comment[sr@Latn]=„Kiseonik“ od Nuna Pinjeira
+Comment[sv]="Oxygen" av Nuno Pinheiro
+Comment[zh_TW]="Oxygen",由 Nuno Pinheiro 提供
diff --git a/konversation/images/nickicons/oxygen/irc_admin.png b/konversation/images/nickicons/oxygen/irc_admin.png
new file mode 100644
index 0000000..0f94c26
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/irc_admin.png
Binary files differ
diff --git a/konversation/images/nickicons/oxygen/irc_away.png b/konversation/images/nickicons/oxygen/irc_away.png
new file mode 100644
index 0000000..9569339
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/irc_away.png
Binary files differ
diff --git a/konversation/images/nickicons/oxygen/irc_halfop.png b/konversation/images/nickicons/oxygen/irc_halfop.png
new file mode 100644
index 0000000..a3ae78f
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/irc_halfop.png
Binary files differ
diff --git a/konversation/images/nickicons/oxygen/irc_normal.png b/konversation/images/nickicons/oxygen/irc_normal.png
new file mode 100644
index 0000000..1ad0090
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/irc_normal.png
Binary files differ
diff --git a/konversation/images/nickicons/oxygen/irc_op.png b/konversation/images/nickicons/oxygen/irc_op.png
new file mode 100644
index 0000000..308beba
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/irc_op.png
Binary files differ
diff --git a/konversation/images/nickicons/oxygen/irc_owner.png b/konversation/images/nickicons/oxygen/irc_owner.png
new file mode 100644
index 0000000..3fb0969
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/irc_owner.png
Binary files differ
diff --git a/konversation/images/nickicons/oxygen/irc_voice.png b/konversation/images/nickicons/oxygen/irc_voice.png
new file mode 100644
index 0000000..7bfdb40
--- /dev/null
+++ b/konversation/images/nickicons/oxygen/irc_voice.png
Binary files differ
diff --git a/konversation/images/nickicons/smiling/Makefile.am b/konversation/images/nickicons/smiling/Makefile.am
new file mode 100644
index 0000000..8b92cf0
--- /dev/null
+++ b/konversation/images/nickicons/smiling/Makefile.am
@@ -0,0 +1,3 @@
+konversationicondir = $(kde_datadir)/konversation/themes/smiling
+konversationicon_DATA = irc_admin.png irc_away.png irc_halfop.png irc_normal.png irc_op.png \
+ irc_owner.png irc_voice.png index.desktop
diff --git a/konversation/images/nickicons/smiling/index.desktop b/konversation/images/nickicons/smiling/index.desktop
new file mode 100644
index 0000000..f749f61
--- /dev/null
+++ b/konversation/images/nickicons/smiling/index.desktop
@@ -0,0 +1,70 @@
+[Desktop Entry]
+Type=Theme
+Name=Smiling Theme
+Name[ar]=سمة ضاحِكة ( Smiling )
+Name[bg]=Тема "Усмивка"
+Name[br]=Giz Mousc'hoarzh
+Name[ca]=Tema Smiling
+Name[cs]=Smajlíkové téma
+Name[da]=Smilende tema
+Name[de]=Smiley-Design
+Name[el]=Θέμα χαμόγελου
+Name[es]=Tema «Smiling»
+Name[et]=Naerusuuteema
+Name[fi]=Hymiöteema
+Name[fr]=Thème sourire
+Name[ga]=Téama "Smiling"
+Name[gl]=Tema Sorrinte
+Name[he]=ערכה מחייכת
+Name[hi]=मुस्कुराता प्रसंग
+Name[it]=Tema sorridente
+Name[ja]=スマイリー
+Name[ka]=მომღიმარი გაფორმება
+Name[lt]=Besišypsanti tema
+Name[nl]=Smiling-thema
+Name[pa]=ਖੁਸ਼ ਸਰੂਪ
+Name[pt]=Tema "Smiling"
+Name[pt_BR]=Tema Sorridente
+Name[ru]=Улыбочки
+Name[sr]=Насмејана тема
+Name[sr@Latn]=Nasmejana tema
+Name[sv]=Leende tema
+Name[ta]=சிரிக்கின்ற பொருள்
+Name[tr]=Gülümseyen Tema
+Name[uk]=Тема посмішок
+Name[xx]=xxSmiling Themexx
+Name[zh_CN]=微笑主题
+Name[zh_TW]=微笑主題
+Comment="Smiling" stolen from Kopete by John Tapsell
+Comment[ar]=أخِذ "Smiling" من Kopete من قِبل John Tapsell
+Comment[bg]=Тема "Усмивка" е откраднато от Kopete от John Tapsell
+Comment[ca]="Smiling" robat del Kopete per John Tapsell
+Comment[cs]=Smajlíkové téma od Johna Tapsella převzato z Kopete
+Comment[da]="Smilende" stjålet fra Kopete af John Tapsell
+Comment[de]="Smiling" geklaut bei Kopete von John Tapsell
+Comment[el]="Χαμόγελο" κλεμμένο από το Kopete από John Tapsell
+Comment[es]=«Smiling» tomado de Kopete por John Tapsell
+Comment[et]="Smiling" (Kopetest hiivas John Tapsell)
+Comment[fi]="Hymiöteema" Kopetesta (John Tapsell)
+Comment[fr]=« Sourire » volé à Kopete par John Tapsell
+Comment[ga]="Smiling", goidte ó Kopete ag John Tapsell
+Comment[gl]="Sorriso" roubado de Kopete por John Tapsell
+Comment[he]="ערכה מחייכת" נגנבה מ־Kopete על ידי John Tapsell
+Comment[hi]="मुस्कान" को के-ऑप्टी से जॉन तापसेल द्वारा चुराया गया
+Comment[it]=«Sorridente» rubato a Kopete da John Tapsell
+Comment[ja]=John Tapsell 作の Kopete のテーマを借用
+Comment[ka]="მომღიმარი" მოპარულია Kopete-დან ჯონ ტაპსელის მიერ
+Comment[lt]=„Besišypsanti tema“, John Tapsell pavogta iš Kopete programos
+Comment[nl]="Smiling", overgenomen van Kopete, door John Tapsell
+Comment[pt]="Smiling" retirado do Kopete por John Tapsell
+Comment[pt_BR]="Sorridente" roubado do Kopete por John Tapsell
+Comment[ru]=Украл у Kopete John Tapsell
+Comment[sr]=„Насмејано“ украо из Kopete-а Џон Тапсел (John Tapsell)
+Comment[sr@Latn]=„Nasmejano“ ukrao iz Kopete-a Džon Tapsel (John Tapsell)
+Comment[sv]="Leende" stulen från Kopete av John Tapsell
+Comment[ta]=Kopeteல் இருந்து "Smiling" ஸ்டோலன் ஜான் டாப்செல்
+Comment[tr]=John Tapsell tarafından Kopete'in "Gülümseyen"i
+Comment[uk]="Посмішки" взято з Kopete - John Tapsell
+Comment[xx]=xx"Smiling" stolen from Kopete by John Tapsellxx
+Comment[zh_CN]=“微笑”,借鉴自 John Tapsell 为 Kopete 设计的造型
+Comment[zh_TW]="微笑"主題,從 Kopete 偷來的,由 John Tapsell 撰寫
diff --git a/konversation/images/nickicons/smiling/irc_admin.png b/konversation/images/nickicons/smiling/irc_admin.png
new file mode 100644
index 0000000..7b259d5
--- /dev/null
+++ b/konversation/images/nickicons/smiling/irc_admin.png
Binary files differ
diff --git a/konversation/images/nickicons/smiling/irc_away.png b/konversation/images/nickicons/smiling/irc_away.png
new file mode 100644
index 0000000..3a3f3c2
--- /dev/null
+++ b/konversation/images/nickicons/smiling/irc_away.png
Binary files differ
diff --git a/konversation/images/nickicons/smiling/irc_halfop.png b/konversation/images/nickicons/smiling/irc_halfop.png
new file mode 100644
index 0000000..2335545
--- /dev/null
+++ b/konversation/images/nickicons/smiling/irc_halfop.png
Binary files differ
diff --git a/konversation/images/nickicons/smiling/irc_normal.png b/konversation/images/nickicons/smiling/irc_normal.png
new file mode 100644
index 0000000..2224c37
--- /dev/null
+++ b/konversation/images/nickicons/smiling/irc_normal.png
Binary files differ
diff --git a/konversation/images/nickicons/smiling/irc_op.png b/konversation/images/nickicons/smiling/irc_op.png
new file mode 100644
index 0000000..a9317a2
--- /dev/null
+++ b/konversation/images/nickicons/smiling/irc_op.png
Binary files differ
diff --git a/konversation/images/nickicons/smiling/irc_owner.png b/konversation/images/nickicons/smiling/irc_owner.png
new file mode 100644
index 0000000..fb7e483
--- /dev/null
+++ b/konversation/images/nickicons/smiling/irc_owner.png
Binary files differ
diff --git a/konversation/images/nickicons/smiling/irc_voice.png b/konversation/images/nickicons/smiling/irc_voice.png
new file mode 100644
index 0000000..8df68ee
--- /dev/null
+++ b/konversation/images/nickicons/smiling/irc_voice.png
Binary files differ
diff --git a/konversation/images/nickicons/square/Makefile.am b/konversation/images/nickicons/square/Makefile.am
new file mode 100644
index 0000000..6ef4b68
--- /dev/null
+++ b/konversation/images/nickicons/square/Makefile.am
@@ -0,0 +1,3 @@
+konversationicondir = $(kde_datadir)/konversation/themes/square
+konversationicon_DATA = irc_admin.png irc_away.png irc_halfop.png irc_normal.png irc_op.png \
+ irc_owner.png irc_voice.png index.desktop
diff --git a/konversation/images/nickicons/square/index.desktop b/konversation/images/nickicons/square/index.desktop
new file mode 100644
index 0000000..955b293
--- /dev/null
+++ b/konversation/images/nickicons/square/index.desktop
@@ -0,0 +1,69 @@
+[Desktop Entry]
+Type=Theme
+Name=Square Theme
+Name[ar]=سمة Square
+Name[bg]=Тема "Квадрат"
+Name[br]=Giz karrez
+Name[ca]=Tema Square
+Name[cs]=Square téma
+Name[da]=Square tema
+Name[de]=Rechteck-Design
+Name[el]=Τετράγωνο θέμα
+Name[es]=Tema «Square»
+Name[et]=Kandiline teema
+Name[fi]=Neliöteema
+Name[fr]=Thème carré
+Name[ga]=Téama "Square"
+Name[gl]=Tema Cadrado
+Name[he]=ערכה מרובעת
+Name[it]=Tema quadrato
+Name[ja]=四角
+Name[ka]=კვადრატული გაფორმება
+Name[lt]=Kvadratinė tema
+Name[nl]=Vierkant thema
+Name[pa]=ਵਰਗ ਸਰੂਪ
+Name[pt]=Tema Quadrado
+Name[pt_BR]=Tema Quadrado
+Name[ru]=Квадратики
+Name[sr]=Квадратна тема
+Name[sr@Latn]=Kvadratna tema
+Name[sv]=Kvadratiskt tema
+Name[ta]=சதுர பொருள்
+Name[tr]=Kare Teması
+Name[uk]=Квадратна тема
+Name[xx]=xxSquare Themexx
+Name[zh_CN]=方块主题
+Name[zh_TW]=方塊主題
+Comment="SQUARE" by Kenichiro Takahashi
+Comment[ar]="SQUARE" تأليف Kenichiro Takahashi
+Comment[bg]=Тема "Квадрат" от Kenichiro Takahashi
+Comment[br]=« SQUARE » gant Kenichiro Takahashi
+Comment[ca]="SQUARE" per Kenichiro Takahashi
+Comment[cs]="SQUARE" od Kenichiro Takahashi
+Comment[da]="SQUARE" af Kenichiro Takahashi
+Comment[de]="SQUARE" von Kenichiro Takahashi
+Comment[el]="Τετράγωνο" από Kenichiro Takahashi
+Comment[es]=«SQUARE» de Kenichiro Takahashi
+Comment[et]="SQUARE" (autor Kenichiro Takahashi)
+Comment[fi]="NELIÖT" (Kenichiro Takahashi)
+Comment[fr]=« CARRÉ » par Kenichiro Takahashi
+Comment[ga]="SQUARE" le Kenichiro Takahashi
+Comment[gl]="Cadrado" por Kenichiro Takahashi
+Comment[he]="ערכה מרובעת" של Kenichiro Takahashi
+Comment[it]=«Quadrato» di Kenichiro Takahashi
+Comment[ja]=Kenichiro Takahashi 作の四角いテーマ
+Comment[ka]="კვადრატული" კენიჩირო ტაკაჰაშის მიერ
+Comment[lt]=„Kvadratinė tema“, sukurta Kenichiro Takahashi
+Comment[nl]="SQUARE" door Kenichiro Takahashi
+Comment[pt]="QUADRADO" por Kenichiro Takahashi
+Comment[pt_BR]="Quadrado" por Kenichiro Takahashi
+Comment[ru]=Сделал Kenichiro Takahashi
+Comment[sr]=„Квадрат“ од Кеничира Такахашија (Kenichiro Takahashi)
+Comment[sr@Latn]=„Kvadrat“ od Keničira Takahašija (Kenichiro Takahashi)
+Comment[sv]="KVADRATISK" av Kenichiro Takahashi
+Comment[ta]="SQUARE" கெனிசிரோ டகாஹாஷி
+Comment[tr]=Kenichiro Takahashi tarafından "KARE"
+Comment[uk]="КВАДРАТ" - Kenichiro Takahashi
+Comment[xx]=xx"SQUARE" by Kenichiro Takahashixx
+Comment[zh_CN]=“方块”,由 Kenichiro Takahashi 设计
+Comment[zh_TW]="方塊"主題,由 Kenichiro Takahashi 撰寫
diff --git a/konversation/images/nickicons/square/irc_admin.png b/konversation/images/nickicons/square/irc_admin.png
new file mode 100644
index 0000000..8c78d47
--- /dev/null
+++ b/konversation/images/nickicons/square/irc_admin.png
Binary files differ
diff --git a/konversation/images/nickicons/square/irc_away.png b/konversation/images/nickicons/square/irc_away.png
new file mode 100644
index 0000000..532b0d4
--- /dev/null
+++ b/konversation/images/nickicons/square/irc_away.png
Binary files differ
diff --git a/konversation/images/nickicons/square/irc_halfop.png b/konversation/images/nickicons/square/irc_halfop.png
new file mode 100644
index 0000000..6047bc6
--- /dev/null
+++ b/konversation/images/nickicons/square/irc_halfop.png
Binary files differ
diff --git a/konversation/images/nickicons/square/irc_normal.png b/konversation/images/nickicons/square/irc_normal.png
new file mode 100644
index 0000000..5a25719
--- /dev/null
+++ b/konversation/images/nickicons/square/irc_normal.png
Binary files differ
diff --git a/konversation/images/nickicons/square/irc_op.png b/konversation/images/nickicons/square/irc_op.png
new file mode 100644
index 0000000..a9dd9f5
--- /dev/null
+++ b/konversation/images/nickicons/square/irc_op.png
Binary files differ
diff --git a/konversation/images/nickicons/square/irc_owner.png b/konversation/images/nickicons/square/irc_owner.png
new file mode 100644
index 0000000..e9f51df
--- /dev/null
+++ b/konversation/images/nickicons/square/irc_owner.png
Binary files differ
diff --git a/konversation/images/nickicons/square/irc_voice.png b/konversation/images/nickicons/square/irc_voice.png
new file mode 100644
index 0000000..7aab216
--- /dev/null
+++ b/konversation/images/nickicons/square/irc_voice.png
Binary files differ
diff --git a/konversation/scripts/Makefile.am b/konversation/scripts/Makefile.am
new file mode 100644
index 0000000..77ce9cd
--- /dev/null
+++ b/konversation/scripts/Makefile.am
@@ -0,0 +1,4 @@
+scriptsdir=$(kde_datadir)/konversation/scripts
+scripts_SCRIPTS=bug fortune gauge uptime kdeversion cmd sayclip weather sysinfo media mail tinyurl
+scripts_DATA=fortunes.dat
+
diff --git a/konversation/scripts/README b/konversation/scripts/README
new file mode 100644
index 0000000..6efdefe
--- /dev/null
+++ b/konversation/scripts/README
@@ -0,0 +1,55 @@
+Here are some scripts that can help you in everyday life with konvi.
+
+They are usually put in ~/.kde/share/apps/konversation/scripts/ and executed by
+typing /exec <scriptname> in konversation.
+
+Since scripts are executed by system, they should have 'executable' flag on.
+This is most easily achieved by running chmod +x <scriptname>
+
+Please add descriptions here as new scripts appear:
+
+bug Opens up konqueror with kde bugzilla on specified bug number.
+ usage: /bug 65090
+
+gauge Displays funny "beer load" meter. Author: Eisfuchs, Idea: berkus.
+ usage: /gauge 55
+
+
+uptime Displays the system uptime on the current channel.
+ usage: /uptime
+
+fortune Displays a random fortune cookie
+ usage: /fortune
+
+kdeversion Displays the Qt/KDE version.
+ usage: /kdeversion
+
+cmd Prints the output of a given command.
+ usage: /cmd command
+
+sayclip Prints the contents of the clipboard on the current channel
+ with flood protection. Klipper must be running.
+ usage: /sayclip [pause-time]
+
+weather Displays current weather using KWeather applet.
+ usage: /weather
+
+colorizer Randomly colorizes the message.
+ usage: /colorize message
+
+media Plays the currently played media.
+ Supports amaroK,JuK,Noatun,Kaffeine
+ Usage: /media
+
+mail Print the number of unread emails in your inbox folder in
+ kmail. Kmail or kontact must be running.
+ Usage: /mail [folder-substring]
+
+google Use Google services
+ Usage: /google (--search) <query> # Search <query> in Google
+ /google --spell <word> # Spellcheck <word> using Google
+ /google -b|--browser <query> # Launch konqueror with google search for <query>
+
+qurl Display a short version of the url using qurl.net
+ Requires Ruby.
+ usage: /qurl http://some.very.long.url.com/
diff --git a/konversation/scripts/bug b/konversation/scripts/bug
new file mode 100755
index 0000000..0f5ec86
--- /dev/null
+++ b/konversation/scripts/bug
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+PORT=$1
+SERVER=$2
+TARGET=$3
+BUG=$4
+
+if [ ! $TARGET ]
+then
+ dcop $PORT default error "Can't write into status view."
+else
+ if [ -z $BUG ]
+ then
+ dcop $PORT default error "You forgot the bug number!"
+ else
+ kfmclient openURL http://bugs.kde.org/show_bug.cgi?id=$4
+ fi
+fi
diff --git a/konversation/scripts/cmd b/konversation/scripts/cmd
new file mode 100755
index 0000000..ebd7112
--- /dev/null
+++ b/konversation/scripts/cmd
@@ -0,0 +1,31 @@
+#!/usr/bin/env perl
+# Copyright (C) 2004 by İsmail Dönmez
+# Licensed under GPL v2 or later at your option
+
+$PORT= shift;
+$SERVER= shift;
+$TARGET= shift;
+
+my $i;
+my $command;
+
+if( $ARGV[0] eq "yes" ){
+ exec 'dcop', $PORT, 'default', 'error', 'Requested command is not executed!';
+}
+
+foreach $word (@ARGV) {
+ $command = $command." ".$word;
+}
+
+$ARG_MESSAGE = `exec $command`;
+
+foreach $entry (split(/\n/, $ARG_MESSAGE)) {
+ chomp $entry;
+ $i=1;
+ $entry =~ s/^\//\/\//;
+ system 'dcop', $PORT, 'default', 'say', $SERVER, $TARGET, $entry;
+}
+
+unless($i) {
+ exec 'dcop', $PORT, 'default', 'error', "Command @ARGV doesn't exist";
+}
diff --git a/konversation/scripts/fortune b/konversation/scripts/fortune
new file mode 100755
index 0000000..a3b61d3
--- /dev/null
+++ b/konversation/scripts/fortune
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+# Copyright (C) 2004 by İsmail Dönmez
+# Licensed under GPL v2 or later at your option
+
+$PORT = shift;
+$SERVER = shift;
+$TARGET = shift;
+
+sub RANDOM_INT ($$) {
+ my($min, $max) = @_;
+ return $min if $min == $max;
+ ($min, $max) = ($max, $min) if $min > $max;
+ return $min + int rand(1 + $max - $min);
+}
+
+open(FORTUNES,"fortunes.dat") or die("Could not open fortunes file!");
+
+while (<FORTUNES>) {
+ chomp;
+ ++$TOTAL_LINES;
+}
+
+seek(FORTUNES,0,0);
+
+srand;
+$LINE = RANDOM_INT(0,$TOTAL_LINES - 5);
+
+$MESSAGE = "4Random Fortune: ";
+
+while (<FORTUNES>) {
+ $LINE_COUNT++;
+
+ if ( !$START && $LINE_COUNT >= $LINE ) {
+ if ( $_ eq "\%\n" ) {
+ $START = 1;
+ next;
+ }
+ next;
+ }
+
+ elsif ( $START ) {
+ if( $_ eq "\%\n" ) {
+ last;
+ }
+ else {
+ chomp;
+ s/(\s)+/$1/g;
+ $MESSAGE .= $_." ";
+ }
+ }
+}
+close(FORTUNES);
+exec 'dcop', $PORT, 'default', 'say', $SERVER, $TARGET, $MESSAGE;
diff --git a/konversation/scripts/fortunes.dat b/konversation/scripts/fortunes.dat
new file mode 100644
index 0000000..756f18a
--- /dev/null
+++ b/konversation/scripts/fortunes.dat
@@ -0,0 +1,680 @@
+%
+Documentation is like sex: when it is good, it is very, very good; and when it is bad, it is better than nothing.
+%
+Let's call it an accidental feature.
+ -- Larry Wall
+%
+I did this 'cause Linux gives me a woody. It doesn't generate revenue.
+ -- Dave '-ddt->` Taylor, announcing DOOM for Linux
+%
+Feel free to contact me (flames about my english and the useless of this
+driver will be redirected to /dev/null, oh no, it's full...).
+ -- Michael Beck, describing the PC-speaker sound device
+%
+lp1 on fire
+ -- One of the more obfuscated kernel messages
+%
+A Linux machine! Because a 486 is a terrible thing to waste!
+ -- Joe Sloan, jjs@wintermute.ucr.edu
+%
+Microsoft is not the answer.
+Microsoft is the question.
+NO (or Linux) is the answer.
+ -- Taken from a .signature from someone from the UK, source unknown
+%
+In most countries selling harmful things like drugs is punishable.
+Then howcome people can sell Microsoft software and go unpunished?
+ -- Hasse Skrifvars, hasku@rost.abo.fi,
+%
+Windows without the X is like making love without a partner.
+Sex, Drugs & Linux Rules
+win-nt from the people who invented edlin.
+Apples have meant trouble since eden.
+Linux, the way to get rid of boot viruses
+ -- MaDsen Wikholm, mwikholm@at8.abo.fi
+%
+Once upon a time there was a DOS user who saw Unix, and saw that it was
+good. After typing cp on his DOS machine at home, he downloaded GNU's
+unix tools ported to DOS and installed them. He rm'd, cp'd, and mv'd
+happily for many days, and upon finding elvis, he vi'd and was happy. After
+a long day at work (on a Unix box) he came home, started editing a file,
+and couldn't figure out why he couldn't suspend vi (w/ ctrl-z) to do
+a compile.
+ -- Erik Troan, ewt@tipper.oit.unc.edu
+%
+We are MicroSoft. You will be assimilated. Resistance is futile.
+ -- Attributed to B.G., Gill Bates
+%
+Avoid the Gates of Hell. Use Linux
+ -- unknown source
+%
+Intel engineering seem to have misheard Intel marketing strategy. The
+phrase was "Divide and conquer" not "Divide and cock up"
+ -- Alan Cox, iialan@www.linux.org.uk
+%
+Linux! Guerrilla UNIX Development Venimus, Vidimus, Dolavimus.
+ -- Mark A. Horton KA4YBR, mah@ka4ybr.com
+%
+"Who is General Failure and why is he reading my hard disk?"
+Microsoft spel chekar vor sail, worgs grate !!
+ -- Felix von Leitner, leitner@inf.fu-berlin.de
+%
+Personally, I think my choice in the mostest-superlative-computer wars has to
+be the HP-48 series of calculators. They'll run almost anything. And if they
+can't, while I'll just plug a Linux box into the serial port and load up the
+HP-48 VT-100 emulator.
+ -- Jeff Dege, jdege@winternet.com
+%
+There are no threads in a.b.p.erotica, so there's no gain in using a
+threaded news reader.
+ -- unknown source
+%
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+*/
+die_if_kernel("Oops", regs, error_code);
+ -- From linux/arch/i386/mm/fault.c
+%
+Linux: because a PC is a terrible thing to waste
+ -- ksh@cis.ufl.edu put this on Tshirts in '93
+%
+Linux: the choice of a GNU generation
+ -- ksh@cis.ufl.edu put this on Tshirts in '93
+%
+There are two types of Linux developers - those who can spell, and
+those who can't. There is a constant pitched battle between the two.
+ -- From one of the post-1.1.54 kernel update messages posted to c.o.l.a
+%
+When you say "I wrote a program that crashed Windows", people just stare at
+you blankly and say "Hey, I got those with the system, *for free*".
+ -- Linus Torvalds
+%
+We come to bury DOS, not to praise it.
+ -- Paul Vojta, vojta@math.berkeley.edu
+%
+Be warned that typing killall name may not have the desired
+effect on non-Linux systems, especially when done by a privileged user.
+ -- From the killall manual page
+%
+Note that if I can get you to "su and say" something just by asking,
+you have a very serious security problem on your system and you should
+look into it.
+ -- Paul Vixie, vixie-cron 3.0.1 installation notes
+%
+How should I know if it works? That's what beta testers are for. I
+only coded it.
+ -- Attributed to Linus Torvalds, somewhere in a posting
+%
+I develop for Linux for a living, I used to develop for DOS.
+Going from DOS to Linux is like trading a glider for an F117.
+ -- Lawrence Foard, entropy@world.std.com
+%
+Absolutely nothing should be concluded from these figures except that
+no conclusion can be drawn from them.
+ -- Joseph L. Brothers, Linux/PowerPC Project)
+%
+If the future navigation system [for interactive networked services on
+the NII] looks like something from Microsoft, it will never work.
+ -- Chairman of Walt Disney Television & Telecommunications
+%
+Problem solving under Linux has never been the circus that it is under
+AIX.
+ -- Pete Ehlke in comp.unix.aix
+%
+I don't know why, but first C programs tend to look a lot worse than
+first programs in any other language (maybe except for fortran, but then
+I suspect all fortran programs look like `firsts')
+ -- Olaf Kirch
+%
+On a normal ascii line, the only safe condition to detect is a 'BREAK'
+- everything else having been assigned functions by Gnu EMACS.
+ -- Tarl Neustaedter
+%
+By golly, I'm beginning to think Linux really *is* the best thing since
+sliced bread.
+ -- Vance Petree, Virginia Power
+%
+I'd crawl over an acre of 'Visual This++' and 'Integrated Development
+That' to get to gcc, Emacs, and gdb. Thank you.
+ -- Vance Petree, Virginia Power
+%
+Oh, I've seen copies [of Linux Journal] around the terminal room at The Labs.
+ -- Dennis Ritchie
+%
+If you want to travel around the world and be invited to speak at a lot
+of different places, just write a Unix operating system.
+ -- Linus Torvalds
+%
+...and scantily clad females, of course. Who cares if it's below zero
+outside.
+ -- Linus Torvalds
+%
+...you might as well skip the Xmas celebration completely, and instead
+sit in front of your linux computer playing with the all-new-and-improved
+linux kernel version.
+ -- Linus Torvalds
+%
+Besides, I think Slackware sounds better than 'Microsoft,' don't you?
+ -- Patrick Volkerding
+%
+All language designers are arrogant. Goes with the territory...
+ -- Larry Wall
+%
+And the next time you consider complaining that running Lucid Emacs
+19.05 via NFS from a remote Linux machine in Paraguay doesn't seem to
+get the background colors right, you'll know who to thank.
+ -- Matt Welsh
+%
+Are Linux users lemmings collectively jumping off of the cliff of
+reliable, well-engineered commercial software?
+ -- Matt Welsh
+%
+Even more amazing was the realization that God has Internet access. I
+wonder if He has a full newsfeed?
+ -- Matt Welsh
+%
+I once witnessed a long-winded, month-long flamewar over the use of
+mice vs. trackballs... It was very silly.
+ -- Matt Welsh
+%
+Linux poses a real challenge for those with a taste for late-night
+hacking (and/or conversations with God).
+ -- Matt Welsh
+%
+What you end up with, after running an operating system concept through
+these many marketing coffee filters, is something not unlike plain hot
+water.
+ -- Matt Welsh
+%
+...Deep Hack Mode -- that mysterious and frightening state of
+consciousness where Mortal Users fear to tread.
+ -- Matt Welsh
+%
+...Unix, MS-DOS, and Windows NT (also known as the Good, the Bad, and
+the Ugly).
+ -- Matt Welsh
+%
+...very few phenomena can pull someone out of Deep Hack Mode, with two
+noted exceptions: being struck by lightning, or worse, your *computer*
+being struck by lightning.
+ -- Matt Welsh
+%
+..you could spend *all day* customizing the title bar. Believe me. I
+speak from experience.
+ -- Matt Welsh
+%
+[In 'Doctor' mode], I spent a good ten minutes telling Emacs what I
+thought of it. (The response was, 'Perhaps you could try to be less
+abusive.')
+ -- Matt Welsh
+%
+I would rather spend 10 hours reading someone else's source code than
+10 minutes listening to Musak waiting for technical support which isn't.
+ -- Dr. Greg Wettstein, Roger Maris Cancer Center
+%
+...[Linux's] capacity to talk via any medium except smoke signals.
+ -- Dr. Greg Wettstein, Roger Maris Cancer Center
+%
+Whip me. Beat me. Make me maintain AIX.
+ -- Stephan Zielinski
+%
+Your job is being a professor and researcher: That's one hell of a good excuse
+for some of the brain-damages of minix.
+ -- Linus Torvalds to Andrew Tanenbaum
+%
+I still maintain the point that designing a monolithic kernel in 1991 is a
+fundamental error. Be thankful you are not my student. You would not get a
+high grade for such a design :-)
+ -- Andrew Tanenbaum to Linus Torvalds
+%
+We use Linux for all our mission-critical applications. Having the source code
+means that we are not held hostage by anyone's support department.
+ -- Russell Nelson, President of Crynwr Software
+%
+Linux is obsolete
+ -- Andrew Tanenbaum
+%
+Dijkstra probably hates me.
+ -- Linus Torvalds, in kernel/sched.c
+%
+And 1.1.81 is officially BugFree(tm), so if you receive any bug-reports
+on it, you know they are just evil lies.
+ -- Linus Torvalds
+%
+We are Pentium of Borg. Division is futile. You will be approximated.
+ -- seen in someone's .signature
+%
+Linux: the operating system with a CLUE... Command Line User Environment.
+ -- seen in a posting in comp.software.testing
+%
+quit When the quit statement is read, the bc processor
+ is terminated, regardless of where the quit state-
+ ment is found. For example, "if (0 == 1) quit"
+ will cause bc to terminate.
+ -- seen in the manpage for "bc". Note the "if" statement's logic
+%
+Sic transit discus mundi
+ -- From the System Administrator's Guide, by Lars Wirzenius
+%
+Sigh. I like to think it's just the Linux people who want to be on
+the "leading edge" so bad they walk right off the precipice.
+ -- Craig E. Groeschel
+%
+We all know Linux is great... it does infinite loops in 5 seconds.
+ - Linus Torvalds about the superiority of Linux on the Amterdam Linux Symposium
+%
+Waving away a cloud of smoke, I look up, and am blinded by a bright, white
+light. It's God. No, not Richard Stallman, or Linus Torvalds, but God. In
+a booming voice, He says: "THIS IS A SIGN. USE LINUX, THE FREE UNIX SYSTEM
+FOR THE 386.
+ -- Matt Welsh
+%
+The chat program is in public domain. This is not the GNU public license.
+If it breaks then you get to keep both pieces.
+ -- Copyright notice for the chat program
+%
+'Mounten' wird fr drei Dinge benutzt: 'Aufsitzen' auf Pferde, 'einklinken'
+von Festplatten in Dateisysteme, und, nun, 'besteigen' beim Sex.
+ -- Christa Keil
+%
+Manchmal stehe nachts auf und installier's mir einfach...
+ -- H0arry @ IRC
+%
+'Mounting' is used for three things: climbing on a horse, linking in a
+hard disk unit in data systems, and, well, mounting during sex.
+ -- Christa Keil
+%
+We are using Linux daily to UP our productivity - so UP yours!
+ -- Adapted from Pat Paulsen by Joe Sloan
+%
+But what can you do with it?
+ -- ubiquitous cry from Linux-user partner
+%
+/*
+ * [...] Note that 120 sec is defined in the protocol as the maximum
+ * possible RTT. I guess we'll have to use something other than TCP
+ * to talk to the University of Mars.
+ * PAWS allows us longer timeouts and large windows, so once implemented
+ * ftp to mars will work nicely.
+ */
+ -- from /usr/src/linux/net/inet/tcp.c, concerning RTT [round trip time]
+%
+DOS: n., A small annoying boot virus that causes random spontaneous system
+ crashes, usually just before saving a massive project. Easily cured by
+ UNIX. See also MS-DOS, IBM-DOS, DR-DOS.
+ -- David Vicker's .plan
+%
+MSDOS didn't get as bad as it is overnight -- it took over ten years
+of careful development.
+ -- dmeggins@aix1.uottawa.ca
+%
+LILO, you've got me on my knees!
+ -- David Black, dblack@pilot.njin.net, with apologies to Derek and the
+Dominos, and Werner Almsberger
+%
+I've run DOOM more in the last few days than I have the last few
+months. I just love debugging ;-)
+ -- Linus Torvalds
+%
+Microsoft Corp., concerned by the growing popularity of the free 32-bit
+operating system for Intel systems, Linux, has employed a number of top
+programmers from the underground world of virus development. Bill Gates stated
+yesterday: "World domination, fast -- it's either us or Linus". Mr. Torvalds
+was unavailable for comment ...
+ -- Robert Manners, rjm@swift.eng.ox.ac.uk, in comp.os.linux.setup
+%
+The only "intuitive" interface is the nipple. After that, it's all learned.
+ -- Bruce Ediger, bediger@teal.csn.org, on X interfaces
+%
+After watching my newly-retired dad spend two weeks learning how to make a new
+folder, it became obvious that "intuitive" mostly means "what the writer or
+speaker of intuitive likes".
+ -- Bruce Ediger, bediger@teal.csn.org, on X the intuitiveness of a Mac interface
+%
+Now I know someone out there is going to claim, "Well then, UNIX is intuitive,
+because you only need to learn 5000 commands, and then everything else follows
+from that! Har har har!"
+ -- Andy Bates on "intuitive interfaces", slightly defending Macs
+%
+> No manual is ever necessary.
+May I politely interject here: BULLSHIT. That's the biggest Apple lie of all!
+ -- Discussion in comp.os.linux.misc on the intuitiveness of interfaces
+%
+How do I type "for i in *.dvi do xdvi $i done" in a GUI?
+ -- Discussion in comp.os.linux.misc on the intuitiveness of interfaces
+%
+>Ever heard of .cshrc?
+That's a city in Bosnia. Right?
+ -- Discussion in comp.os.linux.misc on the intuitiveness of commands
+%
+Who wants to remember that escape-x-alt-control-left shift-b puts you into
+super-edit-debug-compile mode?
+ -- Discussion on the intuitiveness of commands, especially Emacs
+%
+Anyone who thinks UNIX is intuitive should be forced to write 5000 lines of
+code using nothing but vi or emacs. AAAAACK!
+ -- Discussion on the intuitiveness of commands, especially Emacs
+%
+Now, it we had this sort of thing:
+ yield -a for yield to all traffic
+ yield -t for yield to trucks
+ yield -f for yield to people walking (yield foot)
+ yield -d t* for yield on days starting with t
+
+...you'd have a lot of dead people at intersections, and traffic jams you
+wouldn't believe...
+ -- Discussion on the intuitiveness of commands
+%
+Actually, typing random strings in the Finder does the equivalent of
+filename completion.
+ -- Discussion on file completion vs. the Mac Finder
+%
+Not me, guy. I read the Bash man page each day like a Jehovah's Witness reads
+the Bible. No wait, the Bash man page IS the bible. Excuse me...
+ -- More on confusing aliases, taken from comp.os.linux.misc
+%
+On the Internet, no one knows you're using Windows NT
+ -- Submitted by Ramiro Estrugo, restrugo@fateware.com
+%
+> I'm an idiot.. At least this [bug] took about 5 minutes to find..
+Disquieting ...
+ -- Gonzalo Tornaria in response to Linus Torvalds's
+%
+> I'm an idiot.. At least this [bug] took about 5 minutes to find..
+We need to find some new terms to describe the rest of us mere mortals
+then.
+ -- Craig Schlenter in response to Linus Torvalds's
+%
+> I'm an idiot.. At least this [bug] took about 5 minutes to find..
+Surely, Linus is talking about the kind of idiocy that others aspire to :-).
+ -- Bruce Perens in response to Linus Torvalds's
+%
+Never make any mistaeks.
+ -- Anonymous, in a mail discussion about to a kernel bug report
+%
++#if defined(__alpha__) && defined(CONFIG_PCI)
++ /*
++ * The meaning of life, the universe, and everything. Plus
++ * this makes the year come out right.
++ */
++ year -= 42;
++#endif
+ -- From the patch for 1.3.2: (kernel/time.c), submitted by Marcus Meissner
+%
+As usual, this being a 1.3.x release, I haven't even compiled this
+kernel yet. So if it works, you should be doubly impressed.
+ -- Linus Torvalds, announcing kernel 1.3.3
+%
+People disagree with me. I just ignore them.
+ -- Linus Torvalds, regarding the use of C++ for the Linux kernel
+%
+It's now the GNU Emacs of all terminal emulators.
+ -- Linus Torvalds, regarding the fact that Linux started off as a terminal emulator
+%
+Audience: What will become of Linux when the Hurd is ready?
+Eric Youngdale: Err... is Richard Stallman here?
+ -- From the Linux conference in spring '95, Berlin
+%
+Linux: The OS people choose without $200,000,000 of persuasion.
+ -- Mike Coleman
+%
+The memory management on the PowerPC can be used to frighten small children.
+ -- Linus Torvalds
+%
+... faster BogoMIPS calculations (yes, it now boots 2 seconds faster than
+it used to: we're considering changing the name from "Linux" to "InstaBOOT"
+ -- Linus, in the announcement for 1.3.26
+%
+... of course, this probably only happens for tcsh which uses wait4(),
+which is why I never saw it. Serves people who use that abomination
+right 8^)
+ -- Linus Torvalds, about a patch that fixes getrusage for 1.3.26
+%
+It's a bird..
+It's a plane..
+No, it's KernelMan, faster than a speeding bullet, to your rescue.
+Doing new kernel versions in under 5 seconds flat..
+ -- Linus, in the announcement for 1.3.27
+%
+Eh, that's it, I guess. No 300 million dollar unveiling event for this
+kernel, I'm afraid, but you're still supposed to think of this as the
+"happening of the century" (at least until the next kernel comes along).
+ -- Linus, in the announcement for 1.3.27
+%
+Oh, and this is another kernel in that great and venerable "BugFree(tm)"
+series of kernels. So be not afraid of bugs, but go out in the streets
+and deliver this message of joy to the masses.
+ -- Linus, in the announcement for 1.3.27
+%
+When you say 'I wrote a program that crashed Windows', people just stare at
+you blankly and say 'Hey, I got those with the system, *for free*'.
+ -- Linus Torvalds
+%
+Never trust an operating system you don't have sources for. ;-)
+ -- Unknown source
+%
+> Linux is not user-friendly.
+It _is_ user-friendly. It is not ignorant-friendly and idiot-friendly.
+ -- Seen somewhere on the net
+%
+Keep me informed on the behaviour of this kernel.. As the "BugFree(tm)"
+series didn't turn out too well, I'm starting a new series called the
+"ItWorksForMe(tm)" series, of which this new kernel is yet another
+shining example.
+ -- Linus, in the announcement for 1.3.29
+%
+Seriously, the way I did this was by using a special /sbin/loader binary
+with debugging hooks that I made ("dd" is your friend: binary editors
+are for wimps).
+ -- Linus Torvalds, in an article on a dnserver
+%
+(I tried to get some documentation out of Digital on this, but as far as
+I can tell even _they_ don't have it ;-)
+ -- Linus Torvalds, in an article on a dnserver
+%
+Q: Why shouldn't I simply delete the stuff I never use, it's just taking up
+ space?
+A: This question is in the category of Famous Last Words..
+ -- From the Frequently Unasked Questions
+%
+Q: What's the big deal about rm, I have been deleting stuff for years? And
+ never lost anything.. oops!
+A: ...
+ -- From the Frequently Unasked Questions
+%
+Linux is addictive, I'm hooked!
+ -- MaDsen Wikholm's .sig
+%
+panic("Foooooooood fight!");
+ -- In the kernel source aha1542.c, after detecting a bad segment list
+%
+Convention organizer to Linus Torvalds: "You might like to come with us
+to some licensed[1] place, and have some pizza."
+
+Linus: "Oh, I did not know that you needed a license to eat pizza".
+
+[1] Licenced - refers in Australia to a restaurant which has government
+licence to sell liquor.
+ -- Linus at a talk at the Melbourne University
+%
+Footnotes are for things you believe don't really belong in LDP manuals,
+but want to include anyway.
+ -- Joel N. Weber II discussing the 'make' chapter of LPG
+%
+Eh, that's it, I guess. No 300 million dollar unveiling event for this
+kernel, I'm afraid, but you're still supposed to think of this as the
+"happening of the century" (at least until the next kernel comes along).
+Oh, and this is another kernel in that great and venerable "BugFree(tm)"
+series of kernels. So be not afraid of bugs, but go out in the streets
+and deliver this message of joy to the masses.
+ -- Linus Torvalds, on releasing 1.3.27
+%
+Ok, I'm just uploading the new version of the kernel, v1.3.33, also
+known as "the buggiest kernel ever".
+ -- Linus Torvalds
+%
+Go not unto the Usenet for advice, for you will be told both yea and nay (and
+quite a few things that just have nothing at all to do with the question).
+ -- seen in a .sig somewhere
+%
+Those who don't understand Linux are doomed to reinvent it, poorly.
+ -- unidentified source
+%
+Look, I'm about to buy me a double barreled sawed off shotgun and show
+Linus what I think about backspace and delete not working.
+ -- some anonymous .signature
+%
+We apologize for the inconvenience, but we'd still like yout to test out
+this kernel.
+ -- Linus Torvalds, announcing another kernel patch
+%
+The new Linux anthem will be "He's an idiot, but he's ok", as performed by
+Monthy Python. You'd better start practicing.
+ -- Linus Torvalds, announcing another kernel patch
+%
+How do you power off this machine?
+ -- Linus, when upgrading linux.cs.helsinki.fi, and after using the machine for several months
+%
+Excusing bad programming is a shooting offence, no matter _what_ the
+circumstances.
+ -- Linus Torvalds, to the linux-kernel list
+%
+Linus? Whose that?
+ -- clueless newbie on #Linux
+%
+Whoa...I did a 'zcat /vmlinuz > /dev/audio' and I think I heard God...
+ -- mikecd on #Linux
+%
+Some people have told me they don't think a fat penguin really embodies the
+grace of Linux, which just tells me they have never seen a angry penguin
+charging at them in excess of 100mph. They'd be a lot more careful about what
+they say if they had.
+ -- Linus Torvalds, announcing Linux v2.0
+%
+MS-DOS, you can't live with it, you can live without it.
+ -- from Lars Wirzenius' .sig
+%
+.. I used to get in more fights with SCO than I did my girlfriend, but
+now, thanks to Linux, she has more than happily accepted her place back at
+number one antagonist in my life..
+ -- Jason Stiefel, krypto@s30.nmex.com
+%
+I mean, well, if it were not for Linux I might be roaming the streets looking
+for drugs or prostitutes or something. Hannu and Linus have my highest
+admiration (apple polishing mode off).
+ -- Phil Lewis, plewis@nyx.nyx.net
+%
+> What does ELF stand for (in respect to Linux?)
+ELF is the first rock group that Ronnie James Dio performed with back in
+the early 1970's. In constrast, a.out is a misspelling of the French word
+for the month of August. What the two have in common is beyond me, but
+Linux users seem to use the two words together.
+ -- seen on c.o.l.misc
+%
+"Linux was made by foreign terrorists to take money from true US companies
+like Microsoft." - Some AOL'er.
+"To this end we dedicate ourselves..." -Don
+ -- From the sig of "Don", don@cs.byu.edu
+%
+Shoot me again.
+Just proving that the quickest way to solve the problem is to post a
+whine to the newsgroups: within moments the solution presents itself to
+me, and meanwhile my ass is hanging out on the Net... *sigh*...
+ -- Dave Phillips, dlphilp@bright.net, about problem solving via news
+%
+Besides, its really not worthwhile to use more than two times your physical
+ram in swap (except in a select few situations). The performance of the system
+becomes so abysmal you'd rather heat pins under your toenails while reciting
+Windows95 source code and staring at porn flicks of Bob Dole than actually try
+to type something.
+ -- seen on c.o.l.development.system, about the size of the swap space
+%
+Only wimps use tape backup: _real_ men just upload their important stuff
+on ftp, and let the rest of the world mirror it ;)
+ -- Linus Torvalds, about his failing hard drive on linux.cs.helsinki.fi
+%
+One of the things that hamper Linux's climb to world domination is the
+shortage of bad Computer Role Playing Games, or CRaPGs. No operating system
+can be considered respectable without one.
+ -- Brian O'Donnell, odonnllb@tcd.ie
+%
+The game, anoraks.2.0.0.tgz, will be available from sunsite until somebody
+responsible notices it and deletes it, and shortly from
+ftp.mee.tcd.ie/pub/Brian, though they don't know that yet.
+ -- Brian O'Donnell, odonnllb@tcd.ie
+%
+'Ooohh.. "FreeBSD is faster over loopback, when compared to Linux
+over the wire". Film at 11.'
+ -- Linus Torvalds
+%
+Q: Would you like to see the WINE list?
+A: What's on it, anything expensive?
+Q: No, just Solitaire and MineSweeper for now, but the WINE is free.
+ -- Kevin M. Bealer, about the WINdows Emulator
+%
+So in the future, one 'client' at a time or you'll be spending CPU time with
+lots of little 'child processes'.
+ -- Kevin M. Bealer, commenting on the private life of a Linux nerd
+%
+By the way, I can hardly feel sorry for you... All last night I had to listen
+to her tears, so great they were redirected to a stream. What? Of _course_
+you didn't know. You and your little group no longer have any permissions
+around here. She changed her .lock files, too.
+ -- Kevin M. Bealer, commenting on the private life of a Linux nerd
+%
+We should start referring to processes which run in the background by their
+correct technical name... paenguins.
+ -- Kevin M. Bealer, commenting on the penguin Linux logo
+%
+We can use symlinks of course... syslogd would be a symlink to syslogp and
+ftpd and ircd would be linked to ftpp and ircp... and of course the
+point-to-point protocal paenguin.
+ -- Kevin M. Bealer, commenting on the penguin Linux logo
+%
+This is a logical analogy too... anyone who's been around, knows the world is
+run by paenguins. Always a paenguin behind the curtain, really getting things
+done. And paenguins in politics--who can deny it?
+ -- Kevin M. Bealer, commenting on the penguin Linux logo
+%
+Linux: Where Don't We Want To Go Today?
+ -- Submitted by Pancrazio De Mauro, paraphrasing some well-known sales talk
+%
+The most important design issue... is the fact that Linux is supposed to
+be fun...
+ -- Linus Torvalds at the First Dutch International Symposium on Linux
+%
+In short, at least give the penguin a fair viewing. If you still don't
+like it, that's ok: that's why I'm boss. I simply know better than you do.
+ -- Linus "what, me arrogant?" Torvalds, on c.o.l.advocacy
+%
+<SomeLamer> what's the difference between chattr and chmod?
+<SomeGuru> SomeLamer: man chattr > 1; man chmod > 2; diff -u 1 2 | less
+ -- Seen on #linux on irc
+%
+The linuX Files -- The Source is Out There.
+ -- Sent in by Craig S. Bell, goat@aracnet.com
+%
+"... being a Linux user is sort of like living in a house inhabited
+by a large family of carpenters and architects. Every morning when
+you wake up, the house is a little different. Maybe there is a new
+turret, or some walls have moved. Or perhaps someone has temporarily
+removed the floor under your bed." - Unix for Dummies, 2nd Edition
+ -- found in the .sig of Rob Riggs, rriggs@tesser.com
+%
+C is quirky, flawed, and an enormous success
+ -- Dennis M. Ritchie
+%
+If Bill Gates is the Devil then Linus Torvalds must be the Messiah.
+ -- Unknown source
+%
+Vini, vidi, Linux!
+ -- Unknown source
+%
+The good thing about standards is that there are so many to choose from.
+ -- Andrew S. Tanenbaum
+%
+I'm telling you that the kernel is stable not because it's a kernel,
+but because I refuse to listen to arguments like this.
+ -- Linus Torvalds
+% \ No newline at end of file
diff --git a/konversation/scripts/gauge b/konversation/scripts/gauge
new file mode 100755
index 0000000..2befb87
--- /dev/null
+++ b/konversation/scripts/gauge
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+
+PORT=$1
+SERVER=$2
+TARGET=$3
+
+shift
+shift
+shift
+
+PERCENTAGE=$1
+
+if [ ! $TARGET ]
+then
+ dcop $PORT default error "Can't write into status view."
+else
+ if [ ! $PERCENTAGE ]
+ then
+ dcop $PORT default error "USAGE: $0 <percentage>"
+ else
+ PERCENTAGE=`echo $PERCENTAGE | sed 's/^0\+//'`
+ LEFT=$(($PERCENTAGE/5))
+ RIGHT=$((20-$LEFT))
+
+ if [[ $PERCENTAGE -lt 0 ]]; then
+ dcop $PORT default error "Percentage has to be bigger than 0"
+ exit
+ fi
+
+ if [[ $PERCENTAGE -gt 100 ]]; then
+ dcop $PORT default error "Percentage has to be smaller than 100"
+ exit
+ fi
+
+
+ if [ $PERCENTAGE = 50 ]
+ then
+ METER="|"
+ else
+ if [[ $PERCENTAGE -lt 50 ]]
+ then
+ METER="\\"
+ else
+ METER="/"
+ fi
+ fi
+
+ for (( i=$LEFT ; $i != 0 ; i-- ))
+ do
+ OUTPUT="$OUTPUT,"
+ done
+
+ OUTPUT="$OUTPUT$METER"
+
+ for (( i=$RIGHT ; $i != 0 ; i-- ))
+ do
+ OUTPUT="$OUTPUT,"
+ done
+
+ OUTPUT=`echo $OUTPUT | sed 's/,/ /g'`
+
+ OUTPUT="[$OUTPUT] $PERCENTAGE%"
+
+ if [ $PERCENTAGE = 100 ]
+ then
+ OUTPUT="$OUTPUT *ding*"
+ fi
+
+ dcop $PORT default say $SERVER "$TARGET" "Beer load $OUTPUT"
+ fi
+fi
diff --git a/konversation/scripts/kdeversion b/konversation/scripts/kdeversion
new file mode 100755
index 0000000..34fef02
--- /dev/null
+++ b/konversation/scripts/kdeversion
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# Prints the KDE version.
+
+PORT=$1;
+SERVER=$2;
+TARGET=$3;
+
+kde-config --version | while read line; do dcop $PORT default say $SERVER "$TARGET" "$line"; done
diff --git a/konversation/scripts/mail b/konversation/scripts/mail
new file mode 100755
index 0000000..b5882b1
--- /dev/null
+++ b/konversation/scripts/mail
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+#
+# Licensed under GPL v2 or later at your option
+# Copyright 2005 by John Tapsell <john.tapsell@kdemail.net>
+
+PORT=$1;
+SERVER=$2;
+TARGET=$3;
+
+FOLDER=$4;
+
+
+if [ "$FOLDER" = "" ] ; then
+ FOLDER="inbox"
+fi
+
+
+getmails()
+{
+
+ dcop kmail > /dev/null || {
+ dcop $PORT default info "Sorry kmail is not running"
+ exit
+ }
+
+ for f in $(dcop kmail KMailIface folderList) ; do
+ MAILFOLDER=$(dcop $(dcop kmail KMailIface getFolder $f) "displayPath" | grep -i $FOLDER)
+ if [ "$MAILFOLDER" != "" ] ; then
+ FOUNDFOLDER=1
+ MAILCOUNT=$(dcop $(dcop kmail KMailIface getFolder $f ) "unreadMessages()" )
+ MAILTOTALCOUNT=$(dcop $(dcop kmail KMailIface getFolder $f ) "messages()" )
+ MAILTOTALCOUNT=$(($MAILCOUNT + $MAILTOTALCOUNT))
+ if [[ -z "$MAILCOUNT" ]] ; then MAILCOUNT = "0" ; fi
+ if [ "$MAILCOUNT" != 0 ] ; then
+ FOUNDEMAIL=1
+ echo "Email folder %B$MAILFOLDER%B has %B$MAILCOUNT%B unread messages, out of %B$MAILTOTALCOUNT%B"
+ fi
+ fi
+ done
+
+ if [[ -z "$FOUNDEMAIL" ]] ; then
+ if [[ -z "$FOUNDFOLDER" ]] ; then
+ dcop $PORT default info "No email folders were found that had a name containing '$FOLDER'"
+ exit
+ else
+ echo "No new emails in any folders matching '$FOLDER'"
+ fi
+ fi
+
+}
+
+if [[ -z "$3" ]] ; then
+ echo "Scripts are not meant to be called from the command line."
+ echo "This script should be installed to $KDEDIR/share/apps/konversation/scripts"
+ echo "Then executed with /script from the konversation"
+ echo
+ echo The output is:
+ getmails
+ exit
+fi
+
+#LINECOUNT=$(getmails | wc -l)
+#if [[ "$LINECOUNT" -gt 5 ]] ; then
+# dcop $PORT default info "There are more than 5 matches. Cancelling to avoid flooding the server"
+# exit
+#fi
+
+getmails | head -n 3 | while read line; do dcop $PORT default say $SERVER "$TARGET" "$line"; done
+
diff --git a/konversation/scripts/media b/konversation/scripts/media
new file mode 100755
index 0000000..e369262
--- /dev/null
+++ b/konversation/scripts/media
@@ -0,0 +1,484 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#This program is free software; you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation; either version 2 of the License, or
+#(at your option) any later version.
+
+# Copyright 2006 Eli J. MacKenzie
+# Inspired by `media` (Copyright 2005 İsmail Dönmez)
+
+
+# If you wish to customize the formatting strings, do so in this table.
+# Do not change the numbers unless you're changing the logic.
+# Title, artist, and album will be set once the player is queried.
+# See Player.format() for how these are used.
+
+
+#Changing these 3 values will likely cause the script to fail
+Title =4
+Artist=2
+Album =1
+
+#To disable self-titled (eponymous) checking, subtract 8
+SelfTitled=11
+
+outputFormat="/me $intro $info [$player]"
+formatStrings = {
+ Title+SelfTitled : "$title by $artist (eponymous)",
+ SelfTitled : "${artist}'s self-titled album",
+ Title+Artist+Album : "$title by $artist on $album", #7,15
+ Title+Artist : "$title by $artist", #6,14
+ Title+Album : "$title from $album", #5,13
+ Album+Artist : "$album by $artist", #3,11
+ Title : "$title", #4,12
+ Artist : "$artist", #2,10
+ Album : "$album", #1,9
+}
+
+#Intro defaults to first type the player supports when a specific type was not demanded
+formatVariables={'audio': 'is listening to', 'video': 'is watching'}
+
+## Static player ranking list
+## If you add a new player, you must add it here or it won't get checked when in audio-only or video-only modes.
+playerRankings= {
+ 'video' :['kaffeine','kmplayer', 'kplayer', 'noatun', 'kdetv'],
+ 'audio' :['amarok', 'MPD' 'juk', 'noatun', 'kscd', 'kaffeine', 'kmplayer', 'Audacious', 'xmms', 'yammi']
+}
+
+## Title, album and artist fields to be quoted depending on contents
+# List the possible trigger characters here.
+# If you want a '-', it must be first. if you want a '^', it must be last.
+SIMPLE_FIXUP = '' #I use ' '
+
+# If you want to use a regex for the above, specify it here in which case it will be used
+REGEX_FIXUP = ''
+
+# Quote chars to use:
+QUOTE_BEFORE = '"'
+QUOTE_AFTER = '"'
+
+
+ ###############################
+ ## The Real work is done below
+#############################
+
+import os
+import sys
+import re
+import string
+
+try:
+ APP_ID = sys.argv[1]
+ IRC_SERVER = sys.argv[2]
+ TARGET = sys.argv[3]
+except IndexError:
+ print >>sys.stderr, "This script is intended to be run from within Konversation."
+ sys.exit(0)
+
+if (sys.hexversion >> 16) < 0x0204:
+ err="The media script requires Python 2.4."
+ os.popen('dcop %s default error "%s"' %(APP_ID,err))
+ sys.exit(err)
+
+import subprocess
+
+# Python 2.5 has this ...
+try:
+ any(())
+except NameError:
+ def any(data):
+ """Return true of any of the items in the sequence 'data' are true.
+
+ (ie non-zero or not empty)"""
+ try:
+ return reduce(lambda x,y: bool(x) or bool(y), data)
+ except TypeError:
+ return False
+
+def tell(data, feedback='info'):
+ """Report back to the user"""
+ l=['dcop', APP_ID, 'default', feedback]
+ if type(data) is type(''):
+ l.append(data)
+ else:
+ l.extend(data)
+ subprocess.Popen(l).communicate()
+
+class Player(object):
+ def __init__(self, display_name, playerType=None):
+ if playerType is None:
+ self.type = "audio"
+ else:
+ self.type=playerType
+ self.displayName=display_name
+ self.running = False
+ d={}
+ d.update(formatVariables)
+ d['player']=self.displayName
+ self._format = d
+
+ def get(self, mode):
+ data=self.getData()
+ if any(data):
+ self._format['info']=self.format(*data)
+ if mode and mode != self.displayName:
+ self._format['intro']=self._format[mode]
+ else:
+ self._format['intro']=self._format[self.type.replace(',','').split()[0]]
+ return string.Template(outputFormat).safe_substitute(self._format)
+ return ''
+
+ def format(self, title='', artist='', album=''):
+ """Return a 'pretty-printed' info string for the track.
+
+ Uses formatStrings from above."""
+ #Update args last to prevent non-sensical override in formatVariables
+ x={'title':title, 'artist':artist, 'album':album}
+ if FIXUP:
+ for i,j in x.items():
+ if re.search(FIXUP,j):
+ x[i]='%s%s%s'%(QUOTE_BEFORE,j,QUOTE_AFTER)
+ self._format.update(x)
+ n=0
+ if title:
+ n|=4 #Still binary to make you read the code ;p
+ if artist:
+ if artist == album:
+ n|=SelfTitled
+ else:
+ n|=2
+ if album:
+ n|=1
+ if n:
+ return string.Template(formatStrings[n]).safe_substitute(self._format)
+ return ''
+
+ def getData(self):
+ """Implement this to do the work"""
+ return ''
+
+ def reEncodeString(self, input):
+ if input:
+ try:
+ input = input.decode('utf-8')
+ except UnicodeError:
+ try:
+ input = input.decode('latin-1')
+ except UnicodeError:
+ input = input.decode('ascii', 'replace')
+ except NameError:
+ pass
+ return input.encode('utf-8')
+
+ def test_format(self, title='', artist='', album=''):
+ s=[]
+ l=["to","by","on"]
+ if title:
+ s.append(title)
+ else:
+ album,artist=artist,album
+ l.pop()
+ if artist:
+ s.append(artist)
+ else:
+ del l[1]
+ if album:
+ s.append(album)
+ else:
+ l.pop()
+ t=["is listening"]
+ while l:
+ t.append(l.pop(0))
+ t.append(s.pop(0))
+ return ' '.join(t)
+
+ def isRunning(self):
+ return self.running
+
+class DCOPPlayer(Player):
+ def __init__(self, display_name, service_name, getTitle='', getArtist='', getAlbum='',playerType=None):
+ Player.__init__(self, display_name, playerType)
+ self.serviceName=service_name
+ self._title=getTitle
+ self._artist=getArtist
+ self._album=getAlbum
+ self.DCOP=""
+
+ def getData(self):
+ self.getService()
+ return (self.grab(self._title), self.grab(self._artist), self.grab(self._album))
+
+ def getService(self):
+ if self.DCOP:
+ return self.DCOP
+ running = re.findall('^' + self.serviceName + "(?:-\\d*)?$", DCOP_ITEMS, re.M)
+ if type(running) is list:
+ try:
+ running=running[0]
+ except IndexError:
+ running=''
+ self.DCOP=running.strip()
+ self.running=bool(self.DCOP)
+ return self.DCOP
+
+ def grab(self, item):
+ if item and self.isRunning():
+ return self.reEncodeString(os.popen("dcop %s %s"%(self.DCOP, item)).readline().rstrip('\n'))
+ return ''
+
+ def isRunning(self):
+ self.getService()
+ return self.running
+
+class AmarokPlayer(DCOPPlayer):
+ def __init__(self):
+ DCOPPlayer.__init__(self,'Amarok','amarok','player title','player artist','player album')
+
+ def getData(self):
+ data=DCOPPlayer.getData(self)
+ if not any(data):
+ data=(self.grab('player nowPlaying'),'','')
+ if not data[0]:
+ return ''
+ return data
+
+#class Amarok2Player(Player):
+# def __init__(self):
+# Player.__init__(self, 'Amarok2', 'audio')
+# self.isRunning()
+#
+# def getData(self):
+# playing=os.popen("qdbus org.mpris.amarok /Player PositionGet").readline().strip() != "0"
+# if playing and self.isRunning():
+# for line in os.popen("qdbus org.mpris.amarok /Player GetMetadata").readlines():
+# if re.match("^title", line):
+# title=self.reEncodeString(line.strip().split(None,1)[1])
+# if re.match("^artist", line):
+# artist=self.reEncodeString(line.strip().split(None,1)[1])
+# if re.match("^album", line):
+# album=self.reEncodeString(line.strip().split(None,1)[1])
+# return (title, artist, album)
+# else:
+# return ''
+#
+# def isRunning(self):
+# qdbus_items=subprocess.Popen(['qdbus'], stdout=subprocess.PIPE).communicate()[0]
+# running=re.findall('^ org.mpris.amarok$', qdbus_items, re.M)
+# if type(running) is list:
+# try:
+# running=running[0]
+# except IndexError:
+# running=''
+# self.running=bool(running.strip())
+# return self.running
+
+import socket
+
+class MPD(Player):
+ def __init__(self, display_name):
+ Player.__init__(self, display_name)
+
+ self.host = "localhost"
+ self.port = 6600
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.settimeout(0.5)
+
+ try:
+ self.sock.connect((self.host, self.port))
+ # just welcome message, we don't need it
+ self.sock.recv(1024)
+ self.running = True
+ except socket.error:
+ self.running = False
+
+ def getData(self):
+ if not self.running:
+ return ''
+ try:
+ self.sock.send("currentsong\n")
+ data = self.sock.recv(1024)
+ except socket.error:
+ return ''
+
+ # mpd sends OK always, so if nothing to show, we should seek for at least 3 chars
+ if len(data) < 4:
+ return ''
+ else:
+ # if there is Artist, Title and Album, get it
+ data=data.splitlines()
+ d={}
+ for i in data:
+ if ':' not in i:
+ continue
+ k,v=i.split(':',1)
+ d[k.lower()]=self.reEncodeString(v.strip())
+ data=(d.get('title',''),d.get('artist',''),d.get('album',''))
+ if not any(data):
+ return d.get('file','')
+ return data
+
+class StupidPlayer(DCOPPlayer):
+ def getData(self):
+ data=DCOPPlayer.getData(self)[0]
+ if data:
+ if data.startswith('URL'):
+ # KMPlayer window titles in the form of "URL - file:///path/to/<media file> - KMPlayer"
+ data=data.split(None,2)[2].rsplit(None,2)[0].rsplit('/')[-1]
+ else:
+ # KPlayer window titles in the form of "<media file> - KPlayer"
+ data=data.rsplit(None,2)[0]
+ return (data,'','')
+ return ''
+
+try:
+ import xmms.common
+ class XmmsPlayer(Player):
+ def __init__(self, display_name):
+ Player.__init__(self, display_name)
+
+ def isRunning(self):
+ self.running = xmms.control.is_running()
+ return self.running
+
+ def getData(self):
+ if self.isRunning() and xmms.control.is_playing():
+ # get the position in the playlist for current playing track
+ index = xmms.control.get_playlist_pos();
+ # get the title of the currently playing track
+ return (self.reEncodeString(xmms.control.get_playlist_title(index)),'','')
+ return ''
+
+except ImportError:
+ XmmsPlayer=Player
+
+class AudaciousPlayer(Player):
+ def __init__(self, display_name):
+ Player.__init__(self, display_name)
+
+ def isRunning(self):
+ self.running = not os.system('audtool current-song')
+ return self.running
+
+ def getData(self):
+ if self.isRunning() and not os.system('audtool playback-playing'):
+ # get the title of the currently playing track
+ data = os.popen('audtool current-song').read().strip()
+ data_list = data.split(' - ')
+ list_length = len(data_list)
+ if list_length == 1:
+ return (self.reEncodeString(data_list[0]),'','')
+ elif list_length == 3:
+ return (self.reEncodeString(data_list[-1]),data_list[0],data_list[1])
+ else:
+ return (self.reEncodeString(data),'','')
+ else:
+ return ''
+
+
+def playing(playerList, mode=None):
+ for i in playerList:
+ s=i.get(mode)
+ if s:
+ tell([IRC_SERVER, TARGET, s], 'say' )
+ return 1
+ return 0
+
+def handleErrors(playerList, kind):
+ if kind:
+ kind=kind.strip()
+ kind=kind.center(len(kind)+2)
+ else:
+ kind= ' supported '
+ x=any([i.running for i in playerList])
+ if x:
+ l=[i.displayName for i in playerList if i.isRunning()]
+ err= "Nothing is playing in %s."%(', '.join(l))
+ else:
+ err= "No%splayers are running."%(kind,)
+ tell(err,'error')
+
+def run(kind):
+ if not kind:
+ kind = ''
+ play=PLAYERS
+ else:
+ if kind in ['audio', 'video']:
+ unsorted=dict([(i.displayName.lower(),i) for i in PLAYERS if kind in i.type])
+ play=[unsorted.pop(i.lower(),Player("ImproperlySupported")) for i in playerRankings[kind]]
+ if len(unsorted):
+ play.extend(unsorted.values())
+ else:
+ play=[i for i in PLAYERS if i.displayName.lower() == kind]
+ try:
+ kind=play[0].displayName
+ except IndexError:
+ tell("%s is not a supported player."%(kind,),'error')
+ sys.exit(0)
+
+ if not playing(play, kind):
+ handleErrors(play, kind)
+
+
+#It would be so nice to just keep this pipe open and use it for all the dcop action,
+#but of course you're supposed to use the big iron (language bindings) instead of
+#the command line tools. One could consider `dcop` the bash dcop language binding,
+#but of course when using shell you don't need to be efficient at all, right?
+
+DCOP_ITEMS=subprocess.Popen(['dcop'], stdout=subprocess.PIPE).communicate()[0] #re.findall("^amarok(?:-\\d*)?$",l,re.M)
+
+# Add your new players here. No more faulty logic due to copy+paste.
+
+PLAYERS = [
+AmarokPlayer(),
+DCOPPlayer("JuK","juk","Player trackProperty Title","Player trackProperty Artist","Player trackProperty Album"),
+DCOPPlayer("Noatun",'noatun',"Noatun title",playerType='audio, video'),
+DCOPPlayer("Kaffeine","kaffeine","KaffeineIface title","KaffeineIface artist","KaffeineIface album",playerType='video, audio'),
+StupidPlayer("KMPlayer","kmplayer","kmplayer-mainwindow#1 caption",playerType="video audio"),
+StupidPlayer("KPlayer","kplayer","kplayer-mainwindow#1 caption",playerType="video audio"),
+DCOPPlayer("KsCD","kscd","CDPlayer currentTrackTitle","CDPlayer currentArtist","CDPlayer currentAlbum"),
+DCOPPlayer("kdetv","kdetv","KdetvIface channelName",playerType='video'),
+AudaciousPlayer('Audacious'), XmmsPlayer('XMMS'),
+DCOPPlayer("Yammi","yammi","YammiPlayer songTitle","YammiPlayer songArtist","YammiPlayer songAlbum"),
+MPD('MPD')
+]
+
+# Get rid of players that didn't get subclassed so they don't appear in the available players list
+for i in PLAYERS[:]:
+ if type(i) is Player:
+ PLAYERS.remove(i)
+
+if REGEX_FIXUP:
+ FIXUP=REGEX_FIXUP
+elif SIMPLE_FIXUP:
+ FIXUP="[%s]"%(SIMPLE_FIXUP)
+else:
+ FIXUP=''
+
+# It all comes together right here
+if __name__=="__main__":
+
+ if not TARGET:
+ s="""media v2.0.1 for Konversation 1.0. One media command to rule them all, inspired from Kopete's now listening plugin.
+Usage:
+ "\00312/media\017" - report what the first player found is playing
+ "\00312/media\017 [ '\00312audio\017' | '\00312video\017' ]" - report what is playing in a supported audio or video player
+ "\00312/media\017 { \00312Player\017 }" - report what is playing in \00312Player\017 if it is supported
+
+ Available players are:
+ """ + ', '.join([("%s (%s)"%(i.displayName,i.type)) for i in PLAYERS])
+
+ for i in s.splitlines():
+ tell(i)
+ #tell("%s"%(len(s.splitlines()),))
+ # called from the server tab
+ pass
+ else:
+ try:
+ kind = sys.argv[4].lower()
+ except IndexError:
+ kind = None
+
+ run(kind)
+
diff --git a/konversation/scripts/sayclip b/konversation/scripts/sayclip
new file mode 100755
index 0000000..06b8234
--- /dev/null
+++ b/konversation/scripts/sayclip
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# Prints the contents of the clipbaord into Konversation with flood protection.
+# Klipper must be running.
+# Usage: /exec sayclip [pause-time]
+# Pause time defaults to 1 second.
+# By Gary Cramblitt (garycramblitt@comcast.net)
+# Use however you wish.
+
+PORT=$1;
+SERVER=$2;
+TARGET=$3;
+PAUSETIME="1s";
+if [ -n "$4" ]
+then
+ PAUSETIME="$4"
+fi
+
+dcop klipper klipper getClipboardContents | while read line; do dcop $PORT default say $SERVER "$TARGET" " $line"; sleep $PAUSETIME; done
diff --git a/konversation/scripts/sysinfo b/konversation/scripts/sysinfo
new file mode 100644
index 0000000..d2b3851
--- /dev/null
+++ b/konversation/scripts/sysinfo
@@ -0,0 +1,86 @@
+#!/bin/sh
+#
+# Licensed under GPL v2 or later at your option
+# Copyright 2004 by Michiel de Boer <infobash@rebelhomicide.demon.nl>
+# Copyright 2006 by Emil Obermayr <nobs@tigress.com>
+#
+# this version is stripped down to no-color
+#
+# get full original version at http://rebelhomicide.demon.nl/scripts/
+
+PORT=$1;
+SERVER=$2;
+TARGET=$3;
+
+export LC_ALL="C"
+
+HN="$(hostname)"
+OSKERN="$(uname -s) $(uname -r)"
+if [ "$KDE_FULL_SESSION" = "true" ]; then
+ if [ "$KDE_SESSION_VERSION" = 4 ]; then
+ KDE="$(kde4-config --version | sed -n '2p' | sed 's/://;s/ *$//')"
+ else
+ KDE="$(kde-config --version | sed -n '2p' | sed 's/://;s/ *$//')"
+ fi
+fi
+
+CPU=$(awk -F':' '/model name/{name=$2}
+ /cpu MHz/{mhz=int($2)}
+ /bogomips/ {bogo=int($2)
+}
+END{
+ gsub (/ *\(tm\) */, " ", name);
+ gsub (/ *\(TM\) */, " ", name);
+ gsub (/ *Processor */, " ", name);
+ gsub (/ *$/, "", name);
+ gsub (/^ */, "", name);
+ printf "CPU: %s at %d MHz (%d bogomips)", name, mhz, bogo;
+}
+' /proc/cpuinfo )
+
+HDD=$(df -lP| awk '($1~/\/dev/){
+ use+=$3/1024^2;
+ tot+=$2/1024^2;
+ }
+ END{print "HD: " int(use) "/" int(tot) "GB"}')
+
+MEM=$(awk '($1=="MemTotal:"){tot=int($2/1024)}
+ ($1=="MemFree:"){free=int($2/1024)}
+ END{
+use=tot-free
+print "RAM: " use "/" tot "MB"}
+' /proc/meminfo)
+
+PROC="$(($(ps aux | wc -l)-1))"
+
+UPT=$(awk '{u="s";
+n=$1;
+if (n>60){
+ n2=n%60;
+ n/=60;
+ u="min";
+ if (n>60){
+ n2=n%60;
+ n/=60;
+ u="h";
+ if (n>24){
+ n2=n%24;
+ n/=24;
+ u="d";
+ }
+ }
+ }
+printf ("%d.%d%s up",n, n2, u);
+}' /proc/uptime )
+
+out="Sysinfo for '$HN': $OSKERN running $KDE, $CPU, $HDD, $MEM, $PROC proc's, ${UPT}"
+
+if [ "x$PORT" = "x" ] ; then
+ echo "$out"
+else
+ if [ "x$TARGET" = "x" ] ; then
+ dcop $PORT default error "$out"
+ else
+ dcop $PORT default say $SERVER "$TARGET" "$out"
+ fi
+fi
diff --git a/konversation/scripts/tinyurl b/konversation/scripts/tinyurl
new file mode 100755
index 0000000..14efde9
--- /dev/null
+++ b/konversation/scripts/tinyurl
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+#
+# Creates a TinyURL from a long URL
+# Licensed under GPL v2 or later at your option
+# Copyright 2007 Terence Simpson
+
+PORT=$1
+SERVER=$2
+TARGET=$3
+export URL="$4"
+NICK="$5"
+
+if test ! -z $URL; then
+ if test $(which curl); then
+ TINYURL="$(curl -s -i http://tinyurl.com/create.php?url=$URL|grep "The following URL" -A3|tail -1|awk -F\> '{print $3}'|sed 's,</b,,')"
+ else
+ TINYURL="$(wget -T10 -t2 -qO- http://tinyurl.com/create.php?url=$URL|grep "The following URL" -A3|tail -1|awk -F\> '{print $3}'|sed 's,</b,,')"
+ fi
+else dcop $PORT default error "No url given: usage is \"/tinyurl URL [NickName]\""
+ exit 1
+fi
+
+if test -z $TINYURL; then
+ dcop $PORT default error "Unable run tinyurl script, please make sure you have curl or wget installed"
+else
+ if test ! -z $NICK; then
+ dcop $PORT default say $SERVER "$TARGET" "${NICK}: $TINYURL"
+ else
+ dcop $PORT default say $SERVER "$TARGET" "$TINYURL"
+ fi
+fi
diff --git a/konversation/scripts/uptime b/konversation/scripts/uptime
new file mode 100755
index 0000000..8c25cfd
--- /dev/null
+++ b/konversation/scripts/uptime
@@ -0,0 +1,56 @@
+#!/usr/bin/env perl
+
+# Uptime script for Konversation
+# made by Magnus Romnes (gromnes@online.no)
+# The script might be uncompatible with other unix variants than linux.
+# only tested on Debian GNU/Linux Sid
+# use the code for whatever you wish :-)
+
+
+$PORT = shift;
+$SERVER = shift;
+$TARGET = shift;
+
+$PLATFORM = `uname -s`;
+chomp($PLATFORM);
+if($PLATFORM eq "FreeBSD") {
+ $BOOTTIME = `sysctl kern.boottime`;
+ $BOOTTIME =~ s/.* sec = ([0-9]+).*/\1/;
+ $TIMENOW = `date +%s`;
+ $seconds = $TIMENOW - $BOOTTIME;
+} else {
+ $UPTIME = `cat /proc/uptime`;
+ if (not $UPTIME) {
+ exec 'dcop', $PORT, 'default', 'info', 'Could not read uptime. Check that /proc/uptime exists.';
+ }
+ @uparray = split(/\./, $UPTIME);
+ $seconds = $uparray[0];
+}
+
+if($seconds >= 86400)
+{
+ $days = int($seconds/86400);
+ $seconds = $seconds-($days*86400);
+}
+if($seconds >= 3600)
+{
+ $hours = int($seconds/3600);
+ $seconds = $seconds-($hours*3600);
+}
+if($seconds > 60)
+{
+ $minutes = int($seconds/60);
+}
+if( $days && $hours ) {
+ exec 'dcop', $PORT, 'default', 'say', $SERVER, $TARGET, "Uptime: $days days, $hours hours and $minutes minutes";
+}
+elsif( !$days && $hours ) {
+ exec 'dcop', $PORT, 'default', 'say', $SERVER, $TARGET, "Uptime: $hours hours and $minutes minutes";
+}
+elsif( $days && !$hours ) {
+ exec 'dcop', $PORT, 'default', 'say', $SERVER, $TARGET, "Uptime: $days days and $minutes minutes";
+}
+elsif( !$days && !$hours ) {
+ exec 'dcop', $PORT, 'default', 'say', $SERVER, $TARGET, "Uptime: $minutes minutes";
+}
+
diff --git a/konversation/scripts/weather b/konversation/scripts/weather
new file mode 100755
index 0000000..5d3dafb
--- /dev/null
+++ b/konversation/scripts/weather
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2005,2007 by İsmail Dönmez <ismail@pardus.org.tr>
+# Licensed under GPL v2 or later at your option
+
+import sys
+from subprocess import *
+
+port = sys.argv[1]
+server = sys.argv[2]
+target = sys.argv[3]
+
+msg_template = "Current weather for %%B%s%%B : Temperature: %%B%s%%B, Pressure: %%B%s%%B, Wind: %%B%s%%B"
+msg_detailed_template = "Current weather for %%B%s%%B : %%B%s%%B, Temperature: %%B%s%%B, Pressure: %%B%s%%B, Wind: %%B%s%%B"
+
+def printMessage(message=None):
+ Popen(['dcop', port, 'default', 'say', server, target, message]).communicate()
+
+def printError(message=None):
+ Popen(['dcop', port, 'default', 'error', message]).communicate()
+
+def getData(section, station=None):
+ if station:
+ data = Popen(['dcop','KWeatherService','WeatherService', section, station], stdout=PIPE).communicate()[0].rstrip("\n")
+ else:
+ data = Popen(['dcop','KWeatherService','WeatherService', section], stdout=PIPE).communicate()[0].rstrip("\n")
+
+ return data
+
+def stationMessage(station):
+ city = getData("stationName", station)
+ temperature = getData("temperature", station)
+ pressure = getData("pressure", station)
+ wind = getData("wind", station)
+ detail = getData("weather", station)
+ detail2 = getData("cover", station)
+
+ if detail2:
+ if detail:
+ detail = detail+', '+detail2
+ else:
+ detail = detail2
+
+ if detail:
+ return msg_detailed_template % (city,detail,temperature,pressure,wind)
+ else:
+ return msg_template % (city,temperature,pressure,wind)
+
+def printWeather(index):
+ stations = getData("listStations").split("\n")
+
+ if index != None:
+ if index <= 0:
+ printError("Station index should be bigger than zero!")
+ elif index > len(stations):
+ printError("Station index is out of range")
+ else:
+ printMessage(stationMessage(stations[index-1]))
+ else:
+ for station in stations:
+ printMessage(stationMessage(station))
+
+if __name__ == "__main__":
+ try:
+ index = int(sys.argv[4])
+ except IndexError:
+ index = None
+
+ printWeather(index)
diff --git a/konversation/src/Makefile.am b/konversation/src/Makefile.am
new file mode 100644
index 0000000..ee2fb29
--- /dev/null
+++ b/konversation/src/Makefile.am
@@ -0,0 +1,73 @@
+SUBDIRS = config linkaddressbook blowfish .
+
+METASOURCES = AUTO
+
+bin_PROGRAMS = konversation
+
+konversation_SOURCES = konviface.skel konviconfigdialog.cpp konversationstatusbar.cpp \
+ konvisettingsdialog.cpp viewcontainer.cpp viewtree.cpp viewtreeitem.cpp konversationmainwindow.cpp \
+ valuelistviewitem.cpp urlcatcher.cpp scriptlauncher.cpp rawlog.cpp konvdcop.cpp channellistpanel.cpp \
+ konsolepanel.cpp identity.cpp statuspanel.cpp dccrecipientdialog.cpp topiccombobox.cpp nicksonline.cpp \
+ dcctransferpanel.cpp dcctransferpanelitem.cpp highlight.cpp highlightviewitem.cpp modebutton.cpp \
+ ignore_preferencesui.ui ignore_preferences.cpp ignore.cpp nicklistview.cpp ircinput.cpp \
+ channeloptionsui.ui channeloptionsdialog.cpp ignorelistviewitem.cpp images.cpp quickbutton.cpp \
+ chatwindow.cpp outputfilter.cpp logfilereader.cpp query.cpp konversationapplication.cpp nick.cpp \
+ inputfilter.cpp channel.cpp ircview.cpp server.cpp main.cpp dcc_preferencesui.ui log_preferences.ui \
+ tabs_preferencesui.ui tabs_preferences.cpp chatwindowappearance_preferences.ui irccolorchooserui.ui \
+ colorsappearance_preferences.ui irccolorchooser.cpp channellistviewitem.cpp osd.cpp trayicon.cpp \
+ dccchat.cpp multilineedit.cpp nickinfo.cpp dccresumedialog.cpp konversationsound.cpp quickconnectdialog.cpp \
+ serverlistdialog.cpp dcctransfersend.cpp dcctransferrecv.cpp channelnick.cpp insertchardialog.cpp \
+ irccharsets.cpp editnotifydialog.cpp common.cpp serverison.cpp sslsocket.cpp servergroupsettings.cpp \
+ serversettings.cpp servergroupdialog.cpp ssllabel.cpp serverdialog.cpp channeldialog.cpp identitydialog.cpp \
+ topiclabel.cpp notificationhandler.cpp joinchannelui.ui joinchanneldialog.cpp emoticon.cpp \
+ chatwindowbehaviour_preferences.ui alias_preferencesui.ui osd_preferencesui.ui theme_preferencesui.ui \
+ highlight_preferencesui.ui warnings_preferencesui.ui warnings_preferences.cpp quickbuttons_preferencesui.ui \
+ watchednicknames_preferencesui.ui generalbehavior_preferences.ui connectionbehavior_preferences.ui \
+ fontappearance_preferences.ui nicklistbehavior_preferencesui.ui konvibookmarkhandler.cpp konvibookmarkmenu.cpp \
+ ircviewbox.cpp searchbar.cpp osd_preferences.cpp theme_preferences.cpp dcc_preferences.cpp \
+ alias_preferences.cpp highlight_preferences.cpp watchednicknames_preferences.cpp quickbuttons_preferences.cpp \
+ nicklistbehavior_preferences.cpp tabnotifications_preferences.ui multilinetextedit.cpp serverlistview.cpp \
+ nicksonlineitem.cpp searchbarbase.ui autoreplace_preferencesui.ui autoreplace_preferences.cpp \
+ servergroupdialogui.ui dcctransfer.cpp dcctransfermanager.cpp dcctransferdetailedinfopanelui.ui \
+ dcctransferdetailedinfopanel.cpp dcccommon.cpp queuetunerbase.ui queuetuner.cpp ircqueue.cpp \
+ connectionsettings.cpp connectionmanager.cpp awaymanager.cpp
+
+konversation_COMPILE_FIRST = config/preferences_base.h
+konversation_LDADD = $(LIB_KIO) $(LIB_XSS) $(LIB_KABC) $(LIB_KIMIFACE) linkaddressbook/liblinkaddressbookui.la blowfish/libblowfish.la config/libkonversationconfig.la
+
+xdg_apps_DATA = konversation.desktop
+
+# set the include path for X, qt and KDE
+INCLUDES= -Ilinkaddressbook -I$(srcdir)/linkaddressbook -Iblowfish -I$(srcdir)/blowfish\
+ -Iconfig -I$(srcdir)/config $(all_includes)
+# the library search path.
+konversation_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+rcdir = $(kde_datadir)/konversation
+rc_DATA = konversationui.rc eventsrc
+
+kdelnk_DATA = konvirc.protocol konvirc6.protocol
+kdelnkdir = $(kde_servicesdir)
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = konversation.upd
+update_SCRIPTS = konversation-0.19-colors.pl konversation-0.19-sortorder.pl konversation-0.19-appearance.pl \
+ konversation-0.19-tabplacement.pl konversation-0.19-custombrowser.pl \
+ konversation-0.19-colorcodes.pl konversation-0.19-notifylists.pl \
+ konversation-0.20-quickbuttons.pl konversation-0.20-customfonts.pl
+
+messages: rc.cpp
+ LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \
+ if test -n "$$LIST"; then \
+ $(XGETTEXT) $$LIST -o $(podir)/konversation.pot; \
+ fi
+
+noinst_HEADERS = serverlistdialog.h dcctransfersend.h dcctransferrecv.h \
+ insertchardialog.h editnotifydialog.h serverison.h \
+ servergroupsettings.h serversettings.h servergroupdialog.h serverdialog.h channeldialog.h \
+ identitydialog.h topiclabel.h channeloptionsdialog.h joinchanneldialog.h \
+ highlight_preferences.h watchednicknames_preferences.h quickbuttons_preferences.h \
+ nicklistbehavior_preferences.h konvisettingspage.h nicksonlineitem.h
+
+# User might still have these in src/ instead of src/config, so remove these in that case
+CLEANFILES = preferences_base.h preferences_base.cpp watchednicknamesconfigcontroller.*
diff --git a/konversation/src/alias_preferences.cpp b/konversation/src/alias_preferences.cpp
new file mode 100644
index 0000000..ed3498e
--- /dev/null
+++ b/konversation/src/alias_preferences.cpp
@@ -0,0 +1,223 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#include "alias_preferences.h"
+#include "config/preferences.h"
+
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qheader.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <klineedit.h>
+#include <klistview.h>
+
+
+Alias_Config::Alias_Config(QWidget* parent, const char* name)
+ : Alias_ConfigUI(parent, name)
+{
+ // reset flag to defined state (used to block signals when just selecting a new item)
+ m_newItemSelected = false;
+
+ // populate listview
+ loadSettings();
+
+ // make items react to drag & drop
+ aliasListView->setSorting(-1,false);
+ aliasListView->header()->setMovingEnabled(false);
+
+ connect(aliasListView, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(entrySelected(QListViewItem*)));
+ connect(aliasListView, SIGNAL(clicked(QListViewItem*)), this, SLOT(entrySelected(QListViewItem*)) );
+ connect(aliasListView, SIGNAL(moved()), this, SIGNAL(modified()));
+
+ connect(aliasInput, SIGNAL(textChanged(const QString&)), this, SLOT(nameChanged(const QString&)));
+ connect(replacementInput, SIGNAL(textChanged(const QString&)), this, SLOT(actionChanged(const QString&)));
+
+ connect(newButton, SIGNAL(clicked()), this, SLOT(addEntry()));
+ connect(removeButton, SIGNAL(clicked()), this, SLOT(removeEntry()));
+}
+
+Alias_Config::~Alias_Config()
+{
+}
+
+void Alias_Config::loadSettings()
+{
+ setAliasListView(Preferences::aliasList());
+}
+
+void Alias_Config::saveSettings()
+{
+ QStringList newList=currentAliasList();
+ Preferences::setAliasList(newList);
+
+ // saved list is now old list, to check for changes
+ m_oldAliasList=newList;
+}
+
+void Alias_Config::restorePageToDefaults()
+{
+ aliasListView->clear();
+ setAliasListView(Preferences::defaultAliasList());
+}
+
+bool Alias_Config::hasChanged()
+{
+ return (currentAliasList() != m_oldAliasList);
+}
+
+void Alias_Config::setAliasListView(const QStringList& aliasList)
+{
+ aliasListView->clear();
+
+ // Insert alias items backwards to get them sorted properly
+ for(int index=aliasList.count(); index!=0; index--)
+ {
+ QString item=aliasList[index-1];
+ new KListViewItem(aliasListView,item.section(' ',0,0),item.section(' ',1));
+ }
+
+ aliasListView->setSelected(aliasListView->firstChild(), true);
+ // remember alias list
+ m_oldAliasList=aliasList;
+}
+
+QStringList Alias_Config::currentAliasList()
+{
+ QStringList newList;
+
+ QListViewItem* item=aliasListView->itemAtIndex(0);
+ while(item)
+ {
+ newList.append(item->text(0)+' '+item->text(1));
+ item=item->itemBelow();
+ }
+ return newList;
+}
+
+// what to do when the user selects an item
+void Alias_Config::entrySelected(QListViewItem* aliasEntry)
+{
+ // play it safe, assume disabling all widgets first
+ bool enabled = false;
+
+ // check if there really was an item selected
+ if (aliasEntry)
+ {
+ // remember to enable the editing widgets
+ enabled = true;
+ // tell the editing widgets not to emit modified() on signals now
+ m_newItemSelected = true;
+ // update editing widget contents
+ aliasInput->setText(aliasEntry->text(0));
+ replacementInput->setText(aliasEntry->text(1));
+ // re-enable modified() signal on text changes in edit widgets
+ m_newItemSelected = false;
+ }
+ // enable or disable editing widgets
+ removeButton->setEnabled(enabled);
+ aliasLabel->setEnabled(enabled);
+ aliasInput->setEnabled(enabled);
+ replacementLabel->setEnabled(enabled);
+ replacementInput->setEnabled(enabled);
+}
+
+// what to do when the user change the name of a quick button
+void Alias_Config::nameChanged(const QString& newName)
+{
+ // get possible first selected item
+ QListViewItem* item = aliasListView->selectedItem();
+
+ // sanity check
+ if (item)
+ {
+ // rename item
+ item->setText(0,newName);
+ // tell the config system that something has changed
+ if (!m_newItemSelected) emit modified();
+ }
+}
+
+// what to do when the user change the action definition of a quick button
+void Alias_Config::actionChanged(const QString& newAction)
+{
+ // get possible first selected item
+ QListViewItem* item = aliasListView->selectedItem();
+
+ // sanity check
+ if (item)
+ {
+ // rename item
+ item->setText(1,newAction);
+ // tell the config system that something has changed
+ if(!m_newItemSelected) emit modified();
+ }
+}
+
+// add button pressed
+void Alias_Config::addEntry()
+{
+ // add new item at the bottom of list view
+ KListViewItem* newItem = new KListViewItem(aliasListView,aliasListView->lastChild(),i18n("New"),QString());
+ // if successful ...
+ if (newItem)
+ {
+ // select new item and make it the current one
+ aliasListView->setSelected(newItem,true);
+ aliasListView->setCurrentItem(newItem);
+ // set input focus on item name edit
+ aliasInput->setFocus();
+ // select all text to make overwriting easier
+ aliasInput->selectAll();
+ // tell the config system that something has changed
+ emit modified();
+ }
+}
+
+// remove button pressed
+void Alias_Config::removeEntry()
+{
+ // get possible first selected item
+ QListViewItem* item = aliasListView->selectedItem();
+
+ // sanity check
+ if (item)
+ {
+ // get item below the current one
+ QListViewItem* nextItem = item->itemBelow();
+ // if there was none, get the one above
+ if(!nextItem) nextItem = item->itemAbove();
+
+ // remove the item from the list
+ delete item;
+
+ // check if we found the next item
+ if (nextItem)
+ {
+ // select the item and make it the current ite,
+ aliasListView->setSelected(nextItem,true);
+ aliasListView->setCurrentItem(nextItem);
+ }
+ else
+ {
+ // no next item found, this means the list is empty
+ entrySelected(0);
+ }
+ // tell the config system that somethig has changed
+ emit modified();
+ }
+}
+
+#include "alias_preferences.moc"
diff --git a/konversation/src/alias_preferences.h b/konversation/src/alias_preferences.h
new file mode 100644
index 0000000..4f1cfff
--- /dev/null
+++ b/konversation/src/alias_preferences.h
@@ -0,0 +1,51 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef EXALIASPREFERENCES_H
+#define EXALIASPREFERENCES_H
+
+#include "alias_preferencesui.h"
+#include "konvisettingspage.h"
+
+class Alias_Config : public Alias_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit Alias_Config(QWidget* parent, const char* name = 0);
+ ~Alias_Config();
+
+ virtual void saveSettings();
+ virtual void loadSettings();
+ virtual void restorePageToDefaults();
+ virtual bool hasChanged();
+
+ signals:
+ void modified();
+
+ protected slots:
+ void entrySelected(QListViewItem* aliasEntry);
+ void nameChanged(const QString& newName);
+ void actionChanged(const QString& newAction);
+ void addEntry();
+ void removeEntry();
+
+ protected:
+ void setAliasListView(const QStringList& aliasList);
+
+ bool m_newItemSelected;
+
+ QStringList m_oldAliasList;
+ QStringList currentAliasList();
+};
+
+#endif
diff --git a/konversation/src/alias_preferencesui.ui b/konversation/src/alias_preferencesui.ui
new file mode 100644
index 0000000..2e144cc
--- /dev/null
+++ b/konversation/src/alias_preferencesui.ui
@@ -0,0 +1,174 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Alias_ConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Alias_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>292</width>
+ <height>206</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>aliasLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Alias:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>replacementLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Replacement:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>aliasInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>replacementInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="KListView" row="0" column="0">
+ <column>
+ <property name="text">
+ <string>Alias</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Replacement</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>aliasListView</cstring>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="hScrollBarMode">
+ <enum>Auto</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>newButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer18</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>350</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>aliasListView</tabstop>
+ <tabstop>aliasInput</tabstop>
+ <tabstop>replacementInput</tabstop>
+ <tabstop>newButton</tabstop>
+ <tabstop>removeButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/autoreplace_preferences.cpp b/konversation/src/autoreplace_preferences.cpp
new file mode 100644
index 0000000..67fd9cd
--- /dev/null
+++ b/konversation/src/autoreplace_preferences.cpp
@@ -0,0 +1,403 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "autoreplace_preferences.h"
+#include "config/preferences.h"
+
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qcombobox.h>
+#include <qheader.h>
+#include <qtooltip.h>
+#include <qdialog.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <klineedit.h>
+#include <klistview.h>
+#include <kparts/componentfactory.h>
+#include <kregexpeditorinterface.h>
+
+#define DIRECTION_OUTPUT 0
+#define DIRECTION_INPUT 1
+#define DIRECTION_BOTH 2
+
+
+Autoreplace_Config::Autoreplace_Config(QWidget* parent, const char* name)
+ : Autoreplace_ConfigUI(parent, name)
+{
+ // reset flag to defined state (used to block signals when just selecting a new item)
+ m_newItemSelected=false;
+
+ //Check if the regexp editor is installed
+ bool installed = !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty();
+
+ if(installed)
+ {
+ regExpEditorButton->setEnabled(true);
+ QToolTip::add(regExpEditorButton, i18n("Click to run Regular Expression Editor (KRegExpEditor)"));
+ }
+ else
+ {
+ regExpEditorButton->setEnabled(false);
+ QToolTip::add(regExpEditorButton, i18n("The Regular Expression Editor (KRegExpEditor) is not installed"));
+ }
+
+ // populate combobox
+ directionCombo->insertItem(i18n("Outgoing"),DIRECTION_OUTPUT);
+ directionCombo->insertItem(i18n("Incoming"),DIRECTION_INPUT);
+ directionCombo->insertItem(i18n("Both"),DIRECTION_BOTH);
+
+ // make items react to drag & drop
+ patternListView->setSorting(-1,false);
+ patternListView->setShowSortIndicator(true);
+ patternListView->setShadeSortColumn(true);
+ patternListView->header()->setMovingEnabled(false);
+
+ // populate listview
+ loadSettings();
+
+ connect(patternListView, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(entrySelected(QListViewItem*)));
+ connect(patternListView, SIGNAL(clicked(QListViewItem*)), this, SLOT(entrySelected(QListViewItem*)));
+ connect(patternListView, SIGNAL(moved()), SIGNAL(modified()));
+
+ connect(patternListView, SIGNAL(aboutToMove()), SLOT(disableSort()));
+ connect(patternListView->header(), SIGNAL(clicked(int)), SLOT(sort(int)));
+
+ connect(directionCombo, SIGNAL(activated(int)), this, SLOT(directionChanged(int)));
+
+ connect(patternInput, SIGNAL(textChanged(const QString&)), this, SLOT(patternChanged(const QString&)));
+ connect(regExpEditorButton, SIGNAL(clicked()), this, SLOT(showRegExpEditor()));
+ connect(replacementInput, SIGNAL(textChanged(const QString&)), this, SLOT(replacementChanged(const QString&)));
+
+ connect(newButton, SIGNAL(clicked()), this, SLOT(addEntry()));
+ connect(removeButton, SIGNAL(clicked()), this, SLOT(removeEntry()));
+}
+
+Autoreplace_Config::~Autoreplace_Config()
+{
+}
+
+void Autoreplace_Config::loadSettings()
+{
+ setAutoreplaceListView(Preferences::autoreplaceList());
+
+ // remember autoreplace list for hasChanged()
+ m_oldAutoreplaceList=Preferences::autoreplaceList();
+}
+
+// fill listview with autoreplace definitions
+void Autoreplace_Config::setAutoreplaceListView(const QStringList &autoreplaceList)
+{
+ // clear listView
+ patternListView->clear();
+ // go through the list
+ for(unsigned int index=autoreplaceList.count();index!=0;index--)
+ {
+ // get autoreplace definition
+ QString definition=autoreplaceList[index-1];
+ // cut definition apart in name and action, and create a new listview item
+ QCheckListItem* newItem=new QCheckListItem(patternListView,QString(),QCheckListItem::CheckBox);
+ // Regular expression?
+ if(definition.section(',',0,0)=="1") newItem->setOn(true);
+ // direction input/output/both
+ if(definition.section(',',1,1)=="i") newItem->setText(1,directionCombo->text(DIRECTION_INPUT));
+ else if(definition.section(',',1,1)=="o") newItem->setText(1,directionCombo->text(DIRECTION_OUTPUT));
+ else if(definition.section(',',1,1)=="io") newItem->setText(1,directionCombo->text(DIRECTION_BOTH));
+ // pattern
+ newItem->setText(2,definition.section(',',2,2));
+ // replacement
+ newItem->setText(3,definition.section(',',3));
+ // hidden column, so we are independent of the i18n()ed display string
+ newItem->setText(4,definition.section(',',1,1));
+ } // for
+ patternListView->setSelected(patternListView->firstChild(), true);
+}
+
+// save autoreplace entries to configuration
+void Autoreplace_Config::saveSettings()
+{
+ // get configuration object
+ KConfig* config=kapp->config();
+
+ // delete all patterns
+ config->deleteGroup("Autoreplace List");
+ // create new empty autoreplace group
+ config->setGroup("Autoreplace List");
+
+ // create empty list
+ QStringList newList=currentAutoreplaceList();
+
+ // check if there are any patterns in the list view
+ if(newList.count())
+ {
+ // go through all patterns and save them into the configuration
+ for(unsigned int index=0;index<newList.count();index++)
+ {
+ // write the current entry's pattern and replacement (adds a "#" to preserve blanks at the end of the line)
+ config->writeEntry(QString("Autoreplace%1").arg(index),newList[index]+'#');
+ } // for
+ }
+ // if there were no entries at all, write a dummy entry to prevent KConfigXT from "optimizing"
+ // the group out, which would in turn make konvi restore the default entries
+ else
+ config->writeEntry("Empty List",QString());
+
+ // set internal autoreplace list
+ Preferences::setAutoreplaceList(newList);
+
+ // remember autoreplace list for hasChanged()
+ m_oldAutoreplaceList=newList;
+}
+
+void Autoreplace_Config::restorePageToDefaults()
+{
+ setAutoreplaceListView(Preferences::defaultAutoreplaceList());
+}
+
+QStringList Autoreplace_Config::currentAutoreplaceList()
+{
+ // get first item of the autoreplace listview
+ QListViewItem* item=patternListView->firstChild();
+ // create empty list
+ QStringList newList;
+
+ // go through all items and save them into the configuration
+ while(item)
+ {
+ QString checked="0";
+ if(static_cast<QCheckListItem*>(item)->isOn()) checked="1";
+
+ // remember entry in internal list (col 4 is hidden for input/output)
+ newList.append(checked+','+item->text(4)+','+item->text(2)+','+item->text(3));
+ // get next item in the listview
+ item=item->itemBelow();
+ } // while
+
+ // return list
+ return newList;
+}
+
+bool Autoreplace_Config::hasChanged()
+{
+ return(m_oldAutoreplaceList!=currentAutoreplaceList());
+}
+
+// slots
+
+// what to do when the user selects an item
+void Autoreplace_Config::entrySelected(QListViewItem* autoreplaceEntry)
+{
+ // play it safe, assume disabling all widgets first
+ bool enabled=false;
+
+ // check if there really was an item selected
+ if(autoreplaceEntry)
+ {
+ // remember to enable the editing widgets
+ enabled=true;
+
+ // tell the editing widgets not to emit modified() on signals now
+ m_newItemSelected=true;
+ // update editing widget contents
+ patternInput->setText(autoreplaceEntry->text(2));
+ replacementInput->setText(autoreplaceEntry->text(3));
+
+ // set combobox to selected item
+ int itemIndex=0;
+ QString direction=autoreplaceEntry->text(4);
+ if(direction=="i") itemIndex=DIRECTION_INPUT;
+ else if(direction=="o") itemIndex=DIRECTION_OUTPUT;
+ else if(direction=="io") itemIndex=DIRECTION_BOTH;
+ directionCombo->setCurrentItem(itemIndex);
+ // re-enable modified() signal on text changes in edit widgets
+ m_newItemSelected=false;
+ }
+ // enable or disable editing widgets
+ removeButton->setEnabled(enabled);
+ directionLabel->setEnabled(enabled);
+ directionCombo->setEnabled(enabled);
+ patternLabel->setEnabled(enabled);
+ patternInput->setEnabled(enabled);
+ replacementLabel->setEnabled(enabled);
+ replacementInput->setEnabled(enabled);
+
+ if(!KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty())
+ {
+ regExpEditorButton->setEnabled(enabled);
+ }
+
+ // make checkboxes work
+ emit modified();
+}
+
+// what to do when the user changes the direction of an entry
+void Autoreplace_Config::directionChanged(int newDirection)
+{
+ // get possible selected item
+ QListViewItem* item=patternListView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // prepare hidden identifier string
+ QString id;
+ // find the direction strings to set up in the item
+ if(newDirection==DIRECTION_INPUT) id="i";
+ else if(newDirection==DIRECTION_OUTPUT) id="o";
+ else if(newDirection==DIRECTION_BOTH) id="io";
+ // rename direction
+ item->setText(1,directionCombo->text(newDirection));
+ item->setText(4,id);
+ // tell the config system that something has changed
+ if(!m_newItemSelected) emit modified();
+ }
+}
+
+// what to do when the user changes the pattern of an entry
+void Autoreplace_Config::patternChanged(const QString& newPattern)
+{
+ // get possible selected item
+ QListViewItem* item=patternListView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // rename pattern
+ item->setText(2,newPattern);
+ // tell the config system that something has changed
+ if(!m_newItemSelected) emit modified();
+ }
+}
+
+// what to do when the user changes the replacement of an entry
+void Autoreplace_Config::replacementChanged(const QString& newReplacement)
+{
+ // get possible selected item
+ QListViewItem* item=patternListView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // rename item
+ item->setText(3,newReplacement);
+ // tell the config system that something has changed
+ if(!m_newItemSelected) emit modified();
+ }
+}
+
+// add button pressed
+void Autoreplace_Config::addEntry()
+{
+ disableSort();
+
+ // add new item at the bottom of list view
+ QCheckListItem* newItem=new QCheckListItem(patternListView,QString(),QCheckListItem::CheckBox);
+ // if successful ...
+ if(newItem)
+ {
+ // set default direction
+ newItem->setText(1,directionCombo->text(DIRECTION_OUTPUT));
+ // set default pattern name
+ newItem->setText(2,i18n("New"));
+ // set default direction
+ newItem->setText(4,"o");
+ // select new item and make it the current one
+ patternListView->setSelected(newItem,true);
+ patternListView->setCurrentItem(newItem);
+ // set input focus on item pattern edit
+ patternInput->setFocus();
+ // select all text to make overwriting easier
+ patternInput->selectAll();
+ // tell the config system that something has changed
+ emit modified();
+ }
+}
+
+// remove button pressed
+void Autoreplace_Config::removeEntry()
+{
+ // get possible first selected item
+ QListViewItem* item=patternListView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // get item below the current one
+ QListViewItem* nextItem=item->itemBelow();
+ // if there was none, get the one above
+ if(!nextItem) nextItem=item->itemAbove();
+
+ // remove the item from the list
+ delete item;
+
+ // check if we found the next item
+ if(nextItem)
+ {
+ // select the item and make it the current item
+ patternListView->setSelected(nextItem,true);
+ patternListView->setCurrentItem(nextItem);
+ }
+ else
+ {
+ // no next item found, this means the list is empty
+ entrySelected(0);
+ }
+ // tell the config system that somethig has changed
+ emit modified();
+ }
+}
+
+void Autoreplace_Config::sort(int column)
+{
+ bool ascending = true;
+
+ if (patternListView->sortColumn() != -1)
+ ascending = (patternListView->sortOrder() == Qt::Ascending);
+
+ patternListView->setSorting(column, ascending);
+
+ emit modified();
+}
+
+void Autoreplace_Config::disableSort()
+{
+ patternListView->setSorting(-1);
+}
+
+void Autoreplace_Config::showRegExpEditor()
+{
+ QDialog *editorDialog =
+ KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" );
+
+ if(editorDialog)
+ {
+ // kdeutils was installed, so the dialog was found. Fetch the editor interface.
+ KRegExpEditorInterface *reEditor =
+ static_cast<KRegExpEditorInterface *>(editorDialog->qt_cast( "KRegExpEditorInterface" ) );
+ Q_ASSERT(reEditor); // This should not fail!
+ reEditor->setRegExp(patternInput->text());
+ int dlgResult = editorDialog->exec();
+
+ if(dlgResult == QDialog::Accepted)
+ {
+ QString re = reEditor->regExp();
+ patternInput->setText(re);
+ }
+
+ delete editorDialog;
+ }
+}
+
+#include "autoreplace_preferences.moc"
diff --git a/konversation/src/autoreplace_preferences.h b/konversation/src/autoreplace_preferences.h
new file mode 100644
index 0000000..303487c
--- /dev/null
+++ b/konversation/src/autoreplace_preferences.h
@@ -0,0 +1,57 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef AUTOREPLACECONFIG_H
+#define AUTOREPLACECONFIG_H
+
+#include "autoreplace_preferencesui.h"
+#include "konvisettingspage.h"
+
+
+class Autoreplace_Config : public Autoreplace_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit Autoreplace_Config(QWidget* parent, const char* name=NULL);
+ ~Autoreplace_Config();
+
+ virtual void saveSettings();
+ virtual void loadSettings();
+ virtual void restorePageToDefaults();
+
+ virtual bool hasChanged();
+
+ signals:
+ void modified();
+
+ protected slots:
+ void entrySelected(QListViewItem* autoreplaceEntry);
+ void directionChanged(int newDirection);
+ void patternChanged(const QString& newPattern);
+ void replacementChanged(const QString& newReplacement);
+ void addEntry();
+ void removeEntry();
+ void sort(int column);
+ void disableSort();
+ void showRegExpEditor();
+
+ protected:
+ void setAutoreplaceListView(const QStringList &autoreplaceList);
+
+ bool m_newItemSelected;
+
+ QStringList m_oldAutoreplaceList;
+ QStringList currentAutoreplaceList();
+};
+
+#endif
diff --git a/konversation/src/autoreplace_preferencesui.ui b/konversation/src/autoreplace_preferencesui.ui
new file mode 100644
index 0000000..db6521c
--- /dev/null
+++ b/konversation/src/autoreplace_preferencesui.ui
@@ -0,0 +1,292 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Autoreplace_ConfigUI</class>
+<author>Dario Abatianni</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Autoreplace_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>586</width>
+ <height>576</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>newButton</cstring>
+ </property>
+ <property name="text">
+ <string>Ne&amp;w</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="text">
+ <string>R&amp;emove</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>506</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="KListView" row="0" column="0">
+ <column>
+ <property name="text">
+ <string>RegEx</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Replace In</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Find</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Replace With</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>patternListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>false</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>regExpEditorButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>directionLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Replace in:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>patternInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>replacementInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>patternLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Find:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>replacementLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Replace with:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>directionCombo</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>patternListView</tabstop>
+ <tabstop>directionCombo</tabstop>
+ <tabstop>patternInput</tabstop>
+ <tabstop>regExpEditorButton</tabstop>
+ <tabstop>replacementInput</tabstop>
+ <tabstop>newButton</tabstop>
+ <tabstop>removeButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/awaymanager.cpp b/konversation/src/awaymanager.cpp
new file mode 100644
index 0000000..5676bd1
--- /dev/null
+++ b/konversation/src/awaymanager.cpp
@@ -0,0 +1,372 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (c) 1999 Martin R. Jones <mjones@kde.org>
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#include "awaymanager.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "connectionmanager.h"
+#include "server.h"
+#include "preferences.h"
+
+#include <qtimer.h>
+
+#include <dcopref.h>
+#include <kaction.h>
+#include <klocale.h>
+
+#include <kdebug.h>
+
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#ifdef HAVE_XSCREENSAVER
+#define HasScreenSaver
+#include <X11/extensions/scrnsaver.h>
+#endif
+#endif
+
+// Don't use XIdle for now, it's experimental.
+#undef HAVE_XIDLE
+#undef HasXidle
+
+
+struct AwayManagerPrivate
+{
+ int mouseX;
+ int mouseY;
+ unsigned int mouseMask;
+#ifdef Q_WS_X11
+ Window root;
+ Screen* screen;
+ Time xIdleTime;
+#endif
+ bool useXidle;
+ bool useMit;
+};
+
+AwayManager::AwayManager(QObject* parent) : QObject(parent)
+{
+ int dummy = 0;
+ dummy = dummy;
+
+ d = new AwayManagerPrivate;
+
+ d->mouseX = d->mouseY = 0;
+ d->mouseMask = 0;
+ d->useXidle = false;
+ d->useMit = false;
+
+ m_connectionManager = static_cast<KonversationApplication*>(kapp)->getConnectionManager();
+
+#ifdef Q_WS_X11
+ Display* display = qt_xdisplay();
+ d->root = DefaultRootWindow(display);
+ d->screen = ScreenOfDisplay(display, DefaultScreen (display));
+
+ d->xIdleTime = 0;
+#endif
+
+#ifdef HasXidle
+ d->useXidle = XidleQueryExtension(qt_xdisplay(), &dummy, &dummy);
+#endif
+
+#ifdef HasScreenSaver
+ if (!d->useXidle)
+ d->useMit = XScreenSaverQueryExtension(qt_xdisplay(), &dummy, &dummy);
+#endif
+
+ m_activityTimer = new QTimer(this, "AwayTimer");
+ connect(m_activityTimer, SIGNAL(timeout()), this, SLOT(checkActivity()));
+
+ m_idleTime.start();
+}
+
+AwayManager::~AwayManager()
+{
+ delete d;
+}
+
+void AwayManager::identitiesChanged()
+{
+ QValueList<int> newIdentityList;
+
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ IdentityPtr identity = server->getIdentity();
+
+ if (identity && identity->getAutomaticAway() && server->isConnected())
+ newIdentityList.append(identity->id());
+ }
+
+ m_identitiesOnAutoAway = newIdentityList;
+
+ toggleTimer();
+}
+
+void AwayManager::identityOnline(int identityId)
+{
+ IdentityPtr identity = Preferences::identityById(identityId);
+
+ if (identity && identity->getAutomaticAway() &&
+ !m_identitiesOnAutoAway.contains(identityId))
+ {
+ m_identitiesOnAutoAway.append(identityId);
+
+ toggleTimer();
+ }
+}
+
+void AwayManager::identityOffline(int identityId)
+{
+ if (m_identitiesOnAutoAway.contains(identityId))
+ {
+ m_identitiesOnAutoAway.remove(identityId);
+
+ toggleTimer();
+ }
+}
+
+void AwayManager::toggleTimer()
+{
+ if (m_identitiesOnAutoAway.count() > 0)
+ {
+ if (!m_activityTimer->isActive())
+ m_activityTimer->start(Preferences::autoAwayPollInterval() * 1000);
+ }
+ else if (m_activityTimer->isActive())
+ m_activityTimer->stop();
+}
+
+void AwayManager::checkActivity()
+{
+ // Allow the event loop to be called, to avoid deadlock.
+ static bool rentrencyProtection = false;
+ if (rentrencyProtection) return;
+
+ rentrencyProtection = true;
+
+ DCOPRef screenSaver("kdesktop", "KScreensaverIface");
+ DCOPReply isBlanked = screenSaver.callExt("isBlanked", DCOPRef::UseEventLoop, 10);
+
+ rentrencyProtection = false;
+
+ if (!(isBlanked.isValid() && isBlanked.type == "bool" && ((bool)isBlanked)))
+ implementIdleAutoAway(Xactivity());
+}
+
+bool AwayManager::Xactivity()
+{
+ bool activity = false;
+
+#ifdef Q_WS_X11
+ Display* display = qt_xdisplay();
+ Window dummyW;
+ int dummyC;
+ unsigned int mask;
+ int rootX;
+ int rootY;
+
+ if (!XQueryPointer (display, d->root, &(d->root), &dummyW, &rootX, &rootY,
+ &dummyC, &dummyC, &mask))
+ {
+ // Figure out which screen the pointer has moved to.
+ for (int i = 0; i < ScreenCount(display); i++)
+ {
+ if (d->root == RootWindow(display, i))
+ {
+ d->screen = ScreenOfDisplay (display, i);
+
+ break;
+ }
+ }
+ }
+
+ Time xIdleTime = 0;
+
+ #ifdef HasXidle
+ if (d->useXidle)
+ XGetIdleTime(display, &xIdleTime);
+ else
+ #endif
+ {
+ #ifdef HasScreenSaver
+ if (d->useMit)
+ {
+ static XScreenSaverInfo* mitInfo = 0;
+ if (!mitInfo) mitInfo = XScreenSaverAllocInfo();
+ XScreenSaverQueryInfo (display, d->root, mitInfo);
+ xIdleTime = mitInfo->idle;
+ }
+ #endif
+ }
+
+ if (rootX != d->mouseX || rootY != d->mouseY || mask != d->mouseMask
+ || ((d->useXidle || d->useMit) && xIdleTime < d->xIdleTime + 2000))
+ {
+ // Set by setManagedIdentitiesAway() to skip X-based activity checking for one
+ // round, to avoid jumping on residual mouse activity after manual screensaver
+ // activation.
+ if (d->mouseX != -1) activity = true;
+
+ d->mouseX = rootX;
+ d->mouseY = rootY;
+ d->mouseMask = mask;
+ d->xIdleTime = xIdleTime;
+ }
+#endif
+
+ return activity;
+}
+
+void AwayManager::implementIdleAutoAway(bool activity)
+{
+ if (activity)
+ {
+ m_idleTime.start();
+
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ IdentityPtr identity = server->getIdentity();
+
+ if (m_identitiesOnAutoAway.contains(identity->id()) && identity->getAutomaticUnaway()
+ && server->isConnected() && server->isAway())
+ {
+ server->requestUnaway();
+ }
+ }
+ }
+ else
+ {
+ long int idleTime = m_idleTime.elapsed() / 1000;
+
+ QValueList<int> identitiesIdleTimeExceeded;
+ QValueList<int>::ConstIterator it;
+
+ for (it = m_identitiesOnAutoAway.begin(); it != m_identitiesOnAutoAway.end(); ++it)
+ {
+ if (idleTime >= Preferences::identityById((*it))->getAwayInactivity() * 60)
+ identitiesIdleTimeExceeded.append((*it));
+ }
+
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ int identityId = server->getIdentity()->id();
+
+ if (identitiesIdleTimeExceeded.contains(identityId) && server->isConnected() && !server->isAway())
+ server->requestAway();
+ }
+ }
+}
+
+void AwayManager::setManagedIdentitiesAway()
+{
+ // Used to skip X-based activity checking for one round, to avoid jumping
+ // on residual mouse activity after manual screensaver activation.
+ d->mouseX = -1;
+
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ if (m_identitiesOnAutoAway.contains(server->getIdentity()->id()) && server->isConnected() && !server->isAway())
+ server->requestAway();
+ }
+}
+
+void AwayManager::setManagedIdentitiesUnaway()
+{
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ IdentityPtr identity = server->getIdentity();
+
+ if (m_identitiesOnAutoAway.contains(identity->id()) && identity->getAutomaticUnaway()
+ && server->isConnected() && server->isAway())
+ {
+ server->requestUnaway();
+ }
+ }
+}
+
+void AwayManager::requestAllAway(const QString& reason)
+{
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ if (server->isConnected()) server->requestAway(reason);
+}
+
+void AwayManager::requestAllUnaway()
+{
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ if (server->isConnected() && server->isAway()) server->requestUnaway();
+}
+
+void AwayManager::toggleGlobalAway(bool away)
+{
+ if (away)
+ requestAllAway();
+ else
+ requestAllUnaway();
+}
+
+void AwayManager::updateGlobalAwayAction(bool away)
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ KToggleAction* awayAction = static_cast<KToggleAction*>(konvApp->getMainWindow()->actionCollection()->action("toggle_away"));
+
+ if (!awayAction) return;
+
+ if (away)
+ {
+ QPtrList<Server> serverList = m_connectionManager->getServerList();
+ Server* server = 0;
+ uint awayCount = 0;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ if (server->isAway())
+ awayCount++;
+ }
+
+ if (awayCount == serverList.count())
+ {
+ awayAction->setChecked(true);
+ awayAction->setIcon("konversationaway");
+ }
+ }
+ else
+ {
+ awayAction->setChecked(false);
+ awayAction->setIcon("konversationavailable");
+ }
+}
+
+#include "awaymanager.moc"
diff --git a/konversation/src/awaymanager.h b/konversation/src/awaymanager.h
new file mode 100644
index 0000000..76819f3
--- /dev/null
+++ b/konversation/src/awaymanager.h
@@ -0,0 +1,73 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef AWAYMANAGER_H
+#define AWAYMANAGER_H
+
+
+#include <qobject.h>
+#include <qdatetime.h>
+
+
+class ConnectionManager;
+
+class QTimer;
+
+
+struct AwayManagerPrivate;
+
+
+class AwayManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit AwayManager(QObject* parent = 0);
+ ~AwayManager();
+
+
+ public slots:
+ void identitiesChanged();
+
+ void identityOnline(int identityId);
+ void identityOffline(int identityId);
+
+ void requestAllAway(const QString& reason = "");
+ void requestAllUnaway();
+
+ void setManagedIdentitiesAway();
+ void setManagedIdentitiesUnaway();
+
+ void toggleGlobalAway(bool away);
+ void updateGlobalAwayAction(bool away);
+
+
+ private slots:
+ void checkActivity();
+
+
+ private:
+ void toggleTimer();
+ bool Xactivity();
+
+ void implementIdleAutoAway(bool activity);
+
+ AwayManagerPrivate* d;
+
+ QTime m_idleTime;
+ QTimer* m_activityTimer;
+
+ QValueList<int> m_identitiesOnAutoAway;
+
+ ConnectionManager* m_connectionManager;
+};
+
+#endif
diff --git a/konversation/src/blowfish/BlowfishCbc.cpp b/konversation/src/blowfish/BlowfishCbc.cpp
new file mode 100644
index 0000000..fd8ff2f
--- /dev/null
+++ b/konversation/src/blowfish/BlowfishCbc.cpp
@@ -0,0 +1,654 @@
+/*
+ Public Domain. Without any warranty.
+
+ Copyright (C) George Anescu <unknown@unknown>
+ Copyright (C) mouser <fvarick@users.sourceforge.net>
+*/
+
+//---------------------------------------------------------------------------
+//The implementation of blowfish for CBC mode is from http://www.thecodeproject.com/cpp/blowfish.asp by George Anescu
+// I removed exception handling, so it is callers responsibility to insure
+// that strings are length multiples of 8 bytes
+// -mouser 1/08/05
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// Mouser 1/14/05
+// I'm not sure this implementation of cbc is exactly the same as other cbc
+// algorithms you may find, though the different is insignificant and just
+// requires you to call your other algorithm slightly differently.
+// This blowfish cbc class basically pretends that the IV is 00000000 but
+// but that the first 8byte block of the plaintext to encrypt is treated
+// like the IV (ie its a random nonce).
+// In other words, it does not accept a separate IV, but expects the first
+// 8bytes to be the IV (this is a slight difference from textbook cbc since
+// the encrypted IV is now part of the ciphertext).
+// This actually works out well for us since we want to send the IV to
+// our recipient as part of our message.
+// ANYWAY, bottom line is, if you have a blowfish implementation that takes
+// an explicit IV parameter as its input, just pass it 0 as the IV, and
+// when encrypting, prefix your plaintext with an 8 byte random nonce, and
+// you will have a compatible algorithm. Remember its not that this
+// algorithm uses a fixed IV of 0, but rather that the IV is the first
+// block of the test to encrypt.
+//---------------------------------------------------------------------------
+
+
+////////////////////////////////////////////////////////////////////////////
+///
+// BlowFish.cpp
+//
+// Implementation of Bruce Schneier's BLOWFISH algorithm from "Applied
+// Cryptography", Second Edition.
+
+#include "BlowfishCbc.h"
+
+#include <cstring>
+#include <memory.h>
+
+//#include <exception>
+
+//Initialization with a fixed string which consists of the hexadecimal digits of PI (less the initial 3)
+//P-array, 18 32-bit subkeys
+const unsigned int CBlowFish::scm_auiInitP[18] = {
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+};
+
+//Four 32-bit S-boxes with 256 entries each
+const unsigned int CBlowFish::scm_auiInitS[4][256] = {
+ //0
+ {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a},
+
+ //1
+ {0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7},
+
+ //2
+ {0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0},
+
+ //3
+ {0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6}
+};
+
+//Constructor - Initialize the P and S boxes for a given Key
+CBlowFish::CBlowFish(unsigned char* ucKey, size_t keysize, const SBlock& roChain) : m_oChain0(roChain), m_oChain(roChain)
+{
+ if(keysize<1)
+ // throw exception("Incorrect key length");
+ return;
+
+
+ //Check the Key - the key length should be between 1 and 56 bytes
+ if(keysize>56)
+ keysize = 56;
+ unsigned char aucLocalKey[56];
+ unsigned int i, j;
+ memcpy(aucLocalKey, ucKey, keysize);
+ //Reflexive Initialization of the Blowfish.
+ //Generating the Subkeys from the Key flood P and S boxes with PI
+ memcpy(m_auiP, scm_auiInitP, sizeof m_auiP);
+ memcpy(m_auiS, scm_auiInitS, sizeof m_auiS);
+ //Load P boxes with key bytes
+ const unsigned char* p = aucLocalKey;
+ unsigned int x=0;
+ //Repeatedly cycle through the key bits until the entire P array has been XORed with key bits
+ unsigned int iCount = 0;
+ for(i=0; i<18; i++)
+ {
+ x=0;
+ for(int n=4; n--; )
+ {
+ //int iVal = (int)(*p);
+ x <<= 8;
+ x |= *(p++);
+ iCount++;
+ if(iCount == keysize)
+ {
+ //All bytes used, so recycle bytes
+ iCount = 0;
+ p = aucLocalKey;
+ }
+ }
+ m_auiP[i] ^= x;
+ }
+ //Reflect P and S boxes through the evolving Blowfish
+ SBlock block(0UL,0UL); //all-zero block
+ for(i=0; i<18; )
+ Encrypt(block), m_auiP[i++] = block.m_uil, m_auiP[i++] = block.m_uir;
+ for(j=0; j<4; j++)
+ for(int k=0; k<256; )
+ Encrypt(block), m_auiS[j][k++] = block.m_uil, m_auiS[j][k++] = block.m_uir;
+}
+
+//Sixteen Round Encipher of Block
+void CBlowFish::Encrypt(SBlock& block)
+{
+ unsigned int uiLeft = block.m_uil;
+ unsigned int uiRight = block.m_uir;
+ uiLeft ^= m_auiP[0];
+ uiRight ^= F(uiLeft)^m_auiP[1]; uiLeft ^= F(uiRight)^m_auiP[2];
+ uiRight ^= F(uiLeft)^m_auiP[3]; uiLeft ^= F(uiRight)^m_auiP[4];
+ uiRight ^= F(uiLeft)^m_auiP[5]; uiLeft ^= F(uiRight)^m_auiP[6];
+ uiRight ^= F(uiLeft)^m_auiP[7]; uiLeft ^= F(uiRight)^m_auiP[8];
+ uiRight ^= F(uiLeft)^m_auiP[9]; uiLeft ^= F(uiRight)^m_auiP[10];
+ uiRight ^= F(uiLeft)^m_auiP[11]; uiLeft ^= F(uiRight)^m_auiP[12];
+ uiRight ^= F(uiLeft)^m_auiP[13]; uiLeft ^= F(uiRight)^m_auiP[14];
+ uiRight ^= F(uiLeft)^m_auiP[15]; uiLeft ^= F(uiRight)^m_auiP[16];
+ uiRight ^= m_auiP[17];
+ block.m_uil = uiRight;
+ block.m_uir = uiLeft;
+}
+
+//Sixteen Round Decipher of SBlock
+void CBlowFish::Decrypt(SBlock& block)
+{
+ unsigned int uiLeft = block.m_uil;
+ unsigned int uiRight = block.m_uir;
+ uiLeft ^= m_auiP[17];
+ uiRight ^= F(uiLeft)^m_auiP[16]; uiLeft ^= F(uiRight)^m_auiP[15];
+ uiRight ^= F(uiLeft)^m_auiP[14]; uiLeft ^= F(uiRight)^m_auiP[13];
+ uiRight ^= F(uiLeft)^m_auiP[12]; uiLeft ^= F(uiRight)^m_auiP[11];
+ uiRight ^= F(uiLeft)^m_auiP[10]; uiLeft ^= F(uiRight)^m_auiP[9];
+ uiRight ^= F(uiLeft)^m_auiP[8]; uiLeft ^= F(uiRight)^m_auiP[7];
+ uiRight ^= F(uiLeft)^m_auiP[6]; uiLeft ^= F(uiRight)^m_auiP[5];
+ uiRight ^= F(uiLeft)^m_auiP[4]; uiLeft ^= F(uiRight)^m_auiP[3];
+ uiRight ^= F(uiLeft)^m_auiP[2]; uiLeft ^= F(uiRight)^m_auiP[1];
+ uiRight ^= m_auiP[0];
+ block.m_uil = uiRight;
+ block.m_uir = uiLeft;
+}
+
+//Semi-Portable Byte Shuffling
+inline void BytesToBlock(unsigned char const* p, SBlock& b)
+{
+ unsigned int y;
+ //Left
+ b.m_uil = 0;
+ y = *p++;
+ y <<= 24;
+ b.m_uil |= y;
+ y = *p++;
+ y <<= 16;
+ b.m_uil |= y;
+ y = *p++;
+ y <<= 8;
+ b.m_uil |= y;
+ y = *p++;
+ b.m_uil |= y;
+ //Right
+ b.m_uir = 0;
+ y = *p++;
+ y <<= 24;
+ b.m_uir |= y;
+ y = *p++;
+ y <<= 16;
+ b.m_uir |= y;
+ y = *p++;
+ y <<= 8;
+ b.m_uir |= y;
+ y = *p++;
+ b.m_uir |= y;
+}
+
+inline void BlockToBytes(SBlock const& b, unsigned char* p)
+{
+ unsigned int y;
+ //Right
+ y = b.m_uir;
+ *--p = Byte(y);
+ y = b.m_uir >> 8;
+ *--p = Byte(y);
+ y = b.m_uir >> 16;
+ *--p = Byte(y);
+ y = b.m_uir >> 24;
+ *--p = Byte(y);
+ //Left
+ y = b.m_uil;
+ *--p = Byte(y);
+ y = b.m_uil >> 8;
+ *--p = Byte(y);
+ y = b.m_uil >> 16;
+ *--p = Byte(y);
+ y = b.m_uil >> 24;
+ *--p = Byte(y);
+}
+
+//Encrypt Buffer in Place
+//Returns false if n is multiple of 8
+void CBlowFish::Encrypt(unsigned char* buf, size_t n, int iMode)
+{
+ //Check the buffer's length - should be > 0 and multiple of 8
+ if((n==0)||(n%8!=0))
+ //throw exception("Incorrect buffer length");
+ return;
+
+ SBlock work;
+ if(iMode == CBC) //CBC mode, using the Chain
+ {
+ SBlock chain(m_oChain);
+ for(; n >= 8; n -= 8)
+ {
+ BytesToBlock(buf, work);
+ work ^= chain;
+ Encrypt(work);
+ chain = work;
+ BlockToBytes(work, buf+=8);
+ }
+ }
+ else if(iMode == CFB) //CFB mode, using the Chain
+ {
+ SBlock chain(m_oChain);
+ for(; n >= 8; n -= 8)
+ {
+ Encrypt(chain);
+ BytesToBlock(buf, work);
+ work ^= chain;
+ chain = work;
+ BlockToBytes(work, buf+=8);
+ }
+ }
+ else //ECB mode, not using the Chain
+ {
+ for(; n >= 8; n -= 8)
+ {
+ BytesToBlock(buf, work);
+ Encrypt(work);
+ BlockToBytes(work, buf+=8);
+ }
+ }
+}
+
+//Decrypt Buffer in Place
+//Returns false if n is multiple of 8
+void CBlowFish::Decrypt(unsigned char* buf, size_t n, int iMode)
+{
+ //Check the buffer's length - should be > 0 and multiple of 8
+ if((n==0)||(n%8!=0))
+ //throw exception("Incorrect buffer length");
+ return;
+
+ SBlock work;
+ if(iMode == CBC) //CBC mode, using the Chain
+ {
+ SBlock crypt, chain(m_oChain);
+ for(; n >= 8; n -= 8)
+ {
+ BytesToBlock(buf, work);
+ crypt = work;
+ Decrypt(work);
+ work ^= chain;
+ chain = crypt;
+ BlockToBytes(work, buf+=8);
+ }
+ }
+ else if(iMode == CFB) //CFB mode, using the Chain, not using Decrypt()
+ {
+ SBlock crypt, chain(m_oChain);
+ for(; n >= 8; n -= 8)
+ {
+ BytesToBlock(buf, work);
+ Encrypt(chain);
+ crypt = work;
+ work ^= chain;
+ chain = crypt;
+ BlockToBytes(work, buf+=8);
+ }
+ }
+ else //ECB mode, not using the Chain
+ {
+ for(; n >= 8; n -= 8)
+ {
+ BytesToBlock(buf, work);
+ Decrypt(work);
+ BlockToBytes(work, buf+=8);
+ }
+ }
+}
+
+//Encrypt from Input Buffer to Output Buffer
+//Returns false if n is multiple of 8
+void CBlowFish::Encrypt(const unsigned char* in, unsigned char* out, size_t n, int iMode)
+{
+ //Check the buffer's length - should be > 0 and multiple of 8
+ if((n==0)||(n%8!=0))
+ //throw exception("Incorrect buffer length");
+ return;
+
+ SBlock work;
+ if(iMode == CBC) //CBC mode, using the Chain
+ {
+ SBlock chain(m_oChain);
+ for(; n >= 8; n -= 8, in += 8)
+ {
+ BytesToBlock(in, work);
+ work ^= chain;
+ Encrypt(work);
+ chain = work;
+ BlockToBytes(work, out+=8);
+ }
+ }
+ else if(iMode == CFB) //CFB mode, using the Chain
+ {
+ SBlock chain(m_oChain);
+ for(; n >= 8; n -= 8, in += 8)
+ {
+ Encrypt(chain);
+ BytesToBlock(in, work);
+ work ^= chain;
+ chain = work;
+ BlockToBytes(work, out+=8);
+ }
+ }
+ else //ECB mode, not using the Chain
+ {
+ for(; n >= 8; n -= 8, in += 8)
+ {
+ BytesToBlock(in, work);
+ Encrypt(work);
+ BlockToBytes(work, out+=8);
+ }
+ }
+}
+
+//Decrypt from Input Buffer to Output Buffer
+//Returns false if n is multiple of 8
+void CBlowFish::Decrypt(const unsigned char* in, unsigned char* out, size_t n, int iMode)
+{
+ //Check the buffer's length - should be > 0 and multiple of 8
+ if((n==0)||(n%8!=0))
+ //throw exception("Incorrect buffer length");
+ return;
+
+ SBlock work;
+ if(iMode == CBC) //CBC mode, using the Chain
+ {
+ SBlock crypt, chain(m_oChain);
+ for(; n >= 8; n -= 8, in += 8)
+ {
+ BytesToBlock(in, work);
+ crypt = work;
+ Decrypt(work);
+ work ^= chain;
+ chain = crypt;
+ BlockToBytes(work, out+=8);
+ }
+ }
+ else if(iMode == CFB) //CFB mode, using the Chain, not using Decrypt()
+ {
+ SBlock crypt, chain(m_oChain);
+ for(; n >= 8; n -= 8, in += 8)
+ {
+ BytesToBlock(in, work);
+ Encrypt(chain);
+ crypt = work;
+ work ^= chain;
+ chain = crypt;
+ BlockToBytes(work, out+=8);
+ }
+ }
+ else //ECB mode, not using the Chain
+ {
+ for(; n >= 8; n -= 8, in += 8)
+ {
+ BytesToBlock(in, work);
+ Decrypt(work);
+ BlockToBytes(work, out+=8);
+ }
+ }
+}
+
diff --git a/konversation/src/blowfish/BlowfishCbc.h b/konversation/src/blowfish/BlowfishCbc.h
new file mode 100644
index 0000000..2abb335
--- /dev/null
+++ b/konversation/src/blowfish/BlowfishCbc.h
@@ -0,0 +1,228 @@
+/*
+ Public Domain. Without any warranty.
+
+ Copyright (C) George Anescu <unknown@unknown>
+ Copyright (C) mouser <fvarick@users.sourceforge.net>
+*/
+
+//---------------------------------------------------------------------------
+//The implementation of blowfish for CBC mode is from http://www.thecodeproject.com/cpp/blowfish.asp by George Anescu
+// I removed exception handling, so it is callers responsibility to insure
+// that strings are length multiples of 8 bytes
+// -mouser 1/08/05
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// Mouser 1/14/05
+// I'm not sure this implementation of cbc is exactly the same as other cbc
+// algorithms you may find, though the difference is insignificant and just
+// requires you to call your other algorithm slightly differently.
+// This blowfish cbc class basically pretends that the IV is 00000000 but
+// but that the first 8byte block of the plaintext to encrypt is treated
+// like the IV (ie its a random nonce).
+// In other words, it does not accept a separate IV, but expects the first
+// 8bytes to be the IV (this is a slight difference from textbook cbc since
+// the encrypted IV is now part of the ciphertext).
+// This actually works out well for us since we want to send the IV to
+// our recipient as part of our message.
+// ANYWAY, bottom line is, if you have a blowfish implementation that takes
+// an explicit IV parameter as its input, just pass it 0 as the IV, and
+// when encrypting, prefix your plaintext with an 8 byte random nonce, and
+// you will have a compatible algorithm. Remember its not that this
+// algorithm uses a fixed IV of 0, but rather that the IV is the first
+// block of the test to encrypt.
+//---------------------------------------------------------------------------
+
+
+
+////////////////////////////////////////////////////////////////////////////
+///
+// Blowfish.h Header File
+//
+// BLOWFISH ENCRYPTION ALGORITHM
+//
+// Encryption and Decryption of Byte Strings using the Blowfish Encryption Algorithm.
+// Blowfish is a block cipher that encrypts data in 8-byte blocks. The algorithm consists
+// of two parts: a key-expansion part and a data-ancryption part. Key expansion converts a
+// variable key of at least 1 and at most 56 bytes into several subkey arrays totaling
+// 4168 bytes. Blowfish has 16 rounds. Each round consists of a key-dependent permutation,
+// and a key and data-dependent substitution. All operations are XORs and additions on 32-bit words.
+// The only additional operations are four indexed array data lookups per round.
+// Blowfish uses a large number of subkeys. These keys must be precomputed before any data
+// encryption or decryption. The P-array consists of 18 32-bit subkeys: P0, P1,...,P17.
+// There are also four 32-bit S-boxes with 256 entries each: S0,0, S0,1,...,S0,255;
+// S1,0, S1,1,...,S1,255; S2,0, S2,1,...,S2,255; S3,0, S3,1,...,S3,255;
+//
+// The Electronic Code Book (ECB), Cipher Block Chaining (CBC) and Cipher Feedback modes
+// are used:
+//
+// In ECB mode if the same block is encrypted twice with the same key, the resulting
+// ciphertext blocks are the same.
+//
+// In CBC Mode a ciphertext block is obtained by first xoring the
+// plaintext block with the previous ciphertext block, and encrypting the resulting value.
+//
+// In CFB mode a ciphertext block is obtained by encrypting the previous ciphertext block
+// and xoring the resulting value with the plaintext
+//
+// The previous ciphertext block is usually stored in an Initialization Vector (IV).
+// An Initialization Vector of zero is commonly used for the first block, though other
+// arrangements are also in use.
+
+/*
+http://www.counterpane.com/vectors.txt
+Test vectors by Eric Young. These tests all assume Blowfish with 16
+rounds.
+
+All data is shown as a hex string with 012345 loading as
+data[0]=0x01;
+data[1]=0x23;
+data[2]=0x45;
+ecb test data (taken from the DES validation tests)
+
+key bytes clear bytes cipher bytes
+0000000000000000 0000000000000000 4EF997456198DD78
+FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 51866FD5B85ECB8A
+3000000000000000 1000000000000001 7D856F9A613063F2 ???
+1111111111111111 1111111111111111 2466DD878B963C9D
+0123456789ABCDEF 1111111111111111 61F9C3802281B096
+1111111111111111 0123456789ABCDEF 7D0CC630AFDA1EC7
+0000000000000000 0000000000000000 4EF997456198DD78
+FEDCBA9876543210 0123456789ABCDEF 0ACEAB0FC6A0A28D
+7CA110454A1A6E57 01A1D6D039776742 59C68245EB05282B
+0131D9619DC1376E 5CD54CA83DEF57DA B1B8CC0B250F09A0
+07A1133E4A0B2686 0248D43806F67172 1730E5778BEA1DA4
+3849674C2602319E 51454B582DDF440A A25E7856CF2651EB
+04B915BA43FEB5B6 42FD443059577FA2 353882B109CE8F1A
+0113B970FD34F2CE 059B5E0851CF143A 48F4D0884C379918
+0170F175468FB5E6 0756D8E0774761D2 432193B78951FC98
+43297FAD38E373FE 762514B829BF486A 13F04154D69D1AE5
+07A7137045DA2A16 3BDD119049372802 2EEDDA93FFD39C79
+04689104C2FD3B2F 26955F6835AF609A D887E0393C2DA6E3
+37D06BB516CB7546 164D5E404F275232 5F99D04F5B163969
+1F08260D1AC2465E 6B056E18759F5CCA 4A057A3B24D3977B
+584023641ABA6176 004BD6EF09176062 452031C1E4FADA8E
+025816164629B007 480D39006EE762F2 7555AE39F59B87BD
+49793EBC79B3258F 437540C8698F3CFA 53C55F9CB49FC019
+4FB05E1515AB73A7 072D43A077075292 7A8E7BFA937E89A3
+49E95D6D4CA229BF 02FE55778117F12A CF9C5D7A4986ADB5
+018310DC409B26D6 1D9D5C5018F728C2 D1ABB290658BC778
+1C587F1C13924FEF 305532286D6F295A 55CB3774D13EF201
+0101010101010101 0123456789ABCDEF FA34EC4847B268B2
+1F1F1F1F0E0E0E0E 0123456789ABCDEF A790795108EA3CAE
+E0FEE0FEF1FEF1FE 0123456789ABCDEF C39E072D9FAC631D
+0000000000000000 FFFFFFFFFFFFFFFF 014933E0CDAFF6E4
+FFFFFFFFFFFFFFFF 0000000000000000 F21E9A77B71C49BC
+0123456789ABCDEF 0000000000000000 245946885754369A
+FEDCBA9876543210 FFFFFFFFFFFFFFFF 6B5C5A9C5D9E0A5A
+
+set_key test data
+data[8]= FEDCBA9876543210
+c=F9AD597C49DB005E k[ 1]=F0
+c=E91D21C1D961A6D6 k[ 2]=F0E1
+c=E9C2B70A1BC65CF3 k[ 3]=F0E1D2
+c=BE1E639408640F05 k[ 4]=F0E1D2C3
+c=B39E44481BDB1E6E k[ 5]=F0E1D2C3B4
+c=9457AA83B1928C0D k[ 6]=F0E1D2C3B4A5
+c=8BB77032F960629D k[ 7]=F0E1D2C3B4A596
+c=E87A244E2CC85E82 k[ 8]=F0E1D2C3B4A59687
+c=15750E7A4F4EC577 k[ 9]=F0E1D2C3B4A5968778
+c=122BA70B3AB64AE0 k[10]=F0E1D2C3B4A596877869
+c=3A833C9AFFC537F6 k[11]=F0E1D2C3B4A5968778695A
+c=9409DA87A90F6BF2 k[12]=F0E1D2C3B4A5968778695A4B
+c=884F80625060B8B4 k[13]=F0E1D2C3B4A5968778695A4B3C
+c=1F85031C19E11968 k[14]=F0E1D2C3B4A5968778695A4B3C2D
+c=79D9373A714CA34F k[15]=F0E1D2C3B4A5968778695A4B3C2D1E ???
+c=93142887EE3BE15C k[16]=F0E1D2C3B4A5968778695A4B3C2D1E0F
+c=03429E838CE2D14B k[17]=F0E1D2C3B4A5968778695A4B3C2D1E0F00
+c=A4299E27469FF67B k[18]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011
+c=AFD5AED1C1BC96A8 k[19]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122
+c=10851C0E3858DA9F k[20]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233
+c=E6F51ED79B9DB21F k[21]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344
+c=64A6E14AFD36B46F k[22]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122334455
+c=80C7D7D45A5479AD k[23]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233445566
+c=05044B62FA52D080 k[24]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677
+
+chaining mode test data
+key[16] = 0123456789ABCDEFF0E1D2C3B4A59687
+iv[8] = FEDCBA9876543210
+data[29] = "7654321 Now is the time for " (includes trailing '\0')
+data[29] = 37363534333231204E6F77206973207468652074696D6520666F722000
+cbc cipher text
+cipher[32]= 6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC
+cfb64 cipher text cipher[29]=
+E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3
+ofb64 cipher text cipher[29]=
+E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA
+
+*/
+
+//---------------------------------------------------------------------------
+// includes for size_t
+#include <stddef.h>
+//---------------------------------------------------------------------------
+
+
+#ifndef BLOWFISH_H
+#define BLOWFISH_H
+
+//Block Structure
+struct SBlock
+{
+ //Constructors
+ SBlock(unsigned int l=0, unsigned int r=0) : m_uil(l), m_uir(r) {}
+ //Copy Constructor
+ SBlock(const SBlock& roBlock) : m_uil(roBlock.m_uil), m_uir(roBlock.m_uir) {}
+ SBlock& operator^=(SBlock& b) { m_uil ^= b.m_uil; m_uir ^= b.m_uir; return *this; }
+ unsigned int m_uil, m_uir;
+};
+
+class CBlowFish
+{
+public:
+ enum { ECB=0, CBC=1, CFB=2 };
+
+ //Constructor - Initialize the P and S boxes for a given Key
+ CBlowFish(unsigned char* ucKey, size_t n, const SBlock& roChain = SBlock(0UL,0UL));
+
+ //Resetting the chaining block
+ void ResetChain() { m_oChain = m_oChain0; }
+
+ // Encrypt/Decrypt Buffer in Place
+ void Encrypt(unsigned char* buf, size_t n, int iMode=ECB);
+ void Decrypt(unsigned char* buf, size_t n, int iMode=ECB);
+
+ // Encrypt/Decrypt from Input Buffer to Output Buffer
+ void Encrypt(const unsigned char* in, unsigned char* out, size_t n, int iMode=ECB);
+ void Decrypt(const unsigned char* in, unsigned char* out, size_t n, int iMode=ECB);
+
+//Private Functions
+private:
+ unsigned int F(unsigned int ui);
+ void Encrypt(SBlock&);
+ void Decrypt(SBlock&);
+
+private:
+ //The Initialization Vector, by default {0, 0}
+ SBlock m_oChain0;
+ SBlock m_oChain;
+ unsigned int m_auiP[18];
+ unsigned int m_auiS[4][256];
+ static const unsigned int scm_auiInitP[18];
+ static const unsigned int scm_auiInitS[4][256];
+};
+
+//Extract low order byte
+inline unsigned char Byte(unsigned int ui)
+{
+ return (unsigned char)(ui & 0xff);
+}
+
+//Function F
+inline unsigned int CBlowFish::F(unsigned int ui)
+{
+ return ((m_auiS[0][Byte(ui>>24)] + m_auiS[1][Byte(ui>>16)]) ^ m_auiS[2][Byte(ui>>8)]) + m_auiS[3][Byte(ui)];
+}
+
+#endif // __BLOWFISH_H__
+
diff --git a/konversation/src/blowfish/Makefile.am b/konversation/src/blowfish/Makefile.am
new file mode 100644
index 0000000..07abe04
--- /dev/null
+++ b/konversation/src/blowfish/Makefile.am
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = $(all_includes) -I$(srcdir)/../
+
+noinst_LTLIBRARIES = libblowfish.la
+noinst_HEADERS = b64stuff.h BlowfishCbc.h blowfish.h mc_blowfish.h newblowfish.h oldblowfish1.h oldblowfish2.h
+
+libblowfish_la_SOURCES = b64stuff.cpp BlowfishCbc.cpp blowfish.cpp mc_blowfish.cpp newblowfish.cpp oldblowfish.cpp
+libblowfish_la_LDFLAGS = $(all_libraries) -no-undefined
diff --git a/konversation/src/blowfish/README b/konversation/src/blowfish/README
new file mode 100644
index 0000000..d949ba0
--- /dev/null
+++ b/konversation/src/blowfish/README
@@ -0,0 +1,3 @@
+b64stuff.{h,cpp} is public domain
+blowfish.{cpp,h} is written by İsmail Dönmez <ismail@kde.org> and licensed under GPLv2
+Rest of the code is from Mircryption v1.11.27 ( http://mircryption.sourceforge.net/ ) project and licensed under GPLv2
diff --git a/konversation/src/blowfish/b64stuff.cpp b/konversation/src/blowfish/b64stuff.cpp
new file mode 100644
index 0000000..72f0776
--- /dev/null
+++ b/konversation/src/blowfish/b64stuff.cpp
@@ -0,0 +1,218 @@
+/*
+ Copyright (C) John Viega <viega@viega.org>
+ Copyright (C) Matt Messier <mmessier@prilnari.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the <OWNER> nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+//---------------------------------------------------------------------------
+#include "b64stuff.h"
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+//static char b64table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+// "abcdefghijklmnopqrstuvwxyz"
+// "0123456789+/";
+static char b64table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* Accepts a binary buffer with an associated size.
+ * Returns a base64-encoded, NULL-terminated string.
+ */
+unsigned char *spc_base64_encode(unsigned char *input, size_t len, int wrap) {
+ unsigned char *output, *p;
+ size_t i = 0, mod = len % 3, toalloc;
+
+ // ATTN: WTF is this allocation function - does it fail sometimes?)
+ //toalloc = (len / 3) * 4 + (3 - mod) % 3 + 1;
+ toalloc = (len / 3) * 4 + (3 - mod) % 3 + 1 + 8;
+
+ if (wrap) {
+ toalloc += len / 57;
+ if (len % 57) toalloc++;
+ }
+
+ if (toalloc < len) return 0;
+
+ //p = output = (unsigned char *)malloc(toalloc);
+ p = output = new unsigned char [toalloc];
+
+ if (!p) return 0;
+
+ while (i < len - mod) {
+ *p++ = b64table[input[i++] >> 2];
+ *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
+ *p++ = b64table[((input[i] << 2) | (input[i + 1] >> 6)) & 0x3f];
+ *p++ = b64table[input[i + 1] & 0x3f];
+ i += 2;
+ if (wrap && !(i % 57)) *p++ = '\n';
+ }
+ if (!mod) {
+ if (wrap && i % 57) *p++ = '\n';
+ *p = 0;
+ return output;
+ } else {
+ *p++ = b64table[input[i++] >> 2];
+ *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
+ if (mod == 1) {
+ *p++ = '=';
+ *p++ = '=';
+ if (wrap) *p++ = '\n';
+ *p = 0;
+ return output;
+ } else {
+ *p++ = b64table[(input[i] << 2) & 0x3f];
+ *p++ = '=';
+ if (wrap) *p++ = '\n';
+ *p = 0;
+ return output;
+ }
+ }
+}
+//---------------------------------------------------------------------------
+
+
+
+
+//---------------------------------------------------------------------------
+static char b64revtb[256] = {
+ -3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0-15*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16-31*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /*32-47*/
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, /*48-63*/
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /*64-79*/
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /*80-95*/
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /*96-111*/
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /*112-127*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128-143*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*144-159*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*160-175*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*176-191*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*192-207*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*208-223*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*224-239*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /*240-255*/
+};
+
+static unsigned int raw_base64_decode(unsigned char *in, unsigned char *out,
+ int strict, int *err) {
+ unsigned int result = 0;
+ int x;
+ unsigned char buf[3], *p = in, pad = 0;
+
+ *err = 0;
+ while (!pad) {
+ switch ((x = b64revtb[*p++])) {
+ case -3: /* NULL TERMINATOR */
+ if (((p - 1) - in) % 4) *err = 1;
+ return result;
+ case -2: /* PADDING CHARACTER. INVALID HERE */
+ if (((p - 1) - in) % 4 < 2) {
+ *err = 1;
+ return result;
+ } else if (((p - 1) - in) % 4 == 2) {
+ /* Make sure there's appropriate padding */
+ if (*p != '=') {
+ *err = 1;
+ return result;
+ }
+ buf[2] = 0;
+ pad = 2;
+ result++;
+ break;
+ } else {
+ pad = 1;
+ result += 2;
+ break;
+ }
+ return result;
+ case -1:
+ if (strict) {
+ *err = 2;
+ return result;
+ }
+ break;
+ default:
+ switch (((p - 1) - in) % 4) {
+ case 0:
+ buf[0] = x << 2;
+ break;
+ case 1:
+ buf[0] |= (x >> 4);
+ buf[1] = x << 4;
+ break;
+ case 2:
+ buf[1] |= (x >> 2);
+ buf[2] = x << 6;
+ break;
+ case 3:
+ buf[2] |= x;
+ result += 3;
+ for (x = 0; x < 3 - pad; x++) *out++ = buf[x];
+ break;
+ }
+ break;
+ }
+ }
+ for (x = 0; x < 3 - pad; x++) *out++ = buf[x];
+ return result;
+}
+
+/* If err is non-zero on exit, then there was an incorrect padding error. We
+ * allocate enough space for all circumstances, but when there is padding, or
+ * there are characters outside the character set in the string (which we are
+ * supposed to ignore), then we end up allocating too much space. You can
+ * realloc() to the correct length if you wish.
+ */
+
+unsigned char *spc_base64_decode(unsigned char *buf, size_t *len, int strict, int *err) {
+ unsigned char *outbuf;
+
+// outbuf = (unsigned char *)malloc(3 * (strlen(buf) / 4 + 1));
+ // ATTN: WTF is this allocation function - does it fail sometimes?)
+ //outbuf = (unsigned char *)malloc(3 * (strlen((char*)buf) / 4 + 1));
+ //outbuf = (unsigned char *)malloc(3 * (strlen((char*)buf)+1));
+ //outbuf = new unsigned char [3 * (strlen((char*)buf)+1)];
+ outbuf = new unsigned char [3 * (strlen((char*)buf) / 4 + 1 + 8)];
+
+ if (!outbuf) {
+ *err = -3;
+ *len = 0;
+ return 0;
+ }
+ *len = raw_base64_decode(buf, outbuf, strict, err);
+ if (*err) {
+ //free(outbuf);
+ delete [] outbuf;
+ *len = 0;
+ outbuf = 0;
+ }
+ return outbuf;
+}
+//---------------------------------------------------------------------------
+
diff --git a/konversation/src/blowfish/b64stuff.h b/konversation/src/blowfish/b64stuff.h
new file mode 100644
index 0000000..292300b
--- /dev/null
+++ b/konversation/src/blowfish/b64stuff.h
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) John Viega <viega@viega.org>
+ Copyright (C) Matt Messier <mmessier@prilnari.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the <OWNER> nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+//---------------------------------------------------------------------------
+// To prevent multiple includes
+#ifndef _b64stuffh
+#define _b64stuffh
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+#include <stdlib.h>
+#include <string.h>
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+unsigned char *spc_base64_encode(unsigned char *input, size_t len, int wrap);
+unsigned char *spc_base64_decode(unsigned char *buf, size_t *len, int strict, int *err);
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+#endif
+//---------------------------------------------------------------------------
diff --git a/konversation/src/blowfish/blowfish.cpp b/konversation/src/blowfish/blowfish.cpp
new file mode 100644
index 0000000..7951202
--- /dev/null
+++ b/konversation/src/blowfish/blowfish.cpp
@@ -0,0 +1,129 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 İsmail Dönmez <ismail@kde.org>
+*/
+
+#include "blowfish.h"
+#include "mc_blowfish.h"
+#include "server.h"
+#include "channel.h"
+
+#include <qcstring.h>
+#include <qstringlist.h>
+
+
+namespace Konversation
+{
+ // Find n'th occurrence of separator in input and return the index
+ int findOccurrence(const QCString& input, const QCString& separator, int nth)
+ {
+ int j=1;
+ uint i;
+
+ for(i=0; i < input.length(); ++i)
+ {
+ if((input.mid(i,1) == separator))
+ {
+ if (j == nth)
+ return i;
+ else
+ ++j;
+ }
+ }
+ return i;
+ }
+
+ void decrypt(const QString& recipient, QCString& cipher, Server* server)
+ {
+ QCString key = server->getKeyForRecipient(recipient);
+
+ if(!key.isEmpty())
+ {
+ int index = findOccurrence(cipher, ":", 2);
+ QCString backup = cipher.mid(0,index+1);
+ QCString tmp = cipher.mid(index+1);
+ char* tmp2;
+
+ if(server->identifyMsgEnabled()) // Workaround braindead Freenode prefixing messages with +
+ tmp = tmp.mid(1);
+
+ if( !(tmp.mid(0,4) == "+OK ") && !(tmp.mid(0,5) == "mcps ") )
+ return;
+ else
+ cipher = tmp;
+
+ if(cipher.mid(0,5) == "mcps ")
+ cipher = cipher.mid(5);
+ else
+ cipher = cipher.mid(4);
+
+ QCString ckey( key.length()+2 );
+ QCString result( cipher.length()+1 );
+ qstrncpy(result.data(), cipher.data(), cipher.length());
+ qstrncpy(ckey.data(), key.data(), key.length()+1);
+ tmp2 = decrypt_string(ckey.data(),result.data());
+ const char *pfx="(e) ";
+ // If it's a CTCP we don't want to have the (e) interfering with the processing
+ if (tmp2[0] == 1)
+ pfx = "\x0";
+ cipher = backup+pfx+tmp2+' '+'\n'; // FIXME(??) why is there an added space here?
+ free(tmp2);
+ }
+ }
+
+ void decryptTopic(const QString& recipient, QCString& cipher, Server* server)
+ {
+ QCString key = server->getKeyForRecipient(recipient);
+
+ if(!key.isEmpty())
+ {
+ int index = findOccurrence(cipher, ":", 2);
+ QCString backup = cipher.mid(0,index+1);
+ QCString tmp = cipher.mid(index+1);
+ char* tmp2;
+
+ if(tmp.mid(0,4) == "+OK ") // FiSH style topic
+ cipher = tmp.mid(4);
+ else if(tmp.left(5) == "«m«")
+ cipher = tmp.mid(5,tmp.length()-10);
+ else
+ return;
+
+ QCString result( cipher.length()+1 );
+ QCString ckey( key.length()+2 );
+ qstrncpy(ckey.data(), key.data(), key.length()+1);
+ qstrncpy(result.data(), cipher.data(), cipher.length());
+ tmp2 = decrypt_string(ckey.data(),result.data());
+ cipher = tmp2;
+ if(cipher.mid(0,2) == "@@")
+ cipher = cipher.mid(2);
+
+ cipher = backup+"(e) "+cipher;
+ free(tmp2);
+ }
+ }
+
+ bool encrypt(const QString& key, QCString& cipher)
+ {
+ if(key.isEmpty())
+ return false;
+
+ if (cipher.left(3) == "+p ")
+ cipher = cipher.mid(3);
+ else
+ {
+ QCString ckey(key.local8Bit());
+
+ char *tmp = encrypt_string(ckey.data(), cipher.data());
+ cipher = QCString("+OK ") + tmp;
+ free(tmp);
+ }
+ return true;
+ }
+}
diff --git a/konversation/src/blowfish/blowfish.h b/konversation/src/blowfish/blowfish.h
new file mode 100644
index 0000000..0c37317
--- /dev/null
+++ b/konversation/src/blowfish/blowfish.h
@@ -0,0 +1,30 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 İsmail Dönmez <ismail@kde.org>
+*/
+
+#ifndef BLOWFISH_H
+#define BLOWFISH_H
+
+
+class Server;
+
+class QString;
+class QCString;
+
+
+namespace Konversation
+{
+
+ int findOccurrence(const QCString& input, const QCString& separator, int nth);
+ void decrypt(const QString& recipient, QCString& cipher, Server* server);
+ void decryptTopic(const QString& recipient, QCString& cipher, Server* server);
+ bool encrypt(const QString& key, QCString& cipher);
+}
+#endif
diff --git a/konversation/src/blowfish/mc_blowfish.cpp b/konversation/src/blowfish/mc_blowfish.cpp
new file mode 100644
index 0000000..0fb8da9
--- /dev/null
+++ b/konversation/src/blowfish/mc_blowfish.cpp
@@ -0,0 +1,63 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+*/
+
+//---------------------------------------------------------------------------
+// mc_blowfish.cpp
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// include header
+#include "mc_blowfish.h"
+#include "newblowfish.h"
+#include "oldblowfish1.h"
+
+#include <string.h>
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+// Proxies for deciding which algorithm to use based on a key prefix
+
+// encrypt and decrypt
+char *encrypt_string(char *key, char *str)
+{
+ // Note: Returned string must be freed when done with it!
+ if (key!=0 && (strncmp(key,"cbc:",4)==0 || strncmp(key,"CBC:",4)==0))
+ {
+ // new method
+ return encrypt_string_new(key+4,str);
+ }
+
+ // invoke old ecb method
+ return encrypt_string_oldecb(key,str);
+}
+
+char *decrypt_string(char *key, char *str)
+{
+ // Note: Returned string must be freed when done with it!
+ if (key!=NULL && (strncmp(key,"cbc:",4)==0 || strncmp(key,"CBC:",4)==0))
+ {
+ // new method
+ if (str[0]=='*')
+ return decrypt_string_new(key+4,str+1);
+ // it wasnt in cbc as expected, so use old method and warn user
+ char *cp=decrypt_string_oldecb(key,str);
+ char *cp2 = new char[strlen(cp)+15];
+ strcpy(cp2,"ERROR_NONCBC:");
+ strcat(cp2,cp);
+ delete cp;
+ return cp2;
+ }
+ // invoke old ecb method
+ return decrypt_string_oldecb(key,str);
+}
+//---------------------------------------------------------------------------
diff --git a/konversation/src/blowfish/mc_blowfish.h b/konversation/src/blowfish/mc_blowfish.h
new file mode 100644
index 0000000..b1a3537
--- /dev/null
+++ b/konversation/src/blowfish/mc_blowfish.h
@@ -0,0 +1,34 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+*/
+
+// mc_blowfish.h
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+// To prevent multiple includes
+#ifndef _mcblowfishh
+#define _mcblowfishh
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+// forward declarations
+char *encrypt_string(char *key, char *str);
+char *decrypt_string(char *key, char *str);
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+// header guard
+#endif
+//---------------------------------------------------------------------------
diff --git a/konversation/src/blowfish/newblowfish.cpp b/konversation/src/blowfish/newblowfish.cpp
new file mode 100644
index 0000000..501c9d1
--- /dev/null
+++ b/konversation/src/blowfish/newblowfish.cpp
@@ -0,0 +1,208 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+*/
+
+// mc_blowfishnew.cpp
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+#include "newblowfish.h"
+#include "b64stuff.h"
+#include "BlowfishCbc.h"
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+void ChooseIv(char *iv)
+{
+ // file the 8 byte IV with a nonce
+ // ATTN: this is not the best nonce IV generator, we'd like to improve it later
+
+ static unsigned int staticcounter=0;
+ static int didinit=0;
+ int i;
+ time_t t1;
+ int part1,part2;
+ char *part1p,*part2p;
+
+ // get current time
+ time(&t1);
+
+ // on first use, initialize random number generator to time
+ if (didinit==0)
+ {
+ srand((long) t1);
+ didinit=1;
+ }
+
+ // increment counter
+ ++staticcounter;
+ if (staticcounter>=65534L)
+ staticcounter=0;
+
+ // ok now the IV will be 4 bytes random and 4 bytes time_t
+ int intsize=sizeof(int);
+
+ // now part1 and part2 of the 8 byte iv
+ // part1 is a timestamp, number of seconds past since 1970
+ part1=(int)t1;
+ part1p=(char*)(&part1);
+ // part2 is a random number, from prng initted with time, plus a counter
+ part2=(int)(rand()+staticcounter);
+ part2p=(char*)(&part2);
+
+ // now fill IV
+ for (i=0;i<4;++i)
+ iv[i]=part1p[i%intsize];
+ for (i=0;i<4;++i)
+ iv[4+i]=part2p[i%intsize];
+}
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+// NEW CBCB METHODS
+char *encrypt_string_new(char *key, char *str)
+{
+ char *p;
+ char *s, *dest,*dest2;
+ char iv[8];
+ int i;
+ int len;
+
+ // Pad fake string with 8 bytes to make sure there's enough PLUS add 8 bytes for IV
+ s = new char[strlen(str) + 9+8];
+ if ((!key) || (!key[0]))
+ return s;
+ // allocate max space we will need for encrypted, base64d
+ dest = new char[(strlen(str) + 9+8) * 2];
+
+ // choose a (random or timestamped) IV block, 8 bytes wide and stick it into first 8 bytes of s
+ ChooseIv(iv);
+
+ // load the iv into start of s
+ for (i=0;i<8;++i)
+ s[i]=iv[i];
+ // add user string after
+ strcpy(&s[8], str);
+ len=8+strlen(str);
+
+ // pad at end of source with 0s
+ p = s+len;
+ for (i = 0; i < 8; i++)
+ *p++ = 0;
+
+ // modify len to be mod%8
+ if (len%8!=0)
+ len+=8-(len%8);
+
+ // encrypt into dest (note len does not change)
+ CBlowFish oBlowFish((unsigned char*)key, strlen(key));
+ oBlowFish.ResetChain();
+ oBlowFish.Encrypt((unsigned char*)s,(unsigned char*)dest, len, CBlowFish::CBC);
+
+ // now base 64 it, allocating new dest2 in the process
+ dest2=(char*)(spc_base64_encode((unsigned char*)dest,len,0));
+ if (dest2==0)
+ {
+ delete s;
+ return dest;
+ }
+
+ // now prefix a * to it while copying to dest and delete s
+ strcpy(dest,"*");
+ strcat(dest,dest2);
+ delete dest2;
+ delete s;
+
+ // return the dest (user will free)
+ return dest;
+}
+
+
+// Returned string must be freed when done with it!
+char *decrypt_string_new(char *key, char *str)
+{
+ char *p, *s, *dest;
+ char *dest2,*dest3;
+ int i;
+ int err;
+ size_t len;
+ char iv[9];
+
+ // Pad encoded string with 0 bits in case it's bogus
+ s = new char[strlen(str) + 12];
+ strcpy(s, str);
+
+ if ((!key) || (!key[0]))
+ return s;
+ dest = new char[strlen(str) + 12 + 8];
+
+ // pad with 0s
+ p = s+strlen(str);
+ for (i = 0; i < 12; i++)
+ *p++ = 0;
+
+ // now unbase64 it, allocating new dest2 in the process
+ len=strlen(str);
+ dest2=(char*)(spc_base64_decode((unsigned char*)s,&len,0,&err));
+ if (dest2==0)
+ {
+ delete dest;
+ return s;
+ }
+ if (err)
+ {
+ // FOR TESTING:
+ // printf("ERROR IN BASE 64 decode!\n");
+ delete dest;
+ return s;
+ }
+
+ // copy unbase64'd to new temp dest3 then delete dest2
+ // pad it to be mod%8 (this should not be necesary on decrypt but could be on a truncated transmission);
+ dest3 = new char[strlen(str) + 12 + 8];
+ memcpy(dest3,dest2,len);
+ if (len%8!=0)
+ {
+ int newbytes=8-(len%8);
+ while (newbytes>0)
+ {
+ ++len;
+ --newbytes;
+ dest3[len]='\0';
+ }
+ }
+ delete dest2;
+
+ // decrypt from dest3 (length len) to dest
+ CBlowFish oBlowFish((unsigned char*)key, strlen(key));
+ oBlowFish.ResetChain();
+ oBlowFish.Decrypt((unsigned char*)dest3,(unsigned char*)dest, len, CBlowFish::CBC);
+ // 0 terminate in case it is exactly mod 8 real characters
+ dest[len]='\0';
+
+ // now the first block (8bytes) is just the IV so we don't want to return that
+ strncpy(iv,dest,8);
+ iv[8]='\0';
+ strcpy(dest,&dest[8]);
+
+ // delete s and dest3
+ delete s;
+ delete dest3;
+
+ // return dest
+ return dest;
+}
+//---------------------------------------------------------------------------
diff --git a/konversation/src/blowfish/newblowfish.h b/konversation/src/blowfish/newblowfish.h
new file mode 100644
index 0000000..16e50c2
--- /dev/null
+++ b/konversation/src/blowfish/newblowfish.h
@@ -0,0 +1,32 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+*/
+
+// mc_blowfishnew.h
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// To prevent multiple includes
+#ifndef _newblowfishh
+#define _newblowfishh
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// forward declarations
+char *encrypt_string_new(char *key, char *str);
+char *decrypt_string_new(char *key, char *str);
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+// header guard
+#endif
+//---------------------------------------------------------------------------
diff --git a/konversation/src/blowfish/oldblowfish.cpp b/konversation/src/blowfish/oldblowfish.cpp
new file mode 100644
index 0000000..1f274db
--- /dev/null
+++ b/konversation/src/blowfish/oldblowfish.cpp
@@ -0,0 +1,425 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Blowfish algorythms: Bruce Schneier and Jim Conger
+ Bruce Schneier, 1996, Applied Cryptography, 2nd ed., John Wiley & Sons
+ Blowfish Eggdrop algorythms: Robey Pointer
+
+ Copyright (C) 1994 Bruce Schneier <schneier@counterpane.com>
+ Copyright (C) 1996 Jim Conger <jconger@cox.net>
+ Copyright (C) Robey Pointer <robey@lag.net>
+*/
+
+//---------------------------------------------------------------------------
+#include <string.h>
+#include "oldblowfish1.h"
+#include "oldblowfish2.h"
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+#define S(x,i) (SBoxes[i][x.w.byte##i])
+#define bf_F(x) (((S(x,0) + S(x,1)) ^ S(x,2)) + S(x,3))
+#define ROUND(a,b,n) (a.dword ^= bf_F(b) ^ PArray[n])
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+oldCBlowFish::oldCBlowFish ()
+{
+ PArray = new DWORD [18] ;
+ SBoxes = new DWORD [4][256] ;
+}
+
+oldCBlowFish::~oldCBlowFish ()
+{
+ delete PArray ;
+ delete [] SBoxes ;
+}
+
+// the low level (private) encryption function
+void oldCBlowFish::Blowfish_encipher (DWORD *xl, DWORD *xr)
+{
+ union aword Xl, Xr ;
+
+ Xl.dword = *xl ;
+ Xr.dword = *xr ;
+
+ Xl.dword ^= PArray [0];
+ ROUND (Xr, Xl, 1) ; ROUND (Xl, Xr, 2) ;
+ ROUND (Xr, Xl, 3) ; ROUND (Xl, Xr, 4) ;
+ ROUND (Xr, Xl, 5) ; ROUND (Xl, Xr, 6) ;
+ ROUND (Xr, Xl, 7) ; ROUND (Xl, Xr, 8) ;
+ ROUND (Xr, Xl, 9) ; ROUND (Xl, Xr, 10) ;
+ ROUND (Xr, Xl, 11) ; ROUND (Xl, Xr, 12) ;
+ ROUND (Xr, Xl, 13) ; ROUND (Xl, Xr, 14) ;
+ ROUND (Xr, Xl, 15) ; ROUND (Xl, Xr, 16) ;
+ Xr.dword ^= PArray [17] ;
+
+ *xr = Xl.dword ;
+ *xl = Xr.dword ;
+}
+
+// the low level (private) decryption function
+void oldCBlowFish::Blowfish_decipher (DWORD *xl, DWORD *xr)
+{
+ union aword Xl ;
+ union aword Xr ;
+
+ Xl.dword = *xl ;
+ Xr.dword = *xr ;
+
+ Xl.dword ^= PArray [17] ;
+ ROUND (Xr, Xl, 16) ; ROUND (Xl, Xr, 15) ;
+ ROUND (Xr, Xl, 14) ; ROUND (Xl, Xr, 13) ;
+ ROUND (Xr, Xl, 12) ; ROUND (Xl, Xr, 11) ;
+ ROUND (Xr, Xl, 10) ; ROUND (Xl, Xr, 9) ;
+ ROUND (Xr, Xl, 8) ; ROUND (Xl, Xr, 7) ;
+ ROUND (Xr, Xl, 6) ; ROUND (Xl, Xr, 5) ;
+ ROUND (Xr, Xl, 4) ; ROUND (Xl, Xr, 3) ;
+ ROUND (Xr, Xl, 2) ; ROUND (Xl, Xr, 1) ;
+ Xr.dword ^= PArray[0];
+
+ *xl = Xr.dword;
+ *xr = Xl.dword;
+}
+
+
+// constructs the enctryption sieve
+void oldCBlowFish::Initialize (BYTE key[], int keybytes)
+{
+ int i, j ;
+ DWORD data, datal, datar ;
+ union aword temp ;
+
+
+ // ATTN: new fix for keys > 56, should make it more compatible with FISH
+ // but fish uses 80 ???
+ if (keybytes>MAXKEYBYTES_COMPATMODE)
+ keybytes=MAXKEYBYTES_COMPATMODE;
+
+ // first fill arrays from data tables
+ for (i = 0 ; i < 18 ; i++)
+ PArray [i] = bf_P [i] ;
+
+ for (i = 0 ; i < 4 ; i++)
+ {
+ for (j = 0 ; j < 256 ; j++)
+ SBoxes [i][j] = bf_S [i][j] ;
+ }
+
+
+ j = 0 ;
+ for (i = 0 ; i < NPASS + 2 ; ++i)
+ {
+ temp.dword = 0 ;
+ temp.w.byte0 = key[j];
+ temp.w.byte1 = key[(j+1) % keybytes] ;
+ temp.w.byte2 = key[(j+2) % keybytes] ;
+ temp.w.byte3 = key[(j+3) % keybytes] ;
+ data = temp.dword ;
+ PArray [i] ^= data ;
+ j = (j + 4) % keybytes ;
+ }
+
+ datal = 0 ;
+ datar = 0 ;
+
+ for (i = 0 ; i < NPASS + 2 ; i += 2)
+ {
+ Blowfish_encipher (&datal, &datar) ;
+ PArray [i] = datal ;
+ PArray [i + 1] = datar ;
+ }
+
+ for (i = 0 ; i < 4 ; ++i)
+ {
+ for (j = 0 ; j < 256 ; j += 2)
+ {
+ Blowfish_encipher (&datal, &datar) ;
+ SBoxes [i][j] = datal ;
+ SBoxes [i][j + 1] = datar ;
+ }
+ }
+}
+
+// get output length, which must be even MOD 8
+DWORD oldCBlowFish::GetOutputLength (DWORD lInputLong)
+{
+ DWORD lVal ;
+
+ lVal = lInputLong % 8 ; // find out if uneven number of bytes at the end
+ if (lVal != 0)
+ return lInputLong + 8 - lVal ;
+ else
+ return lInputLong ;
+}
+
+
+// Encode pIntput into pOutput. Input length in lSize. Returned value
+// is length of output which will be even MOD 8 bytes. Input buffer and
+// output buffer can be the same, but be sure buffer length is even MOD 8.
+DWORD oldCBlowFish::Encode (BYTE * pInput, BYTE * pOutput, DWORD lSize)
+{
+ DWORD lCount, lOutSize, lGoodBytes ;
+ BYTE *pi, *po ;
+ int i, j ;
+ int SameDest = (pInput == pOutput ? 1 : 0) ;
+
+ lOutSize = GetOutputLength (lSize) ;
+ for (lCount = 0 ; lCount < lOutSize ; lCount += 8)
+ {
+ if (SameDest) // if encoded data is being written into input buffer
+ {
+ if (lCount < lSize - 7) // if not dealing with uneven bytes at end
+ {
+ Blowfish_encipher ((DWORD *) pInput,
+ (DWORD *) (pInput + 4)) ;
+ }
+ else // pad end of data with null bytes to complete encryption
+ {
+ po = pInput + lSize ; // point at byte past the end of actual data
+ j = (int) (lOutSize - lSize) ; // number of bytes to set to null
+ for (i = 0 ; i < j ; i++)
+ *po++ = 0 ;
+ Blowfish_encipher ((DWORD *) pInput,
+ (DWORD *) (pInput + 4)) ;
+ }
+ pInput += 8 ;
+ }
+ else // output buffer not equal to input buffer, so must copy
+ { // input to output buffer prior to encrypting
+ if (lCount < lSize - 7) // if not dealing with uneven bytes at end
+ {
+ pi = pInput ;
+ po = pOutput ;
+ for (i = 0 ; i < 8 ; i++) // copy bytes to output
+ *po++ = *pi++ ;
+ Blowfish_encipher ((DWORD *) pOutput, // now encrypt them
+ (DWORD *) (pOutput + 4)) ;
+ }
+ else // pad end of data with null bytes to complete encryption
+ {
+ lGoodBytes = lSize - lCount ; // number of remaining data bytes
+ po = pOutput ;
+ for (i = 0 ; i < (int) lGoodBytes ; i++)
+ *po++ = *pInput++ ;
+ for (j = i ; j < 8 ; j++)
+ *po++ = 0 ;
+ Blowfish_encipher ((DWORD *) pOutput,
+ (DWORD *) (pOutput + 4)) ;
+ }
+ pInput += 8 ;
+ pOutput += 8 ;
+ }
+ }
+ return lOutSize ;
+ }
+
+// Decode pIntput into pOutput. Input length in lSize. Input buffer and
+// output buffer can be the same, but be sure buffer length is even MOD 8.
+void oldCBlowFish::Decode (BYTE * pInput, BYTE * pOutput, DWORD lSize)
+{
+ DWORD lCount ;
+ BYTE *pi, *po ;
+ int i ;
+ int SameDest = (pInput == pOutput ? 1 : 0) ;
+
+ for (lCount = 0 ; lCount < lSize ; lCount += 8)
+ {
+ if (SameDest) // if encoded data is being written into input buffer
+ {
+ Blowfish_decipher ((DWORD *) pInput,
+ (DWORD *) (pInput + 4)) ;
+ pInput += 8 ;
+ }
+ else // output buffer not equal to input buffer
+ { // so copy input to output before decoding
+ pi = pInput ;
+ po = pOutput ;
+ for (i = 0 ; i < 8 ; i++)
+ *po++ = *pi++ ;
+ Blowfish_decipher ((DWORD *) pOutput,
+ (DWORD *) (pOutput + 4)) ;
+ pInput += 8 ;
+ pOutput += 8 ;
+ }
+ }
+}
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+// The following code is adapted from the eggdrop bot source:
+// by Robey Pointer
+// DR - as i understand this, what we have here is a function where you pass
+// it a string(passphrase), and it convolutes it into an encrypted version
+// (witout using a separate key). and you use this one-way encrypted
+// string later, when you ask person for their passphrase. when they give
+// you there passphrase, you call this again, and compare the two outputs.
+// if the outputs are the same, then the inputs presumably were, and so
+// passphrase is judged to match. In this way you can store one-way encrypted
+// passphrases in plain files.
+//
+
+#define SALT1 0xdeadd061
+#define SALT2 0x23f6b095
+
+// Convert 64-bit encrypted passphrase to text for userfile
+char base64[] = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+int base64dec(char c)
+{
+ // updated 1/20/03 for speed improvement
+ static char base64unmap[255];
+ static bool didinit=false;
+ int i;
+
+ if (!didinit)
+ {
+ // initialize base64unmap
+ for (i=0;i<255;++i)
+ base64unmap[i]=0;
+ for (i=0;i<64;++i)
+ base64unmap[(int)base64[i]]=i;
+ didinit=true;
+ }
+
+ return base64unmap[(int)c];
+}
+
+void blowfish_encrypt_pass(char *text, char *str)
+{
+ DWORD left,right;
+ int n;
+ char *p;
+ oldCBlowFish caca;
+ caca.Initialize((unsigned char*)text,strlen(text));
+ left = SALT1;
+ right = SALT2;
+ caca.Blowfish_encipher(&left, &right);
+ p = str;
+ *p++ = '+'; // + means encrypted pass
+ n = 32;
+ while (n > 0) {
+ *p++ = base64[right & 0x3f];
+ right = (right >> 6);
+ n -= 6;
+ }
+ n = 32;
+ while (n > 0) {
+ *p++ = base64[left & 0x3f];
+ left = (left >> 6);
+ n -= 6;
+ }
+ *p = 0;
+}
+//---------------------------------------------------------------------------
+
+
+
+
+
+
+
+
+
+
+
+
+//---------------------------------------------------------------------------
+// ORIGINAL COMPATIBLE (ECB) METHODS
+
+char *encrypt_string_oldecb(char *key, char *str)
+{
+ DWORD left, right;
+ unsigned char *p;
+ char *s, *dest, *d;
+ int i;
+
+ // Pad fake string with 8 bytes to make sure there's enough
+ s = new char[strlen(str) + 9];
+ strcpy(s, str);
+ if ((!key) || (!key[0]))
+ return s;
+ p = (unsigned char*)s;
+ dest = new char[(strlen(str) + 9) * 2];
+ while (*p)
+ p++;
+ for (i = 0; i < 8; i++)
+ *p++ = 0;
+
+ oldCBlowFish caca;
+ caca.Initialize((unsigned char*)key,strlen(key));
+ p = (unsigned char*)s;
+ d = dest;
+ while (*p) {
+ left = ((*p++) << 24);
+ left += ((*p++) << 16);
+ left += ((*p++) << 8);
+ left += (*p++);
+ right = ((*p++) << 24);
+ right += ((*p++) << 16);
+ right += ((*p++) << 8);
+ right += (*p++);
+ caca.Blowfish_encipher(&left, &right);
+ for (i = 0; i < 6; i++) {
+ *d++ = base64[right & 0x3f];
+ right = (right >> 6);
+ }
+ for (i = 0; i < 6; i++) {
+ *d++ = base64[left & 0x3f];
+ left = (left >> 6);
+ }
+ }
+ *d = 0;
+ delete s;
+ return dest;
+}
+
+
+// Returned string must be freed when done with it!
+char *decrypt_string_oldecb(char *key, char *str)
+{
+ DWORD left, right;
+ char *p, *s, *dest, *d;
+ int i;
+
+ // Pad encoded string with 0 bits in case it's bogus
+ s = new char[strlen(str) + 12];
+ strcpy(s, str);
+ if ((!key) || (!key[0]))
+ return s;
+ p = s;
+ dest = new char[strlen(str) + 12];
+ while (*p)
+ p++;
+ for (i = 0; i < 12; i++)
+ *p++ = 0;
+ oldCBlowFish caca;
+ caca.Initialize((unsigned char*)key,strlen(key));
+ p = s;
+ d = dest;
+ while (*p) {
+ right = 0L;
+ left = 0L;
+ for (i = 0; i < 6; i++)
+ right |= (base64dec(*p++)) << (i * 6);
+ for (i = 0; i < 6; i++)
+ left |= (base64dec(*p++)) << (i * 6);
+ caca.Blowfish_decipher(&left, &right);
+ for (i = 0; i < 4; i++)
+ *d++ = (char) ((left & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8));
+ for (i = 0; i < 4; i++)
+ *d++ = (char) ((right & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8));
+ }
+ *d = 0;
+ delete s;
+ return dest;
+}
+//---------------------------------------------------------------------------
diff --git a/konversation/src/blowfish/oldblowfish1.h b/konversation/src/blowfish/oldblowfish1.h
new file mode 100644
index 0000000..729e27c
--- /dev/null
+++ b/konversation/src/blowfish/oldblowfish1.h
@@ -0,0 +1,141 @@
+/*
+ blowfish1.h interface file for mc_blowfish.cpp
+ _THE BLOWFISH ENCRYPTION ALGORITHM_
+ by Bruce Schneier, 1996, Applied Cryptography, 2nd ed., John Wiley & Sons
+
+ Public Domain.
+
+ Copyright (C) 1994 Bruce Schneier <schneier@counterpane.com>
+ Copyright (C) 1996 Jim Conger <jconger@cox.net>
+*/
+
+//---------------------------------------------------------------------------
+// To prevent multiple includes
+#ifndef _oldblowfish1h
+#define _oldblowfish1h
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// Forward declarations
+char *encrypt_string_oldecb(char *key, char *str);
+char *decrypt_string_oldecb(char *key, char *str);
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+#define MAXKEYBYTES 56 // 448 bits max
+#define NPASS 16 // SBox passes
+
+#define DWORD unsigned long
+#define WORD unsigned short
+#define BYTE unsigned char
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// for fish compatibility we use 80 instead of 56
+#define MAXKEYBYTES_COMPATMODE 80
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+//#include "oldblowfish2.h"
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+// choose a byte order for your hardware
+
+// NEW 7/10/03 trying to be smarter about endian
+// first try to be smart about it, and user can define LITTLE_ENDIAN or BIG_ENDIAN from makefile
+#ifdef LITTLE_ENDIAN
+ #define ORDER_DCBA // choosing Intel in this case
+#else
+ #ifdef BIG_ENDIAN
+ #define ORDER_ABCD
+ #endif
+#endif
+
+// default to intel if not overridden
+#ifndef ORDER_DCBA
+ #ifndef ORDER_ABCD
+ #define ORDER_DCBA
+ #endif
+#endif
+
+#ifdef ORDER_DCBA // DCBA - little endian - intel
+// ATTN: THIS IS THE MAJOR CAUSE OF INCOMPATIBILITIES WITH OTHER BLOWFISH IMPLEMENTATIONS
+// AS THIS LITTLE ENDIAN MODE IS USED ON THE PC BY THIS BLOWFISH, AND *NOT* IN OTHER BLOWFISHES
+// THE FIX I THINK IS SIMPLE, THE bytex VARS ARE USED IN ONLY 1 PLACE IN MC_BLOWFISH.CPP AND YOU
+// SHOULD BE ABLE TO MODIFY YOUR BLOWFISH CODE TO USE (4-BYTE#) instead of BYTE# IN ONE LINE OF CODE
+// TO FIX THE INCOMPATIBILITY.
+// THERE IS ALSO A BASE64 INCOMPATIBILITY THAT YOU NEED TO FIX TO USE OTHER BLOWFISH ALGORITHMS.
+// WE ARE WORKING TO MAKE A COLLECTION OF COMPATIBLE ROUTINES IN DIF. LANGUAGES (java, php, perl).
+// PLEASE BARE WITH US - WE CHOSE TO USE THESE METHODS TO INSURE BACKWARD COMPATIBILITY WITH EXISTING SCRIPTS
+ union aword {
+ DWORD dword;
+ BYTE byte [4];
+ struct {
+ unsigned int byte3:8;
+ unsigned int byte2:8;
+ unsigned int byte1:8;
+ unsigned int byte0:8;
+ } w;
+ };
+#endif
+
+#ifdef ORDER_ABCD // ABCD - big endian - motorola
+ union aword {
+ DWORD dword;
+ BYTE byte [4];
+ struct {
+ unsigned int byte0:8;
+ unsigned int byte1:8;
+ unsigned int byte2:8;
+ unsigned int byte3:8;
+ } w;
+ };
+#endif
+
+#ifdef ORDER_BADC // BADC - vax
+ union aword {
+ DWORD dword;
+ BYTE byte [4];
+ struct {
+ unsigned int byte1:8;
+ unsigned int byte0:8;
+ unsigned int byte3:8;
+ unsigned int byte2:8;
+ } w;
+};
+#endif
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+class oldCBlowFish
+{
+private:
+ DWORD * PArray ;
+ DWORD (* SBoxes)[256];
+public:
+ void Blowfish_encipher (DWORD *xl, DWORD *xr) ;
+ void Blowfish_decipher (DWORD *xl, DWORD *xr) ;
+public:
+ oldCBlowFish () ;
+ ~oldCBlowFish () ;
+ void Initialize (BYTE key[], int keybytes) ;
+ DWORD GetOutputLength (DWORD lInputLong) ;
+ DWORD Encode (BYTE * pInput, BYTE * pOutput, DWORD lSize) ;
+ void Decode (BYTE * pInput, BYTE * pOutput, DWORD lSize) ;
+} ;
+//---------------------------------------------------------------------------
+
+
+
+
+
+//---------------------------------------------------------------------------
+#endif
+//---------------------------------------------------------------------------
+
diff --git a/konversation/src/blowfish/oldblowfish2.h b/konversation/src/blowfish/oldblowfish2.h
new file mode 100644
index 0000000..8afbe93
--- /dev/null
+++ b/konversation/src/blowfish/oldblowfish2.h
@@ -0,0 +1,295 @@
+/*
+ blowfish1.h interface file for mc_blowfish.cpp
+ _THE BLOWFISH ENCRYPTION ALGORITHM_
+ by Bruce Schneier, 1996, Applied Cryptography, 2nd ed., John Wiley & Sons
+
+ Public Domain.
+
+ Copyright (C) 1994 Bruce Schneier <schneier@counterpane.com>
+ Copyright (C) 1996 Jim Conger <jconger@cox.net>
+*/
+
+// blowfish.h2 header file containing random number tables
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+// To prevent multiple includes
+#ifndef _oldblowfish2h
+#define _oldblowfish2h
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+static DWORD bf_P[NPASS + 2] = {
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b,
+};
+static DWORD bf_S[4][256] = {
+ {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a},
+ {0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7},
+ {0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0},
+ {0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6},
+};
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+// header guard
+#endif
+//---------------------------------------------------------------------------
diff --git a/konversation/src/channel.cpp b/konversation/src/channel.cpp
new file mode 100644
index 0000000..38a7e63
--- /dev/null
+++ b/konversation/src/channel.cpp
@@ -0,0 +1,2960 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004-2006 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "channel.h"
+#include "konversationapplication.h"
+#include "server.h"
+#include "blowfish.h"
+#include "nick.h"
+#include "nicklistview.h"
+#include "quickbutton.h"
+#include "modebutton.h"
+#include "ircinput.h"
+#include "ircviewbox.h"
+#include "ircview.h"
+#include <kabc/addressbook.h>
+#include <kabc/stdaddressbook.h>
+#include "common.h"
+#include "topiclabel.h"
+#include "channeloptionsdialog.h"
+#include "notificationhandler.h"
+#include "viewcontainer.h"
+#include "linkaddressbook/linkaddressbookui.h"
+#include "linkaddressbook/addressbook.h"
+
+#include <qlabel.h>
+#include <qvbox.h>
+#include <qevent.h>
+#include <qhbox.h>
+#include <qgrid.h>
+#include <qdragobject.h>
+#include <qsizepolicy.h>
+#include <qheader.h>
+#include <qregexp.h>
+#include <qtooltip.h>
+#include <qsplitter.h>
+#include <qcheckbox.h>
+#include <qtimer.h>
+#include <qcombobox.h>
+#include <qtextcodec.h>
+#include <qwhatsthis.h>
+#include <qtoolbutton.h>
+#include <qlayout.h>
+
+#include <kprocess.h>
+
+#include <klineedit.h>
+#include <kinputdialog.h>
+#include <kpassdlg.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <kdeversion.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kwin.h>
+
+
+Channel::Channel(QWidget* parent, QString _name) : ChatWindow(parent)
+{
+ // init variables
+
+ //HACK I needed the channel name at time of setServer, but setName needs m_server..
+ // This effectively assigns the name twice, but none of the other logic has been moved or updated.
+ name=_name;
+ m_processingTimer = 0;
+ m_delayedSortTimer = 0;
+ m_optionsDialog = NULL;
+ m_pendingChannelNickLists.clear();
+ m_currentIndex = 0;
+ m_opsToAdd = 0;
+ nicks = 0;
+ ops = 0;
+ completionPosition = 0;
+ nickChangeDialog = 0;
+ channelCommand = false;
+
+ m_joined = false;
+
+ quickButtonsChanged = false;
+ quickButtonsState = false;
+
+ modeButtonsChanged = false;
+ modeButtonsState = false;
+
+ awayChanged = false;
+ awayState = false;
+
+ splittersInitialized = false;
+ topicSplitterHidden = false;
+ channelSplitterHidden = false;
+
+ // flag for first seen topic
+ topicAuthorUnknown = true;
+
+ setType(ChatWindow::Channel);
+
+ setChannelEncodingSupported(true);
+
+ // Build some size policies for the widgets
+ QSizePolicy hfixed = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ QSizePolicy hmodest = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ QSizePolicy vmodest = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ QSizePolicy vfixed = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ QSizePolicy modest = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ QSizePolicy greedy = QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
+ m_vertSplitter = new QSplitter(Qt::Vertical, this);
+ m_vertSplitter->setOpaqueResize(KGlobalSettings::opaqueResize());
+
+
+ QWidget* topicWidget = new QWidget(m_vertSplitter);
+ m_vertSplitter->setResizeMode(topicWidget,QSplitter::KeepSize);
+
+ QGridLayout* topicLayout = new QGridLayout(topicWidget, 2, 3, 0, 0);
+
+ m_topicButton = new QToolButton(topicWidget);
+ m_topicButton->setIconSet(SmallIconSet("edit", 16));
+ QToolTip::add(m_topicButton, i18n("Edit Channel Settings"));
+ connect(m_topicButton, SIGNAL(clicked()), this, SLOT(showOptionsDialog()));
+
+ topicLine = new Konversation::TopicLabel(topicWidget);
+ QWhatsThis::add(topicLine, i18n("<qt>Every channel on IRC has a topic associated with it. This is simply a message that everybody can see.<p>If you are an operator, or the channel mode <em>'T'</em> has not been set, then you can change the topic by clicking the Edit Channel Properties button to the left of the topic. You can also view the history of topics there.</qt>"));
+ connect(topicLine, SIGNAL(setStatusBarTempText(const QString&)), this, SIGNAL(setStatusBarTempText(const QString&)));
+ connect(topicLine, SIGNAL(clearStatusBarTempText()), this, SIGNAL(clearStatusBarTempText()));
+ connect(topicLine,SIGNAL(popupCommand(int)),this,SLOT(popupChannelCommand(int)));
+
+ topicLayout->addWidget(m_topicButton, 0, 0);
+ topicLayout->addMultiCellWidget(topicLine, 0, 1, 1, 1);
+
+ // The box holding the channel modes
+ modeBox = new QHBox(topicWidget);
+ modeBox->setSizePolicy(hfixed);
+ modeT = new ModeButton("T",modeBox,0);
+ modeN = new ModeButton("N",modeBox,1);
+ modeS = new ModeButton("S",modeBox,2);
+ modeI = new ModeButton("I",modeBox,3);
+ modeP = new ModeButton("P",modeBox,4);
+ modeM = new ModeButton("M",modeBox,5);
+ modeK = new ModeButton("K",modeBox,6);
+ modeL = new ModeButton("L",modeBox,7);
+
+ QWhatsThis::add(modeT, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p>The <b>T</b>opic mode means that only the channel operator can change the topic for the channel.</qt>"));
+ QWhatsThis::add(modeN, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p><b>N</b>o messages from outside means that users that are not in the channel cannot send messages that everybody in the channel can see. Almost all channels have this set to prevent nuisance messages.</qt>"));
+ QWhatsThis::add(modeS, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p>A <b>S</b>ecret channel will not show up in the channel list, nor will any user be able to see that you are in the channel with the <em>WHOIS</em> command or anything similar. Only the people that are in the same channel will know that you are in this channel, if this mode is set.</qt>"));
+ QWhatsThis::add(modeI, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p>An <b>I</b>nvite only channel means that people can only join the channel if they are invited. To invite someone, a channel operator needs to issue the command <em>/invite nick</em> from within the channel.</qt>"));
+ QWhatsThis::add(modeP, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p>A <b>P</b>rivate channel is shown in a listing of all channels, but the topic is not shown. A user's <em>WHOIS</e> may or may not show them as being in a private channel depending on the IRC server.</qt>"));
+ QWhatsThis::add(modeM, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p>A <b>M</b>oderated channel is one where only operators, half-operators and those with voice can talk.</qt>"));
+ QWhatsThis::add(modeK, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p>A <b>P</b>rotected channel requires users to enter a password in order to join.</qt>"));
+ QWhatsThis::add(modeL, i18n("<qt>These control the <em>mode</em> of the channel. Only an operator can change these.<p>A channel that has a user <b>L</b>imit means that only that many users can be in the channel at any one time. Some channels have a bot that sits in the channel and changes this automatically depending on how busy the channel is.</qt>"));
+
+ connect(modeT,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+ connect(modeN,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+ connect(modeS,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+ connect(modeI,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+ connect(modeP,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+ connect(modeM,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+ connect(modeK,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+ connect(modeL,SIGNAL(clicked(int,bool)),this,SLOT(modeButtonClicked(int,bool)));
+
+ limit=new KLineEdit(modeBox);
+ QToolTip::add(limit, i18n("Maximum users allowed in channel"));
+ QWhatsThis::add(limit, i18n("<qt>This is the channel user limit - the maximum number of users that can be in the channel at a time. If you are an operator, you can set this. The channel mode <b>T</b>opic (button to left) will automatically be set if set this.</qt>"));
+ connect(limit,SIGNAL (returnPressed()),this,SLOT (channelLimitChanged()) );
+ connect(limit,SIGNAL (lostFocus()), this, SLOT(channelLimitChanged()) );
+
+ topicLayout->addWidget(modeBox, 0, 2);
+ topicLayout->setRowStretch(1, 10);
+ topicLayout->setColStretch(1, 10);
+
+ showTopic(Preferences::showTopic());
+ showModeButtons(Preferences::showModeButtons());
+
+ // (this) The main Box, holding the channel view/topic and the input line
+ m_horizSplitter = new QSplitter(m_vertSplitter);
+ m_horizSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
+
+ // Server will be set later in setServer()
+ IRCViewBox* ircViewBox = new IRCViewBox(m_horizSplitter, NULL);
+ setTextView(ircViewBox->ircView());
+ connect(textView,SIGNAL(popupCommand(int)),this,SLOT(popupChannelCommand(int)));
+ connect(topicLine, SIGNAL(currentChannelChanged(const QString&)),textView,SLOT(setCurrentChannel(const QString&)));
+
+ // The box that holds the Nick List and the quick action buttons
+ nickListButtons = new QVBox(m_horizSplitter);
+ m_horizSplitter->setResizeMode(nickListButtons,QSplitter::KeepSize);
+ nickListButtons->setSpacing(spacing());
+
+ nicknameListView=new NickListView(nickListButtons, this);
+ nicknameListView->setHScrollBarMode(QScrollView::AlwaysOff);
+ nicknameListView->setSelectionModeExt(KListView::Extended);
+ nicknameListView->setAllColumnsShowFocus(true);
+ nicknameListView->setSorting(1,true);
+ nicknameListView->addColumn(QString());
+ nicknameListView->addColumn(QString());
+ nicknameListView->setColumnWidthMode(1,KListView::Maximum);
+
+ nicknameListView->header()->hide();
+
+ // setResizeMode must be called after all the columns are added
+ nicknameListView->setResizeMode(KListView::LastColumn);
+
+ // separate LED from Text a little more
+ nicknameListView->setColumnWidth(0, 10);
+ nicknameListView->setColumnAlignment(0, Qt::AlignHCenter);
+
+ nicknameListView->installEventFilter(this);
+
+ // initialize buttons grid, will be set up in updateQuickButtons
+ buttonsGrid=0;
+
+ // The box holding the Nickname button and Channel input
+ commandLineBox = new QHBox(this);
+ commandLineBox->setSpacing(spacing());
+
+ nicknameCombobox = new QComboBox(commandLineBox);
+ nicknameCombobox->setEditable(true);
+ nicknameCombobox->insertStringList(Preferences::nicknameList());
+ QWhatsThis::add(nicknameCombobox, i18n("<qt>This shows your current nick, and any alternatives you have set up. If you select or type in a different nickname, then a request will be sent to the IRC server to change your nick. If the server allows it, the new nickname will be selected. If you type in a new nickname, you need to press 'Enter' at the end.<p>You can add change the alternative nicknames from the <em>Identities</em> option in the <em>File</em> menu.</qt>"));
+ oldNick = nicknameCombobox->currentText();
+
+ awayLabel = new QLabel(i18n("(away)"), commandLineBox);
+ awayLabel->hide();
+ blowfishLabel = new QLabel(commandLineBox);
+ blowfishLabel->hide();
+ blowfishLabel->setPixmap(KGlobal::iconLoader()->loadIcon("encrypted", KIcon::Toolbar));
+ channelInput = new IRCInput(commandLineBox);
+
+ getTextView()->installEventFilter(channelInput);
+ topicLine->installEventFilter(channelInput);
+ channelInput->installEventFilter(this);
+
+ // Set the widgets size policies
+ m_topicButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+ topicLine->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum));
+
+ commandLineBox->setSizePolicy(vfixed);
+
+ limit->setMaximumSize(40,100);
+ limit->setSizePolicy(hfixed);
+
+ modeT->setMaximumSize(20,100);
+ modeT->setSizePolicy(hfixed);
+ modeN->setMaximumSize(20,100);
+ modeN->setSizePolicy(hfixed);
+ modeS->setMaximumSize(20,100);
+ modeS->setSizePolicy(hfixed);
+ modeI->setMaximumSize(20,100);
+ modeI->setSizePolicy(hfixed);
+ modeP->setMaximumSize(20,100);
+ modeP->setSizePolicy(hfixed);
+ modeM->setMaximumSize(20,100);
+ modeM->setSizePolicy(hfixed);
+ modeK->setMaximumSize(20,100);
+ modeK->setSizePolicy(hfixed);
+ modeL->setMaximumSize(20,100);
+ modeL->setSizePolicy(hfixed);
+
+ getTextView()->setSizePolicy(greedy);
+ nicknameListView->setSizePolicy(hmodest);
+
+ connect(channelInput,SIGNAL (submit()),this,SLOT (channelTextEntered()) );
+ connect(channelInput,SIGNAL (envelopeCommand()),this,SLOT (channelPassthroughCommand()) );
+ connect(channelInput,SIGNAL (nickCompletion()),this,SLOT (completeNick()) );
+ connect(channelInput,SIGNAL (endCompletion()),this,SLOT (endCompleteNick()) );
+ connect(channelInput,SIGNAL (textPasted(const QString&)),this,SLOT (textPasted(const QString&)) );
+
+ connect(getTextView(), SIGNAL(textPasted(bool)), channelInput, SLOT(paste(bool)));
+ connect(getTextView(),SIGNAL (gotFocus()),channelInput,SLOT (setFocus()) );
+ connect(getTextView(),SIGNAL (sendFile()),this,SLOT (sendFileMenu()) );
+ connect(getTextView(),SIGNAL (autoText(const QString&)),this,SLOT (sendChannelText(const QString&)) );
+
+ connect(nicknameListView,SIGNAL (popupCommand(int)),this,SLOT (popupCommand(int)) );
+ connect(nicknameListView,SIGNAL (doubleClicked(QListViewItem*)),this,SLOT (doubleClickCommand(QListViewItem*)) );
+ connect(nicknameListView,SIGNAL (dropped(QDropEvent*,QListViewItem*)),this,SLOT (filesDropped(QDropEvent*)) );
+ connect(nicknameCombobox,SIGNAL (activated(int)),this,SLOT(nicknameComboboxChanged()));
+
+ if(nicknameCombobox->lineEdit())
+ connect(nicknameCombobox->lineEdit(), SIGNAL (lostFocus()),this,SLOT(nicknameComboboxChanged()));
+
+ nicknameList.setAutoDelete(true);
+
+ setLog(Preferences::log());
+
+ connect(&userhostTimer,SIGNAL (timeout()),this,SLOT (autoUserhost()));
+
+ // every few seconds try to get more userhosts
+ userhostTimer.start(10000);
+
+ connect(&m_whoTimer,SIGNAL (timeout()),this,SLOT (autoWho()));
+
+ // every 5 minutes decrease everyone's activity by 1 unit
+ m_fadeActivityTimer.start(5*60*1000);
+
+ connect(&m_fadeActivityTimer, SIGNAL(timeout()), this, SLOT(fadeActivity()));
+
+ // re-schedule when the settings were changed
+ connect(Preferences::self(), SIGNAL (autoContinuousWhoChanged()),this,SLOT (scheduleAutoWho()));
+
+ updateAppearance();
+
+ //FIXME JOHNFLUX
+ // connect( Konversation::Addressbook::self()->getAddressBook(), SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( slotLoadAddressees() ) );
+ // connect( Konversation::Addressbook::self(), SIGNAL(addresseesChanged()), this, SLOT(slotLoadAddressees()));
+}
+
+//FIXME there is some logic in setLogfileName that needs to be split out and called here if the server display name gets changed
+void Channel::setServer(Server* server)
+{
+ if (m_server != server)
+ connect(server, SIGNAL(connectionStateChanged(Server*, Konversation::ConnectionState)),
+ SLOT(connectionStateChanged(Server*, Konversation::ConnectionState)));
+ ChatWindow::setServer(server);
+ if (server->getKeyForRecipient(getName()))
+ blowfishLabel->show();
+ topicLine->setServer(server);
+ refreshModeButtons();
+ setIdentity(server->getIdentity());
+}
+
+void Channel::connectionStateChanged(Server* server, Konversation::ConnectionState state)
+{
+ if (server == m_server)
+ {
+ if (state != Konversation::SSConnected)
+ {
+ m_joined = false;
+
+ //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now.
+ if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl))
+ KonversationApplication::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
+ }
+ }
+}
+
+void Channel::setEncryptedOutput(bool e)
+{
+ if (e) {
+ blowfishLabel->show();
+ //scan the channel topic and decrypt it if necessary
+ QString topic(m_topicHistory[0].section(' ',2));
+
+ //prepend two colons to make it appear to be an irc message for decryption,
+ // \r because it won't decrypt without it, even though the message did not have a \r
+ // when encrypted. Bring on the QCA!
+ QCString cipher="::"+topic.utf8()+'\x0d';
+ Konversation::decryptTopic(getName(), cipher, m_server);
+ topic=QString::fromUtf8(cipher.data()+2, cipher.length()-2);
+ m_topicHistory[0] = m_topicHistory[0].section(' ', 0, 1) + ' ' + topic;
+ topicLine->setText(topic);
+ emit topicHistoryChanged();
+ }
+ else
+ blowfishLabel->hide();
+}
+
+Channel::~Channel()
+{
+ kdDebug() << "Channel::~Channel(" << getName() << ")" << endl;
+
+ // Purge nickname list
+ purgeNicks();
+ kdDebug() << "nicks purged" << endl;
+
+ // Unlink this channel from channel list
+ m_server->removeChannel(this);
+ kdDebug() << "Channel removed." << endl;
+
+}
+
+bool Channel::rejoinable()
+{
+ if (getServer() && getServer()->isConnected())
+ return !m_joined;
+
+ return false;
+}
+
+void Channel::rejoin()
+{
+ if (rejoinable())
+ m_server->sendJoinCommand(getName(), getPassword());
+}
+
+ChannelNickPtr Channel::getOwnChannelNick()
+{
+ return m_ownChannelNick;
+}
+
+ChannelNickPtr Channel::getChannelNick(const QString &ircnick)
+{
+ return m_server->getChannelNick(getName(), ircnick);
+}
+
+void Channel::purgeNicks()
+{
+ // Purge nickname list
+ nicknameList.clear();
+
+ // clear stats counter
+ nicks=0;
+ ops=0;
+}
+
+void Channel::showOptionsDialog()
+{
+ if(!m_optionsDialog)
+ m_optionsDialog = new Konversation::ChannelOptionsDialog(this);
+
+ m_optionsDialog->refreshModes();
+ m_optionsDialog->refreshTopicHistory();
+ m_optionsDialog->show();
+}
+
+void Channel::filesDropped(QDropEvent* e)
+{
+ QPoint p(nicknameListView->contentsToViewport(e->pos()));
+ Nick* it = dynamic_cast<Nick*>(nicknameListView->itemAt(p));
+ if (!it) return;
+ QStrList uris;
+ if (QUriDrag::decode(e,uris))
+ m_server->sendURIs(uris, it->getChannelNick()->getNickname());
+}
+
+void Channel::textPasted(const QString& text)
+{
+ if(m_server)
+ {
+ QStringList multiline=QStringList::split('\n',text);
+ for(unsigned int index=0;index<multiline.count();index++)
+ {
+ QString line=multiline[index];
+ QString cChar(Preferences::commandChar());
+ // make sure that lines starting with command char get escaped
+ if(line.startsWith(cChar)) line=cChar+line;
+ sendChannelText(line);
+ }
+ }
+}
+
+// Will be connected to IRCView::popupCommand(int)
+void Channel::popupChannelCommand(int id)
+{
+ channelCommand = true; // Context menu executed from ircview
+ popupCommand(id);
+ textView->clearContextNick();
+ channelCommand = false;
+}
+
+// Will be connected to NickListView::popupCommand(int)
+void Channel::popupCommand(int id)
+{
+ QString pattern;
+ QString cc = Preferences::commandChar();
+ QString args;
+ QString question;
+ bool raw=false;
+ QStringList nickList = getSelectedNickList();
+
+ switch(id)
+ {
+ case Konversation::AddressbookEdit:
+ {
+ ChannelNickList nickList=getSelectedChannelNicks();
+ for(ChannelNickList::ConstIterator it=nickList.begin();it!=nickList.end();++it)
+ {
+ if(!(*it)->getNickInfo()->editAddressee()) break;;
+ }
+ break;
+ }
+ case Konversation::AddressbookNew:
+ case Konversation::AddressbookDelete:
+ {
+ Konversation::Addressbook *addressbook = Konversation::Addressbook::self();
+ ChannelNickList nickList=getSelectedChannelNicks();
+ //Handle all the selected nicks in one go. Either they all save, or none do.
+ if(addressbook->getAndCheckTicket())
+ {
+ for(ChannelNickList::ConstIterator it=nickList.begin();it!=nickList.end();++it)
+ {
+ if(id == Konversation::AddressbookDelete)
+ {
+ KABC::Addressee addr = (*it)->getNickInfo()->getAddressee();
+ addressbook->unassociateNick(addr, (*it)->getNickname(), m_server->getServerName(), m_server->getDisplayName());
+ }
+ else
+ {
+ //make new addressbook contact
+ KABC::Addressee addr;
+ NickInfoPtr nickInfo = (*it)->getNickInfo();
+ if(nickInfo->getRealName().isEmpty())
+ addr.setGivenName(nickInfo->getNickname());
+ else
+ addr.setGivenName(nickInfo->getRealName());
+ addr.setNickName(nickInfo->getNickname());
+ addressbook->associateNickAndUnassociateFromEveryoneElse(addr, (*it)->getNickname(), m_server->getServerName(), m_server->getDisplayName());
+ }
+ }
+ addressbook->saveTicket(); // This will refresh the nicks automatically for us. At least, if it doesn't, it's a bug :)
+ }
+ break;
+ }
+ case Konversation::AddressbookChange:
+ {
+ ChannelNickList nickList=getSelectedChannelNicks();
+ for(ChannelNickList::ConstIterator it=nickList.begin();it!=nickList.end();++it)
+ {
+ (*it)->getNickInfo()->showLinkAddressbookUI();
+ }
+ break;
+ }
+ case Konversation::SendEmail:
+ {
+ Konversation::Addressbook::self()->sendEmail(getSelectedChannelNicks());
+ break;
+ }
+ case Konversation::AddressbookSub:
+ kdDebug() << "sub called" << endl;
+ break;
+ case Konversation::GiveOp:
+ pattern="MODE %c +o %u";
+ raw=true;
+ break;
+ case Konversation::TakeOp:
+ pattern="MODE %c -o %u";
+ raw=true;
+ break;
+ case Konversation::GiveHalfOp:
+ pattern="MODE %c +h %u";
+ raw=true;
+ break;
+ case Konversation::TakeHalfOp:
+ pattern="MODE %c -h %u";
+ raw=true;
+ break;
+ case Konversation::GiveVoice:
+ pattern="MODE %c +v %u";
+ raw=true;
+ break;
+ case Konversation::TakeVoice:
+ pattern="MODE %c -v %u";
+ raw=true;
+ break;
+ case Konversation::Version:
+ pattern=cc+"CTCP %u VERSION";
+ break;
+ case Konversation::Whois:
+ pattern="WHOIS %u %u";
+ raw=true;
+ break;
+ case Konversation::Topic:
+ m_server->requestTopic(getTextView()->currentChannel());
+ break;
+ case Konversation::Names:
+ m_server->queue("NAMES " + getTextView()->currentChannel(), Server::LowPriority);
+ break;
+ case Konversation::Join:
+ m_server->queue("JOIN " + getTextView()->currentChannel());
+ break;
+ case Konversation::Ping:
+ {
+ unsigned int time_t = QDateTime::currentDateTime().toTime_t();
+ pattern=QString(cc+"CTCP %u PING %1").arg(time_t);
+ }
+ break;
+ case Konversation::Kick:
+ pattern=cc+"KICK %u";
+ break;
+ case Konversation::KickBan:
+ pattern=cc+"BAN %u\n"+
+ cc+"KICK %u";
+ break;
+ case Konversation::BanNick:
+ pattern=cc+"BAN %u";
+ break;
+ case Konversation::BanHost:
+ pattern=cc+"BAN -HOST %u";
+ break;
+ case Konversation::BanDomain:
+ pattern=cc+"BAN -DOMAIN %u";
+ break;
+ case Konversation::BanUserHost:
+ pattern=cc+"BAN -USERHOST %u";
+ break;
+ case Konversation::BanUserDomain:
+ pattern=cc+"BAN -USERDOMAIN %u";
+ break;
+ case Konversation::KickBanHost:
+ pattern=cc+"KICKBAN -HOST %u";
+ break;
+ case Konversation::KickBanDomain:
+ pattern=cc+"KICKBAN -DOMAIN %u";
+ break;
+ case Konversation::KickBanUserHost:
+ pattern=cc+"KICKBAN -USERHOST %u";
+ break;
+ case Konversation::KickBanUserDomain:
+ pattern=cc+"KICKBAN -USERDOMAIN %u";
+ break;
+ case Konversation::OpenQuery:
+ pattern=cc+"QUERY %u";
+ break;
+ case Konversation::StartDccChat:
+ pattern=cc+"DCC CHAT %u";
+ break;
+ case Konversation::DccSend:
+ pattern=cc+"DCC SEND %u";
+ break;
+ case Konversation::IgnoreNick:
+ if (nickList.size() == 1)
+ question=i18n("Do you want to ignore %1?").arg(nickList.first());
+ else
+ question = i18n("Do you want to ignore the selected users?");
+ if (KMessageBox::warningContinueCancel(this, question, i18n("Ignore"), i18n("Ignore"), "IgnoreNick") ==
+ KMessageBox::Continue)
+ pattern = cc+"IGNORE -ALL %l";
+ break;
+ case Konversation::UnignoreNick:
+ {
+ QStringList selectedIgnoredNicks;
+
+ for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
+ {
+ if (Preferences::isIgnored((*it)))
+ selectedIgnoredNicks.append((*it));
+ }
+
+ if (selectedIgnoredNicks.count() == 1)
+ question=i18n("Do you want to stop ignoring %1?").arg(selectedIgnoredNicks.first());
+ else
+ question = i18n("Do you want to stop ignoring the selected users?");
+ if (KMessageBox::warningContinueCancel(this, question, i18n("Unignore"), i18n("Unignore"), "UnignoreNick") ==
+ KMessageBox::Continue)
+ {
+ sendChannelText(cc+"UNIGNORE "+selectedIgnoredNicks.join(" "));
+ }
+ break;
+ }
+ case Konversation::AddNotify:
+ {
+ if (m_server->getServerGroup())
+ {
+ for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
+ {
+ if (!Preferences::isNotify(m_server->getServerGroup()->id(), (*it)))
+ Preferences::addNotify(m_server->getServerGroup()->id(), (*it));
+ }
+ }
+ break;
+ }
+ } // switch
+
+ if (!pattern.isEmpty())
+ {
+ pattern.replace("%c",getName());
+
+ QString command;
+
+ if (pattern.contains("%l"))
+ {
+ QStringList list;
+
+ for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
+ list.append((*it));
+
+ command = pattern.replace("%l", list.join(" "));
+
+ if (raw)
+ m_server->queue(command);
+ else
+ sendChannelText(command);
+ }
+ else
+ {
+ QStringList patternList = QStringList::split('\n',pattern);
+
+ for (QStringList::Iterator it=nickList.begin(); it!=nickList.end(); ++it)
+ {
+ for (unsigned int index = 0; index<patternList.count(); index++)
+ {
+ command = patternList[index];
+ command.replace("%u", (*it));
+
+ if (raw)
+ m_server->queue(command);
+ else
+ sendChannelText(command);
+ }
+ }
+ }
+ }
+}
+
+// Will be connected to NickListView::doubleClicked()
+void Channel::doubleClickCommand(QListViewItem* item)
+{
+ if(item)
+ {
+ nicknameListView->clearSelection();
+ nicknameListView->setSelected(item, true);
+ // TODO: put the quick button code in another function to make reusal more legitimate
+ quickButtonClicked(Preferences::channelDoubleClickAction());
+ }
+}
+
+void Channel::completeNick()
+{
+ int pos, oldPos;
+
+ channelInput->getCursorPosition(&oldPos,&pos);// oldPos is a dummy here, taking the paragraph parameter
+ oldPos = channelInput->getOldCursorPosition();
+
+ QString line=channelInput->text();
+ QString newLine;
+ // Check if completion position is out of range
+ if(completionPosition >= nicknameList.count()) completionPosition = 0;
+
+ // Check, which completion mode is active
+ char mode = channelInput->getCompletionMode();
+
+ if(mode == 'c')
+ {
+ line.remove(oldPos, pos - oldPos);
+ pos = oldPos;
+ }
+
+ // If the cursor is at beginning of line, insert last completion
+ if(pos == 0 && !channelInput->lastCompletion().isEmpty())
+ {
+ QString addStart(Preferences::nickCompleteSuffixStart());
+ newLine = channelInput->lastCompletion() + addStart;
+ // New cursor position is behind nickname
+ pos = newLine.length();
+ // Add rest of the line
+ newLine += line;
+ }
+ else
+ {
+ // remember old cursor position in input field
+ channelInput->setOldCursorPosition(pos);
+ // remember old cursor position locally
+ oldPos = pos;
+ // step back to last space or start of line
+ while(pos && line[pos-1] != ' ') pos--;
+ // copy search pattern (lowercase)
+ QString pattern = line.mid(pos, oldPos - pos);
+ // copy line to newLine-buffer
+ newLine = line;
+
+ // did we find any pattern?
+ if(!pattern.isEmpty())
+ {
+ bool complete = false;
+ QString foundNick;
+
+ // try to find matching nickname in list of names
+ if(Preferences::nickCompletionMode() == 1 ||
+ Preferences::nickCompletionMode() == 2)
+ { // Shell like completion
+ QStringList found;
+ foundNick = nicknameList.completeNick(pattern, complete, found,
+ (Preferences::nickCompletionMode() == 2),
+ Preferences::nickCompletionCaseSensitive());
+
+ if(!complete && !found.isEmpty())
+ {
+ if(Preferences::nickCompletionMode() == 1)
+ {
+ QString nicksFound = found.join(" ");
+ appendServerMessage(i18n("Completion"), i18n("Possible completions: %1.").arg(nicksFound));
+ }
+ else
+ {
+ channelInput->showCompletionList(found);
+ }
+ }
+ } // Cycle completion
+ else if(Preferences::nickCompletionMode() == 0)
+ {
+ if(mode == '\0') {
+ QPtrListIterator<Nick> it(nicknameList);
+ uint timeStamp = 0;
+ int listPosition = 0;
+ Nick* nick = 0;
+
+ while(it.current() != 0)
+ {
+ nick = it.current();
+
+ if(nick->getChannelNick()->getNickname().startsWith(pattern, Preferences::nickCompletionCaseSensitive()) &&
+ (nick->getChannelNick()->timeStamp() > timeStamp))
+ {
+ timeStamp = nick->getChannelNick()->timeStamp();
+ completionPosition = listPosition;
+ }
+
+ ++listPosition;
+ ++it;
+ }
+ }
+
+ // remember old nick completion position
+ unsigned int oldCompletionPosition = completionPosition;
+ complete = true;
+ QString prefixCharacter = Preferences::prefixCharacter();
+
+ do
+ {
+ QString lookNick = nicknameList.at(completionPosition)->getChannelNick()->getNickname();
+
+ if(!prefixCharacter.isEmpty() && lookNick.contains(prefixCharacter))
+ {
+ lookNick = lookNick.section( prefixCharacter,1 );
+ }
+
+ if(lookNick.startsWith(pattern, Preferences::nickCompletionCaseSensitive()))
+ {
+ foundNick = lookNick;
+ }
+
+ // increment search position
+ completionPosition++;
+
+ // wrap around
+ if(completionPosition == nicknameList.count())
+ {
+ completionPosition = 0;
+ }
+
+ // the search ends when we either find a suitable nick or we end up at the
+ // first search position
+ } while((completionPosition != oldCompletionPosition) && foundNick.isEmpty());
+ }
+
+ // did we find a suitable nick?
+ if(!foundNick.isEmpty())
+ {
+ // set channel nicks completion mode
+ channelInput->setCompletionMode('c');
+
+ // remove pattern from line
+ newLine.remove(pos, pattern.length());
+
+ // did we find the nick in the middle of the line?
+ if(pos && complete)
+ {
+ channelInput->setLastCompletion(foundNick);
+ QString addMiddle = Preferences::nickCompleteSuffixMiddle();
+ newLine.insert(pos, foundNick + addMiddle);
+ pos = pos + foundNick.length() + addMiddle.length();
+ }
+ // no, it was at the beginning
+ else if(complete)
+ {
+ channelInput->setLastCompletion(foundNick);
+ QString addStart = Preferences::nickCompleteSuffixStart();
+ newLine.insert(pos, foundNick + addStart);
+ pos = pos + foundNick.length() + addStart.length();
+ }
+ // the nick wasn't complete
+ else
+ {
+ newLine.insert(pos, foundNick);
+ pos = pos + foundNick.length();
+ }
+ }
+ // no pattern found, so restore old cursor position
+ else pos = oldPos;
+ }
+ }
+
+ // Set new text and cursor position
+ channelInput->setText(newLine);
+ channelInput->setCursorPosition(0, pos);
+}
+
+// make sure to step back one position when completion ends so the user starts
+// with the last complete they made
+void Channel::endCompleteNick()
+{
+ if(completionPosition) completionPosition--;
+ else completionPosition=nicknameList.count()-1;
+}
+
+void Channel::setName(const QString& newName)
+{
+ ChatWindow::setName(newName);
+ setLogfileName(newName.lower());
+}
+
+bool Channel::autoJoin()
+{
+ if (!m_server->getServerGroup()) return false;
+
+ Konversation::ChannelList channelList = m_server->getServerGroup()->channelList();
+
+ if (!channelList.empty())
+ return channelList.find(channelSettings()) != channelList.end();
+ else
+ return false;
+}
+
+void Channel::setAutoJoin(bool autojoin)
+{
+ if (autojoin && !(autoJoin()))
+ {
+ Konversation::ChannelSettings before;
+
+ QPtrList<Channel> channelList = m_server->getChannelList();
+
+ if (channelList.count() > 1)
+ {
+ QPtrListIterator<Channel> it(channelList);
+ Channel* channel;
+ QMap<int, Channel*> channelMap;
+
+ int index = -1;
+ int ownIndex = m_server->getViewContainer()->getViewIndex(this);
+
+ while ((channel = it.current()) != 0)
+ {
+ ++it;
+
+ index = m_server->getViewContainer()->getViewIndex(channel);
+
+ if (index && index > ownIndex) channelMap.insert(index, channel);
+ }
+
+ if (channelMap.count())
+ {
+ QMap<int, Channel*>::Iterator it2;
+
+ for (it2 = channelMap.begin(); it2 != channelMap.end(); ++it2)
+ {
+ channel = it2.data();
+
+ if (channel->autoJoin())
+ {
+ before = channel->channelSettings();
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (m_server->getServerGroup())
+ m_server->getServerGroup()->addChannel(channelSettings(), before);
+ }
+ else
+ {
+ if (m_server->getServerGroup())
+ m_server->getServerGroup()->removeChannel(channelSettings());
+ }
+}
+
+
+
+QString Channel::getPassword()
+{
+ QString password;
+
+ for (QStringList::const_iterator it = m_modeList.begin(); it != m_modeList.end(); ++it)
+ {
+ if ((*it)[0] == 'k') password = (*it).mid(1);
+ }
+
+ return password;
+}
+
+const Konversation::ChannelSettings Channel::channelSettings()
+{
+ Konversation::ChannelSettings channel;
+
+ channel.setName(getName());
+ channel.setPassword(getPassword());
+ channel.setNotificationsEnabled(notificationsEnabled());
+
+ return channel;
+}
+
+void Channel::sendFileMenu()
+{
+ emit sendFile();
+}
+
+void Channel::channelTextEntered()
+{
+ QString line = channelInput->text();
+ channelInput->setText ("");
+
+ if(line.lower().stripWhiteSpace() == Preferences::commandChar()+"clear")
+ {
+ textView->clear();
+ }
+ else if(line.lower().stripWhiteSpace() == Preferences::commandChar()+"cycle")
+ {
+ cycleChannel();
+ }
+ else
+ {
+ if(!line.isEmpty())
+ sendChannelText(line);
+ }
+}
+
+void Channel::channelPassthroughCommand()
+{
+ QString commandChar = Preferences::commandChar();
+ QString line = channelInput->text();
+
+ channelInput->setText("");
+
+ if(!line.isEmpty())
+ {
+ // Prepend commandChar on Ctrl+Enter to bypass outputfilter command recognition
+ if (line.startsWith(commandChar))
+ {
+ line = commandChar + line;
+ }
+ sendChannelText(line);
+ }
+}
+
+void Channel::sendChannelText(const QString& sendLine)
+{
+ // create a work copy
+ QString outputAll(sendLine);
+ // replace aliases and wildcards
+ if(m_server->getOutputFilter()->replaceAliases(outputAll))
+ {
+ outputAll = m_server->parseWildcards(outputAll,m_server->getNickname(),getName(),getPassword(),
+ getSelectedNickList(),QString());
+ }
+
+ // Send all strings, one after another
+ QStringList outList=QStringList::split(QRegExp("[\r\n]+"),outputAll);
+ for(unsigned int index=0;index<outList.count();index++)
+ {
+ QString output(outList[index]);
+
+ // encoding stuff is done in Server()
+ Konversation::OutputFilterResult result = m_server->getOutputFilter()->parse(m_server->getNickname(),output,getName());
+
+ // Is there something we need to display for ourselves?
+ if(!result.output.isEmpty())
+ {
+ if(result.type == Konversation::Action) appendAction(m_server->getNickname(), result.output);
+ else if(result.type == Konversation::Command) appendCommandMessage(result.typeString, result.output);
+ else if(result.type == Konversation::Program) appendServerMessage(result.typeString, result.output);
+ else if(result.type == Konversation::PrivateMessage) appendQuery(result.typeString, result.output, true);
+ else append(m_server->getNickname(), result.output);
+ }
+ else if (result.outputList.count())
+ {
+ Q_ASSERT(result.type==Konversation::Message);
+ for ( QStringList::Iterator it = result.outputList.begin(); it != result.outputList.end(); ++it )
+ {
+ append(m_server->getNickname(), *it);
+ }
+ }
+ // Send anything else to the server
+ if(!result.toServerList.empty())
+ {
+ m_server->queueList(result.toServerList);
+ }
+ else
+ {
+ m_server->queue(result.toServer);
+ }
+ } // for
+}
+
+void Channel::setNickname(const QString& newNickname)
+{
+ nicknameCombobox->setCurrentText(newNickname);
+}
+
+QStringList Channel::getSelectedNickList()
+{
+ QStringList result;
+
+ if (channelCommand)
+ result.append(textView->getContextNick());
+ else
+ {
+ Nick* nick=nicknameList.first();
+
+ while(nick)
+ {
+ if(nick->isSelected()) result.append(nick->getChannelNick()->getNickname());
+ nick=nicknameList.next();
+ }
+ }
+
+ return result;
+}
+
+ChannelNickList Channel::getSelectedChannelNicks()
+{
+ ChannelNickList result;
+ Nick* nick=nicknameList.first();
+
+ while(nick)
+ {
+ if(channelCommand)
+ {
+ if(nick->getChannelNick()->getNickname() == textView->getContextNick())
+ {
+ result.append(nick->getChannelNick());
+ return result;
+ }
+ }
+ else if(nick->isSelected())
+ result.append(nick->getChannelNick());
+
+ nick=nicknameList.next();
+ }
+
+ return result;
+
+}
+
+void Channel::channelLimitChanged()
+{
+ unsigned int lim=limit->text().toUInt();
+
+ modeButtonClicked(7,lim>0);
+}
+
+void Channel::modeButtonClicked(int id, bool on)
+{
+ char mode[]={'t','n','s','i','p','m','k','l'};
+ QString command("MODE %1 %2%3 %4");
+ QString args = getPassword();
+
+ if (mode[id] == 'k')
+ {
+ if (args.isEmpty())
+ {
+ QCString newPassword;
+
+ int result = KPasswordDialog::getPassword(newPassword, i18n("Channel Password"));
+
+ if (result == KPasswordDialog::Accepted && !newPassword.isEmpty())
+ args = newPassword;
+ }
+
+ }
+ else if(mode[id]=='l')
+ {
+ if(limit->text().isEmpty() && on)
+ {
+ bool ok=false;
+ // ask user how many nicks should be the limit
+ args=KInputDialog::getText(i18n("Nick Limit"),
+ i18n("Enter the new nick limit:"),
+ limit->text(), // will be always "" but what the hell ;)
+ &ok,
+ this);
+ // leave this function if user cancels
+ if(!ok) return;
+ }
+ else if(on)
+ args=limit->text();
+ }
+ // put together the mode command and send it to the server queue
+ m_server->queue(command.arg(getName()).arg((on) ? "+" : "-").arg(mode[id]).arg(args));
+}
+
+void Channel::quickButtonClicked(const QString &buttonText)
+{
+ // parse wildcards (toParse,nickname,channelName,nickList,queryName,parameter)
+ QString out=m_server->parseWildcards(buttonText,m_server->getNickname(),getName(),getPassword(),getSelectedNickList(),QString());
+
+ // are there any newlines in the definition?
+ if(out.find('\n')!=-1)
+ sendChannelText(out);
+ // single line without newline needs to be copied into input line
+ else
+ channelInput->setText(out);
+}
+
+void Channel::addNickname(ChannelNickPtr channelnick)
+{
+
+ QString nickname = channelnick->loweredNickname();
+
+ Nick* nick=0;
+ Nick* lookNick;
+ QPtrListIterator<Nick> it(nicknameList);
+
+ while((lookNick = it.current()) != 0)
+ {
+ if(lookNick->getChannelNick()->loweredNickname() == nickname)
+ {
+ nick = lookNick;
+ break;
+ }
+
+ ++it;
+ }
+
+ if(nick == 0)
+ {
+ fastAddNickname(channelnick);
+
+ if(channelnick->isAnyTypeOfOp())
+ {
+ adjustOps(1);
+ }
+
+ adjustNicks(1);
+ requestNickListSort();
+ }
+ else
+ {
+ Q_ASSERT(false); // We shouldn't be adding someone that is already in the channel.
+ }
+}
+
+// Use with caution! Does not sort or check for duplicates!
+void Channel::fastAddNickname(ChannelNickPtr channelnick)
+{
+ Q_ASSERT(channelnick);
+ if(!channelnick) return;
+ Nick* nick = new Nick(nicknameListView, channelnick);
+ // nicks get sorted later
+ nicknameList.append(nick);
+}
+
+void Channel::nickRenamed(const QString &oldNick, const NickInfo& nickInfo)
+{
+
+ /* Did we change our nick name? */
+ QString newNick = nickInfo.getNickname();
+
+ if(newNick == m_server->getNickname()) /* Check newNick because m_server->getNickname() is already updated to new nick */
+ {
+ setNickname(newNick);
+ appendCommandMessage(i18n("Nick"),i18n("You are now known as %1.").arg(newNick), false, true, true);
+ }
+ else
+ {
+ /* No, must've been someone else */
+ appendCommandMessage(i18n("Nick"),i18n("%1 is now known as %2.").arg(oldNick).arg(newNick),false);
+ }
+
+ nicknameListView->sort();
+
+}
+
+void Channel::joinNickname(ChannelNickPtr channelNick)
+{
+ if(channelNick->getNickname() == m_server->getNickname())
+ {
+ m_joined = true;
+ emit joined(this);
+ appendCommandMessage(i18n("Join"),i18n("%1 is the channel and %2 is our hostmask",
+ "You have joined the channel %1 (%2).").arg(getName()).arg(channelNick->getHostmask()),false, false, true);
+ m_ownChannelNick = channelNick;
+ connect(m_ownChannelNick, SIGNAL(channelNickChanged()), SLOT(refreshModeButtons()));
+ refreshModeButtons();
+ setActive(true);
+
+ //HACK the way the notification priorities work sucks, this forces the tab text color to ungray right now.
+ if (m_currentTabNotify == Konversation::tnfNone || !Preferences::tabNotificationsEvents())
+ KonversationApplication::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
+
+ KonversationApplication::instance()->notificationHandler()->channelJoin(this,getName());
+ }
+ else
+ {
+ QString nick = channelNick->getNickname();
+ QString hostname = channelNick->getHostmask();
+ appendCommandMessage(i18n("Join"), i18n("%1 is the nick joining and %2 the hostmask of that nick",
+ "%1 has joined this channel (%2).").arg(nick).arg(hostname),false, false);
+ addNickname(channelNick);
+ }
+}
+
+void Channel::removeNick(ChannelNickPtr channelNick, const QString &reason, bool quit)
+{
+ QString displayReason = reason;
+
+ if(!displayReason.isEmpty())
+ {
+ // if the reason contains text markup characters, play it safe and reset all
+ if(displayReason.find(QRegExp("[\\0000-\\0037]")) != -1)
+ displayReason += "\017";
+ }
+
+ if(channelNick->getNickname() == m_server->getNickname())
+ {
+ //If in the future we can leave a channel, but not close the window, refreshModeButtons() has to be called.
+ if (quit)
+ {
+ if (displayReason.isEmpty())
+ appendCommandMessage(i18n("Quit"), i18n("You have left this server."), false);
+ else
+ appendCommandMessage(i18n("Quit"), i18n("%1 adds the reason", "You have left this server (%1).").arg(displayReason), false);
+ }
+ else
+ {
+ if (displayReason.isEmpty())
+ appendCommandMessage(i18n("Part"), i18n("You have left channel %1.").arg(getName()), false);
+ else
+ appendCommandMessage(i18n("Part"), i18n("%1 adds the channel and %2 the reason",
+ "You have left channel %1 (%2).").arg(getName()).arg(displayReason), false);
+
+ }
+
+ delete this;
+ }
+ else
+ {
+ if (quit)
+ {
+ if (displayReason.isEmpty())
+ appendCommandMessage(i18n("Quit"), i18n("%1 has left this server.").arg(channelNick->getNickname()), false);
+ else
+ appendCommandMessage(i18n("Quit"), i18n("%1 adds the nick and %2 the reason",
+ "%1 has left this server (%2).").arg(channelNick->getNickname()).arg(displayReason), false);
+ }
+ else
+ {
+ if (displayReason.isEmpty())
+ appendCommandMessage(i18n("Part"), i18n("%1 has left this channel.").arg(channelNick->getNickname()), false);
+ else
+ appendCommandMessage(i18n("Part"), i18n("%1 adds the nick and %2 the reason",
+ "%1 has left this channel (%2).").arg(channelNick->getNickname()).arg(displayReason), false);
+ }
+
+ if(channelNick->isAnyTypeOfOp())
+ {
+ adjustOps(-1);
+ }
+
+ adjustNicks(-1);
+ Nick* nick = getNickByName(channelNick->getNickname());
+
+ if(nick)
+ {
+ nicknameList.removeRef(nick);
+ }
+ else
+ {
+ kdWarning() << "Channel::removeNick(): Nickname " << channelNick->getNickname() << " not found!"<< endl;
+ }
+ }
+}
+
+void Channel::kickNick(ChannelNickPtr channelNick, const QString &kicker, const QString &reason)
+{
+ QString displayReason = reason;
+
+ if(!displayReason.isEmpty())
+ {
+ // if the reason contains text markup characters, play it safe and reset all
+ if(displayReason.find(QRegExp("[\\0000-\\0037]")) != -1)
+ displayReason += "\017";
+ }
+
+ if(channelNick->getNickname() == m_server->getNickname())
+ {
+ if(kicker == m_server->getNickname())
+ {
+ if (displayReason.isEmpty())
+ appendCommandMessage(i18n("Kick"), i18n("You have kicked yourself from channel %1.").arg(getName()));
+ else
+ appendCommandMessage(i18n("Kick"), i18n("%1 adds the channel and %2 the reason",
+ "You have kicked yourself from channel %1 (%2).").arg(getName()).arg(displayReason));
+ }
+ else
+ {
+ if (displayReason.isEmpty())
+ {
+ appendCommandMessage(i18n("Kick"), i18n("%1 adds the channel, %2 adds the kicker",
+ "You have been kicked from channel %1 by %2.")
+ .arg(getName()).arg(kicker), true);
+ }
+ else
+ {
+ appendCommandMessage(i18n("Kick"), i18n("%1 adds the channel, %2 the kicker and %3 the reason",
+ "You have been kicked from channel %1 by %2 (%3).")
+ .arg(getName()).arg(kicker).arg(displayReason), true);
+ }
+
+ KonversationApplication::instance()->notificationHandler()->kick(this,getName(), kicker);
+ }
+
+ m_joined=false;
+ setActive(false);
+
+ //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now.
+ if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl))
+ KonversationApplication::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
+
+ return;
+ }
+ else
+ {
+ if(kicker == m_server->getNickname())
+ {
+ if (displayReason.isEmpty())
+ appendCommandMessage(i18n("Kick"), i18n("You have kicked %1 from the channel.").arg(channelNick->getNickname()));
+ else
+ appendCommandMessage(i18n("Kick"), i18n("%1 adds the kicked nick and %2 the reason",
+ "You have kicked %1 from the channel (%2).").arg(channelNick->getNickname()).arg(displayReason), true);
+ }
+ else
+ {
+ if (displayReason.isEmpty())
+ {
+ appendCommandMessage(i18n("Kick"), i18n("%1 adds the kicked nick, %2 adds the kicker",
+ "%1 has been kicked from the channel by %2.")
+ .arg(channelNick->getNickname()).arg(kicker), true);
+ }
+ else
+ {
+ appendCommandMessage(i18n("Kick"), i18n("%1 adds the kicked nick, %2 the kicker and %3 the reason",
+ "%1 has been kicked from the channel by %2 (%3).")
+ .arg(channelNick->getNickname()).arg(kicker).arg(displayReason), true);
+ }
+ }
+
+ if(channelNick->isAnyTypeOfOp())
+ adjustOps(-1);
+
+ adjustNicks(-1);
+ Nick* nick = getNickByName(channelNick->getNickname());
+
+ if(nick == 0)
+ {
+ kdWarning() << "Channel::kickNick(): Nickname " << channelNick->getNickname() << " not found!"<< endl;
+ }
+ else
+ {
+ nicknameList.removeRef(nick);
+ }
+ }
+}
+
+Nick* Channel::getNickByName(const QString &lookname)
+{
+ QString lcLookname = lookname.lower();
+ QPtrListIterator<Nick> it(nicknameList);
+
+ while(it.current() != 0)
+ {
+ if(it.current()->getChannelNick()->loweredNickname() == lcLookname)
+ return it.current();
+
+ ++it;
+ }
+
+ return 0;
+}
+
+void Channel::adjustNicks(int value)
+{
+ if((nicks == 0) && (value <= 0))
+ {
+ return;
+ }
+
+ nicks += value;
+
+ if(nicks < 0)
+ {
+ nicks = 0;
+ }
+
+ emitUpdateInfo();
+}
+
+void Channel::adjustOps(int value)
+{
+ if((ops == 0) && (value <= 0))
+ {
+ return;
+ }
+
+ ops += value;
+
+ if(ops < 0)
+ {
+ ops = 0;
+ }
+
+ emitUpdateInfo();
+}
+
+void Channel::emitUpdateInfo()
+{
+ QString info = getName() + " - ";
+ info += i18n("%n nick", "%n nicks", numberOfNicks());
+ info += i18n(" (%n op)", " (%n ops)", numberOfOps());
+
+ emit updateInfo(info);
+}
+
+void Channel::setTopic(const QString &newTopic)
+{
+ appendCommandMessage(i18n("Topic"), i18n("The channel topic is \"%1\".").arg(newTopic));
+ QString topic = Konversation::removeIrcMarkup(newTopic);
+ topicLine->setText(topic);
+ topicAuthorUnknown=true; // if we only get called with a topic, it was a 332, which usually has a 333 next
+
+ // cut off "nickname" and "time_t" portion of the topic before comparing, otherwise the history
+ // list will fill up with the same entries while the user only requests the topic to be seen.
+
+ if(m_topicHistory.first().section(' ', 2) != newTopic)
+ {
+ m_topicHistory.prepend(QString("%1 "+i18n("unknown")+" %2").arg(QDateTime::currentDateTime().toTime_t()).arg(newTopic));
+ emit topicHistoryChanged();
+ }
+}
+
+void Channel::setTopic(const QString &nickname, const QString &newTopic) // Overloaded
+{
+ if(nickname == m_server->getNickname())
+ {
+ appendCommandMessage(i18n("Topic"), i18n("You set the channel topic to \"%1\".").arg(newTopic));
+ }
+ else
+ {
+ appendCommandMessage(i18n("Topic"), i18n("%1 sets the channel topic to \"%2\".").arg(nickname).arg(newTopic));
+ }
+
+ m_topicHistory.prepend(QString("%1 %2 %3").arg(QDateTime::currentDateTime().toTime_t()).arg(nickname).arg(newTopic));
+ QString topic = Konversation::removeIrcMarkup(newTopic);
+ topicLine->setText(topic);
+
+ emit topicHistoryChanged();
+}
+
+QStringList Channel::getTopicHistory()
+{
+ return m_topicHistory;
+}
+
+QString Channel::getTopic()
+{
+ return m_topicHistory[0];
+}
+
+void Channel::setTopicAuthor(const QString& newAuthor, QDateTime time)
+{
+ if (time.isNull() || !time.isValid())
+ time=QDateTime::currentDateTime();
+
+ if(topicAuthorUnknown)
+ {
+ m_topicHistory[0] = QString("%1").arg(time.toTime_t()) + ' ' + newAuthor + ' ' + m_topicHistory[0].section(' ', 2);
+ topicAuthorUnknown = false;
+
+ emit topicHistoryChanged();
+ }
+}
+
+void Channel::updateMode(const QString& sourceNick, char mode, bool plus, const QString &parameter)
+{
+ //Note for future expansion: doing m_server->getChannelNick(getName(), sourceNick); may not return a valid channelNickPtr if the
+ //mode is updated by the server.
+
+ QString message;
+ ChannelNickPtr parameterChannelNick=m_server->getChannelNick(getName(), parameter);
+
+ bool fromMe=false;
+ bool toMe=false;
+
+ // remember if this nick had any type of op.
+ bool wasAnyOp=false;
+ if(parameterChannelNick)
+ wasAnyOp=parameterChannelNick->isAnyTypeOfOp();
+
+ if(sourceNick.lower()==m_server->loweredNickname())
+ fromMe=true;
+ if(parameter.lower()==m_server->loweredNickname())
+ toMe=true;
+
+ switch(mode)
+ {
+ case 'q':
+ if(plus)
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You give channel owner privileges to yourself.");
+ else
+ message=i18n("You give channel owner privileges to %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 gives channel owner privileges to you.").arg(sourceNick);
+ else
+ message=i18n("%1 gives channel owner privileges to %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ else
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You take channel owner privileges from yourself.");
+ else
+ message=i18n("You take channel owner privileges from %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 takes channel owner privileges from you.").arg(sourceNick);
+ else
+ message=i18n("%1 takes channel owner privileges from %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ if(parameterChannelNick)
+ {
+ parameterChannelNick->setOwner(plus);
+ emitUpdateInfo();
+ nicknameListView->sort();
+ }
+ break;
+
+ case 'a':
+ if(plus)
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You give channel admin privileges to yourself.");
+ else
+ message=i18n("You give channel admin privileges to %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 gives channel admin privileges to you.").arg(sourceNick);
+ else
+ message=i18n("%1 gives channel admin privileges to %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ else
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You take channel admin privileges from yourself.");
+ else
+ message=i18n("You take channel admin privileges from %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 takes channel admin privileges from you.").arg(sourceNick);
+ else
+ message=i18n("%1 takes channel admin privileges from %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ if(parameterChannelNick)
+ {
+ parameterChannelNick->setOwner(plus);
+ emitUpdateInfo();
+ nicknameListView->sort();
+ }
+ break;
+
+ case 'o':
+ if(plus)
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You give channel operator privileges to yourself.");
+ else
+ message=i18n("You give channel operator privileges to %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 gives channel operator privileges to you.").arg(sourceNick);
+ else
+ message=i18n("%1 gives channel operator privileges to %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ else
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You take channel operator privileges from yourself.");
+ else
+ message=i18n("You take channel operator privileges from %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 takes channel operator privileges from you.").arg(sourceNick);
+ else
+ message=i18n("%1 takes channel operator privileges from %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ if(parameterChannelNick)
+ {
+ parameterChannelNick->setOp(plus);
+ emitUpdateInfo();
+ nicknameListView->sort();
+ }
+ break;
+
+ case 'h':
+ if(plus)
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You give channel halfop privileges to yourself.");
+ else
+ message=i18n("You give channel halfop privileges to %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 gives channel halfop privileges to you.").arg(sourceNick);
+ else
+ message=i18n("%1 gives channel halfop privileges to %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ else
+ {
+ if(fromMe)
+ {
+ if(toMe)
+ message=i18n("You take channel halfop privileges from yourself.");
+ else
+ message=i18n("You take channel halfop privileges from %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe)
+ message=i18n("%1 takes channel halfop privileges from you.").arg(sourceNick);
+ else
+ message=i18n("%1 takes channel halfop privileges from %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ if(parameterChannelNick)
+ {
+ parameterChannelNick->setHalfOp(plus);
+ emitUpdateInfo();
+ nicknameListView->sort();
+ }
+ break;
+
+ //case 'O': break;
+
+ case 'v':
+ if(plus)
+ {
+ if(fromMe)
+ {
+ if(toMe) message=i18n("You give yourself permission to talk.");
+ else message=i18n("You give %1 permission to talk.").arg(parameter);
+ }
+ else
+ {
+ if(toMe) message=i18n("%1 gives you permission to talk.").arg(sourceNick);
+ else message=i18n("%1 gives %2 permission to talk.").arg(sourceNick).arg(parameter);
+ }
+ }
+ else
+ {
+ if(fromMe)
+ {
+ if(toMe) message=i18n("You take the permission to talk from yourself.");
+ else message=i18n("You take the permission to talk from %1.").arg(parameter);
+ }
+ else
+ {
+ if(toMe) message=i18n("%1 takes the permission to talk from you.").arg(sourceNick);
+ else message=i18n("%1 takes the permission to talk from %2.").arg(sourceNick).arg(parameter);
+ }
+ }
+ if(parameterChannelNick)
+ {
+ parameterChannelNick->setVoice(plus);
+ nicknameListView->sort();
+ }
+ break;
+
+ case 'c':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'no colors allowed'.");
+ else message=i18n("%1 sets the channel mode to 'no colors allowed'.").arg(sourceNick);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'allow color codes'.");
+ else message=i18n("%1 sets the channel mode to 'allow color codes'.").arg(sourceNick);
+ }
+ break;
+
+ case 'i':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'invite only'.");
+ else message=i18n("%1 sets the channel mode to 'invite only'.").arg(sourceNick);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You remove the 'invite only' mode from the channel.");
+ else message=i18n("%1 removes the 'invite only' mode from the channel.").arg(sourceNick);
+ }
+ modeI->setDown(plus);
+ break;
+
+ case 'm':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'moderated'.");
+ else message=i18n("%1 sets the channel mode to 'moderated'.").arg(sourceNick);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'unmoderated'.");
+ else message=i18n("%1 sets the channel mode to 'unmoderated'.").arg(sourceNick);
+ }
+ modeM->setDown(plus);
+ break;
+
+ case 'n':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'no messages from outside'.");
+ else message=i18n("%1 sets the channel mode to 'no messages from outside'.").arg(sourceNick);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'allow messages from outside'.");
+ else message=i18n("%1 sets the channel mode to 'allow messages from outside'.").arg(sourceNick);
+ }
+ modeN->setDown(plus);
+ break;
+
+ case 'p':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'private'.");
+ else message=i18n("%1 sets the channel mode to 'private'.").arg(sourceNick);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'public'.");
+ else message=i18n("%1 sets the channel mode to 'public'.").arg(sourceNick);
+ }
+ modeP->setDown(plus);
+ if(plus) modeS->setDown(false);
+ break;
+
+ case 's':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'secret'.");
+ else message=i18n("%1 sets the channel mode to 'secret'.").arg(sourceNick);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You set the channel mode to 'visible'.");
+ else message=i18n("%1 sets the channel mode to 'visible'.").arg(sourceNick);
+ }
+ modeS->setDown(plus);
+ if(plus) modeP->setDown(false);
+ break;
+
+ //case 'r': break;
+
+ case 't':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You switch on 'topic protection'.");
+ else message=i18n("%1 switches on 'topic protection'.").arg(sourceNick);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You switch off 'topic protection'.");
+ else message=i18n("%1 switches off 'topic protection'.").arg(sourceNick);
+ }
+ modeT->setDown(plus);
+ break;
+
+ case 'k':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel key to '%1'.").arg(parameter);
+ else message=i18n("%1 sets the channel key to '%2'.").arg(sourceNick).arg(parameter);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You remove the channel key.");
+ else message=i18n("%1 removes the channel key.").arg(sourceNick);
+ }
+ modeK->setDown(plus);
+ break;
+
+ case 'l':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set the channel limit to %1 nicks.").arg(parameter);
+ else message=i18n("%1 sets the channel limit to %2 nicks.").arg(sourceNick).arg(parameter);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You remove the channel limit.");
+ else message=i18n("%1 removes the channel limit.").arg(sourceNick);
+ }
+ modeL->setDown(plus);
+ if(plus) limit->setText(parameter);
+ else limit->clear();
+ break;
+
+ case 'b':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set a ban on %1.").arg(parameter);
+ else message=i18n("%1 sets a ban on %2.").arg(sourceNick).arg(parameter);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You remove the ban on %1.").arg(parameter);
+ else message=i18n("%1 removes the ban on %2.").arg(sourceNick).arg(parameter);
+ }
+ break;
+
+ case 'e':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set a ban exception on %1.").arg(parameter);
+ else message=i18n("%1 sets a ban exception on %2.").arg(sourceNick).arg(parameter);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You remove the ban exception on %1.").arg(parameter);
+ else message=i18n("%1 removes the ban exception on %2.").arg(sourceNick).arg(parameter);
+ }
+ break;
+
+ case 'I':
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set invitation mask %1.").arg(parameter);
+ else message=i18n("%1 sets invitation mask %2.").arg(sourceNick).arg(parameter);
+ }
+ else
+ {
+ if(fromMe) message=i18n("You remove the invitation mask %1.").arg(parameter);
+ else message=i18n("%1 removes the invitation mask %2.").arg(sourceNick).arg(parameter);
+ }
+ break;
+ default:
+ if(plus)
+ {
+ if(fromMe) message=i18n("You set channel mode +%1").arg(mode);
+ else message=i18n("%1 sets channel mode +%2").arg(sourceNick).arg(mode);
+ }
+ else
+ {
+ if (fromMe) message=i18n("You set channel mode -%1").arg(mode);
+ else message= i18n("%1 sets channel mode -%2").arg(sourceNick).arg(mode);
+ }
+ }
+
+ // check if this nick's anyOp-status has changed and adjust ops accordingly
+ if(parameterChannelNick)
+ {
+ if(wasAnyOp && (!parameterChannelNick->isAnyTypeOfOp()))
+ adjustOps(-1);
+ else if((!wasAnyOp) && parameterChannelNick->isAnyTypeOfOp())
+ adjustOps(1);
+ }
+
+ if(!message.isEmpty() && !Preferences::useLiteralModes())
+ {
+ appendCommandMessage(i18n("Mode"),message);
+ }
+
+ updateModeWidgets(mode,plus,parameter);
+}
+
+void Channel::clearModeList()
+{
+ QString k;
+
+ // Keep channel password in the backing store, for rejoins.
+ for (QStringList::const_iterator it = m_modeList.begin(); it != m_modeList.end(); ++it)
+ {
+ if ((*it)[0] == 'k') k = (*it);
+ }
+
+ m_modeList.clear();
+
+ if (!k.isEmpty()) m_modeList << k;
+
+ modeT->setOn(0);
+ modeT->setDown(0);
+
+ modeN->setOn(0);
+ modeN->setDown(0);
+
+ modeS->setOn(0);
+ modeS->setDown(0);
+
+ modeI->setOn(0);
+ modeI->setDown(0);
+
+ modeP->setOn(0);
+ modeP->setDown(0);
+
+ modeM->setOn(0);
+ modeM->setDown(0);
+
+ modeK->setOn(0);
+ modeK->setDown(0);
+
+ modeL->setOn(0);
+ modeL->setDown(0);
+
+ limit->clear();
+
+ emit modesChanged();
+}
+
+void Channel::updateModeWidgets(char mode, bool plus, const QString &parameter)
+{
+ ModeButton* widget=0;
+
+ if(mode=='t') widget=modeT;
+ else if(mode=='n') widget=modeN;
+ else if(mode=='s') widget=modeS;
+ else if(mode=='i') widget=modeI;
+ else if(mode=='p') widget=modeP;
+ else if(mode=='m') widget=modeM;
+ else if(mode=='k') widget=modeK;
+ else if(mode=='l')
+ {
+ widget=modeL;
+ if(plus) limit->setText(parameter);
+ else limit->clear();
+ }
+
+ if(widget) widget->setOn(plus);
+
+ if(plus)
+ {
+ m_modeList.append(QString(mode + parameter));
+ }
+ else
+ {
+ QStringList removable = m_modeList.grep(QRegExp(QString("^%1.*").arg(mode)));
+
+ for(QStringList::iterator it = removable.begin(); it != removable.end(); ++it)
+ {
+ m_modeList.remove(m_modeList.find((*it)));
+ }
+ }
+ emit modesChanged();
+}
+
+void Channel::updateQuickButtons(const QStringList &newButtonList)
+{
+ // remove quick buttons from memory and GUI
+ while(buttonList.count())
+ {
+ QuickButton* button=buttonList.at(0);
+ // remove tool tips as well
+ QToolTip::remove(button);
+ buttonList.remove(button);
+ delete button;
+ }
+
+ if(buttonsGrid)delete buttonsGrid;
+
+ // the grid that holds the quick action buttons
+ buttonsGrid = new QGrid(2, nickListButtons);
+
+ // add new quick buttons
+ for(unsigned int index=0;index<newButtonList.count();index++)
+ {
+ // generate empty buttons first, text will be added later
+ QuickButton* quickButton = new QuickButton(QString(), QString(), buttonsGrid);
+ buttonList.append(quickButton);
+
+ connect(quickButton, SIGNAL(clicked(const QString &)), this, SLOT(quickButtonClicked(const QString &)));
+
+ // Get the button definition
+ QString buttonText=newButtonList[index];
+ // Extract button label
+ QString buttonLabel=buttonText.section(',',0,0);
+ // Extract button definition
+ buttonText=buttonText.section(',',1);
+
+ quickButton->setText(buttonLabel);
+ quickButton->setDefinition(buttonText);
+
+ // Add tool tips
+ QString toolTip=buttonText.replace("&","&amp;").
+ replace("<","&lt;").
+ replace(">","&gt;");
+
+ QToolTip::add(quickButton,toolTip);
+
+ quickButton->show();
+ } // for
+
+ // set hide() or show() on grid
+ showQuickButtons(Preferences::showQuickButtons());
+}
+
+void Channel::showQuickButtons(bool show)
+{
+ // Qt does not redraw the buttons properly when they are not on screen
+ // while getting hidden, so we remember the "soon to be" state here.
+ if(isHidden() || !buttonsGrid)
+ {
+ quickButtonsChanged=true;
+ quickButtonsState=show;
+ }
+ else
+ {
+ if(show)
+ buttonsGrid->show();
+ else
+ buttonsGrid->hide();
+ }
+}
+
+void Channel::showModeButtons(bool show)
+{
+ // Qt does not redraw the buttons properly when they are not on screen
+ // while getting hidden, so we remember the "soon to be" state here.
+ if(isHidden())
+ {
+ modeButtonsChanged=true;
+ modeButtonsState=show;
+ }
+ else
+ {
+ if(show)
+ {
+ topicSplitterHidden = false;
+ modeBox->show();
+ modeBox->parentWidget()->show();
+ }
+ else
+ {
+ modeBox->hide();
+
+ if(topicLine->isHidden())
+ {
+ topicSplitterHidden = true;
+ modeBox->parentWidget()->hide();
+ }
+ }
+ }
+}
+
+void Channel::indicateAway(bool show)
+{
+ // Qt does not redraw the label properly when they are not on screen
+ // while getting hidden, so we remember the "soon to be" state here.
+ if(isHidden())
+ {
+ awayChanged=true;
+ awayState=show;
+ }
+ else
+ {
+ if(show)
+ awayLabel->show();
+ else
+ awayLabel->hide();
+ }
+}
+
+void Channel::showEvent(QShowEvent*)
+{
+ // If the show quick/mode button settings have changed, apply the changes now
+ if(quickButtonsChanged)
+ {
+ quickButtonsChanged=false;
+ showQuickButtons(quickButtonsState);
+ }
+
+ if(modeButtonsChanged)
+ {
+ modeButtonsChanged=false;
+ showModeButtons(modeButtonsState);
+ }
+
+ if(awayChanged)
+ {
+ awayChanged=false;
+ indicateAway(awayState);
+ }
+
+ syncSplitters();
+}
+
+void Channel::syncSplitters()
+{
+ QValueList<int> vertSizes = Preferences::topicSplitterSizes();
+ QValueList<int> horizSizes = Preferences::channelSplitterSizes();
+
+ if (vertSizes.isEmpty())
+ {
+ vertSizes << m_topicButton->height() << (height() - m_topicButton->height());
+ Preferences::setTopicSplitterSizes(vertSizes);
+ }
+
+ if (horizSizes.isEmpty())
+ {
+ int listWidth = nicknameListView->columnWidth(0) + nicknameListView->columnWidth(1);
+ horizSizes << (width() - listWidth) << listWidth;
+ Preferences::setChannelSplitterSizes(horizSizes);
+ }
+
+ m_vertSplitter->setSizes(vertSizes);
+ m_horizSplitter->setSizes(horizSizes);
+
+ splittersInitialized = true;
+}
+
+void Channel::updateAppearance()
+{
+ QColor fg,bg,abg;
+
+ if(Preferences::inputFieldsBackgroundColor())
+ {
+ fg=Preferences::color(Preferences::ChannelMessage);
+ bg=Preferences::color(Preferences::TextViewBackground);
+ abg=Preferences::color(Preferences::AlternateBackground);
+ }
+ else
+ {
+ fg=colorGroup().foreground();
+ bg=colorGroup().base();
+ abg=KGlobalSettings::alternateBackgroundColor();
+ }
+
+ channelInput->unsetPalette();
+ channelInput->setPaletteForegroundColor(fg);
+ channelInput->setPaletteBackgroundColor(bg);
+
+ limit->unsetPalette();
+ limit->setPaletteForegroundColor(fg);
+ limit->setPaletteBackgroundColor(bg);
+
+ getTextView()->unsetPalette();
+
+ if(Preferences::showBackgroundImage())
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ Preferences::backgroundImage());
+ }
+ else
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ QString());
+ }
+
+ if (Preferences::customTextFont())
+ {
+ getTextView()->setFont(Preferences::textFont());
+ topicLine->setFont(Preferences::textFont());
+ channelInput->setFont(Preferences::textFont());
+ nicknameCombobox->setFont(Preferences::textFont());
+ limit->setFont(Preferences::textFont());
+ }
+ else
+ {
+ getTextView()->setFont(KGlobalSettings::generalFont());
+ topicLine->setFont(KGlobalSettings::generalFont());
+ channelInput->setFont(KGlobalSettings::generalFont());
+ nicknameCombobox->setFont(KGlobalSettings::generalFont());
+ limit->setFont(KGlobalSettings::generalFont());
+ }
+
+ nicknameListView->resort();
+ nicknameListView->unsetPalette();
+ nicknameListView->setPaletteForegroundColor(fg);
+ nicknameListView->setPaletteBackgroundColor(bg);
+ nicknameListView->setAlternateBackground(abg);
+
+ if (Preferences::customListFont())
+ nicknameListView->setFont(Preferences::listFont());
+ else
+ nicknameListView->setFont(KGlobalSettings::generalFont());
+
+ nicknameListView->refresh();
+
+ showModeButtons(Preferences::showModeButtons());
+ showNicknameList(Preferences::showNickList());
+ showNicknameBox(Preferences::showNicknameBox());
+ showTopic(Preferences::showTopic());
+ setAutoUserhost(Preferences::autoUserhost());
+
+ updateQuickButtons(Preferences::quickButtonList());
+
+ ChatWindow::updateAppearance();
+}
+
+void Channel::nicknameComboboxChanged()
+{
+ QString newNick=nicknameCombobox->currentText();
+ oldNick=m_server->getNickname();
+ if(oldNick!=newNick)
+ {
+ nicknameCombobox->setCurrentText(oldNick);
+ changeNickname(newNick);
+ // return focus to input line
+ channelInput->setFocus();
+ }
+}
+
+void Channel::changeNickname(const QString& newNickname)
+{
+ if (!newNickname.isEmpty())
+ m_server->queue("NICK "+newNickname);
+}
+
+void Channel::resetNickList()
+{
+ nicknameListView->setUpdatesEnabled(false);
+ purgeNicks();
+}
+
+void Channel::addPendingNickList(const QStringList& pendingChannelNickList)
+{
+ if(pendingChannelNickList.isEmpty())
+ return;
+
+ if (!m_processingTimer)
+ {
+ m_processingTimer = new QTimer(this);
+ connect(m_processingTimer, SIGNAL(timeout()), this, SLOT(processPendingNicks()));
+ }
+
+ m_pendingChannelNickLists.append(pendingChannelNickList);
+
+ if (!m_processingTimer->isActive())
+ m_processingTimer->start(0);
+}
+
+void Channel::childAdjustFocus()
+{
+ channelInput->setFocus();
+ refreshModeButtons();
+}
+
+void Channel::refreshModeButtons()
+{
+ bool enable = true;
+ if(getOwnChannelNick())
+ {
+ enable=getOwnChannelNick()->isAnyTypeOfOp();
+ } // if not channel nick, then enable is true - fall back to assuming they are op
+
+ //don't disable the mode buttons since you can't then tell if they are enabled or not.
+ //needs to be fixed somehow
+
+ /* modeT->setEnabled(enable);
+ modeN->setEnabled(enable);
+ modeS->setEnabled(enable);
+ modeI->setEnabled(enable);
+ modeP->setEnabled(enable);
+ modeM->setEnabled(enable);
+ modeK->setEnabled(enable);
+ modeL->setEnabled(enable);*/
+ limit->setEnabled(enable);
+
+ // Tooltips for the ModeButtons
+ QString opOnly;
+ if(!enable) opOnly = i18n("You have to be an operator to change this.");
+
+ QToolTip::add(modeT, i18n("Topic can be changed by channel operator only. %1").arg(opOnly));
+ QToolTip::add(modeN, i18n("No messages to channel from clients on the outside. %1").arg(opOnly));
+ QToolTip::add(modeS, i18n("Secret channel. %1").arg(opOnly));
+ QToolTip::add(modeI, i18n("Invite only channel. %1").arg(opOnly));
+ QToolTip::add(modeP, i18n("Private channel. %1").arg(opOnly));
+ QToolTip::add(modeM, i18n("Moderated channel. %1").arg(opOnly));
+ QToolTip::add(modeK, i18n("Protect channel with a password."));
+ QToolTip::add(modeL, i18n("Set user limit to channel."));
+
+}
+
+void Channel::cycleChannel()
+{
+ closeYourself();
+ m_server->sendJoinCommand(getName(), getPassword());
+}
+
+void Channel::autoUserhost()
+{
+ if(Preferences::autoUserhost() && !Preferences::autoWhoContinuousEnabled())
+ {
+ int limit = 5;
+
+ QString nickString;
+ QPtrList<Nick> nickList = getNickList();
+ QPtrListIterator<Nick> it(nickList);
+ Nick* nick;
+
+ while((nick = it.current()) != 0)
+ {
+ if(nick->getChannelNick()->getHostmask().isEmpty())
+ {
+ if(limit--) nickString = nickString + nick->getChannelNick()->getNickname() + ' ';
+ else break;
+ }
+
+ ++it;
+ }
+
+ if(!nickString.isEmpty()) m_server->requestUserhost(nickString);
+ }
+}
+
+void Channel::setAutoUserhost(bool state)
+{
+ if(state)
+ {
+ // we can't have automatic resizing with three columns; the hostname column is too wide
+ nicknameListView->setHScrollBarMode(QScrollView::Auto);
+
+ // restart userhost timer
+ userhostTimer.start(10000);
+ // if the column was actually gone (just to be sure) ...
+ if(nicknameListView->columns()==2)
+ {
+ // re-add the hostmask column
+ nicknameListView->addColumn(QString());
+ nicknameListView->setColumnWidthMode(2,KListView::Maximum);
+ nicknameListView->setResizeMode(QListView::NoColumn);
+
+ // re-add already known hostmasks
+ QListViewItem* item=nicknameListView->itemAtIndex(0);
+ while(item)
+ {
+ Nick* lookNick=getNickByName(item->text(1));
+ if(lookNick) item->setText(2,lookNick->getChannelNick()->getHostmask());
+ item=item->itemBelow();
+ }
+ }
+ }
+ else
+ {
+ userhostTimer.stop();
+ if(nicknameListView->columns()==3) nicknameListView->removeColumn(2);
+ nicknameListView->setHScrollBarMode(QScrollView::AlwaysOff);
+ // make the nick column resize itself automatically to prevent horizontal scrollbar
+ nicknameListView->setResizeMode(QListView::LastColumn);
+ }
+}
+
+void Channel::scheduleAutoWho() // slot
+{
+ if(m_whoTimer.isActive())
+ m_whoTimer.stop();
+ if(Preferences::autoWhoContinuousEnabled())
+ m_whoTimer.start(Preferences::autoWhoContinuousInterval() * 1000, true);
+}
+
+void Channel::autoWho()
+{
+ // don't use auto /WHO when the number of nicks is too large, or get banned.
+ if((nicks > Preferences::autoWhoNicksLimit()) ||
+ m_server->getInputFilter()->isWhoRequestUnderProcess(getName()))
+ {
+ scheduleAutoWho();
+ return;
+ }
+
+ m_server->requestWho(getName());
+}
+
+void Channel::fadeActivity()
+{
+ QPtrListIterator<Nick> it( nicknameList );
+ Nick *nick;
+ while ( (nick = it.current()) != 0 ) {
+ ++it;
+ nick->getChannelNick()->lessActive();
+ }
+}
+
+QString Channel::getTextInLine()
+{
+ return channelInput->text();
+}
+
+bool Channel::canBeFrontView()
+{
+ return true;
+}
+
+bool Channel::searchView()
+{
+ return true;
+}
+
+void Channel::appendInputText(const QString& s, bool fromCursor)
+{
+ if(!fromCursor)
+ {
+ channelInput->append(s);
+ }
+ else
+ {
+ int para = 0, index = 0;
+ channelInput->getCursorPosition(&para, &index);
+ channelInput->insertAt(s, para, index);
+ channelInput->setCursorPosition(para, index + s.length());
+ }
+}
+
+bool Channel::closeYourself(bool confirm)
+{
+ int result=KMessageBox::Continue;
+ if (confirm)
+ result = KMessageBox::warningContinueCancel(this, i18n("Do you want to leave %1?").arg(getName()),
+ i18n("Leave Channel"), i18n("Leave"), "QuitChannelTab");
+
+ if (result==KMessageBox::Continue)
+ {
+ m_server->closeChannel(getName());
+ m_server->removeChannel(this);
+ Preferences::setSpellChecking(channelInput->checkSpellingEnabled());
+ deleteLater();
+ return true;
+ }
+ return false;
+}
+
+void Channel::serverOnline(bool online)
+{
+ setActive(online);
+}
+
+//Used to disable functions when not connected, does not necessarily mean the server is offline
+void Channel::setActive(bool active)
+{
+ if (active)
+ {
+ getTextView()->setNickAndChannelContextMenusEnabled(true);
+ nicknameCombobox->setEnabled(true);
+ }
+ else
+ {
+ purgeNicks();
+ getTextView()->setNickAndChannelContextMenusEnabled(false);
+ nicknameCombobox->setEnabled(false);
+ topicLine->setText(QString::null);
+ clearModeList();
+ clearBanList();
+ }
+}
+
+void Channel::showTopic(bool show)
+{
+ if(show)
+ {
+ topicSplitterHidden = false;
+ topicLine->show();
+ m_topicButton->show();
+ topicLine->parentWidget()->show();
+ }
+ else
+ {
+ topicLine->hide();
+ m_topicButton->hide();
+
+ if(modeBox->isHidden())
+ {
+ topicSplitterHidden = true;
+ topicLine->parentWidget()->hide();
+ }
+ }
+}
+
+void Channel::processPendingNicks()
+{
+ QString nickname = m_pendingChannelNickLists.first()[m_currentIndex];
+
+ bool admin = false;
+ bool owner = false;
+ bool op = false;
+ bool halfop = false;
+ bool voice = false;
+
+ // Remove possible mode characters from nickname and store the resulting mode
+ m_server->mangleNicknameWithModes(nickname, admin, owner, op, halfop, voice);
+
+ // TODO: make these an enumeration in KApplication or somewhere, we can use them as well
+ unsigned int mode = (admin ? 16 : 0) +
+ (owner ? 8 : 0) +
+ (op ? 4 : 0) +
+ (halfop ? 2 : 0) +
+ (voice ? 1 : 0);
+
+ // Check if nick is already in the nicklist
+ if (!getNickByName(nickname))
+ {
+ ChannelNickPtr nick = m_server->addNickToJoinedChannelsList(getName(), nickname);
+ Q_ASSERT(nick);
+ nick->setMode(mode);
+
+ fastAddNickname(nick);
+
+ if (nick->isAdmin() || nick->isOwner() || nick->isOp() || nick->isHalfOp())
+ m_opsToAdd++;
+
+ m_currentIndex++;
+ }
+ else
+ {
+ m_pendingChannelNickLists.first().pop_front();
+ }
+
+ if (m_pendingChannelNickLists.first().count() <= m_currentIndex)
+ {
+ adjustNicks(m_pendingChannelNickLists.first().count());
+ adjustOps(m_opsToAdd);
+ m_pendingChannelNickLists.pop_front();
+ m_currentIndex = 0;
+ m_opsToAdd = 0;
+ }
+
+ if (m_pendingChannelNickLists.isEmpty())
+ {
+ m_processingTimer->stop();
+ nicknameListView->sort();
+ sortNickList();
+ nicknameListView->setUpdatesEnabled(true);
+ nicknameListView->triggerUpdate();
+ }
+}
+
+void Channel::setChannelEncoding(const QString& encoding) // virtual
+{
+ Preferences::setChannelEncoding(m_server->getDisplayName(), getName(), encoding);
+}
+
+QString Channel::getChannelEncoding() // virtual
+{
+ return Preferences::channelEncoding(m_server->getDisplayName(), getName());
+}
+
+QString Channel::getChannelEncodingDefaultDesc() // virtual
+{
+ return i18n("Identity Default ( %1 )").arg(getServer()->getIdentity()->getCodecName());
+}
+
+void Channel::showNicknameBox(bool show)
+{
+ if(show)
+ {
+ nicknameCombobox->show();
+ }
+ else
+ {
+ nicknameCombobox->hide();
+ }
+}
+
+void Channel::showNicknameList(bool show)
+{
+ if (show)
+ {
+ channelSplitterHidden = false;
+ nickListButtons->show();
+ }
+ else
+ {
+ channelSplitterHidden = true;
+ nickListButtons->hide();
+ }
+}
+
+void Channel::requestNickListSort()
+{
+ if(!m_delayedSortTimer)
+ {
+ m_delayedSortTimer = new QTimer(this);
+ connect(m_delayedSortTimer, SIGNAL(timeout()), this, SLOT(sortNickList()));
+ }
+
+ if(!m_delayedSortTimer->isActive())
+ {
+ m_delayedSortTimer->start(1000, true);
+ }
+}
+
+void Channel::sortNickList()
+{
+ nicknameList.sort();
+ nicknameListView->resort();
+
+ if(m_delayedSortTimer)
+ {
+ m_delayedSortTimer->stop();
+ }
+}
+
+void Channel::setIdentity(const IdentityPtr identity)
+{
+ if (identity)
+ {
+ nicknameCombobox->clear();
+ nicknameCombobox->insertStringList(identity->getNicknameList());
+ }
+}
+
+bool Channel::eventFilter(QObject* watched, QEvent* e)
+{
+ if((watched == nicknameListView) && (e->type() == QEvent::Resize) && splittersInitialized && isShown())
+ {
+ if (!topicSplitterHidden && !channelSplitterHidden)
+ {
+ Preferences::setChannelSplitterSizes(m_horizSplitter->sizes());
+ Preferences::setTopicSplitterSizes(m_vertSplitter->sizes());
+ }
+ if (!topicSplitterHidden && channelSplitterHidden)
+ {
+ Preferences::setTopicSplitterSizes(m_vertSplitter->sizes());
+ }
+ if (!channelSplitterHidden && topicSplitterHidden)
+ {
+ Preferences::setChannelSplitterSizes(m_horizSplitter->sizes());
+ }
+ }
+
+ return ChatWindow::eventFilter(watched, e);
+}
+
+void Channel::addBan(const QString& ban)
+{
+ for ( QStringList::Iterator it = m_BanList.begin(); it != m_BanList.end(); ++it )
+ {
+ if ((*it).section(' ', 0, 0) == ban.section(' ', 0, 0))
+ {
+ // Ban is already in list.
+ it = m_BanList.remove(it);
+
+ emit banRemoved(ban.section(' ', 0, 0));
+ }
+ }
+
+ m_BanList.prepend(ban);
+
+ emit banAdded(ban);
+}
+
+void Channel::removeBan(const QString& ban)
+{
+ for ( QStringList::Iterator it = m_BanList.begin(); it != m_BanList.end(); ++it )
+ {
+ if ((*it).section(' ', 0, 0) == ban)
+ {
+ it = m_BanList.remove(it);
+
+ emit banRemoved(ban);
+ }
+ }
+}
+
+void Channel::clearBanList()
+{
+ m_BanList.clear();
+
+ emit banListCleared();
+}
+
+void Channel::append(const QString& nickname, const QString& message)
+{
+ if(nickname != getServer()->getNickname()) {
+ Nick* nick = getNickByName(nickname);
+
+ if(nick) {
+ nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t());
+ }
+ }
+
+ ChatWindow::append(nickname, message);
+ nickActive(nickname);
+}
+
+void Channel::appendAction(const QString& nickname, const QString& message)
+{
+ if(nickname != getServer()->getNickname()) {
+ Nick* nick = getNickByName(nickname);
+
+ if(nick) {
+ nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t());
+ }
+ }
+
+ ChatWindow::appendAction(nickname, message);
+ nickActive(nickname);
+}
+
+void Channel::nickActive(const QString& nickname) //FIXME reported to crash, can't reproduce
+{
+ ChannelNickPtr nick=getChannelNick(nickname);
+ //XXX Would be nice to know why it can be null here...
+ if (nick) {
+ nick->moreActive();
+ sortNickList(); //FIXME: no need to completely resort, we can just see if this particular nick should move
+ }
+}
+
+//
+// NickList
+//
+
+NickList::NickList() : QPtrList<Nick>()
+{
+ m_compareMethod = NickList::AlphaNumeric;
+}
+
+void NickList::setCompareMethod(CompareMethod method)
+{
+ m_compareMethod = method;
+}
+
+//doesn't the following somehow duplicate NickListViewItem::compare()?
+int NickList::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2)
+{
+ if(m_compareMethod == NickList::TimeStamp) {
+ int returnValue = static_cast<Nick*>(item2)->getChannelNick()->timeStamp() - static_cast<Nick*>(item1)->getChannelNick()->timeStamp();
+
+ if(returnValue == 0) {
+ returnValue = QString::compare(static_cast<Nick*>(item1)->getChannelNick()->loweredNickname(),
+ static_cast<Nick*>(item2)->getChannelNick()->loweredNickname());
+ }
+
+ return returnValue;
+ }
+
+ return QString::compare(static_cast<Nick*>(item1)->getChannelNick()->loweredNickname(),
+ static_cast<Nick*>(item2)->getChannelNick()->loweredNickname());
+}
+
+QString NickList::completeNick(const QString& pattern, bool& complete, QStringList& found,
+ bool skipNonAlfaNum, bool caseSensitive)
+{
+ found.clear();
+ QString prefix = "^";
+ QString newNick;
+ QString prefixCharacter = Preferences::prefixCharacter();
+ NickList foundNicks;
+ foundNicks.setCompareMethod(NickList::TimeStamp);
+
+ if((pattern.find(QRegExp("^(\\d|\\w)")) != -1) && skipNonAlfaNum)
+ {
+ prefix = "^([^\\d\\w]|[\\_]){0,}";
+ }
+
+ QRegExp regexp(prefix + QRegExp::escape(pattern));
+ regexp.setCaseSensitive(caseSensitive);
+ QPtrListIterator<Nick> it(*this);
+
+ while(it.current() != 0)
+ {
+ newNick = it.current()->getChannelNick()->getNickname();
+
+ if(!prefix.isEmpty() && newNick.contains(prefixCharacter))
+ {
+ newNick = newNick.section( prefixCharacter,1 );
+ }
+
+ if(newNick.find(regexp) != -1)
+ {
+ foundNicks.append(it.current());
+ }
+
+ ++it;
+ }
+
+ foundNicks.sort();
+
+ QPtrListIterator<Nick> it2(foundNicks);
+
+ while(it2.current() != 0)
+ {
+ found.append(it2.current()->getChannelNick()->getNickname());
+ ++it2;
+ }
+
+ if(found.count() > 1)
+ {
+ bool ok = true;
+ unsigned int patternLength = pattern.length();
+ QString firstNick = found[0];
+ unsigned int firstNickLength = firstNick.length();
+ unsigned int foundCount = found.count();
+
+ while(ok && ((patternLength) < firstNickLength))
+ {
+ ++patternLength;
+ QStringList tmp = found.grep(firstNick.left(patternLength), caseSensitive);
+
+ if(tmp.count() != foundCount)
+ {
+ ok = false;
+ --patternLength;
+ }
+ }
+
+ complete = false;
+ return firstNick.left(patternLength);
+ }
+ else if(found.count() == 1)
+ {
+ complete = true;
+ return found[0];
+ }
+
+ return QString();
+}
+
+bool NickList::containsNick(const QString& nickname)
+{
+ QPtrListIterator<Nick> it(*this);
+
+ while (it.current() != 0)
+ {
+ if (it.current()->getChannelNick()->getNickname()==nickname)
+ return true;
+
+ ++it;
+ }
+
+ return false;
+}
+
+#include "channel.moc"
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/channel.h b/konversation/src/channel.h
new file mode 100644
index 0000000..e04d52b
--- /dev/null
+++ b/konversation/src/channel.h
@@ -0,0 +1,375 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004-2006 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef CHANNEL_H
+#define CHANNEL_H
+
+#include "server.h"
+#include "chatwindow.h"
+#include "channelnick.h"
+#include "nick.h"
+
+#include <qtimer.h>
+#include <qstring.h>
+
+
+class QPushButton;
+class QCheckBox;
+class QLabel;
+class QTimer;
+class QListViewItem;
+class QHBox;
+class QStringList;
+class QSplitter;
+class QGrid;
+class QComboBox;
+class QDropEvent;
+class QToolButton;
+
+class KLineEdit;
+
+class NickListView;
+class QuickButton;
+class ModeButton;
+class IRCInput;
+class NickChangeDialog;
+
+namespace Konversation
+{
+ class TopicLabel;
+ class ChannelOptionsDialog;
+}
+
+class NickList : public QPtrList<Nick>
+{
+ public:
+ NickList();
+
+ enum CompareMethod { AlphaNumeric, TimeStamp };
+
+ QString completeNick(const QString& pattern, bool& complete, QStringList& found,
+ bool skipNonAlfaNum, bool caseSensitive);
+
+ void setCompareMethod(CompareMethod method);
+
+ bool containsNick(const QString& nickname);
+
+ protected:
+ virtual int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2);
+
+ private:
+ CompareMethod m_compareMethod;
+};
+
+class Channel : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit Channel(QWidget* parent, QString name);
+ ~Channel();
+//META
+ virtual bool canBeFrontView();
+ virtual bool searchView();
+
+ virtual void append(const QString& nickname,const QString& message);
+ virtual void appendAction(const QString& nickname,const QString& message);
+ void nickActive(const QString& nickname);
+
+//General administrative stuff
+ public:
+ void setName(const QString& newName);
+ QString getPassword();
+
+ const Konversation::ChannelSettings channelSettings();
+
+ QString getPassword() const;
+
+ virtual void setServer(Server* newServer);
+ void setIdentity(const IdentityPtr identity);
+
+ void setEncryptedOutput(bool);
+
+ bool joined() { return m_joined; }
+ bool rejoinable();
+//Unsure of future placement and/or continued existence of these members
+ int numberOfNicks() const { return nicks; }
+ int numberOfOps() const { return ops; }
+ virtual void setChannelEncoding(const QString& encoding);
+ virtual QString getChannelEncoding();
+ virtual QString getChannelEncodingDefaultDesc();
+ virtual bool isInsertSupported() { return true; }
+
+ protected:
+ // use with caution! does not check for duplicates
+ void fastAddNickname(ChannelNickPtr channelnick);
+ void setActive(bool active);
+
+ public slots:
+ void setNickname(const QString& newNickname);
+ void scheduleAutoWho();
+ void setAutoUserhost(bool state);
+ void rejoin();
+
+ protected slots:
+ void autoUserhost();
+ void autoWho();
+ void fadeActivity();
+ virtual void serverOnline(bool online);
+
+
+//Nicklist
+ public:
+ ChannelNickPtr getOwnChannelNick();
+ ChannelNickPtr getChannelNick(const QString &ircnick);
+
+ void joinNickname(ChannelNickPtr channelNick);
+ void removeNick(ChannelNickPtr channelNick, const QString &reason, bool quit);
+ void kickNick(ChannelNickPtr channelNick, const QString &kicker, const QString &reason);
+ void addNickname(ChannelNickPtr channelNick);
+ void nickRenamed(const QString &oldNick, const NickInfo& channelnick);
+ void resetNickList();
+ void addPendingNickList(const QStringList& pendingChannelNickList);
+ Nick *getNickByName(const QString& lookname);
+ NickList getNickList() { return nicknameList; }
+
+ void adjustNicks(int value);
+ void adjustOps(int value);
+ virtual void emitUpdateInfo();
+
+ protected slots:
+ void purgeNicks();
+ void processPendingNicks();
+
+//Topic
+ public:
+ /** Get the current channel topic.
+ *
+ * The topic may or may not have the author that set it at the start of the string,
+ * like: "<author> topic"
+ *
+ * The internal variable topicAuthorUnknown stores whether the "<author>" bit is there or not.
+ *
+ * */
+ QString getTopic();
+ /** Get the channel topic history sorted in reverse chronological order.
+ *
+ * Each topic may or may not have the author that set it at the start of the string,
+ * like: "<author> topic"
+ *
+ * @return a list of topics this channel used to have, current at the top.
+ */
+ QStringList getTopicHistory();
+
+ void setTopic(const QString& topic);
+ void setTopic(const QString& nickname, const QString& topic);
+ void setTopicAuthor(const QString& author, QDateTime t);
+
+ signals:
+ void topicHistoryChanged();
+ void joined(Channel* channel);
+
+
+//Modes
+//TODO: the only representation of the channel limit is held in the GUI
+
+ public:
+ /// Internal - Empty the modelist
+ void clearModeList();
+ /// Get the list of modes that this channel has - e.g. {+l,+s,-m}
+ //TODO: does this method return a list of all modes, all modes that have been changed, or all modes that are +?
+ QStringList getModeList() const { return m_modeList; }
+
+ /** Outputs a message on the channel, and modifies the mode for a ChannelNick.
+ * @param sourceNick The server or the nick of the person that made the mode change.
+ * @param mode The mode that is changing one of v,h,o,a for voice halfop op admin
+ * @param plus True if the mode is being granted, false if it's being taken away.
+ * @param parameter This depends on what the mode change is. In most cases it is the nickname of the person that is being given voice/op/admin etc. See the code.
+ */
+ void updateMode(const QString& sourceNick, char mode, bool plus, const QString &parameter);
+
+ signals:
+ void modesChanged();
+
+//Bans
+ public:
+
+ void addBan(const QString& ban);
+ void removeBan(const QString& ban);
+
+ void clearBanList();
+ QStringList getBanList() const { return m_BanList; }
+
+ signals:
+ void banAdded(const QString& newban);
+ void banRemoved(const QString& newban);
+ void banListCleared();
+
+//Generic GUI
+ public:
+ virtual bool eventFilter(QObject* watched, QEvent* e);
+
+//Specific GUI
+ public:
+ void updateModeWidgets(char mode, bool plus, const QString &parameter);
+ void updateQuickButtons(const QStringList &newButtonList);
+
+ /// Get the contents of the input line.
+ virtual QString getTextInLine();
+ /// Sounds suspiciously like a destructor..
+ virtual bool closeYourself(bool askForConfirmation=true);
+
+ bool autoJoin();
+
+ ChannelNickList getSelectedChannelNicks();
+ ///TODO: this looks like a half-arsed overload.
+ QStringList getSelectedNickList();
+
+ NickListView* getNickListView() const { return nicknameListView; }
+
+ Konversation::ChannelSettings channelSettings() const;
+
+ signals:
+ void sendFile();
+
+ public slots:
+ void updateAppearance();
+ void channelTextEntered();
+ void channelPassthroughCommand();
+ void sendChannelText(const QString& line);
+ void showOptionsDialog();
+ void showQuickButtons(bool show);
+ void showModeButtons(bool show);
+
+ void appendInputText(const QString& s, bool fromCursor);
+ virtual void indicateAway(bool show);
+ void showTopic(bool show);
+ void showNicknameBox(bool show);
+ void showNicknameList(bool show);
+
+ void setAutoJoin(bool autojoin);
+
+ void connectionStateChanged(Server*, Konversation::ConnectionState);
+
+ protected slots:
+ void completeNick(); ///< I guess this is a GUI function, might be nice to have at DCOP level though --argonel
+ void endCompleteNick();
+ void filesDropped(QDropEvent* e);
+ void quickButtonClicked(const QString& definition);
+ void modeButtonClicked(int id,bool on);
+ void channelLimitChanged();
+
+ void popupChannelCommand(int id); ///< Connected to IRCView::popupCommand()
+ void popupCommand(int id); ///< Connected to NickListView::popupCommand()
+ void doubleClickCommand(QListViewItem*); ///< Connected to NickListView::doubleClicked()
+ // Dialogs
+ void changeNickname(const QString& newNickname);
+
+ void textPasted(const QString& text); ///< connected to IRCInput::textPasted() - used to handle large/multiline pastings
+
+ void sendFileMenu(); ///< connected to IRCInput::sendFile()
+ void nicknameComboboxChanged();
+ /// Enable/disable the mode buttons depending on whether you are op or not.
+ void refreshModeButtons();
+
+//only the GUI cares about sorted nicklists
+ ///Request a delayed nicklist sorting
+ void requestNickListSort();
+ ///Sort the nicklist
+ void sortNickList();
+
+ protected:
+ void showEvent(QShowEvent* event);
+ void syncSplitters();
+ /// Called from ChatWindow adjustFocus
+ virtual void childAdjustFocus();
+ /// Close the channel then come back in
+ void cycleChannel(); ///< TODO this is definately implemented and hooked incorrectly.
+
+ bool channelCommand;///< True if nick context menu is executed from IRCView
+
+ // to take care of redraw problem if hidden
+ bool quickButtonsChanged;
+ bool quickButtonsState;
+ bool modeButtonsChanged;
+ bool modeButtonsState;
+ bool awayChanged;
+ bool awayState;
+ bool splittersInitialized;
+ bool topicSplitterHidden;
+ bool channelSplitterHidden;
+
+ unsigned int completionPosition;
+
+ QSplitter* m_horizSplitter;
+ QSplitter* m_vertSplitter;
+ QWidget* topicWidget;
+ QToolButton* m_topicButton;
+ Konversation::TopicLabel* topicLine;
+
+ //TODO: abstract these
+ QHBox* modeBox;
+ ModeButton* modeT;
+ ModeButton* modeN;
+ ModeButton* modeS;
+ ModeButton* modeI;
+ ModeButton* modeP;
+ ModeButton* modeM;
+ ModeButton* modeK;
+ ModeButton* modeL;
+
+ KLineEdit* limit; //TODO: this GUI element is the only storage for the mode
+
+ NickListView* nicknameListView;
+ QHBox* commandLineBox;
+ QVBox* nickListButtons;
+ QGrid* buttonsGrid;
+ QComboBox* nicknameCombobox;
+ QString oldNick; ///< GUI
+ QLabel* awayLabel;
+ QLabel* blowfishLabel;
+ IRCInput* channelInput;
+
+ NickChangeDialog* nickChangeDialog;
+ QPtrList<QuickButton> buttonList;
+
+//Members from here to end are not GUI
+ bool m_joined;
+ NickList nicknameList;
+ QTimer userhostTimer;
+
+ QStringList m_topicHistory;
+ QStringList m_BanList;
+ bool topicAuthorUnknown; ///< Stores whether the "<author>" bit is there or not.
+
+ bool m_firstAutoWhoDone;
+ QTimer m_whoTimer; ///< For continuous auto /WHO
+ QTimer m_fadeActivityTimer; ///< For the smoothing function used in activity sorting
+
+ QValueList<QStringList> m_pendingChannelNickLists;
+ int m_opsToAdd;
+ uint m_currentIndex;
+
+ QTimer* m_processingTimer;
+ QTimer* m_delayedSortTimer;
+
+ QStringList m_modeList;
+ ChannelNickPtr m_ownChannelNick;
+
+ bool pendingNicks; ///< are there still nicks to be added by /names reply?
+ int nicks; ///< How many nicks on the channel
+ int ops; ///< How many ops on the channel
+
+ Konversation::ChannelOptionsDialog *m_optionsDialog;
+};
+#endif
diff --git a/konversation/src/channeldialog.cpp b/konversation/src/channeldialog.cpp
new file mode 100644
index 0000000..2c07d5f
--- /dev/null
+++ b/konversation/src/channeldialog.cpp
@@ -0,0 +1,82 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#include "channeldialog.h"
+#include "servergroupsettings.h"
+
+#include <qlayout.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+
+namespace Konversation
+{
+
+ ChannelDialog::ChannelDialog(const QString& title, QWidget *parent, const char *name)
+ : KDialogBase(Plain, title, Ok|Cancel, Ok, parent, name)
+ {
+ QFrame* mainWidget = plainPage();
+ QGridLayout* mainLayout = new QGridLayout(mainWidget, 1, 2, 0, spacingHint());
+
+ QLabel* channelLbl = new QLabel(i18n("C&hannel:"), mainWidget);
+ m_channelEdit = new QLineEdit(mainWidget);
+ m_channelEdit->setMaxLength(50);
+ channelLbl->setBuddy(m_channelEdit);
+
+ QLabel* passwordLbl = new QLabel(i18n("Pass&word:"), mainWidget);
+ m_passwordEdit = new QLineEdit(mainWidget);
+ m_passwordEdit->setEchoMode(QLineEdit::Password);
+ passwordLbl->setBuddy(m_passwordEdit);
+
+ mainLayout->addWidget(channelLbl, 0, 0);
+ mainLayout->addWidget(m_channelEdit, 0, 1);
+ mainLayout->addWidget(passwordLbl, 1, 0);
+ mainLayout->addWidget(m_passwordEdit, 1, 1);
+
+ m_channelEdit->setFocus();
+ }
+
+ ChannelDialog::~ChannelDialog()
+ {
+ }
+
+ void ChannelDialog::setChannelSettings(const ChannelSettings& channel)
+ {
+ m_channelEdit->setText(channel.name());
+ m_passwordEdit->setText(channel.password());
+ }
+
+ ChannelSettings ChannelDialog::channelSettings()
+ {
+ ChannelSettings channel;
+ channel.setName(m_channelEdit->text());
+ channel.setPassword(m_passwordEdit->text());
+
+ return channel;
+ }
+
+ void ChannelDialog::slotOk()
+ {
+ if (m_channelEdit->text().isEmpty())
+ {
+ KMessageBox::error(this, i18n("The channel name is required."));
+ }
+ else
+ {
+ accept();
+ }
+ }
+}
+
+#include "channeldialog.moc"
diff --git a/konversation/src/channeldialog.h b/konversation/src/channeldialog.h
new file mode 100644
index 0000000..7af0195
--- /dev/null
+++ b/konversation/src/channeldialog.h
@@ -0,0 +1,46 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#ifndef KONVERSATIONCHANNELDIALOG_H
+#define KONVERSATIONCHANNELDIALOG_H
+
+#include <kdialogbase.h>
+
+
+class QLineEdit;
+
+namespace Konversation
+{
+
+ class ChannelSettings;
+
+ class ChannelDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+ explicit ChannelDialog(const QString& title, QWidget *parent = 0, const char *name = 0);
+ ~ChannelDialog();
+
+ void setChannelSettings(const ChannelSettings& channel);
+ ChannelSettings channelSettings();
+
+ protected slots:
+ void slotOk();
+
+ private:
+ QLineEdit* m_channelEdit;
+ QLineEdit* m_passwordEdit;
+
+ };
+
+}
+#endif
diff --git a/konversation/src/channellistpanel.cpp b/konversation/src/channellistpanel.cpp
new file mode 100644
index 0000000..3b20d57
--- /dev/null
+++ b/konversation/src/channellistpanel.cpp
@@ -0,0 +1,597 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Shows the list of channels
+ begin: Die Apr 29 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "channellistpanel.h"
+#include "channel.h"
+#include "channellistviewitem.h"
+#include "server.h"
+#include "common.h"
+
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qgrid.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <qpushbutton.h>
+#include <qhgroupbox.h>
+#include <qregexp.h>
+#include <qcheckbox.h>
+#include <qtimer.h>
+#include <qwhatsthis.h>
+
+#include <krun.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kdeversion.h>
+
+
+ChannelListPanel::ChannelListPanel(QWidget* parent) : ChatWindow(parent)
+{
+ setType(ChatWindow::ChannelList);
+ setName(i18n("Channel List"));
+
+ m_oldSortColumn = 0;
+
+ setNumChannels(0);
+ setNumUsers(0);
+ setVisibleChannels(0);
+ setVisibleUsers(0);
+
+ setMinUsers(0);
+ setMaxUsers(0);
+
+ setChannelTarget(true);
+ setTopicTarget(false);
+ setRegExp(false);
+
+ filterTextChanged(QString());
+
+ QHGroupBox* filterGroup=new QHGroupBox(i18n("Filter Settings"),this);
+ QGrid* mainGrid=new QGrid(2,Qt::Vertical,filterGroup);
+ mainGrid->setSpacing(spacing());
+
+ QLabel* minLabel=new QLabel(i18n("Minimum users:"),mainGrid);
+ QLabel* maxLabel=new QLabel(i18n("Maximum users:"),mainGrid);
+ QSpinBox* minUsersSpin=new QSpinBox(0, 9999, 1, mainGrid,"min_users_spin");
+ QWhatsThis::add(minUsersSpin, i18n("You can limit the channel list to those channels with a minimum number of users here. Choosing 0 disables this criterion."));
+ QSpinBox* maxUsersSpin=new QSpinBox(0, 9999, 1, mainGrid,"max_users_spin");
+ QWhatsThis::add(maxUsersSpin, i18n("You can limit the channel list to those channels with a maximum number of users here. Choosing 0 disables this criterion."));
+ minUsersSpin->setValue(getMinUsers());
+ maxUsersSpin->setValue(getMaxUsers());
+ minLabel->setBuddy(minUsersSpin);
+ maxLabel->setBuddy(maxUsersSpin);
+
+ QLabel* patternLabel=new QLabel(i18n("Filter pattern:"),mainGrid);
+ new QLabel(i18n("Filter target:"),mainGrid);
+
+ filterInput=new KLineEdit(mainGrid,"channel_list_filter_input");
+ QWhatsThis::add(filterInput, i18n("Enter a filter string here."));
+ filterInput->setText(getFilterText());
+
+ patternLabel->setBuddy(filterInput);
+
+ QHBox* targetBox=new QHBox(mainGrid);
+ targetBox->setSpacing(spacing());
+
+ channelFilter=new QCheckBox(i18n("Channel"),targetBox,"filter_target_channel_check");
+ topicFilter=new QCheckBox(i18n("Topic"),targetBox,"filter_target_topic_check");
+ regexpCheck=new QCheckBox(i18n("Regular expression"),targetBox,"regexp_check");
+ applyFilter=new QPushButton(i18n("Apply Filter"),targetBox,"apply_filter_button");
+ QWhatsThis::add(applyFilter, i18n("Click here to retrieve the list of channels from the server and apply the filter."));
+
+ channelFilter->setChecked(getChannelTarget());
+ topicFilter->setChecked(getTopicTarget());
+ regexpCheck->setChecked(getRegExp());
+
+ targetBox->setStretchFactor(topicFilter,10);
+
+ channelListView=new KListView(this,"channel_list_view");
+ QWhatsThis::add(channelListView, i18n("The filtered list of channels is displayed here. Notice that if you do not use regular expressions, Konversation lists any channel whose name contains the filter string you entered. The channel name does not have to start with the string you entered.\n\nSelect a channel you want to join by clicking on it. Right click on the channel to get a list of all web addresses mentioned in the channel's topic."));
+ channelListView->addColumn(i18n("Channel Name"));
+ channelListView->addColumn(i18n("Users"));
+ channelListView->addColumn(i18n("Channel Topic"));
+ channelListView->setAllColumnsShowFocus(true);
+ channelListView->setResizeMode( KListView::LastColumn );
+ channelListView->setSortColumn(-1); //Disable sorting
+
+ QHBox* statsBox=new QHBox(this);
+ statsBox->setSpacing(spacing());
+
+ QLabel* channelsLabel=new QLabel(QString(),statsBox);
+ QLabel* usersLabel=new QLabel(QString(),statsBox);
+
+ statsBox->setStretchFactor(usersLabel,10);
+
+ QHBox* actionBox=new QHBox(this);
+ actionBox->setSpacing(spacing());
+
+ refreshListButton=new QPushButton(i18n("Refresh List"),actionBox,"refresh_list_button");
+ QPushButton* saveListButton=new QPushButton(i18n("Save List..."),actionBox,"save_list_button");
+ joinChannelButton=new QPushButton(i18n("Join Channel"),actionBox,"join_channel_button");
+ QWhatsThis::add(joinChannelButton, i18n("Click here to join the channel. A new tab is created for the channel."));
+
+ connect(&updateTimer,SIGNAL (timeout()),this,SLOT (updateDisplay()));
+
+ // double click on channel entry joins the channel
+ connect(channelListView,SIGNAL (doubleClicked(QListViewItem*)),
+ this,SLOT (joinChannelClicked()) );
+
+ connect(channelListView,SIGNAL (contextMenu (KListView*, QListViewItem*, const QPoint&) ),
+ this, SLOT (contextMenu (KListView*, QListViewItem*, const QPoint&)) );
+
+ connect(minUsersSpin,SIGNAL (valueChanged(int)),this,SLOT(setMinUsers(int)) );
+ connect(maxUsersSpin,SIGNAL (valueChanged(int)),this,SLOT(setMaxUsers(int)) );
+ connect(this,SIGNAL (adjustMinValue(int)),minUsersSpin,SLOT (setValue(int)) );
+ connect(this,SIGNAL (adjustMaxValue(int)),maxUsersSpin,SLOT (setValue(int)) );
+
+ connect(filterInput,SIGNAL (textChanged(const QString&)),this,SLOT (filterTextChanged(const QString&)) );
+ connect(filterInput,SIGNAL (returnPressed()),this,SLOT (applyFilterClicked()) );
+
+ connect(channelFilter,SIGNAL (clicked()),this,SLOT (channelTargetClicked()) );
+ connect(topicFilter,SIGNAL (clicked()),this,SLOT (topicTargetClicked()) );
+ connect(regexpCheck,SIGNAL (clicked()),this,SLOT (regExpClicked()) );
+
+ connect(applyFilter,SIGNAL (clicked()),this,SLOT (applyFilterClicked()) );
+
+ connect(refreshListButton,SIGNAL (clicked()),this,SLOT (refreshList()) );
+ connect(saveListButton,SIGNAL (clicked()),this,SLOT (saveList()) );
+ connect(joinChannelButton,SIGNAL (clicked()),this,SLOT (joinChannelClicked()) );
+
+ connect(this,SIGNAL (updateNumUsers(const QString&)),usersLabel,SLOT (setText(const QString&)) );
+ connect(this,SIGNAL (updateNumChannels(const QString&)),channelsLabel,SLOT (setText(const QString&)) );
+
+ updateUsersChannels();
+}
+
+ChannelListPanel::~ChannelListPanel()
+{
+}
+
+void ChannelListPanel::refreshList()
+{
+ channelListView->clear();
+
+ setNumChannels(0);
+ setNumUsers(0);
+ setVisibleChannels(0);
+ setVisibleUsers(0);
+
+ updateUsersChannels();
+
+ /* No good idea: If the server is "temporary loaded" they stay disabled :-(
+ applyFilter->setEnabled(false);
+ refreshListButton->setEnabled(false); */
+
+ emit refreshChannelList();
+}
+
+void ChannelListPanel::saveList()
+{
+ // Ask user for file name
+ QString fileName=KFileDialog::getSaveFileName(
+ QString(),
+ QString(),
+ this,
+ i18n("Save Channel List"));
+
+ if(!fileName.isEmpty())
+ {
+ // first find the longest channel name and nick number for clean table layouting
+ unsigned int maxChannelWidth=0;
+ unsigned int maxNicksWidth=0;
+
+ QListViewItem* item = channelListView->firstChild();
+ while(item)
+ {
+ if(item->isVisible())
+ {
+ if(item->text(0).length()>maxChannelWidth)
+ {
+ maxChannelWidth = item->text(0).length();
+ }
+
+ if(item->text(1).length()>maxNicksWidth)
+ {
+ maxNicksWidth = item->text(1).length();
+ }
+ }
+
+ item = item->nextSibling();
+ }
+
+ // now save the list to disk
+ QFile listFile(fileName);
+ listFile.open(IO_WriteOnly);
+ // wrap the file into a stream
+ QTextStream stream(&listFile);
+
+ QString header(i18n("Konversation Channel List: %1 - %2\n\n")
+ .arg(m_server->getServerName())
+ .arg(QDateTime::currentDateTime().toString()));
+
+ // send header to stream
+ stream << header;
+
+ item = channelListView->firstChild();
+
+ while(item)
+ {
+ if(item->isVisible())
+ {
+ QString channelName;
+ channelName.fill(' ',maxChannelWidth);
+ channelName.replace(0,item->text(0).length(),item->text(0));
+
+ QString nicksPad;
+ nicksPad.fill(' ',maxNicksWidth);
+ QString nicksNum(nicksPad+item->text(1));
+ nicksNum=nicksNum.right(maxNicksWidth);
+
+ QString line(channelName+' '+nicksNum+' '+item->text(2)+'\n');
+
+ // send final line to stream
+ stream << line;
+ }
+
+ item = item->nextSibling();
+ }
+
+ listFile.close();
+ }
+}
+
+void ChannelListPanel::joinChannelClicked()
+{
+ QListViewItem* item=channelListView->selectedItem();
+ if(item)
+ {
+ emit joinChannel(item->text(0));
+ }
+}
+
+void ChannelListPanel::addToChannelList(const QString& channel,int users,const QString& topic)
+{
+ pendingChannels.append(channel + ' ' + QString::number(users)
+ + ' ' + Konversation::removeIrcMarkup(topic));
+
+ // set internal numbers of channels and users, display will be updated by a timer
+ setNumChannels(getNumChannels()+1);
+ setNumUsers(getNumUsers()+users);
+
+ if(!updateTimer.isActive())
+ {
+ updateTimer.start(10);
+
+ if(channelListView->sortColumn() != -1)
+ m_oldSortColumn = channelListView->sortColumn();
+
+ channelListView->setSortColumn(-1); //Disable sorting
+ }
+}
+
+void ChannelListPanel::updateDisplay()
+{
+ if(!pendingChannels.isEmpty())
+ {
+ // fetch next channel line
+ QString channelLine = pendingChannels.first();
+ // split it up into the single parts we need
+ QString channel = channelLine.section(' ',0,0);
+ QString users = channelLine.section(' ',1,1);
+ QString topic = channelLine.section(' ',2);
+ ChannelListViewItem* item = new ChannelListViewItem(channelListView, channel, users, topic);
+ applyFilterToItem(item);
+ pendingChannels.pop_front();
+ }
+
+ if(pendingChannels.isEmpty())
+ {
+ updateTimer.stop();
+ updateUsersChannels();
+ channelListView->setSortColumn(m_oldSortColumn); //Disable sorting
+ applyFilter->setEnabled(true);
+ refreshListButton->setEnabled(true);
+ }
+}
+
+void ChannelListPanel::filterTextChanged(const QString& newText)
+{
+ filterText=newText;
+}
+
+int ChannelListPanel::getNumChannels()
+{
+ return numChannels;
+}
+
+int ChannelListPanel::getNumUsers()
+{
+ return numUsers;
+}
+
+void ChannelListPanel::setNumChannels(int num)
+{
+ numChannels=num;
+}
+
+void ChannelListPanel::setNumUsers(int num)
+{
+ numUsers=num;
+}
+
+int ChannelListPanel::getVisibleChannels()
+{
+ return visibleChannels;
+}
+
+int ChannelListPanel::getVisibleUsers()
+{
+ return visibleUsers;
+}
+
+void ChannelListPanel::setVisibleChannels(int num)
+{
+ visibleChannels=num;
+}
+
+void ChannelListPanel::setVisibleUsers(int num)
+{
+ visibleUsers=num;
+}
+
+int ChannelListPanel::getMinUsers()
+{
+ return minUsers;
+}
+
+int ChannelListPanel::getMaxUsers()
+{
+ return maxUsers;
+}
+
+bool ChannelListPanel::getChannelTarget()
+{
+ return channelTarget;
+}
+
+bool ChannelListPanel::getTopicTarget()
+{
+ return topicTarget;
+}
+
+bool ChannelListPanel::getRegExp()
+{
+ return regExp;
+}
+
+const QString& ChannelListPanel::getFilterText()
+{
+ return filterText;
+}
+
+void ChannelListPanel::setMinUsers(int num)
+{
+ minUsers=num;
+}
+
+void ChannelListPanel::setMaxUsers(int num)
+{
+ maxUsers=num;
+}
+
+void ChannelListPanel::setChannelTarget(bool state)
+{
+ channelTarget=state;
+}
+
+void ChannelListPanel::setTopicTarget(bool state)
+{
+ topicTarget=state;
+}
+
+void ChannelListPanel::setRegExp(bool state)
+{
+ regExp=state;
+}
+
+void ChannelListPanel::channelTargetClicked()
+{
+ setChannelTarget(channelFilter->state()==2);
+}
+
+void ChannelListPanel::topicTargetClicked()
+{
+ setTopicTarget(topicFilter->state()==2);
+}
+
+void ChannelListPanel::regExpClicked()
+{
+ setRegExp(regexpCheck->state()==2);
+}
+
+void ChannelListPanel::applyFilterToItem(QListViewItem* item)
+{
+ bool visible=true;
+
+ if(getMinUsers() || getMaxUsers())
+ {
+ if(item->text(1).toInt()<getMinUsers() || (getMaxUsers()>=getMinUsers() &&
+ item->text(1).toInt()>getMaxUsers()))
+ visible=false;
+ }
+
+ if(!getFilterText().isEmpty())
+ {
+ if(getChannelTarget())
+ {
+ if(item->text(0).find(QRegExp(getFilterText(),false,!getRegExp()))==-1) visible=false;
+ }
+
+ if(getTopicTarget())
+ {
+ if(item->text(2).find(QRegExp(getFilterText(),false,!getRegExp()))==-1) visible=false;
+ }
+ }
+
+ item->setVisible(visible);
+ if(visible)
+ {
+ setVisibleUsers(getVisibleUsers()+item->text(1).toInt());
+ setVisibleChannels(getVisibleChannels()+1);
+ }
+}
+
+void ChannelListPanel::applyFilterClicked()
+{
+ if(!getNumChannels())
+ {
+ refreshList();
+ return;
+ }
+ else
+ {
+ QListViewItem* item = channelListView->firstChild();
+
+ setVisibleChannels(0);
+ setVisibleUsers(0);
+
+ while(item)
+ {
+ applyFilterToItem(item);
+ item = item->nextSibling();
+ }
+
+ updateUsersChannels();
+ }
+}
+
+void ChannelListPanel::updateUsersChannels()
+{
+ emit updateNumChannels(i18n("Channels: %1 (%2 shown)").arg(getNumChannels()).arg(getVisibleChannels()));
+ emit updateNumUsers(i18n("Non-unique users: %1 (%2 shown)").arg(getNumUsers()).arg(getVisibleUsers()));
+}
+
+bool ChannelListPanel::closeYourself()
+{
+ // make the server delete us so server can reset the pointer to us
+ m_server->closeChannelListPanel();
+ return true;
+}
+
+void ChannelListPanel::childAdjustFocus()
+{
+}
+
+void ChannelListPanel::contextMenu (KListView* /* l */, QListViewItem* i, const QPoint& p)
+{
+ if(!i) return;
+
+ KPopupMenu* showURLmenu = new KPopupMenu(this);
+ showURLmenu->insertTitle( i18n("Open URL") );
+ QString filteredLine(i->text(2));
+
+ QRegExp pattern("((http://|https://|ftp://|nntp://|news://|gopher://|www\\.|ftp\\.)"
+ // IP Address
+ "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}|"
+ // Decimal IP address
+ "[0-9]{1,12}|"
+ // Standard host name
+ "[a-z0-9][\\.@%a-z0-9_-]+\\.[a-z]{2,}"
+ // Port number, path to document
+ ")(:[0-9]{1,5})?(/[^)>\"'\\s]*)?|"
+ // eDonkey2000 links need special treatment
+ "ed2k://\\|([^|]+\\|){4})");
+
+ pattern.setCaseSensitive(false);
+
+ int pos=0;
+ while(static_cast<unsigned int>(pos) < filteredLine.length())
+ {
+ if(pattern.search(filteredLine,pos)!=-1)
+ {
+ // Remember where we found the url
+ pos=pattern.pos();
+
+ // Extract url
+ QString url=pattern.capturedTexts()[0];
+ QString href(url);
+
+ // clean up href for browser
+ if(href.startsWith("www.")) href="http://"+href;
+ else if(href.startsWith("ftp.")) href="ftp://"+href;
+
+ // Replace all spaces with %20 in href
+ href.replace(' ', "%20");
+ href.replace('&', "&&");
+
+ // next search begins right after the link
+ pos+=url.length();
+
+ // tell the program that we have found a new url
+ showURLmenu->insertItem(href);
+ }
+ else
+ {
+ pos++;
+ }
+ }
+
+ if (showURLmenu->count()==1)
+ {
+ showURLmenu->insertItem(i18n("<<No URL found>>"),5);
+ showURLmenu->setItemEnabled(5,false);
+ }
+
+ int selected = showURLmenu->exec(p);
+ if (selected!=-1)
+ {
+ QMenuItem* item = showURLmenu->findItem( selected );
+ new KRun(KURL(item->text().replace("&&","&")));
+ }
+
+ delete showURLmenu;
+}
+
+void ChannelListPanel::appendInputText(const QString& text, bool fromCursor)
+{
+ Q_UNUSED(fromCursor);
+ filterInput->setText(filterInput->text() + text);
+}
+
+//Used to disable functions when not connected
+void ChannelListPanel::serverOnline(bool online)
+{
+ refreshListButton->setEnabled(online);
+ applyFilter->setEnabled(online);
+ joinChannelButton->setEnabled(online);
+}
+
+void ChannelListPanel::emitUpdateInfo()
+{
+ QString info;
+ info = i18n("Channel List for %1").arg(m_server->getDisplayName());
+ emit updateInfo(info);
+}
+
+void ChannelListPanel::setFilter(const QString& filter)
+{
+ filterInput->setText(filter);
+}
+
+#include "channellistpanel.moc"
diff --git a/konversation/src/channellistpanel.h b/konversation/src/channellistpanel.h
new file mode 100644
index 0000000..fa51ddf
--- /dev/null
+++ b/konversation/src/channellistpanel.h
@@ -0,0 +1,147 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Shows the list of channels
+ begin: Die Apr 29 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef CHANNELLISTPANEL_H
+#define CHANNELLISTPANEL_H
+
+#include "chatwindow.h"
+
+#include <qtimer.h>
+
+
+class QCheckBox;
+class QStringList;
+class QTimer;
+class QListView;
+class QListViewItem;
+class QPushButton;
+
+class KListView;
+class KLineEdit;
+class ChannelListViewItem;
+
+class ChannelListPanel : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit ChannelListPanel(QWidget* parent);
+ ~ChannelListPanel();
+
+ virtual bool closeYourself();
+ virtual void emitUpdateInfo();
+
+ signals:
+ void refreshChannelList();
+ void joinChannel(const QString& channelName);
+ void adjustMinValue(int num);
+ void adjustMaxValue(int num);
+ void updateNumUsers(const QString& num);
+ void updateNumChannels(const QString& num);
+
+ public slots:
+ void addToChannelList(const QString& channel,int users,const QString& topic);
+
+ virtual void appendInputText(const QString&, bool fromCursor);
+ void setFilter(const QString& filter);
+
+ void applyFilterClicked();
+
+ protected slots:
+ void refreshList();
+ void updateDisplay(); // will be called by a timer to update regularly
+ void saveList();
+ void joinChannelClicked();
+
+ void setMinUsers(int num);
+ void setMaxUsers(int num);
+
+ void filterTextChanged(const QString& newText);
+ void channelTargetClicked();
+ void topicTargetClicked();
+ void regExpClicked();
+
+ void contextMenu (KListView* l, QListViewItem* i, const QPoint& p);
+
+ //Used to disable functions when not connected
+ virtual void serverOnline(bool online);
+
+ protected:
+
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ virtual bool isInsertCharacterSupported() { return true; }
+
+ int getNumChannels();
+ int getNumUsers();
+ int getVisibleChannels();
+ int getVisibleUsers();
+
+ void setNumChannels(int num);
+ void setNumUsers(int num);
+ void setVisibleChannels(int num);
+ void setVisibleUsers(int num);
+
+ void setChannelTarget(bool state);
+ bool getChannelTarget();
+
+ void setTopicTarget(bool state);
+ bool getTopicTarget();
+
+ void setRegExp(bool state);
+ bool getRegExp();
+
+ int getMinUsers();
+ int getMaxUsers();
+
+ const QString& getFilterText();
+ void applyFilterToItem(QListViewItem* item);
+
+ void updateUsersChannels();
+
+ int numChannels;
+ int numUsers;
+ int visibleChannels;
+ int visibleUsers;
+
+ int minUsers;
+ int maxUsers;
+
+ bool channelTarget;
+ bool topicTarget;
+
+ bool regExp;
+
+ // store channels to be inserted in ListView here first
+ QStringList pendingChannels;
+ QTimer updateTimer;
+
+ QCheckBox* channelFilter;
+ QCheckBox* topicFilter;
+ QCheckBox* regexpCheck;
+
+ QPushButton* applyFilter;
+ QPushButton* refreshListButton;
+ QPushButton* joinChannelButton;
+
+ KListView* channelListView;
+
+ KLineEdit* filterInput;
+
+ QString filterText;
+
+ int m_oldSortColumn;
+};
+#endif
diff --git a/konversation/src/channellistviewitem.cpp b/konversation/src/channellistviewitem.cpp
new file mode 100644
index 0000000..fad3eab
--- /dev/null
+++ b/konversation/src/channellistviewitem.cpp
@@ -0,0 +1,40 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ item in the channel list
+ begin: Die Apr 29 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "channellistviewitem.h"
+
+
+ChannelListViewItem::ChannelListViewItem(KListView* parent, const QString& channel, const QString& users, const QString& topic)
+: KListViewItem(parent,channel,users,topic)
+{
+}
+
+ChannelListViewItem::~ChannelListViewItem()
+{
+}
+
+int ChannelListViewItem::compare(QListViewItem* item, int col, bool ascending) const
+{
+ if(col==1)
+ {
+ bool ok;
+ int i=text(col).toInt(&ok);
+ if(ok)
+ {
+ int j=item->text(col).toInt(&ok);
+ if(ok) return (i<j) ? -1 : (i>j) ? 1 : 0;
+ }
+ }
+ return KListViewItem::compare(item,col,ascending);
+}
diff --git a/konversation/src/channellistviewitem.h b/konversation/src/channellistviewitem.h
new file mode 100644
index 0000000..28d9437
--- /dev/null
+++ b/konversation/src/channellistviewitem.h
@@ -0,0 +1,31 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ item in the channel list
+ begin: Die Apr 29 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef CHANNELLISTVIEWITEM_H
+#define CHANNELLISTVIEWITEM_H
+
+#include <qstring.h>
+
+#include <klistview.h>
+
+
+class ChannelListViewItem : public KListViewItem
+{
+ public:
+ ChannelListViewItem(KListView* parent, const QString& channel, const QString& users, const QString& topic);
+ ~ChannelListViewItem();
+
+ int compare(QListViewItem* item, int col, bool ascending) const;
+};
+#endif
diff --git a/konversation/src/channelnick.cpp b/konversation/src/channelnick.cpp
new file mode 100644
index 0000000..8640acd
--- /dev/null
+++ b/konversation/src/channelnick.cpp
@@ -0,0 +1,259 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Wed Aug 04 2004
+ copyright: (C) 2002,2003,2004 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+/* An instance of ChannelNick is made for each nick in each channel.
+ So for a person in multiple channels, they will have one NickInfo, and multiple ChannelNicks.
+ It contains a pointer to the NickInfo, and the mode of that person in the channel.
+*/
+
+#include "channelnick.h"
+#include "channel.h"
+#include "server.h"
+
+#include <klocale.h>
+
+
+ChannelNick::ChannelNick(const NickInfoPtr& nickInfo, const bool& isop, const bool& isadmin,
+ const bool& isowner, const bool& ishalfop, const bool& hasvoice)
+: KShared()
+{
+ this->nickInfo = nickInfo;
+ this->isop = isop;
+ this->isadmin = isadmin;
+ this->isowner = isowner;
+ this->ishalfop = ishalfop;
+ this->hasvoice = hasvoice;
+ m_timeStamp = 0;
+ m_recentActivity = 0;
+}
+
+ChannelNick::~ChannelNick()
+{
+}
+
+bool ChannelNick::isOp() const
+{
+ return isop;
+}
+
+bool ChannelNick::isAdmin() const
+{
+ return isadmin;
+}
+
+bool ChannelNick::isOwner() const
+{
+ return isowner;
+}
+
+bool ChannelNick::isHalfOp() const
+{
+ return ishalfop;
+}
+
+bool ChannelNick::hasVoice() const
+{
+ return hasvoice;
+}
+
+bool ChannelNick::isAnyTypeOfOp() const
+{
+ return isop || isadmin || isowner || ishalfop;
+}
+
+NickInfoPtr ChannelNick::getNickInfo() const
+{
+ return nickInfo;
+}
+
+/** @param mode 'v' to set voice, 'a' to set admin, 'h' to set halfop, 'o' to set op.
+ * @param state what to set the mode to.
+ */
+bool ChannelNick::setMode(char mode, bool state)
+{
+ switch (mode)
+ {
+ case 'q':
+ return setOwner(state);
+ case 'a':
+ return setAdmin(state);
+ case 'o':
+ return setOp(state);
+ case 'h':
+ return setHalfOp(state);
+ case 'v':
+ return setVoice(state);
+ default:
+ kdDebug() << "Mode '" << mode << "' not recognised in setModeForChannelNick";
+ return false;
+ }
+}
+
+/** Used still for passing modes from inputfilter to Server. Should be removed.
+ */
+bool ChannelNick::setMode(int mode)
+{
+ bool voice = mode%2;
+ mode >>= 1;
+ bool halfop = mode %2;
+ mode >>= 1;
+ bool op = mode %2;
+ mode >>= 1;
+ bool owner = mode %2;
+ mode >>= 1;
+ bool admin = mode %2;
+ return setMode(admin, owner, op, halfop, voice);
+}
+
+bool ChannelNick::setMode(bool admin,bool owner,bool op,bool halfop,bool voice)
+{
+ if(isadmin==admin && isowner==owner && isop==op && ishalfop==halfop && hasvoice==voice)
+ return false;
+ isadmin=admin;
+ isowner=owner;
+ isop=op;
+ ishalfop=halfop;
+ hasvoice=voice;
+ nickInfo->getServer()->emitChannelNickChanged(this);
+ emit channelNickChanged();
+ return true;
+}
+
+/** set the voice for the nick, and update
+ * @returns Whether it needed to be changed. False for no change.
+ */
+bool ChannelNick::setVoice(bool state)
+{
+ if(hasvoice==state) return false;
+ hasvoice=state;
+ nickInfo->getServer()->emitChannelNickChanged(this);
+ emit channelNickChanged();
+ return true;
+}
+
+bool ChannelNick::setOwner(bool state)
+{
+ if(isowner==state) return false;
+ isowner=state;
+ nickInfo->getServer()->emitChannelNickChanged(this);
+ emit channelNickChanged();
+ return true;
+}
+
+bool ChannelNick::setAdmin(bool state)
+{
+ if(isadmin==state) return false;
+ isadmin=state;
+ nickInfo->getServer()->emitChannelNickChanged(this);
+ emit channelNickChanged();
+ return true;
+}
+
+bool ChannelNick::setHalfOp(bool state)
+{
+ if(ishalfop==state) return false;
+ ishalfop=state;
+ nickInfo->getServer()->emitChannelNickChanged(this);
+ emit channelNickChanged();
+ return true;
+}
+
+bool ChannelNick::setOp(bool state)
+{
+ if(isop==state) return false;
+ isop=state;
+ nickInfo->getServer()->emitChannelNickChanged(this);
+ emit channelNickChanged();
+ return true;
+}
+
+//Purely provided for convience because they are used so often.
+//Just calls nickInfo->getNickname() etc
+QString ChannelNick::getNickname() const
+{
+ if ( this )
+ return nickInfo->getNickname();
+ else
+ return QString();
+}
+
+QString ChannelNick::getHostmask() const
+{
+ if ( this )
+ return nickInfo->getHostmask();
+ else
+ return QString();
+}
+
+QString ChannelNick::tooltip()
+{
+ // if(addressee.isEmpty()) return QString();
+ KABC::Addressee addressee = nickInfo->getAddressee();
+ QString strTooltip;
+ QTextStream tooltip( &strTooltip, IO_WriteOnly );
+
+ tooltip << "<qt>";
+
+ tooltip << "<table cellspacing=\"0\" cellpadding=\"0\">";
+
+ nickInfo->tooltipTableData(tooltip);
+
+ QStringList modes;
+ if(isOp()) modes << i18n("Operator");
+ if(isAdmin()) modes << i18n("Admin");
+ if(isOwner()) modes << i18n("Owner");
+ if(isHalfOp()) modes << i18n("Half-operator");
+ if(hasVoice()) modes << i18n("Has voice");
+ //Don't show anything if the user is just a normal user
+ //if(modes.empty()) modes << i18n("A normal user");
+ if(!modes.empty())
+ {
+ tooltip << "<tr><td><b>" << i18n("Mode") << ": </b></td><td>" << modes.join(", ") << "</td></tr>";
+ }
+ tooltip << "</table></qt>";
+ //kdDebug() << strTooltip << endl;
+ //if(!dirty) return QString();
+ return strTooltip;
+}
+
+QString ChannelNick::loweredNickname() const
+{
+ return nickInfo->loweredNickname();
+}
+
+uint ChannelNick::timeStamp() const
+{
+ return m_timeStamp;
+}
+
+uint ChannelNick::recentActivity() const
+{
+ return m_recentActivity;
+}
+
+void ChannelNick::moreActive()
+{
+ m_recentActivity++;
+}
+
+void ChannelNick::lessActive()
+{
+ m_recentActivity--;
+}
+
+void ChannelNick::setTimeStamp(uint stamp)
+{
+ m_timeStamp = stamp;
+}
+
+#include "channelnick.moc"
diff --git a/konversation/src/channelnick.h b/konversation/src/channelnick.h
new file mode 100644
index 0000000..a7c5bbc
--- /dev/null
+++ b/konversation/src/channelnick.h
@@ -0,0 +1,95 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ channelnick.h - There is an instance of this for each nick in each channel. So for a person in multiple channels, they will have one NickInfo, and multiple ChannelNicks.
+ begin: Wed Aug 04 2004
+ copyright: (C) 2002,2003,2004 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef CHANNEL_NICK_H
+#define CHANNEL_NICK_H
+
+#include "nickinfo.h"
+
+#include <ksharedptr.h>
+
+
+class ChannelNick : public QObject, public KShared
+{
+ Q_OBJECT
+
+ public:
+ ChannelNick(const NickInfoPtr& nickInfo, const bool& isop, const bool& isadmin,
+ const bool& isowner, const bool& ishalfop, const bool& hasvoice);
+ ~ChannelNick();
+ bool isOp() const;
+ bool isAdmin() const;
+ bool isOwner() const;
+ bool isHalfOp() const;
+
+ /** Return true if the may have any privillages at all
+ * @return true if isOp() || isAdmin() || isOwner() || isHalfOp()
+ */
+ bool isAnyTypeOfOp() const;
+ bool hasVoice() const;
+ uint timeStamp() const;
+ uint recentActivity() const;
+ void moreActive();
+
+ bool setVoice(bool state);
+ bool setOp(bool state);
+ bool setHalfOp(bool state);
+ bool setAdmin(bool state);
+ bool setOwner(bool state);
+ bool setMode(char mode, bool plus);
+ bool setMode(int mode);
+ bool setMode(bool admin,bool owner,bool op,bool halfop,bool voice);
+ void setTimeStamp(uint stamp);
+
+ NickInfoPtr getNickInfo() const;
+ //Purely provided for convenience because they are used so often.
+ //Just calls nickInfo->getNickname() etc
+ QString getNickname() const;
+ QString loweredNickname() const;
+ QString getHostmask() const;
+ QString tooltip();
+ private:
+ NickInfoPtr nickInfo;
+ bool isop;
+ bool isadmin;
+ bool isowner;
+ bool ishalfop;
+ bool hasvoice;
+ uint m_timeStamp;
+ uint m_recentActivity;
+
+ signals:
+ void channelNickChanged();
+
+ public slots:
+ void lessActive();
+};
+
+/** A ChannelNickPtr is a pointer to a ChannelNick. Since it is a KSharedPtr,
+ * the ChannelNick object is automatically destroyed when all references are destroyed.
+ */
+typedef KSharedPtr<ChannelNick> ChannelNickPtr;
+
+/** A ChannelNickMap is a list of ChannelNick pointers, indexed and sorted by
+ * lowercase nickname.
+ */
+typedef QMap<QString,ChannelNickPtr> ChannelNickMap;
+
+typedef QValueList<ChannelNickPtr> ChannelNickList;
+
+/** A ChannelMembershipMap is a list of ChannelNickMap pointers, indexed and
+ * sorted by lowercase channel name.
+ */
+typedef QMap<QString,ChannelNickMap *> ChannelMembershipMap;
+#endif /* CHANNEL_NICK_H */
diff --git a/konversation/src/channeloptionsdialog.cpp b/konversation/src/channeloptionsdialog.cpp
new file mode 100644
index 0000000..e10f950
--- /dev/null
+++ b/konversation/src/channeloptionsdialog.cpp
@@ -0,0 +1,537 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005-2007 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006-2007 Eike Hein <hein@kde.org>
+*/
+
+#include "channeloptionsdialog.h"
+#include "konversationapplication.h"
+#include "channeloptionsui.h"
+#include "channel.h"
+
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qheader.h>
+#include <qtoolbutton.h>
+
+#include <klocale.h>
+#include <klistview.h>
+#include <ktextedit.h>
+#include <klineedit.h>
+#include <knuminput.h>
+#include <klistviewsearchline.h>
+#include <kiconloader.h>
+
+
+namespace Konversation
+{
+
+ ChannelOptionsDialog::ChannelOptionsDialog(Channel *channel)
+ : KDialogBase(channel, "channelOptions", false, i18n("Channel Settings for %1").arg(channel->getName()), Ok|Cancel, Ok)
+ {
+ Q_ASSERT(channel);
+ m_widget = new ChannelOptionsUI(this);
+ setMainWidget(m_widget);
+
+ m_widget->otherModesList->setRenameable(0, false);
+ m_widget->otherModesList->setRenameable(1, true);
+ m_widget->otherModesList->hide();
+
+ // don't allow sorting. most recent topic is always first
+ m_widget->topicHistoryList->setSortColumn(-1);
+ m_widget->clearButton->setIconSet(SmallIconSet("locationbar_erase"));
+ m_widget->banList->setDefaultRenameAction(QListView::Accept);
+ m_widget->banListSearchLine->setListView(m_widget->banList);
+ // hide column where the complete topic will be put in for convenience
+ m_widget->topicHistoryList->hideColumn(2);
+ // do not allow the user to resize the hidden column back into view
+ m_widget->topicHistoryList->header()->setResizeEnabled(false,2);
+
+ m_channel = channel;
+ m_editingTopic = false;
+
+ connect(m_widget->topicHistoryList, SIGNAL(clicked(QListViewItem*)), this, SLOT(topicHistoryItemClicked(QListViewItem*)));
+ connect(m_widget->topicHistoryList, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(topicHistoryItemClicked(QListViewItem*)));
+ connect(m_widget->toggleAdvancedModes, SIGNAL(clicked()), this, SLOT(toggleAdvancedModes()));
+ connect(m_widget->topicEdit, SIGNAL(modificationChanged(bool)), this, SLOT(topicBeingEdited(bool)));
+
+ connect(m_channel, SIGNAL(topicHistoryChanged()), this, SLOT(refreshTopicHistory()));
+
+ connect(m_channel, SIGNAL(modesChanged()), this, SLOT(refreshModes()));
+ connect(m_channel->getOwnChannelNick(), SIGNAL(channelNickChanged()), this, SLOT(refreshEnableModes()));
+
+ connect(this, SIGNAL(cancelClicked()), this, SLOT(cancelClicked()));
+ connect(this, SIGNAL(okClicked()), this, SLOT(changeOptions()));
+ connect(this, SIGNAL(okClicked()), this, SLOT(okClicked()));
+
+ connect(m_channel, SIGNAL(banAdded(const QString&)), this, SLOT(addBan(const QString&)));
+ connect(m_channel, SIGNAL(banRemoved(const QString&)), this, SLOT(removeBan(const QString&)));
+ connect(m_channel, SIGNAL(banListCleared()), m_widget->banList, SLOT(clear()));
+
+ connect(m_widget->addBan, SIGNAL(clicked()), this, SLOT(addBanClicked()));
+ connect(m_widget->removeBan, SIGNAL(clicked()), this, SLOT(removeBanClicked()));
+ connect(m_widget->banList, SIGNAL(itemRenamed (QListViewItem*)), this, SLOT(banEdited(QListViewItem*)));
+ connect(m_widget->banList, SIGNAL(itemRenamed (QListViewItem*, int, const QString&)), this, SLOT(banEdited(QListViewItem*)));
+
+ refreshTopicHistory();
+ refreshBanList();
+ refreshAllowedChannelModes();
+ refreshModes();
+ }
+
+ ChannelOptionsDialog::~ChannelOptionsDialog()
+ {
+ }
+
+ void ChannelOptionsDialog::changeOptions()
+ {
+ QString newTopic = topic(), oldTopic=m_channel->getTopicHistory().first().section(' ', 2);
+
+ if(newTopic != oldTopic)
+ {
+ // Pass a ^A so we can determine if we want to clear the channel topic.
+ if (newTopic.isEmpty())
+ {
+ if (!oldTopic.isEmpty())
+ m_channel->sendChannelText(Preferences::commandChar() + "TOPIC " + m_channel->getName() + " \x01");
+ }
+ else
+ m_channel->sendChannelText(Preferences::commandChar() + "TOPIC " + m_channel->getName() + ' ' + newTopic);
+ }
+
+ QStringList newModeList = modes();
+ QStringList currentModeList = m_channel->getModeList();
+ QStringList rmModes;
+ QStringList addModes;
+ QStringList tmp;
+ QString modeString;
+ bool plus;
+ QString command("MODE %1 %2%3 %4");
+
+ for(QStringList::iterator it = newModeList.begin(); it != newModeList.end(); ++it)
+ {
+ modeString = (*it).mid(1);
+ plus = ((*it)[0] == '+');
+ tmp = currentModeList.grep(QRegExp('^' + modeString));
+
+ if(tmp.isEmpty() && plus)
+ {
+ m_channel->getServer()->queue(command.arg(m_channel->getName()).arg("+").arg(modeString[0]).arg(modeString.mid(1)));
+ }
+ else if(!tmp.isEmpty() && !plus)
+ {
+ //FIXME: Bahamuth requires the key parameter for -k, but ircd breaks on -l with limit number.
+ //Hence two versions of this.
+ if (modeString[0] == 'k')
+ m_channel->getServer()->queue(command.arg(m_channel->getName()).arg("-").arg(modeString[0]).arg(modeString.mid(1)));
+ else
+ m_channel->getServer()->queue(command.arg(m_channel->getName()).arg("-").arg(modeString[0]).arg(""));
+ }
+ }
+ hide();
+ }
+
+ void ChannelOptionsDialog::toggleAdvancedModes()
+ {
+ bool ison = m_widget->toggleAdvancedModes->isOn();
+ m_widget->otherModesList->setShown(ison);
+ if(ison)
+ {
+ m_widget->toggleAdvancedModes->setText(i18n("&Hide Advanced Modes <<"));
+ }
+ else
+ {
+ m_widget->toggleAdvancedModes->setText(i18n("&Show Advanced Modes >>"));
+ }
+ }
+
+ void ChannelOptionsDialog::topicBeingEdited(bool state)
+ {
+ m_editingTopic = state;
+ }
+
+ QString ChannelOptionsDialog::topic()
+ {
+ return m_widget->topicEdit->text().replace("\n"," ");
+ }
+
+ void ChannelOptionsDialog::refreshTopicHistory()
+ {
+ QStringList history = m_channel->getTopicHistory();
+ m_widget->topicHistoryList->clear();
+
+ for(QStringList::const_iterator it = history.fromLast(); it != history.end(); --it)
+ {
+ QDateTime date;
+ date.setTime_t((*it).section(' ', 0 ,0).toUInt());
+ new KListViewItem(m_widget->topicHistoryList, (*it).section(' ', 1, 1), date.toString(Qt::LocalDate), (*it).section(' ', 2));
+ }
+
+ // update topic preview
+ topicHistoryItemClicked(m_widget->topicHistoryList->selectedItem());
+ // don't destroy the user's edit box if they started editing
+ if(!m_editingTopic)
+ m_widget->topicEdit->setText(history.first().section(' ', 2));
+ }
+
+ void ChannelOptionsDialog::topicHistoryItemClicked(QListViewItem* item)
+ {
+ // if they didn't click on anything, item is null
+ if(item)
+ // update topic preview
+ m_widget->topicPreview->setText(item->text(2));
+ else
+ // clear topic preview
+ m_widget->topicPreview->setText("");
+ }
+
+ void ChannelOptionsDialog::refreshEnableModes()
+ {
+ bool enable = m_channel->getOwnChannelNick()->isAnyTypeOfOp();
+ m_widget->otherModesList->setEnabled(enable);
+ m_widget->topicEdit->setReadOnly(!enable && m_widget->topicModeChBox->isChecked());
+
+ m_widget->topicModeChBox->setEnabled(enable);
+ m_widget->messageModeChBox->setEnabled(enable);
+ m_widget->userLimitChBox->setEnabled(enable);
+ m_widget->userLimitEdit->setEnabled(enable);
+ m_widget->inviteModeChBox->setEnabled(enable);
+ m_widget->moderatedModeChBox->setEnabled(enable);
+ m_widget->secretModeChBox->setEnabled(enable);
+ m_widget->keyModeChBox->setEnabled(enable);
+ m_widget->keyModeEdit->setEnabled(enable);
+
+ m_widget->banList->setItemsRenameable(enable);
+ m_widget->addBan->setEnabled(enable);
+ m_widget->removeBan->setEnabled(enable);
+ }
+
+ void ChannelOptionsDialog::refreshAllowedChannelModes()
+ {
+ QString modeString = m_channel->getServer()->allowedChannelModes();
+ // These modes are handled in a special way: ntimslkbeI
+ modeString.remove('t');
+ modeString.remove('n');
+ modeString.remove('l');
+ modeString.remove('i');
+ modeString.remove('m');
+ modeString.remove('s');
+ modeString.remove('k');
+ modeString.remove('b');
+ modeString.remove('e');
+ modeString.remove('I');
+ modeString.remove('O');
+ modeString.remove('o');
+ modeString.remove('v');
+
+ for(unsigned int i = 0; i < modeString.length(); i++)
+ {
+ new QCheckListItem(m_widget->otherModesList, QString(modeString[i]), QCheckListItem::CheckBox);
+ }
+ }
+
+ void ChannelOptionsDialog::refreshModes()
+ {
+ QStringList modes = m_channel->getModeList();
+
+ m_widget->topicModeChBox->setChecked(false);
+ m_widget->messageModeChBox->setChecked(false);
+ m_widget->userLimitChBox->setChecked(false);
+ m_widget->userLimitEdit->setValue(0);
+ m_widget->inviteModeChBox->setChecked(false);
+ m_widget->moderatedModeChBox->setChecked(false);
+ m_widget->secretModeChBox->setChecked(false);
+ m_widget->keyModeChBox->setChecked(false);
+ m_widget->keyModeEdit->setText("");
+
+ QListViewItem* item = m_widget->otherModesList->firstChild();
+
+ while(item)
+ {
+ static_cast<QCheckListItem*>(item)->setOn(false);
+ item = item->nextSibling();
+ }
+
+ char mode;
+
+ for(QStringList::const_iterator it = modes.begin(); it != modes.end(); ++it)
+ {
+ mode = (*it)[0];
+
+ switch(mode)
+ {
+ case 't':
+ m_widget->topicModeChBox->setChecked(true);
+ break;
+ case 'n':
+ m_widget->messageModeChBox->setChecked(true);
+ break;
+ case 'l':
+ m_widget->userLimitChBox->setChecked(true);
+ m_widget->userLimitEdit->setValue((*it).mid(1).toInt());
+ break;
+ case 'i':
+ m_widget->inviteModeChBox->setChecked(true);
+ break;
+ case 'm':
+ m_widget->moderatedModeChBox->setChecked(true);
+ break;
+ case 's':
+ m_widget->secretModeChBox->setChecked(true);
+ break;
+ case 'k':
+ m_widget->keyModeChBox->setChecked(true);
+ m_widget->keyModeEdit->setText((*it).mid(1));
+ break;
+ default:
+ {
+ bool found = false;
+ item = m_widget->otherModesList->firstChild();
+ QString modeString;
+ modeString = mode;
+
+ while(item && !found)
+ {
+ if(item->text(0) == modeString)
+ {
+ found = true;
+ static_cast<QCheckListItem*>(item)->setOn(true);
+ item->setText(1, (*it).mid(1));
+ }
+ else
+ {
+ item = item->nextSibling();
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ refreshEnableModes();
+ }
+
+ QStringList ChannelOptionsDialog::modes()
+ {
+ QStringList modes;
+ QString mode;
+
+ mode = (m_widget->topicModeChBox->isChecked() ? "+" : "-");
+ mode += 't';
+ modes.append(mode);
+ mode = (m_widget->messageModeChBox->isChecked() ? "+" : "-");
+ mode += 'n';
+ modes.append(mode);
+ mode = (m_widget->userLimitChBox->isChecked() ? "+" : "-");
+ mode += 'l' + QString::number( m_widget->userLimitEdit->value() );
+ modes.append(mode);
+ mode = (m_widget->inviteModeChBox->isChecked() ? "+" : "-");
+ mode += 'i';
+ modes.append(mode);
+ mode = (m_widget->moderatedModeChBox->isChecked() ? "+" : "-");
+ mode += 'm';
+ modes.append(mode);
+ mode = (m_widget->secretModeChBox->isChecked() ? "+" : "-");
+ mode += 's';
+ modes.append(mode);
+
+ if (m_widget->keyModeChBox->isChecked() && !m_widget->keyModeEdit->text().isEmpty())
+ {
+ mode = '+';
+ mode += 'k' + m_widget->keyModeEdit->text();
+ modes.append(mode);
+ }
+ else if (!m_widget->keyModeChBox->isChecked())
+ {
+ mode = '-';
+ mode += 'k' + m_widget->keyModeEdit->text();
+ modes.append(mode);
+ }
+
+ QListViewItem* item = m_widget->otherModesList->firstChild();
+
+ while(item)
+ {
+ mode = (static_cast<QCheckListItem*>(item)->isOn() ? "+" : "-");
+ mode += item->text(0) + item->text(1);
+ modes.append(mode);
+ item = item->nextSibling();
+ }
+
+ return modes;
+ }
+
+ // Ban List tab related functions
+
+ void ChannelOptionsDialog::refreshBanList()
+ {
+ QStringList banlist = m_channel->getBanList();
+ m_widget->banList->clear();
+
+ for (QStringList::const_iterator it = banlist.fromLast(); it != banlist.end(); --it)
+ addBan((*it));
+ }
+
+ void ChannelOptionsDialog::addBan(const QString& newban)
+ {
+ new BanListViewItem(m_widget->banList, newban.section(' ', 0, 0), newban.section(' ', 1, 1).section('!', 0, 0), newban.section(' ', 2 ,2).toUInt());
+ }
+
+ void ChannelOptionsDialog::removeBan(const QString& ban)
+ {
+ delete m_widget->banList->findItem(ban, 0);
+ }
+
+ void ChannelOptionsDialog::banEdited(QListViewItem *edited)
+ {
+ if (edited == m_NewBan)
+ {
+ if (!m_NewBan->text(0).isEmpty())
+ {
+ m_channel->getServer()->requestBan(QStringList(m_NewBan->text(0)), m_channel->getName(), QString());
+ }
+
+ // We will delete the item and let the addBan slot handle
+ // readding the item because for some odd reason using
+ // startRename causes further attempts to rename the item
+ // using 2 mouse clicks to fail in odd ways.
+ delete edited;
+
+ return;
+ }
+
+ BanListViewItem *new_edited = dynamic_cast <BanListViewItem*> (edited);
+ if (new_edited == NULL) return; // Should not happen.
+
+ if (new_edited->getOldValue() != new_edited->text(0))
+ {
+ m_channel->getServer()->requestUnban(new_edited->getOldValue(), m_channel->getName());
+
+ if (!new_edited->text(0).isEmpty())
+ {
+ m_channel->getServer()->requestBan(QStringList(new_edited->text(0)), m_channel->getName(), QString());
+ }
+
+ // We delete the existing item because it's possible the server may
+ // Modify the ban causing us not to catch it. If that happens we'll be
+ // stuck with a stale item and a new item with the modified hostmask.
+ delete new_edited;
+ }
+ }
+
+ void ChannelOptionsDialog::addBanClicked()
+ {
+ m_NewBan = new BanListViewItem(m_widget->banList, true);
+
+ m_NewBan->setRenameEnabled(0,true);
+ m_NewBan->startRename(0);
+ }
+
+ void ChannelOptionsDialog::removeBanClicked()
+ {
+ if (m_widget->banList->currentItem())
+ m_channel->getServer()->requestUnban(m_widget->banList->currentItem()->text(0), m_channel->getName());
+ }
+
+ void ChannelOptionsDialog::cancelClicked()
+ {
+ if (m_widget->banList->renameLineEdit()->isShown())
+ {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_Escape, 27, Qt::NoButton);
+
+ KApplication::sendEvent(m_widget->banList->renameLineEdit(), &e);
+ }
+
+ topicBeingEdited(false);
+ hide();
+ }
+
+ void ChannelOptionsDialog::okClicked()
+ {
+ if (m_widget->banList->renameLineEdit()->isShown())
+ {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_Return, 13, Qt::NoButton);
+
+ KApplication::sendEvent(m_widget->banList->renameLineEdit(), &e);
+ }
+ }
+
+ // This is our implementation of BanListViewItem
+
+ BanListViewItem::BanListViewItem(QListView *parent)
+ : KListViewItem(parent)
+ {
+ m_isNewBan = 0;
+ }
+
+ BanListViewItem::BanListViewItem(QListView *parent, bool isNew)
+ : KListViewItem(parent)
+ {
+ m_isNewBan = isNew;
+ }
+
+ BanListViewItem::BanListViewItem (QListView *parent, const QString& label1, const QString& label2,
+ uint timestamp) : KListViewItem(parent, label1, label2)
+ {
+ m_isNewBan = 0;
+ m_timestamp.setTime_t(timestamp);
+ }
+
+ BanListViewItem::BanListViewItem (QListView *parent, bool isNew, const QString& label1, const QString& label2,
+ uint timestamp) : KListViewItem(parent, label1, label2)
+ {
+ m_isNewBan = isNew;
+ m_timestamp.setTime_t(timestamp);
+ }
+
+ QString BanListViewItem::text(int column) const
+ {
+ if (column == 2)
+ return KGlobal::locale()->formatDateTime(m_timestamp, true, true);
+
+ return KListViewItem::text(column);
+ }
+
+ int BanListViewItem::compare(QListViewItem *i, int col, bool ascending) const
+ {
+ if (col == 2)
+ {
+ BanListViewItem* item = static_cast<BanListViewItem*>(i);
+
+ if (m_timestamp == item->timestamp())
+ return 0;
+ else if (m_timestamp < item->timestamp())
+ return -1;
+ else
+ return 1;
+ }
+
+ return KListViewItem::compare(i, col, ascending);
+ }
+
+ void BanListViewItem::startRename( int col )
+ {
+ m_oldValue = text(col);
+
+ KListViewItem::startRename(col);
+ }
+
+ void BanListViewItem::cancelRename( int col )
+ {
+ if (text(col).isEmpty() && m_isNewBan)
+ delete this;
+ else
+ KListViewItem::cancelRename(col);
+ }
+}
+
+#include "channeloptionsdialog.moc"
diff --git a/konversation/src/channeloptionsdialog.h b/konversation/src/channeloptionsdialog.h
new file mode 100644
index 0000000..b947fee
--- /dev/null
+++ b/konversation/src/channeloptionsdialog.h
@@ -0,0 +1,102 @@
+/*
+ This program is free software; you can redistribute it and/or modifydvancedModes
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#ifndef KONVERSATIONCHANNELOPTIONSDIALOG_H
+#define KONVERSATIONCHANNELOPTIONSDIALOG_H
+
+#include "channel.h"
+
+#include <qstringlist.h>
+
+#include <kdialogbase.h>
+#include <klistview.h>
+
+
+namespace Konversation
+{
+ class ChannelOptionsUI;
+
+ class ChannelOptionsDialog : public KDialogBase
+ {
+ Q_OBJECT
+ public:
+ explicit ChannelOptionsDialog(Channel *channel);
+ ~ChannelOptionsDialog();
+
+ QString topic();
+ QStringList modes();
+
+
+ public slots:
+ void refreshTopicHistory();
+ void refreshAllowedChannelModes();
+ void refreshModes();
+ void refreshEnableModes();
+ void toggleAdvancedModes();
+
+ void refreshBanList();
+ void addBan(const QString& newban);
+ void addBanClicked();
+ void removeBan(const QString& ban);
+ void removeBanClicked();
+ void banEdited(QListViewItem *edited);
+
+ void changeOptions();
+
+
+ protected slots:
+ void topicHistoryItemClicked(QListViewItem* item);
+ void topicBeingEdited(bool state);
+
+ void cancelClicked();
+ void okClicked();
+
+
+ protected:
+ bool m_editingTopic;
+ QListViewItem *m_NewBan;
+
+
+ private:
+ ChannelOptionsUI* m_widget;
+ Channel *m_channel;
+ };
+
+
+ // This is needed to overcome two deficiencies in KListViewItem
+ // First there is no signal emitted when a rename is canceled
+ // Second there is no way to get the old value of an item after a rename
+ class BanListViewItem : public KListViewItem
+ {
+ public:
+ explicit BanListViewItem( QListView *parent );
+ BanListViewItem(QListView *parent, bool isNew);
+ BanListViewItem(QListView *parent, const QString& label1, const QString& label2 = QString(), uint timestamp = 0);
+ BanListViewItem (QListView *parent, bool isNew, const QString& label1, const QString& label2 = QString(), uint timestamp = 0);
+
+ QString getOldValue() { return m_oldValue; }
+ QDateTime timestamp() { return m_timestamp; }
+
+ virtual QString text(int column) const;
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+ virtual void startRename(int col);
+
+
+ protected:
+ virtual void cancelRename(int col);
+
+ QString m_oldValue;
+ bool m_isNewBan;
+ QDateTime m_timestamp;
+ };
+
+}
+#endif
diff --git a/konversation/src/channeloptionsui.ui b/konversation/src/channeloptionsui.ui
new file mode 100644
index 0000000..2c2f062
--- /dev/null
+++ b/konversation/src/channeloptionsui.ui
@@ -0,0 +1,559 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Konversation::ChannelOptionsUI</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</comment>
+<author>Peter Simonsson</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Konversation::ChannelOptionsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>502</width>
+ <height>421</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>channelTabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>topicTab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Topi&amp;c</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSplitter">
+ <property name="name">
+ <cstring>splitter4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Nickname</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Date</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Topic</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>topicHistoryList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>Single</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="showSortIndicator">
+ <bool>true</bool>
+ </property>
+ <property name="showToolTips">
+ <bool>false</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KTextEdit">
+ <property name="name">
+ <cstring>topicPreview</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="backgroundOrigin">
+ <enum>WidgetOrigin</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ <widget class="KTextEdit">
+ <property name="name">
+ <cstring>topicEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>2</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>PlainText</enum>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>modesPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Mo&amp;des</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>topicModeChBox</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Topic can only be changed by channel operators</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;The &lt;b&gt;T&lt;/b&gt;opic mode means that only the channel operator can change the topic for the channel.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>messageModeChBox</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;No messages from outside the channel</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;&lt;b&gt;N&lt;/b&gt;o messages from outside means that users that are not in the channel cannot send messages that everybody in the channel can see. Almost all channels have this set to prevent nuisance messages.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>secretModeChBox</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Secret channel, the channel is not listed in the channel list</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;A &lt;b&gt;S&lt;/b&gt;ecret channel will not show up in the channel list, nor will any user be able to see that you are in the channel with the &lt;em&gt;WHOIS&lt;/em&gt; command or anything similar. Only the people that are in the same channel will know that you are in this channel, if this mode is set.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>inviteModeChBox</cstring>
+ </property>
+ <property name="text">
+ <string>Only &amp;invited are allowed to join the channel</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;An &lt;b&gt;I&lt;/b&gt;nvite only channel means that people can only join the channel if they are invited. To invite someone, a channel operator needs to issue the command&lt;em&gt;/invite nick&lt;/em&gt; from within the channel.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>moderatedModeChBox</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Moderated channel, only people with voice can write to the channel</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;A &lt;b&gt;M&lt;/b&gt;oderated channel is one where only operators, half-operators and those with voice can talk.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>keyModeChBox</cstring>
+ </property>
+ <property name="text">
+ <string>Channel &amp;password:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;A &lt;b&gt;P&lt;/b&gt;rotected channel requires users to enter a password in order to join.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="KIntNumInput" row="0" column="1">
+ <property name="name">
+ <cstring>userLimitEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;A channel that has a user &lt;b&gt;L&lt;/b&gt;imit means that only that many users can be in the channel at any one time. Some channels have a bot that sits in the channel and changes this automatically depending on how busy the channel is.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>userLimitChBox</cstring>
+ </property>
+ <property name="text">
+ <string>User &amp;limit:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;A channel that has a user &lt;b&gt;L&lt;/b&gt;imit means that only that many users can be in the channel at any one time. Some channels have a bot that sits in the channel and changes this automatically depending on how busy the channel is.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>211</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>keyModeEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;These control the &lt;em&gt;mode&lt;/em&gt; of the channel. Only an operator can change these.&lt;p&gt;A &lt;b&gt;P&lt;/b&gt;rotected channel requires users to enter a password in order to join.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>toggleAdvancedModes</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;Advanced Modes &gt;&gt;</string>
+ </property>
+ <property name="accel">
+ <string>Alt+A, Backspace, Tab, Backspace</string>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>301</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Mode</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Parameter</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>otherModesList</cstring>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>Single</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ <property name="defaultRenameAction">
+ <enum>Accept</enum>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Ban List</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>removeBan</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Remove Ban</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="3">
+ <property name="name">
+ <cstring>addBan</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Add Ban</string>
+ </property>
+ </widget>
+ <widget class="KListView" row="1" column="0" rowspan="1" colspan="4">
+ <column>
+ <property name="text">
+ <string>Hostmask</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Set By</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Time Set</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>banList</cstring>
+ </property>
+ <property name="showSortIndicator">
+ <bool>true</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="0" column="0">
+ <property name="name">
+ <cstring>clearButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Search:</string>
+ </property>
+ </widget>
+ <widget class="KListViewSearchLine" row="0" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>banListSearchLine</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>keyModeChBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>keyModeEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>userLimitChBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>userLimitEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>clearButton</sender>
+ <signal>clicked()</signal>
+ <receiver>banListSearchLine</receiver>
+ <slot>clear()</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>ktextedit.h</includehint>
+ <includehint>ktextedit.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistviewsearchline.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/chatwindow.cpp b/konversation/src/chatwindow.cpp
new file mode 100644
index 0000000..b3a024b
--- /dev/null
+++ b/konversation/src/chatwindow.cpp
@@ -0,0 +1,526 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "chatwindow.h"
+#include "channel.h"
+#include "ircview.h"
+#include "server.h"
+#include "konversationapplication.h"
+#include "logfilereader.h"
+
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <qtooltip.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kdialog.h>
+#include <kdebug.h>
+#include <kactioncollection.h>
+#include <kaction.h>
+
+
+ChatWindow::ChatWindow(QWidget* parent) : QVBox(parent)
+{
+ setName("ChatWindowObject");
+ setTextView(0);
+ parentWidget=parent;
+ firstLog=true;
+ m_server=0;
+ m_notificationsEnabled = true;
+ m_channelEncodingSupported = false;
+ m_currentTabNotify = Konversation::tnfNone;
+
+ setMargin(margin());
+ setSpacing(spacing());
+
+ // The font size of the KTabWidget container may be inappropriately
+ // small due to the "Tab bar" font size setting.
+ setFont(KGlobalSettings::generalFont());
+}
+
+ChatWindow::~ChatWindow()
+{
+ emit closing(this);
+ m_server=0;
+}
+
+void ChatWindow::updateAppearance()
+{
+ // The font size of the KTabWidget container may be inappropriately
+ // small due to the "Tab bar" font size setting.
+ setFont(KGlobalSettings::generalFont());
+
+ if (textView)
+ {
+ if (Preferences::showIRCViewScrollBar())
+ textView->setVScrollBarMode(QScrollView::AlwaysOn);
+ else
+ textView->setVScrollBarMode(QScrollView::AlwaysOff);
+ }
+}
+
+void ChatWindow::setName(const QString& newName)
+{
+ name=newName;
+ emit nameChanged(this,newName);
+}
+
+QString ChatWindow::getName()
+{
+ return name;
+}
+
+void ChatWindow::setType(WindowType newType)
+{
+ type=newType;
+}
+
+ChatWindow::WindowType ChatWindow::getType()
+{
+ return type;
+}
+
+void ChatWindow::setServer(Server* newServer)
+{
+ if (!newServer)
+ {
+ kdDebug("ChatWindow::setServer(0)!") << endl;
+ }
+ else
+ {
+ m_server=newServer;
+ connect(m_server,SIGNAL (serverOnline(bool)),this,SLOT (serverOnline(bool)) );
+
+ // check if we need to set up the signals
+ if(getType() != ChannelList)
+ {
+ if(textView) textView->setServer(newServer);
+ else kdDebug() << "ChatWindow::setServer(): textView==0!" << endl;
+ }
+
+ emit serverOnline(m_server->isConnected());
+ }
+}
+
+Server* ChatWindow::getServer()
+{
+ return m_server;
+}
+
+void ChatWindow::serverOnline(bool /* state */)
+{
+ //emit online(this,state);
+}
+
+void ChatWindow::setTextView(IRCView* newView)
+{
+ textView = newView;
+
+ if(!textView)
+ {
+ return;
+ }
+
+ if(Preferences::showIRCViewScrollBar())
+ {
+ textView->setVScrollBarMode(QScrollView::Auto);
+ }
+ else
+ {
+ textView->setVScrollBarMode(QScrollView::AlwaysOff);
+ }
+
+ textView->setChatWin(this);
+ connect(textView,SIGNAL(textToLog(const QString&)), this,SLOT(logText(const QString&)));
+ connect(textView,SIGNAL(setStatusBarTempText(const QString&)), this, SIGNAL(setStatusBarTempText(const QString&)));
+ connect(textView,SIGNAL(clearStatusBarTempText()), this, SIGNAL(clearStatusBarTempText()));
+}
+
+void ChatWindow::appendRaw(const QString& message, bool suppressTimestamps)
+{
+ if(!textView) return;
+ textView->appendRaw(message, suppressTimestamps);
+}
+
+void ChatWindow::append(const QString& nickname,const QString& message)
+{
+ if(!textView) return ;
+ textView->append(nickname,message);
+}
+
+void ChatWindow::appendQuery(const QString& nickname,const QString& message, bool inChannel)
+{
+ if(!textView) return ;
+ textView->appendQuery(nickname,message, inChannel);
+}
+
+void ChatWindow::appendAction(const QString& nickname, const QString& message)
+{
+ if(!textView) return;
+
+ if (getType() == Query || getType() == DccChat)
+ textView->appendQueryAction(nickname, message);
+ else
+ textView->appendChannelAction(nickname, message);
+}
+
+void ChatWindow::appendServerMessage(const QString& type,const QString& message, bool parseURL)
+{
+ if(!textView) return ;
+ textView->appendServerMessage(type,message, parseURL);
+}
+
+void ChatWindow::appendCommandMessage(const QString& command,const QString& message, bool important, bool parseURL, bool self)
+{
+ if(!textView) return ;
+ textView->appendCommandMessage(command,message,important, parseURL, self);
+}
+
+void ChatWindow::appendBacklogMessage(const QString& firstColumn,const QString& message)
+{
+ if(!textView) return ;
+ textView->appendBacklogMessage(firstColumn,message);
+}
+
+void ChatWindow::cdIntoLogPath()
+{
+ QDir logPath=QDir::home();
+ // Try to "cd" into the logfile path
+ if(!logPath.cd(Preferences::logfilePath(),true))
+ {
+ // Only create log path if logging is enabled
+ if(log)
+ {
+ // Try to create the logfile path and "cd" into it again
+ logPath.mkdir(Preferences::logfilePath(),true);
+ logPath.cd(Preferences::logfilePath(),true);
+ }
+ }
+
+ // add the logfile name to the path
+ logfile.setName(logPath.path()+'/'+logName);
+}
+
+void ChatWindow::setLogfileName(const QString& name)
+{
+ // Only change name of logfile if the window was new.
+ if(firstLog)
+ {
+ // status panels get special treatment here, since they have no server at the beginning
+ if (getType() == Status || getType() == DccChat)
+ {
+ logName = name + ".log";
+ }
+ else if (m_server)
+ {
+ // make sure that no path delimiters are in the name
+ logName = QString(m_server->getDisplayName().lower()).append('_').append(name).append(".log").replace('/','_');
+ }
+
+ // load backlog to show
+ if(Preferences::showBacklog())
+ {
+ // "cd" into log path or create path, if it's not there
+ cdIntoLogPath();
+ // Show last log lines. This idea was stole ... um ... inspired by PMP :)
+ // Don't do this for the server status windows, though
+ if((getType() != Status) && logfile.open(IO_ReadOnly))
+ {
+ unsigned long filePosition;
+
+ QString backlogLine;
+ QTextStream backlog(&logfile);
+ backlog.setEncoding(QTextStream::UnicodeUTF8);
+
+ QStringList firstColumns;
+ QStringList messages;
+ int offset = 0;
+ unsigned int lastPacketHeadPosition = backlog.device()->size();
+ const unsigned int packetSize = 4096;
+ while(messages.count() < (unsigned int)Preferences::backlogLines() && backlog.device()->size() > packetSize * offset)
+ {
+ QStringList firstColumnsInPacket;
+ QStringList messagesInPacket;
+
+ // packetSize * offset < size <= packetSize * ( offset + 1 )
+
+ // Check if the log is bigger than packetSize * ( offset + 1 )
+ if(backlog.device()->size() > packetSize * ( offset + 1 ))
+ {
+ // Set file pointer to the packet size above the offset
+ backlog.device()->at(backlog.device()->size() - packetSize * ( offset + 1 ));
+ // Skip first line, since it may be incomplete
+ backlog.readLine();
+ }
+ else
+ {
+ // Set file pointer to the head
+ backlog.device()->reset();
+ }
+
+ unsigned int currentPacketHeadPosition = backlog.device()->at();
+
+ // Loop until end of file reached
+ while(!backlog.atEnd() && backlog.device()->at() < lastPacketHeadPosition)
+ {
+ // remember actual file position to check for deadlocks
+ filePosition = backlog.device()->at();
+ backlogLine = backlog.readLine();
+
+ // check for deadlocks
+ if(backlog.device()->at() == filePosition) backlog.device()->at(filePosition + 1);
+
+ // if a tab character is present in the line
+ if(backlogLine.find('\t') != -1)
+ {
+ // extract first column from log
+ QString backlogFirst = backlogLine.left(backlogLine.find('\t'));
+ // cut first column from line
+ backlogLine = backlogLine.mid(backlogLine.find('\t') + 1);
+ // Logfile is in utf8 so we don't need to do encoding stuff here
+ // append backlog with time and first column to text view
+ firstColumnsInPacket << backlogFirst;
+ messagesInPacket << backlogLine;
+ }
+ } // while
+
+ // remember the position not to read the same lines again
+ lastPacketHeadPosition = currentPacketHeadPosition;
+ ++offset;
+
+ firstColumns = firstColumnsInPacket + firstColumns;
+ messages = messagesInPacket + messages;
+ }
+ backlog.unsetDevice();
+ logfile.close();
+
+ // trim
+ int surplus = messages.count() - Preferences::backlogLines();
+ // "surplus" can be a minus value. (when the backlog is too short)
+ if(surplus > 0)
+ {
+ for(int i = 0 ; i < surplus ; ++i)
+ {
+ firstColumns.pop_front();
+ messages.pop_front();
+ }
+ }
+
+ QStringList::Iterator itFirstColumn = firstColumns.begin();
+ QStringList::Iterator itMessage = messages.begin();
+ for( ; itFirstColumn != firstColumns.end() ; ++itFirstColumn, ++itMessage )
+ appendBacklogMessage(*itFirstColumn, *itMessage);
+ }
+ } // if(Preferences::showBacklog())
+ }
+}
+
+void ChatWindow::logText(const QString& text)
+{
+ if(log)
+ {
+ // "cd" into log path or create path, if it's not there
+ cdIntoLogPath();
+
+ if(logfile.open(IO_WriteOnly | IO_Append))
+ {
+ // wrap the file into a stream
+ QTextStream logStream(&logfile);
+ // write log in utf8 to help i18n
+ logStream.setEncoding(QTextStream::UnicodeUTF8);
+
+ if(firstLog)
+ {
+ QString intro(i18n("\n*** Logfile started\n*** on %1\n\n").arg(QDateTime::currentDateTime().toString()));
+ logStream << intro;
+ firstLog=false;
+ }
+
+ QTime time=QTime::currentTime();
+ QString logLine(QString("[%1] [%2] %3\n").arg(QDate::currentDate(Qt::LocalTime).toString()).
+ arg(time.toString("hh:mm:ss")).arg(text));
+
+ logStream << logLine;
+
+ // detach stream from file
+ logStream.unsetDevice();
+
+ // close file
+ logfile.close();
+ }
+ else kdWarning() << "ChatWindow::logText(): open(IO_Append) for " << logfile.name() << " failed!" << endl;
+ }
+}
+
+void ChatWindow::setChannelEncodingSupported(bool enabled)
+{
+ m_channelEncodingSupported = enabled;
+}
+
+bool ChatWindow::isChannelEncodingSupported() const
+{
+ return m_channelEncodingSupported;
+}
+
+int ChatWindow::spacing()
+{
+ if(Preferences::useSpacing())
+ return Preferences::spacing();
+ else
+ return KDialog::spacingHint();
+}
+
+int ChatWindow::margin()
+{
+ if(Preferences::useSpacing())
+ return Preferences::margin();
+ else
+ return 0;
+}
+
+// Accessors
+IRCView* ChatWindow::getTextView() const
+{
+ return textView;
+}
+
+void ChatWindow::setLog(bool activate)
+{
+ log=activate;
+}
+
+// reimplement this in all panels that have user input
+QString ChatWindow::getTextInLine()
+{
+ return QString();
+}
+
+bool ChatWindow::canBeFrontView()
+{
+ return false;
+}
+
+bool ChatWindow::searchView()
+{
+ return false;
+}
+
+// reimplement this in all panels that have user input
+void ChatWindow::indicateAway(bool)
+{
+}
+
+// reimplement this in all panels that have user input
+void ChatWindow::appendInputText(const QString&, bool)
+{
+}
+
+// reimplement this if your window needs special close treatment
+bool ChatWindow::closeYourself(bool /* askForConfirmation */)
+{
+ deleteLater();
+
+ return true;
+}
+
+bool ChatWindow::eventFilter(QObject* watched, QEvent* e)
+{
+ if(e->type() == QEvent::KeyPress)
+ {
+ QKeyEvent* ke = static_cast<QKeyEvent*>(e);
+
+ bool scrollMod = (Preferences::useMultiRowInputBox() ? false : (ke->state() == Qt::ShiftButton));
+
+ if(ke->key() == Qt::Key_Up && scrollMod)
+ {
+ if(textView)
+ {
+ QScrollBar* sbar = textView->verticalScrollBar();
+ sbar->setValue(sbar->value() - sbar->lineStep());
+ }
+
+ return true;
+ }
+ else if(ke->key() == Qt::Key_Down && scrollMod)
+ {
+ if(textView)
+ {
+ QScrollBar* sbar = textView->verticalScrollBar();
+ sbar->setValue(sbar->value() + sbar->lineStep());
+ }
+
+ return true;
+ }
+ else if(ke->key() == Qt::Key_Prior)
+ {
+ if(textView)
+ {
+ QScrollBar* sbar = textView->verticalScrollBar();
+ sbar->setValue(sbar->value() - sbar->pageStep());
+ }
+
+ return true;
+ }
+ else if(ke->key() == Qt::Key_Next)
+ {
+ if(textView)
+ {
+ QScrollBar* sbar = textView->verticalScrollBar();
+ sbar->setValue(sbar->value() + sbar->pageStep());
+ }
+
+ return true;
+ }
+
+ }
+
+ return QVBox::eventFilter(watched, e);
+}
+
+void ChatWindow::adjustFocus()
+{
+ childAdjustFocus();
+}
+
+void ChatWindow::emitUpdateInfo()
+{
+ QString info = getName();
+ emit updateInfo(info);
+}
+
+QColor ChatWindow::highlightColor()
+{
+ return getTextView()->highlightColor();
+}
+
+void ChatWindow::activateTabNotification(Konversation::TabNotifyType type)
+{
+ if (!notificationsEnabled())
+ return;
+
+ if(type > m_currentTabNotify)
+ return;
+
+ m_currentTabNotify = type;
+
+ emit updateTabNotification(this,type);
+}
+
+void ChatWindow::resetTabNotification()
+{
+ m_currentTabNotify = Konversation::tnfNone;
+}
+
+#include "chatwindow.moc"
diff --git a/konversation/src/chatwindow.h b/konversation/src/chatwindow.h
new file mode 100644
index 0000000..55c0efc
--- /dev/null
+++ b/konversation/src/chatwindow.h
@@ -0,0 +1,205 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef CHATWINDOW_H
+#define CHATWINDOW_H
+
+#include "identity.h"
+#include "common.h"
+
+#include <qvbox.h>
+#include <qfile.h>
+
+
+class IRCView;
+class Server;
+class KonversationMainWindow;
+
+class ChatWindow : public QVBox
+{
+ Q_OBJECT
+
+ public:
+ explicit ChatWindow(QWidget* parent);
+ ~ChatWindow();
+
+ enum WindowType
+ {
+ Status=0,
+ Channel,
+ Query,
+ DccChat,
+ DccTransferPanel,
+ RawLog,
+ Notice,
+ SNotice,
+ ChannelList,
+ Konsole,
+ UrlCatcher,
+ NicksOnline,
+ LogFileReader
+ };
+
+ /** This should be called and set with a non-null server as soon
+ * as possibly after ChatWindow is created.
+ * @param newServer The server to set it to.
+ */
+ virtual void setServer(Server* newServer);
+ /** This should be called if setServer is not called - e.g.
+ * in the case of konsolepanel. This should be set as soon
+ * as possible after creation.
+ */
+
+ /** Get the server this is linked to.
+ * @return The server it is associated with, or null if none.
+ */
+ Server* getServer();
+ void setTextView(IRCView* newView);
+ IRCView* getTextView() const;
+ void setLog(bool activate);
+
+ QString getName();
+
+ void setType(WindowType newType);
+ WindowType getType();
+
+ virtual void append(const QString& nickname,const QString& message);
+ virtual void appendRaw(const QString& message, bool suppressTimestamps=false);
+ virtual void appendQuery(const QString& nickname,const QString& message, bool inChannel = false);
+ virtual void appendAction(const QString& nickname,const QString& message);
+ virtual void appendServerMessage(const QString& type,const QString& message, bool parseURL = true);
+ virtual void appendCommandMessage(const QString& command, const QString& message, bool important = true,
+ bool parseURL = true, bool self = false);
+ virtual void appendBacklogMessage(const QString& firstColumn,const QString& message);
+
+ QWidget* parentWidget;
+
+ virtual QString getTextInLine();
+ /** Clean up and close this tab. Return false if you want to cancel the close. */
+ virtual bool closeYourself(bool askForConfirmation = true);
+ /** Reimplement this to return true in all classes that /can/ become front view.
+ */
+ virtual bool canBeFrontView();
+
+ /** Reimplement this to return true in all classes that you can search in - i.e. use "Edit->Find Text" in.
+ */
+ virtual bool searchView();
+
+ virtual bool notificationsEnabled() { return m_notificationsEnabled; }
+
+ virtual bool eventFilter(QObject* watched, QEvent* e);
+
+ QString logFileName() { return logfile.name(); }
+
+ virtual void setChannelEncoding(const QString& /* encoding */) {}
+ virtual QString getChannelEncoding() { return QString(); }
+ virtual QString getChannelEncodingDefaultDesc() { return QString(); }
+ bool isChannelEncodingSupported() const;
+
+ /** Force updateInfo(info) to be emitted.
+ * Useful for when this tab has just gained focus
+ */
+ virtual void emitUpdateInfo();
+
+ /** child classes have to override this and return true if they want the
+ * "insert character" item on the menu to be enabled.
+ */
+ virtual bool isInsertSupported() { return false; }
+
+ /** child classes have to override this and return true if they want the
+ * "irc color" item on the menu to be enabled.
+ */
+ virtual bool areIRCColorsSupported() {return false; }
+
+ Konversation::TabNotifyType currentTabNotification() { return m_currentTabNotify; }
+ QColor highlightColor();
+
+ signals:
+ void nameChanged(ChatWindow* view, const QString& newName);
+ //void online(ChatWindow* myself, bool state);
+ /** Emit this signal when you want to change the status bar text for this tab.
+ * It is ignored if this tab isn't focused.
+ */
+ void updateInfo(const QString &info);
+ void updateTabNotification(ChatWindow* chatWin, const Konversation::TabNotifyType& type);
+
+ void setStatusBarTempText(const QString&);
+ void clearStatusBarTempText();
+
+ void closing(ChatWindow* myself);
+
+ public slots:
+ void updateAppearance();
+
+ void logText(const QString& text);
+
+ /**
+ * This is called when a chat window gains focus.
+ * It enables and disables the appropriate menu items,
+ * then calls childAdjustFocus.
+ * You can call this manually to focus this tab.
+ */
+ void adjustFocus();
+
+ virtual void appendInputText(const QString&, bool fromCursor);
+ virtual void indicateAway(bool away);
+
+
+ virtual void setNotificationsEnabled(bool enable) { m_notificationsEnabled = enable; }
+ void activateTabNotification(Konversation::TabNotifyType type);
+ void resetTabNotification();
+
+ protected slots:
+ ///Used to disable functions when not connected
+ virtual void serverOnline(bool online);
+
+ protected:
+
+ /** Some children may handle the name themselves, and not want this public.
+ * Increase the visibility in the subclass if you want outsiders to call this.
+ * The name is the string that is shown in the tab.
+ * @param newName The name to show in the tab
+ */
+ virtual void setName(const QString& newName);
+
+ /** Called from adjustFocus */
+ virtual void childAdjustFocus() = 0;
+
+ void setLogfileName(const QString& name);
+ void setChannelEncodingSupported(bool enabled);
+ void cdIntoLogPath();
+
+ int spacing();
+ int margin();
+
+ bool log;
+ bool firstLog;
+ QString name;
+ QString logName;
+
+ QFont font;
+
+ IRCView* textView;
+ /** A pointer to the server this chatwindow is part of.
+ * Not always non-null - e.g. for konsolepanel
+ */
+ Server* m_server;
+ QFile logfile;
+ WindowType type;
+
+ bool m_notificationsEnabled;
+
+ bool m_channelEncodingSupported;
+
+ Konversation::TabNotifyType m_currentTabNotify;
+};
+#endif
diff --git a/konversation/src/chatwindowappearance_preferences.ui b/konversation/src/chatwindowappearance_preferences.ui
new file mode 100644
index 0000000..5a90533
--- /dev/null
+++ b/konversation/src/chatwindowappearance_preferences.ui
@@ -0,0 +1,378 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ChatWindowAppearance_Config</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.</comment>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ChatWindowAppearance_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>401</width>
+ <height>502</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>kcfg_Timestamping</cstring>
+ </property>
+ <property name="title">
+ <string>Enable &amp;Timestamps</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_ShowDate</cstring>
+ </property>
+ <property name="text">
+ <string>Sho&amp;w dates</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Format:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_TimestampFormat</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_TimestampFormat</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer127</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>191</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>kcfg_ShowBacklog</cstring>
+ </property>
+ <property name="title">
+ <string>Show &amp;Backlog</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Lines: </string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_BacklogLines</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_BacklogLines</cstring>
+ </property>
+ <property name="maxValue">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>251</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Layout</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_ShowTopic</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show channel topic</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_ShowModeButtons</cstring>
+ </property>
+ <property name="text">
+ <string>Show channel &amp;mode buttons</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="8" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_ShowIRCViewScrollBar</cstring>
+ </property>
+ <property name="text">
+ <string>Show sc&amp;rollbar</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="7" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_ShowNicknameBox</cstring>
+ </property>
+ <property name="text">
+ <string>Show bo&amp;x to change own nickname</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="1">
+ <property name="name">
+ <cstring>kcfg_ShowQuickButtons</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Show &amp;quick buttons</string>
+ </property>
+ </widget>
+ <spacer row="6" column="0">
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="5" column="1">
+ <property name="name">
+ <cstring>kcfg_ShowRealNames</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Show real names in nickname list</string>
+ </property>
+ </widget>
+ <spacer row="4" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_ShowNickList</cstring>
+ </property>
+ <property name="text">
+ <string>Show channel &amp;nick list and quick buttons</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>kcfg_AutoUserhost</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Show hostmas&amp;ks in nickname list</string>
+ </property>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer9_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>kcfg_ShowBackgroundImage</cstring>
+ </property>
+ <property name="title">
+ <string>Enable Back&amp;ground Image</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>P&amp;ath:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_BackgroundImage</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>kcfg_BackgroundImage</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer90</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_ShowNickList</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_AutoUserhost</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_ShowNickList</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_ShowQuickButtons</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_ShowNickList</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_ShowRealNames</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_TimestampFormat</tabstop>
+ <tabstop>kcfg_ShowDate</tabstop>
+ <tabstop>kcfg_BacklogLines</tabstop>
+ <tabstop>kcfg_ShowTopic</tabstop>
+ <tabstop>kcfg_ShowModeButtons</tabstop>
+ <tabstop>kcfg_ShowNickList</tabstop>
+ <tabstop>kcfg_AutoUserhost</tabstop>
+ <tabstop>kcfg_ShowRealNames</tabstop>
+ <tabstop>kcfg_ShowQuickButtons</tabstop>
+ <tabstop>kcfg_ShowNicknameBox</tabstop>
+ <tabstop>kcfg_ShowIRCViewScrollBar</tabstop>
+ <tabstop>kcfg_BackgroundImage</tabstop>
+ <tabstop>kcfg_ShowBackgroundImage</tabstop>
+ <tabstop>kcfg_Timestamping</tabstop>
+ <tabstop>kcfg_ShowBacklog</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/chatwindowbehaviour_preferences.ui b/konversation/src/chatwindowbehaviour_preferences.ui
new file mode 100644
index 0000000..b142b1e
--- /dev/null
+++ b/konversation/src/chatwindowbehaviour_preferences.ui
@@ -0,0 +1,374 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ChatwindowBehaviour_Config</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ChatwindowBehaviour_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>458</width>
+ <height>461</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>General</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_Beep</cstring>
+ </property>
+ <property name="text">
+ <string>Enable s&amp;ystem bell on incoming ASCII BEL</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Sounds the system bell when you receive an ASCII BEL (0x07) control character</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_HideUnimportantEvents</cstring>
+ </property>
+ <property name="text">
+ <string>Hide &amp;Join/Part/Nick events</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_DisableExpansion</cstring>
+ </property>
+ <property name="text">
+ <string>Disable variable (e.g. %C, %B, %G...) e&amp;xpansion</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Do not expand variables when sending text to the server. A variable starts with '%'; for example, %B will expand to the characters needed to make a text bold.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_RedirectServerAndAppMsgToStatusPane</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Redirect status messages to the server status window</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Scroll&amp;back limit:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ScrollbackMax</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>How many lines to keep in buffers, 0 = unlimited</string>
+ </property>
+ </widget>
+ <spacer row="5" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>spacer1_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>410</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_UseLiteralModes</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use raw modes for mode changes</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Keep channel mode string as a combination of characters instead of translating them into human readable words. E.g. '*** Channel modes: no messages from outside' will become '*** Channel modes: n'</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="6" column="1">
+ <property name="name">
+ <cstring>kcfg_ScrollbackMax</cstring>
+ </property>
+ <property name="suffix">
+ <string> lines</string>
+ </property>
+ <property name="specialValueText">
+ <string>Unlimited</string>
+ </property>
+ <property name="maxValue">
+ <number>2000</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>How many lines to keep in buffers, 0 = unlimited</string>
+ </property>
+ </widget>
+ <spacer row="6" column="2">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>230</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3_2</cstring>
+ </property>
+ <property name="title">
+ <string>Marker Lines</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_MarkerLineInAllViews</cstring>
+ </property>
+ <property name="text">
+ <string>Show manually inserted lines in all chat windows</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_AutomaticRememberLine</cstring>
+ </property>
+ <property name="text">
+ <string>Mark the last position in a chat window when it is hidden</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Inserts a remember line into the chat window when you switch to another chat window or minimize the application.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_AutomaticRememberLineOnlyOnTextChange</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Move the line only when new text is about to be shown</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>kcfg_AutoWhoContinuousEnabled</cstring>
+ </property>
+ <property name="title">
+ <string>Enable Automatic User Information Look Up (/WHO)</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>170</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="2" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>190</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Max. number of users in a channel:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_AutoWhoNicksLimit</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>kcfg_AutoWhoNicksLimit</cstring>
+ </property>
+ <property name="suffix">
+ <string> nicks</string>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>300</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_AutoWhoContinuousInterval</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="prefix">
+ <string></string>
+ </property>
+ <property name="suffix">
+ <string> seconds</string>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="minValue">
+ <number>30</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>whoIntervalLbl</cstring>
+ </property>
+ <property name="text">
+ <string>Update interval:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_AutoWhoContinuousInterval</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_AutoWhoContinuousEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_AutoWhoContinuousInterval</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_AutomaticRememberLine</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_AutomaticRememberLineOnlyOnTextChange</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_Beep</tabstop>
+ <tabstop>kcfg_HideUnimportantEvents</tabstop>
+ <tabstop>kcfg_DisableExpansion</tabstop>
+ <tabstop>kcfg_RedirectServerAndAppMsgToStatusPane</tabstop>
+ <tabstop>kcfg_UseLiteralModes</tabstop>
+ <tabstop>kcfg_ScrollbackMax</tabstop>
+ <tabstop>kcfg_AutomaticRememberLine</tabstop>
+ <tabstop>kcfg_AutomaticRememberLineOnlyOnTextChange</tabstop>
+ <tabstop>kcfg_MarkerLineInAllViews</tabstop>
+ <tabstop>kcfg_AutoWhoNicksLimit</tabstop>
+ <tabstop>kcfg_AutoWhoContinuousInterval</tabstop>
+ <tabstop>kcfg_AutoWhoContinuousEnabled</tabstop>
+ <tabstop>kcfg_AutoWhoContinuousEnabled</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/konversation/src/colorsappearance_preferences.ui b/konversation/src/colorsappearance_preferences.ui
new file mode 100644
index 0000000..e089ade
--- /dev/null
+++ b/konversation/src/colorsappearance_preferences.ui
@@ -0,0 +1,1395 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ColorsAppearance_Config</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.</comment>
+<author>İsmail Dönmez</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ColorsAppearance_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>457</width>
+ <height>512</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Custom Colors</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>kcfg_InputFieldsBackgroundColor</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use custom colors for input box, nickname list and tab list</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="4">
+ <property name="name">
+ <cstring>kcfg_TimeColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="4" column="4">
+ <property name="name">
+ <cstring>kcfg_AlternateBackgroundColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="4">
+ <property name="name">
+ <cstring>kcfg_QueryMessageColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="4">
+ <property name="name">
+ <cstring>kcfg_CommandMessageColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="4">
+ <property name="name">
+ <cstring>kcfg_BacklogMessageColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="4" column="1">
+ <property name="name">
+ <cstring>kcfg_TextViewBackgroundColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>kcfg_ServerMessageColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_HyperlinkColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_ChannelMessageColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_ActionMessageColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer185</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer186</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer187</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="2">
+ <property name="name">
+ <cstring>spacer188</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="4" column="2">
+ <property name="name">
+ <cstring>spacer189</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="5">
+ <property name="name">
+ <cstring>spacer190</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="5">
+ <property name="name">
+ <cstring>spacer191</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="5">
+ <property name="name">
+ <cstring>spacer192</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="5">
+ <property name="name">
+ <cstring>spacer193</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="4" column="5">
+ <property name="name">
+ <cstring>spacer194</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Background:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_TextViewBackgroundColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Server message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ServerMessageColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>H&amp;yperlink:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_HyperlinkColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Channel &amp;message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ChannelMessageColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Actio&amp;n:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ActionMessageColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Bac&amp;klog:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_BacklogMessageColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Comman&amp;d message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_CommandMessageColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="3">
+ <property name="name">
+ <cstring>textLabel1_8</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Query message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_QueryMessageColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="3">
+ <property name="name">
+ <cstring>textLabel1_9</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Timestamp:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_TimeColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="3">
+ <property name="name">
+ <cstring>textLabel1_10</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;lternate background:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_AlternateBackgroundColor</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_UseColoredNicks</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Colored Nicks</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="6">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>5:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="7">
+ <property name="name">
+ <cstring>kcfg_NickColor4</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="6">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>4:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="7">
+ <property name="name">
+ <cstring>kcfg_NickColor5</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>3:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="4">
+ <property name="name">
+ <cstring>kcfg_NickColor2</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="4">
+ <property name="name">
+ <cstring>kcfg_NickColor3</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>2:</string>
+ </property>
+ </widget>
+ <spacer row="0" column="5">
+ <property name="name">
+ <cstring>spacer196</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="5">
+ <property name="name">
+ <cstring>spacer197</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer197_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer196_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_NickColor0</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>0:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_NickColor1</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>1:</string>
+ </property>
+ </widget>
+ <spacer row="1" column="8">
+ <property name="name">
+ <cstring>spacer197_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="8">
+ <property name="name">
+ <cstring>spacer196_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="1" column="10">
+ <property name="name">
+ <cstring>kcfg_NickColor7</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="10">
+ <property name="name">
+ <cstring>kcfg_NickColor6</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="9">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>7:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="9">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>6:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="12">
+ <property name="name">
+ <cstring>layout18</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Own nick color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_NickColor8</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>kcfg_NickColor8</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer204</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>271</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_AllowColorCodes</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>&amp;Allow Colored Text in IRC Messages</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>By enabling this option, color codes added to IRC messages will be displayed in your chat window as colored text. You can add color codes to your messages, by selecting Insert -&gt; IRC Color</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel12_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>1:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel12_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>2:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel12_4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>3:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode3</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode2</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode0</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode1</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel12_14</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>0:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="3">
+ <property name="name">
+ <cstring>textLabel12_8</cstring>
+ </property>
+ <property name="text">
+ <string>7:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="3">
+ <property name="name">
+ <cstring>textLabel12_7</cstring>
+ </property>
+ <property name="text">
+ <string>6:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>textLabel12_6</cstring>
+ </property>
+ <property name="text">
+ <string>5:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>textLabel12_5</cstring>
+ </property>
+ <property name="text">
+ <string>4:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="4">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode5</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="4">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode4</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="4">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode7</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="4">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode6</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="3" column="2">
+ <property name="name">
+ <cstring>spacer211_4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer211_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer211_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer211</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="5">
+ <property name="name">
+ <cstring>spacer211_3_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="5">
+ <property name="name">
+ <cstring>spacer211_5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="5">
+ <property name="name">
+ <cstring>spacer211_2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="5">
+ <property name="name">
+ <cstring>spacer211_4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="3" column="7">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode11</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="6">
+ <property name="name">
+ <cstring>textLabel12_12</cstring>
+ </property>
+ <property name="text">
+ <string>11:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="7">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode9</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="7">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode10</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="7">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode8</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="6">
+ <property name="name">
+ <cstring>textLabel12_10</cstring>
+ </property>
+ <property name="text">
+ <string>9:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="6">
+ <property name="name">
+ <cstring>textLabel12_9</cstring>
+ </property>
+ <property name="text">
+ <string>8:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="6">
+ <property name="name">
+ <cstring>textLabel12_11</cstring>
+ </property>
+ <property name="text">
+ <string>10:</string>
+ </property>
+ </widget>
+ <spacer row="0" column="8">
+ <property name="name">
+ <cstring>spacer211_6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="8">
+ <property name="name">
+ <cstring>spacer211_2_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="8">
+ <property name="name">
+ <cstring>spacer211_3_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="8">
+ <property name="name">
+ <cstring>spacer211_4_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="3" column="10">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode15</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="9">
+ <property name="name">
+ <cstring>textLabel12_13</cstring>
+ </property>
+ <property name="text">
+ <string>12:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="10">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode13</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="9">
+ <property name="name">
+ <cstring>textLabel12_16</cstring>
+ </property>
+ <property name="text">
+ <string>15:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="10">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode12</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="10">
+ <property name="name">
+ <cstring>kcfg_IrcColorCode14</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="9">
+ <property name="name">
+ <cstring>textLabel12_15</cstring>
+ </property>
+ <property name="text">
+ <string>14:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="9">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>13:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer227</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>kcfg_ActionMessageColor</tabstop>
+ <tabstop>kcfg_ChannelMessageColor</tabstop>
+ <tabstop>kcfg_HyperlinkColor</tabstop>
+ <tabstop>kcfg_ServerMessageColor</tabstop>
+ <tabstop>kcfg_TextViewBackgroundColor</tabstop>
+ <tabstop>kcfg_BacklogMessageColor</tabstop>
+ <tabstop>kcfg_CommandMessageColor</tabstop>
+ <tabstop>kcfg_QueryMessageColor</tabstop>
+ <tabstop>kcfg_TimeColor</tabstop>
+ <tabstop>kcfg_AlternateBackgroundColor</tabstop>
+ <tabstop>kcfg_InputFieldsBackgroundColor</tabstop>
+ <tabstop>kcfg_UseColoredNicks</tabstop>
+ <tabstop>kcfg_NickColor0</tabstop>
+ <tabstop>kcfg_NickColor1</tabstop>
+ <tabstop>kcfg_NickColor2</tabstop>
+ <tabstop>kcfg_NickColor3</tabstop>
+ <tabstop>kcfg_NickColor4</tabstop>
+ <tabstop>kcfg_NickColor5</tabstop>
+ <tabstop>kcfg_NickColor6</tabstop>
+ <tabstop>kcfg_NickColor7</tabstop>
+ <tabstop>kcfg_NickColor8</tabstop>
+ <tabstop>kcfg_AllowColorCodes</tabstop>
+ <tabstop>kcfg_IrcColorCode0</tabstop>
+ <tabstop>kcfg_IrcColorCode1</tabstop>
+ <tabstop>kcfg_IrcColorCode2</tabstop>
+ <tabstop>kcfg_IrcColorCode3</tabstop>
+ <tabstop>kcfg_IrcColorCode4</tabstop>
+ <tabstop>kcfg_IrcColorCode5</tabstop>
+ <tabstop>kcfg_IrcColorCode6</tabstop>
+ <tabstop>kcfg_IrcColorCode7</tabstop>
+ <tabstop>kcfg_IrcColorCode8</tabstop>
+ <tabstop>kcfg_IrcColorCode9</tabstop>
+ <tabstop>kcfg_IrcColorCode10</tabstop>
+ <tabstop>kcfg_IrcColorCode11</tabstop>
+ <tabstop>kcfg_IrcColorCode12</tabstop>
+ <tabstop>kcfg_IrcColorCode13</tabstop>
+ <tabstop>kcfg_IrcColorCode14</tabstop>
+ <tabstop>kcfg_IrcColorCode15</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+<connections>
+ <connection>
+ <sender>kcfg_InputFieldsBackgroundColor</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_AlternateBackgroundColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+</UI>
diff --git a/konversation/src/commit.h b/konversation/src/commit.h
new file mode 100644
index 0000000..cc8815a
--- /dev/null
+++ b/konversation/src/commit.h
@@ -0,0 +1,4 @@
+// This COMMIT number is added to version string to be used as "patch level"
+#ifndef COMMIT
+#define COMMIT 3300
+#endif
diff --git a/konversation/src/common.cpp b/konversation/src/common.cpp
new file mode 100644
index 0000000..afd6c97
--- /dev/null
+++ b/konversation/src/common.cpp
@@ -0,0 +1,196 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "common.h"
+#include "konversationapplication.h"
+#include "config/preferences.h"
+
+#include <qcstring.h>
+#include <qstring.h>
+#include <qregexp.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <klocale.h>
+
+
+namespace Konversation
+{
+
+ #include "guess_ja.cpp"
+ #include "unicode.cpp"
+
+ static QRegExp colorRegExp("((\003([0-9]|0[0-9]|1[0-5])(,([0-9]|0[0-9]|1[0-5])|)|\017)|\x02|\x09|\x13|\x16|\x1f)");
+ static QRegExp urlPattern("((www\\.(?!\\.)|(fish|irc|(f|sf|ht)tp(|s))://)(\\.?[\\d\\w/,\\':~\\?=;#@\\-\\+\\%\\*\\{\\}\\!\\(\\)]|&)+)|"
+ "([-.\\d\\w]+@[-.\\d\\w]{2,}\\.[\\w]{2,})");
+ static QRegExp tdlPattern("(.*)\\.(\\w+),$");
+
+ QString removeIrcMarkup(const QString& text)
+ {
+ QString escaped = text;
+ // Escape text decoration
+ escaped.remove(colorRegExp);
+
+ // Remove Mirc's 0x03 characters too, they show up as rectangles
+ escaped.remove(QChar(0x03));
+
+ return escaped;
+ }
+
+ QString tagURLs(const QString& text, const QString& fromNick, bool useCustomColor)
+ {
+ // QTime timer;
+ // timer.start();
+
+ QString filteredLine = text;
+ QString linkColor = Preferences::color(Preferences::Hyperlink).name();
+ QString link;
+ QString insertText;
+ int pos = 0;
+ int urlLen = 0;
+ QString href;
+
+ if(useCustomColor)
+ {
+ link = "<font color=\""+linkColor+"\"><a href=\"#%1\">%2</a></font>";
+ }
+ else
+ {
+ link = "<a href=\"#%1\">%2</a>";
+ }
+
+ if(filteredLine.contains("#"))
+ {
+ QRegExp chanExp("(^|\\s|^\"|\\s\"|,|'|\\(|\\:|!|@|%|\\+)(#[^,\\s;\\)\\:\\/\\(\\<\\>]*[^.,\\s;\\)\\:\\/\\(\"\''\\<\\>])");
+ while((pos = chanExp.search(filteredLine, pos)) >= 0)
+ {
+ href = chanExp.cap(2);
+ urlLen = href.length();
+ pos += chanExp.cap(1).length();
+
+ // HACK:Use space as a placeholder for \ as Qt tries to be clever and does a replace to / in urls in QTextEdit
+ insertText = link.arg(QString(href).replace('\\', " "), href);
+ filteredLine.replace(pos, urlLen, insertText);
+ pos += insertText.length();
+ }
+ }
+
+ pos = 0;
+ urlLen = 0;
+
+ urlPattern.setCaseSensitive(false);
+ QString protocol;
+
+ if(useCustomColor)
+ {
+ link = "<font color=\"" + linkColor + "\"><u><a href=\"%1%2\">%3</a></u></font>";
+ }
+ else
+ {
+ link = "<u><a href=\"%1%2\">%3</a></u>";
+ }
+
+ while((pos = urlPattern.search(filteredLine, pos)) >= 0)
+ {
+ QString append;
+
+ // check if the matched text is already replaced as a channel
+ if ( filteredLine.findRev( "<a", pos ) > filteredLine.findRev( "</a>", pos ) )
+ {
+ ++pos;
+ continue;
+ }
+
+ protocol="";
+ urlLen = urlPattern.matchedLength();
+ href = filteredLine.mid( pos, urlLen );
+
+ // Don't consider trailing comma part of link.
+ if (href.right(1) == ",")
+ {
+ href.truncate(href.length()-1);
+ append = ',';
+ }
+
+ // Don't consider trailing semicolon part of link.
+ if (href.right(1) == ";")
+ {
+ href.truncate(href.length()-1);
+ append = ';';
+ }
+
+ // Don't consider trailing closing parenthesis part of link when
+ // there's an opening parenthesis preceding the beginning of the
+ // URL or there is no opening parenthesis in the URL at all.
+ if (href.right(1) == ")" && (filteredLine.mid(pos-1,1) == "(" || !href.contains("(")))
+ {
+ href.truncate(href.length()-1);
+ append.prepend(")");
+ }
+
+ // Qt doesn't support (?<=pattern) so we do it here
+ if((pos > 0) && filteredLine[pos-1].isLetterOrNumber())
+ {
+ pos++;
+ continue;
+ }
+
+ if (urlPattern.cap(1).startsWith("www.", false))
+ protocol = "http://";
+ else if (urlPattern.cap(1).isEmpty())
+ protocol = "mailto:";
+
+ // Use \x0b as a placeholder for & so we can readd them after changing all & in the normal text to &amp;
+ // HACK Replace % with \x03 in the url to keep Qt from doing stupid things
+ insertText = link.arg(protocol, QString(href).replace('&', "\x0b").replace('%', "\x03"), href) + append;
+ filteredLine.replace(pos, urlLen, insertText);
+ pos += insertText.length();
+ KonversationApplication::instance()->storeUrl(fromNick, href);
+ }
+
+ // Change & to &amp; to prevent html entities to do strange things to the text
+ filteredLine.replace('&', "&amp;");
+ filteredLine.replace("\x0b", "&");
+
+ // kdDebug() << "Took (msecs) : " << timer.elapsed() << " for " << filteredLine << endl;
+
+ return filteredLine;
+ }
+
+ //TODO: there's room for optimization as pahlibar said. (strm)
+
+ // the below two functions were taken from kopeteonlinestatus.cpp.
+ QBitmap overlayMasks( const QBitmap *under, const QBitmap *over )
+ {
+ if ( !under && !over ) return QBitmap();
+ if ( !under ) return *over;
+ if ( !over ) return *under;
+
+ QBitmap result = *under;
+ bitBlt( &result, 0, 0, over, 0, 0, over->width(), over->height(), Qt::OrROP );
+ return result;
+ }
+
+ QPixmap overlayPixmaps( const QPixmap &under, const QPixmap &over )
+ {
+ if ( over.isNull() ) return under;
+
+ QImage imResult; imResult=under;
+ QImage imOver; imOver=over;
+ QPixmap result;
+
+ bitBlt(&imResult,0,0,&imOver,0,0,imOver.width(),imOver.height(),0);
+ result=imResult;
+ return result;
+ }
+
+}
diff --git a/konversation/src/common.h b/konversation/src/common.h
new file mode 100644
index 0000000..fe5fa6d
--- /dev/null
+++ b/konversation/src/common.h
@@ -0,0 +1,56 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+class QCString;
+class QString;
+class QBitmap;
+class QPixmap;
+
+namespace Konversation
+{
+ QString removeIrcMarkup(const QString& text);
+ QString tagURLs(const QString& text, const QString& fromNick, bool useCustomColor = true);
+ QBitmap overlayMasks( const QBitmap *under, const QBitmap *over );
+ QPixmap overlayPixmaps(const QPixmap &under, const QPixmap &over);
+ bool isUtf8(const QCString& text);
+
+ enum TabNotifyType
+ {
+ tnfNick,
+ tnfHighlight,
+ tnfPrivate,
+ tnfNormal,
+ tnfSystem,
+ tnfControl,
+ tnfNone
+ };
+
+ enum ConnectionState
+ {
+ SSNeverConnected,
+ SSDeliberatelyDisconnected,
+ SSInvoluntarilyDisconnected,
+ SSConnecting,
+ SSConnected
+ };
+
+ enum ConnectionFlag
+ {
+ SilentlyReuseConnection,
+ PromptToReuseConnection,
+ CreateNewConnection
+ };
+}
+#endif
diff --git a/konversation/src/config/Makefile.am b/konversation/src/config/Makefile.am
new file mode 100644
index 0000000..23e37bb
--- /dev/null
+++ b/konversation/src/config/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = -I.. -I$(srcdir)/.. $(all_includes)
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libkonversationconfig.la
+libkonversationconfig_la_SOURCES = preferences_base.kcfgc preferences.cpp
+libkonversationconfig_la_LDFLAGS = $(all_libraries) -no-undefined
+
+kde_kcfg_DATA = konversation.kcfg
diff --git a/konversation/src/config/konversation.kcfg b/konversation/src/config/konversation.kcfg
new file mode 100644
index 0000000..9346e18
--- /dev/null
+++ b/konversation/src/config/konversation.kcfg
@@ -0,0 +1,996 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <include>qfont.h</include>
+ <include>qsize.h</include>
+ <include>kdialog.h</include>
+ <include>kuser.h</include>
+ <include>kglobalsettings.h</include>
+ <kcfgfile name="konversationrc" />
+
+ <group name="Appearance">
+ <entry key="ShowMenuBar" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="UseMaxSizedTabs" type="Bool">
+ <default>true</default>
+ <label>Limit the size of the tab labels to fit all on screen</label>
+ </entry>
+ <entry key="TreeSplitterSizes" type="IntList">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TopicSplitterSizes" type="IntList">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TextFont" type="Font">
+ <default code="true">KGlobalSettings::generalFont()</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ListFont" type="Font">
+ <default code="true">KGlobalSettings::generalFont()</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabFont" type="Font">
+ <default code="true">KGlobalSettings::generalFont()</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="CustomTextFont" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="CustomListFont" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="CustomTabFont" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="Timestamping" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowDate" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TimestampFormat" type="String">
+ <default>hh:mm</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowBacklog" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="BacklogLines" type="Int">
+ <default>10</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowNickList" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowQuickButtons" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+<!-- quick buttons are handled outside of KConfigXT for now (Eisfuchs)
+ <entry key="QuickButtons" type="StringList" name="ButtonList">
+ <default code="true">(QStringList() &lt;&lt; "Op,/OP %u%n"&lt;&lt;"DeOp,/DEOP %u%n"&lt;&lt;"WhoIs,/WHOIS %s,%%u%n"&lt;&lt;"Version,/CTCP %s,%%u VERSION%n"&lt;&lt;"Kick,/KICK %u%n"&lt;&lt;"Kick,/KICK %u%n"&lt;&lt;"Ban,/BAN %u%n"&lt;&lt;"Part,/PART %c Leaving...%n"&lt;&lt;"Quit,/QUIT Leaving...%n")</default>
+ </entry> -->
+ <entry key="ShowModeButtons" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="CloseButtons" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutoUserhost" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="UseSpacing" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="Spacing" type="Int">
+ <default>KDialog::spacingHint()</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="Margin" type="Int">
+ <default>KDialog::marginHint()</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="UseParagraphSpacing" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ParagraphSpacing" type="Int">
+ <default>2</default>
+ <min>0</min>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ChannelSplitterSizes" type="IntList">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="BackgroundImage" type="String">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="IrcColorCode$(colorNumber)" type="Color">
+ <parameter name="colorNumber" type="UInt" max="15"/>
+ <default param="0">#ffffff</default>
+ <default param="1">#000000</default>
+ <default param="2">#000080</default>
+ <default param="3">#008000</default>
+ <default param="4">#ff0000</default>
+ <default param="5">#a52a2a</default>
+ <default param="6">#800080</default>
+ <default param="7">#ff8000</default>
+ <default param="8">#808000</default>
+ <default param="9">#00ff00</default>
+ <default param="10">#008080</default>
+ <default param="11">#00ffff</default>
+ <default param="12">#0000ff</default>
+ <default param="13">#ffc0cb</default>
+ <default param="14">#a0a0a0</default>
+ <default param="15">#c0c0c0</default>
+ </entry>
+ <entry key="AllowColorCodes" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="NickColor$(nickNumber)" type="Color">
+ <parameter name="nickNumber" type="UInt" max="9"/>
+ <default param="0">#E90E7F</default>
+ <default param="1">#8E55E9</default>
+ <default param="2">#B30E0E</default>
+ <default param="3">#18B33C</default>
+ <default param="4">#58ADB3</default>
+ <default param="5">#9E54B3</default>
+ <default param="6">#B39875</default>
+ <default param="7">#3176B3</default>
+ <default param="8">#000001</default>
+ </entry>
+ <entry key="UseColoredNicks" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowTabBarCloseButton" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowTopic" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowNicknameBox" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="UseBoldNicks" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="UseLiteralModes" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="FocusNewQueries" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowIRCViewScrollBar" type="Bool">
+ <default>true</default>
+ <label>Hide the scrollbar</label>
+ </entry>
+ </group>
+ <group name="General Options">
+ <entry key="SpellChecking" type="Bool">
+ <default>false</default>
+ <whatsthis>Enable if you want all the IRC input lines to check your spelling as you type</whatsthis>
+ </entry>
+ <entry key="CustomVersionReplyEnabled" type="Bool">
+ <default>false</default>
+ </entry>
+ <entry key="CustomVersionReply" type="String">
+ </entry>
+ <entry key="UseMultiRowInputBox" type="Bool">
+ <default>false</default>
+ <whatsthis>Enabling this will cause the input box to grow vertically when it fills up.</whatsthis>
+ </entry>
+ <entry key="CommandChar" type="String">
+ <default>/</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="PreShellCommand" type="String">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowTrayIcon" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TrayNotify" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TrayNotifyOnlyOwnNick" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TrayNotifyBlink" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="HiddenToTray" type="Bool">
+ <default>false</default>
+ <label>Hidden to system tray</label>
+ </entry>
+ <entry key="HideToTrayOnStartup" type="Bool">
+ <default>false</default>
+ <label>Start with hidden mainwindow</label>
+ </entry>
+ <entry key="ShowBackgroundImage" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="NicksOnlineGeometry" type="Size" name="NicksOnlineSize">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="NicknameGeometry" type="Size" name="NicknameSize">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="LogfileReaderGeometry" type="Size" name="LogfileReaderSize">
+ <default code="true">QSize(400, 200)</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="MultilineEditGeometry" type="Size" name="MultilineEditSize">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="LogfileBufferSize" type="Int">
+ <default>100</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ScrollbackMax" type="Int">
+ <default>1000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutoWhoNicksLimit" type="Int">
+ <default>200</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutoWhoContinuousEnabled" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutoWhoContinuousInterval" type="Int">
+ <default>90</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowRealNames" type="Bool">
+ <default>false</default>
+ <label>&amp;Show real names next to nicknames</label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ChannelDoubleClickAction" type="String">
+ <default>/QUERY %u%n</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="NotifyDoubleClickAction" type="String">
+ <default>/QUERY %u%n</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="Beep" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="RawLog" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="VersionReply" type="String">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="MaximumLag" type="Int" name="MaximumLagTime">
+ <default>180</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="RedirectServerAndAppMsgToStatusPane" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="DisableNotifyWhileAway" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutoAwayPollInterval" type="Int">
+ <default>10</default>
+ <min>10</min>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutomaticRememberLine" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutomaticRememberLineOnlyOnTextChange" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="MarkerLineInAllViews" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Sort Nicknames">
+<!-- done outside of KConfigXT (Eisfuchs)
+ <entry key="AdminValue" type="Int">
+ <default>1</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OwnerValue" type="Int">
+ <default>2</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OperatorValue" type="Int" name="OpValue">
+ <default>4</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="HalfopValue" type="Int">
+ <default>8</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="VoiceValue" type="Int">
+ <default>16</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AwayValue" type="Int">
+ <default>32</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="NoRightsValue" type="Int">
+ <default>64</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+-->
+ <entry key="SortByStatus" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SortByActivity" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SortCaseInsensitive" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="OSD">
+ <entry key="UseOSD" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowOwnNick" type="Bool" name="OSDShowOwnNick">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowChannel" type="Bool" name="OSDShowChannel">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowQuery" type="Bool" name="OSDShowQuery">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowChannelEvent" type="Bool" name="OSDShowChannelEvent">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDFont" type="Font">
+ <default code="true">KGlobalSettings::generalFont()</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDUseCustomColors" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDDuration" type="Int">
+ <default>3000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDScreen" type="Int">
+ <default>0</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDDrawShadow" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OffsetX" type="Int" name="OSDOffsetX">
+ <default>30</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OffsetY" type="Int" name="OSDOffsetY">
+ <default>50</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="Alignment" type="Int" name="OSDAlignment">
+ <default>0</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDTextColor" type="Color">
+ <default>#ffffff</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDBackgroundColor" type="Color">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OSDCheckDesktopLock" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Notify List">
+ <entry key="NotifyDelay" type="Int">
+ <default>20</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="UseNotify" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="OnStartup" type="Bool" name="OpenWatchedNicksAtStartup">
+ <default>false</default>
+ <label>Open Watched Nicks tab at application startup</label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Highlight List">
+ <entry key="HighlightSoundsEnabled" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="HighlightNick" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="HighlightNickColor" type="Color">
+ <default>#FF0000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="HighlightOwnLines" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="HighlightOwnLinesColor" type="Color">
+ <default>#ff0000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="Highlight" type="String">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Aliases">
+ <entry key="AliasList" type="StringList">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Nick Completion">
+ <entry key="Mode" type="Int" name="NickCompletionMode">
+ <default>2</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SuffixStart" type="String" name="NickCompleteSuffixStart">
+ <default>: </default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SuffixMiddle" type="String" name="NickCompleteSuffixMiddle">
+ <default> </default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="PrefixCharacter" type="String">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="CaseSensitive" type="Bool" name="NickCompletionCaseSensitive">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="DCC Settings">
+ <entry key="BufferSize" type="Int" name="DccBufferSize">
+ <default>8192</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="MethodToGetOwnIp" type="Int" name="DccMethodToGetOwnIp">
+ <default>1</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SpecificOwnIp" type="String" name="DccSpecificOwnIp">
+ <default>0.0.0.0</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SpecificSendPorts" type="Bool" name="DccSpecificSendPorts">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SendPortsFirst" type="Int" name="DccSendPortsFirst">
+ <default>1026</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SendPortsLast" type="UInt" name="DccSendPortsLast">
+ <default>7000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SpecificChatPorts" type="Bool" name="DccSpecificChatPorts">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ChatPortsFirst" type="Int" name="DccChatPortsFirst">
+ <default>1026</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ChatPortsLast" type="UInt" name="DccChatPortsLast">
+ <default>7000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AddPartner" type="Bool" name="DccAddPartner">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="CreateFolder" type="Bool" name="DccCreateFolder">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SpaceToUnderscore" type="Bool" name="DccSpaceToUnderscore">
+ <default>true</default>
+ <label></label>
+ <whatis></whatis>
+ </entry>
+ <entry key="AutoGet" type="Bool" name="DccAutoGet">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutoResume" type="Bool" name="DccAutoResume">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="FastSend" type="Bool" name="DccFastSend">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SendTimeout" type="Int" name="DccSendTimeout">
+ <default>180</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="IPv4Fallback" type="Bool" name="DccIPv4Fallback">
+ <default>false</default>
+ </entry>
+ <entry key="IPv4FallbackIface" type="String" name="DccIPv4FallbackIface">
+ <default>eth0</default>
+ </entry>
+ <entry key="PassiveSend" type="Bool" name="DccPassiveSend">
+ <default>false</default>
+ </entry>
+ <entry key="ColumnWidths" type="IntList" name="DccColumnWidths">
+ <default>16,90,80,150,70,90,120,80,70,120</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Path Settings">
+ <entry key="LogfilePath" type="Path">
+ <default code="true">KUser(KUser::UseRealUserID).homeDir()+"/logs"</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="DccPath" type="Path">
+ <default code="true">KUser(KUser::UseRealUserID).homeDir()+"/dccrecv"</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Flags">
+ <entry key="Log" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="LowerLog" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AddHostnameToLog" type="Bool">
+ <default>false</default>
+ </entry>
+ <entry key="TabPlacement" type="Enum">
+ <label></label>
+ <whatsthis></whatsthis>
+ <choices>
+ <choice name="Top" />
+ <choice name="Bottom" />
+ <choice name="Left" />
+ </choices>
+ <default>Bottom</default>
+ </entry>
+ <entry key="MiddleClickClose" type="Bool">
+ <default>false</default>
+ </entry>
+ <entry key="UseClickableNicks" type="Bool">
+ <default>true</default>
+ </entry>
+ <entry key="BringToFront" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="HideUnimportantEvents" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="DisableExpansion" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="AutoReconnect" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ReconnectDelay" type="UInt">
+ <default>10</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ReconnectCount" type="UInt">
+ <default>0</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="FixedMOTD" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="SkipMOTD" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="ShowServerList" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="InputFieldsBackgroundColor" type="Bool" >
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Web Browser Settings">
+ <entry key="UseCustomBrowser" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="WebBrowserCmd" type="String">
+ <default>firefox '%u'</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Themes">
+ <entry key="IconTheme" type="String">
+ <default>default</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="EnableEmotIcons" type="Bool">
+ <default>false</default>
+ <label>Enable emoticons</label>
+ </entry>
+ <entry key="EmotIconTheme" type="String">
+ <default>Default</default>
+ <label>Emoticons theme</label>
+ </entry>
+ </group>
+ <group name="PreferencesDialog">
+ <entry name="PreferencesDialogSize" key="Size" type="Size">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="LastActiveModule" type="Int">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="ServerListDialog">
+ <entry name="ServerListDialogSize" key="Size" type="Size">
+ <default></default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Message Text Colors">
+ <entry name="$(colorName)Color" type="Color" key="$(colorName)">
+ <parameter name="colorName" type="Enum">
+ <values>
+ <value>ActionMessage</value>
+ <value>BacklogMessage</value>
+ <value>ChannelMessage</value>
+ <value>CommandMessage</value>
+ <value>QueryMessage</value>
+ <value>ServerMessage</value>
+ <value>Time</value>
+ <value>Action</value>
+ <value>TextViewBackground</value>
+ <value>AlternateBackground</value>
+ <value>Hyperlink</value>
+ </values>
+ </parameter>
+ <default param="ActionMessage">#0000ff</default>
+ <default param="BacklogMessage">#aaaaaa</default>
+ <default param="ChannelMessage">#000000</default>
+ <default param="CommandMessage">#960096</default>
+ <default param="QueryMessage">#000000</default>
+ <default param="ServerMessage">#91640a</default>
+ <default param="Time">#709070</default>
+ <default param="Action">#0000ff</default>
+ <default param="TextViewBackground">#ffffff</default>
+ <default param="AlternateBackground">#EDF4F9</default>
+ <default param="Hyperlink">#0000ff</default>
+ </entry>
+ </group>
+ <group name="Tab Notifications">
+ <entry key="TabNotificationsText" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsLeds" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsSystem" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsSystemColor" type="Color">
+ <default>#C3C300</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsMsgs" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsMsgsColor" type="Color">
+ <default>#008000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsPrivate" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsPrivateColor" type="Color">
+ <default>#800000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsEvents" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsEventsColor" type="Color">
+ <default>#008000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsNick" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsNickColor" type="Color">
+ <default>#FF0000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsHighlights" type="Bool">
+ <default>true</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsHighlightsColor" type="Color">
+ <default>#FF0000</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ <entry key="TabNotificationsOverride" type="Bool">
+ <default>false</default>
+ <label></label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+ <group name="Sort Nicknames">
+ <entry key="SortOrder" type="String">
+ <default>qpohv-</default>
+ <label></label>
+ </entry>
+ </group>
+ <group name="QueueRates">
+ <entry name="queueRate$(QueueIndex)" type="IntList" key="EmptyingRate $(QueueIndex)">
+ <!-- kconfig_compiler really should do this for me... -->
+ <code> QValueList&lt; QValueList&lt;int&gt; &gt; defaultRate;
+ QValueList&lt; QValueList&lt;int&gt; &gt;::iterator defaultRateInitIter;
+
+ defaultRateInitIter = defaultRate.append(QValueList&lt;int&gt;());
+ (*defaultRateInitIter).append( 15 );
+ (*defaultRateInitIter).append( 60 );
+ (*defaultRateInitIter).append( 0 );
+
+ defaultRateInitIter = defaultRate.append(QValueList&lt;int&gt;());
+ (*defaultRateInitIter).append( 40 );
+ (*defaultRateInitIter).append( 60 );
+ (*defaultRateInitIter).append( 0 );
+
+ defaultRateInitIter = defaultRate.append(QValueList&lt;int&gt;());
+ (*defaultRateInitIter).append( 1 );
+ (*defaultRateInitIter).append( 1 );
+ (*defaultRateInitIter).append( 0 );
+
+ if (0) //kconfig_compiler generates invalid code for int lists</code>
+ <parameter name="QueueIndex" type="Int" max="2"/>
+ <default param="0" code="true">defaultRate[0]</default>
+ <default param="1" code="true">defaultRate[1]</default>
+ <default param="2" code="true">defaultRate[2]</default>
+ </entry>
+ <entry key="ShowQueueTuner" type="Bool">
+ <default>false</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/konversation/src/config/preferences.cpp b/konversation/src/config/preferences.cpp
new file mode 100644
index 0000000..9910256
--- /dev/null
+++ b/konversation/src/config/preferences.cpp
@@ -0,0 +1,605 @@
+/*
+ This program is free software; you can redistribute it and/or self()->modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "config/preferences.h"
+#include "identity.h"
+#include "ignore.h"
+#include "highlight.h"
+#include "commit.h"
+#include "version.h"
+
+#include <ktoolbar.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kuser.h>
+
+#include <qpalette.h>
+#include <qregexp.h>
+#include <qfileinfo.h>
+
+
+Preferences *Preferences::mSelf = 0;
+static KStaticDeleter<Preferences> staticPreferencesDeleter;
+
+Preferences *Preferences::self()
+{
+ if ( !mSelf ) {
+ staticPreferencesDeleter.setObject( mSelf, new Preferences() );
+ mSelf->readConfig();
+ }
+
+ return mSelf;
+}
+
+
+Preferences::Preferences()
+{
+ mSelf = this;
+ // create default identity
+ mIdentity=new Identity();
+ mIdentity->setName(i18n("Default Identity"));
+ addIdentity(mIdentity);
+ mIgnoreList.setAutoDelete(true);
+
+ KUser user(KUser::UseRealUserID);
+ mIdentity->setIdent(user.loginName());
+ mIdentity->setRealName(user.fullName());
+
+ QStringList nickList;
+ nickList.append(user.loginName());
+ nickList.append(user.loginName() + '_');
+ nickList.append(user.loginName() + "__");
+ mIdentity->setNicknameList(nickList);
+
+ Konversation::ServerGroupSettingsPtr serverGroup = new Konversation::ServerGroupSettings;
+ serverGroup->setName("Ubuntu IRC");
+ Konversation::ServerSettings server;
+ server.setHost("irc.ubuntu.com");
+ server.setPort(8001);
+ serverGroup->addServer(server);
+ serverGroup->setIdentityId(mIdentity->id());
+ Konversation::ChannelSettings channel;
+ channel.setName("#kubuntu");
+ serverGroup->addChannel(channel);
+ serverGroup->setExpanded(false);
+ addServerGroup(serverGroup);
+ setQuickButtonList(defaultQuickButtonList());
+ setAutoreplaceList(defaultAutoreplaceList());
+}
+
+Preferences::~Preferences()
+{
+ mIdentityList.clear();
+
+ if ( mSelf == this )
+ staticPreferencesDeleter.setObject( mSelf, 0, false );
+}
+const Konversation::ServerGroupList Preferences::serverGroupList()
+{
+ return self()->mServerGroupList;
+}
+
+const QStringList Preferences::defaultQuickButtonList()
+{
+ return QStringList() << "Op,/OP %u%n"
+ << "DeOp,/DEOP %u%n"
+ << "WhoIs,/WHOIS %s,%%u%n"
+ << "Version,/CTCP %s,%%u VERSION%n"
+ << "Kick,/KICK %u%n"
+ << "Ban,/BAN %u%n"
+ << "Part,/PART %c Leaving...%n"
+ << "Quit,/QUIT Leaving...%n";
+}
+
+const QStringList Preferences::quickButtonList()
+{
+ return self()->mQuickButtonList;
+}
+
+void Preferences::setQuickButtonList(const QStringList newList)
+{
+ self()->mQuickButtonList=newList;
+}
+
+void Preferences::clearQuickButtonList()
+{
+ self()->mQuickButtonList.clear();
+}
+
+// --------------------------- AutoReplace ---------------------------
+
+const QStringList Preferences::defaultAutoreplaceList()
+{
+ return QStringList() << "1,o,\\[\\[([^\\s]+)\\]\\],http://en.wikipedia.org/wiki/Special:Search?go=Go&search=%1"
+ << "1,o,([Dd][Bb][Uu][Gg]:)(\\w+),http://bugs.debian.org/%2"
+ << "1,o,(BUG:|bug:)([0-9]+),http://bugs.kde.org/show_bug.cgi?id=%2";
+}
+
+const QStringList Preferences::autoreplaceList()
+{
+ return self()->mAutoreplaceList;
+}
+
+void Preferences::setAutoreplaceList(const QStringList newList)
+{
+ self()->mAutoreplaceList=newList;
+}
+
+void Preferences::clearAutoreplaceList()
+{
+ self()->mAutoreplaceList.clear();
+}
+
+// --------------------------- AutoReplace ---------------------------
+
+void Preferences::setServerGroupList(const Konversation::ServerGroupList& list)
+{
+ self()->mServerGroupList.clear();
+ self()->mServerGroupList = list;
+
+ Konversation::ServerGroupList::iterator it;
+}
+
+void Preferences::addServerGroup(Konversation::ServerGroupSettingsPtr serverGroup)
+{
+ self()->mServerGroupList.append(serverGroup);
+}
+
+const Konversation::ServerGroupSettingsPtr Preferences::serverGroupById(int id)
+{
+ if (!self()->mServerGroupList.count())
+ {
+ return 0;
+ }
+
+ Konversation::ServerGroupList::iterator it;
+
+ for (it = self()->mServerGroupList.begin(); it != self()->mServerGroupList.end(); ++it)
+ {
+ if ((*it)->id() == id)
+ {
+ return (*it);
+ }
+ }
+
+ return 0;
+}
+
+const Konversation::ServerGroupSettingsPtr Preferences::serverGroupByServer(const QString& server)
+{
+ if (!self()->mServerGroupList.count())
+ {
+ return 0;
+ }
+
+ Konversation::ServerGroupList::iterator it;
+
+ for (it = self()->mServerGroupList.begin(); it != self()->mServerGroupList.end(); ++it)
+ {
+ for (uint i = 0; i != (*it)->serverList().count(); i++)
+ {
+ if ((*it)->serverByIndex(i).host().lower() == server)
+ {
+ return (*it);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int Preferences::serverGroupIdByName(const QString& serverGroup)
+{
+ Konversation::ServerGroupList::iterator it;
+
+ for (it = self()->mServerGroupList.begin(); it != self()->mServerGroupList.end(); ++it)
+ {
+ if((*it)->name().lower() == serverGroup.lower())
+ {
+ return (*it)->id();
+ }
+ }
+
+ return -1;
+}
+
+bool Preferences::isServerGroup(const QString& server)
+{
+ Konversation::ServerGroupList::iterator it;
+
+ for(it = self()->mServerGroupList.begin(); it != self()->mServerGroupList.end(); ++it)
+ {
+ if((*it)->name().lower() == server.lower())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Preferences::removeServerGroup(int id)
+{
+ if (!self()->mServerGroupList.count())
+ {
+ return;
+ }
+
+ Konversation::ServerGroupList::iterator it;
+
+ for (it = self()->mServerGroupList.begin(); it != self()->mServerGroupList.end(); ++it)
+ {
+ if ((*it)->id() == id)
+ {
+ self()->mServerGroupList.remove(it);
+
+ return;
+ }
+ }
+}
+
+
+const QPtrList<Highlight> Preferences::highlightList()
+{
+ return self()->mHighlightList;
+}
+
+void Preferences::setHighlightList(QPtrList<Highlight> newList)
+{
+ self()->mHighlightList.clear();
+ self()->mHighlightList=newList;
+}
+
+void Preferences::addHighlight(const QString& newHighlight,
+bool regExp,
+const QColor &newColor,
+const QString& sound,
+const QString& autoText)
+{
+ self()->mHighlightList.append(new Highlight(newHighlight,regExp,newColor,KURL(sound),autoText));
+}
+
+void Preferences::setIgnoreList(QPtrList<Ignore> newList)
+{
+ self()->mIgnoreList.clear();
+ self()->mIgnoreList=newList;
+}
+
+void Preferences::addIgnore(const QString &newIgnore)
+{
+ QStringList ignore = QStringList::split(',',newIgnore);
+ removeIgnore(ignore[0]);
+ self()->mIgnoreList.append(new Ignore(ignore[0],ignore[1].toInt()));
+}
+
+bool Preferences::removeIgnore(const QString &oldIgnore)
+{
+ QPtrListIterator<Ignore> ignoreList( self()->mIgnoreList );
+
+ while (ignoreList.current())
+ {
+ if (ignoreList.current()->getName().lower()==oldIgnore.lower())
+ {
+ self()->mIgnoreList.remove(ignoreList.current());
+ return true;
+ }
+ ++ignoreList;
+ }
+
+ return false;
+}
+
+bool Preferences::isIgnored(const QString &nickname)
+{
+ QPtrListIterator<Ignore> ignoreList( self()->mIgnoreList );
+
+ while (ignoreList.current())
+ {
+ if (ignoreList.current()->getName().section('!',0,0).lower()==nickname.lower())
+ {
+ return true;
+ }
+ ++ignoreList;
+ }
+
+ return false;
+}
+
+void Preferences::setNotifyList(const QMap<int, QStringList> &newList)
+{ self()->mNotifyList=newList; }
+
+const QMap<int, QStringList> Preferences::notifyList() { return self()->mNotifyList; }
+
+const QStringList Preferences::notifyListByGroupName(const QString& groupName)
+{
+ int id=serverGroupIdByName(groupName);
+ if (id && self()->mNotifyList.find(id) != self()->mNotifyList.end())
+ return self()->mNotifyList[id];
+ else
+ return QStringList();
+}
+
+const QString Preferences::notifyStringByGroupName(const QString& groupName)
+{
+ return notifyListByGroupName(groupName).join(" ");
+}
+
+bool Preferences::addNotify(int serverGroupId, const QString& newPattern)
+{
+ if (!self()->mNotifyList[serverGroupId].contains(newPattern))
+ {
+ QStringList nicknameList = self()->mNotifyList[serverGroupId];
+ nicknameList.append(newPattern);
+ self()->mNotifyList[serverGroupId] = nicknameList;
+ return true;
+ }
+ return false;
+}
+
+bool Preferences::removeNotify(const QString& groupName, const QString& pattern)
+{
+ int id=serverGroupIdByName(groupName);
+ if(!id) return false;
+
+ if (self()->mNotifyList.find(id) != self()->mNotifyList.end())
+ {
+ QStringList nicknameList = self()->mNotifyList[id];
+ nicknameList.remove(pattern);
+ if (nicknameList.isEmpty())
+ self()->mNotifyList.remove(id);
+ else
+ self()->mNotifyList[id] = nicknameList;
+ return true;
+ }
+ return false;
+}
+
+bool Preferences::isNotify(int serverGroupId, const QString& pattern)
+{
+ if (self()->mNotifyList.find(serverGroupId) != self()->mNotifyList.end())
+ {
+ QStringList nicknameList = self()->mNotifyList[serverGroupId];
+
+ if (nicknameList.contains(pattern)) return true;
+ }
+ return false;
+}
+
+bool Preferences::hasNotifyList(int serverGroupId)
+{
+ if (self()->mNotifyList.find(serverGroupId) != self()->mNotifyList.end())
+ return true;
+ else
+ return false;
+}
+
+// Default identity functions
+void Preferences::addIdentity(IdentityPtr identity) { self()->mIdentityList.append(identity); }
+void Preferences::removeIdentity(IdentityPtr identity) { self()->mIdentityList.remove(identity); }
+
+void Preferences::clearIdentityList()
+{
+ self()->mIdentityList.clear();
+}
+
+const IdentityList Preferences::identityList() { return self()->mIdentityList; }
+
+void Preferences::setIdentityList(const IdentityList& list)
+{
+ self()->mIdentityList.clear();
+ self()->mIdentityList = list;
+}
+
+const IdentityPtr Preferences::identityByName(const QString& name)
+{
+ QValueList<IdentityPtr> identities = identityList();
+ QValueList<IdentityPtr>::iterator it = identities.begin();
+
+ while(it != identities.end())
+ {
+ if((*it)->getName() == name)
+ {
+ return (*it);
+ }
+
+ ++it;
+ }
+
+ // no self()->matching identity found, return default identity
+ return identities.first();
+}
+
+const IdentityPtr Preferences::identityById(int id)
+{
+ QValueList<IdentityPtr> identList = identityList();
+ for(QValueList<IdentityPtr>::iterator it = identList.begin(); it != identList.end(); ++it)
+ {
+ if((*it)->id() == id)
+ {
+ return (*it);
+ }
+ }
+
+ return identList.first();
+}
+
+QStringList Preferences::defaultAliasList()
+{
+ // Auto-alias scripts
+ QStringList scripts = KGlobal::dirs()->findAllResources("data","konversation/scripts/*");
+ QFileInfo* fileInfo = new QFileInfo();
+ QStringList aliasList;
+ QString newAlias;
+
+ for (QStringList::ConstIterator it = scripts.begin(); it != scripts.end(); ++it)
+ {
+ fileInfo->setFile(*it);
+ if (fileInfo->isExecutable())
+ {
+ newAlias = (*it).section('/',-1)+' '+"/exec "+(*it).section('/', -1 );
+ aliasList.append(newAlias);
+
+ // FIXME: Historically, defaultAliasList() is primarily used to dynamically
+ // compile a list of installed scripts and generate appropriate aliases for
+ // them. It's not only used when the alias preferences are reset or initia-
+ // lized, but also on application start. The following crudely adds two
+ // aliases when the 'media' script is found, to provide easy access to its
+ // capability to differentiate between audio and video media. This method
+ // needs at the very least to be split up in two, or scripts may in the
+ // future determine what aliases they want to add.
+ if ((*it).section('/',-1) == "media")
+ {
+ aliasList.append("audio /exec media audio");
+ aliasList.append("video /exec media video");
+ }
+ }
+ }
+
+ delete fileInfo;
+
+ return aliasList;
+}
+
+
+const QString Preferences::realName() { return self()->mIdentityList[0]->getRealName(); }
+void Preferences::setRealName(const QString &name) { self()->mIdentityList[0]->setRealName(name); }
+
+const QString Preferences::ident() { return self()->mIdentityList[0]->getIdent(); }
+void Preferences::setIdent(const QString &ident) { self()->mIdentityList[0]->setIdent(ident); }
+
+const QString Preferences::partReason() { return self()->mIdentityList[0]->getPartReason(); }
+void Preferences::setPartReason(const QString &newReason) { self()->mIdentityList[0]->setPartReason(newReason); }
+
+const QString Preferences::kickReason() { return self()->mIdentityList[0]->getKickReason(); }
+void Preferences::setKickReason(const QString &newReason) { self()->mIdentityList[0]->setKickReason(newReason); }
+
+bool Preferences::showAwayMessage() { return self()->mIdentityList[0]->getShowAwayMessage(); }
+void Preferences::setShowAwayMessage(bool state) { self()->mIdentityList[0]->setShowAwayMessage(state); }
+
+const QString Preferences::awayMessage() { return self()->mIdentityList[0]->getAwayMessage(); }
+void Preferences::setAwayMessage(const QString &newMessage) { self()->mIdentityList[0]->setAwayMessage(newMessage); }
+const QString Preferences::unAwayMessage() { return self()->mIdentityList[0]->getReturnMessage(); }
+void Preferences::setUnAwayMessage(const QString &newMessage) { self()->mIdentityList[0]->setReturnMessage(newMessage); }
+
+void Preferences::clearIgnoreList() { self()->mIgnoreList.clear(); }
+const QPtrList<Ignore> Preferences::ignoreList() { return self()->mIgnoreList; }
+
+const QString Preferences::nickname(int index) { return self()->mIdentityList[0]->getNickname(index); }
+const QStringList Preferences::nicknameList() { return self()->mIdentityList[0]->getNicknameList(); }
+void Preferences::setNickname(int index,const QString &newName) { self()->mIdentityList[0]->setNickname(index,newName); }
+void Preferences::setNicknameList(const QStringList &newList) { self()->mIdentityList[0]->setNicknameList(newList); }
+
+void Preferences::setShowTrayIcon(bool state)
+{
+ PreferencesBase::setShowTrayIcon(state);
+ emit self()->updateTrayIcon();
+}
+
+void Preferences::setTrayNotify(bool state)
+{
+ PreferencesBase::setTrayNotify(state);
+ emit self()->updateTrayIcon();
+}
+
+
+void Preferences::setAutoUserhost(bool state)
+{
+ PreferencesBase::setAutoUserhost(state);
+}
+
+bool Preferences::dialogFlag(const QString& flagName)
+{
+ KConfig* config=KApplication::kApplication()->config();
+
+ config->setGroup("Notification self()->Messages");
+
+ if( !config->readEntry(flagName).isEmpty() )
+ return false;
+ else
+ return true;
+}
+
+void Preferences::setDialogFlag(const QString& flagName,bool state)
+{
+ KConfig* config=KApplication::kApplication()->config();
+
+ config->setGroup("Notification self()->Messages");
+
+ if(state)
+ config->deleteEntry(flagName);
+ else
+ {
+ if ( config->readEntry(flagName).isEmpty() )
+ config->writeEntry(flagName,"no");
+ }
+
+ config->sync();
+}
+
+
+// Channel Encodings
+const QString Preferences::channelEncoding(const QString& server,const QString& channel)
+{
+ return channelEncoding(serverGroupIdByName(server),channel);
+}
+
+const QString Preferences::channelEncoding(int serverGroupId,const QString& channel)
+{
+ if(self()->mChannelEncodingsMap.contains(serverGroupId))
+ if(self()->mChannelEncodingsMap[serverGroupId].contains(channel.lower()))
+ return self()->mChannelEncodingsMap[serverGroupId][channel.lower()];
+ return QString();
+}
+
+void Preferences::setChannelEncoding(const QString& server,const QString& channel,const QString& encoding)
+{
+ setChannelEncoding(serverGroupIdByName(server),channel,encoding);
+}
+
+void Preferences::setChannelEncoding(int serverGroupId,const QString& channel,const QString& encoding)
+{
+ self()->mChannelEncodingsMap[serverGroupId][channel.lower()]=encoding;
+}
+
+const QValueList<int> Preferences::channelEncodingsServerGroupIdList()
+{
+ return self()->mChannelEncodingsMap.keys();
+}
+
+const QStringList Preferences::channelEncodingsChannelList(int serverGroupId)
+{
+ return self()->mChannelEncodingsMap[serverGroupId].keys();
+}
+
+const QString Preferences::defaultNicknameSortingOrder()
+{
+ return "qpohv-";
+}
+
+// override to add %u if needed
+QString Preferences::webBrowserCmd()
+{
+ // add %u to command if it's not in there
+ QString cmd=self()->mWebBrowserCmd;
+ if(cmd.find("%u")==-1) cmd+=" %u";
+ return cmd;
+}
+
+#include "preferences.moc"
diff --git a/konversation/src/config/preferences.h b/konversation/src/config/preferences.h
new file mode 100644
index 0000000..ed27d0a
--- /dev/null
+++ b/konversation/src/config/preferences.h
@@ -0,0 +1,169 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef PREFERENCES_H
+#define PREFERENCES_H
+
+#include "servergroupsettings.h"
+#include "identity.h"
+#include "preferences_base.h"
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qsize.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <qfont.h>
+#include <qcolor.h>
+#include <qmap.h>
+
+#include <kdeversion.h>
+
+
+/*
+Options still to be GUIfied:
+
+Operator LEDs (int)
+OperatorColor (int)
+VoiceColor (int)
+NoRightsColor (int)
+*/
+
+class Ignore;
+class Highlight;
+
+class Preferences : public QObject, public PreferencesBase
+{
+ Q_OBJECT
+
+ protected:
+ Preferences();
+ static Preferences *mSelf;
+
+ public:
+
+ static Preferences *self();
+ ~Preferences();
+ enum Pages
+ {
+ NotifyPage,
+ ChatWinAppearancePage
+ };
+ static const Konversation::ServerGroupList serverGroupList();
+ static void setServerGroupList(const Konversation::ServerGroupList& list);
+ static void addServerGroup(Konversation::ServerGroupSettingsPtr serverGroup);
+ static const Konversation::ServerGroupSettingsPtr serverGroupById(int id);
+ static const Konversation::ServerGroupSettingsPtr serverGroupByServer(const QString& server);
+ static int serverGroupIdByName(const QString& serverGroup);
+ static bool isServerGroup(const QString& server);
+ static void removeServerGroup(int id);
+
+ /** Returns a list of alias set up by default. This is a set of aliases for the scripts found. */
+ static QStringList defaultAliasList();
+
+ //notifylist is in kconfigxt - FIXME
+ static const QMap<int, QStringList> notifyList();
+ static const QStringList notifyListByGroupName(const QString& groupName);
+ static const QString notifyStringByGroupName(const QString& groupName);
+ static void setNotifyList(const QMap<int, QStringList>& newList);
+ static bool addNotify(int serverGroupId, const QString& newPattern);
+ static bool removeNotify(const QString& groupName, const QString& pattern);
+ static bool isNotify(int serverGroupId, const QString& pattern);
+ static bool hasNotifyList(int serverGroupId);
+
+ static const QPtrList<Highlight> highlightList();
+ static void setHighlightList(QPtrList<Highlight> newList);
+ static void addHighlight(const QString& newHighlight,bool regExp, const QColor &color,const QString& sound,const QString& autoText);
+
+ /* All of the below work on the first (default) identity in your identity list*/
+ static void addIgnore(const QString &newIgnore);
+ static bool removeIgnore(const QString &oldIgnore);
+ static bool isIgnored(const QString &nickname);
+ static void clearIgnoreList();
+ static const QPtrList<Ignore> ignoreList();
+ static void setIgnoreList(QPtrList<Ignore> newList);
+
+ static const QStringList quickButtonList();
+ static const QStringList defaultQuickButtonList();
+ static void setQuickButtonList(const QStringList newList);
+ static void clearQuickButtonList();
+
+ static const QStringList autoreplaceList();
+ static const QStringList defaultAutoreplaceList();
+ static void setAutoreplaceList(const QStringList newList);
+ static void clearAutoreplaceList();
+
+ static void addIdentity(IdentityPtr identity);
+ static void removeIdentity(IdentityPtr identity);
+ static void clearIdentityList();
+ static const IdentityList identityList();
+ static void setIdentityList(const IdentityList& list);
+ static const IdentityPtr identityByName(const QString& name);
+ static const IdentityPtr identityById(int id);
+ static const QString ident();
+ static void setIdent(const QString &ident);
+ static const QString realName();
+ static void setRealName(const QString &name);
+ static const QString partReason();
+ static void setPartReason(const QString &newReason);
+ static const QString kickReason();
+ static void setKickReason(const QString &newReason);
+ static void setShowAwayMessage(bool state);
+ static bool showAwayMessage();
+ static const QString awayMessage();
+ static void setAwayMessage(const QString &newMessage);
+ static const QString unAwayMessage();
+ static void setUnAwayMessage(const QString &newMessage);
+ static const QString defaultNicknameSortingOrder();
+ static const QString nickname(int index);
+ static const QStringList nicknameList();
+ static void setNickname(int index,const QString &newName);
+ static void setNicknameList(const QStringList &newList);
+
+ static bool dialogFlag(const QString& flagName);
+ static void setDialogFlag(const QString& flagName,bool state);
+
+ static const QString channelEncoding(const QString& server,const QString& channel);
+ static const QString channelEncoding(int serverGroupId,const QString& channel);
+ static void setChannelEncoding(const QString& server,const QString& channel,const QString& encoding);
+ static void setChannelEncoding(int serverGroupId,const QString& channel,const QString& encoding);
+ static const QValueList<int> channelEncodingsServerGroupIdList();
+ static const QStringList channelEncodingsChannelList(int serverGroupId);
+
+ static void setShowTrayIcon(bool state);
+ static void setTrayNotify(bool state);
+ static void setAutoUserhost(bool state);
+
+ static QString webBrowserCmd();
+
+ signals:
+ void requestServerConnection(int number);
+ void requestSaveOptions();
+ void autoContinuousWhoChanged();
+ void updateTrayIcon();
+
+ protected:
+ IdentityPtr mIdentity;
+ Konversation::ServerGroupList mServerGroupList;
+ QPtrList<Ignore> mIgnoreList;
+ QValueList<IdentityPtr> mIdentityList;
+ QPtrList<Highlight> mHighlightList;
+ QMap<int, QStringList> mNotifyList; // network id, list of nicks
+ QMap< int,QMap<QString,QString> > mChannelEncodingsMap; // mChannelEncodingsMap[serverGroupdId][channelName]
+ QStringList mQuickButtonList;
+ QStringList mAutoreplaceList;
+ QString mSortingOrder;
+};
+#endif
diff --git a/konversation/src/config/preferences_base.kcfgc b/konversation/src/config/preferences_base.kcfgc
new file mode 100644
index 0000000..a728d44
--- /dev/null
+++ b/konversation/src/config/preferences_base.kcfgc
@@ -0,0 +1,8 @@
+ClassName=PreferencesBase
+File=konversation.kcfg
+GlobalEnums=true
+Mutators=true
+SetUserTexts=true
+Singleton=true
+MemberVariables=private
+ItemAccessors=true
diff --git a/konversation/src/connectionbehavior_preferences.ui b/konversation/src/connectionbehavior_preferences.ui
new file mode 100644
index 0000000..c1489ca
--- /dev/null
+++ b/konversation/src/connectionbehavior_preferences.ui
@@ -0,0 +1,187 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ConnectionBehavior_Config</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</comment>
+<author>Copyright (C) 2005 Peter Simonsson</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConnectionBehavior_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>421</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_AutoReconnect</cstring>
+ </property>
+ <property name="title">
+ <string>Enable Automatic Reconnect</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>reconnectDelayLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Reconnect delay:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>reconnectDelaySpin</cstring>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer53</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>278</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Reconnection attempts:</string>
+ </property>
+ </widget>
+ <widget class="KIntNumInput" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_ReconnectDelay</cstring>
+ </property>
+ <property name="label">
+ <string></string>
+ </property>
+ <property name="value">
+ <number>120</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>9999</number>
+ </property>
+ <property name="suffix">
+ <string> seconds</string>
+ </property>
+ </widget>
+ <widget class="KIntNumInput" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_ReconnectCount</cstring>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="specialValueText">
+ <string>Unlimited</string>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer53_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>280</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer54</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>60</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>debugGBox</cstring>
+ </property>
+ <property name="title">
+ <string>Debug</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_RawLog</cstring>
+ </property>
+ <property name="text">
+ <string>Show raw &amp;log window when connecting</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>kcfg_AutoReconnect</tabstop>
+ <tabstop>kcfg_ReconnectDelay</tabstop>
+ <tabstop>kcfg_ReconnectCount</tabstop>
+ <tabstop>kcfg_RawLog</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/connectionmanager.cpp b/konversation/src/connectionmanager.cpp
new file mode 100644
index 0000000..bb491c3
--- /dev/null
+++ b/konversation/src/connectionmanager.cpp
@@ -0,0 +1,580 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#include "connectionmanager.h"
+#include "connectionsettings.h"
+#include "serversettings.h"
+#include "servergroupsettings.h"
+#include "config/preferences.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "statuspanel.h"
+
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+
+ConnectionManager::ConnectionManager(QObject* parent) : QObject(parent)
+{
+ connect(this, SIGNAL(requestReconnect(Server*)), this, SLOT(handleReconnect(Server*)));
+}
+
+ConnectionManager::~ConnectionManager()
+{
+}
+
+void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, const QString& target,
+ const QString& port, const QString& password, const QString& nick, const QString& channel,
+ bool useSSL)
+{
+ ConnectionSettings settings;
+
+ if (target.startsWith("irc://"))
+ decodeIrcUrl(target, settings);
+ else
+ {
+ decodeAddress(target, settings);
+
+ Konversation::ServerSettings server = settings.server();
+
+ if (!port.isEmpty()) server.setPort(port.toInt());
+
+ if (!password.isEmpty()) server.setPassword(password);
+
+ if (useSSL) server.setSSLEnabled(true);
+
+ settings.setServer(server);
+
+ if (!nick.isEmpty()) settings.setInitialNick(nick);
+
+ if (!channel.isEmpty())
+ {
+ Konversation::ChannelSettings channelSettings(channel);
+
+ settings.setInitialChannel(channelSettings);
+ }
+ }
+
+ connectTo(flag, settings);
+}
+
+void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, int serverGroupId)
+{
+ ConnectionSettings settings;
+
+ Konversation::ServerGroupSettingsPtr serverGroup;
+
+ serverGroup = Preferences::serverGroupById(serverGroupId);
+
+ if (serverGroup)
+ {
+ settings.setServerGroup(serverGroup);
+
+ if (serverGroup->serverList().size() > 0)
+ settings.setServer(serverGroup->serverList()[0]);
+ }
+
+ connectTo(flag, settings);
+}
+
+void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, ConnectionSettings& settings)
+{
+ if (!settings.isValid()) return;
+
+ emit closeServerList();
+
+ if (flag != Konversation::CreateNewConnection
+ && reuseExistingConnection(settings, (flag == Konversation::PromptToReuseConnection)))
+ {
+ return;
+ }
+
+ IdentityPtr identity = settings.identity();
+
+ if (!identity || !validateIdentity(identity)) return;
+
+ KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
+ KonversationMainWindow* mainWindow = konvApp->getMainWindow();
+
+ Server* server = new Server(this, settings);
+
+ enlistConnection(server->connectionId(), server);
+
+ connect(server, SIGNAL(destroyed(int)), this, SLOT(delistConnection(int)));
+
+ connect(server, SIGNAL(connectionStateChanged(Server*, Konversation::ConnectionState)),
+ this, SLOT(handleConnectionStateChange(Server*, Konversation::ConnectionState)));
+
+ connect(server, SIGNAL(awayState(bool)), this, SIGNAL(connectionChangedAwayState(bool)));
+
+ connect(server, SIGNAL(nicksNowOnline(Server*, const QStringList&, bool)),
+ mainWindow, SLOT(setOnlineList(Server*, const QStringList&,bool)));
+ connect(server, SIGNAL(awayInsertRememberLine(Server*)),
+ mainWindow, SIGNAL(triggerRememberLines(Server*)));
+
+ connect(mainWindow, SIGNAL(startNotifyTimer(int)), server, SLOT(startNotifyTimer(int)));
+
+ connect(server, SIGNAL(multiServerCommand(const QString&, const QString&)),
+ konvApp, SLOT(sendMultiServerCommand(const QString&, const QString&)));
+}
+
+void ConnectionManager::enlistConnection(int connectionId, Server* server)
+{
+ m_connectionList.insert(connectionId, server);
+}
+
+void ConnectionManager::delistConnection(int connectionId)
+{
+ m_connectionList.remove(connectionId);
+}
+
+void ConnectionManager::handleConnectionStateChange(Server* server, Konversation::ConnectionState state)
+{
+ emit connectionChangedState(server, state);
+
+ int identityId = server->getIdentity()->id();
+
+ if (state == Konversation::SSConnected)
+ {
+ if (!m_activeIdentities.contains(identityId))
+ {
+ m_activeIdentities.append(identityId);
+
+ emit identityOnline(identityId);
+ }
+ }
+ else if (state != Konversation::SSConnecting)
+ {
+ if (m_activeIdentities.contains(identityId))
+ {
+ m_activeIdentities.remove(identityId);
+
+ emit identityOffline(identityId);
+ }
+ }
+
+ if (state == Konversation::SSInvoluntarilyDisconnected)
+ {
+ // The asynchronous invocation of handleReconnect() makes sure that
+ // connectionChangedState() is emitted and delivered before it runs
+ // (and causes the next connection state change to occur).
+ emit requestReconnect(server);
+ }
+}
+
+void ConnectionManager::handleReconnect(Server* server)
+{
+ if (!Preferences::autoReconnect()) return;
+
+ ConnectionSettings settings = server->getConnectionSettings();
+
+ uint reconnectCount = Preferences::reconnectCount();
+
+ // For server groups, one iteration over their server list shall count as one
+ // connection attempt.
+ if (settings.serverGroup())
+ reconnectCount = reconnectCount * settings.serverGroup()->serverList().size();
+
+ if (reconnectCount == 0 || settings.reconnectCount() < reconnectCount)
+ {
+ if (settings.serverGroup() && settings.serverGroup()->serverList().size() > 1)
+ {
+ Konversation::ServerList serverList = settings.serverGroup()->serverList();
+
+ int index = serverList.findIndex(settings.server());
+ int size = serverList.size();
+
+ if (index == size - 1 || index == -1)
+ settings.setServer(serverList[0]);
+ else if (index < size - 1)
+ settings.setServer(serverList[index+1]);
+
+ server->setConnectionSettings(settings);
+
+ server->getStatusView()->appendServerMessage(i18n("Info"),
+ i18n("Trying to connect to %1 in %2 seconds.")
+ .arg(settings.server().host())
+ .arg(Preferences::reconnectDelay()));
+ }
+ else
+ {
+ server->getStatusView()->appendServerMessage(i18n("Info"),
+ i18n("Trying to reconnect to %1 in %2 seconds.")
+ .arg(settings.server().host())
+ .arg(Preferences::reconnectDelay()));
+ }
+
+ server->getConnectionSettings().incrementReconnectCount();
+
+ QTimer::singleShot(Preferences::reconnectDelay() * 1000, server, SLOT(connectToIRCServer()));
+ }
+ else
+ server->getStatusView()->appendServerMessage(i18n("Error"), i18n("Reconnection attempts exceeded."));
+}
+
+void ConnectionManager::quitServers()
+{
+ QMap<int, Server*>::ConstIterator it;
+
+ for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
+ it.data()->quitServer();
+}
+
+void ConnectionManager::decodeIrcUrl(const QString& url, ConnectionSettings& settings)
+{
+ if (!url.startsWith("irc://")) return;
+
+ QString mangledUrl = url;
+
+ mangledUrl.remove(QRegExp("^irc:/+"));
+
+ if (mangledUrl.isEmpty()) return;
+
+ // Parsing address and channel.
+ QStringList mangledUrlSegments;
+
+ mangledUrlSegments = QStringList::split('/', mangledUrl, false);
+
+ // Check for ",isserver".
+ if (mangledUrlSegments[0].contains(','))
+ {
+ QStringList addressSegments;
+ bool checkIfServerGroup = true;
+
+ addressSegments = QStringList::split(',', mangledUrlSegments[0], false);
+
+ if (addressSegments.grep("isserver", false).size() > 0)
+ checkIfServerGroup = false;
+
+ decodeAddress(addressSegments[0], settings, checkIfServerGroup);
+ }
+ else
+ decodeAddress(mangledUrlSegments[0], settings);
+
+ QString channel;
+ Konversation::ChannelSettings channelSettings;
+
+ // Grabbing channel from in front of potential ?key=value parameters.
+ if (mangledUrlSegments.size() > 1)
+ channel = mangledUrlSegments[1].section('?', 0, 0);
+
+ if (!channel.isEmpty())
+ {
+ // Add default prefix to channel if necessary.
+ if (!channel.contains(QRegExp("^[#+&]{1}")))
+ channel = '#' + channel;
+
+ channelSettings.setName(channel);
+ }
+
+ // Parsing ?key=value parameters.
+ QString parameterString;
+
+ if (mangledUrlSegments.size() > 1)
+ parameterString = mangledUrlSegments[1].section('?', 1);
+
+ if (parameterString.isEmpty() && mangledUrlSegments.size() > 2)
+ parameterString = mangledUrlSegments[2];
+
+ if (!parameterString.isEmpty())
+ {
+ QRegExp parameterCatcher;
+
+ parameterCatcher.setPattern("pass=([^&]+)");
+
+ if (parameterCatcher.search(parameterString) != -1)
+ {
+ Konversation::ServerSettings server = settings.server();
+
+ server.setPassword(parameterCatcher.cap(1));
+
+ settings.setServer(server);
+ }
+
+ parameterCatcher.setPattern("key=([^&]+)");
+
+ if (parameterCatcher.search(parameterString) != -1)
+ channelSettings.setPassword(parameterCatcher.cap(1));
+ }
+
+ // Assigning channel.
+ if (!channelSettings.name().isEmpty())
+ settings.setInitialChannel(channelSettings);
+}
+
+void ConnectionManager::decodeAddress(const QString& address, ConnectionSettings& settings,
+ bool checkIfServerGroup)
+{
+ QString host;
+ QString port = "6667";
+
+ // Full-length IPv6 address with port
+ // Example: RFC 2732 notation: [2001:0DB8:0000:0000:0000:0000:1428:57ab]:6666
+ // Example: Non-RFC 2732 notation: 2001:0DB8:0000:0000:0000:0000:1428:57ab:6666
+ if (address.contains(':')==8)
+ {
+ host = address.section(':',0,-2).remove("[").remove("]");
+ port = address.section(':',-1);
+ }
+ // Full-length IPv6 address without port or not-full-length IPv6 address with port
+ // Example: Without port, RFC 2732 notation: [2001:0DB8:0000:0000:0000:0000:1428:57ab]
+ // Example: Without port, Non-RFC 2732 notation: 2001:0DB8:0000:0000:0000:0000:1428:57ab
+ // Example: With port, RFC 2732 notation: [2001:0DB8::1428:57ab]:6666
+ else if (address.contains(':')>=4)
+ {
+ // Last segment does not end with ], but the next to last does;
+ // Assume not-full-length IPv6 address with port
+ // Example: [2001:0DB8::1428:57ab]:6666
+ if (address.section(':',0,-2).endsWith("]") && !address.section(':',-1).endsWith("]"))
+ {
+ host = address.section(':',0,-2).remove("[").remove("]");
+ port = address.section(':',-1);
+ }
+ else
+ {
+ QString addressCopy = address;
+ host = addressCopy.remove("[").remove("]");
+ }
+ }
+ // IPv4 address or ordinary hostname with port
+ // Example: IPv4 address with port: 123.123.123.123:6666
+ // Example: Hostname with port: irc.bla.org:6666
+ else if (address.contains(':')==1)
+ {
+ host = address.section(':',0,-2);
+ port = address.section(':',-1);
+ }
+ else
+ host = address;
+
+ // Try to assign server group.
+ if (checkIfServerGroup && Preferences::isServerGroup(host))
+ {
+ // If host is found to be the name of a server group.
+
+ int serverGroupId = Preferences::serverGroupIdByName(host);
+
+ Konversation::ServerGroupSettingsPtr serverGroup;
+
+ serverGroup = Preferences::serverGroupById(serverGroupId);
+
+ settings.setServerGroup(serverGroup);
+
+ if (serverGroup->serverList().size() > 0)
+ settings.setServer(serverGroup->serverList()[0]);
+ }
+ else
+ {
+ if (Preferences::serverGroupByServer(host))
+ {
+ // If the host is found to be part of a server group's server list.
+
+ Konversation::ServerGroupSettingsPtr serverGroup;
+
+ serverGroup = Preferences::serverGroupByServer(host);
+
+ settings.setServerGroup(serverGroup);
+ }
+
+ Konversation::ServerSettings server;
+
+ server.setHost(host);
+ server.setPort(port.toInt());
+
+ settings.setServer(server);
+ }
+}
+
+bool ConnectionManager::reuseExistingConnection(ConnectionSettings& settings, bool interactive)
+{
+ Server* dupe = 0;
+ ConnectionDupe dupeType;
+ bool doReuse = true;
+
+ KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
+ KonversationMainWindow* mainWindow = konvApp->getMainWindow();
+
+ QMap<int, Server*>::ConstIterator it;
+
+ for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
+ {
+ if (it.data()->getServerGroup() && settings.serverGroup()
+ && it.data()->getServerGroup() == settings.serverGroup())
+ {
+ dupe = it.data();
+ dupeType = SameServerGroup;
+
+ break;
+ }
+ }
+
+ if (!dupe)
+ {
+ for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
+ {
+ if (it.data()->getConnectionSettings().server() == settings.server())
+ {
+ dupe = it.data();
+ dupeType = SameServer;
+
+ break;
+ }
+ }
+ }
+
+ if (dupe && interactive)
+ {
+ int result = KMessageBox::warningContinueCancel(
+ mainWindow,
+ i18n("You are already connected to %1. Do you want to open another connection?")
+ .arg(dupe->getDisplayName()),
+ i18n("Already connected to %1").arg(dupe->getDisplayName()),
+ i18n("Create connection"),
+ "ReuseExistingConnection");
+
+ if (result == KMessageBox::Continue) doReuse = false;
+ }
+
+ if (dupe && doReuse)
+ {
+ if (interactive && dupeType == SameServerGroup
+ && !(dupe->getConnectionSettings().server() == settings.server()))
+ {
+ int result = KMessageBox::warningContinueCancel(
+ mainWindow,
+ i18n("You are presently connected to %1 via '%2' (port %3). Do you want to switch to '%4' (port %5) instead?")
+ .arg(dupe->getDisplayName())
+ .arg(dupe->getServerName())
+ .arg(dupe->getPort())
+ .arg(settings.server().host())
+ .arg(settings.server().port()),
+ i18n("Already connected to %1").arg(dupe->getDisplayName()),
+ i18n("Switch Server"),
+ "ReconnectWithDifferentServer");
+
+ if (result == KMessageBox::Continue)
+ {
+ dupe->disconnect();
+
+ dupe->setConnectionSettings(settings);
+ }
+ }
+
+ if (!dupe->isConnected())
+ {
+ if (!settings.initialChannel().name().isEmpty())
+ dupe->updateAutoJoin(settings.initialChannel());
+
+ if (!dupe->isConnecting())
+ dupe->reconnect();
+ }
+ else
+ {
+ if (!settings.initialChannel().name().isEmpty())
+ {
+ dupe->sendJoinCommand(settings.initialChannel().name(),
+ settings.initialChannel().password());
+ }
+ }
+ }
+
+ return (dupe && doReuse);
+}
+
+bool ConnectionManager::validateIdentity(IdentityPtr identity, bool interactive)
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
+ KonversationMainWindow* mainWindow = konvApp->getMainWindow();
+
+ QString errors;
+
+ if (identity->getIdent().isEmpty())
+ errors+=i18n("Please fill in your <b>Ident</b>.<br>");
+
+ if (identity->getRealName().isEmpty())
+ errors+=i18n("Please fill in your <b>Real name</b>.<br>");
+
+ if (identity->getNickname(0).isEmpty())
+ errors+=i18n("Please provide at least one <b>Nickname</b>.<br>");
+
+ if (!errors.isEmpty())
+ {
+ if (interactive)
+ {
+ int result = KMessageBox::warningContinueCancel(mainWindow,
+ i18n("<qt>Your identity \"%1\" is not set up correctly:<br>%2</qt>")
+ .arg(identity->getName()).arg(errors),
+ i18n("Identity Settings"),
+ i18n("Edit Identity..."));
+
+ if (result == KMessageBox::Continue)
+ {
+ identity = mainWindow->editIdentity(identity);
+
+ if (identity && validateIdentity(identity, false))
+ return true;
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+QPtrList<Server> ConnectionManager::getServerList()
+{
+ QPtrList<Server> serverList;
+
+ QMap<int, Server*>::ConstIterator it;
+
+ for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
+ serverList.append(it.data());
+
+ return serverList;
+}
+
+Server* ConnectionManager::getServerByConnectionId(int connectionId)
+{
+ if (m_connectionList.contains(connectionId))
+ return m_connectionList[connectionId];
+ else
+ return 0;
+}
+
+Server* ConnectionManager::getServerByName(const QString& name)
+{
+ QMap<int, Server*>::ConstIterator it;
+
+ for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
+ {
+ if (it.data()->getDisplayName() == name || it.data()->getServerName() == name)
+ return it.data();
+ }
+
+ return 0;
+}
+
+Server* ConnectionManager::getAnyServer()
+{
+ if ( m_connectionList.count() > 0)
+ return m_connectionList[0];
+
+ return 0;
+}
+
+#include "connectionmanager.moc"
diff --git a/konversation/src/connectionmanager.h b/konversation/src/connectionmanager.h
new file mode 100644
index 0000000..9ada279
--- /dev/null
+++ b/konversation/src/connectionmanager.h
@@ -0,0 +1,95 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef CONNECTIONMANAGER_H
+#define CONNECTIONMANAGER_H
+
+#include "server.h"
+#include "identity.h"
+
+#include <qobject.h>
+
+
+class ConnectionSettings;
+
+class ConnectionManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit ConnectionManager(QObject* parent = 0);
+ ~ConnectionManager();
+
+ uint connectionCount() const { return m_connectionList.count(); }
+
+ QPtrList<Server> getServerList();
+
+ Server* getServerByConnectionId(int connectionId);
+ Server* getServerByName(const QString& name);
+ Server* getAnyServer();
+
+ void quitServers();
+ void toggleGlobalAway();
+
+
+ public slots:
+ void connectTo(Konversation::ConnectionFlag flag,
+ const QString& target,
+ const QString& port = "",
+ const QString& password = "",
+ const QString& nick = "",
+ const QString& channel = "",
+ bool useSSL = false);
+
+ void connectTo(Konversation::ConnectionFlag flag, int serverGroupId);
+ void connectTo(Konversation::ConnectionFlag flag, ConnectionSettings& settings);
+
+
+ signals:
+ void connectionChangedState(Server* server, Konversation::ConnectionState state);
+
+ void connectionChangedAwayState(bool away);
+
+ void requestReconnect(Server* server);
+
+ void identityOnline(int identityId);
+ void identityOffline(int identityId);
+
+ void closeServerList();
+
+
+ private slots:
+ void delistConnection(int connectionId);
+
+ void handleConnectionStateChange(Server* server, Konversation::ConnectionState state);
+
+ void handleReconnect(Server* server);
+
+
+ private:
+ void enlistConnection(int connectionId, Server* server);
+
+ void decodeIrcUrl(const QString& url, ConnectionSettings& settings);
+
+ void decodeAddress(const QString& address,
+ ConnectionSettings& settings,
+ bool checkIfServerGroup = true);
+
+ bool reuseExistingConnection(ConnectionSettings& settings, bool interactive);
+ bool validateIdentity(IdentityPtr identity, bool interactive = true);
+
+ QMap<int, Server*> m_connectionList;
+ QValueList<uint> m_activeIdentities;
+
+ enum ConnectionDupe { SameServer, SameServerGroup };
+};
+
+#endif
diff --git a/konversation/src/connectionsettings.cpp b/konversation/src/connectionsettings.cpp
new file mode 100644
index 0000000..f835406
--- /dev/null
+++ b/konversation/src/connectionsettings.cpp
@@ -0,0 +1,55 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#include "connectionsettings.h"
+#include "config/preferences.h"
+
+
+ConnectionSettings::ConnectionSettings()
+{
+ m_reconnectCount = 0;
+}
+
+ConnectionSettings::~ConnectionSettings()
+{
+}
+
+bool ConnectionSettings::isValid()
+{
+ if (m_server.host().isEmpty()) return false;
+
+ return true;
+}
+
+QString ConnectionSettings::name() const
+{
+ if (m_serverGroup)
+ return m_serverGroup->name();
+ else
+ return m_server.host();
+}
+
+IdentityPtr ConnectionSettings::identity() const
+{
+ if (m_serverGroup)
+ return m_serverGroup->identity();
+
+ return Preferences::identityById(0);
+}
+
+QString ConnectionSettings::initialNick() const
+{
+ if (!m_initialNick.isEmpty())
+ return m_initialNick;
+
+ return identity()->getNickname(0);
+}
+
diff --git a/konversation/src/connectionsettings.h b/konversation/src/connectionsettings.h
new file mode 100644
index 0000000..caa01ce
--- /dev/null
+++ b/konversation/src/connectionsettings.h
@@ -0,0 +1,61 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef CONNECTIONSETTINGS_H
+#define CONNECTIONSETTINGS_H
+
+#include "servergroupsettings.h"
+#include "serversettings.h"
+
+#include <qstring.h>
+
+
+class ConnectionSettings
+{
+ public:
+ explicit ConnectionSettings();
+ ~ConnectionSettings();
+
+ bool isValid();
+
+ QString name() const;
+
+ Konversation::ServerSettings server() const { return m_server; }
+ void setServer(Konversation::ServerSettings server) { m_server = server; }
+
+ Konversation::ServerGroupSettingsPtr serverGroup() const { return m_serverGroup; }
+ void setServerGroup(Konversation::ServerGroupSettingsPtr serverGroup) { m_serverGroup = serverGroup; }
+
+ IdentityPtr identity() const;
+
+ QString initialNick() const;
+ void setInitialNick(const QString& nick) { m_initialNick = nick; }
+
+ Konversation::ChannelSettings initialChannel() const { return m_initialChannel; }
+ void setInitialChannel(Konversation::ChannelSettings& channel) { m_initialChannel = channel; }
+
+ uint reconnectCount() const { return m_reconnectCount; }
+ void incrementReconnectCount() { m_reconnectCount++; }
+ void setReconnectCount(uint count) { m_reconnectCount = count; }
+
+
+ private:
+ Konversation::ServerSettings m_server;
+ Konversation::ServerGroupSettingsPtr m_serverGroup;
+
+ QString m_initialNick;
+ Konversation::ChannelSettings m_initialChannel;
+
+ uint m_reconnectCount;
+};
+
+
+#endif
diff --git a/konversation/src/dcc_preferences.cpp b/konversation/src/dcc_preferences.cpp
new file mode 100644
index 0000000..89ad86b
--- /dev/null
+++ b/konversation/src/dcc_preferences.cpp
@@ -0,0 +1,54 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#include "dcc_preferences.h"
+
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <klocale.h>
+
+
+DCC_Config::DCC_Config(QWidget *parent, const char* name) :
+ DCC_ConfigUI(parent,name)
+{
+ languageChange();
+ connect(kcfg_DccMethodToGetOwnIp, SIGNAL(activated(int)), this, SLOT(dccMethodChanged(int))); dccMethodChanged(kcfg_DccMethodToGetOwnIp->currentItem());
+
+
+}
+
+void DCC_Config::show()
+{
+ QWidget::show();
+
+ kcfg_DccSpecificOwnIp->setEnabled(kcfg_DccMethodToGetOwnIp->currentItem() == 2);
+}
+
+void DCC_Config::dccMethodChanged(int index)
+{
+ kcfg_DccSpecificOwnIp->setEnabled( index == 2 );
+}
+
+void DCC_Config::languageChange()
+{
+ kcfg_DccMethodToGetOwnIp->clear();
+ kcfg_DccMethodToGetOwnIp->insertItem(i18n("Network Interface"));
+ kcfg_DccMethodToGetOwnIp->insertItem(i18n("Reply From IRC Server"));
+ kcfg_DccMethodToGetOwnIp->insertItem(i18n("Specify Manually"));
+
+}
+
+DCC_Config::~DCC_Config()
+{
+}
+#include "dcc_preferences.moc"
+
diff --git a/konversation/src/dcc_preferences.h b/konversation/src/dcc_preferences.h
new file mode 100644
index 0000000..cfbcf51
--- /dev/null
+++ b/konversation/src/dcc_preferences.h
@@ -0,0 +1,38 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef EXDCCPREFERENCES_H
+#define EXDCCPREFERENCES_H
+
+#include "dcc_preferencesui.h"
+
+
+class QComboBox;
+
+class DCC_Config : public DCC_ConfigUI
+{
+ Q_OBJECT
+
+ public:
+ DCC_Config(QWidget* parent, const char* name);
+ ~DCC_Config();
+
+ public slots:
+ virtual void show();
+
+
+ protected slots:
+ virtual void languageChange();
+ void dccMethodChanged(int index);
+};
+
+#endif
diff --git a/konversation/src/dcc_preferencesui.ui b/konversation/src/dcc_preferencesui.ui
new file mode 100644
index 0000000..9bfdfcb
--- /dev/null
+++ b/konversation/src/dcc_preferencesui.ui
@@ -0,0 +1,527 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DCC_ConfigUI</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.</comment>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DCC_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>578</width>
+ <height>517</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>DCC_Settings</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>dccPathLbl</cstring>
+ </property>
+ <property name="text">
+ <string>Download &amp;folder:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_DccPath</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_DccPath</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="mode">
+ <number>2</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>kcfg_DccAddPartner</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add sender to file name</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>kcfg_DccCreateFolder</cstring>
+ </property>
+ <property name="text">
+ <string>Cr&amp;eate folder for sender</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>kcfg_DccSpaceToUnderscore</cstring>
+ </property>
+ <property name="text">
+ <string>Convert spaces in file names to underscores before sending</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>kcfg_DccAutoGet</cstring>
+ </property>
+ <property name="text">
+ <string>Automaticall&amp;y accept download offer</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>kcfg_DccAutoResume</cstring>
+ </property>
+ <property name="text">
+ <string>Au&amp;tomatically resume download</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>kcfg_DccFastSend</cstring>
+ </property>
+ <property name="text">
+ <string>Enable fast DCC send (Might &amp;not work with all clients)</string>
+ </property>
+ </widget>
+ <spacer row="13" column="2">
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="10" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Buffer si&amp;ze:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_DccBufferSize</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="9" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>DCC send t&amp;imeout:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_DccSendTimeout</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="8" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_DccIPv4Fallback</cstring>
+ </property>
+ <property name="text">
+ <string>Fallback to an IPv&amp;4 interface for DCC send:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this option enabled dcc transfers for IPv6 connections will be done over IPv4 interface set here</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="10" column="2">
+ <property name="name">
+ <cstring>kcfg_DccBufferSize</cstring>
+ </property>
+ <property name="suffix">
+ <string> bytes</string>
+ </property>
+ <property name="maxValue">
+ <number>65536</number>
+ </property>
+ <property name="minValue">
+ <number>512</number>
+ </property>
+ <property name="lineStep">
+ <number>128</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="9" column="2">
+ <property name="name">
+ <cstring>kcfg_DccSendTimeout</cstring>
+ </property>
+ <property name="suffix">
+ <string> sec</string>
+ </property>
+ <property name="maxValue">
+ <number>300</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="8" column="2">
+ <property name="name">
+ <cstring>kcfg_DccIPv4FallbackIface</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="7" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_DccPassiveSend</cstring>
+ </property>
+ <property name="text">
+ <string>Enable passive DCC send</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="8" column="3">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="9" column="3">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="10" column="3">
+ <property name="name">
+ <cstring>spacer3_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="11" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>IP</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_DccSpecificOwnIp</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Method to get own IP:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_DccMethodToGetOwnIp</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>ownIP</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;wn IP:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_DccSpecificOwnIp</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_DccMethodToGetOwnIp</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="12" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Ports</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_DccSpecificChatPorts</cstring>
+ </property>
+ <property name="text">
+ <string>Enable specific p&amp;orts for DCC chat:</string>
+ </property>
+ </widget>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox" row="1" column="2">
+ <property name="name">
+ <cstring>kcfg_DccChatPortsFirst</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1026</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>to</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="4">
+ <property name="name">
+ <cstring>kcfg_DccChatPortsLast</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1027</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_DccSpecificSendPorts</cstring>
+ </property>
+ <property name="text">
+ <string>Enable specific &amp;ports for DCC transfer server:</string>
+ </property>
+ </widget>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox" row="0" column="2">
+ <property name="name">
+ <cstring>kcfg_DccSendPortsFirst</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1026</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>to</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="4">
+ <property name="name">
+ <cstring>kcfg_DccSendPortsLast</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1027</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_DccSpecificSendPorts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_DccSendPortsFirst</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_DccSpecificSendPorts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_DccSpecificSendPorts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_DccSendPortsLast</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_DccSpecificChatPorts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_DccChatPortsFirst</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_DccSpecificChatPorts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel2_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_DccSpecificChatPorts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_DccChatPortsLast</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_DccIPv4Fallback</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_DccIPv4FallbackIface</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_DccPath</tabstop>
+ <tabstop>kcfg_DccAddPartner</tabstop>
+ <tabstop>kcfg_DccCreateFolder</tabstop>
+ <tabstop>kcfg_DccSpaceToUnderscore</tabstop>
+ <tabstop>kcfg_DccAutoGet</tabstop>
+ <tabstop>kcfg_DccAutoResume</tabstop>
+ <tabstop>kcfg_DccFastSend</tabstop>
+ <tabstop>kcfg_DccPassiveSend</tabstop>
+ <tabstop>kcfg_DccIPv4Fallback</tabstop>
+ <tabstop>kcfg_DccIPv4FallbackIface</tabstop>
+ <tabstop>kcfg_DccSendTimeout</tabstop>
+ <tabstop>kcfg_DccBufferSize</tabstop>
+ <tabstop>kcfg_DccMethodToGetOwnIp</tabstop>
+ <tabstop>kcfg_DccSpecificOwnIp</tabstop>
+ <tabstop>kcfg_DccSpecificSendPorts</tabstop>
+ <tabstop>kcfg_DccSendPortsFirst</tabstop>
+ <tabstop>kcfg_DccSendPortsLast</tabstop>
+ <tabstop>kcfg_DccSpecificChatPorts</tabstop>
+ <tabstop>kcfg_DccChatPortsFirst</tabstop>
+ <tabstop>kcfg_DccChatPortsLast</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/dccchat.cpp b/konversation/src/dccchat.cpp
new file mode 100644
index 0000000..37b8b6f
--- /dev/null
+++ b/konversation/src/dccchat.cpp
@@ -0,0 +1,473 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+ Copyright (C) 2004,2007 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#include "dccchat.h"
+#include "dcccommon.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "irccharsets.h"
+#include "ircview.h"
+#include "ircviewbox.h"
+#include "ircinput.h"
+#include "topiclabel.h"
+#include "server.h"
+#include "channel.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <qvbox.h>
+#include <qhostaddress.h>
+#include <qtextcodec.h>
+#include <qsplitter.h>
+#include <qpopupmenu.h>
+
+#include <klineedit.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kserversocket.h>
+#include <ksocketaddress.h>
+#include <kstreamsocket.h>
+#include <kaction.h>
+
+#define DCCCHAT_BUFFER_SIZE 1024
+
+
+DccChat::DccChat(QWidget* parent, bool listen, Server* server, const QString& ownNick, const QString& partnerNick, const QString& partnerHost, int partnerPort)
+ : ChatWindow(parent)
+{
+ kdDebug() << "DccChat::DccChat() [BEGIN]" << endl;
+
+ m_dccSocket = 0;
+ m_listenSocket = 0;
+
+ m_ownNick = ownNick;
+
+ m_partnerNick = partnerNick;
+ m_partnerHost = partnerHost;
+ m_partnerPort = partnerPort;
+
+ setType(ChatWindow::DccChat);
+ setChannelEncodingSupported(true);
+
+ m_headerSplitter = new QSplitter(Qt::Vertical, this);
+
+ m_sourceLine = new Konversation::TopicLabel(m_headerSplitter);
+
+ IRCViewBox* ircViewBox = new IRCViewBox(m_headerSplitter, NULL);
+ setTextView(ircViewBox->ircView());
+
+ m_dccChatInput = new IRCInput(this);
+ getTextView()->installEventFilter(m_dccChatInput);
+ m_dccChatInput->setEnabled( false );
+
+ ChatWindow::setName( '-' + m_partnerNick + '-' );
+ ChatWindow::setLogfileName( '-' + m_partnerNick + '-' );
+
+ QPopupMenu* popup = textView->getPopup();
+
+ if (popup)
+ {
+ KAction* action = KonversationApplication::instance()->getMainWindow()->actionCollection()->action("open_logfile");
+
+ if (action)
+ {
+ popup->insertSeparator();
+ action->plug(popup);
+ }
+ }
+
+ // connect the signals and slots
+ connect( m_dccChatInput, SIGNAL( submit() ), this, SLOT( dccChatTextEntered() ) );
+ connect( m_dccChatInput, SIGNAL( textPasted( const QString& ) ), this, SLOT( textPasted( const QString& ) ) );
+
+ connect( getTextView(), SIGNAL( textPasted(bool) ), m_dccChatInput, SLOT( paste(bool) ) );
+ connect( getTextView(), SIGNAL( gotFocus() ), m_dccChatInput, SLOT( setFocus() ) );
+ connect( getTextView(), SIGNAL( autoText(const QString&) ), this, SLOT( sendDccChatText( const QString& ) ) );
+
+ if (listen)
+ {
+ listenForPartner();
+ QString ownNumericalIp = DccCommon::textIpToNumericalIp( DccCommon::getOwnIp( server ) );
+ server->requestDccChat( m_partnerNick, ownNumericalIp, QString::number( m_ownPort ) );
+ }
+ else
+ {
+ connectToPartner();
+ }
+
+ kdDebug() << "DccChat::DccChat() [END]" << endl;
+
+ updateAppearance();
+}
+
+DccChat::~DccChat()
+{
+ kdDebug() << "DccChat::~DccChat()" << endl;
+ if(m_dccSocket)
+ m_dccSocket->close();
+ if(m_listenSocket)
+ m_listenSocket->close();
+}
+
+void DccChat::listenForPartner()
+{
+ kdDebug() << "DccChat::listenForPartner() [BEGIN]" << endl;
+
+ // Set up server socket
+ QString failedReason;
+ if ( Preferences::dccSpecificChatPorts() )
+ m_listenSocket = DccCommon::createServerSocketAndListen( this, &failedReason, Preferences::dccChatPortsFirst(), Preferences::dccChatPortsLast() );
+ else
+ m_listenSocket = DccCommon::createServerSocketAndListen( this, &failedReason );
+ if ( !m_listenSocket )
+ {
+ getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "Could not open a socket for listening: %1" ).arg( failedReason ) );
+ return;
+ }
+
+ connect( m_listenSocket, SIGNAL(readyAccept()), this, SLOT(heardPartner()) );
+
+ // Get our own port number
+ m_ownPort = DccCommon::getServerSocketPort( m_listenSocket );
+ kdDebug() << "DccChat::listenForPartner(): using port " << m_ownPort << endl;
+
+ getTextView()->appendServerMessage( i18n("DCC"), i18n("Offering DCC Chat connection to %1 on port %2...").arg( m_partnerNick ).arg( m_ownPort ) );
+ m_sourceLine->setText(i18n( "DCC chat with %1 on port %2." ).arg( m_partnerNick ).arg( m_ownPort ) );
+ kdDebug() << "DccChat::listenForPartner() [END]" << endl;
+}
+
+void DccChat::connectToPartner()
+{
+ QHostAddress ip;
+
+ ip.setAddress(m_partnerHost.toUInt());
+ m_partnerHost=ip.toString();
+
+ getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "%1 = nickname, %2 = IP, %3 = port",
+ "Establishing DCC Chat connection to %1 (%2:%3)..." ).arg( m_partnerNick ).arg( m_partnerHost ).arg( m_partnerPort ) );
+
+ m_sourceLine->setText( i18n( "%1 = nickname, %2 = IP, %3 = port", "DCC chat with %1 on %2:%3." ).arg( m_partnerNick ).arg( host ).arg( m_partnerPort ) );
+
+ m_dccSocket = new KNetwork::KStreamSocket( m_partnerHost, QString::number( m_partnerPort ), this );
+
+ m_dccSocket->setBlocking(false);
+ m_dccSocket->setFamily(KNetwork::KResolver::InetFamily);
+ m_dccSocket->enableRead(false);
+ m_dccSocket->enableWrite(false);
+ m_dccSocket->setTimeout(10000);
+ m_dccSocket->blockSignals(false);
+
+ connect( m_dccSocket, SIGNAL( hostFound() ), this, SLOT( lookupFinished() ) );
+ connect( m_dccSocket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( dccChatConnectionSuccess() ) );
+ connect( m_dccSocket, SIGNAL( gotError( int ) ), this, SLOT( dccChatBroken( int ) ) );
+ connect( m_dccSocket, SIGNAL( readyRead() ), this, SLOT( readData() ) );
+ connect( m_dccSocket, SIGNAL( closed() ), this, SLOT( socketClosed() ) );
+
+ m_dccSocket->connect();
+
+#if 0
+ //getTextView()->appendServerMessage(i18n("DCC"),i18n("Looking for host %1...").arg(host));
+#endif
+
+}
+
+void DccChat::lookupFinished()
+{
+
+#if 0
+ //getTextView()->appendServerMessage(i18n("DCC"),i18n("Host found, connecting..."));
+#endif
+
+}
+
+void DccChat::dccChatConnectionSuccess()
+{
+ getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "Established DCC Chat connection to %1." ).arg( m_partnerNick ) );
+ m_dccSocket->enableRead(true);
+ m_dccChatInput->setEnabled(true);
+}
+
+void DccChat::dccChatBroken(int error)
+{
+ getTextView()->appendServerMessage(i18n("Error"),i18n("Connection broken, error code %1.").arg(error));
+ m_dccSocket->enableRead(false);
+ m_dccSocket->blockSignals(true);
+ m_dccSocket->close();
+}
+
+void DccChat::readData()
+{
+ kdDebug() << k_funcinfo << ( m_listenSocket == 0 ) << " BEGIN" << endl;
+ int available=0;
+ int actual=0;
+ char* buffer=0;
+ QString line;
+ QTextCodec* codec = Konversation::IRCCharsets::self()->codecForName(m_encoding.isEmpty() ? Konversation::IRCCharsets::self()->encodingForLocale() : m_encoding);
+
+ available = m_dccSocket->bytesAvailable();
+ if( available > 0 )
+ {
+ buffer = new char[ available + 1 ];
+ actual = m_dccSocket->readBlock( buffer, available );
+ buffer[ actual ] = 0;
+ line.append( codec->toUnicode( buffer ) );
+ delete[] buffer;
+
+ QStringList lines = QStringList::split( '\n', line );
+
+ for( QStringList::iterator itLine = lines.begin() ; itLine != lines.end() ; itLine++ )
+ {
+ if( (*itLine).startsWith( "\x01" ) )
+ {
+ // cut out the CTCP command
+ QString ctcp = (*itLine).mid( 1, (*itLine).find( 1, 1 ) - 1 );
+
+ QString ctcpCommand = ctcp.section( " ", 0, 0 );
+ QString ctcpArgument = ctcp.section( " ", 1 );
+
+ if( ctcpCommand.lower() == "action" )
+ appendAction( m_partnerNick, ctcpArgument );
+ else
+ getTextView()->appendServerMessage( i18n( "CTCP" ), i18n( "Received unknown CTCP-%1 request from %2" ).arg( ctcp ).arg( m_partnerNick ) );
+ }
+ else getTextView()->append( m_partnerNick, *itLine );
+ } // endfor
+ } else {
+ dccChatBroken(m_dccSocket->error());
+ }
+
+ kdDebug() << k_funcinfo << " END" << endl;
+}
+
+void DccChat::dccChatTextEntered()
+{
+ QString line = m_dccChatInput->text();
+ m_dccChatInput->setText("");
+ if ( line.lower() == Preferences::commandChar()+"clear" )
+ {
+ textView->clear();
+ }
+ else if ( !line.isEmpty() )
+ {
+ sendDccChatText(line);
+ }
+}
+
+void DccChat::sendDccChatText(const QString& sendLine)
+{
+ kdDebug() << k_funcinfo << " BEGIN" << endl;
+ // create a work copy
+ QString output(sendLine);
+ QString cc=Preferences::commandChar();
+
+ if(!output.isEmpty())
+ {
+ QStringList lines = QStringList::split('\n',output);
+ // wrap socket into a stream
+ QTextStream stream(m_dccSocket);
+ // init stream props
+ stream.setCodec(Konversation::IRCCharsets::self()->codecForName(m_encoding.isEmpty() ? Konversation::IRCCharsets::self()->encodingForLocale() : m_encoding));
+
+ for( QStringList::iterator itLine = lines.begin() ; itLine != lines.end() ; itLine++ )
+ {
+ QString line( *itLine );
+
+ // replace aliases and wildcards
+ // if(filter.replaceAliases(line)) line=server->parseWildcards(line,nick,getName(),QString::null,QString::null,QString::null);
+
+ // line=filter.parse(nick,line,getName());
+
+ // convert /me actions
+ QString cmd=line.section(' ', 0,0).lower();
+ if (cmd == cc+"me")
+ {
+ appendAction( m_ownNick, line.section( " ", 1 ) );
+ line=QString("\x01%1 %2\x01").arg("ACTION").arg(line.section(" ",1));
+ }
+ else if (cmd == cc+"close")
+ {
+ closeYourself(false);
+ return;
+ }
+ else
+ getTextView()->append( m_ownNick, line );
+
+ stream << line << endl;
+ } // endfor
+
+ // detach stream
+ stream.unsetDevice();
+ }
+ kdDebug() << k_funcinfo << " END" << endl;
+}
+
+void DccChat::heardPartner()
+{
+ m_dccSocket = static_cast<KNetwork::KStreamSocket*>( m_listenSocket->accept() );
+
+ if( !m_dccSocket )
+ {
+ getTextView()->appendServerMessage( i18n( "Error" ),i18n( "Could not accept the client." ) );
+ return;
+ }
+
+ connect( m_dccSocket, SIGNAL( readyRead() ), this, SLOT( readData() ) );
+ connect( m_dccSocket, SIGNAL( closed() ), this, SLOT( socketClosed() ) );
+ connect( m_dccSocket, SIGNAL( gotError( int ) ), this, SLOT( dccChatBroken( int ) ) );
+
+ // the listen socket isn't needed anymore
+ disconnect( m_listenSocket, 0, 0, 0 );
+ m_listenSocket->close();
+ m_listenSocket = 0;
+
+ m_dccSocket->enableRead(true);
+ m_dccChatInput->setEnabled(true);
+
+ getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "Established DCC Chat connection to %1." ).arg( m_partnerNick ) );
+}
+
+void DccChat::socketClosed()
+{
+ getTextView()->appendServerMessage(i18n("DCC"),"Connection closed.");
+ m_dccChatInput->setEnabled(false);
+ m_dccSocket = 0;
+}
+
+void DccChat::textPasted(const QString& text)
+{
+ sendDccChatText(text);
+}
+
+void DccChat::childAdjustFocus()
+{
+ m_dccChatInput->setFocus();
+}
+
+bool DccChat::canBeFrontView()
+{
+ return true;
+}
+
+bool DccChat::searchView()
+{
+ return true;
+}
+
+int DccChat::getOwnPort()
+{
+ return m_ownPort;
+}
+
+QString DccChat::getTextInLine()
+{
+ return m_dccChatInput->text();
+}
+
+void DccChat::appendInputText( const QString& s, bool fromCursor )
+{
+ if(!fromCursor)
+ {
+ m_dccChatInput->append(s);
+ }
+ else
+ {
+ int para = 0, index = 0;
+ m_dccChatInput->getCursorPosition(&para, &index);
+ m_dccChatInput->insertAt(s, para, index);
+ m_dccChatInput->setCursorPosition(para, index + s.length());
+ }
+}
+
+//FIXME uh... where is the confimation for this?
+bool DccChat::closeYourself(bool)
+{
+ deleteLater();
+ return true;
+}
+
+void DccChat::setChannelEncoding(const QString& encoding) // virtual
+{
+ m_encoding = encoding;
+}
+
+QString DccChat::getChannelEncoding() // virtual
+{
+ return m_encoding;
+}
+
+QString DccChat::getChannelEncodingDefaultDesc() // virtual
+{
+ return i18n("Default ( %1 )").arg(Konversation::IRCCharsets::self()->encodingForLocale());
+}
+
+void DccChat::showEvent(QShowEvent* /* event */)
+{
+ if(m_initialShow) {
+ m_initialShow = false;
+ QValueList<int> sizes;
+ sizes << m_sourceLine->sizeHint().height() << (height() - m_sourceLine->sizeHint().height());
+ m_headerSplitter->setSizes(sizes);
+ }
+}
+
+void DccChat::updateAppearance()
+{
+ QColor fg;
+ QColor bg;
+
+ if(Preferences::inputFieldsBackgroundColor())
+ {
+ fg=Preferences::color(Preferences::ChannelMessage);
+ bg=Preferences::color(Preferences::TextViewBackground);
+ }
+ else
+ {
+ fg=colorGroup().foreground();
+ bg=colorGroup().base();
+ }
+
+ m_dccChatInput->unsetPalette();
+ m_dccChatInput->setPaletteForegroundColor(fg);
+ m_dccChatInput->setPaletteBackgroundColor(bg);
+
+ getTextView()->unsetPalette();
+
+ if(Preferences::showBackgroundImage())
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ Preferences::backgroundImage());
+ }
+ else
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ QString());
+ }
+
+ if (Preferences::customTextFont())
+ {
+ getTextView()->setFont(Preferences::textFont());
+ m_dccChatInput->setFont(Preferences::textFont());
+ }
+ else
+ {
+ getTextView()->setFont(KGlobalSettings::generalFont());
+ m_dccChatInput->setFont(KGlobalSettings::generalFont());
+ }
+
+ ChatWindow::updateAppearance();
+}
+
+#include "dccchat.moc"
diff --git a/konversation/src/dccchat.h b/konversation/src/dccchat.h
new file mode 100644
index 0000000..d4a5f13
--- /dev/null
+++ b/konversation/src/dccchat.h
@@ -0,0 +1,102 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef DCCCHAT_H
+#define DCCCHAT_H
+
+#include "chatwindow.h"
+
+
+class QSplitter;
+
+class IRCInput;
+class KLineEdit;
+class Server;
+
+namespace KNetwork
+{
+ class KServerSocket;
+ class KStreamSocket;
+}
+
+namespace Konversation
+{
+ class TopicLabel;
+}
+
+class DccChat : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ DccChat(QWidget* parent, bool listen, Server* server, const QString& ownNick, const QString& partnerNick, const QString& partnerHost = QString(), int partnerPort = 0);
+ ~DccChat();
+
+ virtual QString getTextInLine();
+ virtual bool closeYourself(bool askForConfirmation=true);
+ virtual bool canBeFrontView();
+ virtual bool searchView();
+
+ int getOwnPort();
+
+ virtual void setChannelEncoding(const QString& encoding);
+ virtual QString getChannelEncoding();
+ virtual QString getChannelEncodingDefaultDesc();
+
+ virtual bool isInsertSupported() { return true; }
+
+ QString getOwnNick() { return m_ownNick; }
+
+ public slots:
+ void appendInputText(const QString& s, bool fromCursor);
+ void updateAppearance();
+
+ protected slots:
+ void lookupFinished();
+ void dccChatConnectionSuccess();
+ void dccChatBroken(int error);
+ void readData();
+ void dccChatTextEntered();
+ void sendDccChatText(const QString& sendLine);
+ void textPasted(const QString& text);
+ void heardPartner();
+ void socketClosed();
+
+ protected:
+ void listenForPartner();
+ void connectToPartner();
+
+ virtual void showEvent(QShowEvent* event);
+
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ QString m_ownNick;
+ QString m_partnerNick;
+ QString m_partnerHost;
+ int m_partnerPort;
+ QString host;
+
+ //QString m_ip;
+ int m_ownPort;
+
+ QSplitter* m_headerSplitter;
+ Konversation::TopicLabel* m_sourceLine;
+ IRCInput* m_dccChatInput;
+ KNetwork::KStreamSocket* m_dccSocket;
+ KNetwork::KServerSocket* m_listenSocket;
+
+ QString m_encoding;
+
+ bool m_initialShow;
+};
+#endif
diff --git a/konversation/src/dcccommon.cpp b/konversation/src/dcccommon.cpp
new file mode 100644
index 0000000..d03548d
--- /dev/null
+++ b/konversation/src/dcccommon.cpp
@@ -0,0 +1,118 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2007 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#include "dcccommon.h"
+#include "channel.h"
+#include "preferences.h"
+#include "server.h"
+
+#include <arpa/inet.h>
+
+#include <qhostaddress.h>
+
+#include <klocale.h>
+#include <kresolver.h>
+#include <kserversocket.h>
+
+
+QString DccCommon::textIpToNumericalIp( const QString& ipString )
+{
+ QHostAddress ip;
+ ip.setAddress( ipString );
+
+ return QString::number( ip.ip4Addr() );
+}
+
+QString DccCommon::numericalIpToTextIp( const QString& numericalIp )
+{
+ QHostAddress ip;
+ ip.setAddress( numericalIp.toULong() );
+
+ return ip.toString();
+}
+
+QString DccCommon::getOwnIp( Server* server )
+{
+ QString ownIp;
+ int methodId = Preferences::dccMethodToGetOwnIp();
+
+ if ( methodId == 1 && server )
+ {
+ // by the WELCOME message or the USERHOST message from the server
+ ownIp = server->getOwnIpByServerMessage();
+ }
+ else if ( methodId == 2 && !Preferences::dccSpecificOwnIp().isEmpty() )
+ {
+ // manual
+ KNetwork::KResolverResults res = KNetwork::KResolver::resolve(Preferences::dccSpecificOwnIp(), "");
+ if(res.error() == KResolver::NoError && res.size() > 0)
+ {
+ ownIp = res.first().address().nodeName();
+ }
+ }
+
+ // fallback or methodId == 3 (network interface)
+ if ( ownIp.isEmpty() && server )
+ {
+ ownIp = server->getOwnIpByNetworkInterface();
+ }
+
+ kdDebug() << "DccCommon::getOwnIp(): " << ownIp << endl;
+ return ownIp;
+}
+
+KNetwork::KServerSocket* DccCommon::createServerSocketAndListen( QObject* parent, QString* failedReason, int minPort, int maxPort )
+{
+ KNetwork::KServerSocket* socket = new KNetwork::KServerSocket( parent );
+ socket->setFamily( KNetwork::KResolver::InetFamily );
+
+ if ( minPort > 0 && maxPort >= minPort ) // ports are configured manually
+ {
+ // set port
+ bool found = false; // whether succeeded to set port
+ for ( int port = minPort; port <= maxPort ; ++port )
+ {
+ socket->setAddress( QString::number( port ) );
+ bool success = socket->listen();
+ if ( ( found = ( success && socket->error() == KNetwork::KSocketBase::NoError ) ) )
+ break;
+ socket->close();
+ }
+ if ( !found )
+ {
+ if ( failedReason )
+ *failedReason = i18n( "No vacant port" );
+ delete socket;
+ return 0;
+ }
+ }
+ else
+ {
+ // Let the operating system choose a port
+ socket->setAddress( "0" );
+ if ( !socket->listen() )
+ {
+ if ( failedReason )
+ *failedReason = i18n( "Could not open a socket" );
+ delete socket;
+ return 0;
+ }
+ }
+
+ return socket;
+}
+
+int DccCommon::getServerSocketPort( KNetwork::KServerSocket* serverSocket )
+{
+ KNetwork::KSocketAddress ipAddr = serverSocket->localAddress();
+ const struct sockaddr_in* socketAddress = (sockaddr_in*)ipAddr.address();
+ return ntohs( socketAddress->sin_port );
+}
diff --git a/konversation/src/dcccommon.h b/konversation/src/dcccommon.h
new file mode 100644
index 0000000..f3e8220
--- /dev/null
+++ b/konversation/src/dcccommon.h
@@ -0,0 +1,48 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2007 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#ifndef DCCCOMMON_H
+#define DCCCOMMON_H
+
+#include <qstring.h>
+
+class QObject;
+
+namespace KNetwork
+{
+ class KServerSocket;
+}
+
+class Server;
+
+class DccCommon
+{
+ public:
+ // converts an IP text like "127.0.0.1" to a number.
+ static QString textIpToNumericalIp( const QString& ipString );
+
+ // converts a numerical IP text like "12345678" to a normal IP text.
+ static QString numericalIpToTextIp( const QString& numericalIp );
+
+ // returns the self IP following the setting.
+ static QString getOwnIp( Server* server = 0 );
+
+ // creates an instance of KNetwork::ServerSocket following the DCC settings
+ static KNetwork::KServerSocket* createServerSocketAndListen( QObject* parent = 0, QString* failedReason = 0, int minPort = 0, int maxPort = 0 );
+
+ // returns the port number from a server socket
+ static int getServerSocketPort( KNetwork::KServerSocket* serverSocket );
+
+ private:
+ DccCommon();
+};
+
+#endif // DCCCOMMON_H
diff --git a/konversation/src/dccrecipientdialog.cpp b/konversation/src/dccrecipientdialog.cpp
new file mode 100644
index 0000000..8c86caf
--- /dev/null
+++ b/konversation/src/dccrecipientdialog.cpp
@@ -0,0 +1,100 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ lets the user choose a nick from the list
+ begin: Sam Dez 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "dccrecipientdialog.h"
+
+#include <qlayout.h>
+
+#include <klineedit.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+
+QString DccRecipientDialog::selectedNickname; // static
+
+DccRecipientDialog::DccRecipientDialog(QWidget* parent, const QStringList &list,const QSize &size) :
+ KDialogBase(parent,"dcc_recipient_dialog",true,i18n("Select Recipient"),
+ KDialogBase::Ok | KDialogBase::Cancel,KDialogBase::Ok,true)
+{
+ // Create the top level widget
+ QWidget* page=new QWidget(this);
+ setMainWidget(page);
+ // Add the layout to the widget
+ QVBoxLayout* dialogLayout=new QVBoxLayout(page);
+ dialogLayout->setSpacing(spacingHint());
+ // Add the nickname list widget
+ KListBox* nicknameList=new KListBox(page,"recipient_list");
+
+ nicknameList->insertStringList(list);
+ nicknameList->sort(true);
+
+ nicknameInput=new KLineEdit(page,"nickname_input");
+
+ dialogLayout->addWidget(nicknameList);
+ dialogLayout->addWidget(nicknameInput);
+
+ connect(nicknameList,SIGNAL (highlighted(QListBoxItem*)),this,SLOT (newNicknameSelected(QListBoxItem*)) );
+ connect(nicknameList,SIGNAL (doubleClicked(QListBoxItem*)),this,SLOT (newNicknameSelectedQuit(QListBoxItem*)) );
+
+ setButtonOK(KGuiItem(i18n("&OK"),"button_ok",i18n("Select nickname and close the window")));
+ setButtonCancel(KGuiItem(i18n("&Cancel"),"button_cancel",i18n("Close the window without changes")));
+
+ setInitialSize(size);
+ show();
+}
+
+DccRecipientDialog::~DccRecipientDialog()
+{
+}
+
+QString DccRecipientDialog::getSelectedNickname()
+{
+ return selectedNickname;
+}
+
+void DccRecipientDialog::newNicknameSelected(QListBoxItem* item)
+{
+ nicknameInput->setText(item->text());
+}
+
+void DccRecipientDialog::newNicknameSelectedQuit(QListBoxItem* item)
+{
+ newNicknameSelected(item);
+ selectedNickname=nicknameInput->text();
+
+ delayedDestruct();
+}
+
+void DccRecipientDialog::slotCancel()
+{
+ selectedNickname=QString();
+ KDialogBase::slotCancel();
+}
+
+void DccRecipientDialog::slotOk()
+{
+ selectedNickname=nicknameInput->text();
+ KDialogBase::slotOk();
+}
+
+QString DccRecipientDialog::getNickname(QWidget* parent, const QStringList& list)
+{
+ QSize size; // TODO: get it from Preferences
+ DccRecipientDialog dlg(parent,list,size);
+ dlg.exec();
+
+ return dlg.getSelectedNickname();
+}
+
+#include "dccrecipientdialog.moc"
diff --git a/konversation/src/dccrecipientdialog.h b/konversation/src/dccrecipientdialog.h
new file mode 100644
index 0000000..94d77ca
--- /dev/null
+++ b/konversation/src/dccrecipientdialog.h
@@ -0,0 +1,47 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ lets the user choose a nick from the list
+ begin: Sam Dez 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef DCCRECIPIENTDIALOG_H
+#define DCCRECIPIENTDIALOG_H
+
+#include <kdialogbase.h>
+
+
+class KLineEdit;
+
+class DccRecipientDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ DccRecipientDialog(QWidget* parent, const QStringList &list, const QSize &size);
+ ~DccRecipientDialog();
+
+ static QString getNickname(QWidget* parent, const QStringList& list);
+
+ protected slots:
+ void newNicknameSelected(QListBoxItem* item);
+ // KDE double click
+ void newNicknameSelectedQuit(QListBoxItem* item);
+
+ void slotOk();
+ void slotCancel();
+
+ protected:
+ QString getSelectedNickname();
+ static QString selectedNickname;
+
+ KLineEdit* nicknameInput;
+};
+#endif
diff --git a/konversation/src/dccresumedialog.cpp b/konversation/src/dccresumedialog.cpp
new file mode 100644
index 0000000..b8376b8
--- /dev/null
+++ b/konversation/src/dccresumedialog.cpp
@@ -0,0 +1,198 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#include "dccresumedialog.h"
+#include "dcctransferrecv.h"
+
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qvbox.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kurlrequester.h>
+
+
+DccResumeDialog::ReceiveAction DccResumeDialog::ask(DccTransferRecv* item, const QString& message, int enabledActions, ReceiveAction defaultAction)
+{
+ int enabledButtonCodes = 0;
+ KDialogBase::ButtonCode defaultButtonCode = KDialogBase::Ok;
+
+ if(enabledActions & RA_Rename || enabledActions & RA_Overwrite)
+ enabledButtonCodes |= KDialogBase::Ok;
+ if(enabledActions & RA_Resume)
+ enabledButtonCodes |= KDialogBase::User1;
+ if(enabledActions & RA_Cancel)
+ enabledButtonCodes |= KDialogBase::Cancel;
+
+ if(defaultAction == RA_Rename || defaultAction == RA_Overwrite)
+ defaultButtonCode = KDialogBase::Ok;
+ else if(defaultAction == RA_Resume)
+ defaultButtonCode = KDialogBase::User1;
+ else if(defaultAction == RA_Cancel)
+ defaultButtonCode = KDialogBase::Cancel;
+
+ DccResumeDialog dlg(item, i18n("DCC Receive Question"), message, enabledActions, enabledButtonCodes, defaultButtonCode);
+ dlg.exec();
+
+ ReceiveAction ra = dlg.m_selectedAction;
+
+ if(ra == RA_Rename)
+ item->setFileURL( dlg.m_urlreqFileURL->url() );
+
+ return ra;
+}
+
+DccResumeDialog::DccResumeDialog(DccTransferRecv* item, const QString& caption, const QString& message, int enabledActions, int enabledButtonCodes,
+ KDialogBase::ButtonCode defaultButtonCode)
+: KDialogBase(0, "dcc_resume_dialog", true, caption, enabledButtonCodes, defaultButtonCode, true)
+, m_item(item)
+, m_enabledActions(enabledActions)
+, m_selectedAction(RA_Cancel)
+{
+ if(enabledButtonCodes & KDialogBase::User1)
+ setButtonText(KDialogBase::User1, i18n("&Resume"));
+
+ QFrame* page = new QFrame(this);
+ QVBoxLayout* pageLayout = new QVBoxLayout(page);
+ pageLayout->setSpacing(spacingHint());
+ setMainWidget(page);
+
+ QLabel* labelMessage = new QLabel(page);
+ labelMessage->setText(message);
+
+ m_urlreqFileURL = new KURLRequester(m_item->getFileURL().prettyURL(), page);
+ connect(m_urlreqFileURL, SIGNAL(textChanged(const QString&)), this, SLOT(updateDialogButtons()));
+
+ pageLayout->addWidget(labelMessage);
+ pageLayout->addWidget(m_urlreqFileURL);
+
+ if(m_enabledActions & RA_Rename)
+ {
+ QFrame* filePathToolsFrame = new QFrame(page);
+ QHBoxLayout* filePathToolsLayout = new QHBoxLayout(filePathToolsFrame);
+ filePathToolsLayout->setSpacing(spacingHint());
+
+ QPushButton* btnDefaultName = new QPushButton(i18n("O&riginal Filename"),filePathToolsFrame);
+ QPushButton* btnSuggestNewName = new QPushButton(i18n("Suggest &New Filename"),filePathToolsFrame);
+ filePathToolsLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
+ filePathToolsLayout->addWidget(btnDefaultName);
+ filePathToolsLayout->addWidget(btnSuggestNewName);
+ connect(btnSuggestNewName, SIGNAL(clicked()), this, SLOT(suggestNewName()));
+ connect(btnDefaultName, SIGNAL(clicked()), this, SLOT(setDefaultName()));
+
+ pageLayout->addWidget(filePathToolsFrame);
+
+ }
+
+ updateDialogButtons();
+ setInitialSize(QSize(500, sizeHint().height()));
+
+}
+
+DccResumeDialog::~DccResumeDialog()
+{
+}
+
+void DccResumeDialog::slotOk()
+{
+ if(m_item->getFileURL() == m_urlreqFileURL->url())
+ m_selectedAction = RA_Overwrite;
+ else
+ m_selectedAction = RA_Rename;
+ KDialogBase::slotOk();
+}
+
+void DccResumeDialog::slotUser1()
+{
+ m_selectedAction = RA_Resume;
+ done(KDialogBase::User1);
+}
+
+void DccResumeDialog::slotCancel()
+{
+ m_selectedAction = RA_Cancel;
+ KDialogBase::slotCancel();
+}
+
+void DccResumeDialog::updateDialogButtons() // slot
+{
+ if(m_item->getFileURL() == m_urlreqFileURL->url())
+ {
+ setButtonText(KDialogBase::Ok, i18n("&Overwrite"));
+ enableButton(KDialogBase::Ok, m_enabledActions & RA_Overwrite);
+ enableButton(KDialogBase::User1, true);
+ }
+ else
+ {
+ setButtonText(KDialogBase::Ok, i18n("R&ename"));
+ enableButton(KDialogBase::Ok, m_enabledActions & RA_Rename);
+ enableButton(KDialogBase::User1, false);
+ }
+}
+
+// FIXME: kio-fy me!
+// taken and adapted from kio::renamedlg.cpp
+void DccResumeDialog::suggestNewName() // slot
+{
+ QString dotSuffix, suggestedName;
+ QString basename = m_urlreqFileURL->url().section("/", -1);
+ KURL baseURL(m_urlreqFileURL->url().section("/", 0, -2));
+
+ int index = basename.find( '.' );
+ if ( index != -1 )
+ {
+ dotSuffix = basename.mid( index );
+ basename.truncate( index );
+ }
+
+ int pos = basename.findRev( '_' );
+ if(pos != -1 )
+ {
+ QString tmp = basename.mid( pos+1 );
+ bool ok;
+ int number = tmp.toInt( &ok );
+ if ( !ok ) // ok there is no number
+ {
+ suggestedName = basename + '1' + dotSuffix;
+ }
+ else
+ {
+ // yes there's already a number behind the _ so increment it by one
+ basename.replace( pos+1, tmp.length(), QString::number(number+1) );
+ suggestedName = basename + dotSuffix;
+ }
+ }
+ else // no underscore yet
+ suggestedName = basename + "_1" + dotSuffix ;
+
+ // Check if suggested name already exists
+ bool exists = false;
+ // TODO: network transparency. However, using NetAccess from a modal dialog
+ // could be a problem, no? (given that it uses a modal widget itself....)
+ if ( baseURL.isLocalFile() )
+ exists = QFileInfo( baseURL.path(+1) + suggestedName ).exists();
+
+ m_urlreqFileURL->setURL( baseURL.path(+1) + suggestedName );
+
+ if ( exists ) // already exists -> recurse
+ suggestNewName();
+}
+
+void DccResumeDialog::setDefaultName() // slot
+{
+ m_urlreqFileURL->setURL(m_item->getFileURL().prettyURL());
+}
+
+#include "dccresumedialog.moc"
diff --git a/konversation/src/dccresumedialog.h b/konversation/src/dccresumedialog.h
new file mode 100644
index 0000000..03135f8
--- /dev/null
+++ b/konversation/src/dccresumedialog.h
@@ -0,0 +1,59 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#ifndef DCCRESUMEDIALOG_H
+#define DCCRESUMEDIALOG_H
+
+#include <kdialogbase.h>
+
+
+class KURLRequester;
+class DccTransferRecv;
+
+class DccResumeDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ enum ReceiveAction
+ {
+ RA_Rename = 0x01,
+ RA_Overwrite = 0x02,
+ RA_Resume = 0x04,
+ RA_Cancel = 0x08
+ };
+
+ virtual ~DccResumeDialog();
+
+ static ReceiveAction ask(DccTransferRecv* item, const QString& message, int enabledActions, ReceiveAction defaultAction);
+
+ protected slots:
+ void slotOk();
+ void slotUser1();
+ void slotCancel();
+ void suggestNewName();
+ void setDefaultName();
+ void updateDialogButtons();
+
+ protected:
+ DccResumeDialog(DccTransferRecv* item, const QString& caption, const QString& message, int enabledActions, int enabledButtonCodes, KDialogBase::ButtonCode defaultButtonCode);
+
+ // UI
+ KURLRequester* m_urlreqFileURL;
+
+ // data
+ DccTransferRecv* m_item;
+ int m_enabledActions;
+ ReceiveAction m_selectedAction;
+
+};
+#endif
diff --git a/konversation/src/dcctransfer.cpp b/konversation/src/dcctransfer.cpp
new file mode 100644
index 0000000..30ae5c2
--- /dev/null
+++ b/konversation/src/dcctransfer.cpp
@@ -0,0 +1,358 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002-2004 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+ Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+*/
+
+#include "dcctransfer.h"
+#include "preferences.h"
+
+#include <qfileinfo.h>
+#include <qhostaddress.h>
+
+#include <kdebug.h>
+
+
+DccTransfer::DccTransfer( DccType dccType, QObject* parent ) : QObject(parent)
+{
+ kdDebug() << "DccTransfer::DccTransfer()" << endl;
+
+ m_type = dccType;
+
+ m_status = Configuring;
+
+ m_fileSize = 0;
+ m_resumed = false;
+ m_reverse = false;
+ m_connectionId = -1; // Not configured
+ m_timeLeft = DccTransfer::NotInTransfer;
+ m_transferringPosition = 0;
+ m_transferStartPosition = 0;
+ m_averageSpeed = 0.0;
+ m_currentSpeed = 0.0;
+
+ m_bufferSize = Preferences::dccBufferSize();
+ m_buffer = new char[ m_bufferSize ];
+
+ connect( &m_loggerTimer, SIGNAL( timeout() ), this, SLOT( logTransfer() ) );
+
+ m_timeOffer = QDateTime::currentDateTime();
+}
+
+DccTransfer::~DccTransfer()
+{
+ kdDebug() << "DccTransfer::~DccTransfer()" << endl;
+ delete[] m_buffer;
+ m_loggerTimer.stop();
+}
+
+DccTransfer::DccTransfer( const DccTransfer& obj )
+ : QObject()
+{
+ m_buffer = 0;
+ m_bufferSize = 0;
+ m_averageSpeed = obj.getAverageSpeed();
+ m_currentSpeed = obj.getCurrentSpeed();
+ m_status = obj.getStatus();
+ m_statusDetail = obj.getStatusDetail();
+ m_type = obj.getType();
+ m_fileName = obj.getFileName();
+ m_fileSize = obj.getFileSize();
+ m_fileURL = obj.getFileURL();
+ // m_loggerBaseTime
+ // m_loggerTimer
+ m_ownIp = obj.getOwnIp();
+ m_ownPort = obj.getOwnPort();
+ m_partnerIp = obj.getPartnerIp();
+ m_partnerNick = obj.getPartnerNick();
+ m_partnerPort = obj.getPartnerPort();
+ m_resumed = obj.isResumed();
+ m_reverse = obj.isReverse();
+ m_connectionId = obj.getConnectionId();
+ m_timeLeft = obj.getTimeLeft();
+ m_timeOffer = obj.getTimeOffer();
+ m_timeTransferFinished = obj.getTimeTransferFinished();
+ m_timeTransferStarted = obj.getTimeTransferStarted();
+ // m_transferLogPosition
+ // m_transferLogTime
+ m_transferringPosition = obj.getTransferringPosition();
+ m_transferStartPosition = obj.getTransferStartPosition();
+}
+
+void DccTransfer::setConnectionId( int id )
+{
+ if ( getStatus() == Configuring || getStatus() == Queued )
+ m_connectionId = id;
+}
+
+void DccTransfer::setPartnerNick( const QString& nick )
+{
+ if ( getStatus() == Configuring || getStatus() == Queued )
+ m_partnerNick = nick;
+}
+
+bool DccTransfer::queue()
+{
+ kdDebug() << "DccTransfer::queue()" << endl;
+ if ( getStatus() != Configuring )
+ return false;
+
+ if ( m_fileName.isEmpty() )
+ return false;
+
+ if ( m_connectionId == -1 || m_partnerNick.isEmpty() )
+ return false;
+
+ setStatus( Queued );
+ return true;
+}
+
+void DccTransfer::startTransferLogger()
+{
+ m_timeTransferStarted = QDateTime::currentDateTime();
+ m_loggerBaseTime.start();
+ m_loggerTimer.start( 100 );
+}
+
+void DccTransfer::finishTransferLogger()
+{
+ if ( m_timeTransferFinished.isNull() )
+ m_timeTransferFinished = QDateTime::currentDateTime();
+ m_loggerTimer.stop();
+ updateTransferMeters();
+}
+
+// called by m_loggerTimer
+void DccTransfer::logTransfer()
+{
+ m_transferLogTime.append( m_loggerBaseTime.elapsed() );
+ m_transferLogPosition.append( m_transferringPosition );
+ updateTransferMeters();
+}
+
+void DccTransfer::setStatus( DccStatus status, const QString& statusDetail )
+{
+ bool changed = ( status != m_status );
+ DccStatus oldStatus = m_status;
+ m_status = status;
+ m_statusDetail = statusDetail;
+ if ( changed )
+ emit statusChanged( this, m_status, oldStatus );
+}
+
+void DccTransfer::updateTransferMeters()
+{
+ const int timeToCalc = 5;
+
+ if ( getStatus() == Transferring )
+ {
+ // update CurrentSpeed
+
+ // remove too old data
+ QValueList<int>::iterator itTime = m_transferLogTime.begin();
+ QValueList<KIO::fileoffset_t>::iterator itPos = m_transferLogPosition.begin();
+ while ( itTime != m_transferLogTime.end() && ( m_transferLogTime.last() - (*itTime) > timeToCalc * 1000 ) )
+ {
+ itTime = m_transferLogTime.remove( itTime );
+ itPos = m_transferLogPosition.remove( itPos );
+ }
+
+ // shift the base of the time (m_transferLoggerBaseTime)
+ // reason: QTime can't handle a time longer than 24 hours
+ int shiftOffset = m_loggerBaseTime.restart();
+ itTime = m_transferLogTime.begin();
+ for ( ; itTime != m_transferLogTime.end() ; ++itTime )
+ (*itTime) = (*itTime) - shiftOffset;
+
+ if ( m_transferLogTime.count() >= 2 )
+ {
+ // FIXME: precision of average speed is too bad
+ m_averageSpeed = (double)( m_transferringPosition - m_transferStartPosition ) / (double)m_timeTransferStarted.secsTo( QDateTime::currentDateTime() );
+ m_currentSpeed = (double)( m_transferLogPosition.last() - m_transferLogPosition.front() ) / (double)( m_transferLogTime.last() - m_transferLogTime.front() ) * 1000;
+ }
+ else // avoid zero devision
+ {
+ m_averageSpeed = DccTransfer::Calculating;
+ m_currentSpeed = DccTransfer::Calculating;
+ }
+
+ // update the remaining time
+ if ( m_currentSpeed <= 0 )
+ m_timeLeft = DccTransfer::InfiniteValue;
+ else
+ m_timeLeft = (int)( (double)( m_fileSize - m_transferringPosition ) / m_currentSpeed );
+ }
+ else if ( m_status >= Done )
+ {
+ if ( m_timeTransferStarted.secsTo( m_timeTransferFinished ) > 1 )
+ m_averageSpeed = (double)( m_transferringPosition - m_transferStartPosition ) / (double)m_timeTransferStarted.secsTo( m_timeTransferFinished );
+ else
+ m_averageSpeed = DccTransfer::InfiniteValue;
+ m_currentSpeed = 0;
+ m_timeLeft = DccTransfer::NotInTransfer;
+ }
+ else
+ {
+ m_averageSpeed = 0;
+ m_currentSpeed = 0;
+ m_timeLeft = DccTransfer::NotInTransfer;
+ }
+}
+
+QString DccTransfer::sanitizeFileName( const QString& fileName )
+{
+ QString fileNameTmp = QFileInfo( fileName ).fileName();
+ if ( fileNameTmp.startsWith( "." ) )
+ fileNameTmp.replace( 0, 1, '_' ); // Don't create hidden files
+ if ( fileNameTmp.isEmpty() )
+ fileNameTmp = "unnamed";
+ return fileNameTmp;
+}
+
+//FIXME: IPv6 support
+QString DccTransfer::getNumericalIpText( const QString& ipString )
+{
+ QHostAddress ip;
+ ip.setAddress( ipString );
+
+ return QString::number( ip.ip4Addr() );
+}
+
+unsigned long DccTransfer::intel( unsigned long value )
+{
+ value = ( (value & 0xff000000) >> 24 ) +
+ ( (value & 0xff0000) >> 8 ) +
+ ( (value & 0xff00) << 8 ) +
+ ( (value & 0xff) << 24 );
+
+ return value;
+}
+
+DccTransfer::DccType DccTransfer::getType() const
+{
+ return m_type;
+}
+
+DccTransfer::DccStatus DccTransfer::getStatus() const
+{
+ return m_status;
+}
+
+const QString& DccTransfer::getStatusDetail() const
+{
+ return m_statusDetail;
+}
+
+QDateTime DccTransfer::getTimeOffer() const
+{
+ return m_timeOffer;
+}
+
+int DccTransfer::getConnectionId() const
+{
+ return m_connectionId;
+}
+
+QString DccTransfer::getOwnIp() const
+{
+ return m_ownIp;
+}
+
+QString DccTransfer::getOwnPort() const
+{
+ return m_ownPort;
+}
+
+QString DccTransfer::getPartnerNick() const
+{
+ return m_partnerNick;
+}
+
+QString DccTransfer::getPartnerIp() const
+{
+ return m_partnerIp;
+}
+
+QString DccTransfer::getPartnerPort() const
+{
+ return m_partnerPort;
+}
+
+QString DccTransfer::getFileName() const
+{
+ return m_fileName;
+}
+
+KIO::filesize_t DccTransfer::getFileSize() const
+{
+ return m_fileSize;
+}
+
+KIO::fileoffset_t DccTransfer::getTransferringPosition() const
+{
+ return m_transferringPosition;
+}
+
+KIO::fileoffset_t DccTransfer::getTransferStartPosition() const
+{
+ return m_transferStartPosition;
+}
+
+KURL DccTransfer::getFileURL() const
+{
+ return m_fileURL;
+}
+
+bool DccTransfer::isResumed() const
+{
+ return m_resumed;
+}
+
+bool DccTransfer::isReverse() const
+{
+ return m_reverse;
+}
+
+QString DccTransfer::getReverseToken() const
+{
+ return m_reverseToken;
+}
+
+transferspeed_t DccTransfer::getAverageSpeed() const
+{
+ return m_averageSpeed;
+}
+
+transferspeed_t DccTransfer::getCurrentSpeed() const
+{
+ return m_currentSpeed;
+}
+
+int DccTransfer::getTimeLeft() const
+{
+ return m_timeLeft;
+}
+
+int DccTransfer::getProgress() const
+{
+ return (int)( ( (double)getTransferringPosition() / (double)getFileSize() ) * 100 );
+}
+
+QDateTime DccTransfer::getTimeTransferStarted() const
+{
+ return m_timeTransferStarted;
+}
+
+QDateTime DccTransfer::getTimeTransferFinished() const
+{
+ return m_timeTransferFinished;
+}
+
+#include "dcctransfer.moc"
diff --git a/konversation/src/dcctransfer.h b/konversation/src/dcctransfer.h
new file mode 100644
index 0000000..d264def
--- /dev/null
+++ b/konversation/src/dcctransfer.h
@@ -0,0 +1,186 @@
+/*
+ This class represents a DCC transfer.
+*/
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002-2004 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+ Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+*/
+
+#ifndef DCCTRANSFER_H
+#define DCCTRANSFER_H
+
+#include <qdatetime.h>
+#include <qobject.h>
+#include <qtimer.h>
+
+#include <kurl.h>
+#include <kio/global.h>
+
+typedef double transferspeed_t;
+
+class DccTransfer : public QObject
+{
+ Q_OBJECT
+
+ public:
+ enum DccType
+ {
+ Receive,
+ Send
+ };
+
+ enum DccStatus
+ {
+ Configuring = 0, // Not queud yet (this means that user can't see the item at this time)
+ Queued, // Newly added DCC, waiting user's response
+ Preparing, // Opening KIO to write received data
+ WaitingRemote, // Waiting for remote host's response
+ Connecting, // RECV: trying to connect to the server
+ Transferring,
+ Done,
+ Failed,
+ Aborted
+ };
+
+ enum UnavailableStatus
+ {
+ Calculating = -1,
+ NotInTransfer = -2,
+ InfiniteValue = -3
+ };
+
+ DccTransfer( DccType dccType, QObject* parent );
+ virtual ~DccTransfer();
+
+ // info of DccTransfer can be copied with this constructor.
+ DccTransfer( const DccTransfer& obj );
+
+ DccType getType() const;
+ DccStatus getStatus() const;
+ const QString& getStatusDetail() const;
+ QDateTime getTimeOffer() const;
+ int getConnectionId() const;
+ QString getOwnIp() const;
+ QString getOwnPort() const;
+ QString getPartnerNick() const;
+ QString getPartnerIp() const;
+ QString getPartnerPort() const;
+ QString getFileName() const;
+ KIO::filesize_t getFileSize() const;
+ KIO::fileoffset_t getTransferringPosition() const;
+ KIO::fileoffset_t getTransferStartPosition() const;
+ KURL getFileURL() const;
+ bool isResumed() const;
+ bool isReverse() const;
+ QString getReverseToken() const;
+ transferspeed_t getAverageSpeed() const;
+ transferspeed_t getCurrentSpeed() const;
+ int getTimeLeft() const;
+ int getProgress() const;
+ QDateTime getTimeTransferStarted() const;
+ QDateTime getTimeTransferFinished() const;
+
+ // common settings for DccTransferRecv / DccTransferSend
+
+ // REQUIRED
+ void setConnectionId( int connectionId );
+ // REQUIRED
+ void setPartnerNick( const QString& nick );
+
+ signals:
+ void transferStarted( DccTransfer* item );
+ void done( DccTransfer* item );
+ void statusChanged( DccTransfer* item, int newStatus, int oldStatus );
+
+ public slots:
+ virtual bool queue();
+ virtual void start() {};
+ virtual void abort() {};
+
+ protected:
+ void setStatus( DccStatus status, const QString& statusDetail = QString() );
+ void startTransferLogger();
+ void finishTransferLogger();
+
+ static QString sanitizeFileName( const QString& fileName );
+ static QString getNumericalIpText( const QString& ipString );
+ static unsigned long intel( unsigned long value );
+
+ protected slots:
+ void logTransfer();
+
+ protected:
+ // transfer information
+ DccType m_type;
+ DccStatus m_status;
+ QString m_statusDetail;
+ bool m_resumed;
+ bool m_reverse;
+ QString m_reverseToken;
+ KIO::fileoffset_t m_transferringPosition;
+ KIO::fileoffset_t m_transferStartPosition;
+
+ /*
+ QValueList<QDateTime> m_transferTimeLog; // write per packet to calc CPS
+ QValueList<KIO::fileoffset_t> m_transferPositionLog; // write per packet to calc CPS
+ */
+
+ // we'll communicate with the partner via this server
+ int m_connectionId;
+ QString m_partnerNick;
+ QString m_partnerIp; // null when unknown
+ QString m_partnerPort;
+ QString m_ownIp;
+ QString m_ownPort;
+
+ unsigned long m_bufferSize;
+ char* m_buffer;
+
+ /**
+ * The filename.
+ * For receiving, it holds the filename as the sender said.
+ * So be careful, it can contain "../" and so on.
+ */
+ QString m_fileName;
+
+ /** The file size of the complete file sending/recieving. */
+ KIO::filesize_t m_fileSize;
+
+ /**
+ * If we are sending a file, this is the url of the file we are sending.
+ * If we are recieving a file, this is the url of the file we are saving
+ * to in the end (Temporararily it will be filename+".part" ).
+ */
+ KURL m_fileURL;
+
+ private:
+ DccTransfer& operator = ( const DccTransfer& obj );
+
+ void updateTransferMeters();
+
+ private:
+ QDateTime m_timeOffer;
+ QDateTime m_timeTransferStarted;
+ //QDateTime m_timeLastActive;
+ QDateTime m_timeTransferFinished;
+
+ QTimer m_loggerTimer;
+ QTime m_loggerBaseTime; // for calculating CPS
+ QValueList<int> m_transferLogTime;
+ QValueList<KIO::fileoffset_t> m_transferLogPosition;
+
+ transferspeed_t m_averageSpeed;
+ transferspeed_t m_currentSpeed;
+ int m_timeLeft;
+};
+
+#endif // DCCTRANSFER_H
diff --git a/konversation/src/dcctransferdetailedinfopanel.cpp b/konversation/src/dcctransferdetailedinfopanel.cpp
new file mode 100644
index 0000000..f7a55ff
--- /dev/null
+++ b/konversation/src/dcctransferdetailedinfopanel.cpp
@@ -0,0 +1,214 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2007 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#include "dcctransferdetailedinfopanel.h"
+#include "channel.h"
+#include "dcctransfer.h"
+#include "dcctransfermanager.h"
+#include "dcctransferrecv.h"
+#include "dcctransferpanelitem.h"
+#include "konversationapplication.h"
+#include "connectionmanager.h"
+#include "server.h"
+
+#include <qlabel.h>
+#include <qtimer.h>
+
+#include <klineedit.h>
+#include <klocale.h>
+#include <kprogress.h>
+#include <krun.h>
+#include <kurlrequester.h>
+#include <ksqueezedtextlabel.h>
+
+
+DccTransferDetailedInfoPanel::DccTransferDetailedInfoPanel( QWidget* parent, const char* name )
+ : DccTransferDetailedInfoPanelUI( parent, name )
+{
+ m_autoViewUpdateTimer = new QTimer( this );
+
+ connect( m_urlreqLocation, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotLocationChanged( const QString& ) ) );
+ connect( m_buttonOpenFolder, SIGNAL( clicked() ), this, SLOT( slotOpenFolderButtonClicked() ) );
+ connect( KonversationApplication::instance()->getDccTransferManager(), SIGNAL( fileURLChanged( DccTransferRecv* ) ),
+ this, SLOT( updateView() ) ); // it's a little rough..
+}
+
+DccTransferDetailedInfoPanel::~DccTransferDetailedInfoPanel()
+{
+}
+
+void DccTransferDetailedInfoPanel::setItem( DccTransferPanelItem* item )
+{
+ m_autoViewUpdateTimer->stop();
+
+ // disconnect all slots once
+ disconnect( this );
+ // we can't do disconnect( m_item->transfer(), 0, this, 0 ) here
+ // because m_item can have been deleted already.
+
+ // set up the auto view-update timer
+ connect( m_autoViewUpdateTimer, SIGNAL( timeout() ), this, SLOT( updateView() ) );
+
+ m_item = item;
+
+ // If the file is already being transferred, the timer must be started here,
+ // otherwise the information will not be updated every 0.5sec
+ if (m_item->transfer()->getStatus() == DccTransfer::Transferring)
+ m_autoViewUpdateTimer->start(500, false);
+ connect( m_item->transfer(), SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, SLOT( slotTransferStatusChanged( DccTransfer*, int, int ) ) );
+
+ updateView();
+}
+
+void DccTransferDetailedInfoPanel::updateView()
+{
+ DccTransfer* transfer = m_item->transfer();
+
+ // Type:
+ QString type( transfer->getType() == DccTransfer::Send ? i18n( "DCC Send" ) : i18n( "DCC Receive" ) );
+ if ( transfer->isReverse() )
+ type += i18n( " (Reverse DCC)" );
+ m_labelDccType->setText( type );
+
+ // Filename:
+ m_labelFilename->setText( transfer->getFileName() );
+
+ // Location:
+ m_urlreqLocation->setURL( transfer->getFileURL().prettyURL() );
+ //m_urlreqLocation->lineEdit()->setFocusPolicy( transfer->getStatus() == DccTransfer::Queued ? StrongFocus : ClickFocus );
+ m_urlreqLocation->lineEdit()->setReadOnly( transfer->getStatus() != DccTransfer::Queued );
+ m_urlreqLocation->lineEdit()->setFrame( transfer->getStatus() == DccTransfer::Queued );
+ m_urlreqLocation->button()->setEnabled( transfer->getStatus() == DccTransfer::Queued );
+ m_buttonOpenFolder->setEnabled( !m_urlreqLocation->lineEdit()->text().isEmpty() );
+
+ // Partner:
+ QString partnerInfoServerName;
+ if ( transfer->getConnectionId() == -1 )
+ partnerInfoServerName = i18n( "Unknown server" );
+ else if ( KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( transfer->getConnectionId() ) )
+ partnerInfoServerName = KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( transfer->getConnectionId() )->getServerName();
+ else
+ partnerInfoServerName = i18n( "Unknown server" );
+ QString partnerInfo( i18n( "%1 on %2" )
+ .arg( transfer->getPartnerNick().isEmpty() ? "?" : transfer->getPartnerNick() )
+ .arg( partnerInfoServerName ) );
+ if ( !transfer->getPartnerIp().isEmpty() )
+ partnerInfo += i18n( ", %1 (port %2)" ).arg( transfer->getPartnerIp() ).arg( transfer->getPartnerPort() );
+ m_labelPartner->setText( partnerInfo );
+
+ // Self:
+ if ( transfer->getOwnIp().isEmpty() )
+ m_labelSelf->setText( "" );
+ else
+ m_labelSelf->setText( i18n( "%1 (port %2)" ).arg( transfer->getOwnIp() ).arg( transfer->getOwnPort() ) );
+
+ // Status:
+ if ( transfer->getStatus() == DccTransfer::Transferring )
+ m_labelStatus->setText( m_item->getStatusText() + " ( " + m_item->getCurrentSpeedPrettyText() + " )" );
+ else
+ m_labelStatus->setText( transfer->getStatusDetail().isEmpty() ? m_item->getStatusText() : m_item->getStatusText() + " (" + transfer->getStatusDetail() + ')' );
+
+ // Progress:
+ m_progress->setProgress( transfer->getProgress() );
+
+ // Current Position:
+ m_labelCurrentPosition->setText( KGlobal::locale()->formatNumber( transfer->getTransferringPosition(), 0 ) );
+
+ // File Size:
+ m_labelFileSize->setText( KGlobal::locale()->formatNumber( transfer->getFileSize(), 0 ) );
+
+ // Current Speed:
+ m_labelCurrentSpeed->setText( m_item->getCurrentSpeedPrettyText() );
+
+ // Average Speed:
+ m_labelAverageSpeed->setText( m_item->getAverageSpeedPrettyText() );
+
+ // Resumed:
+ if ( transfer->isResumed() )
+ m_labelIsResumed->setText( i18n( "Yes, %1" ).arg( KGlobal::locale()->formatNumber( transfer->getTransferStartPosition(), 0 ) ) );
+ else
+ m_labelIsResumed->setText( i18n( "No" ) );
+
+ // Transferring Time:
+ if ( transfer->getTimeTransferStarted().isNull() )
+ m_labelTransferringTime->setText( "" );
+ else
+ {
+ int transferringTime;
+
+ // The transfer is still in progress
+ if ( transfer->getTimeTransferFinished().isNull() )
+ transferringTime = transfer->getTimeTransferStarted().secsTo( QDateTime::currentDateTime() );
+ // The transfer has finished
+ else
+ transferringTime = transfer->getTimeTransferStarted().secsTo( transfer->getTimeTransferFinished() );
+
+ if ( transferringTime >= 1 )
+ m_labelTransferringTime->setText( DccTransferPanelItem::secToHMS( transferringTime ) );
+ else
+ m_labelTransferringTime->setText( i18n( "< 1sec" ) );
+ }
+
+ // Estimated Time Left:
+ m_labelTimeLeft->setText( m_item->getTimeLeftPrettyText() );
+
+ // Offered at:
+ m_labelTimeOffered->setText( transfer->getTimeOffer().toString( "hh:mm:ss" ) );
+
+ // Started at:
+ if ( !transfer->getTimeTransferStarted().isNull() )
+ m_labelTimeStarted->setText( transfer->getTimeTransferStarted().toString( "hh:mm:ss" ) );
+ else
+ m_labelTimeStarted->setText( "" );
+
+ // Finished at:
+ if ( !transfer->getTimeTransferFinished().isNull() )
+ m_labelTimeFinished->setText( transfer->getTimeTransferFinished().toString( "hh:mm:ss" ) );
+ else
+ m_labelTimeFinished->setText( "" );
+}
+
+void DccTransferDetailedInfoPanel::slotTransferStatusChanged( DccTransfer* /* transfer */, int newStatus, int oldStatus )
+{
+ updateView();
+ if ( newStatus == DccTransfer::Transferring )
+ {
+ // start auto view-update timer
+ m_autoViewUpdateTimer->start( 500, false );
+ }
+ else if ( oldStatus == DccTransfer::Transferring )
+ {
+ // stop auto view-update timer
+ m_autoViewUpdateTimer->stop();
+ }
+}
+
+void DccTransferDetailedInfoPanel::slotLocationChanged( const QString& url )
+{
+ if ( m_item->transfer()->getType() == DccTransfer::Receive )
+ {
+ DccTransferRecv* transfer = static_cast< DccTransferRecv* >( m_item->transfer() );
+ transfer->setFileURL( KURL::fromPathOrURL( url ) );
+ }
+}
+
+void DccTransferDetailedInfoPanel::slotOpenFolderButtonClicked()
+{
+ QString urlString = m_urlreqLocation->lineEdit()->text();
+ if ( !urlString.isEmpty() )
+ {
+ KURL url = KURL::fromPathOrURL( urlString );
+ url.setFileName( QString() );
+ new KRun( url, 0, true, true );
+ }
+}
+
+#include "dcctransferdetailedinfopanel.moc"
diff --git a/konversation/src/dcctransferdetailedinfopanel.h b/konversation/src/dcctransferdetailedinfopanel.h
new file mode 100644
index 0000000..38eb692
--- /dev/null
+++ b/konversation/src/dcctransferdetailedinfopanel.h
@@ -0,0 +1,44 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2007 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#ifndef DCCTRANSFERDETAILEDINFOPANEL_H
+#define DCCTRANSFERDETAILEDINFOPANEL_H
+
+#include "dcctransferdetailedinfopanelui.h"
+
+
+class QTimer;
+
+class DccTransfer;
+class DccTransferPanelItem;
+
+class DccTransferDetailedInfoPanel : public DccTransferDetailedInfoPanelUI
+{
+ Q_OBJECT
+
+ public:
+ explicit DccTransferDetailedInfoPanel( QWidget* parent = 0, const char* name = 0 );
+ virtual ~DccTransferDetailedInfoPanel();
+
+ void setItem( DccTransferPanelItem* item );
+
+ private slots:
+ void updateView();
+ void slotTransferStatusChanged( DccTransfer* transfer, int newStatus, int oldStatus );
+ void slotLocationChanged( const QString& url );
+ void slotOpenFolderButtonClicked();
+
+ private:
+ DccTransferPanelItem* m_item;
+ QTimer* m_autoViewUpdateTimer;
+};
+
+#endif // DCCTRANSFERDETAILEDINFOPANEL_H
diff --git a/konversation/src/dcctransferdetailedinfopanelui.ui b/konversation/src/dcctransferdetailedinfopanelui.ui
new file mode 100644
index 0000000..9c3f136
--- /dev/null
+++ b/konversation/src/dcctransferdetailedinfopanelui.ui
@@ -0,0 +1,492 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DccTransferDetailedInfoPanelUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DccTransferDetailedInfoPanelUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>646</width>
+ <height>289</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KSqueezedTextLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_labelFilename</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_labelDccType</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Filename:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Self:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Status:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Location:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel18</cstring>
+ </property>
+ <property name="text">
+ <string>Progress:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>m_labelStatus</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>m_labelPartner</cstring>
+ </property>
+ </widget>
+ <widget class="KProgress" row="5" column="1">
+ <property name="name">
+ <cstring>m_progress</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel23</cstring>
+ </property>
+ <property name="text">
+ <string>Partner:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="6" column="1">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_urlreqLocation</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_buttonOpenFolder</cstring>
+ </property>
+ <property name="text">
+ <string>Open Folder</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>m_labelSelf</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout26</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout24</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>m_labelCurrentSpeed</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel21</cstring>
+ </property>
+ <property name="text">
+ <string>File Size:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>m_labelIsResumed</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_labelFileSize</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Average Speed:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel14</cstring>
+ </property>
+ <property name="text">
+ <string>Resumed:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_labelCurrentPosition</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Current Speed:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel19</cstring>
+ </property>
+ <property name="text">
+ <string>Current Position:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>m_labelAverageSpeed</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line3</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>VLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout25</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Estimated Time Left:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Offered at:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>m_labelTimeFinished</cstring>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_labelTransferringTime</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Finished at:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel6_2</cstring>
+ </property>
+ <property name="text">
+ <string>Started at:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_labelTimeLeft</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>m_labelTimeOffered</cstring>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Transferring Time:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>m_labelTimeStarted</cstring>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>110</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kprogress.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>ksqueezedtextlabel.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/dcctransfermanager.cpp b/konversation/src/dcctransfermanager.cpp
new file mode 100644
index 0000000..41b73bd
--- /dev/null
+++ b/konversation/src/dcctransfermanager.cpp
@@ -0,0 +1,238 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2007 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#include "dcctransfermanager.h"
+#include "dcctransferrecv.h"
+#include "dcctransfersend.h"
+#include "konversationapplication.h"
+#include "preferences.h"
+
+#include <kdebug.h>
+
+
+DccTransferManager::DccTransferManager( QObject* parent )
+ : QObject( parent )
+{
+ // initial number
+ m_nextReverseTokenNumber = 1001;
+
+ m_defaultIncomingFolder = Preferences::dccPath();
+
+ connect( KonversationApplication::instance(), SIGNAL( appearanceChanged() ),
+ this, SLOT( slotSettingsChanged() ) );
+}
+
+DccTransferManager::~DccTransferManager()
+{
+ m_sendItems.clear();
+ m_recvItems.clear();
+}
+
+DccTransferRecv* DccTransferManager::newDownload()
+{
+ DccTransferRecv* transfer = new DccTransferRecv(this);
+ m_recvItems.push_back( transfer );
+ connect( transfer, SIGNAL( done( DccTransfer* ) ), this, SLOT( removeRecvItem( DccTransfer* ) ) );
+ initTransfer( transfer );
+ return transfer;
+}
+
+DccTransferSend* DccTransferManager::newUpload()
+{
+ DccTransferSend* transfer = new DccTransferSend(this);
+ m_sendItems.push_back( transfer );
+ connect( transfer, SIGNAL( done( DccTransfer* ) ), this, SLOT( removeSendItem( DccTransfer* ) ) );
+ initTransfer( transfer );
+ return transfer;
+}
+
+DccTransferRecv* DccTransferManager::resumeDownload( int connectionId, const QString& partnerNick, const QString& fileName, const QString& ownPort, unsigned long position )
+{
+ DccTransferRecv* transfer = 0;
+
+ // find applicable one
+ QValueListConstIterator< DccTransferRecv* > it;
+ for ( it = m_recvItems.begin() ; it != m_recvItems.end() ; ++it )
+ {
+ if ( ( (*it)->getStatus() == DccTransfer::Queued || (*it)->getStatus() == DccTransfer::WaitingRemote ) &&
+ (*it)->getConnectionId() == connectionId &&
+ (*it)->getPartnerNick() == partnerNick &&
+ (*it)->getFileName() == fileName &&
+ (*it)->isResumed() )
+ {
+ transfer = (*it);
+ kdDebug() << "DccTransferManager::resumeDownload(): filename match: " << fileName << ", claimed port: " << ownPort << ", item port: " << transfer->getOwnPort() << endl;
+ // the port number can be changed behind NAT, so we pick an item which only the filename is correspondent in that case.
+ if ( transfer->getOwnPort() == ownPort )
+ {
+ break;
+ }
+ }
+ }
+
+ if ( transfer )
+ transfer->startResume( position );
+
+ return transfer;
+}
+
+DccTransferSend* DccTransferManager::resumeUpload( int connectionId, const QString& partnerNick, const QString& fileName, const QString& ownPort, unsigned long position )
+{
+ DccTransferSend* transfer = 0;
+
+ // find applicable one
+ QValueListConstIterator< DccTransferSend* > it;
+ for ( it = m_sendItems.begin() ; it != m_sendItems.end() ; ++it )
+ {
+ if ( ( (*it)->getStatus() == DccTransfer::Queued || (*it)->getStatus() == DccTransfer::WaitingRemote ) &&
+ (*it)->getConnectionId() == connectionId &&
+ (*it)->getPartnerNick() == partnerNick &&
+ (*it)->getFileName() == fileName &&
+ !(*it)->isResumed() )
+ {
+ transfer = (*it);
+ kdDebug() << "DccTransferManager::resumeUpload(): filename match: " << fileName << ", claimed port: " << ownPort << ", item port: " << transfer->getOwnPort() << endl;
+ // the port number can be changed behind NAT, so we pick an item which only the filename is correspondent in that case.
+ if ( transfer->getOwnPort() == ownPort )
+ {
+ break;
+ }
+ }
+ }
+
+ if ( transfer )
+ transfer->setResume( position );
+
+ return transfer;
+}
+
+DccTransferSend* DccTransferManager::startReverseSending( int connectionId, const QString& partnerNick, const QString& fileName, const QString& partnerHost, const QString& partnerPort, unsigned long fileSize, const QString& token )
+{
+ kdDebug() << "DccTransferManager::startReverseSending(): server group ID: " << connectionId << ", partner: " << partnerNick << ", filename: " << fileName << ", partner IP: " << partnerHost << ", parnter port: " << partnerPort << ", filesize: " << fileSize << ", token: " << token << endl;
+ DccTransferSend* transfer = 0;
+
+ // find applicable one
+ QValueListConstIterator< DccTransferSend* > it;
+ for ( it = m_sendItems.begin() ; it != m_sendItems.end() ; ++it )
+ {
+ if (
+ (*it)->getStatus() == DccTransfer::WaitingRemote &&
+ (*it)->getConnectionId() == connectionId &&
+ (*it)->getPartnerNick() == partnerNick &&
+ (*it)->getFileName() == fileName &&
+ (*it)->getFileSize() == fileSize &&
+ (*it)->getReverseToken() == token
+ )
+ {
+ transfer = (*it);
+ break;
+ }
+ }
+
+ if ( transfer )
+ transfer->connectToReceiver( partnerHost, partnerPort );
+
+ return transfer;
+}
+
+void DccTransferManager::initTransfer( DccTransfer* transfer )
+{
+ connect( transfer, SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, SLOT( slotTransferStatusChanged( DccTransfer*, int, int ) ) );
+
+ emit newTransferAdded( transfer );
+}
+
+bool DccTransferManager::isLocalFileInWritingProcess( const KURL& url ) const
+{
+ QValueListConstIterator< DccTransferRecv* > it;
+ for ( it = m_recvItems.begin() ; it != m_recvItems.end() ; ++it )
+ {
+ if ( ( (*it)->getStatus() == DccTransfer::Connecting ||
+ (*it)->getStatus() == DccTransfer::Transferring ) &&
+ (*it)->getFileURL() == url )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+int DccTransferManager::generateReverseTokenNumber()
+{
+ return m_nextReverseTokenNumber++;
+}
+
+bool DccTransferManager::hasActiveTransfers()
+{
+ QValueListConstIterator< DccTransferSend* > it;
+ for ( it = m_sendItems.begin() ; it != m_sendItems.end() ; ++it )
+ {
+ if ((*it)->getStatus() == DccTransfer::Transferring)
+ return true;
+ }
+
+ QValueListConstIterator< DccTransferRecv* > it2;
+ for ( it2 = m_recvItems.begin() ; it2 != m_recvItems.end() ; ++it2 )
+ {
+ if ((*it2)->getStatus() == DccTransfer::Transferring)
+ return true;
+ }
+
+ return false;
+}
+
+void DccTransferManager::slotTransferStatusChanged( DccTransfer* item, int newStatus, int oldStatus )
+{
+ kdDebug() << "DccTransferManager::slotTransferStatusChanged(): " << oldStatus << " -> " << newStatus << " " << item->getFileName() << " (" << item->getType() << ")" << endl;
+
+ if ( newStatus == DccTransfer::Queued )
+ emit newTransferQueued( item );
+}
+
+void DccTransferManager::slotSettingsChanged()
+{
+ // update the default incoming directory for already existed DCCRECV items
+ if ( Preferences::dccPath() != m_defaultIncomingFolder )
+ {
+ QValueListConstIterator< DccTransferRecv* > it;
+ for ( it = m_recvItems.begin() ; it != m_recvItems.end() ; ++it )
+ {
+ if ( (*it)->getStatus() == DccTransfer::Queued &&
+ (*it)->getFileURL().directory() == m_defaultIncomingFolder )
+ {
+ KURL url;
+ url.setDirectory( Preferences::dccPath() );
+ url.setFileName( (*it)->getFileURL().fileName() );
+ (*it)->setFileURL( url );
+
+ emit fileURLChanged( *it );
+ }
+ }
+
+ m_defaultIncomingFolder = Preferences::dccPath();
+ }
+}
+
+void DccTransferManager::removeSendItem( DccTransfer* item_ )
+{
+ DccTransferSend* item = static_cast< DccTransferSend* > ( item_ );
+ m_sendItems.remove( item );
+ item->deleteLater();
+}
+
+void DccTransferManager::removeRecvItem( DccTransfer* item_ )
+{
+ DccTransferRecv* item = static_cast< DccTransferRecv* > ( item_ );
+ m_recvItems.remove( item );
+ item->deleteLater();
+}
+
+#include "dcctransfermanager.moc"
diff --git a/konversation/src/dcctransfermanager.h b/konversation/src/dcctransfermanager.h
new file mode 100644
index 0000000..0f2d655
--- /dev/null
+++ b/konversation/src/dcctransfermanager.h
@@ -0,0 +1,95 @@
+/*
+ DccTransferManager controls DccTransfer instances.
+ All DccTransferRecv/DccTransferSend instances are created and deleted by this class.
+ Each DccTransfer instance is deleted immediately after its transfer done.
+*/
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2007 Shintaro Matsuoka <shin@shoegazed.org>
+*/
+
+#ifndef DCCTRANSFERMANAGER_H
+#define DCCTRANSFERMANAGER_H
+
+#include "dcctransfer.h"
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+
+class KURL;
+
+class DccTransferRecv;
+class DccTransferSend;
+
+class DccTransferManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ DccTransferManager( QObject* parent = 0 );
+ ~DccTransferManager();
+
+ signals:
+ /*
+ * The status of the item is DccTransfer::Configuring when this signal is emitted.
+ */
+ void newTransferAdded( DccTransfer* transfer );
+ /*
+ * The status of the item is DccTransfer::Queued when this signal is emitted.
+ */
+ void newTransferQueued( DccTransfer* transfer );
+
+ void fileURLChanged( DccTransferRecv* transfer );
+
+ public:
+ DccTransferRecv* newDownload();
+ DccTransferSend* newUpload();
+
+ /**
+ * @return a DccTransferRecv item if applicable one found, otherwise 0.
+ */
+ DccTransferRecv* resumeDownload(int connectionId, const QString& partnerNick, const QString& fileName, const QString& ownPort, unsigned long position );
+
+ /**
+ * @return a DccTransferSend item if applicable one found, otherwise 0.
+ */
+ DccTransferSend* resumeUpload(int connectionId, const QString& partnerNick, const QString& fileName, const QString& ownPort, unsigned long position );
+
+ DccTransferSend* startReverseSending(int connectionId, const QString& partnerNick, const QString& fileName, const QString& partnerHost, const QString& partnerPort, unsigned long fileSize, const QString& token );
+
+ bool isLocalFileInWritingProcess( const KURL& localUrl ) const;
+
+ int generateReverseTokenNumber();
+
+ bool hasActiveTransfers();
+
+ private:
+ /*
+ * initTransfer() does the common jobs for newDownload() and newUpload()
+ */
+ void initTransfer( DccTransfer* transfer );
+
+ private slots:
+ void slotTransferStatusChanged( DccTransfer* item, int newStatus, int oldStatus );
+ void removeSendItem( DccTransfer* item );
+ void removeRecvItem( DccTransfer* item );
+
+ void slotSettingsChanged();
+
+ private:
+ QValueList< DccTransferSend* > m_sendItems;
+ QValueList< DccTransferRecv* > m_recvItems;
+
+ int m_nextReverseTokenNumber;
+ QString m_defaultIncomingFolder; // store here to know if this settings is changed
+};
+
+#endif // DCCTRANSFERMANAGER_H
diff --git a/konversation/src/dcctransferpanel.cpp b/konversation/src/dcctransferpanel.cpp
new file mode 100644
index 0000000..3a2df9c
--- /dev/null
+++ b/konversation/src/dcctransferpanel.cpp
@@ -0,0 +1,459 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2008 Shintaro Matsuoka <shin@shoegazed.org>
+
+#include "dcctransferpanel.h"
+#include "konversationapplication.h"
+#include "dcctransferdetailedinfopanel.h"
+#include "dcctransfermanager.h"
+#include "dcctransferpanelitem.h"
+#include "dcctransfersend.h"
+#include "preferences.h"
+
+#include <qhbox.h>
+#include <qheader.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kdialog.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <krun.h>
+#include <kapplication.h>
+
+
+DccTransferPanel::DccTransferPanel(QWidget* parent) : ChatWindow(parent)
+{
+ setType(ChatWindow::DccTransferPanel);
+ setName(i18n("DCC Status"));
+
+ initGUI();
+
+ connect( KonversationApplication::instance()->getDccTransferManager(), SIGNAL( newTransferAdded( DccTransfer* ) ), this, SLOT( slotNewTransferAdded( DccTransfer* ) ) );
+}
+
+DccTransferPanel::~DccTransferPanel()
+{
+ // remember column widths
+ QValueList<int> columnWidths;
+ for ( uint i = 0 ; i < Column::COUNT ; ++i )
+ columnWidths.push_back( m_listView->columnWidth( i ) );
+ Preferences::setDccColumnWidths( columnWidths );
+}
+
+void DccTransferPanel::initGUI()
+{
+ setSpacing( 0 );
+
+ m_listView = new KListView(this,"dcc_control_panel");
+
+ m_listView->setSelectionMode(QListView::Extended);
+ m_listView->setDragEnabled(true);
+ m_listView->setAcceptDrops(true);
+ m_listView->setSorting(-1,false);
+ m_listView->setAllColumnsShowFocus(true);
+
+ for(unsigned int i=0 ; i < Column::COUNT ; ++i)
+ m_listView->addColumn("");
+
+ //m_listView->setColumnText(Column::TypeIcon, "");
+ m_listView->setColumnText(Column::OfferDate, i18n("Started at"));
+ m_listView->setColumnText(Column::Status, i18n("Status"));
+ m_listView->setColumnText(Column::FileName, i18n("File"));
+ m_listView->setColumnText(Column::PartnerNick, i18n("Partner"));
+ m_listView->setColumnText(Column::Progress, i18n("Progress"));
+ m_listView->setColumnText(Column::Position, i18n("Position"));
+ m_listView->setColumnText(Column::TimeLeft, i18n("Remaining"));
+ m_listView->setColumnText(Column::CurrentSpeed, i18n("Speed"));
+ m_listView->setColumnText(Column::SenderAddress, i18n("Sender Address"));
+
+ QValueList<int> columnWidths = Preferences::dccColumnWidths();
+ for ( uint i = 0 ; i < Column::COUNT && i < columnWidths.count() ; ++i )
+ m_listView->setColumnWidth( i, columnWidths[i] );
+
+ m_listView->setColumnWidthMode(Column::FileName, QListView::Manual);
+
+ m_listView->setColumnAlignment(Column::OfferDate, AlignHCenter);
+ m_listView->setColumnAlignment(Column::Progress, AlignHCenter);
+ m_listView->setColumnAlignment(Column::Position, AlignHCenter);
+ m_listView->setColumnAlignment(Column::TimeLeft, AlignHCenter);
+ m_listView->setColumnAlignment(Column::CurrentSpeed, AlignHCenter);
+
+ m_listView->setSorting(Column::OfferDate, false);
+
+ connect(m_listView,SIGNAL (selectionChanged()),this,SLOT (updateButton()) );
+
+ // detailed info panel
+ m_detailPanel = new DccTransferDetailedInfoPanel(this);
+
+ // button
+
+ QHBox* buttonsBox=new QHBox(this);
+ buttonsBox->setSpacing(spacing());
+
+ // convenience, undeffed below again to avoid name clashes
+ #define icon(s) KGlobal::iconLoader()->loadIconSet( s, KIcon::Small )
+
+ m_buttonAccept = new QPushButton(icon("player_play"), i18n("Accept"), buttonsBox, "start_dcc");
+ m_buttonAbort = new QPushButton(icon("stop"), i18n("Abort"), buttonsBox, "abort_dcc");
+ m_buttonClear = new QPushButton(icon("editdelete"), i18n("Clear"), buttonsBox, "clear_dcc");
+ m_buttonOpen = new QPushButton(icon("exec"), i18n("Open File"), buttonsBox, "open_dcc_file");
+ m_buttonDetail = new QPushButton(icon("view_text"), i18n("Details"), buttonsBox, "detail_dcc");
+ m_buttonDetail->setToggleButton( true );
+
+ QToolTip::add( m_buttonAccept, i18n( "Start receiving" ) );
+ QToolTip::add( m_buttonAbort, i18n( "Abort the transfer(s)" ) );
+ QToolTip::add( m_buttonOpen, i18n( "Run the file" ) );
+ QToolTip::add( m_buttonDetail, i18n( "View DCC transfer details" ) );
+
+ connect( m_buttonAccept, SIGNAL(clicked()), this, SLOT(acceptDcc()) );
+ connect( m_buttonAbort, SIGNAL(clicked()), this, SLOT(abortDcc()) );
+ connect( m_buttonClear, SIGNAL(clicked()), this, SLOT(clearDcc()) );
+ connect( m_buttonOpen, SIGNAL(clicked()), this, SLOT(runDcc()) );
+ //connect( m_buttonDetail, SIGNAL(clicked()), this, SLOT(openDetail()) );
+ connect( m_buttonDetail, SIGNAL(toggled(bool)), m_detailPanel, SLOT(setShown(bool)) );
+ m_buttonDetail->setOn(true);
+
+
+ // popup menu
+
+ m_popup = new KPopupMenu(this);
+ m_popup->insertItem( i18n("&Select All Items"), Popup::SelectAll);
+ m_popup->insertItem( i18n("S&elect All Completed Items"), Popup::SelectAllCompleted);
+ m_popup->insertSeparator(); // -----
+ m_popup->insertItem(icon("player_play"), i18n("&Accept"), Popup::Accept);
+ m_popup->insertItem(icon("stop"), i18n("A&bort"), Popup::Abort);
+ m_popup->insertSeparator(); // -----
+ // FIXME: make it neat
+ m_popup->insertItem(icon("redo"), i18n("Resend"), Popup::Resend);
+ m_popup->insertItem(icon("editdelete"), i18n("&Clear"), Popup::Clear);
+ m_popup->insertSeparator(); // -----
+ m_popup->insertItem(icon("exec"), i18n("&Open File"), Popup::Open);
+ m_popup->insertItem(icon("messagebox_info"), i18n("File &Information"), Popup::Info);
+
+ #undef icon
+
+ connect(m_listView, SIGNAL(contextMenuRequested(QListViewItem*,const QPoint&,int)), this, SLOT(popupRequested(QListViewItem*,const QPoint&,int)));
+ connect(m_popup, SIGNAL(activated(int)), this, SLOT(popupActivated(int)));
+
+ // misc.
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem*,const QPoint&,int)), this, SLOT(doubleClicked(QListViewItem*,const QPoint&,int)));
+
+ connect(m_listView, SIGNAL(currentChanged(QListViewItem*)), this, SLOT(setDetailPanelItem(QListViewItem*)));
+
+ updateButton();
+}
+
+void DccTransferPanel::slotNewTransferAdded( DccTransfer* transfer )
+{
+ DccTransferPanelItem* item = new DccTransferPanelItem( this, transfer );
+ connect( transfer, SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, SLOT( slotTransferStatusChanged() ) );
+ if ( m_listView->childCount() == 1 )
+ {
+ m_listView->clearSelection();
+ m_listView->setSelected( item, true );
+ m_listView->setCurrentItem( item );
+ updateButton();
+ setDetailPanelItem( item );
+ }
+}
+
+void DccTransferPanel::slotTransferStatusChanged()
+{
+ updateButton();
+ activateTabNotification(Konversation::tnfSystem);
+}
+
+void DccTransferPanel::updateButton()
+{
+ bool accept = true,
+ abort = false,
+ clear = false,
+ info = true,
+ open = true,
+ resend = false,
+ selectAll = false,
+ selectAllCompleted = false;
+
+ int selectedItems = 0;
+ QListViewItemIterator it( m_listView );
+
+ while( it.current() )
+ {
+ DccTransferPanelItem* item = static_cast<DccTransferPanelItem*>( it.current() );
+
+ DccTransfer::DccType type = item->transfer()->getType();
+ DccTransfer::DccStatus status = item->transfer()->getStatus();
+
+ selectAll = true;
+ selectAllCompleted |= ( status >= DccTransfer::Done );
+
+ if( it.current()->isSelected() )
+ {
+ ++selectedItems;
+
+ accept &= ( status == DccTransfer::Queued );
+
+ abort |= ( status < DccTransfer::Done );
+
+ clear |= ( status >= DccTransfer::Done );
+
+ info &= ( type == DccTransfer::Send ||
+ status == DccTransfer::Done );
+
+ open &= ( type == DccTransfer::Send ||
+ status == DccTransfer::Done );
+
+ resend |= ( type == DccTransfer::Send &&
+ status >= DccTransfer::Done );
+ }
+ ++it;
+ }
+
+ if( !selectedItems )
+ {
+ accept = false;
+ abort = false;
+ clear = false;
+ info = false;
+ open = false;
+ resend = false;
+ }
+
+ if (!kapp->authorize("allow_downloading"))
+ {
+ accept = false;
+ }
+
+ m_buttonAccept->setEnabled( accept );
+ m_buttonAbort->setEnabled( abort );
+ m_buttonClear->setEnabled( clear );
+ m_buttonOpen->setEnabled( open );
+
+ m_popup->setItemEnabled( Popup::SelectAll, selectAll );
+ m_popup->setItemEnabled( Popup::SelectAllCompleted, selectAllCompleted );
+ m_popup->setItemEnabled( Popup::Accept, accept );
+ m_popup->setItemEnabled( Popup::Abort, abort );
+ m_popup->setItemEnabled( Popup::Clear, clear );
+ m_popup->setItemEnabled( Popup::Open, open );
+ m_popup->setItemEnabled( Popup::Resend, resend );
+ m_popup->setItemEnabled( Popup::Info, info );
+}
+
+void DccTransferPanel::setDetailPanelItem(QListViewItem* item_)
+{
+ if ( item_ )
+ {
+ DccTransferPanelItem* item = static_cast< DccTransferPanelItem* >( item_ );
+ m_detailPanel->setItem( item );
+ }
+}
+
+void DccTransferPanel::acceptDcc()
+{
+ QListViewItemIterator it( m_listView );
+ while( it.current() )
+ {
+ if( it.current()->isSelected() )
+ {
+ DccTransferPanelItem* item=static_cast<DccTransferPanelItem*>( it.current() );
+ DccTransfer* transfer = item->transfer();
+ if( transfer->getType() == DccTransfer::Receive && transfer->getStatus() == DccTransfer::Queued )
+ transfer->start();
+ }
+ ++it;
+ }
+}
+
+void DccTransferPanel::abortDcc()
+{
+ QListViewItemIterator it( m_listView );
+ while( it.current() )
+ {
+ if( it.current()->isSelected() )
+ {
+ DccTransferPanelItem* item=static_cast<DccTransferPanelItem*>( it.current() );
+ DccTransfer* transfer = item->transfer();
+ if( transfer->getStatus() < DccTransfer::Done )
+ transfer->abort();
+ }
+ ++it;
+ }
+}
+
+void DccTransferPanel::resendFile()
+{
+ QListViewItemIterator it( m_listView );
+ while( it.current() )
+ {
+ if( it.current()->isSelected() )
+ {
+ DccTransferPanelItem* item=static_cast<DccTransferPanelItem*>( it.current() );
+ DccTransfer* transfer = item->transfer();
+ if( transfer->getType() == DccTransfer::Send && transfer->getStatus() >= DccTransfer::Done )
+ {
+ DccTransferSend* newTransfer = KonversationApplication::instance()->getDccTransferManager()->newUpload();
+
+ newTransfer->setConnectionId( transfer->getConnectionId() );
+ newTransfer->setPartnerNick( transfer->getPartnerNick() );
+ newTransfer->setFileURL( transfer->getFileURL() );
+ newTransfer->setFileName( transfer->getFileName() );
+ // FIXME
+ newTransfer->setOwnIp( transfer->getOwnIp() );
+
+ if ( newTransfer->queue() )
+ newTransfer->start();
+ }
+ }
+ ++it;
+ }
+}
+
+void DccTransferPanel::clearDcc()
+{
+ QPtrList<QListViewItem> lst;
+ QListViewItemIterator it( m_listView );
+ while( it.current() )
+ {
+ DccTransferPanelItem* item = static_cast<DccTransferPanelItem*>( it.current() );
+ // should we check that [item] is not null?
+ if( it.current()->isSelected() && item->transfer()->getStatus() >= DccTransfer::Done )
+ lst.append( it.current() );
+ ++it;
+ }
+
+ // Figure out the first 'gap' in the selection and select that item,
+ // or, if there are no gaps, select first item below the selection
+ QPtrListIterator<QListViewItem> selected( lst );
+ bool itemSelected = false;
+ while( selected.current() )
+ {
+ if (selected.current()->itemBelow() && !lst.containsRef(selected.current()->itemBelow()))
+ {
+ m_listView->setSelected(selected.current()->itemBelow(),true);
+ m_listView->setCurrentItem(selected.current()->itemBelow());
+ itemSelected = true;
+ break;
+ }
+ ++selected;
+ }
+
+ // When there are neither gaps in nor items below the selection, select the first item
+ if (!itemSelected)
+ {
+ m_listView->setSelected(m_listView->firstChild(),true);
+ m_listView->setCurrentItem(m_listView->firstChild());
+ }
+
+ lst.setAutoDelete( true );
+ while( lst.remove() ) ;
+ updateButton();
+}
+
+void DccTransferPanel::runDcc()
+{
+ QListViewItemIterator it( m_listView );
+ while( it.current() )
+ {
+ if( it.current()->isSelected() )
+ {
+ DccTransferPanelItem* item=static_cast<DccTransferPanelItem*>( it.current() );
+ DccTransfer* transfer = item->transfer();
+ if( transfer->getType() == DccTransfer::Send || transfer->getStatus() == DccTransfer::Done )
+ item->runFile();
+ }
+ ++it;
+ }
+}
+
+void DccTransferPanel::showFileInfo()
+{
+ QListViewItemIterator it( m_listView );
+ while( it.current() )
+ {
+ if( it.current()->isSelected() )
+ {
+ DccTransferPanelItem* item=static_cast<DccTransferPanelItem*>( it.current() );
+ if( item->transfer()->getType() == DccTransfer::Send || item->transfer()->getStatus() == DccTransfer::Done )
+ item->openFileInfoDialog();
+ }
+ ++it;
+ }
+}
+
+void DccTransferPanel::selectAll()
+{
+ QListViewItemIterator it( m_listView );
+ while ( it.current() )
+ {
+ m_listView->setSelected( *it, true );
+ ++it;
+ }
+ updateButton();
+}
+
+void DccTransferPanel::selectAllCompleted()
+{
+ QListViewItemIterator it( m_listView );
+ while ( it.current() )
+ {
+ DccTransferPanelItem* item=static_cast<DccTransferPanelItem*>( it.current() );
+ m_listView->setSelected( *it, item->transfer()->getStatus() >= DccTransfer::Done );
+ ++it;
+ }
+ updateButton();
+}
+
+void DccTransferPanel::popupRequested(QListViewItem* /* item */, const QPoint& pos, int /* col */) // slot
+{
+ updateButton();
+ m_popup->popup(pos);
+}
+
+void DccTransferPanel::popupActivated( int id ) // slot
+{
+ if ( id == Popup::Abort ) abortDcc();
+ else if ( id == Popup::Accept ) acceptDcc();
+ else if ( id == Popup::Clear ) clearDcc();
+ else if ( id == Popup::Info ) showFileInfo();
+ else if ( id == Popup::Open ) runDcc();
+ else if ( id == Popup::SelectAll ) selectAll();
+ else if ( id == Popup::SelectAllCompleted ) selectAllCompleted();
+ else if ( id == Popup::Resend ) resendFile();
+}
+
+void DccTransferPanel::doubleClicked(QListViewItem* _item, const QPoint& /* _pos */, int /* _col */)
+{
+ DccTransferPanelItem* item = static_cast<DccTransferPanelItem*>(_item);
+ item->runFile();
+}
+
+// virtual
+void DccTransferPanel::childAdjustFocus()
+{
+}
+
+KListView* DccTransferPanel::getListView()
+{
+ return m_listView;
+}
+
+#include "dcctransferpanel.moc"
diff --git a/konversation/src/dcctransferpanel.h b/konversation/src/dcctransferpanel.h
new file mode 100644
index 0000000..882edb7
--- /dev/null
+++ b/konversation/src/dcctransferpanel.h
@@ -0,0 +1,113 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+
+#ifndef DCCTRANSFERPANEL_H
+#define DCCTRANSFERPANEL_H
+
+#include "chatwindow.h"
+#include "dcctransferpanelitem.h"
+
+
+class QContextMenuEvent;
+class QPushButton;
+class KListView;
+class KPopupMenu;
+
+class DccTransferDetailedInfoPanel;
+
+class DccTransferPanel : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ class Column
+ {
+ public:
+ enum Object
+ {
+ TypeIcon,
+ OfferDate,
+ Status,
+ FileName,
+ PartnerNick,
+ Progress,
+ Position,
+ TimeLeft,
+ CurrentSpeed,
+ SenderAddress,
+ COUNT
+ };
+ };
+
+ class Popup
+ {
+ public:
+ enum Object
+ {
+ SelectAll,
+ SelectAllCompleted,
+ Accept,
+ Abort,
+ Clear,
+ Open,
+ Info,
+ Resend
+ };
+ };
+
+ DccTransferPanel(QWidget* parent);
+ ~DccTransferPanel();
+
+ KListView* getListView();
+
+ protected slots:
+ void slotNewTransferAdded( DccTransfer* transfer );
+ void slotTransferStatusChanged();
+
+ void acceptDcc();
+ void abortDcc();
+ void resendFile();
+ void clearDcc();
+ void runDcc();
+ void showFileInfo();
+ void selectAll();
+ void selectAllCompleted();
+
+ void popupRequested(QListViewItem* item,const QPoint& pos,int col);
+ void popupActivated(int id);
+
+ void doubleClicked(QListViewItem* _item,const QPoint& _pos,int _col);
+
+ void updateButton();
+
+ void setDetailPanelItem(QListViewItem* item_);
+
+ protected:
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ void initGUI();
+
+ KListView* m_listView;
+ KPopupMenu* m_popup;
+
+ DccTransferDetailedInfoPanel* m_detailPanel;
+
+ QPushButton* m_buttonAccept;
+ QPushButton* m_buttonAbort;
+ QPushButton* m_buttonClear;
+ QPushButton* m_buttonOpen;
+ QPushButton* m_buttonDetail;
+};
+#endif
diff --git a/konversation/src/dcctransferpanelitem.cpp b/konversation/src/dcctransferpanelitem.cpp
new file mode 100644
index 0000000..811828c
--- /dev/null
+++ b/konversation/src/dcctransferpanelitem.cpp
@@ -0,0 +1,403 @@
+/*
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+// Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#include "dcctransferpanelitem.h"
+#include "dcctransferpanel.h"
+#include "konversationapplication.h"
+#include "config/preferences.h"
+
+#include <qheader.h>
+#include <qhostaddress.h>
+#include <qstyle.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kfilemetainfo.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kio/job.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprogress.h>
+#include <krun.h>
+
+
+DccTransferPanelItem::DccTransferPanelItem( DccTransferPanel* panel, DccTransfer* transfer )
+ : KListViewItem( panel->getListView() )
+ , m_panel( panel )
+ , m_transfer( transfer )
+ , m_isTransferInstanceBackup( false )
+{
+ m_autoUpdateViewTimer = 0;
+
+ m_progressBar = new KProgress( 100, listView()->viewport() );
+ m_progressBar->setCenterIndicator( true );
+ m_progressBar->setPercentageVisible( true );
+
+ connect( m_transfer, SIGNAL( transferStarted( DccTransfer* ) ), this, SLOT( startAutoViewUpdate() ) );
+ connect( m_transfer, SIGNAL( done( DccTransfer* ) ), this, SLOT( stopAutoViewUpdate() ) );
+ connect( m_transfer, SIGNAL( done( DccTransfer* ) ), this, SLOT( backupTransferInfo( DccTransfer* ) ) );
+
+ connect( m_transfer, SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, SLOT( slotStatusChanged( DccTransfer*, int, int ) ) );
+
+ updateView();
+}
+
+DccTransferPanelItem::~DccTransferPanelItem()
+{
+ kdDebug() << "DccTransferPanelItem::~DccTransferPanelItem()" << endl;
+ stopAutoViewUpdate();
+ delete m_progressBar;
+ if ( m_isTransferInstanceBackup )
+ delete m_transfer;
+}
+
+void DccTransferPanelItem::updateView()
+{
+ setPixmap( DccTransferPanel::Column::TypeIcon, getTypeIcon() );
+ setPixmap( DccTransferPanel::Column::Status, getStatusIcon() );
+
+ setText( DccTransferPanel::Column::OfferDate, m_transfer->getTimeOffer().toString( "hh:mm:ss" ) );
+ setText( DccTransferPanel::Column::Status, getStatusText() );
+ setText( DccTransferPanel::Column::FileName, m_transfer->getFileName() );
+ setText( DccTransferPanel::Column::PartnerNick, m_transfer->getPartnerNick() );
+ setText( DccTransferPanel::Column::Position, getPositionPrettyText() );
+ setText( DccTransferPanel::Column::TimeLeft, getTimeLeftPrettyText() );
+ setText( DccTransferPanel::Column::CurrentSpeed, getCurrentSpeedPrettyText() );
+ setText( DccTransferPanel::Column::SenderAddress, getSenderAddressPrettyText() );
+
+ if ( m_transfer->getFileSize() )
+ m_progressBar->setProgress( m_transfer->getProgress() );
+ else // filesize is unknown
+ {
+ m_progressBar->hide();
+ setText( DccTransferPanel::Column::Progress, i18n( "unknown" ) );
+ }
+}
+
+
+int DccTransferPanelItem::compare( QListViewItem* i, int col, bool ascending ) const
+{
+ DccTransferPanelItem* item = static_cast<DccTransferPanelItem*>( i );
+
+ switch ( col )
+ {
+ case DccTransferPanel::Column::TypeIcon:
+ if ( m_transfer->getType() > item->transfer()->getType() ) return 1;
+ if ( m_transfer->getType() < item->transfer()->getType() ) return -1;
+ return 0;
+ break;
+ case DccTransferPanel::Column::OfferDate:
+ if ( m_transfer->getTimeOffer() > item->transfer()->getTimeOffer() ) return 1;
+ if ( m_transfer->getTimeOffer() < item->transfer()->getTimeOffer() ) return -1;
+ return 0;
+ break;
+ case DccTransferPanel::Column::Status:
+ if ( m_transfer->getStatus() > item->transfer()->getStatus() ) return 1;
+ if ( m_transfer->getStatus() < item->transfer()->getStatus() ) return -1;
+ return 0;
+ break;
+ case DccTransferPanel::Column::Position:
+ if ( m_transfer->getTransferringPosition() > item->transfer()->getTransferringPosition() ) return 1;
+ if ( m_transfer->getTransferringPosition() < item->transfer()->getTransferringPosition() ) return -1;
+ return 0;
+ break;
+ case DccTransferPanel::Column::TimeLeft:
+ if ( m_transfer->getTimeLeft() > item->transfer()->getTimeLeft() ) return 1;
+ if ( m_transfer->getTimeLeft() < item->transfer()->getTimeLeft() ) return -1;
+ return 0;
+ break;
+ case DccTransferPanel::Column::CurrentSpeed:
+ if ( m_transfer->getCurrentSpeed() > item->transfer()->getCurrentSpeed() ) return 1;
+ if ( m_transfer->getCurrentSpeed() < item->transfer()->getCurrentSpeed() ) return -1;
+ return 0;
+ break;
+ default:
+ return QListViewItem::compare( i, col, ascending );
+ }
+ return QListViewItem::compare( i, col, ascending );
+}
+
+void DccTransferPanelItem::slotStatusChanged( DccTransfer* /* transfer */, int newStatus, int /* oldStatus */ )
+{
+ updateView();
+
+ if ( newStatus == DccTransfer::Transferring )
+ startAutoViewUpdate();
+}
+
+void DccTransferPanelItem::startAutoViewUpdate()
+{
+ stopAutoViewUpdate();
+ m_autoUpdateViewTimer = new QTimer( this );
+ connect( m_autoUpdateViewTimer, SIGNAL( timeout() ), this, SLOT( updateView()) );
+ m_autoUpdateViewTimer->start( 500 );
+}
+
+void DccTransferPanelItem::stopAutoViewUpdate()
+{
+ if ( m_autoUpdateViewTimer )
+ {
+ m_autoUpdateViewTimer->stop();
+ delete m_autoUpdateViewTimer;
+ m_autoUpdateViewTimer = 0;
+ }
+}
+
+void DccTransferPanelItem::paintCell( QPainter* painter, const QColorGroup& colorgroup, int column, int width, int alignment ) // virtual public
+{
+ KListViewItem::paintCell( painter, colorgroup, column, width, alignment );
+ if ( column == DccTransferPanel::Column::Progress )
+ showProgressBar();
+}
+
+void DccTransferPanelItem::showProgressBar()
+{
+ if ( m_transfer->getFileSize() )
+ {
+ QRect rect = listView()->itemRect( this );
+ QHeader *head = listView()->header();
+ rect.setLeft( head->sectionPos( DccTransferPanel::Column::Progress ) - head->offset() );
+ rect.setWidth( head->sectionSize( DccTransferPanel::Column::Progress ) );
+ m_progressBar->setGeometry( rect );
+ m_progressBar->show();
+ }
+}
+
+void DccTransferPanelItem::runFile()
+{
+ if ( m_transfer->getType() == DccTransfer::Send || m_transfer->getStatus() == DccTransfer::Done )
+ new KRun( m_transfer->getFileURL(), listView() );
+}
+
+void DccTransferPanelItem::openFileInfoDialog()
+{
+ if ( m_transfer->getType() == DccTransfer::Send || m_transfer->getStatus() == DccTransfer::Done )
+ {
+ QStringList infoList;
+
+ QString path=m_transfer->getFileURL().path();
+
+ // get meta info object
+ KFileMetaInfo fileInfo(path,QString(),KFileMetaInfo::Everything);
+
+ // is there any info for this file?
+ if(!fileInfo.isEmpty())
+ {
+ // get list of meta information groups
+ QStringList groupList=fileInfo.groups();
+ // look inside for keys
+ for(unsigned int index=0;index<groupList.count();index++)
+ {
+ // get next group
+ KFileMetaInfoGroup group=fileInfo.group(groupList[index]);
+ // check if there are keys in this group at all
+ if(!group.isEmpty())
+ {
+ // append group name to list
+ infoList.append(groupList[index]);
+ // get list of keys in this group
+ QStringList keys=group.keys();
+ for(unsigned keyIndex=0;keyIndex<keys.count();keyIndex++)
+ {
+ // get meta information item for this key
+ KFileMetaInfoItem item=group.item(keys[keyIndex]);
+ if(item.isValid())
+ {
+ // append item information to list
+ infoList.append("- "+item.translatedKey()+' '+item.string());
+ }
+ } // endfor
+ }
+ } // endfor
+
+ // display information list if any available
+ if(infoList.count())
+ {
+ #ifdef USE_INFOLIST
+ KMessageBox::informationList(
+ listView(),
+ i18n("Available information for file %1:").arg(path),
+ infoList,
+ i18n("File Information")
+ );
+ #else
+ KMessageBox::information(
+ listView(),
+ "<qt>"+infoList.join("<br>")+"</qt>",
+ i18n("File Information")
+ );
+ #endif
+ }
+ }
+ else
+ {
+ KMessageBox::sorry(listView(),i18n("No detailed information for this file found."),i18n("File Information"));
+ }
+ }
+}
+
+void DccTransferPanelItem::backupTransferInfo( DccTransfer* transfer )
+{
+ kdDebug() << "DccTransferPanelItem::backupTransferInfo()" << endl;
+ // instances of DccTransfer are deleted immediately after the transfer is done
+ // so we need to make a backup of DccTransfer.
+
+ m_transfer = new DccTransfer( *transfer );
+ m_isTransferInstanceBackup = true;
+}
+
+QString DccTransferPanelItem::getTypeText() const
+{
+ if ( m_transfer->getType() == DccTransfer::Send )
+ return i18n( "Send" );
+ else
+ return i18n( "Receive" );
+}
+
+QPixmap DccTransferPanelItem::getTypeIcon() const
+{
+ if ( m_transfer->getType() == DccTransfer::Send )
+ return KGlobal::iconLoader()->loadIcon( "up", KIcon::Small );
+ else
+ return KGlobal::iconLoader()->loadIcon( "down", KIcon::Small );
+}
+
+QPixmap DccTransferPanelItem::getStatusIcon() const
+{
+ QString icon;
+ switch ( m_transfer->getStatus() )
+ {
+ case DccTransfer::Queued:
+ icon = "player_stop";
+ break;
+ case DccTransfer::Preparing:
+ case DccTransfer::WaitingRemote:
+ case DccTransfer::Connecting:
+ icon = "goto";
+ break;
+ case DccTransfer::Transferring:
+ icon = "player_play";
+ break;
+ case DccTransfer::Done:
+ icon = "ok";
+ break;
+ case DccTransfer::Aborted:
+ case DccTransfer::Failed:
+ icon = "stop";
+ break;
+ default:
+ break;
+ }
+ return KGlobal::iconLoader()->loadIcon( icon, KIcon::Small );
+}
+
+QString DccTransferPanelItem::getStatusText() const
+{
+ DccTransfer::DccStatus status = m_transfer->getStatus();
+ DccTransfer::DccType type = m_transfer->getType();
+
+ if ( status == DccTransfer::Queued )
+ return i18n( "Queued" );
+ else if ( status == DccTransfer::Preparing )
+ return i18n( "Preparing" );
+ else if ( status == DccTransfer::WaitingRemote )
+ return i18n( "Awaiting" );
+ else if ( status == DccTransfer::Connecting )
+ return i18n( "Connecting" );
+ else if ( status == DccTransfer::Transferring && type == DccTransfer::Receive )
+ return i18n( "Receiving" );
+ else if ( status == DccTransfer::Transferring && type == DccTransfer::Send )
+ return i18n( "Sending" );
+ else if ( status == DccTransfer::Done )
+ return i18n( "Done" );
+ else if ( status == DccTransfer::Failed )
+ return i18n( "Failed" );
+ else if ( status == DccTransfer::Aborted )
+ return i18n( "Aborted" );
+
+ return QString();
+}
+
+QString DccTransferPanelItem::getFileSizePrettyText() const
+{
+ return KIO::convertSize( m_transfer->getFileSize() );
+}
+
+QString DccTransferPanelItem::getPositionPrettyText( bool detailed ) const
+{
+ if ( detailed )
+ return KGlobal::locale()->formatNumber( m_transfer->getTransferringPosition(), 0 ) + " / " +
+ KGlobal::locale()->formatNumber( m_transfer->getFileSize(), 0 );
+ else
+ return KIO::convertSize( m_transfer->getTransferringPosition() ) + " / " + KIO::convertSize( m_transfer->getFileSize() );
+}
+
+QString DccTransferPanelItem::getTimeLeftPrettyText() const
+{
+ QString text;
+
+ if ( m_transfer->getTimeLeft() == DccTransfer::NotInTransfer )
+ ;
+ else if ( m_transfer->getTimeLeft() == DccTransfer::InfiniteValue )
+ text = "?";
+ else
+ text = secToHMS( m_transfer->getTimeLeft() );
+
+ return text;
+}
+
+QString DccTransferPanelItem::getAverageSpeedPrettyText() const
+{
+ return getSpeedPrettyText( m_transfer->getAverageSpeed() );
+}
+
+QString DccTransferPanelItem::getCurrentSpeedPrettyText() const
+{
+ return getSpeedPrettyText( m_transfer->getCurrentSpeed() );
+}
+
+QString DccTransferPanelItem::getSenderAddressPrettyText() const
+{
+ if ( m_transfer->getType() == DccTransfer::Send )
+ return QString( "%1:%2" ).arg( m_transfer->getOwnIp() ).arg( m_transfer->getOwnPort() );
+ else
+ return QString( "%1:%2" ).arg( m_transfer->getPartnerIp() ).arg( m_transfer->getPartnerPort() );
+}
+
+QString DccTransferPanelItem::getSpeedPrettyText( transferspeed_t speed )
+{
+ if ( speed == DccTransfer::Calculating || speed == DccTransfer::InfiniteValue )
+ return QString( "?" );
+ else if ( speed == DccTransfer::NotInTransfer )
+ return QString();
+ else
+ return i18n("%1/sec").arg( KIO::convertSize( (KIO::fileoffset_t)speed ) );
+}
+
+QString DccTransferPanelItem::secToHMS( long sec )
+{
+ int remSec = sec;
+ int remHour = remSec / 3600; remSec -= remHour * 3600;
+ int remMin = remSec / 60; remSec -= remMin * 60;
+
+ // remHour can be more than 25, so we can't use QTime here.
+ return QString( "%1:%2:%3" )
+ .arg( QString::number( remHour ).rightJustify( 2, '0', false ) )
+ .arg( QString::number( remMin ).rightJustify( 2, '0' ) )
+ .arg( QString::number( remSec ).rightJustify( 2, '0' ) );
+}
+
+#include "dcctransferpanelitem.moc"
diff --git a/konversation/src/dcctransferpanelitem.h b/konversation/src/dcctransferpanelitem.h
new file mode 100644
index 0000000..e9fbb84
--- /dev/null
+++ b/konversation/src/dcctransferpanelitem.h
@@ -0,0 +1,98 @@
+/*
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+// Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#ifndef DCCTRANSFERPANELITEM_H
+#define DCCTRANSFERPANELITEM_H
+
+#include "dcctransfer.h"
+
+#include <qdatetime.h>
+
+#include <klistview.h>
+#include <kurl.h>
+#include <kio/global.h>
+
+
+class QStringList;
+class QTimer;
+
+class KProgress;
+
+namespace KIO
+{
+ class Job;
+}
+
+class DccTransferPanel;
+
+class DccTransferPanelItem : public QObject, public KListViewItem
+{
+ Q_OBJECT
+
+ public:
+ DccTransferPanelItem( DccTransferPanel* panel, DccTransfer* transfer );
+ virtual ~DccTransferPanelItem();
+
+ virtual void paintCell( QPainter* painter, const QColorGroup& colorgroup, int column, int width, int alignment );
+
+ virtual int compare( QListViewItem* i, int col, bool ascending ) const;
+
+ void runFile();
+ void openFileInfoDialog();
+
+ DccTransfer* transfer() const { return m_transfer; }
+
+ // called from updateView()
+ QString getTypeText() const;
+ QPixmap getTypeIcon() const;
+ QPixmap getStatusIcon() const;
+ QString getStatusText() const;
+ QString getFileSizePrettyText() const;
+ QString getPositionPrettyText( bool detailed = false ) const;
+ QString getTimeLeftPrettyText() const;
+ QString getAverageSpeedPrettyText() const;
+ QString getCurrentSpeedPrettyText() const;
+ QString getSenderAddressPrettyText() const;
+
+ static QString getSpeedPrettyText( transferspeed_t speed );
+ static QString secToHMS( long sec );
+
+ private slots:
+ void slotStatusChanged( DccTransfer* transfer, int newStatus, int oldStatus );
+ void updateView();
+
+ private:
+ DccTransferPanel* m_panel;
+ DccTransfer* m_transfer;
+ bool m_isTransferInstanceBackup;
+
+ private slots:
+ void startAutoViewUpdate();
+ void stopAutoViewUpdate();
+
+ void backupTransferInfo( DccTransfer* transfer );
+
+ private:
+ void updateTransferInfo();
+ void updateTransferMeters();
+
+ void showProgressBar(); // called from printCell()
+
+ // UI
+ QTimer* m_autoUpdateViewTimer;
+ KProgress* m_progressBar;
+};
+
+#endif // DCCTRANSFERPANELITEM_H
diff --git a/konversation/src/dcctransferrecv.cpp b/konversation/src/dcctransferrecv.cpp
new file mode 100644
index 0000000..a1b69d3
--- /dev/null
+++ b/konversation/src/dcctransferrecv.cpp
@@ -0,0 +1,799 @@
+/*
+ receive a file on DCC protocol
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+// Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#include "dcctransferrecv.h"
+#include "dcccommon.h"
+#include "channel.h"
+#include "dcctransfermanager.h"
+#include "konversationapplication.h"
+#include "connectionmanager.h"
+#include "server.h"
+
+#include <kdebug.h>
+#include <kfileitem.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kserversocket.h>
+#include <kstandarddirs.h>
+#include <kstreamsocket.h>
+#include <kdirselectdialog.h>
+#include <kuser.h>
+
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <kio/netaccess.h>
+
+
+class DccResumeDialog;
+
+/*
+ *flow chart*
+
+ DccTransferRecv()
+
+ start() : called from DccTransferPanel when user pushes the accept button
+ | \
+ | requestResume() : called when user chooses to resume in DccResumeDialog. it emits the signal ResumeRequest()
+ |
+ | startResume() : called by "Server"
+ | |
+connectToSender()
+
+connectionSuccess() : called by recvSocket
+
+*/
+
+DccTransferRecv::DccTransferRecv(QObject* parent)
+ : DccTransfer( DccTransfer::Receive, parent )
+{
+ kdDebug() << "DccTransferRecv::DccTransferRecv()" << endl;
+
+ m_serverSocket = 0;
+ m_recvSocket = 0;
+ m_writeCacheHandler = 0;
+
+ m_connectionTimer = new QTimer( this );
+ connect( m_connectionTimer, SIGNAL( timeout() ), this, SLOT( connectionTimeout() ) );
+ //timer hasn't started yet. qtimer will be deleted automatically when 'this' object is deleted
+}
+
+DccTransferRecv::~DccTransferRecv()
+{
+ cleanUp();
+}
+
+QString DccTransferRecv::getTypeText() const
+{
+ return i18n( "Receive" );
+}
+
+QPixmap DccTransferRecv::getTypeIcon() const
+{
+ return KGlobal::iconLoader()->loadIcon( "down", KIcon::Small );
+}
+
+void DccTransferRecv::cleanUp()
+{
+ kdDebug() << "DccTransferRecv::cleanUp()" << endl;
+
+ stopConnectionTimer();
+ finishTransferLogger();
+ if ( m_serverSocket )
+ {
+ m_serverSocket->close();
+ m_serverSocket = 0;
+ }
+ if ( m_recvSocket )
+ {
+ m_recvSocket->close();
+ m_recvSocket = 0; // the instance will be deleted automatically by its parent
+ }
+ if ( m_writeCacheHandler )
+ {
+ m_writeCacheHandler->closeNow();
+ m_writeCacheHandler->deleteLater();
+ m_writeCacheHandler = 0;
+ }
+}
+
+// just for convenience
+void DccTransferRecv::failed( const QString& errorMessage )
+{
+ setStatus( Failed, errorMessage );
+ cleanUp();
+ emit done( this );
+}
+
+void DccTransferRecv::setPartnerIp( const QString& ip )
+{
+ if ( getStatus() == Configuring )
+ m_partnerIp = ip;
+}
+
+void DccTransferRecv::setPartnerPort( const QString& port )
+{
+ if ( getStatus() == Configuring )
+ m_partnerPort = port;
+}
+
+void DccTransferRecv::setFileSize( unsigned long fileSize )
+{
+ if ( getStatus() == Configuring )
+ m_fileSize = fileSize;
+}
+
+void DccTransferRecv::setFileName( const QString& fileName )
+{
+ if ( getStatus() == Configuring )
+ m_fileName = fileName;
+}
+
+void DccTransferRecv::setFileURL( const KURL& url )
+{
+ if ( getStatus() == Configuring || getStatus() == Queued )
+ m_fileURL = url;
+}
+
+void DccTransferRecv::setReverse( bool reverse, const QString& reverseToken )
+{
+ if ( getStatus() == Configuring )
+ {
+ m_reverse = reverse;
+ if ( reverse )
+ {
+ m_partnerPort = QString::number( 0 );
+ m_reverseToken = reverseToken;
+ }
+ }
+}
+
+bool DccTransferRecv::queue()
+{
+ kdDebug() << "DccTransferRecv::queue()" << endl;
+
+ if ( getStatus() != Configuring )
+ return false;
+
+ if ( m_partnerIp.isEmpty() || m_partnerPort.isEmpty() )
+ return false;
+
+ if (!kapp->authorize("allow_downloading"))
+ {
+ //note we have this after the initialisations so that item looks okay
+ //Do not have the rights to send the file. Shouldn't have gotten this far anyway
+ failed(i18n("The admin has restricted the right to receive files"));
+ return false;
+ }
+
+ // check if the sender IP is valid
+ if ( m_partnerIp == "0.0.0.0" )
+ {
+ failed( i18n( "Invalid sender address (%1)" ).arg( m_partnerIp ) );
+ return false;
+ }
+
+ // TODO: should we support it?
+ if ( m_fileSize == 0 )
+ {
+ failed( i18n( "Unsupported negotiation (filesize=0)" ) );
+ return false;
+ }
+
+ if ( m_fileName.isEmpty() )
+ {
+ m_fileName = "unnamed_file";
+ }
+
+ if ( m_fileURL.isEmpty() )
+ {
+ // determine default incoming file URL
+
+ // set default folder
+ if ( !Preferences::dccPath().isEmpty() )
+ m_fileURL = KURL( Preferences::dccPath() );
+ else
+ m_fileURL.setPath( KUser( KUser::UseRealUserID ).homeDir() ); // default folder is *not* specified
+
+ // add a slash if there is none
+ m_fileURL.adjustPath( 1 );
+
+ // Append folder with partner's name if wanted
+ if ( Preferences::dccCreateFolder() )
+ m_fileURL.addPath( m_partnerNick + '/' );
+
+ // Just incase anyone tries to do anything nasty
+ QString fileNameSanitized = sanitizeFileName( m_fileName );
+
+ // Append partner's name to file name if wanted
+ if ( Preferences::dccAddPartner() )
+ m_fileURL.addPath( m_partnerNick + '.' + fileNameSanitized );
+ else
+ m_fileURL.addPath( fileNameSanitized );
+ }
+
+ return DccTransfer::queue();
+}
+
+void DccTransferRecv::abort() // public slot
+{
+ kdDebug() << "DccTransferRecv::abort()" << endl;
+
+ if(m_writeCacheHandler)
+ {
+ m_writeCacheHandler->write( true ); // flush
+ }
+
+ setStatus( Aborted );
+ cleanUp();
+ emit done( this );
+}
+
+void DccTransferRecv::start() // public slot
+{
+ kdDebug() << "DccTransferRecv::start() [BEGIN]" << endl;
+
+ if ( getStatus() != Queued )
+ return;
+
+ setStatus( Preparing );
+
+ prepareLocalKio( false, false );
+
+ kdDebug() << "DccTransferRecv::start() [END]" << endl;
+}
+
+void DccTransferRecv::prepareLocalKio( bool overwrite, bool resume, KIO::fileoffset_t startPosition /* = 0 */ )
+{
+ kdDebug() << "DccTransferRecv::prepareLocalKio()" << endl
+ << "DccTransferRecv::prepareLocalKio(): URL: " << m_fileURL << endl
+ << "DccTransferRecv::prepareLocalKio(): Overwrite: " << overwrite << endl
+ << "DccTransferRecv::prepareLocalKio(): Resume: " << resume << " (Position: " << QString::number( startPosition ) << ")" << endl;
+
+ m_resumed = resume;
+ m_transferringPosition = startPosition;
+
+ if ( !createDirs( m_fileURL.upURL() ) )
+ {
+ askAndPrepareLocalKio( i18n( "<b>Cannot create the folder.</b><br>"
+ "Folder: %1<br>" )
+ .arg( m_fileURL.upURL().prettyURL() ),
+ DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
+ DccResumeDialog::RA_Rename );
+ return;
+ }
+
+ KIO::TransferJob* transferJob = KIO::put( m_fileURL, -1, overwrite, m_resumed, false );
+
+ if ( !transferJob )
+ {
+ kdDebug() << "DccTransferRecv::prepareLocalKio(): KIO::put() returned NULL. what happened?" << endl;
+ failed( i18n( "Could not create a KIO instance" ) );
+ return;
+ }
+
+ connect( transferJob, SIGNAL( canResume( KIO::Job*, KIO::filesize_t ) ), this, SLOT( slotLocalCanResume( KIO::Job*, KIO::filesize_t ) ) );
+ connect( transferJob, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotLocalGotResult( KIO::Job* ) ) );
+ connect( transferJob, SIGNAL( dataReq( KIO::Job*, QByteArray& ) ), this, SLOT( slotLocalReady( KIO::Job* ) ) );
+}
+
+void DccTransferRecv::askAndPrepareLocalKio( const QString& message, int enabledActions, DccResumeDialog::ReceiveAction defaultAction, KIO::fileoffset_t startPosition )
+{
+ switch ( DccResumeDialog::ask( this, message, enabledActions, defaultAction ) )
+ {
+ case DccResumeDialog::RA_Resume:
+ prepareLocalKio( false, true, startPosition );
+ break;
+ case DccResumeDialog::RA_Overwrite:
+ prepareLocalKio( true, false );
+ break;
+ case DccResumeDialog::RA_Rename:
+ prepareLocalKio( false, false );
+ break;
+ case DccResumeDialog::RA_Cancel:
+ default:
+ setStatus( Queued );
+ }
+}
+
+bool DccTransferRecv::createDirs( const KURL& dirURL ) const
+{
+ KURL kurl( dirURL );
+ QString surl = kurl.url();
+
+ //First we split directories until we reach to the top,
+ //since we need to create directories one by one
+
+ QStringList dirList;
+ while ( surl != kurl.upURL().url() )
+ {
+ dirList.prepend( surl );
+ kurl = kurl.upURL();
+ surl = kurl.url();
+ }
+
+ //Now we create the directories
+
+ QStringList::ConstIterator it;
+ for ( it=dirList.begin() ; it!=dirList.end() ; ++it )
+ if ( !KIO::NetAccess::exists( *it, true, NULL ) )
+ if ( !KIO::NetAccess::mkdir( *it, NULL, -1 ) )
+ return false;
+
+ return true;
+}
+
+void DccTransferRecv::slotLocalCanResume( KIO::Job* job, KIO::filesize_t size )
+{
+ kdDebug() << "DccTransferRecv::slotLocalCanResume() [BEGIN]" << endl
+ << "DccTransferRecv::slotLocalCanResume(): size: " << QString::number( size ) << endl;
+
+ if ( size != 0 )
+ {
+ KIO::TransferJob* transferJob = static_cast<KIO::TransferJob*>( job );
+
+ disconnect( transferJob, 0, 0, 0 );
+ transferJob->kill();
+
+ if ( KonversationApplication::instance()->getDccTransferManager()->isLocalFileInWritingProcess( m_fileURL ) )
+ {
+ askAndPrepareLocalKio( i18n( "<b>The file is used by another transfer.</b><br>"
+ "%1<br>" )
+ .arg( m_fileURL.prettyURL() ),
+ DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
+ DccResumeDialog::RA_Rename );
+ }
+ else if ( Preferences::dccAutoResume() )
+ {
+ prepareLocalKio( false, true, size );
+ }
+ else
+ {
+ askAndPrepareLocalKio( i18n( "<b>A partial file exists.</b><br>"
+ "%1<br>"
+ "Size of the partial file: %2 bytes<br>" )
+ .arg( m_fileURL.prettyURL() )
+ .arg( KGlobal::locale()->formatNumber( size, 0 ) ),
+ DccResumeDialog::RA_Resume | DccResumeDialog::RA_Overwrite | DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
+ DccResumeDialog::RA_Resume,
+ size );
+ }
+ }
+
+ kdDebug() << "DccTransferRecv::slotLocalCanResume() [END]" << endl;
+}
+
+void DccTransferRecv::slotLocalGotResult( KIO::Job* job )
+{
+ kdDebug() << "DccTransferRecv::slotLocalGotResult() [BEGIN]" << endl;
+
+ KIO::TransferJob* transferJob = static_cast<KIO::TransferJob*>( job );
+ disconnect( transferJob, 0, 0, 0 );
+
+ switch ( transferJob->error() )
+ {
+ case 0: // no error
+ kdDebug() << "DccTransferRecv::slotLocalGotResult(): job->error() returned 0." << endl
+ << "DccTransferRecv::slotLocalGotResult(): Why was I called in spite of no error?" << endl;
+ break;
+ case KIO::ERR_FILE_ALREADY_EXIST:
+ askAndPrepareLocalKio( i18n( "<b>The file already exists.</b><br>"
+ "%1<br>" )
+ .arg( m_fileURL.prettyURL() ),
+ DccResumeDialog::RA_Overwrite | DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
+ DccResumeDialog::RA_Overwrite );
+ break;
+ default:
+ askAndPrepareLocalKio( i18n( "<b>Could not open the file.<br>"
+ "Error: %1</b><br>"
+ "%2<br>" )
+ .arg( transferJob->error() )
+ .arg( m_fileURL.prettyURL() ),
+ DccResumeDialog::RA_Rename | DccResumeDialog::RA_Cancel,
+ DccResumeDialog::RA_Rename );
+ }
+
+ kdDebug() << "DccTransferRecv::slotLocalGotResult() [END]" << endl;
+}
+
+void DccTransferRecv::slotLocalReady( KIO::Job* job )
+{
+ kdDebug() << "DccTransferRecv::slotLocalReady()" << endl;
+
+ KIO::TransferJob* transferJob = static_cast<KIO::TransferJob*>( job );
+
+ disconnect( transferJob, 0, 0, 0 ); // WriteCacheHandler will control the job after this
+
+ m_writeCacheHandler = new DccTransferRecvWriteCacheHandler( transferJob );
+
+ connect( m_writeCacheHandler, SIGNAL( done() ), this, SLOT( slotLocalWriteDone() ) );
+ connect( m_writeCacheHandler, SIGNAL( gotError( const QString& ) ), this, SLOT( slotLocalGotWriteError( const QString& ) ) );
+
+ if ( !m_resumed )
+ connectWithSender();
+ else
+ requestResume();
+}
+
+void DccTransferRecv::connectWithSender()
+{
+ if ( m_reverse )
+ {
+ if ( !startListeningForSender() )
+ return;
+
+ Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( m_connectionId );
+ if ( !server )
+ {
+ failed( i18n( "Could not send Reverse DCC SEND acknowledgement to the partner via the IRC server." ) );
+ }
+
+ m_ownIp = DccCommon::getOwnIp( server );
+ m_ownPort = QString::number( DccCommon::getServerSocketPort( m_serverSocket ) );
+
+ setStatus( WaitingRemote, i18n( "Waiting for connection" ) );
+
+ server->dccReverseSendAck( m_partnerNick, m_fileName, DccCommon::textIpToNumericalIp( m_ownIp ), m_ownPort, m_fileSize, m_reverseToken );
+
+ //FIXME: add connection timer here
+ }
+ else
+ {
+ connectToSendServer();
+ }
+}
+
+void DccTransferRecv::requestResume()
+{
+ kdDebug() << "DccTransferRecv::requestResume()" << endl;
+
+ setStatus( WaitingRemote, i18n( "Waiting for remote host's acceptance" ) );
+
+ startConnectionTimer( 30 );
+
+ kdDebug() << "DccTransferRecv::requestResume(): requesting resume for " << m_partnerNick << " file " << m_fileName << " partner " << m_partnerPort << endl;
+
+ //TODO m_filename could have been sanitized - will this effect this?
+ Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( m_connectionId );
+ if ( !server )
+ {
+ kdDebug() << "DccTransferSend::start(): could not retrieve the instance of Server. Connection id: " << m_connectionId << endl;
+ failed( i18n( "Could not send DCC RECV resume request to the partner via the IRC server." ) );
+ return;
+ }
+
+ server->dccResumeGetRequest( m_partnerNick, m_fileName, m_partnerPort, m_transferringPosition );
+}
+
+ // public slot
+void DccTransferRecv::startResume( unsigned long position )
+{
+ kdDebug() << "DccTransferRecv::startResume(): position: " << position << endl;
+
+ stopConnectionTimer();
+
+ if ( (unsigned long)m_transferringPosition != position )
+ {
+ kdDebug() << "DccTransferRecv::startResume(): remote responsed an unexpected position" << endl
+ << "DccTransferRecv::startResume(): expected: " << QString::number( m_transferringPosition ) << endl
+ << "DccTransferRecv::startResume(): remote response: " << position << endl;
+ failed( i18n( "Unexpected response from remote host" ) );
+ return;
+ }
+
+ connectWithSender();
+}
+
+void DccTransferRecv::connectToSendServer()
+{
+ kdDebug() << "DccTransferRecv::connectToSendServer()" << endl;
+
+ // connect to sender
+
+ setStatus( Connecting );
+
+ m_recvSocket = new KNetwork::KStreamSocket( m_partnerIp, m_partnerPort, this);
+
+ m_recvSocket->setBlocking( false ); // asynchronous mode
+ m_recvSocket->setFamily( KNetwork::KResolver::InetFamily );
+ m_recvSocket->setResolutionEnabled( false );
+ m_recvSocket->setTimeout( 30000 );
+
+ m_recvSocket->enableRead( false );
+ m_recvSocket->enableWrite( false );
+
+ connect( m_recvSocket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( startReceiving() ) );
+ connect( m_recvSocket, SIGNAL( gotError( int ) ), this, SLOT( connectionFailed( int ) ) );
+
+ kdDebug() << "DccTransferRecv::connectToServer(): attempting to connect to " << m_partnerIp << ":" << m_partnerPort << endl;
+
+ m_recvSocket->connect();
+}
+
+bool DccTransferRecv::startListeningForSender()
+{
+ // Set up server socket
+ QString failedReason;
+ if ( Preferences::dccSpecificSendPorts() )
+ m_serverSocket = DccCommon::createServerSocketAndListen( this, &failedReason, Preferences::dccSendPortsFirst(), Preferences::dccSendPortsLast() );
+ else
+ m_serverSocket = DccCommon::createServerSocketAndListen( this, &failedReason );
+ if ( !m_serverSocket )
+ {
+ failed( failedReason );
+ return false;
+ }
+
+ connect( m_serverSocket, SIGNAL( readyAccept() ), this, SLOT( slotServerSocketReadyAccept() ) );
+ connect( m_serverSocket, SIGNAL( gotError( int ) ), this, SLOT( slotServerSocketGotError( int ) ) );
+
+ return true;
+}
+
+void DccTransferRecv::slotServerSocketReadyAccept()
+{
+ //stopConnectionTimer()
+
+ m_recvSocket = static_cast<KNetwork::KStreamSocket*>( m_serverSocket->accept() );
+ if ( !m_recvSocket )
+ {
+ failed( i18n( "Could not accept the connection. (Socket Error)" ) );
+ return;
+ }
+
+ connect( m_recvSocket, SIGNAL( gotError( int ) ), this, SLOT( connectionFailed( int ) ) );
+
+ // we don't need ServerSocket anymore
+ m_serverSocket->close();
+
+ startReceiving();
+}
+
+void DccTransferRecv::slotServerSocketGotError( int /* errorCode*/ )
+{
+ failed( i18n( "Socket error: %1" ).arg( m_serverSocket->errorString() ) );
+}
+
+void DccTransferRecv::startReceiving()
+{
+ kdDebug() << "DccTransferRecv::startReceiving()" << endl;
+
+ m_recvSocket->setBlocking( false ); // asynchronous mode
+
+ connect( m_recvSocket, SIGNAL( readyRead() ), this, SLOT( readData() ) );
+ connect( m_recvSocket, SIGNAL( readyWrite() ), this, SLOT( sendAck() ) );
+ connect( m_recvSocket, SIGNAL( closed() ), this, SLOT( slotSocketClosed() ) );
+
+ setStatus( Transferring );
+
+ m_transferStartPosition = m_transferringPosition;
+
+ m_recvSocket->enableRead( true );
+ m_recvSocket->enableWrite( false );
+
+ startTransferLogger(); // initialize CPS counter, ETA counter, etc...
+}
+
+ // slot
+void DccTransferRecv::connectionFailed( int errorCode )
+{
+ kdDebug() << "DccTransferRecv::connectionFailed(): code = " << errorCode << ", string = " << m_recvSocket->errorString() << endl;
+ failed( i18n( "Connection failure: %1" ).arg( m_recvSocket->errorString() ) );
+}
+
+void DccTransferRecv::readData() // slot
+{
+ //kdDebug() << "readData()" << endl;
+ int actual = m_recvSocket->readBlock( m_buffer, m_bufferSize );
+ if ( actual > 0 )
+ {
+ //actual is the size we read in, and is guaranteed to be less than m_bufferSize
+ m_transferringPosition += actual;
+ m_writeCacheHandler->append( m_buffer, actual );
+ m_writeCacheHandler->write( false );
+ m_recvSocket->enableWrite( true );
+ }
+}
+
+void DccTransferRecv::sendAck() // slot
+{
+ //kdDebug() << "sendAck()" << endl;
+ KIO::fileoffset_t pos = intel( m_transferringPosition );
+
+ m_recvSocket->enableWrite( false );
+ m_recvSocket->writeBlock( (char*)&pos, 4 );
+ if ( m_transferringPosition == (KIO::fileoffset_t)m_fileSize )
+ {
+ kdDebug() << "DccTransferRecv::sendAck(): Sent final ACK." << endl;
+ m_recvSocket->enableRead( false );
+ disconnect( m_recvSocket, 0, 0, 0 );
+ finishTransferLogger();
+ m_writeCacheHandler->close(); // WriteCacheHandler will send the signal done()
+ }
+ else if ( m_transferringPosition > (KIO::fileoffset_t)m_fileSize )
+ {
+ kdDebug() << "DccTransferRecv::sendAck(): the remote host sent larger data than expected: " << QString::number( m_transferringPosition ) << endl;
+ failed( i18n( "Transferring error" ) );
+ }
+}
+
+void DccTransferRecv::slotLocalWriteDone() // <-WriteCacheHandler::done()
+{
+ kdDebug() << "DccTransferRecv::slotLocalWriteDone()" << endl;
+ setStatus( Done );
+ cleanUp();
+ emit done( this );
+}
+
+ // <- WriteCacheHandler::gotError()
+void DccTransferRecv::slotLocalGotWriteError( const QString& errorString )
+{
+ kdDebug() << "DccTransferRecv::slotLocalGotWriteError()" << endl;
+ failed( i18n( "KIO error: %1" ).arg( errorString ) );
+}
+
+void DccTransferRecv::startConnectionTimer( int sec )
+{
+ stopConnectionTimer();
+ kdDebug() << "DccTransferRecv::startConnectionTimer()" << endl;
+ m_connectionTimer->start( sec*1000, true );
+}
+
+void DccTransferRecv::stopConnectionTimer()
+{
+ if ( m_connectionTimer->isActive() )
+ {
+ m_connectionTimer->stop();
+ kdDebug() << "DccTransferRecv::stopConnectionTimer(): stop" << endl;
+ }
+}
+
+void DccTransferRecv::connectionTimeout() // slot
+{
+ kdDebug() << "DccTransferRecv::connectionTimeout()" << endl;
+ failed( i18n( "Timed out" ) );
+}
+
+void DccTransferRecv::slotSocketClosed()
+{
+ finishTransferLogger();
+ if ( getStatus() == Transferring )
+ failed( i18n( "Remote user disconnected" ) );
+}
+
+// WriteCacheHandler
+
+DccTransferRecvWriteCacheHandler::DccTransferRecvWriteCacheHandler( KIO::TransferJob* transferJob )
+: m_transferJob( transferJob )
+{
+ m_writeReady = true;
+ m_cacheStream = 0;
+
+ connect( this, SIGNAL( dataFinished() ), m_transferJob, SLOT( slotFinished() ) );
+ connect( m_transferJob, SIGNAL( dataReq( KIO::Job*, QByteArray& ) ), this, SLOT( slotKIODataReq( KIO::Job*, QByteArray& ) ) );
+ connect( m_transferJob, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotKIOResult( KIO::Job* ) ) );
+
+ m_transferJob->setAsyncDataEnabled( m_writeAsyncMode = true );
+}
+
+DccTransferRecvWriteCacheHandler::~DccTransferRecvWriteCacheHandler()
+{
+ closeNow();
+}
+
+ // public
+void DccTransferRecvWriteCacheHandler::append( char* data, int size )
+{
+ // sendAsyncData() and dataReq() cost a lot of time, so we should pack some caches.
+
+ // 1meg
+ static const unsigned int maxWritePacketSize = 1 * 1024 * 1024;
+
+ if ( m_cacheList.isEmpty() || m_cacheList.back().size() + size > maxWritePacketSize )
+ {
+ m_cacheList.append( QByteArray() );
+ delete m_cacheStream;
+ m_cacheStream = new QDataStream( m_cacheList.back(), IO_WriteOnly );
+ }
+
+ m_cacheStream->writeRawBytes( data, size );
+}
+
+ // public
+bool DccTransferRecvWriteCacheHandler::write( bool force )
+{
+ // force == false: return without doing anything when the whole cache size is smaller than maxWritePacketSize
+
+ if ( m_cacheList.isEmpty() || !m_transferJob || !m_writeReady || !m_writeAsyncMode )
+ return false;
+
+ if ( !force && m_cacheList.count() < 2 )
+ return false;
+
+ // do write
+
+ m_writeReady = false;
+ m_transferJob->sendAsyncData( m_cacheList.front() );
+ //kdDebug() << "DTRWriteCacheHandler::write(): wrote " << m_cacheList.front().size() << " bytes." << endl;
+ m_cacheList.pop_front();
+
+ return true;
+}
+
+void DccTransferRecvWriteCacheHandler::close() // public
+{
+ kdDebug() << "DTRWriteCacheHandler::close()" << endl;
+ write( true ); // write once if kio is ready to write
+ m_transferJob->setAsyncDataEnabled( m_writeAsyncMode = false );
+ kdDebug() << "DTRWriteCacheHandler::close(): switched to synchronized mode." << endl;
+ kdDebug() << "DTRWriteCacheHandler::close(): flushing... (remaining caches: " << m_cacheList.count() << ")" << endl;
+}
+
+void DccTransferRecvWriteCacheHandler::closeNow() // public
+{
+ write( true ); // flush
+ if ( m_transferJob )
+ {
+ m_transferJob->kill();
+ m_transferJob = 0;
+ }
+ m_cacheList.clear();
+ delete m_cacheStream;
+ m_cacheStream = 0;
+}
+
+void DccTransferRecvWriteCacheHandler::slotKIODataReq( KIO::Job*, QByteArray& data )
+{
+ // We are in writeAsyncMode if there is more data to be read in from dcc
+ if ( m_writeAsyncMode )
+ m_writeReady = true;
+ else
+ {
+ // No more data left to read from incoming dcctransfer
+ if ( !m_cacheList.isEmpty() )
+ {
+ // once we write everything in cache, the file is complete.
+ // This function will be called once more after this last data is written.
+ data = m_cacheList.front();
+ kdDebug() << "DccTransferRecvWriteCacheHandler::slotKIODataReq(): will write " << m_cacheList.front().size() << " bytes." << endl;
+ m_cacheList.pop_front();
+ }
+ else
+ {
+ // finally, no data left to write or read.
+ kdDebug() << "DTRWriteCacheHandler::slotKIODataReq(): flushing done." << endl;
+ m_transferJob = 0;
+ emit done(); // -> DccTransferRecv::slotLocalWriteDone()
+ }
+ }
+}
+
+void DccTransferRecvWriteCacheHandler::slotKIOResult( KIO::Job* job )
+{
+ Q_ASSERT( m_transferJob );
+
+ disconnect( m_transferJob, 0, 0, 0 );
+ m_transferJob = 0;
+
+ if ( job->error() )
+ {
+ QString errorString = job->errorString();
+ closeNow();
+ emit gotError( errorString ); // -> DccTransferRecv::slotLocalGotWriteError()
+ }
+}
+
+#include "dcctransferrecv.moc"
diff --git a/konversation/src/dcctransferrecv.h b/konversation/src/dcctransferrecv.h
new file mode 100644
index 0000000..557b61d
--- /dev/null
+++ b/konversation/src/dcctransferrecv.h
@@ -0,0 +1,184 @@
+/*
+ receive a file on DCC protocol
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+// Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#ifndef DCCTRANSFERRECV_H
+#define DCCTRANSFERRECV_H
+
+#include "dcctransfer.h"
+// TODO: remove the dependence
+#include "dccresumedialog.h"
+
+#include <qptrlist.h>
+
+
+class QFile;
+class QTimer;
+
+namespace KIO
+{
+ class Job;
+ class TransferJob;
+}
+
+namespace KNetwork
+{
+ class KServerSocket;
+ class KStreamSocket;
+}
+
+class DccTransferRecvWriteCacheHandler;
+
+class DccTransferRecv : public DccTransfer
+{
+ Q_OBJECT
+
+ public:
+ DccTransferRecv(QObject* parent);
+ virtual ~DccTransferRecv();
+
+ // REQUIRED
+ void setPartnerIp( const QString& ip );
+ // REQUIRED
+ void setPartnerPort( const QString& port );
+ // REQUIRED
+ void setFileSize( unsigned long fileSize );
+ // OPTIONAL, if not specified, "unnamed_file"
+ // TODO: "$sendername-$receiveddate" is better
+ void setFileName( const QString& fileName );
+ // OPTIONAL, if not specified, default folder + the file name
+ void setFileURL( const KURL& url );
+ // OPTIONAL
+ void setReverse( bool reverse, const QString& reverseToken );
+
+ public slots:
+ virtual bool queue();
+
+ /** The user has accepted the download.
+ * Check we are saving it somewhere valid, create any directories needed, and
+ * connect to remote host.
+ */
+ virtual void start();
+ /** The user has chosen to abort.
+ * Either by chosen to abort directly, or by choosing cancel when
+ * prompted for information on where to save etc.
+ * Not called when it fails due to another problem.
+ */
+ virtual void abort();
+ void startResume( unsigned long position );
+
+ protected slots:
+ // Local KIO
+ void slotLocalCanResume( KIO::Job* job, KIO::filesize_t size );
+ void slotLocalGotResult( KIO::Job* job );
+ void slotLocalReady( KIO::Job* job );
+ void slotLocalWriteDone();
+ void slotLocalGotWriteError( const QString& errorString );
+
+ // Remote DCC
+ void connectWithSender();
+ void startReceiving();
+ void connectionFailed( int errorCode );
+ void readData();
+ void sendAck();
+ void connectionTimeout();
+ void slotSocketClosed();
+
+ // Reverse DCC
+ void slotServerSocketReadyAccept();
+ void slotServerSocketGotError( int errorCode );
+
+ protected:
+ void cleanUp();
+ void failed(const QString& errorMessage = QString() );
+
+ // (startPosition == 0) means "don't resume"
+ void prepareLocalKio( bool overwrite, bool resume, KIO::fileoffset_t startPosition = 0 );
+ void askAndPrepareLocalKio( const QString& message, int enabledActions, DccResumeDialog::ReceiveAction defaultAction, KIO::fileoffset_t startPosition = 0 );
+
+ /**
+ * This calls KIO::NetAccess::mkdir on all the subdirectories of dirURL, to
+ * create the given directory. Note that a url like file:/foo/bar will
+ * make sure both foo and bar are created. It assumes everything in the path is
+ * a directory.
+ * Note: If the directory already exists, returns true.
+ *
+ * @param dirURL A url for the directory to create.
+ * @return True if the directory now exists. False if there was a problem and the directory doesn't exist.
+ */
+ bool createDirs(const KURL &dirURL) const;
+
+ void requestResume();
+ // for non-reverse DCC
+ void connectToSendServer();
+ // for reverse DCC
+ bool startListeningForSender();
+
+ void startConnectionTimer( int sec );
+ void stopConnectionTimer();
+
+ protected:
+ KURL m_saveToTmpFileURL;
+ ///Current filesize of the file saved on the disk.
+ KIO::filesize_t m_saveToFileSize;
+ ///Current filesize of the file+".part" saved on the disk.
+ KIO::filesize_t m_partialFileSize;
+ DccTransferRecvWriteCacheHandler* m_writeCacheHandler;
+ bool m_saveToFileExists;
+ bool m_partialFileExists;
+ QTimer* m_connectionTimer;
+
+ KNetwork::KServerSocket* m_serverSocket;
+ KNetwork::KStreamSocket* m_recvSocket;
+
+ private:
+ virtual QString getTypeText() const;
+ virtual QPixmap getTypeIcon() const;
+};
+
+class DccTransferRecvWriteCacheHandler : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit DccTransferRecvWriteCacheHandler( KIO::TransferJob* transferJob );
+ virtual ~DccTransferRecvWriteCacheHandler();
+
+ void append( char* data, int size );
+ bool write( bool force );
+ void close();
+ void closeNow();
+
+ signals:
+ void dataFinished(); // -> m_transferJob->slotFinished()
+ void done(); // -> DccTransferRecv::writeDone()
+ // -> DccTransferRecv::slotWriteError()
+ void gotError( const QString& errorString );
+
+ protected slots:
+ // <- m_transferJob->dataReq()
+ void slotKIODataReq( KIO::Job* job, QByteArray& data );
+ void slotKIOResult( KIO::Job* job ); // <- m_transferJob->result()
+
+ protected:
+ KIO::TransferJob* m_transferJob;
+ bool m_writeAsyncMode;
+ bool m_writeReady;
+
+ QValueList<QByteArray> m_cacheList;
+ QDataStream* m_cacheStream;
+};
+
+#endif // DCCTRANSFERRECV_H
diff --git a/konversation/src/dcctransfersend.cpp b/konversation/src/dcctransfersend.cpp
new file mode 100644
index 0000000..09fc42c
--- /dev/null
+++ b/konversation/src/dcctransfersend.cpp
@@ -0,0 +1,509 @@
+/*
+ send a file on DCC protocol
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+// Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#include "dcctransfersend.h"
+#include "channel.h"
+#include "dcccommon.h"
+#include "dcctransfermanager.h"
+#include "konversationapplication.h"
+#include "connectionmanager.h"
+#include "server.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <qfile.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kserversocket.h>
+#include <ksocketaddress.h>
+#include <kstreamsocket.h>
+#include <kio/netaccess.h>
+#include <kfileitem.h>
+
+// TODO: remove the dependence
+#include <kinputdialog.h>
+
+
+using namespace KNetwork;
+
+DccTransferSend::DccTransferSend(QObject* parent)
+ : DccTransfer( DccTransfer::Send, parent )
+{
+ kdDebug() << "DccTransferSend::DccTransferSend()" << endl;
+
+ m_serverSocket = 0;
+ m_sendSocket = 0;
+
+ m_connectionTimer = new QTimer( this );
+ connect( m_connectionTimer, SIGNAL( timeout() ), this, SLOT( slotConnectionTimeout() ) );
+
+ // set defualt values
+ m_reverse = Preferences::dccPassiveSend();
+}
+
+DccTransferSend::~DccTransferSend()
+{
+ cleanUp();
+}
+
+void DccTransferSend::cleanUp()
+{
+ kdDebug() << "DccTransferSend::cleanUp()" << endl;
+ stopConnectionTimer();
+ finishTransferLogger();
+ if ( !m_tmpFile.isEmpty() )
+ KIO::NetAccess::removeTempFile( m_tmpFile );
+ m_tmpFile = QString();
+ m_file.close();
+ if ( m_sendSocket )
+ {
+ m_sendSocket->close();
+ m_sendSocket = 0; // the instance will be deleted automatically by its parent
+ }
+ if ( m_serverSocket )
+ {
+ m_serverSocket->close();
+ m_serverSocket = 0; // the instance will be deleted automatically by its parent
+ }
+}
+
+// just for convenience
+void DccTransferSend::failed( const QString& errorMessage )
+{
+ setStatus( Failed, errorMessage );
+ cleanUp();
+ emit done( this );
+}
+
+void DccTransferSend::setFileURL( const KURL& url )
+{
+ if ( getStatus() == Configuring )
+ m_fileURL = url;
+}
+
+void DccTransferSend::setFileName( const QString& fileName )
+{
+ if ( getStatus() == Configuring )
+ m_fileName = fileName;
+}
+
+void DccTransferSend::setOwnIp( const QString& ownIp )
+{
+ if ( getStatus() == Configuring )
+ m_ownIp = ownIp;
+}
+
+void DccTransferSend::setFileSize( KIO::filesize_t fileSize )
+{
+ if ( getStatus() == Configuring )
+ m_fileSize = fileSize;
+}
+
+void DccTransferSend::setReverse( bool reverse )
+{
+ if ( getStatus() == Configuring )
+ m_reverse = reverse;
+}
+
+bool DccTransferSend::queue()
+{
+ kdDebug() << "DccTransferSend::queue()" << endl;
+
+ if ( getStatus() != Configuring )
+ return false;
+
+ if ( m_ownIp.isEmpty() )
+ m_ownIp = DccCommon::getOwnIp( KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( m_connectionId ) );
+
+ if ( !kapp->authorize( "allow_downloading" ) )
+ {
+ //Do not have the rights to send the file. Shouldn't have gotten this far anyway
+ //Note this is after the initialisation so the view looks correct still
+ failed(i18n("The admin has restricted the right to send files"));
+ return false;
+ }
+
+ if ( m_fileName.isEmpty() )
+ m_fileName = sanitizeFileName( m_fileURL.fileName() );
+
+ if ( Preferences::dccIPv4Fallback() )
+ {
+ KIpAddress ip( m_ownIp );
+ if ( ip.isIPv6Addr() )
+ {
+ /* This is fucking ugly but there is no KDE way to do this yet :| -cartman */
+ struct ifreq ifr;
+ const char* address = Preferences::dccIPv4FallbackIface().ascii();
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ strncpy( ifr.ifr_name, address, IF_NAMESIZE );
+ ifr.ifr_addr.sa_family = AF_INET;
+ if ( ioctl( sock, SIOCGIFADDR, &ifr ) >= 0 )
+ m_ownIp = inet_ntoa( ( (struct sockaddr_in *)&ifr.ifr_addr )->sin_addr );
+ kdDebug() << "Falling back to IPv4 address " << m_ownIp << endl;
+ }
+ }
+
+ m_fastSend = Preferences::dccFastSend();
+ kdDebug() << "DccTransferSend::DccTransferSend(): Fast DCC send: " << m_fastSend << endl;
+
+ //Check the file exists
+ if ( !KIO::NetAccess::exists( m_fileURL, true, NULL ) )
+ {
+ failed( i18n( "The url \"%1\" does not exist" ).arg( m_fileURL.prettyURL() ) );
+ return false;
+ }
+
+ //FIXME: KIO::NetAccess::download() is a synchronous function. we should use KIO::get() instead.
+ //Download the file. Does nothing if it's local (file:/)
+ if ( !KIO::NetAccess::download( m_fileURL, m_tmpFile, NULL ) )
+ {
+ failed( i18n( "Could not retrieve \"%1\"" ).arg( m_fileURL.prettyURL() ) );
+ kdDebug() << "DccTransferSend::DccTransferSend(): KIO::NetAccess::download() failed. reason: " << KIO::NetAccess::lastErrorString() << endl;
+ return false;
+ }
+
+ //Some protocols, like http, maybe not return a filename, and altFileName may be empty, So prompt the user for one.
+ if ( m_fileName.isEmpty() )
+ {
+ bool pressedOk;
+ m_fileName = KInputDialog::getText( i18n( "Enter Filename" ), i18n( "<qt>The file that you are sending to <i>%1</i> does not have a filename.<br>Please enter a filename to be presented to the receiver, or cancel the dcc transfer</qt>" ).arg( getPartnerNick() ), "unknown", &pressedOk, NULL );
+
+ if ( !pressedOk )
+ {
+ failed( i18n( "No filename was given" ) );
+ return false;
+ }
+ }
+
+ //FIXME: if "\\\"" works well on other IRC clients, replace "\"" with "\\\""
+ m_fileName.replace( "\"", "_" );
+ if (Preferences::dccSpaceToUnderscore())
+ m_fileName.replace( " ", "_" );
+ else {
+ if (m_fileName.contains(" ") > 0)
+ m_fileName = "\"" + m_fileName + "\"";
+ }
+
+ m_file.setName( m_tmpFile );
+
+ if ( m_fileSize == 0 )
+ m_fileSize = m_file.size();
+
+ return DccTransfer::queue();
+}
+
+void DccTransferSend::abort() // public slot
+{
+ kdDebug() << "DccTransferSend::abort()" << endl;
+
+ setStatus( Aborted );
+ cleanUp();
+ emit done( this );
+}
+
+void DccTransferSend::start() // public slot
+{
+ kdDebug() << "DccTransferSend::start()" << endl;
+
+ if ( getStatus() != Queued )
+ return;
+
+ // common procedure
+
+ Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByConnectionId( m_connectionId );
+ if ( !server )
+ {
+ kdDebug() << "DccTransferSend::start(): could not retrieve the instance of Server. Connection id: " << m_connectionId << endl;
+ failed( i18n( "Could not send a DCC SEND request to the partner via the IRC server." ) );
+ return;
+ }
+
+ if ( !m_reverse )
+ {
+ // Normal DCC SEND
+ kdDebug() << "DccTransferSend::start(): normal DCC SEND" << endl;
+
+ // Set up server socket
+ QString failedReason;
+ if ( Preferences::dccSpecificSendPorts() )
+ m_serverSocket = DccCommon::createServerSocketAndListen( this, &failedReason, Preferences::dccSendPortsFirst(), Preferences::dccSendPortsLast() );
+ else
+ m_serverSocket = DccCommon::createServerSocketAndListen( this, &failedReason );
+ if ( !m_serverSocket )
+ {
+ failed( failedReason );
+ return;
+ }
+
+ connect( m_serverSocket, SIGNAL( readyAccept() ), this, SLOT( acceptClient() ) );
+ connect( m_serverSocket, SIGNAL( gotError( int ) ), this, SLOT( slotGotSocketError( int ) ) );
+ connect( m_serverSocket, SIGNAL( closed() ), this, SLOT( slotServerSocketClosed() ) );
+
+ // Get own port number
+ m_ownPort = QString::number( DccCommon::getServerSocketPort( m_serverSocket ) );
+
+ kdDebug() << "DccTransferSend::start(): own Address=" << m_ownIp << ":" << m_ownPort << endl;
+
+ startConnectionTimer( Preferences::dccSendTimeout() );
+
+ server->dccSendRequest( m_partnerNick, m_fileName, getNumericalIpText( m_ownIp ), m_ownPort, m_fileSize );
+ }
+ else
+ {
+ // Passive DCC SEND
+ kdDebug() << "DccTransferSend::start(): passive DCC SEND" << endl;
+
+ int tokenNumber = KonversationApplication::instance()->getDccTransferManager()->generateReverseTokenNumber();
+ // TODO: should we append a letter "T" to this token?
+ m_reverseToken = QString::number( tokenNumber );
+
+ kdDebug() << "DccTransferSend::start(): passive DCC key(token): " << m_reverseToken << endl;
+
+ server->dccPassiveSendRequest( m_partnerNick, m_fileName, getNumericalIpText( m_ownIp ), m_fileSize, m_reverseToken );
+ }
+
+ setStatus( WaitingRemote, i18n( "Waiting remote user's acceptance" ) );
+}
+
+void DccTransferSend::connectToReceiver( const QString& partnerHost, const QString& partnerPort )
+{
+ // Reverse DCC
+
+ m_partnerIp = partnerHost;
+ m_partnerPort = partnerPort;
+
+ m_sendSocket = new KNetwork::KStreamSocket( partnerHost, partnerPort, this );
+
+ m_sendSocket->setBlocking( false );
+
+ connect( m_sendSocket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( startSending() ) );
+ connect( m_sendSocket, SIGNAL( gotError( int ) ), this, SLOT( slotConnectionFailed( int ) ) );
+
+ setStatus( Connecting );
+
+ m_sendSocket->connect();
+}
+ // public
+bool DccTransferSend::setResume( unsigned long position )
+{
+ kdDebug() << "DccTransferSend::setResume(): position=" << position << endl;
+
+ if ( getStatus() > WaitingRemote )
+ return false;
+
+ if ( position >= m_fileSize )
+ return false;
+
+ m_resumed = true;
+ m_transferringPosition = position;
+ return true;
+}
+
+void DccTransferSend::acceptClient() // slot
+{
+ // Normal DCC
+
+ kdDebug() << "DccTransferSend::acceptClient()" << endl;
+
+ stopConnectionTimer();
+
+ m_sendSocket = static_cast<KNetwork::KStreamSocket*>( m_serverSocket->accept() );
+ if ( !m_sendSocket )
+ {
+ failed( i18n( "Could not accept the connection. (Socket Error)" ) );
+ return;
+ }
+
+ // we don't need ServerSocket anymore
+ m_serverSocket->close();
+
+ startSending();
+}
+
+void DccTransferSend::startSending()
+{
+ connect( m_sendSocket, SIGNAL( readyWrite() ), this, SLOT( writeData() ) );
+ connect( m_sendSocket, SIGNAL( readyRead() ), this, SLOT( getAck() ) );
+ connect( m_sendSocket, SIGNAL( closed() ), this, SLOT( slotSendSocketClosed() ) );
+
+ if ( m_sendSocket->peerAddress().asInet().ipAddress().isV4Mapped() )
+ m_partnerIp = KNetwork::KIpAddress( m_sendSocket->peerAddress().asInet().ipAddress().IPv4Addr() ).toString();
+ else
+ m_partnerIp = m_sendSocket->peerAddress().asInet().ipAddress().toString();
+ m_partnerPort = m_sendSocket->peerAddress().serviceName();
+
+ if ( m_file.open( IO_ReadOnly ) )
+ {
+ // seek to file position to make resume work
+ m_file.at( m_transferringPosition );
+ m_transferStartPosition = m_transferringPosition;
+
+ setStatus( Transferring );
+ m_sendSocket->enableWrite( true );
+ m_sendSocket->enableRead( m_fastSend );
+ startTransferLogger(); // initialize CPS counter, ETA counter, etc...
+ }
+ else
+ failed( i18n( "Could not open the file: %1" ).arg( getQFileErrorString( m_file.status() ) ) );
+}
+
+void DccTransferSend::writeData() // slot
+{
+ //kdDebug() << "DccTransferSend::writeData()" << endl;
+ if ( !m_fastSend )
+ {
+ m_sendSocket->enableWrite( false );
+ m_sendSocket->enableRead( true );
+ }
+ int actual = m_file.readBlock( m_buffer, m_bufferSize );
+ if ( actual > 0 )
+ {
+ m_sendSocket->writeBlock( m_buffer, actual );
+ m_transferringPosition += actual;
+ if ( (KIO::fileoffset_t)m_fileSize <= m_transferringPosition )
+ {
+ Q_ASSERT( (KIO::fileoffset_t)m_fileSize == m_transferringPosition );
+ kdDebug() << "DccTransferSend::writeData(): Done." << endl;
+ m_sendSocket->enableWrite( false ); // there is no need to call this function anymore
+ }
+ }
+}
+
+void DccTransferSend::getAck() // slot
+{
+ //kdDebug() << "DccTransferSend::getAck()" << endl;
+ if ( !m_fastSend && m_transferringPosition < (KIO::fileoffset_t)m_fileSize )
+ {
+ m_sendSocket->enableWrite( true );
+ m_sendSocket->enableRead( false );
+ }
+ unsigned long pos;
+ while ( m_sendSocket->bytesAvailable() >= 4 )
+ {
+ m_sendSocket->readBlock( (char*)&pos, 4 );
+ pos = intel( pos );
+ if ( pos == m_fileSize )
+ {
+ kdDebug() << "DccTransferSend::getAck(): Received final ACK." << endl;
+ finishTransferLogger();
+ setStatus( Done );
+ cleanUp();
+ emit done( this );
+ break; // for safe
+ }
+ }
+}
+
+void DccTransferSend::slotGotSocketError( int errorCode )
+{
+ kdDebug() << "DccTransferSend::slotGotSocketError(): code = " << errorCode << " string = " << m_serverSocket->errorString() << endl;
+ failed( i18n( "Socket error: %1" ).arg( m_serverSocket->errorString() ) );
+}
+
+void DccTransferSend::startConnectionTimer( int sec )
+{
+ kdDebug() << "DccTransferSend::startConnectionTimer()"<< endl;
+ stopConnectionTimer();
+ m_connectionTimer->start( sec*1000, true );
+}
+
+void DccTransferSend::stopConnectionTimer()
+{
+ if ( m_connectionTimer->isActive() )
+ {
+ kdDebug() << "DccTransferSend::stopConnectionTimer(): stop" << endl;
+ m_connectionTimer->stop();
+ }
+}
+
+void DccTransferSend::slotConnectionTimeout() // slot
+{
+ kdDebug() << "DccTransferSend::slotConnectionTimeout()" << endl;
+ failed( i18n( "Timed out" ) );
+}
+
+void DccTransferSend::slotConnectionFailed( int /* errorCode */ )
+{
+ failed( i18n( "Connection failure: %1" ).arg( m_sendSocket->errorString() ) );
+}
+
+void DccTransferSend::slotServerSocketClosed()
+{
+ kdDebug() << "DccTransferSend::slotServerSocketClosed()" << endl;
+}
+
+void DccTransferSend::slotSendSocketClosed()
+{
+ kdDebug() << "DccTransferSend::slotSendSocketClosed()" << endl;
+ finishTransferLogger();
+ if ( getStatus() == Transferring && m_transferringPosition < (KIO::fileoffset_t)m_fileSize )
+ failed( i18n( "Remote user disconnected" ) );
+}
+
+ // protected, static
+QString DccTransferSend::getQFileErrorString( int code )
+{
+ QString errorString;
+
+ switch(code)
+ {
+ case IO_Ok:
+ errorString=i18n("The operation was successful. Should never happen in an error dialog.");
+ break;
+ case IO_ReadError:
+ errorString=i18n("Could not read from file \"%1\".").arg( m_fileName );
+ break;
+ case IO_WriteError:
+ errorString=i18n("Could not write to file \"%1\".").arg( m_fileName );
+ break;
+ case IO_FatalError:
+ errorString=i18n("A fatal unrecoverable error occurred.");
+ break;
+ case IO_OpenError:
+ errorString=i18n("Could not open file \"%1\".").arg( m_fileName );
+ break;
+
+ // Same case value? Damn!
+ // case IO_ConnectError:
+ // errorString="Could not connect to the device.";
+ // break;
+
+ case IO_AbortError:
+ errorString=i18n("The operation was unexpectedly aborted.");
+ break;
+ case IO_TimeOutError:
+ errorString=i18n("The operation timed out.");
+ break;
+ case IO_UnspecifiedError:
+ errorString=i18n("An unspecified error happened on close.");
+ break;
+ default:
+ errorString=i18n("Unknown error. Code %1").arg(code);
+ break;
+ }
+
+ return errorString;
+}
+
+#include "dcctransfersend.moc"
diff --git a/konversation/src/dcctransfersend.h b/konversation/src/dcctransfersend.h
new file mode 100644
index 0000000..dfe6ad6
--- /dev/null
+++ b/konversation/src/dcctransfersend.h
@@ -0,0 +1,98 @@
+/*
+ send a file on DCC protocol
+ begin: Mit Aug 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+// Copyright (C) 2004-2007 Shintaro Matsuoka <shin@shoegazed.org>
+// Copyright (C) 2004,2005 John Tapsell <john@geola.co.uk>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#ifndef DCCTRANSFERSEND_H
+#define DCCTRANSFERSEND_H
+
+#include "dcctransfer.h"
+
+#include <qfile.h>
+
+
+class QTimer;
+
+namespace KNetwork
+{
+ class KServerSocket;
+ class KStreamSocket;
+}
+
+class DccTransferSend : public DccTransfer
+{
+ Q_OBJECT
+
+ public:
+ DccTransferSend(QObject* parent);
+ virtual ~DccTransferSend();
+
+ // REQUIRED
+ void setFileURL( const KURL& url );
+ // OPTIONAL
+ void setFileName( const QString& fileName );
+ // REQUIED
+ // FIXME: this setting should be an optional one or be removed: make DccTransferSend itself read the configuration
+ void setOwnIp( const QString& ownIp );
+ // OPTIONAL
+ void setFileSize( KIO::filesize_t fileSize );
+ // OPTIONAL
+ void setReverse( bool reverse );
+
+ bool setResume( unsigned long position );
+
+ public slots:
+ virtual bool queue();
+ virtual void start();
+ virtual void abort();
+
+ // invoked when the receiver accepts the offer (Reverse DCC)
+ void connectToReceiver( const QString& partnerHost, const QString& partnerPort );
+
+ protected slots:
+ void acceptClient();
+ // it must be invoked when m_sendSocket is ready
+ void startSending();
+ void writeData();
+ void getAck();
+ void slotGotSocketError( int errorCode );
+ void slotConnectionTimeout();
+ void slotConnectionFailed( int errorCode );
+ void slotSendSocketClosed();
+ void slotServerSocketClosed();
+
+ protected:
+ void cleanUp();
+ void failed(const QString& errorMessage = QString() );
+
+ void startConnectionTimer( int sec );
+ void stopConnectionTimer();
+
+ QString getQFileErrorString( int code );
+
+ QFile m_file;
+
+ /*The filename of the temporary file that we downloaded. So if send a file ftp://somewhere/file.txt
+ * Then this will be downloaded to /tmp.
+ */
+ QString m_tmpFile;
+
+ KNetwork::KServerSocket* m_serverSocket;
+ KNetwork::KStreamSocket* m_sendSocket;
+ bool m_fastSend;
+
+ QTimer* m_connectionTimer;
+};
+
+#endif // DCCTRANSFERSEND_H
diff --git a/konversation/src/decoder.h b/konversation/src/decoder.h
new file mode 100644
index 0000000..578dddb
--- /dev/null
+++ b/konversation/src/decoder.h
@@ -0,0 +1,113 @@
+/*
+ This file is part of the KDE libraries
+
+ Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+#ifndef KHTMLDECODER_H
+#define KHTMLDECODER_H
+
+#include <qstring.h>
+
+class QTextCodec;
+class QTextDecoder;
+
+class JapaneseCode;
+
+/**
+ * @internal
+ */
+class Decoder
+{
+public:
+ enum EncodingType {
+ DefaultEncoding,
+ AutoDetectedEncoding,
+ EncodingFromXMLHeader,
+ EncodingFromMetaTag,
+ EncodingFromHTTPHeader,
+ UserChosenEncoding
+ };
+
+ Decoder();
+ ~Decoder();
+
+ void setEncoding(const char *encoding, EncodingType type);
+ const char *encoding() const;
+
+ QString decode(const char *data, int len);
+
+ bool visuallyOrdered() const { return visualRTL; }
+
+ const QTextCodec *codec() const { return m_codec; }
+
+ QString flush() const;
+
+
+ enum AutoDetectLanguage {
+ SemiautomaticDetection,
+ Arabic,
+ Baltic,
+ CentralEuropean,
+ Chinese,
+ Greek,
+ Hebrew,
+ Japanese,
+ Korean,
+ Russian,
+ Thai,
+ Turkish,
+ Ukrainian,
+ Unicode,
+ WesternEuropean
+ };
+
+ void setAutoDetectLanguage( AutoDetectLanguage _language ) { m_autoDetectLanguage = _language; }
+ AutoDetectLanguage autoDetectLanguage() { return m_autoDetectLanguage; }
+
+
+
+private:
+ QCString automaticDetectionForArabic( const unsigned char* str, int size );
+ QCString automaticDetectionForBaltic( const unsigned char* str, int size );
+ QCString automaticDetectionForCentralEuropean( const unsigned char* str, int size );
+ QCString automaticDetectionForCyrillic( const unsigned char* str, int size, AutoDetectLanguage _language );
+ QCString automaticDetectionForGreek( const unsigned char* str, int size );
+ QCString automaticDetectionForHebrew( const unsigned char* str, int size );
+ QCString automaticDetectionForJapanese( const unsigned char* str, int size );
+ QCString automaticDetectionForTurkish( const unsigned char* str, int size );
+ QCString automaticDetectionForWesternEuropean( const unsigned char* str, int size );
+
+ // codec used for decoding. default is Latin1.
+ QTextCodec *m_codec;
+ QTextDecoder *m_decoder; // only used for utf16
+ QCString enc;
+ EncodingType m_type;
+
+ QCString buffer;
+
+ bool body;
+ bool beginning;
+ bool visualRTL;
+
+ AutoDetectLanguage m_autoDetectLanguage;
+
+ JapaneseCode *kc;
+};
+
+#endif
diff --git a/konversation/src/editnotifydialog.cpp b/konversation/src/editnotifydialog.cpp
new file mode 100644
index 0000000..fe6779c
--- /dev/null
+++ b/konversation/src/editnotifydialog.cpp
@@ -0,0 +1,103 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Wed Sep 1 2004
+ copyright: (C) 2004 by Gary Cramblitt
+ email: garycramblitt@comcast.net
+*/
+
+#include "editnotifydialog.h"
+#include "konversationapplication.h"
+#include "servergroupsettings.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qwhatsthis.h>
+
+#include <klineedit.h>
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+
+EditNotifyDialog::EditNotifyDialog(QWidget* parent,
+const QString& network,
+const QString& nickname):
+
+KDialogBase(parent,"editnotify",true,i18n("Edit Watched Nickname"),
+KDialogBase::Ok | KDialogBase::Cancel,
+KDialogBase::Ok,true)
+
+{
+ QWidget* page=new QWidget(this);
+ setMainWidget(page);
+
+ QHBoxLayout* layout = new QHBoxLayout(page);
+ layout->setSpacing(spacingHint());
+
+ QLabel* networkNameLabel=new QLabel(i18n("&Network name:"),page);
+ QString networkNameWT = i18n(
+ "Pick the server network you will connect to here.");
+ QWhatsThis::add(networkNameLabel, networkNameWT);
+ m_networkNameCombo=new KComboBox(page,"notify_network_combo");
+ QWhatsThis::add(m_networkNameCombo, networkNameWT);
+ networkNameLabel->setBuddy(m_networkNameCombo);
+
+ QLabel* nicknameLabel=new QLabel(i18n("N&ickname:"),page);
+ QString nicknameWT = i18n(
+ "<qt>The nickname to watch for when connected to a server in the network.</qt>");
+ QWhatsThis::add(nicknameLabel, nicknameWT);
+ m_nicknameInput = new KLineEdit(nickname, page);
+ QWhatsThis::add(m_nicknameInput, nicknameWT);
+ nicknameLabel->setBuddy(m_nicknameInput);
+
+ // Build a list of unique server network names.
+ // TODO: The "ServerGroupList type is a misnomer (it is actually networks), which
+ // should be fixed at some point.
+ Konversation::ServerGroupList serverNetworks = Preferences::serverGroupList();
+ QStringList networkNames;
+
+ for(Konversation::ServerGroupList::iterator it = serverNetworks.begin(); it != serverNetworks.end(); ++it)
+ {
+ QString name = (*it)->name();
+
+ if (!networkNames.contains(name))
+ {
+ networkNames.append(name);
+ }
+ }
+
+ networkNames.sort();
+ // Add network names to network combobox and select the one corresponding to argument.
+ for (QStringList::ConstIterator it = networkNames.begin(); it != networkNames.end(); ++it)
+ {
+ m_networkNameCombo->insertItem(*it);
+ if(*it == network) m_networkNameCombo->setCurrentItem(m_networkNameCombo->count()-1);
+ }
+
+ layout->addWidget(networkNameLabel);
+ layout->addWidget(m_networkNameCombo);
+ layout->addWidget(nicknameLabel);
+ layout->addWidget(m_nicknameInput);
+
+ setButtonOK(KGuiItem(i18n("&OK"),"button_ok",i18n("Change notify information")));
+ setButtonCancel(KGuiItem(i18n("&Cancel"),"button_cancel",i18n("Discards all changes made")));
+}
+
+EditNotifyDialog::~EditNotifyDialog()
+{
+}
+
+void EditNotifyDialog::slotOk()
+{
+ emit notifyChanged(m_networkNameCombo->currentText(),
+ m_nicknameInput->text());
+ delayedDestruct();
+}
+
+#include "editnotifydialog.moc"
diff --git a/konversation/src/editnotifydialog.h b/konversation/src/editnotifydialog.h
new file mode 100644
index 0000000..0070ac9
--- /dev/null
+++ b/konversation/src/editnotifydialog.h
@@ -0,0 +1,50 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Wed Sep 1 2004
+ copyright: (C) 2004 by Gary Cramblitt
+ email: garycramblitt@comcast.net
+*/
+
+#ifndef EDITNOTIFYDIALOG_H
+#define EDITNOTIFYDIALOG_H
+
+#include <kdialogbase.h>
+
+/**
+ The EditNotifyDialog implements the dialog for user to add or edit a
+ notify. A notify consists of a server network name and a nickname.
+ User must pick the server network name from existing network names
+ in the server list.
+
+ @author Gary Cramblitt <garycramblitt@comcast.net>
+*/
+
+class KLineEdit;
+class KComboBox;
+
+class EditNotifyDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ explicit EditNotifyDialog(QWidget* parent=0,const QString& network=QString(),
+ const QString& nickname=QString());
+ ~EditNotifyDialog();
+
+ signals:
+ void notifyChanged(const QString& network,
+ const QString& nickname);
+ protected slots:
+ void slotOk();
+
+ protected:
+ KComboBox* m_networkNameCombo;
+ KLineEdit* m_nicknameInput;
+};
+#endif
diff --git a/konversation/src/emoticon.cpp b/konversation/src/emoticon.cpp
new file mode 100644
index 0000000..e193e39
--- /dev/null
+++ b/konversation/src/emoticon.cpp
@@ -0,0 +1,209 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+*/
+
+#include "emoticon.h"
+
+#include <qregexp.h>
+#include <qdom.h>
+#include <qfile.h>
+#include <qfontmetrics.h>
+
+#include <kstaticdeleter.h>
+#include <kstandarddirs.h>
+#include <kdeversion.h>
+
+#include "konversationapplication.h"
+#include "config/preferences.h"
+
+
+namespace Konversation
+{
+
+ EmotIcon* EmotIcon::s_self = 0;
+ static KStaticDeleter<EmotIcon> staticEmotIconDeleter;
+
+ EmotIcon* EmotIcon::self()
+ {
+ if(!s_self)
+ {
+ staticEmotIconDeleter.setObject(s_self, new EmotIcon());
+ }
+
+ return s_self;
+ }
+
+ EmotIcon::EmotIcon()
+ {
+ s_self = this;
+
+ if(Preferences::enableEmotIcons())
+ {
+ changeTheme(Preferences::emotIconTheme());
+ }
+ }
+
+ EmotIcon::~EmotIcon()
+ {
+ if(s_self == this)
+ {
+ staticEmotIconDeleter.setObject(s_self, 0, false);
+ }
+ }
+
+ void EmotIcon::changeTheme(const QString& themeName)
+ {
+ if(themeName.isEmpty() || themeName == self()->m_themeName)
+ {
+ return;
+ }
+
+ #if KDE_IS_VERSION(3,3,91)
+ QString filename = KGlobal::dirs()->findResource("emoticons", themeName + "/emoticons.xml");
+ self()->m_themeName = themeName;
+ #else
+ QString app = "konversation";
+ QString filename = KGlobal::dirs()->findResource("data", app + "/pics/emoticons/" + themeName + "/emoticons.xml");
+
+ if(filename.isEmpty())
+ {
+ app = "kopete";
+ filename = KGlobal::dirs()->findResource("data", app + "/pics/emoticons/" + themeName + "/emoticons.xml");
+ }
+ self()->m_themeName = app + '/' + themeName;
+ #endif
+
+ if(filename.isEmpty())
+ {
+ return;
+ }
+
+ QFile file(filename);
+ file.open(IO_ReadOnly);
+ QDomDocument doc;
+ doc.setContent(&file);
+
+ QDomElement docElement = doc.documentElement();
+
+ if(docElement.tagName() != "messaging-emoticon-map")
+ {
+ return;
+ }
+
+ self()->m_emotIconMap.clear();
+
+ QDomNode node = docElement.firstChild();
+ QDomElement element;
+ QDomNode stringNode;
+ QDomElement stringElement;
+ QString fileAttrib;
+ QString regExpStr;
+
+ while(!node.isNull())
+ {
+ element = node.toElement();
+
+ if(!element.isNull() && element.tagName() == "emoticon")
+ {
+ fileAttrib = findIcon(element.attribute("file", QString()));
+ regExpStr = "";
+ stringNode = element.firstChild();
+
+ while(!stringNode.isNull())
+ {
+ stringElement = stringNode.toElement();
+
+ if(!stringElement.isNull() && stringElement.tagName() == "string")
+ {
+ if(regExpStr.isEmpty())
+ {
+ regExpStr = "(";
+ }
+ else
+ {
+ regExpStr += '|';
+ }
+
+ regExpStr += QRegExp::escape(stringElement.text());
+ }
+
+ stringNode = stringNode.nextSibling();
+ }
+
+ if(!regExpStr.isEmpty() && !fileAttrib.isEmpty())
+ {
+ regExpStr += ')';
+ self()->m_emotIconMap[fileAttrib] = regExpStr;
+ }
+ }
+
+ node = node.nextSibling();
+ }
+ }
+
+ QString EmotIcon::filter(const QString& txt, const QFontMetrics& fm)
+ {
+ if(!Preferences::enableEmotIcons())
+ {
+ return txt;
+ }
+
+ QString filteredTxt = txt;
+
+ for(EmotIconMap::iterator it = self()->m_emotIconMap.begin(); it != self()->m_emotIconMap.end(); ++it)
+ {
+ QRegExp regExp(QString("(^|\\s)%1($|\\s)").arg(it.data()));
+ filteredTxt.replace(regExp, " <img width=\"" + QString::number(fm.height()) + "\" height=\"" + QString::number(fm.height())
+ + "\" src=\"" + it.key() + "\" alt=\"" + it.data() + "\">&nbsp;");
+ }
+
+ return filteredTxt;
+ }
+
+ QString EmotIcon::findIcon(const QString& filename)
+ {
+ //
+ // This code was copied from void KopeteEmoticons::addIfPossible( const QString& filename, QStringList emoticons )
+ // Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ // Copyright (c) 2002 by Olivier Goffart <ogoffart@tiscalinet.be>
+ //
+ KStandardDirs *dirs = KGlobal::dirs();
+ QString pic;
+
+ #if KDE_IS_VERSION(3,3,91)
+ QString file = self()->m_themeName + '/' + filename;
+ const char* resource = "emoticons";
+ #else
+ QString app = self()->m_themeName.section('/', 0, 0);
+ QString dir = self()->m_themeName.section('/', 1);
+ QString file = app + "/pics/emoticons/" + dir + '/' + filename;
+ const char* resource = "data";
+ #endif
+
+ //maybe an extension was given, so try to find the exact file
+ pic = dirs->findResource(resource, file);
+
+ if(pic.isEmpty())
+ {
+ pic = dirs->findResource(resource, file + ".mng");
+ }
+ if(pic.isEmpty())
+ {
+ pic = dirs->findResource(resource, file + ".png");
+ }
+ if(pic.isEmpty())
+ {
+ pic = dirs->findResource(resource, file + ".gif");
+ }
+
+ return pic;
+ }
+
+}
diff --git a/konversation/src/emoticon.h b/konversation/src/emoticon.h
new file mode 100644
index 0000000..824a25a
--- /dev/null
+++ b/konversation/src/emoticon.h
@@ -0,0 +1,47 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+*/
+
+#ifndef KONVERSATIONEMOTICON_H
+#define KONVERSATIONEMOTICON_H
+
+#include <qstring.h>
+#include <qmap.h>
+
+
+class QFontMetrics;
+
+namespace Konversation
+{
+
+ typedef QMap<QString, QString> EmotIconMap;
+
+ class EmotIcon
+ {
+ public:
+ ~EmotIcon();
+ static EmotIcon* self();
+
+ static void changeTheme(const QString& themeName);
+ static QString filter(const QString& txt, const QFontMetrics& fm);
+
+ protected:
+ EmotIcon();
+ static EmotIcon* s_self;
+
+ static QString findIcon(const QString& filename);
+
+ private:
+ QString m_themeName;
+ EmotIconMap m_emotIconMap;
+ };
+
+}
+#endif
diff --git a/konversation/src/eventsrc b/konversation/src/eventsrc
new file mode 100644
index 0000000..5fd65ad
--- /dev/null
+++ b/konversation/src/eventsrc
@@ -0,0 +1,973 @@
+[!Global!]
+IconName=konversation
+Comment=Konversation
+Comment[hi]=कनवर्सेसन
+Comment[pa]=ਕੇ-ਗੱਲਬਾਤ
+Comment[ta]=உரையாடல்
+Comment[xx]=xxKonversationxx
+
+[message]
+Name=New message
+Name[ar]=رسالة جديدة
+Name[bg]=Ново съобщение
+Name[br]=Kemennad nevez
+Name[bs]=Nova poruka
+Name[ca]=Nou missatge
+Name[cs]=Nová zpráva
+Name[da]=Ny besked
+Name[de]=Neue Nachricht
+Name[el]=Νέο μήνυμα
+Name[es]=Nuevo mensaje
+Name[et]=Uus sõnum
+Name[fi]=Uusi viesti
+Name[fr]=Nouveau message
+Name[ga]=Teachtaireacht nua
+Name[gl]=Nova mensaxe
+Name[he]=הודעה חדשה
+Name[hi]=नया संदेश
+Name[hu]=Új üzenet
+Name[it]=Nuovo messaggio
+Name[ja]=新規メッセージ
+Name[ka]=ახალი შეტყობინება
+Name[lt]=Nauja žinutė
+Name[nl]=Nieuw bericht
+Name[pa]=ਨਵਾਂ ਸੁਨੇਹਾ
+Name[pt]=Nova mensagem
+Name[pt_BR]=Nova mensagem
+Name[ru]=Новое сообщение
+Name[sr]=Нова порука
+Name[sr@Latn]=Nova poruka
+Name[sv]=Nytt meddelande
+Name[ta]=புதியச் செய்தி
+Name[tr]=Yeni ileti
+Name[uk]=Нове повідомлення
+Name[uz]=Yangi xabar
+Name[uz@cyrillic]=Янги хабар
+Name[xx]=xxNew messagexx
+Name[zh_CN]=新消息
+Name[zh_TW]=新的訊息
+Comment=New message arrived in a channel
+Comment[ar]=وصلة رِسالة جديدة في قناة
+Comment[bg]=Пристигна ново съобщение в канала
+Comment[br]=Deuet eo ur postel nevez en ur c'hanol
+Comment[bs]=Nova poruka pristigla na kanal
+Comment[ca]=Ha arribat un nou missatge a un canal
+Comment[cs]=Na kanálu se objevila nová zpráva
+Comment[da]=Ny besked ankom i kanal
+Comment[de]=Neue Nachricht in einem Kanal
+Comment[el]=Νέο μήνυμα έφτασε στο κανάλι
+Comment[es]=Ha llegado un nuevo mensaje a un canal
+Comment[et]=Kanalile saabus uus sõnum
+Comment[fi]=Kanavalle saapui uusi viesti
+Comment[fr]=Un nouveau message est arrivé dans un canal
+Comment[ga]=Tháinig teachtaireacht nua i gcainéal
+Comment[gl]=Chegou unha mensaxe nova nun canal
+Comment[he]=הודעה חדשה הגיעה לחדר
+Comment[hi]=चैनल में नया संदेश आया
+Comment[hu]=Új üzenet érkezett egy csatornára
+Comment[it]=Nuovo messaggio arrivato in un canale
+Comment[ja]=新しいメッセージがチャンネルに届きました
+Comment[ka]=ახალი შეტყობინება მოვიდა არხზე
+Comment[lt]=Kanale gauta nauja žinutė
+Comment[nl]=Nieuw bericht gearriveerd in een kanaal
+Comment[pa]=ਇੱਕ ਚੈਨਲ 'ਚ ਨਵਾਂ ਸੁਨੇਹਾ ਆਇਆ ਹੈ
+Comment[pt]=Nova mensagem chegou ao canal
+Comment[pt_BR]=Uma nova mensagem chegou em um canal
+Comment[ru]=Получено новое сообщение на канале
+Comment[sr]=Нова порука је стигла у каналу
+Comment[sr@Latn]=Nova poruka je stigla u kanalu
+Comment[sv]=Nytt meddelande anlände i en kanal
+Comment[ta]=தடங்களிருந்து புதிய தகவல் வந்தது
+Comment[tr]=Kanala yeni ileti geldi
+Comment[uk]=В канал надійшло нове повідомлення
+Comment[xx]=xxNew message arrived in a channelxx
+Comment[zh_CN]=频道中有新消息
+Comment[zh_TW]=新訊息已送達頻道
+default_presentation=0
+
+[nick]
+Name=Nick written
+Name[ar]=كُُتِب الأسم المستعار
+Name[bg]=Написан псевдоним
+Name[bs]=Nick zapisan
+Name[ca]=Escriure sobrenom
+Name[cs]=Zapsána přezdívka
+Name[da]=Alias skrevet
+Name[de]=Spitzname erwähnt
+Name[el]=Το ψευδώνυμο γράφτηκε
+Name[es]=Apodo escrito
+Name[et]=Hüüdnimi kirjutatud
+Name[fi]=Nimimerkki kirjoitettiin
+Name[fr]=Pseudonyme écrit
+Name[ga]=Scríobhadh do leasainm
+Name[gl]=Alcume escrito
+Name[he]=הכינוי נכתב
+Name[hi]=निक लिखा
+Name[hu]=becenév kiírva
+Name[it]=Nick scritto
+Name[ja]=ニックが書かれました
+Name[ka]=მეტსახელი დაწერილია
+Name[lt]=Paminėtas slapyvardis
+Name[nl]=Bijnaam geschreven
+Name[pa]=ਨਾਂ ਲਿਖਿਆ
+Name[pt]=Alcunha escrita
+Name[pt_BR]=Apelido escrito
+Name[ru]=Упоминание вашего ника
+Name[sr]=Надимак је уписан
+Name[sr@Latn]=Nadimak je upisan
+Name[sv]=Något skrivet till smeknamnet
+Name[ta]=பட்டை எழுத்து
+Name[tr]=Takma ad yazıldı
+Name[uk]=Написане прізвисько
+Name[xx]=xxNick writtenxx
+Name[zh_CN]=昵称写入
+Name[zh_TW]=有人提到您的暱稱
+Comment=Someone wrote your nick in a message
+Comment[ar]=أحد ما كتب إسمك المستعار في رسالة ما
+Comment[bg]=Псевдонимът ви бе написан в съобщение
+Comment[bs]=Neko je spomenuo vaš nick u poruci
+Comment[ca]=Algú ha escrit el vostre sobrenom en un missatge
+Comment[cs]=Někdo ve zprávě napsal vaší přezdívku
+Comment[da]=Nogen skrev dit alias i en besked
+Comment[de]=Jemand hat Ihren Spitznamen in einer Nachricht erwähnt
+Comment[el]=Κάποιος έγραψε το ψευδώνυμο σας σε ένα μήνυμα
+Comment[es]=Alguien ha escrito su apodo en un mensaje
+Comment[et]=Keegi kasutas sõnumis sinu hüüdnime
+Comment[fi]=Joku kirjoitti nimimerkkisi viestiin
+Comment[fr]=Quelqu'un a écrit votre pseudonyme dans un message
+Comment[ga]=Scríobh duine éigin do leasainm i dteachtaireacht
+Comment[gl]=Alguén escrebeu o seu alcume nunha mensaxe
+Comment[he]=מישהו כתב את הכינוי שלך בהודעה
+Comment[hi]=संदेश मे किसी ने आपका निक लिखा
+Comment[hu]=Valaki leírta az Ön becenevét az egyik csatornán
+Comment[it]=Qualcuno ha scritto il tuo nick in un messaggio
+Comment[ja]=誰かがあなたのニックをメッセージに書きました
+Comment[ka]=ვიღაცამ შეტყობინებაში თქვენი მეტსახელი დაწერა
+Comment[lt]=Kažkas paminėjo jūsų slapyvardį žinutėje
+Comment[nl]=Iemand schreef uw bijnaam in een bericht
+Comment[pt]=Alguém escrever a sua alcunha numa mensagem
+Comment[pt_BR]=Alguém escreveu o seu nick em uma mensagem
+Comment[ru]=Кто-то написал ваше имя в своём собщении
+Comment[sr]=Неко је написао ваш надимак у поруци
+Comment[sr@Latn]=Neko je napisao vaš nadimak u poruci
+Comment[sv]=Någon skrev ett meddelande till ditt smeknamn
+Comment[ta]=உங்கள் வடுத் தகவலில் ஒரு சிலர் எழுதியுள்ளனர்
+Comment[tr]=Birisi iletisinde sizin takma adınızı yazdı
+Comment[uk]=Хтось в повідомленні написав ваше прізвисько
+Comment[xx]=xxSomeone wrote your nick in a messagexx
+Comment[zh_CN]=有人在消息中输入了你的昵称
+Comment[zh_TW]=有人在訊息中提到您的暱稱
+default_presentation=0
+
+[queryMessage]
+Name=Private message
+Name[bg]=Лично съобщение
+Name[cs]=Soukromá zpráva
+Name[da]=Provat besked
+Name[de]=Private Nachricht
+Name[el]=Προσωπικό μήνυμα
+Name[es]=Mensaje privado
+Name[et]=Privaatsõnum
+Name[ga]=Teachtaireacht phríobháideach
+Name[it]=Messaggio privato
+Name[ja]=プライベートメッセージ
+Name[nl]=Privaat bericht
+Name[pa]=ਪ੍ਰਾਈਵੇਟ ਸੁਨੇਹਾ
+Name[pt]=Mensagem privada
+Name[pt_BR]=Mensagem particular
+Name[sr]=Приватна порука
+Name[sr@Latn]=Privatna poruka
+Name[sv]=Privat meddelande
+Name[tr]=Özel ileti
+Name[uk]=Приватне повідомлення
+Name[xx]=xxPrivate messagexx
+Name[zh_CN]=私人消息
+Name[zh_TW]=私密訊息
+Comment=You received a private message
+Comment[bg]=Получихте лично съобщение
+Comment[cs]=Obdrželi jste soukromou zprávu
+Comment[da]=Du modtog en privat besked
+Comment[de]=Sie haben eine private Nachricht erhalten
+Comment[el]=Λάβατε ένα προσωπικό μήνυμα
+Comment[es]=Ha recibido un mensaje privado
+Comment[et]=Sa said privaatsõnumi
+Comment[ga]=Fuair tú teachtaireacht phríobháideach
+Comment[it]=Hai ricevuto un messaggio privato
+Comment[ja]=プライベートメッセージを受け取りました
+Comment[nl]=U ontving een privaat bericht
+Comment[pt]=Você recebeu uma mensagem privada
+Comment[pt_BR]=Você recebeu uma mensagem particular
+Comment[sr]=Примили сте приватну поруку
+Comment[sr@Latn]=Primili ste privatnu poruku
+Comment[sv]=Du har tagit emot ett privat meddelande
+Comment[tr]=Bir özel ileti aldınız
+Comment[uk]=Ви отримали приватне повідомлення
+Comment[xx]=xxYou received a private messagexx
+Comment[zh_CN]=您收到了私人消息
+Comment[zh_TW]=您接收到一個私密訊息
+default_presentation=0
+
+[nickchange]
+Name=Nick changed
+Name[ar]=تغيّر الإسم المستعار
+Name[bg]=Псевдонимът е променен
+Name[bs]=Nick promijenjen
+Name[ca]=Sobrenom canviat
+Name[cs]=Přezdívka změněna
+Name[da]=Alias ændret
+Name[de]=Spitzname geändert
+Name[el]=Το ψευδώνυμο άλλαξε
+Name[es]=Apodo modificado
+Name[et]=Hüüdnimi muudetud
+Name[fi]=Nimimerkki muuttui
+Name[fr]=Pseudonyme modifié
+Name[ga]=Athraíodh leasainm
+Name[gl]=Alcume trocado
+Name[he]=הכינוי שונה
+Name[hi]=निक बदला
+Name[hu]=Becenév módosítva
+Name[it]=Nick cambiato
+Name[ja]=ニック変更
+Name[ka]=მეტსახელი შეიცვალა
+Name[lt]=Slapyvardis pakeistas
+Name[nl]=Bijnaam gewijzigd
+Name[pa]=Nick ਬਦਲਿਆ
+Name[pt]=Alcunha alterada
+Name[pt_BR]=Apelido modificado
+Name[ru]=Изменён ник
+Name[sr]=Надимак је промењен
+Name[sr@Latn]=Nadimak je promenjen
+Name[sv]=Smeknamn ändrat
+Name[ta]= மாற்றியப் பட்டை
+Name[tr]=Takma ad değişti
+Name[uk]=Змінене прізвисько
+Name[xx]=xxNick changedxx
+Name[zh_CN]=昵称改变
+Name[zh_TW]=變更暱稱
+Comment=Someone changed their nick
+Comment[ar]=أحد ما غيّر إسمه المستعار
+Comment[bg]=Някой си промени псевдонима
+Comment[bs]=Neko je promijenio svoj nick
+Comment[ca]=Algú ha canviat el seu sobrenom
+Comment[cs]=Někdo změnil svou přezdívku
+Comment[da]=Nogen ændrede deres alias
+Comment[de]=Jemand hat seinen Spitznamen geändert
+Comment[el]=Κάποιος άλλαξε το ψευδώνυμο του
+Comment[es]=Alguien ha cambiado su apodo
+Comment[et]=Keegi muutis oma hüüdnime
+Comment[fi]=Joku muutti nimimerkkiään
+Comment[fr]=Quelqu'un a changé de pseudonyme
+Comment[ga]=D'athraigh duine éigin a leasainm
+Comment[gl]=Alguén trocou de alcume
+Comment[he]=מישהו שינה את הכינוי שלו
+Comment[hi]=किसी ने अपना निक बदला
+Comment[hu]=Valaki megváltoztatta a becenevét
+Comment[it]=Qualcuno ha cambiato il proprio nick
+Comment[ja]=誰かがニックを変更しました
+Comment[ka]=ვიღაცამ მეტსახელი შეიცვალა
+Comment[lt]=Kažkas pasikeitė savo slapyvardį
+Comment[nl]=Iemand veranderde zijn of haar bijnaam
+Comment[pt]=Alguém mudou a sua alcunha
+Comment[pt_BR]=Alguém mudou o seu apelido
+Comment[ru]=Кто-то сменил своё имя
+Comment[sr]=Неко је променио свој надимак
+Comment[sr@Latn]=Neko je promenio svoj nadimak
+Comment[sv]=Någon ändrade sitt smeknamn
+Comment[ta]=வடுவை ஒரு சிலர் மாற்றினர்
+Comment[tr]=Birileri takma adlarını değiştirdi
+Comment[uk]=Хтось змінив своє прізвисько
+Comment[xx]=xxSomeone changed their nickxx
+Comment[zh_CN]=有人改变了昵称
+Comment[zh_TW]=有人變更了暱稱
+default_presentation=0
+
+[dcc_incoming]
+Name=Incoming file
+Name[ar]=ملف وارِد
+Name[bg]=Пристигащ файл
+Name[br]=Restr resev
+Name[bs]=Dolazeća datoteka
+Name[ca]=Fitxer entrant
+Name[cs]=Příchozí soubor
+Name[da]=Indkommende fil
+Name[de]=Datei angeboten
+Name[el]=Εισερχόμενο αρχείο
+Name[es]=Fichero entrante
+Name[et]=Sisenev fail
+Name[fi]=Saapuva tiedosto
+Name[fr]=Fichier entrant
+Name[ga]=Comhad isteach
+Name[gl]=Ficheiro recebido
+Name[he]=קובץ נכנס
+Name[hi]=आनेवाली फाइल
+Name[hu]=Bejövő fájl
+Name[it]=File in arrivo
+Name[ja]=受信ファイル
+Name[ka]=შემომავალი ფაილი
+Name[lt]=Atsiunčiama byla
+Name[nl]=Bestand aangeboden
+Name[pa]=ਆ ਰਹੀ ਫਾਇਲ
+Name[pt]=Ficheiro recebido
+Name[pt_BR]=Arquivo de entrada
+Name[ru]=Входящий файл
+Name[sr]=Долазећи фајл
+Name[sr@Latn]=Dolazeći fajl
+Name[sv]=Inkommande fil
+Name[ta]=உள்ளீடு கோப்பு
+Name[tr]=Gelen dosya
+Name[uk]=Вхідний файл
+Name[xx]=xxIncoming filexx
+Name[zh_CN]=收到的文件
+Name[zh_TW]=送來檔案
+Comment=Someone wants to transmit a file to you over DCC
+Comment[ar]=أحد ما يريد إرسال ملف إليك عبر DCC
+Comment[bg]=Някой иска да ви изпрати файл чрез DCC
+Comment[bs]=Neko želi da vam pošalje datoteku koristeći DCC
+Comment[ca]=Algú desitja enviar-vos un fitxer sobre DCC
+Comment[cs]=Někdo vám chce přes DCC poslat soubor
+Comment[da]=Nogen ønsker at transmittere en fil til dig over DCC
+Comment[de]=Jemand bietet Ihnen eine Datei zum DCC-Download an
+Comment[el]=Κάποιος θέλει να σας στείλει ένα αρχείο μέσω DCC
+Comment[es]=Alguien quiere transmitirle un fichero a través de DCC
+Comment[et]=Keegi tahab sulle faili edastada üle DCC
+Comment[fi]=Joku haluaa lähettää DCC-tiedoston sinulle
+Comment[fr]=Quelqu'un souhaite vous transmettre un fichier via DCC
+Comment[gl]=Alguén quer transmitir-lle un ficheiro por DCC
+Comment[he]=משיהו רוצה לשלוח לך קובץ דרך DCC
+Comment[hi]=कोई एक फाइल आपके डीसीसी पर ट्रांसमिट करना चाहता है
+Comment[hu]=Valaki fájl szeretne küldeni Önnek DCC-n keresztül
+Comment[it]=Qualcuno vuole trasmetterti un file via DCC
+Comment[ja]=誰かが DCC であなたにファイルを転送したいそうです
+Comment[ka]=ვიღაცას სურს DCC-ს მეშვეობით გადმოგცეთ ფაილი
+Comment[lt]=Kažkas nori jums atsiųsti bylą per DCC
+Comment[nl]=Iemand wil via DCC een bestand naar u sturen
+Comment[pt]=Alguém quer transmitir um ficheiro através de DCC
+Comment[pt_BR]=Alguém deseja transmitir um arquivo para você via DCC
+Comment[ru]=Кто-то хочет передать вам файл
+Comment[sr]=Неко жели да вам пошаље фајл преко DCC-а
+Comment[sr@Latn]=Neko želi da vam pošalje fajl preko DCC-a
+Comment[sv]=Någon vill skicka en fil till dig via direktkommunikation
+Comment[ta]= ஒரு சிலர் உங்களுக்குக் கோப்பினை வழங்க DCCலிருந்து விரும்புகின்றனர்
+Comment[tr]=Birisi size DCC üzerinden dosya göndermek istiyor
+Comment[uk]=Хтось хоче надіслати вам файл через DCC
+Comment[xx]=xxSomeone wants to transmit a file to you over DCCxx
+Comment[zh_CN]=有人希望通过 DCC 向你传输文件
+Comment[zh_TW]=有人要透過 DCC 傳送檔案給您
+default_presentation=0
+
+[join]
+Name=Nick joined channel
+Name[ar]=الإسم المستعار إنضمّ إلى القناة
+Name[bg]=Псевдонимът се присъедини към канала
+Name[bs]=Nick se pridružio kanalu
+Name[ca]=El sobrenom ha accedit al canal
+Name[cs]=Přezdívka se objevila na kanálu
+Name[da]=Alias gik med i kanal
+Name[de]=Benutzer betritt Kanal
+Name[el]=Το ψευδώνυμο συνδέθηκε στο κανάλι
+Name[es]=Apodo se ha unido al canal
+Name[et]=Hüüdnimi ühines kanaliga
+Name[fi]=Nimimerkki liittyi kanavalle
+Name[fr]=Un pseudonyme a rejoint le canal
+Name[ga]=Chuaigh leasainm le cainéal
+Name[gl]=Alguén se uniu ao canal
+Name[he]=כינוי התחבר אל החדר
+Name[hi]=निक चैनल में शामिल हुआ
+Name[hu]=Valaki belépett a csatornára
+Name[it]=Nick entrato nel canale
+Name[ja]=ニックがチャンネルに入りました
+Name[ka]=მეტსახელი არხს შეუერთდა
+Name[lt]=Slapyvardis prisijungė kanale
+Name[nl]=Bijnaam neemt deel aan kanaal
+Name[pa]=Nick ਚੈਨਲ 'ਚ ਦਾਖਲ
+Name[pt]=Alcunha juntou-se ao canal
+Name[pt_BR]=Apelido entrou no canal
+Name[ru]=Пользователь вошёл на канал
+Name[sr]=Надимак се прикључио каналу
+Name[sr@Latn]=Nadimak se priključio kanalu
+Name[sv]=Någon med smeknamnet gick med i en kanal
+Name[ta]=இணைத்தத் தடப்பட்டை
+Name[tr]=Takma ad kanala girdi
+Name[uk]=Прізвисько приєдналось до каналу
+Name[xx]=xxNick joined channelxx
+Name[zh_CN]=昵称加入频道
+Name[zh_TW]=加入頻道
+Comment=New nick joined a channel
+Comment[ar]=إسم مستعار جديد إنضمّ إلى القناة
+Comment[bg]=Нов псевдоним се присъедини към канала
+Comment[bs]=Novi nick se pridružio kanalu
+Comment[ca]=Un nou sobrenom ha accedit a un canal
+Comment[cs]=Na kanálu se objevila nová přezdívka
+Comment[da]=Nyt alias gik med i en kanal
+Comment[de]=Jemand betritt einen Kanal
+Comment[el]=Νέο ψευδώνυμο συνδέθηκε σε ένα κανάλι
+Comment[es]=Un nuevo usuario se ha unido a un canal
+Comment[et]=Kanaliga ühines uus hüüdnimi
+Comment[fi]=Uusi nimimerkki liittyi kanavalle
+Comment[fr]=Un pseudonyme a rejoint le canal
+Comment[ga]=Chuaigh leasainm nua le cainéal
+Comment[gl]=Un alcume novo uniu-se ao canal
+Comment[he]=כינוי חדש התחבר לחדר
+Comment[hi]=नया निक एक चैनल में शामिल हुआ
+Comment[hu]=Új felhasználó lépett be az egyik csatornára
+Comment[it]=Un nuovo nick è entrato nel canale
+Comment[ja]=新しいニックがチャンネルに入りました
+Comment[ka]=ახალი მეტსახელი არხს შეუერთდა
+Comment[lt]=Naujas slapyvardis prisijungė kanale
+Comment[nl]=Nieuwe bijnaam neemt deel aan een kanaal
+Comment[pt]=Um novo utilizador juntou-se ao canal
+Comment[pt_BR]=Um novo apelido entrou em um canal
+Comment[ru]=Новые люди на канале
+Comment[sr]=Нови надимак се прикључио каналу
+Comment[sr@Latn]=Novi nadimak se priključio kanalu
+Comment[sv]=Någon med ett nytt smeknamn gick med i en kanal
+Comment[ta]=புதிய வடுத் தடத்தில் சேர்ந்தது
+Comment[tr]=Kanala yeni bir kişi girdi
+Comment[uk]=Нове прізвисько приєдналось до каналу
+Comment[xx]=xxNew nick joined a channelxx
+Comment[zh_CN]=新昵称加入了频道
+Comment[zh_TW]=有人加入了頻道
+default_presentation=0
+
+[part]
+Name=Nick left channel
+Name[ar]=الإسم المستعار غادر القناة
+Name[bg]=Псевдонимът напусна канала
+Name[bs]=Nick napustio kanal
+Name[ca]=El sobrenom ha abandonat el canal
+Name[cs]=Přezdívka opustila kanál
+Name[da]=Alias forlod kanal
+Name[de]=Benutzer verlässt Kanal
+Name[el]=Το ψευδώνυμο έφυγε από το κανάλι
+Name[es]=Apodo ha dejado el canal
+Name[et]=Hüüdnimi lahkus kanalilt
+Name[fi]=Nimimerkki poistui kanavalta
+Name[fr]=Un pseudonyme a quitté le canal
+Name[ga]=D'fhág leasainm cainéal
+Name[gl]=Alguén deixou o canal
+Name[he]=כינוי עזב את החדר
+Name[hi]=निक चैनल छोड़ा
+Name[hu]=Valaki kilépett a csatornáról
+Name[it]=Nick uscito dal canale
+Name[ja]=ニック退出
+Name[ka]=მეტსახელმა დატოვა არხი
+Name[lt]=Slapyvardis atsijungė nuo kanalo
+Name[nl]=Bijnaam verlaat kanaal
+Name[pa]=Nick ਨੇ ਚੈਨਲ ਛੱਡਿਆ
+Name[pt]=Alcunha saiu do canal
+Name[pt_BR]=Apelido deixou o canal
+Name[ru]=Пользователь вышел из канала
+Name[sr]=Надимак је напустио канал
+Name[sr@Latn]=Nadimak je napustio kanal
+Name[sv]=Någon med smeknamnet lämnade en kanal
+Name[ta]=இடது தடப் பட்டை
+Name[tr]=Takma ad kanaldan ayrıldı
+Name[uk]=Прізвисько покинуло канал
+Name[xx]=xxNick left channelxx
+Name[zh_CN]=昵称离开频道
+Name[zh_TW]=離開頻道
+Comment=A nick left a channel
+Comment[ar]=إسم مستعار غادر القناة
+Comment[bg]=Псевдонимът напусна канала
+Comment[bs]=Nick je napustio kanal
+Comment[ca]=Un sobrenom ha deixat un canal
+Comment[cs]=Přezdívka opustila kanál
+Comment[da]=Et alias forlod en kanal
+Comment[de]=Jemand verlässt einen Kanal
+Comment[el]=Ένα ψευδώνυμο έφυγε από ένα κανάλι
+Comment[es]=Un usuario ha abandonado un canal
+Comment[et]=Hüüdnimi lahkus kanalilt
+Comment[fi]=Nimimerkki poistui kanavalta
+Comment[fr]=Un pseudonyme a quitté le canal
+Comment[ga]=D'fhág leasainm cainéal
+Comment[gl]=Alguén abandonou o canal
+Comment[he]=כינוי עזב את החדר
+Comment[hu]=Valaki kilépett egy csatornáról
+Comment[it]=Un nick è uscito dal canale
+Comment[ja]=ニックがチャンネルから退出しました
+Comment[ka]=მეტსახელმა დატოვა არხი
+Comment[lt]=Slapyvardis atsijungė nuo kanalo
+Comment[nl]=Een bijnaam heeft een kanaal verlaten
+Comment[pa]=ਇੱਕ ਨਾਂ ਨੇ ਚੈਨਲ ਛੱਡਿਆ
+Comment[pt]=Um utilizador abandonou o canal
+Comment[pt_BR]=Alguém deixou um canal
+Comment[ru]=Пользователь покинул канал
+Comment[sr]=Надимак је напустио канал
+Comment[sr@Latn]=Nadimak je napustio kanal
+Comment[sv]=Någon med ett smeknamn lämnade en kanal
+Comment[ta]=தடத்தை விட்டு வடு நீங்கியது
+Comment[tr]=Bir takma ad kanaldan ayrıldı
+Comment[uk]=Прізвисько покинуло канал
+Comment[xx]=xxA nick left a channelxx
+Comment[zh_CN]=一个昵称离开了频道
+Comment[zh_TW]=有人離開了頻道
+default_presentation=0
+
+[mode]
+Name=Mode change
+Name[ar]=تغيير النمط
+Name[bg]=Смяна на режим
+Name[bs]=Promjena režima
+Name[ca]=Mode canviat
+Name[cs]=Změna režimu
+Name[da]=Tilstand ændret
+Name[de]=Moduswechsel
+Name[el]=Αλλαγή κατάστασης
+Name[es]=Cambio de modo
+Name[et]=Režiimi muutus
+Name[fi]=Tilamuutos
+Name[fr]=Changement de mode
+Name[ga]=Athrú móid
+Name[gl]=Cámbio de modo
+Name[he]=שינוי מצב
+Name[hi]=मोड बदला
+Name[hu]=Módváltás
+Name[it]=Cambio di modalità
+Name[ja]=モード変更
+Name[ka]=რეჟიმის შეცვლა
+Name[lt]=Pasikeitė būsena
+Name[nl]=Moduswisseling
+Name[pa]=ਢੰਗ ਤਬਦੀਲ
+Name[pt]=Mudança de modo
+Name[pt_BR]=Mudança de modo
+Name[ru]=Смена режима
+Name[sr]=Режим је измењен
+Name[sr@Latn]=Režim je izmenjen
+Name[sv]=Tillståndsändring
+Name[ta]=பாங்கை மாற்று
+Name[tr]=Kip değiştir
+Name[uk]=Зміна режиму
+Name[xx]=xxMode changexx
+Name[zh_CN]=模式改变
+Name[zh_TW]=模式變更
+Comment=A user or channel mode was changed
+Comment[ar]=تغيّر مستخدِم أو قناة ما
+Comment[bg]=Променен е потребителски режим или канал
+Comment[bs]=Mod korisnika ili kanala je promijenjen
+Comment[ca]=A mode d'usuari o de canal ha estat canviat
+Comment[cs]=Uživatel nebo kanál byl změněn
+Comment[da]=En bruger- eller kanaltilstand blev ændret
+Comment[de]=Ein Benutzer- oder Kanal-Modus wurde geändert
+Comment[el]=Η κατάσταση ενός χρήστης ή καναλιού άλλαξε
+Comment[es]= Un modo de usuario o de canal ha sido modificado
+Comment[et]=Kasutaja või kanalirežiim muudeti
+Comment[fi]=Käyttäjän tai kanavan tila vaihtui
+Comment[fr]=Un utilisateur ou un mode de canal a été changé
+Comment[ga]=Athraíodh mód úsáideora nó mód cainéil
+Comment[gl]=Foi modificado un modo de usuário ou canal
+Comment[he]=משתמש או חדר שינה את המצב שלו
+Comment[hi]=एक उपयोगकर्ता या चैनल मोड बदला
+Comment[hu]=Egy felhasználó vagy egy csatornamód megváltozott
+Comment[it]=Una modalità utente o canale è stata cambiata
+Comment[ja]=ユーザまたはチャンネルモードが変更されました
+Comment[ka]=მომხმარებლის ან არხის რეჟიმი შეიცვალა
+Comment[lt]=Pasikeitė naudotojo arba kanalo būsena
+Comment[nl]=Een gebruiker- of kanaalmodus is gewijzigd
+Comment[pt]=O modo de um utilizador ou canal foi alterado
+Comment[pt_BR]=Um usuário ou modo de canal foi modificado
+Comment[ru]=Режим пользователя или канала изменены
+Comment[sr]=Корисник или режим канала је промењен
+Comment[sr@Latn]=Korisnik ili režim kanala je promenjen
+Comment[sv]=Tillståndet för en användare eller kanal ändrades
+Comment[ta]=பயனர் அல்லது தடங்கள் மாறியது
+Comment[tr]=Bir kullanıcı veya kanal kipi değiştirildi
+Comment[uk]=Змінено користувацький або канальний режим
+Comment[xx]=xxA user or channel mode was changedxx
+Comment[zh_CN]=一个用户或频道模式改变了
+Comment[zh_TW]=某位使用者或某個頻道的模式已被變更
+default_presentation=0
+
+[notify]
+Name=Notify
+Name[ar]=بلِغ
+Name[bg]=Известяване
+Name[bs]=Obavijesti
+Name[ca]=Notificació
+Name[da]=Bekendtgør
+Name[de]=Benachrichtigung
+Name[el]=Ειδοποίηση
+Name[es]=Notificar
+Name[et]=Märguanne
+Name[fi]=Ilmoitus
+Name[fr]=Notification
+Name[ga]=Cuir Fógra Chugam
+Name[gl]=Notificar
+Name[he]=הודעה
+Name[hi]=नोटिफाई
+Name[hu]=Értesítés
+Name[it]=Notifica
+Name[ja]=通知
+Name[ka]=შეტყობინება
+Name[lt]=Perspėti
+Name[nl]=Notificatie
+Name[pa]=ਸੂਚਨਾ
+Name[pt]=Notificação
+Name[pt_BR]=Notificar
+Name[ru]=Уведомление
+Name[sr]=Обавести
+Name[sr@Latn]=Obavesti
+Name[sv]=Underrättelse
+Name[ta]=குறிப்பு
+Name[tr]=Bildirim
+Name[uk]=Сповіщати
+Name[xx]=xxNotifyxx
+Name[zh_CN]=通告
+Name[zh_TW]=通知
+Comment=A user on your watched nicks list has come online
+Comment[ar]=مستخدِم مِن أفراد لائحتك لِلأسماء المستعارة سجل دخولهُ
+Comment[bg]=Потребител от списъка ви с наблюдавани псевдоними влезе в канала
+Comment[ca]=S'ha connectat un usuari de la vostra llista de sobrenoms vigilats
+Comment[cs]=Uživatel z vašeho seznamu je online
+Comment[da]=En bruger på din liste af aliasser gik online
+Comment[de]=Ein Benutzer aus Ihrer "Beobachten"-Liste ist online
+Comment[el]=Ένας χρήστης από τη λίστα ψευδώνυμων παρακολούθησης συνδέθηκε στο δίκτυο
+Comment[es]=Un usuario de su lista de apodos vigilados se ha conectado
+Comment[et]=Jälgitavate hüüdnimede nimekirjas olev kasutaja tuli võrku
+Comment[fi]=Seurattu nimimerkki tuli paikalle
+Comment[fr]=Un utilisateur de votre liste de pseudos surveillés est maintenant en ligne
+Comment[gl]=Un usuário da sua lista de seguimento conectou-se
+Comment[he]=משתמש ברשימה תצוגה שלך נכנס לרשת
+Comment[hi]=आपकी मित्र सूची का एक उपयोक्ता ऑनलाइन हुआ
+Comment[hu]=Az értesítési listán szereplő egyik felhasználó online módba váltott
+Comment[it]=Si è collegato un utente nella tua lista di nick sorvegliati
+Comment[ja]=ニック監視リストのユーザがオンラインになりました
+Comment[ka]=მომხმარებელი თქვენი სათვალყურო სიიდან ხაზზე გამოვიდა
+Comment[lt]=Prisijungė naudotojas iš stebimų slapyvardžių sąrašo
+Comment[nl]=Een gebruiker op uw gevolgdebijnamenlijst is online
+Comment[pt]=Um utilizador da sua lista de notificações ligou-se
+Comment[pt_BR]=Um usuário de sua lista de apelidos ficou online
+Comment[ru]=Наблюдаемый пользователь в сети
+Comment[sr]=Корисник са ваше листе праћених надимака је дошао на везу
+Comment[sr@Latn]=Korisnik sa vaše liste praćenih nadimaka je došao na vezu
+Comment[sv]=En användare i din lista med bevakade smeknamn har kopplat upp
+Comment[ta]= உங்கள் வடுகளின் பட்டியலைப் பயனர் கண்காணித்து நிகழ்நிலையில் வருகின்றனர்
+Comment[tr]=İzlenen takma adlar listenizdeki bir kullanıcı bağlandı
+Comment[uk]=Вийшов у мережу користувач із вашого списку прізвиськ
+Comment[xx]=xxA user on your watched nicks list has come onlinexx
+Comment[zh_CN]=一名在您的监视昵称列表上的用户已经上线
+Comment[zh_TW]=您監看的暱稱清單中有某人上線了
+default_presentation=0
+
+[query]
+Name=Query
+Name[ar]=أطلب
+Name[bg]=Запитване
+Name[br]=Goulenn
+Name[ca]=Conversa
+Name[cs]=Dotaz
+Name[cy]=Ymholiad
+Name[el]=Ερώτηση
+Name[es]=Consulta
+Name[et]=Päring
+Name[fi]=Kysely
+Name[fr]=Requête
+Name[ga]=Iarratas
+Name[gl]=Procura
+Name[he]=שאילתה
+Name[hi]=क्वैरी
+Name[it]=Conversazione privata
+Name[ja]=クエリ
+Name[ka]=გამოკითხვა
+Name[lt]=Pokalbis
+Name[nl]=Aanvraag
+Name[pa]=ਕਿਊਰੀ
+Name[pt]=Procura
+Name[pt_BR]=Busca
+Name[ru]=Диалог
+Name[sr]=Упит
+Name[sr@Latn]=Upit
+Name[sv]=Fråga
+Name[ta]=கேள்வி
+Name[tr]=Sorgu
+Name[uk]=Запит
+Name[xx]=xxQueryxx
+Name[zh_CN]=对话
+Name[zh_TW]=查詢
+Comment=Someone started a conversation (query) with you
+Comment[ar]=أحدٌ ما إبتدء مكالمة ( طلب ) معك
+Comment[bg]=Някой започна личен разговор с вас
+Comment[ca]=Algú ha iniciat una conversa (query) amb vostè
+Comment[cs]=Někdo s vámi zahájil rozhovor (dotaz)
+Comment[da]=Nogen startede en konversation (query) med dig
+Comment[de]=Jemand hat ein Gespräch (query) mit Ihnen begonnen
+Comment[el]=Κάποιος ξεκίνησε μια συζήτηση (ερώτηση) μαζί σας
+Comment[es]=Alguien empezó una conversación (consulta) con usted
+Comment[et]=Keegi alustas sinuga vestlust (esitas päringu)
+Comment[fi]=Joku aloitti keskustelun (kyselyn) kanssasi
+Comment[fr]=Quelqu'un a commencé une conversation (requête) avec vous
+Comment[ga]=Thosaigh duine éigin comhrá (iarratas) leat
+Comment[gl]=Alguén comezou una conversa (está a procurá-la) privada
+Comment[he]=מישהו יזם שיחה אתך (query)
+Comment[hi]=किसी ने आपके साथ वार्तालाप (क्वैरी) प्रारंभ किया
+Comment[it]=Qualcuno ha avviato una conversazione privata (query) con te
+Comment[ja]=誰かがあなたと会話 (クエリ) を開始しました
+Comment[ka]=ვიღაცამ თქვენთან საუბარი (გამოკითხვა) დაიწყო
+Comment[lt]=Kažkas pradėjo su jumis pokalbį
+Comment[nl]=Iemand begon een conversatie (aanvraag) met u
+Comment[pt]=Alguém iniciou uma conversa (procura) consigo
+Comment[pt_BR]=Alguém começou a conversa (query) com você
+Comment[ru]=Кто-то начал диалог с вами
+Comment[sr]=Неко је започео разговор (упит) са вама
+Comment[sr@Latn]=Neko je započeo razgovor (upit) sa vama
+Comment[sv]=Någon startade en konversation med dig (ställde en fråga till dig)
+Comment[ta]=ஒருவர் உங்களுடன் உரையாடலை துவக்கியிருக்கிறார் (கேள்வி)
+Comment[tr]=Birisi sizinle sohbet başlattı
+Comment[uk]=Хтось почав з вами розмову (запит)
+Comment[xx]=xxSomeone started a conversation (query) with youxx
+Comment[zh_CN]=有人向您打开了一个对话。
+Comment[zh_TW]=有人開啟了與您的私密對話
+default_presentation=0
+
+[kick]
+Name=Kick
+Name[ar]=ركل
+Name[bg]=Изритване
+Name[ca]=Expulsió
+Name[cs]=Vykopnutí
+Name[cy]=Lluchio
+Name[el]=Απομάκρυνση
+Name[es]=Expulsar
+Name[et]=Väljaviskamine
+Name[fi]=Ulosheitto
+Name[fr]=Bannir
+Name[ga]=Ciceáil amach
+Name[gl]=Expulsón
+Name[he]=בעיטה
+Name[it]=Espulsione
+Name[ja]=追い出し (Kick)
+Name[lt]=Išspirtas
+Name[pa]=ਠੁੱਢਾ
+Name[pt_BR]=Retirar
+Name[sr]=Избачени
+Name[sr@Latn]=Izbačeni
+Name[tr]=Tekmele
+Name[uk]=Виштовхнуто
+Name[xx]=xxKickxx
+Name[zh_CN]=踢出
+Name[zh_TW]=踢出
+Comment=Someone kicked you out of a channel
+Comment[ar]=أحدٌ ما طلب ركلك خارجأً عن القناة
+Comment[bg]=Някой ви изрита от канала
+Comment[ca]=Algú us ha expulsat d'un canal
+Comment[cs]=Někdo vás vykopnul z kanálu
+Comment[da]=Nogen sparkede dig ud af kanalen
+Comment[de]=Jemand hat Sie aus einem Kanal geworfen
+Comment[el]=Κάποιος σας απομάκρυνε από ένα κανάλι
+Comment[es]=Alguien le ha expulsado de un canal
+Comment[et]=Keegi viskas su kanalilt välja
+Comment[fi]=Joku heitti sinut kanavalta ulos
+Comment[fr]=Quelqu'un vous a banni du canal
+Comment[ga]=Chiceáil duine éigin thú amach as cainéal
+Comment[gl]=Foi expulsado do canal por alguén
+Comment[he]=מישהו בעט אותך מערוץ
+Comment[it]=Qualcuno ti ha espulso da un canale
+Comment[ja]=誰かがあなたをチャンネルから追い出しました
+Comment[ka]=ვიღაცამ არხიდან გაგაპანღურათ
+Comment[lt]=Kažkas jus išspyrė iš kanalo
+Comment[nl]=Iemand heeft u van het kanaal gekicked
+Comment[pt]=Alguém o expulsou de um canal
+Comment[pt_BR]=Alguém retirou você de um canal
+Comment[ru]=Кто-то выпнул вас с канала
+Comment[sr]=Неко вас је избацио са канала
+Comment[sr@Latn]=Neko vas je izbacio sa kanala
+Comment[sv]=Någon sparkade ut dig från en kanal
+Comment[ta]=யாரோ உங்களை வழிமுறையில் இருந்து வெளித்தள்ளிவிட்டார்கள்
+Comment[tr]=Birisi sizi odadan tekmeledi
+Comment[uk]=Хтось виштовхнув вас із каналу
+Comment[xx]=xxSomeone kicked you out of a channelxx
+Comment[zh_CN]=有人把您踢出了频道
+Comment[zh_TW]=有人將您踢出頻道
+default_presentation=0
+
+[connectionFailure]
+Name=Connection failure
+Name[ar]=عطل في الإتصال
+Name[bg]=Неуспешно свързване
+Name[br]=Sac'het eo ar gevreadenn
+Name[ca]=Error de connexió
+Name[cs]=Selhání připojení
+Name[da]=Forbindelse mislykket
+Name[de]=Verbindungsfehler
+Name[el]=Αποτυχία σύνδεσης
+Name[es]=Fallo de la conexión
+Name[et]=Ühenduse viga
+Name[fi]=Yhteysvirhe
+Name[ga]=Theip ar nascadh
+Name[gl]=Erro de conexón
+Name[he]=שגיאת התחברות
+Name[it]=Errore di connessione
+Name[ja]=接続失敗
+Name[ka]=კავშირი ვერ შედგა
+Name[lt]=Nepavyko prisijungti
+Name[nl]=Verbindingsfout
+Name[pa]=ਕੁਨੈਕਸ਼ਨ ਫੇਲ੍ਹ
+Name[pt]=Erro na ligação
+Name[pt_BR]=Erro na conexão
+Name[sr]=Грешка везе
+Name[sr@Latn]=Greška veze
+Name[sv]=Uppkopplingsfel
+Name[tr]=Bağlantı hatası
+Name[uk]=Помилка з'єднання
+Name[xx]=xxConnection failurexx
+Name[zh_CN]=连接失败
+Name[zh_TW]=連線失敗
+Comment=Failed to connect to server
+Comment[ar]=فشلتُ في الإتصال بِلخادِم
+Comment[bg]=Неуспех при свързване към сървър
+Comment[ca]=No s'ha pogut connectar al servidor
+Comment[cs]=Chyba při připojení k serveru
+Comment[da]=Mislykkedes at forbinde til server
+Comment[de]=Fehler beim Verbinden zum Server
+Comment[el]=Η σύνδεση με τον εξυπηρετητή απέτυχε
+Comment[es]=No ha sido posible conectarse al servidor
+Comment[et]=Ühendumine serveriga ebaõnnestus
+Comment[fi]=Palvelimeen yhdistäminen epäonnistui
+Comment[ga]=Theip ar nascadh le freastalaí
+Comment[gl]=Non foi posíbel conectar co servidor
+Comment[he]=החיבור לשרת נכשל
+Comment[it]=Connessione al server non riuscita
+Comment[ja]=サーバへの接続に失敗しました
+Comment[ka]=სერვერთან დაკავშირება ვერ განხორციელდა
+Comment[lt]=Nepavyko prisijungti prie serverio
+Comment[nl]=Verbinden met de server is mislukt
+Comment[pt]=Não foi possível ligar ao servidor
+Comment[pt_BR]=Falha ao conectar-se ao servidor
+Comment[sr]=Неуспело повезивање са сервером
+Comment[sr@Latn]=Neuspelo povezivanje sa serverom
+Comment[sv]=Misslyckades koppla upp till server
+Comment[tr]=Sunucuya bağlanılamadı
+Comment[uk]=Не вдалося з'єднатись з сервером
+Comment[xx]=xxFailed to connect to serverxx
+Comment[zh_CN]=连接服务器失败
+Comment[zh_TW]=連線到伺服器失敗
+default_presentation=64
+
+[channelJoin]
+Name=You joined a channel
+Name[ar]=إنضممت إلى قناة
+Name[bg]=Вие се присъединихте към канал
+Name[br]=Emellet out bet ur c'hanol
+Name[ca]=Heu entrat a un canal
+Name[cs]=Připojili jste se ke kanálu
+Name[da]=Du gik med i en kanal
+Name[de]=Sie haben einen Kanel betreten
+Name[el]=Συνδεθήκατε σε ένα κανάλι
+Name[es]=Se ha unido a un canal
+Name[et]=Sa ühinesid kanaliga
+Name[fi]=Liityit kanavalle
+Name[ga]=Chuaigh tú le cainéal
+Name[gl]=Uniu-se a un canal
+Name[he]=אתה הצטרפת אל חדר
+Name[it]=Sei entrato in un canale
+Name[ja]=チャンネルに入りました
+Name[ka]=თქვენ შეუერთდით არხს
+Name[lt]=Prisijungėte prie kanalo
+Name[nl]=U nam deel aan een kanaal
+Name[pa]=ਤੁਸੀਂ ਇੱਕ ਚੈਨਲ 'ਚ ਦਾਖਲ ਹੋ ਗਏ
+Name[pt]=Você juntou-se a um canal
+Name[pt_BR]=Você entrou no canal
+Name[sr]=Прикључили сте се каналу
+Name[sr@Latn]=Priključili ste se kanalu
+Name[sv]=Du gick med i en kanal
+Name[tr]=Bir odaya girdiniz
+Name[uk]=Ви приєднались до каналу
+Name[xx]=xxYou joined a channelxx
+Name[zh_CN]=您加入了一个频道
+Name[zh_TW]=您加入了一個頻道
+Comment=You joined a channel
+Comment[ar]=إنضممت إلى قناة
+Comment[bg]=Вие се присъединихте към канал
+Comment[br]=Emellet out bet ur c'hanol
+Comment[ca]=Heu entrat a un canal
+Comment[cs]=Připojili jste se ke kanálu
+Comment[da]=Du gik med i en kanal
+Comment[de]=Sie haben einen Kanal betreten
+Comment[el]=Συνδεθήκατε σε ένα κανάλι
+Comment[es]=Se ha unido a un canal
+Comment[et]=Sa ühinesid kanaliga
+Comment[fi]=Liityit kanavalle
+Comment[ga]=Chuaigh tú le cainéal
+Comment[gl]=Uniu-se a un canal
+Comment[he]=אתה הצטרפת אל חדר
+Comment[it]=Sei entrato in un canale
+Comment[ja]=チャンネルに入りました
+Comment[ka]=თქვენ შეუერთდით არხს
+Comment[lt]=Prisijungėte prie kanalo
+Comment[nl]=U nam deel aan een kanaal
+Comment[pa]=ਤੁਸੀਂ ਇੱਕ ਚੈਨਲ 'ਚ ਦਾਖਲ ਹੋ ਗਏ
+Comment[pt]=Você juntou-se a um canal
+Comment[pt_BR]=Você entrou em um canal
+Comment[sr]=Прикључили сте се каналу
+Comment[sr@Latn]=Priključili ste se kanalu
+Comment[sv]=Du gick med i en kanal
+Comment[tr]=Bir odaya girdiniz
+Comment[uk]=Ви приєднались до каналу
+Comment[xx]=xxYou joined a channelxx
+Comment[zh_CN]=您加入了一个频道
+Comment[zh_TW]=您加入了一個頻道
+default_presentation=64
+
+[dccChat]
+Name=DCC Chat
+Name[ar]=مكالمة DCC
+Name[bg]=DCC разговор
+Name[br]=Flapañ DCC
+Name[ca]=Xat DCC
+Name[cs]=DCC pokec
+Name[cy]=Sgwrs DCC
+Name[da]=Dcc-chat
+Name[de]=DCC-Chat
+Name[el]=Συζήτηση DCC
+Name[es]=Charla DCC
+Name[et]=DCC vestlus
+Name[fi]=DCC-keskustelu
+Name[ga]=Comhrá DCC
+Name[gl]=Conversa DCC
+Name[he]=שיחת DCC
+Name[it]=Chat DCC
+Name[ja]=DCC チャット
+Name[ka]=Dcc საუბარი
+Name[lt]=DCC pokalbis
+Name[ms]=Chat DCC
+Name[nl]=DCC-gesprek
+Name[pa]=DCC ਗੱਲਬਾਤ
+Name[pt]=Conversa DCC
+Name[pt_BR]= Conversa privada (DCC)
+Name[sr]=DCC ћаскање
+Name[sr@Latn]=DCC ćaskanje
+Name[sv]=DCC-chatt
+Name[tr]=DCC Sohbeti
+Name[uk]=Балачка DCC
+Name[xx]=xxDCC Chatxx
+Name[zh_CN]=DCC 聊天
+Name[zh_TW]=DCC 聊天
+Comment=Someone started a DCC chat with you
+Comment[ar]=أحدٌ ما إبتدء مكالمة DCC معك
+Comment[bg]=Някой е започнал разговор с вас в DCC
+Comment[ca]=Algú ha iniciat un xat DCC amb vostè
+Comment[cs]=Někdo si s vámi chce pokecat přes DCC
+Comment[da]=Nogen startede en dcc-chat med dig
+Comment[de]=Jemand hat einen DCC-Chat mit Ihnen begonnen
+Comment[el]=Κάποιος ξεκίνησε μια συζήτηση DCC μαζί σας
+Comment[es]=Alguien empezó una charla de tipo DCC con usted
+Comment[et]=Keegi alustas sinuga DCC vestlust
+Comment[fi]=Joku aloitti DCC-keskustelun kanssasi
+Comment[ga]=Thosaigh duine éigin comhrá DCC leat
+Comment[gl]=Alguén comezou unha conversa DCC contigo
+Comment[he]=מישהו התחיל שיחת DCC אתך
+Comment[it]=Qualcuno ha avviato una conversazione DCC con te
+Comment[ja]=誰かがあなたと DCC チャットを開始しました
+Comment[ka]=ვიღაცამ თქვენთან dcc საუბარი დაიწყო
+Comment[lt]=Kažkas su jumis pradėjo DCC pokalbį
+Comment[nl]=Iemand begon een DCC-gesprek met u
+Comment[pt]=Alguém iniciou uma conversa DCC consigo
+Comment[pt_BR]=Alguém começou uma conversa privada (DCC) com você
+Comment[sr]=Неко је започео DCC ћаскање са вама
+Comment[sr@Latn]=Neko je započeo DCC ćaskanje sa vama
+Comment[sv]=Någon startade en DCC-chatt med dig
+Comment[tr]=Birisi sizinle DCC sohbeti başlattı
+Comment[uk]=Хтось почав з вами DCC-балачку
+Comment[xx]=xxSomeone started a DCC chat with youxx
+Comment[zh_CN]=有人向您打开了一个 DCC 聊天对话
+Comment[zh_TW]=有人開啟了與您的 DCC 聊天
+default_presentation=0
diff --git a/konversation/src/fontappearance_preferences.ui b/konversation/src/fontappearance_preferences.ui
new file mode 100644
index 0000000..a59c3f7
--- /dev/null
+++ b/konversation/src/fontappearance_preferences.ui
@@ -0,0 +1,160 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>FontAppearance_Config</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</comment>
+<author>Copyright (C) 2005 Peter Simonsson</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>FontAppearance_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>454</width>
+ <height>213</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_FixedMOTD</cstring>
+ </property>
+ <property name="text">
+ <string>Enable fi&amp;xed font for MOTD messages</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Show MOTD (Message Of The Day) message in fixed font</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_UseBoldNicks</cstring>
+ </property>
+ <property name="text">
+ <string>Show sender nicknames &amp;bold in the chat view</string>
+ </property>
+ </widget>
+ <spacer row="5" column="1">
+ <property name="name">
+ <cstring>spacer89</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>161</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_CustomTextFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Chat text:</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_TextFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_ListFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_TabFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_CustomListFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Nickname list:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_CustomTabFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Tab bar:</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_CustomTextFont</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TextFont</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_CustomListFont</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_ListFont</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_CustomTabFont</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TabFont</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_CustomTextFont</tabstop>
+ <tabstop>kcfg_TextFont</tabstop>
+ <tabstop>kcfg_CustomListFont</tabstop>
+ <tabstop>kcfg_ListFont</tabstop>
+ <tabstop>kcfg_CustomTabFont</tabstop>
+ <tabstop>kcfg_TabFont</tabstop>
+ <tabstop>kcfg_FixedMOTD</tabstop>
+ <tabstop>kcfg_UseBoldNicks</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/generalbehavior_preferences.ui b/konversation/src/generalbehavior_preferences.ui
new file mode 100644
index 0000000..2ef5829
--- /dev/null
+++ b/konversation/src/generalbehavior_preferences.ui
@@ -0,0 +1,360 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>GeneralBehavior_Config</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</comment>
+<author>Copyright (C) 2005 Peter Simonsson</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GeneralBehavior_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>448</width>
+ <height>477</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>kcfg_ShowTrayIcon</cstring>
+ </property>
+ <property name="title">
+ <string>Enable System Tray</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_TrayNotify</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use system tray for new message notification</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer51</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_TrayNotifyOnlyOwnNick</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Only &amp;notify when a highlight is triggered or your current nick is used</string>
+ </property>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer51_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_TrayNotifyBlink</cstring>
+ </property>
+ <property name="text">
+ <string>Blin&amp;k the icon</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_HideToTrayOnStartup</cstring>
+ </property>
+ <property name="text">
+ <string>Hide window on startup</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>middleLabel</cstring>
+ </property>
+ <property name="title">
+ <string>Nickname Completion</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>modeLbl</cstring>
+ </property>
+ <property name="text">
+ <string>Completion &amp;mode:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_NickCompletionMode</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1" rowspan="1" colspan="3">
+ <item>
+ <property name="text">
+ <string>Cycle NickList</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Shell-Like</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Shell-Like with Completion Box</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_NickCompletionMode</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_NickCompleteSuffixStart</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>startOfLineLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Suffi&amp;x at start of line:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_NickCompleteSuffixStart</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>kcfg_NickCompleteSuffixMiddle</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Elsewhere:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_NickCompleteSuffixMiddle</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_NickCompletionCaseSensitive</cstring>
+ </property>
+ <property name="text">
+ <string>Case sensitive</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>miscGBox</cstring>
+ </property>
+ <property name="title">
+ <string>Miscellaneous</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_DisableNotifyWhileAway</cstring>
+ </property>
+ <property name="text">
+ <string>Disable notifications while &amp;away</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0">
+ <property name="name">
+ <cstring>kcfg_CustomVersionReplyEnabled</cstring>
+ </property>
+ <property name="text">
+ <string>Use custom &amp;version reply:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>kcfg_CustomVersionReply</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Here you can set a custom reply for &lt;b&gt;CTCP &lt;i&gt;VERSION&lt;/i&gt;&lt;/b&gt; requests.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>kcfg_CommandChar</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxLength">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_UseMultiRowInputBox</cstring>
+ </property>
+ <property name="text">
+ <string>Input box expands with text</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>kcfg_WebBrowserCmd</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>commandCharLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Comman&amp;d char:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_CommandChar</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_UseCustomBrowser</cstring>
+ </property>
+ <property name="text">
+ <string>Use custom web &amp;browser:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer52</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_TrayNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TrayNotifyOnlyOwnNick</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseCustomBrowser</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_WebBrowserCmd</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_CustomVersionReplyEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_CustomVersionReply</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_ShowTrayIcon</tabstop>
+ <tabstop>kcfg_TrayNotify</tabstop>
+ <tabstop>kcfg_TrayNotifyOnlyOwnNick</tabstop>
+ <tabstop>kcfg_NickCompletionMode</tabstop>
+ <tabstop>kcfg_NickCompleteSuffixStart</tabstop>
+ <tabstop>kcfg_NickCompleteSuffixMiddle</tabstop>
+ <tabstop>kcfg_NickCompletionCaseSensitive</tabstop>
+ <tabstop>kcfg_DisableNotifyWhileAway</tabstop>
+ <tabstop>kcfg_UseCustomBrowser</tabstop>
+ <tabstop>kcfg_WebBrowserCmd</tabstop>
+ <tabstop>kcfg_CommandChar</tabstop>
+ <tabstop>kcfg_CustomVersionReplyEnabled</tabstop>
+ <tabstop>kcfg_CustomVersionReply</tabstop>
+ <tabstop>kcfg_UseMultiRowInputBox</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/guess_ja.cpp b/konversation/src/guess_ja.cpp
new file mode 100644
index 0000000..4e35217
--- /dev/null
+++ b/konversation/src/guess_ja.cpp
@@ -0,0 +1,377 @@
+/*
+ * This file is part of the KDE libraries
+ *
+ * Copyright (c) 2000-2003 Shiro Kawai <shirok@users.sourceforge.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the authors nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/*
+ * original code is here.
+ * http://cvs.sourceforge.net/viewcvs.py/gauche/Gauche/ext/charconv/guess.c?view=markup
+ */
+
+/*
+ * Maybe we should use QTextCodec::heuristicContentMatch()
+ * But it fails detection. It's not useful.
+ */
+#include "guess_ja.h"
+#include "decoder.h"
+
+/* DFA tables */
+const dfa_table guess_eucj_st[] = {
+ { /* state init */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, -1,
+ },
+ { /* state jis0201_kana */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ },
+ { /* state jis0213_1 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -1,
+ },
+ { /* state jis0213_2 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, -1,
+ },
+};
+
+guess_arc guess_eucj_ar[7] = {
+ { 0, 1.0 }, /* init -> init */
+ { 1, 0.8 }, /* init -> jis0201_kana */
+ { 3, 0.95 }, /* init -> jis0213_2 */
+ { 2, 1.0 }, /* init -> jis0213_1 */
+ { 0, 1.0 }, /* jis0201_kana -> init */
+ { 0, 1.0 }, /* jis0213_1 -> init */
+ { 0, 1.0 }, /* jis0213_2 -> init */
+};
+
+const dfa_table guess_sjis_st[] = {
+ { /* state init */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ -1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
+ },
+ { /* state jis0213 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -1,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -1, -1, -1,
+ },
+};
+
+guess_arc guess_sjis_ar[6] = {
+ { 0, 1.0 }, /* init -> init */
+ { 1, 1.0 }, /* init -> jis0213 */
+ { 0, 0.8 }, /* init -> init */
+ { 1, 0.95 }, /* init -> jis0213 */
+ { 0, 0.8 }, /* init -> init */
+ { 0, 1.0 }, /* jis0213 -> init */
+};
+
+const dfa_table guess_utf8_st[] = {
+ { /* state init */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, -1, -1,
+ },
+ { /* state 1byte_more */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ },
+ { /* state 2byte_more */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ },
+ { /* state 3byte_more */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ },
+ { /* state 4byte_more */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ },
+ { /* state 5byte_more */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ },
+};
+
+guess_arc guess_utf8_ar[11] = {
+ { 0, 1.0 }, /* init -> init */
+ { 1, 1.0 }, /* init -> 1byte_more */
+ { 2, 1.0 }, /* init -> 2byte_more */
+ { 3, 1.0 }, /* init -> 3byte_more */
+ { 4, 1.0 }, /* init -> 4byte_more */
+ { 5, 1.0 }, /* init -> 5byte_more */
+ { 0, 1.0 }, /* 1byte_more -> init */
+ { 1, 1.0 }, /* 2byte_more -> 1byte_more */
+ { 2, 1.0 }, /* 3byte_more -> 2byte_more */
+ { 3, 1.0 }, /* 4byte_more -> 3byte_more */
+ { 4, 1.0 }, /* 5byte_more -> 4byte_more */
+};
+
+/* Guessing Routine */
+enum JapaneseCode::Type JapaneseCode::guess_jp(const char *buf, int buflen)
+{
+ int i;
+ guess_dfa *top = NULL;
+
+ for (i=0; i<buflen; i++) {
+ int c = (unsigned char)buf[i];
+
+ /* special treatment of jis escape sequence */
+ if (c == 0x1b || last_JIS_escape) {
+ if (i < buflen-1) {
+ if (last_JIS_escape)
+ c = (unsigned char)buf[i];
+ else
+ c = (unsigned char)buf[++i];
+ last_JIS_escape = false;
+
+ if (c == '$' || c == '(') {
+ return JapaneseCode::JIS;
+ }
+ } else {
+ last_JIS_escape = true;
+ }
+ }
+
+ if (DFA_ALIVE(eucj)) {
+ if (!DFA_ALIVE(sjis) && !DFA_ALIVE(utf8)) return JapaneseCode::EUC;
+ DFA_NEXT(eucj, c);
+ }
+ if (DFA_ALIVE(sjis)) {
+ if (!DFA_ALIVE(eucj) && !DFA_ALIVE(utf8)) return JapaneseCode::SJIS;
+ DFA_NEXT(sjis, c);
+ }
+ if (DFA_ALIVE(utf8)) {
+ if (!DFA_ALIVE(sjis) && !DFA_ALIVE(eucj)) return JapaneseCode::UTF8;
+ DFA_NEXT(utf8, c);
+ }
+
+ if (!DFA_ALIVE(eucj) && !DFA_ALIVE(sjis) && !DFA_ALIVE(utf8)) {
+ /* we ran out the possibilities */
+ return JapaneseCode::ASCII;
+ }
+ }
+
+ /* ascii code check */
+ if (eucj->score == 1.0 && sjis->score == 1.0 && utf8->score == 1.0)
+ return JapaneseCode::ASCII;
+
+ /* Now, we have ambigous code. Pick the highest score. If more than
+ one candidate tie, pick the default encoding. */
+ if (DFA_ALIVE(eucj)) top = eucj;
+ if (DFA_ALIVE(utf8)) {
+ if (top) {
+ if (top->score < utf8->score) top = utf8;
+ } else {
+ top = utf8;
+ }
+ }
+ if (DFA_ALIVE(sjis)) {
+ if (top) {
+ if (top->score <= sjis->score) top = sjis;
+ } else {
+ top = sjis;
+ }
+ }
+
+ if (top == eucj) return JapaneseCode::EUC;
+ if (top == utf8) return JapaneseCode::UTF8;
+ if (top == sjis) return JapaneseCode::SJIS;
+
+ return JapaneseCode::ASCII;
+}
diff --git a/konversation/src/guess_ja.h b/konversation/src/guess_ja.h
new file mode 100644
index 0000000..3b87b55
--- /dev/null
+++ b/konversation/src/guess_ja.h
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the KDE libraries
+ *
+ * Copyright (c) 2000-2003 Shiro Kawai <shirok@users.sourceforge.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the authors nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/*
+ * original code is here.
+ * http://cvs.sourceforge.net/viewcvs.py/gauche/Gauche/ext/charconv/guess.c?view=markup
+ */
+#ifndef GUESS_JA_H
+#define GUESS_JA_H
+
+class guess_arc {
+ public:
+ unsigned int next; /* next state */
+ double score; /* score */
+};
+
+
+typedef signed char dfa_table[256];
+
+/* DFA tables declared in guess_ja.cpp */
+extern const dfa_table guess_eucj_st[];
+extern guess_arc guess_eucj_ar[7];
+extern const dfa_table guess_sjis_st[];
+extern guess_arc guess_sjis_ar[6];
+extern const dfa_table guess_utf8_st[];
+extern guess_arc guess_utf8_ar[11];
+
+class guess_dfa {
+ public:
+ const dfa_table *states;
+ const guess_arc *arcs;
+ int state;
+ double score;
+
+ guess_dfa (const dfa_table stable[], const guess_arc *atable) :
+ states(stable), arcs(atable)
+ {
+ state = 0;
+ score = 1.0;
+ }
+};
+
+class JapaneseCode
+{
+ public:
+ enum Type {ASCII, JIS, EUC, SJIS, UNICODE, UTF8 };
+ enum Type guess_jp(const char* buf, int buflen);
+
+ JapaneseCode () {
+ eucj = new guess_dfa(guess_eucj_st, guess_eucj_ar);
+ sjis = new guess_dfa(guess_sjis_st, guess_sjis_ar);
+ utf8 = new guess_dfa(guess_utf8_st, guess_utf8_ar);
+ last_JIS_escape = false;
+ }
+
+ ~JapaneseCode () {
+ if (eucj) delete eucj;
+ if (sjis) delete sjis;
+ if (utf8) delete utf8;
+ }
+
+ protected:
+ guess_dfa *eucj;
+ guess_dfa *sjis;
+ guess_dfa *utf8;
+
+ bool last_JIS_escape;
+};
+
+#define DFA_NEXT(dfa, ch) \
+ do { \
+ int arc__; \
+ if (dfa->state >= 0) { \
+ arc__ = dfa->states[dfa->state][ch]; \
+ if (arc__ < 0) { \
+ dfa->state = -1; \
+ } else { \
+ dfa->state = dfa->arcs[arc__].next; \
+ dfa->score *= dfa->arcs[arc__].score; \
+ } \
+ } \
+ } while (0)
+
+#define DFA_ALIVE(dfa) (dfa->state >= 0)
+
+#endif /* GUESS_JA_H */
diff --git a/konversation/src/highlight.cpp b/konversation/src/highlight.cpp
new file mode 100644
index 0000000..275c441
--- /dev/null
+++ b/konversation/src/highlight.cpp
@@ -0,0 +1,49 @@
+/***************************************************************************
+ begin : Sat Jun 15 2002
+ copyright : (C) 2002 by Matthias Gierlings
+ email : gismore@users.sourceforge.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "highlight.h"
+
+
+unsigned int Highlight::s_id = 0; // static
+
+Highlight::Highlight(const QString& itemPattern,
+bool regExp,
+const QColor& itemColor,
+const KURL& soundURL,
+const QString& autoText)
+{
+ m_itemPattern = itemPattern;
+ m_autoText = autoText;
+ m_itemColor = itemColor;
+ m_soundURL = soundURL;
+ m_regExp = regExp;
+
+ // unique ID for every Highlight
+ m_itemID = s_id++;
+}
+
+int Highlight::getID() { return m_itemID; }
+QString Highlight::getPattern() { return m_itemPattern; }
+QString Highlight::getAutoText() { return m_autoText; }
+QColor Highlight::getColor() { return m_itemColor; }
+KURL Highlight::getSoundURL() { return m_soundURL; }
+
+void Highlight::setPattern(const QString& itemPattern) { m_itemPattern = itemPattern; }
+void Highlight::setAutoText(const QString& autoText) { m_autoText = autoText; }
+void Highlight::setColor(const QColor& itemColor) { m_itemColor = itemColor; }
+void Highlight::setSoundURL(const KURL& url) { m_soundURL = url; }
+
+void Highlight::setRegExp(bool state) { m_regExp=state; }
+bool Highlight::getRegExp() { return m_regExp; }
diff --git a/konversation/src/highlight.h b/konversation/src/highlight.h
new file mode 100644
index 0000000..e843023
--- /dev/null
+++ b/konversation/src/highlight.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ highlight.cpp - description
+ -------------------
+ begin : Sat Jun 15 2002
+ copyright : (C) 2002 by Matthias Gierlings
+ email : gismore@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef HIGHLIGHT_H
+#define HIGHLIGHT_H
+
+#include <qstring.h>
+#include <qcolor.h>
+
+#include <kurl.h>
+
+/**
+ *@author Matthias Gierlings
+ */
+
+class Highlight
+{
+ public:
+ Highlight(const QString& itemPattern,
+ bool regExp,
+ const QColor& itemColor,
+ const KURL& soundURL,
+ const QString& autoText);
+
+ QString getPattern();
+ QString getAutoText();
+ QColor getColor();
+ int getID();
+ bool getRegExp();
+ KURL getSoundURL();
+
+ void setPattern(const QString& itemPattern);
+ void setColor(const QColor& itemColor);
+ void setSoundURL(const KURL& url);
+ void setAutoText(const QString& autoText);
+ void setRegExp(bool state);
+
+ protected:
+ static unsigned int s_id;
+
+ int m_itemID;
+ bool m_regExp;
+
+ QString m_itemPattern;
+ QString m_autoText;
+ QColor m_itemColor;
+ KURL m_soundURL;
+};
+#endif
diff --git a/konversation/src/highlight_preferences.cpp b/konversation/src/highlight_preferences.cpp
new file mode 100644
index 0000000..be757e6
--- /dev/null
+++ b/konversation/src/highlight_preferences.cpp
@@ -0,0 +1,352 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#include "highlight_preferences.h"
+#include "highlightviewitem.h"
+#include "konversationapplication.h"
+#include "konversationsound.h"
+#include "config/preferences.h"
+
+#include <qdir.h>
+#include <qlabel.h>
+#include <qheader.h>
+#include <qtooltip.h>
+#include <qtoolbutton.h>
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <kcolorbutton.h>
+#include <klocale.h>
+#include <kparts/componentfactory.h>
+#include <kregexpeditorinterface.h>
+#include <kiconloader.h>
+
+
+Highlight_Config::Highlight_Config(QWidget* parent, const char* name)
+ : Highlight_ConfigUI(parent,name)
+{
+ // reset flag to defined state (used to block signals when just selecting a new item)
+ newItemSelected=false;
+
+ loadSettings();
+
+ // make list accept drag & drop
+ highlightListView->setSorting(-1);
+ highlightListView->header()->setMovingEnabled(false);
+
+ soundPlayBtn->setIconSet(SmallIconSet( "player_play" ));
+ soundURL->setCaption(i18n("Select Sound File"));
+
+ // This code was copied from KNotifyWidget::openSoundDialog() (knotifydialog.cpp) [it's under LGPL v2]
+ // find the first "sound"-resource that contains files
+ QStringList soundDirs = KGlobal::dirs()->findDirs("data", "konversation/sounds");
+ soundDirs += KGlobal::dirs()->resourceDirs( "sound" );
+
+ if ( !soundDirs.isEmpty() ) {
+ KURL url;
+ QDir dir;
+ dir.setFilter( QDir::Files | QDir::Readable );
+ QStringList::ConstIterator it = soundDirs.begin();
+ while ( it != soundDirs.end() ) {
+ dir = *it;
+ if ( dir.isReadable() && dir.count() > 2 ) {
+ url.setPath( *it );
+ soundURL->fileDialog()->setURL( url );
+ break;
+ }
+ ++it;
+ }
+ }
+ // End copy
+
+ connect(highlightListView,SIGNAL (selectionChanged(QListViewItem*)),this,SLOT (highlightSelected(QListViewItem*)) );
+ connect(highlightListView,SIGNAL (clicked(QListViewItem*)),this,SLOT (highlightSelected(QListViewItem*)) );
+ connect(highlightListView,SIGNAL (spacePressed(QListViewItem*)),this,SLOT (highlightSelected(QListViewItem*)) );
+
+ connect(highlightListView,SIGNAL (moved()),this,SIGNAL (modified()) );
+
+ connect(patternInput,SIGNAL (textChanged(const QString&)),this,SLOT (highlightTextChanged(const QString&)) );
+ connect(patternButton,SIGNAL (clicked()),this,SLOT(highlightTextEditButtonClicked()));
+ connect(patternColor,SIGNAL (changed(const QColor&)),this,SLOT (highlightColorChanged(const QColor&)) );
+
+ connect(soundURL, SIGNAL(textChanged(const QString&)), this, SLOT(soundURLChanged(const QString&)));
+ connect(soundPlayBtn, SIGNAL(clicked()), this, SLOT(playSound()));
+
+ connect(autoTextInput,SIGNAL (textChanged(const QString&)),this,SLOT (autoTextChanged(const QString&)) );
+
+ connect(newButton,SIGNAL (clicked()),this,SLOT (addHighlight()) );
+ connect(removeButton,SIGNAL (clicked()),this,SLOT (removeHighlight()) );
+
+ updateButtons();
+}
+
+Highlight_Config::~Highlight_Config()
+{
+}
+
+void Highlight_Config::restorePageToDefaults()
+{
+ if(highlightListView->childCount() != 0) {
+ highlightListView->clear();
+ emit modified();
+ }
+}
+
+void Highlight_Config::loadSettings()
+{
+ QPtrList<Highlight> highlightList=Preferences::highlightList();
+ highlightListView->clear();
+ // fill in the highlight patterns backwards to keep the right sorting order
+ for(unsigned int i=highlightList.count();i!=0;i--)
+ {
+ Highlight* currentHighlight=highlightList.at(i-1);
+ new HighlightViewItem(highlightListView,currentHighlight);
+ }
+
+ highlightListView->setSelected(highlightListView->firstChild(), true);
+
+ // remember current list for hasChanged()
+ m_oldHighlightList=currentHighlightList();
+}
+
+bool Highlight_Config::hasChanged()
+{
+ return(m_oldHighlightList!=currentHighlightList());
+}
+
+// Slots:
+
+void Highlight_Config::highlightSelected(QListViewItem* item)
+{
+ // check if there was a widget selected at all
+ if(item)
+ {
+ // make a highlight item out of the generic qlistviewitem
+ HighlightViewItem* highlightItem=static_cast<HighlightViewItem*>(item);
+
+ // check if the checkbox on the item has changed
+ if(highlightItem->hasChanged())
+ {
+ // tell the prefs system it was changed and acknowledge the change to the listview item
+ emit modified();
+ highlightItem->changeAcknowledged();
+ }
+
+ // tell all now emitted signals that we just clicked on a new item, so they should
+ // not emit the modified() signal.
+ newItemSelected=true;
+ patternColor->setColor(highlightItem->getColor());
+ patternInput->setText(highlightItem->getPattern());
+ soundURL->setURL(highlightItem->getSoundURL().prettyURL());
+ autoTextInput->setText(highlightItem->getAutoText());
+ // all signals will now emit the modified() signal again
+ newItemSelected=false;
+ // remember to enable all edit widgets
+ }
+ updateButtons();
+
+ }
+
+void Highlight_Config::updateButtons()
+{
+ bool enabled = highlightListView->selectedItem() != NULL;
+ // is the kregexpeditor installed?
+ bool installed = !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty();
+ // enable or disable edit widgets
+ patternLabel->setEnabled(enabled);
+ patternInput->setEnabled(enabled);
+ patternButton->setEnabled(enabled && installed);
+ colorLabel->setEnabled(enabled);
+ patternColor->setEnabled(enabled);
+ soundURL->setEnabled(enabled);
+ soundLabel->setEnabled(enabled);
+ soundPlayBtn->setEnabled(enabled);
+ autoTextLabel->setEnabled(enabled);
+ autoTextInput->setEnabled(enabled);
+
+ if(installed)
+ {
+ QToolTip::add(patternButton, i18n("Click to run Regular Expression Editor (KRegExpEditor)"));
+ }
+ else
+ {
+ QToolTip::add(patternButton, i18n("The Regular Expression Editor (KRegExpEditor) is not installed"));
+ }
+}
+
+void Highlight_Config::highlightTextChanged(const QString& newPattern)
+{
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->selectedItem());
+
+ if(!newItemSelected && item)
+ {
+ item->setPattern(newPattern);
+ emit modified();
+ }
+}
+
+void Highlight_Config::highlightTextEditButtonClicked()
+{
+ QDialog *editorDialog =
+ KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" );
+ if (editorDialog)
+ {
+ // kdeutils was installed, so the dialog was found. Fetch the editor interface.
+ KRegExpEditorInterface *reEditor =
+ static_cast<KRegExpEditorInterface *>(editorDialog->qt_cast( "KRegExpEditorInterface" ) );
+ Q_ASSERT( reEditor ); // This should not fail!// now use the editor.
+ reEditor->setRegExp(patternInput->text());
+ int dlgResult = editorDialog->exec();
+ if ( dlgResult == QDialog::Accepted )
+ {
+ QString re = reEditor->regExp();
+ patternInput->setText(re);
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->selectedItem());
+ if(item) item->setPattern(re);
+ }
+ delete editorDialog;
+ }
+}
+
+void Highlight_Config::highlightColorChanged(const QColor& newColor)
+{
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->selectedItem());
+
+ if(!newItemSelected && item)
+ {
+ item->setColor(newColor);
+ item->repaint();
+ emit modified();
+ }
+}
+
+void Highlight_Config::soundURLChanged(const QString& newURL)
+{
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->selectedItem());
+
+ if(!newItemSelected && item)
+ {
+ item->setSoundURL(KURL(newURL));
+ emit modified();
+ }
+}
+
+void Highlight_Config::autoTextChanged(const QString& newText)
+{
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->selectedItem());
+
+ if(!newItemSelected && item)
+ {
+ item->setAutoText(newText);
+ emit modified();
+ }
+}
+
+void Highlight_Config::addHighlight()
+{
+ Highlight* newHighlight=new Highlight(i18n("New"),false,QColor("#ff0000"),KURL(),QString());
+
+ HighlightViewItem* item=new HighlightViewItem(highlightListView,newHighlight);
+ highlightListView->setSelected(item,true);
+ patternInput->setFocus();
+ patternInput->selectAll();
+ emit modified();
+}
+
+void Highlight_Config::removeHighlight()
+{
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->selectedItem());
+
+ if(item)
+ {
+ delete item;
+
+ item=static_cast<HighlightViewItem*>(highlightListView->currentItem());
+
+ if(item)
+ highlightListView->setSelected(item,true);
+
+ emit modified();
+ }
+ updateButtons();
+}
+
+QPtrList<Highlight> Highlight_Config::getHighlightList()
+{
+ QPtrList<Highlight> newList;
+
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->firstChild());
+ while(item)
+ {
+ newList.append(new Highlight(item->getPattern(),item->getRegExp(),item->getColor(),item->getSoundURL(),item->getAutoText()));
+ item=item->itemBelow();
+ }
+
+ return newList;
+}
+
+QStringList Highlight_Config::currentHighlightList()
+{
+ QStringList newList;
+
+ HighlightViewItem* item=static_cast<HighlightViewItem*>(highlightListView->firstChild());
+ while(item)
+ {
+ newList.append(item->getPattern()+item->getRegExp()+item->getColor().name()+item->getSoundURL().url()+item->getAutoText());
+ item=item->itemBelow();
+ }
+
+ return newList;
+}
+
+void Highlight_Config::playSound()
+{
+ KonversationApplication *konvApp=static_cast<KonversationApplication *>(KApplication::kApplication());
+ konvApp->sound()->play(KURL(soundURL->url()));
+}
+
+void Highlight_Config::saveSettings()
+{
+ KConfig* config = kapp->config();
+
+ // Write all highlight entries
+ QPtrList<Highlight> hiList=getHighlightList();
+ int i = 0;
+ for(Highlight* hl = hiList.first(); hl; hl = hiList.next())
+ {
+ config->setGroup(QString("Highlight%1").arg(i));
+ config->writeEntry("Pattern", hl->getPattern());
+ config->writeEntry("RegExp", hl->getRegExp());
+ config->writeEntry("Color", hl->getColor());
+ config->writePathEntry("Sound", hl->getSoundURL().prettyURL());
+ config->writeEntry("AutoText", hl->getAutoText());
+ i++;
+ }
+
+ Preferences::setHighlightList(hiList);
+
+ // Remove unused entries...
+ while(config->hasGroup(QString("Highlight%1").arg(i)))
+ {
+ config->deleteGroup(QString("Highlight%1").arg(i));
+ i++;
+ }
+
+ // remember current list for hasChanged()
+ m_oldHighlightList=currentHighlightList();
+}
+
+#include "highlight_preferences.moc"
diff --git a/konversation/src/highlight_preferences.h b/konversation/src/highlight_preferences.h
new file mode 100644
index 0000000..997a4c1
--- /dev/null
+++ b/konversation/src/highlight_preferences.h
@@ -0,0 +1,63 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef KONVERSATIONHIGHLIGHT_CONFIG_H
+#define KONVERSATIONHIGHLIGHT_CONFIG_H
+
+#include "highlight_preferencesui.h"
+#include "konvisettingspage.h"
+
+#include <qobject.h>
+
+
+class Highlight_Config;
+class Highlight;
+
+class Highlight_Config : public Highlight_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit Highlight_Config(QWidget *parent = 0, const char *name = 0);
+ ~Highlight_Config();
+
+ public:
+ virtual void saveSettings();
+ virtual void loadSettings();
+ virtual void restorePageToDefaults();
+
+ virtual bool hasChanged();
+
+ signals:
+ void modified();
+
+ protected slots:
+ void highlightSelected(QListViewItem* item);
+ void highlightTextChanged(const QString& newPattern);
+ void highlightTextEditButtonClicked();
+ void highlightColorChanged(const QColor& newColor);
+ void soundURLChanged(const QString& newURL);
+ void autoTextChanged(const QString& newText);
+ void addHighlight();
+ void removeHighlight();
+ void playSound();
+ QPtrList<Highlight> getHighlightList(); // prefs format
+ QStringList currentHighlightList(); // hasChanged() format
+ protected:
+ void updateButtons();
+
+ bool newItemSelected;
+ QStringList m_oldHighlightList;
+};
+
+#endif
+
diff --git a/konversation/src/highlight_preferencesui.ui b/konversation/src/highlight_preferencesui.ui
new file mode 100644
index 0000000..3e617d6
--- /dev/null
+++ b/konversation/src/highlight_preferencesui.ui
@@ -0,0 +1,453 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Highlight_ConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Highlight_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>614</width>
+ <height>585</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Highlight List</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="4">
+ <column>
+ <property name="text">
+ <string>RegEx</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Highlights</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Sound</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Auto Text</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>highlightListView</cstring>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="4" rowspan="5" colspan="1">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>newButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer27</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>250</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="KPushButton" row="1" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>patternButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Click to run Regular Expression Editor (KRegExpEditor)</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>patternLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Pattern:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>patternInput</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>patternInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>colorLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Color:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>patternColor</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>soundLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Sound:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>soundURL</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="3" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>soundURL</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="filter">
+ <string>audio/x-wav audio/x-mp3 application/ogg audio/x-adpcm</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="3" column="3">
+ <property name="name">
+ <cstring>soundPlayBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Test sound</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>autoTextLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Auto text:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>autoTextInput</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="4" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>autoTextInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_HighlightSoundsEnabled</cstring>
+ </property>
+ <property name="text">
+ <string>Ena&amp;ble sound for highlights</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_HighlightNickColor</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>kcfg_HighlightOwnLinesColor</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_HighlightNick</cstring>
+ </property>
+ <property name="text">
+ <string>Alwa&amp;ys highlight own current nick:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_HighlightOwnLines</cstring>
+ </property>
+ <property name="text">
+ <string>Always highlight own &amp;lines:</string>
+ </property>
+ </widget>
+ <spacer row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>120</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="2">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_HighlightNick</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_HighlightNickColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_HighlightOwnLines</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_HighlightOwnLinesColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>highlightListView</tabstop>
+ <tabstop>patternInput</tabstop>
+ <tabstop>patternButton</tabstop>
+ <tabstop>patternColor</tabstop>
+ <tabstop>soundURL</tabstop>
+ <tabstop>soundPlayBtn</tabstop>
+ <tabstop>autoTextInput</tabstop>
+ <tabstop>kcfg_HighlightSoundsEnabled</tabstop>
+ <tabstop>kcfg_HighlightNick</tabstop>
+ <tabstop>kcfg_HighlightNickColor</tabstop>
+ <tabstop>kcfg_HighlightOwnLines</tabstop>
+ <tabstop>kcfg_HighlightOwnLinesColor</tabstop>
+ <tabstop>newButton</tabstop>
+ <tabstop>removeButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/highlightviewitem.cpp b/konversation/src/highlightviewitem.cpp
new file mode 100644
index 0000000..49c9789
--- /dev/null
+++ b/konversation/src/highlightviewitem.cpp
@@ -0,0 +1,93 @@
+/***************************************************************************
+ begin : Sat Jun 15 2002
+ copyright : (C) 2002 by Matthias Gierlings
+ email : gismore@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "highlightviewitem.h"
+
+#include <kurl.h>
+#include <klistview.h>
+
+
+HighlightViewItem::HighlightViewItem(KListView* parent, Highlight* passed_Highlight)
+: QCheckListItem(parent, QString(), QCheckListItem::CheckBox)
+{
+ setText(1,passed_Highlight->getPattern());
+ itemColor = passed_Highlight->getColor();
+ itemID = passed_Highlight->getID();
+ setSoundURL(passed_Highlight->getSoundURL());
+ setAutoText(passed_Highlight->getAutoText());
+ setOn(passed_Highlight->getRegExp());
+ m_changed=false;
+}
+
+HighlightViewItem::~HighlightViewItem()
+{
+}
+
+void HighlightViewItem::paintCell(QPainter* p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ // copy all colors from cg and only then change needed colors
+ itemColorGroup=cg;
+ itemColorGroup.setColor(QColorGroup::Text, itemColor);
+ QCheckListItem::paintCell(p, itemColorGroup, column, width, alignment);
+}
+
+HighlightViewItem* HighlightViewItem::itemBelow()
+{
+ return (HighlightViewItem*) QCheckListItem::itemBelow();
+}
+
+void HighlightViewItem::setPattern(const QString& newPattern) { setText(1,newPattern); }
+QString HighlightViewItem::getPattern() { return text(1); }
+
+void HighlightViewItem::setSoundURL(const KURL& url)
+{
+ soundURL = url;
+ setText(2, soundURL.prettyURL());
+}
+
+void HighlightViewItem::setAutoText(const QString& newAutoText)
+{
+ autoText = newAutoText;
+ setText(3,newAutoText);
+}
+
+bool HighlightViewItem::getRegExp()
+{
+ return isOn();
+}
+
+QString HighlightViewItem::getAutoText()
+{
+ return autoText;
+}
+
+// override default method to store the change
+void HighlightViewItem::stateChange(bool /* newState */)
+{
+ // remember that the check box has been changed
+ m_changed=true;
+}
+
+// returns true, if the checkbox has been changed
+bool HighlightViewItem::hasChanged()
+{
+ return m_changed;
+}
+
+// tells us that the program has seen us changing
+void HighlightViewItem::changeAcknowledged()
+{
+ m_changed=false;
+}
diff --git a/konversation/src/highlightviewitem.h b/konversation/src/highlightviewitem.h
new file mode 100644
index 0000000..89d4ce0
--- /dev/null
+++ b/konversation/src/highlightviewitem.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ begin : Sat Jun 15 2002
+ copyright : (C) 2002 by Matthias Gierlings
+ email : gismore@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef HIGHLIGHTVIEWITEM_H
+#define HIGHLIGHTVIEWITEM_H
+
+#include "highlight.h"
+
+#include <qlistview.h>
+
+
+class KURL;
+class KListView;
+
+class HighlightViewItem : public QCheckListItem
+{
+ public:
+ HighlightViewItem(KListView* parent, Highlight* passed_Highlight);
+ ~HighlightViewItem();
+
+ QString getPattern();
+ QString getAutoText();
+ QColor getColor() { return itemColor; }
+ int getID() { return itemID; }
+ bool getRegExp();
+ KURL getSoundURL() { return soundURL; }
+
+ void setPattern(const QString& newPattern);
+ void setAutoText(const QString& newAutoText);
+ void setColor(QColor passed_itemColor) { itemColor = passed_itemColor; }
+ void setID(int passed_itemID) { itemID = passed_itemID; }
+ void setSoundURL(const KURL& url);
+
+ /** checks if the checkbox has been changed by the user
+ * stored internally by m_changed
+ *
+ * @return true when the checkbox has been changed
+ */
+ bool hasChanged();
+
+ /** reset the change state of the listview item
+ * call this when you have seen the change and acted upon it properly
+ */
+ void changeAcknowledged();
+
+ HighlightViewItem* itemBelow();
+
+ protected:
+ QColor itemColor;
+ QColorGroup itemColorGroup;
+ int itemID;
+ KURL soundURL;
+ QString autoText;
+
+ bool m_changed; // true if the checkbox has been changed
+
+ void stateChange(bool newState); // reimplemented to store changed value
+ void paintCell(QPainter* p, const QColorGroup &cg, int column, int width, int alignment);
+};
+#endif
diff --git a/konversation/src/identity.cpp b/konversation/src/identity.cpp
new file mode 100644
index 0000000..01697dd
--- /dev/null
+++ b/konversation/src/identity.cpp
@@ -0,0 +1,177 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ This class holds the various user identities
+ begin: Son Feb 9 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "identity.h"
+#include "irccharsets.h"
+
+#include <qtextcodec.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+
+
+int Identity::s_availableId = 0;
+
+Identity::Identity() : KShared()
+{
+ m_id = s_availableId;
+ s_availableId++;
+
+ init();
+}
+
+Identity::Identity(int id) : KShared()
+{
+ if (id < 0)
+ {
+ m_id = s_availableId;
+ s_availableId++;
+ }
+ else
+ {
+ m_id = id;
+ }
+
+ init();
+}
+
+Identity::Identity(const Identity& original) : KShared()
+{
+ copy(original);
+ m_id = original.id();
+}
+
+Identity::~Identity()
+{
+}
+
+void Identity::init()
+{
+ setCodecName(Konversation::IRCCharsets::self()->encodingForLocale());
+
+ setInsertRememberLineOnAway(false);
+
+ setQuitReason("Konversation terminated!");
+ setPartReason("Konversation terminated!");
+ setKickReason("User terminated!");
+
+ setShowAwayMessage(false);
+ setAwayMessage("/me is away: %s");
+ setReturnMessage("/me is back.");
+
+ setAutomaticAway(false);
+ setAwayInactivity(10);
+ setAutomaticUnaway(false);
+}
+
+void Identity::copy(const Identity& original)
+{
+ setName(original.getName());
+ setRealName(original.getRealName());
+ setIdent(original.getIdent());
+ setNicknameList(original.getNicknameList());
+ setBot(original.getBot());
+ setPassword(original.getPassword());
+ setQuitReason(original.getQuitReason());
+ setPartReason(original.getPartReason());
+ setKickReason(original.getKickReason());
+ setInsertRememberLineOnAway(original.getInsertRememberLineOnAway());
+ setShowAwayMessage(original.getShowAwayMessage());
+ setAwayMessage(original.getAwayMessage());
+ setAwayNick(original.getAwayNick());
+ setReturnMessage(original.getReturnMessage());
+ setAutomaticAway(original.getAutomaticAway());
+ setAwayInactivity(original.getAwayInactivity());
+ setAutomaticUnaway(original.getAutomaticUnaway());
+ setShellCommand(original.getShellCommand());
+ setCodecName(original.getCodecName());
+}
+
+void Identity::setName(const QString& newName) { name=newName; }
+QString Identity::getName() const { return name; }
+
+void Identity::setRealName(const QString& name) { realName=name; }
+QString Identity::getRealName() const { return realName; }
+void Identity::setIdent(const QString& newIdent) { ident=newIdent; }
+QString Identity::getIdent() const { return ident; }
+
+void Identity::setNickname(uint index,const QString& newName) { nicknameList[index]=newName; }
+
+QString Identity::getNickname(uint index) const
+{
+ if(index < nicknameList.count())
+ return nicknameList[index];
+ else
+ return QString();
+}
+
+void Identity::setBot(const QString& newBot) { bot=newBot; }
+QString Identity::getBot() const { return bot; }
+
+void Identity::setPassword(const QString& newPassword) { password=newPassword; }
+QString Identity::getPassword() const { return password; }
+
+void Identity::setQuitReason(const QString& reason) { quitReason=reason; }
+QString Identity::getQuitReason() const { return quitReason; }
+void Identity::setPartReason(const QString& reason) { partReason=reason; }
+QString Identity::getPartReason() const { return partReason; }
+void Identity::setKickReason(const QString& reason) { kickReason=reason; }
+QString Identity::getKickReason() const { return kickReason; }
+
+void Identity::setInsertRememberLineOnAway(bool state) { insertRememberLineOnAway = state; }
+bool Identity::getInsertRememberLineOnAway() const { return insertRememberLineOnAway; }
+void Identity::setShowAwayMessage(bool state) { showAwayMessages=state; }
+bool Identity::getShowAwayMessage() const { return showAwayMessages; }
+
+void Identity::setAwayMessage(const QString& message) { awayMessage=message; }
+QString Identity::getAwayMessage() const { return awayMessage; }
+void Identity::setReturnMessage(const QString& message) { returnMessage=message; }
+QString Identity::getReturnMessage() const { return returnMessage; }
+
+void Identity::setAutomaticAway(bool automaticAway) { m_automaticAway = automaticAway; }
+bool Identity::getAutomaticAway() const { return m_automaticAway; }
+void Identity::setAwayInactivity(int awayInactivity) { m_awayInactivity = awayInactivity; }
+int Identity::getAwayInactivity() const { return m_awayInactivity; }
+void Identity::setAutomaticUnaway(bool automaticUnaway) { m_automaticUnaway = automaticUnaway; }
+bool Identity::getAutomaticUnaway() const { return m_automaticUnaway; }
+
+void Identity::setNicknameList(const QStringList& newList)
+{
+ nicknameList.clear();
+ nicknameList = newList;
+}
+
+QStringList Identity::getNicknameList() const { return nicknameList; }
+
+QString Identity::getShellCommand() const { return m_shellCommand;}
+void Identity::setShellCommand(const QString& command) { m_shellCommand=command;}
+
+QTextCodec* Identity::getCodec() const { return m_codec; }
+QString Identity::getCodecName() const { return m_codecName; }
+void Identity::setCodecName(const QString &newCodecName)
+{
+ // NOTE: codecName should be based on KCharsets::availableEncodingNames() / descriptiveEncodingNames()
+ // We can get a QTextCodec from QString based on them, but can't do the reverse of that.
+
+ // never set an empty or borked codec!
+ QString codecName=newCodecName.lower();
+ if(!Konversation::IRCCharsets::self()->isValidEncoding(codecName))
+ codecName=Konversation::IRCCharsets::self()->encodingForLocale();
+
+ m_codecName=codecName;
+ m_codec=Konversation::IRCCharsets::self()->codecForName(codecName);
+}
+
+QString Identity::getAwayNick() const { return awayNick; }
+void Identity::setAwayNick(const QString& n) { awayNick = n; }
diff --git a/konversation/src/identity.h b/konversation/src/identity.h
new file mode 100644
index 0000000..8fdec49
--- /dev/null
+++ b/konversation/src/identity.h
@@ -0,0 +1,133 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ This class holds the various user identities
+ begin: Son Feb 9 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef IDENTITY_H
+#define IDENTITY_H
+
+#include <ksharedptr.h>
+#include <qstringlist.h>
+
+
+class QTextCodec;
+
+class Identity : public KShared
+{
+ public:
+ /// Create an Identity with a new id.
+ Identity();
+ /// Create a new Identity with a set id.
+ explicit Identity(int id);
+ /// Copy all of @param original including the id.
+ Identity(const Identity& original);
+ ~Identity();
+
+ /// This function copies all of @param original but the id
+ void copy(const Identity& original);
+
+ void setName(const QString& name); // the name of this identity
+ QString getName() const;
+
+ void setRealName(const QString& name);
+ QString getRealName() const;
+ void setIdent(const QString& ident);
+ QString getIdent() const;
+
+ void setNickname(uint index,const QString& nick);
+ QString getNickname(uint index) const;
+
+ void setBot(const QString& bot);
+ QString getBot() const;
+ void setPassword(const QString& password);
+ QString getPassword() const;
+
+ void setNicknameList(const QStringList& newList);
+ QStringList getNicknameList() const;
+
+ void setQuitReason(const QString& reason);
+ QString getQuitReason() const;
+ void setPartReason(const QString& reason);
+ QString getPartReason() const;
+ void setKickReason(const QString& reason);
+ QString getKickReason() const;
+
+ void setInsertRememberLineOnAway(bool state);
+ bool getInsertRememberLineOnAway() const;
+ void setShowAwayMessage(bool state);
+ bool getShowAwayMessage() const;
+
+ void setAwayMessage(const QString& message);
+ QString getAwayMessage() const;
+ void setReturnMessage(const QString& message);
+ QString getReturnMessage() const;
+
+ void setAutomaticAway(bool automaticAway);
+ bool getAutomaticAway() const;
+ void setAwayInactivity(int awayInactivity);
+ int getAwayInactivity() const;
+ void setAutomaticUnaway(bool automaticUnaway);
+ bool getAutomaticUnaway() const;
+
+ void setShellCommand(const QString &command);
+ QString getShellCommand() const;
+
+ void setCodecName(const QString &newCodecName);
+ QString getCodecName() const;
+ QTextCodec* getCodec() const;
+
+ QString getAwayNick() const;
+ void setAwayNick(const QString& n);
+
+ int id() const { return m_id; }
+
+ protected:
+ QString name;
+
+ QString bot;
+ QString password;
+
+ QString realName;
+ QString ident;
+
+ QStringList nicknameList;
+
+ QString partReason;
+ QString quitReason;
+ QString kickReason;
+
+ bool insertRememberLineOnAway;
+ bool showAwayMessages;
+ QString awayMessage;
+ QString returnMessage;
+
+ bool m_automaticAway;
+ int m_awayInactivity;
+ bool m_automaticUnaway;
+
+ QString m_codecName;
+ QTextCodec* m_codec;
+
+ QString m_shellCommand;
+
+ QString awayNick;
+
+ private:
+ int m_id;
+ static int s_availableId;
+ void init();
+};
+
+typedef KSharedPtr<Identity> IdentityPtr;
+typedef QValueList<IdentityPtr> IdentityList;
+
+#endif
diff --git a/konversation/src/identitydialog.cpp b/konversation/src/identitydialog.cpp
new file mode 100644
index 0000000..aa054b3
--- /dev/null
+++ b/konversation/src/identitydialog.cpp
@@ -0,0 +1,642 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#include "identitydialog.h"
+#include "konversationapplication.h"
+#include "awaymanager.h"
+#include "irccharsets.h"
+
+#include <qframe.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qvaluelist.h>
+#include <qcheckbox.h>
+#include <qspinbox.h>
+#include <qtoolbutton.h>
+#include <qtabwidget.h>
+#include <qlistbox.h>
+#include <qgroupbox.h>
+#include <qpushbutton.h>
+#include <qwhatsthis.h>
+
+#include <kcombobox.h>
+#include <klocale.h>
+#include <klineedit.h>
+#include <kiconloader.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kuser.h>
+
+
+namespace Konversation
+{
+
+ IdentityDialog::IdentityDialog(QWidget *parent, const char *name)
+ : KDialogBase(Plain, i18n("Identities"), Ok|Cancel, Ok, parent, name)
+ {
+ QFrame* mainWidget = plainPage();
+ QGridLayout* mainLayout = new QGridLayout(mainWidget, 1, 2, 0, spacingHint());
+
+ QLabel* identityLabel = new QLabel(i18n("&Identity:"), mainWidget);
+ m_identityCBox = new KComboBox(mainWidget, "identity_combo");
+ m_identityCBox->setEditable(false);
+ identityLabel->setBuddy(m_identityCBox);
+
+ IdentityList tmpList = Preferences::identityList();
+
+ for(IdentityList::ConstIterator it = tmpList.begin(); it != tmpList.end(); ++it)
+ {
+ m_identityCBox->insertItem((*it)->getName());
+ m_identityList.append(new Identity(*(*it)));
+ }
+
+ QToolButton* newBtn = new QToolButton(mainWidget);
+ newBtn->setIconSet(SmallIconSet("add"));
+ newBtn->setTextLabel(i18n("Add"));
+ connect(newBtn, SIGNAL(clicked()), this, SLOT(newIdentity()));
+
+ QToolButton* copyBtn = new QToolButton(mainWidget);
+ copyBtn->setIconSet(SmallIconSet("editcopy"));
+ copyBtn->setTextLabel(i18n("Duplicate"));
+ connect(copyBtn, SIGNAL(clicked()), this, SLOT(copyIdentity()));
+
+ m_editBtn = new QToolButton(mainWidget);
+ m_editBtn->setIconSet(SmallIconSet("edit"));
+ m_editBtn->setTextLabel(i18n("Rename"));
+ connect(m_editBtn, SIGNAL(clicked()), this, SLOT(renameIdentity()));
+
+ m_delBtn = new QToolButton(mainWidget);
+ m_delBtn->setIconSet(SmallIconSet("editdelete"));
+ m_delBtn->setTextLabel(i18n("Remove"));
+ connect(m_delBtn, SIGNAL(clicked()), this, SLOT(deleteIdentity()));
+
+ QTabWidget* tabWidget = new QTabWidget(mainWidget);
+ QWidget* generalWidget = new QWidget(tabWidget);
+ tabWidget->addTab(generalWidget, i18n("General"));
+ QGridLayout* generalLayout = new QGridLayout(generalWidget, 1, 2, marginHint(), spacingHint());
+
+ QLabel* realNameLabel = new QLabel(i18n("&Real name:"), generalWidget);
+ m_realNameEdit = new KLineEdit(generalWidget);
+ QWhatsThis::add(m_realNameEdit, i18n("Enter your real name here. IRC is not intended to keep you hidden from your friends or enemies. Keep this in mind if you are tempted to behave maliciously. A fake \"real name\" can be a good way to mask your gender from all the nerds out there, but the PC you use can always be traced so you will never be truly anonymous."));
+ realNameLabel->setBuddy(m_realNameEdit);
+
+ QGroupBox* nicknameGBox = new QGroupBox(0, Qt::Horizontal, i18n("Nickname"), generalWidget);
+ nicknameGBox->setMargin(marginHint());
+ QGridLayout* nicknameLayout = new QGridLayout(nicknameGBox->layout(), 1, 2, spacingHint());
+
+ m_nicknameLBox = new QListBox(nicknameGBox);
+ QWhatsThis::add(m_nicknameLBox, i18n("This is your list of nicknames. A nickname is the name that other users will know you by. You may use any name you desire. The first character must be a letter.\n\nSince nicknames must be unique across an entire IRC network, your desired name may be rejected by the server because someone else is already using that nickname. Enter alternate nicknames for yourself. If your first choice is rejected by the server, Konversation will try the alternate nicknames."));
+ m_addNicknameBtn = new QPushButton(i18n("Add..."), nicknameGBox);
+ m_changeNicknameBtn = new QPushButton(i18n("Edit..."), nicknameGBox);
+ m_changeNicknameBtn->setEnabled(false);
+ m_removeNicknameBtn = new QPushButton(i18n("Delete"), nicknameGBox);
+ m_removeNicknameBtn->setEnabled(false);
+ m_upNicknameBtn = new QToolButton(nicknameGBox);
+ m_upNicknameBtn->setIconSet(SmallIconSet("up"));
+ m_upNicknameBtn->setAutoRepeat(true);
+ m_upNicknameBtn->setEnabled(false);
+ m_downNicknameBtn = new QToolButton(nicknameGBox);
+ m_downNicknameBtn->setIconSet(SmallIconSet("down"));
+ m_downNicknameBtn->setAutoRepeat(true);
+ m_downNicknameBtn->setEnabled(false);
+
+ connect(m_addNicknameBtn, SIGNAL(clicked()), this, SLOT(addNickname()));
+ connect(m_changeNicknameBtn, SIGNAL(clicked()), this, SLOT(editNickname()));
+ connect(m_removeNicknameBtn, SIGNAL(clicked()), this, SLOT(deleteNickname()));
+ connect(m_nicknameLBox, SIGNAL(selectionChanged()), this, SLOT(updateButtons()));
+ connect(m_upNicknameBtn, SIGNAL(clicked()), this, SLOT(moveNicknameUp()));
+ connect(m_downNicknameBtn, SIGNAL(clicked()), this, SLOT(moveNicknameDown()));
+
+ nicknameLayout->setColStretch(0, 10);
+ nicknameLayout->setRowStretch(4, 10);
+ nicknameLayout->addMultiCellWidget(m_nicknameLBox, 0, 4, 0, 0);
+ nicknameLayout->addMultiCellWidget(m_addNicknameBtn, 0, 0, 1, 4);
+ nicknameLayout->addMultiCellWidget(m_changeNicknameBtn, 1, 1, 1, 4);
+ nicknameLayout->addMultiCellWidget(m_removeNicknameBtn, 2, 2, 1, 4);
+ nicknameLayout->addWidget(m_upNicknameBtn, 3, 2);
+ nicknameLayout->addWidget(m_downNicknameBtn, 3, 3);
+
+ QGroupBox* autoIdentifyGBox = new QGroupBox(0, Qt::Horizontal, i18n("Auto Identify"), generalWidget);
+ autoIdentifyGBox->setMargin(marginHint());
+ QGridLayout* autoIdentifyLayout = new QGridLayout(autoIdentifyGBox->layout(), 1, 2, spacingHint());
+
+ QLabel* botLabel=new QLabel(i18n("Ser&vice:"), autoIdentifyGBox);
+ QWhatsThis::add(botLabel,i18n("Service name can be <b><i>nickserv</i></b> or a network dependant name like <b><i>nickserv@services.dal.net</i></b>"));
+ m_botEdit = new KLineEdit(autoIdentifyGBox);
+ botLabel->setBuddy(m_botEdit);
+
+ QLabel* passwordLabel = new QLabel(i18n("Pa&ssword:"), autoIdentifyGBox);
+ m_passwordEdit = new KLineEdit(autoIdentifyGBox);
+ m_passwordEdit->setEchoMode(QLineEdit::Password);
+ passwordLabel->setBuddy(m_passwordEdit);
+
+ autoIdentifyLayout->addWidget(botLabel, 0, 0);
+ autoIdentifyLayout->addWidget(m_botEdit, 0, 1);
+ autoIdentifyLayout->addWidget(passwordLabel, 0, 2);
+ autoIdentifyLayout->addWidget(m_passwordEdit, 0, 3);
+
+ int row = 0;
+ generalLayout->addWidget(realNameLabel, row, 0);
+ generalLayout->addWidget(m_realNameEdit, row, 1);
+ row++;
+ generalLayout->addMultiCellWidget(nicknameGBox, row, row, 0, 1);
+ row++;
+ generalLayout->addMultiCellWidget(autoIdentifyGBox, row, row, 0, 1);
+
+ QWidget* awayWidget = new QWidget(tabWidget);
+ tabWidget->addTab(awayWidget, i18n("Tab name", "Away"));
+ QGridLayout* awayLayout = new QGridLayout(awayWidget, 1, 2, marginHint(), spacingHint());
+
+ m_insertRememberLineOnAwayChBox = new QCheckBox(i18n("Mark the last position in chat windows when going away"), awayWidget);
+ QWhatsThis::add(m_insertRememberLineOnAwayChBox, i18n("If you check this box, whenever you perform an <b>/away</b> command, a horizontal line will appear in the channel, marking the point where you went away. Other IRC users do not see this horizontal line."));
+
+ QLabel* awayNickLabel = new QLabel(i18n("Away nickname:"), awayWidget);
+ m_awayNickEdit = new KLineEdit(awayWidget);
+ QWhatsThis::add(m_awayNickEdit, i18n("Enter a nickname that indicates you are away. Whenever you perform an <b>/away msg</b> command in any channel joined with this Identity, Konversation will automatically change your nickname to the Away nickname. Other users will be able to tell you are away from your computer. Whenever you perform an <b>/away</b> command in any channel in which you are away, Konversation will automatically change your nickname back to the original. If you do not wish to automatically change your nickname when going away, leave blank."));
+ awayNickLabel->setBuddy(m_awayNickEdit);
+
+ m_automaticAwayGBox = new QGroupBox(i18n("Automatic Away"), awayWidget);
+ m_automaticAwayGBox->setCheckable(true);
+ m_automaticAwayGBox->setColumnLayout(0, Qt::Horizontal);
+ m_automaticAwayGBox->setMargin(marginHint());
+ QGridLayout* automaticAwayLayout = new QGridLayout(m_automaticAwayGBox->layout(), 1, 2, spacingHint());
+
+ QWhatsThis::add(m_automaticAwayGBox, i18n("If you check this box, Konversation will automatically set all connections using this Identity away when the screensaver starts or after a period of user inactivity configured below."));
+
+ QLabel* autoAwayLabel1 = new QLabel(i18n("Set away after"), m_automaticAwayGBox);
+ m_awayInactivitySpin = new QSpinBox(1, 999, 1, m_automaticAwayGBox);
+ m_awayInactivitySpin->setSuffix(i18n(" minutes"));
+ QLabel* autoAwayLabel2 = new QLabel(i18n("of user inactivity"), m_automaticAwayGBox);
+ autoAwayLabel1->setBuddy(m_awayInactivitySpin);
+ autoAwayLabel2->setBuddy(m_awayInactivitySpin);
+ m_automaticUnawayChBox = new QCheckBox(i18n("Automatically return on activity"), m_automaticAwayGBox);
+ QWhatsThis::add(m_automaticUnawayChBox, i18n("If you check this box, Konversation will automatically cancel away for all connections using this Identity when the screensaver stops or new user activity is detected."));
+
+ connect(m_automaticAwayGBox, SIGNAL(toggled(bool)), autoAwayLabel1, SLOT(setEnabled(bool)));
+ connect(m_automaticAwayGBox, SIGNAL(toggled(bool)), autoAwayLabel2, SLOT(setEnabled(bool)));
+ connect(m_automaticAwayGBox, SIGNAL(toggled(bool)), m_awayInactivitySpin, SLOT(setEnabled(bool)));
+ connect(m_automaticAwayGBox, SIGNAL(toggled(bool)), m_automaticUnawayChBox, SLOT(setEnabled(bool)));
+
+ row = 0;
+ automaticAwayLayout->addWidget(autoAwayLabel1, row, 0);
+ automaticAwayLayout->addWidget(m_awayInactivitySpin, row, 1);
+ automaticAwayLayout->addWidget(autoAwayLabel2, row, 2);
+ QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
+ automaticAwayLayout->addItem(spacer, row, 3);
+ row++;
+ automaticAwayLayout->addMultiCellWidget(m_automaticUnawayChBox, row, row, 0, 3);
+
+ m_awayMessageGBox = new QGroupBox(i18n("Away Messages"), awayWidget);
+ m_awayMessageGBox->setCheckable(true);
+ m_awayMessageGBox->setColumnLayout(0, Qt::Horizontal);
+ m_awayMessageGBox->setMargin(marginHint());
+ QGridLayout* messagesLayout = new QGridLayout(m_awayMessageGBox->layout(), 1, 2, spacingHint());
+
+ QWhatsThis::add(m_awayMessageGBox, i18n("If you check this box, Konversation will automatically send the Away message to all channels joined with this Identity. <b>%s</b> is replaced with <b>msg</b>. Whenever you perform an <b>/away</b> command, the Return message will be displayed in all channels joined with this Identity."));
+
+ QLabel* awayLabel = new QLabel(i18n("Away &message:"), m_awayMessageGBox);
+ m_awayEdit = new KLineEdit(m_awayMessageGBox);
+ awayLabel->setBuddy(m_awayEdit);
+
+ QLabel* unAwayLabel = new QLabel(i18n("Re&turn message:"), m_awayMessageGBox);
+ m_unAwayEdit = new KLineEdit(m_awayMessageGBox);
+ unAwayLabel->setBuddy(m_unAwayEdit);
+
+ connect(m_awayMessageGBox, SIGNAL(toggled(bool)), awayLabel, SLOT(setEnabled(bool)));
+ connect(m_awayMessageGBox, SIGNAL(toggled(bool)), m_awayEdit, SLOT(setEnabled(bool)));
+ connect(m_awayMessageGBox, SIGNAL(toggled(bool)), unAwayLabel, SLOT(setEnabled(bool)));
+ connect(m_awayMessageGBox, SIGNAL(toggled(bool)), m_unAwayEdit, SLOT(setEnabled(bool)));
+
+ row = 0;
+ messagesLayout->addWidget(awayLabel, row, 0);
+ messagesLayout->addWidget(m_awayEdit, row, 1);
+ row++;
+ messagesLayout->addWidget(unAwayLabel, row, 0);
+ messagesLayout->addWidget(m_unAwayEdit, row, 1);
+
+ row = 0;
+ awayLayout->addMultiCellWidget(m_insertRememberLineOnAwayChBox, row, row, 0, 1);
+ row++;
+ awayLayout->addWidget(awayNickLabel, row, 0);
+ awayLayout->addWidget(m_awayNickEdit, row, 1);
+ row++;
+ awayLayout->addMultiCellWidget(m_automaticAwayGBox, row, row, 0, 1);
+ row++;
+ awayLayout->addMultiCellWidget(m_awayMessageGBox, row, row, 0, 1);
+ row++;
+ awayLayout->setRowStretch(row, 10);
+
+ QWidget* advancedWidget = new QWidget(tabWidget);
+ tabWidget->addTab(advancedWidget, i18n("Advanced"));
+ QGridLayout* advancedLayout = new QGridLayout(advancedWidget, 1, 2, marginHint(), spacingHint());
+
+ QLabel* commandLabel = new QLabel(i18n("&Pre-shell command:"), advancedWidget);
+ m_sCommandEdit = new KLineEdit(advancedWidget);
+ QWhatsThis::add(m_sCommandEdit,i18n("Here you can enter a command to be executed before connection to server starts<br>If you have multiple servers in this identity this command will be executed for each server"));
+ commandLabel->setBuddy(m_sCommandEdit);
+
+ QLabel* loginLabel = new QLabel(i18n("I&dent:"), advancedWidget);
+ m_loginEdit = new KLineEdit(advancedWidget);
+ QWhatsThis::add(m_loginEdit, i18n("When you connect, many servers query your computer for an IDENT response. If you computer is not running an IDENT server, this response is sent by Konversation. No spaces are allowed."));
+ loginLabel->setBuddy(m_loginEdit);
+
+ // encoding combo box
+ QLabel* codecLabel = new QLabel(i18n("&Encoding:"), advancedWidget);
+ m_codecCBox = new KComboBox(advancedWidget,"codec_combo_box");
+ QWhatsThis::add(m_codecCBox, i18n("This setting affects how characters you type are encoded for sending to the server. It also affects how messages are displayed. When you first open Konversation, it automatically retrieves this setting from the operating system. If you seem to be having trouble seeing other user's messages correctly, try changing this setting."));
+ codecLabel->setBuddy(m_codecCBox);
+ // add encodings to combo box
+ m_codecCBox->insertStringList(Konversation::IRCCharsets::self()->availableEncodingDescriptiveNames());
+
+ QLabel* quitLabel = new QLabel(i18n("&Quit reason:"), advancedWidget);
+ m_quitEdit = new KLineEdit(advancedWidget);
+ QWhatsThis::add(m_quitEdit, i18n("Whenever you leave a server, this message is shown to others."));
+ quitLabel->setBuddy(m_quitEdit);
+
+ QLabel* partLabel = new QLabel(i18n("&Part reason:"), advancedWidget);
+ m_partEdit = new KLineEdit(advancedWidget);
+ QWhatsThis::add(m_partEdit, i18n("Whenever you leave a channel, this message is sent to the channel."));
+ partLabel->setBuddy(m_partEdit);
+
+ QLabel* kickLabel = new QLabel(i18n("&Kick reason:"), advancedWidget);
+ m_kickEdit = new KLineEdit(advancedWidget);
+ QWhatsThis::add(m_kickEdit, i18n("Whenever you are kicked from a channel (usually by an IRC operator), this message is sent to the channel."));
+ kickLabel->setBuddy(m_kickEdit);
+
+ row = 0;
+ advancedLayout->addWidget(commandLabel,row,0);
+ advancedLayout->addWidget(m_sCommandEdit, row, 1);
+ row++;
+ advancedLayout->addWidget(codecLabel,row,0);
+ advancedLayout->addWidget(m_codecCBox, row, 1);
+ row++;
+ advancedLayout->addWidget(loginLabel,row,0);
+ advancedLayout->addWidget(m_loginEdit, row, 1);
+ row++;
+ advancedLayout->addWidget(quitLabel, row, 0);
+ advancedLayout->addWidget(m_quitEdit, row, 1);
+ row++;
+ advancedLayout->addWidget(partLabel, row, 0);
+ advancedLayout->addWidget(m_partEdit, row, 1);
+ row++;
+ advancedLayout->addWidget(kickLabel, row, 0);
+ advancedLayout->addWidget(m_kickEdit, row, 1);
+ row++;
+ advancedLayout->setRowStretch(row, 10);
+
+ row = 0;
+ mainLayout->addWidget(identityLabel, row, 0);
+ mainLayout->addMultiCellWidget(m_identityCBox, row, row, 1, 2);
+ mainLayout->addWidget(newBtn, row, 3);
+ mainLayout->addWidget(copyBtn, row, 4);
+ mainLayout->addWidget(m_editBtn, row, 5);
+ mainLayout->addWidget(m_delBtn, row, 6);
+ mainLayout->setColStretch(1, 10);
+ row++;
+ mainLayout->addMultiCellWidget(tabWidget, row, row, 0, 6);
+
+ // set values for the widgets
+ updateIdentity(0);
+
+ // Set up signals / slots for identity page
+ connect(m_identityCBox, SIGNAL(activated(int)), this, SLOT(updateIdentity(int)));
+
+ setButtonOK(KGuiItem(i18n("&OK"), "button_ok", i18n("Change identity information")));
+ setButtonCancel(KGuiItem(i18n("&Cancel"), "button_cancel", i18n("Discards all changes made")));
+
+ AwayManager* awayManager = static_cast<KonversationApplication*>(kapp)->getAwayManager();
+ connect(this, SIGNAL(identitiesChanged()), awayManager, SLOT(identitiesChanged()));
+ }
+
+ IdentityDialog::~IdentityDialog()
+ {
+ }
+
+ void IdentityDialog::updateIdentity(int index)
+ {
+ if(m_currentIdentity && (m_nicknameLBox->count() == 0))
+ {
+ KMessageBox::error(this, i18n("You must add at least one nick to the identity."));
+ m_identityCBox->setCurrentText(m_currentIdentity->getName());
+ return;
+ }
+
+ if (isShown() && m_currentIdentity && m_realNameEdit->text().isEmpty())
+ {
+ KMessageBox::error(this, i18n("Please enter a real name."));
+ m_identityCBox->setCurrentText(m_currentIdentity->getName());
+ return;
+ }
+
+ refreshCurrentIdentity();
+
+ m_currentIdentity = m_identityList[index];
+
+ m_realNameEdit->setText(m_currentIdentity->getRealName());
+ m_nicknameLBox->clear();
+ m_nicknameLBox->insertStringList(m_currentIdentity->getNicknameList());
+ m_botEdit->setText(m_currentIdentity->getBot());
+ m_passwordEdit->setText(m_currentIdentity->getPassword());
+
+ m_insertRememberLineOnAwayChBox->setChecked(m_currentIdentity->getInsertRememberLineOnAway());
+ m_awayNickEdit->setText(m_currentIdentity->getAwayNick());
+ m_awayMessageGBox->setChecked(m_currentIdentity->getShowAwayMessage());
+ m_awayEdit->setText(m_currentIdentity->getAwayMessage());
+ m_unAwayEdit->setText(m_currentIdentity->getReturnMessage());
+ m_automaticAwayGBox->setChecked(m_currentIdentity->getAutomaticAway());
+ m_awayInactivitySpin->setValue(m_currentIdentity->getAwayInactivity());
+ m_automaticUnawayChBox->setChecked(m_currentIdentity->getAutomaticUnaway());
+
+ m_sCommandEdit->setText(m_currentIdentity->getShellCommand());
+ m_codecCBox->setCurrentItem(Konversation::IRCCharsets::self()->shortNameToIndex(m_currentIdentity->getCodecName()));
+ m_loginEdit->setText(m_currentIdentity->getIdent());
+ m_quitEdit->setText(m_currentIdentity->getQuitReason());
+ m_partEdit->setText(m_currentIdentity->getPartReason());
+ m_kickEdit->setText(m_currentIdentity->getKickReason());
+
+ if(index == 0)
+ {
+ m_editBtn->setEnabled(false);
+ m_delBtn->setEnabled(false);
+ }
+ else
+ {
+ m_editBtn->setEnabled(true);
+ m_delBtn->setEnabled(true);
+ }
+ }
+
+ void IdentityDialog::addNickname()
+ {
+ bool ok = false;
+ QString txt = KInputDialog::getText(i18n("Add Nickname"), i18n("Nickname:"), QString(), &ok, this);
+
+ if(ok && !txt.isEmpty())
+ {
+ m_nicknameLBox->insertItem(txt);
+ updateButtons();
+ }
+ }
+
+ void IdentityDialog::editNickname()
+ {
+ bool ok = false;
+ QString txt = KInputDialog::getText(i18n("Edit Nickname"), i18n("Nickname:"), m_nicknameLBox->currentText(), &ok, this);
+
+ if(ok && !txt.isEmpty())
+ {
+ m_nicknameLBox->changeItem(txt, m_nicknameLBox->currentItem());
+ }
+ }
+
+ void IdentityDialog::deleteNickname()
+ {
+ m_nicknameLBox->removeItem(m_nicknameLBox->currentItem());
+ updateButtons();
+ }
+
+ void IdentityDialog::updateButtons()
+ {
+ m_changeNicknameBtn->setEnabled(m_nicknameLBox->selectedItem());
+ m_removeNicknameBtn->setEnabled(m_nicknameLBox->selectedItem());
+
+ m_upNicknameBtn->setEnabled(m_nicknameLBox->selectedItem() && m_nicknameLBox->count()>1
+ && m_nicknameLBox->currentItem()>0);
+
+ m_downNicknameBtn->setEnabled(m_nicknameLBox->selectedItem() && m_nicknameLBox->count()>1
+ && m_nicknameLBox->currentItem()<m_nicknameLBox->numRows()-1 );
+
+ }
+
+ void IdentityDialog::moveNicknameUp()
+ {
+ uint current = m_nicknameLBox->currentItem();
+
+ if(current > 0)
+ {
+ QString txt = m_nicknameLBox->text(current);
+ m_nicknameLBox->removeItem(current);
+ m_nicknameLBox->insertItem(txt, current - 1);
+ m_nicknameLBox->setCurrentItem(current - 1);
+ }
+
+ updateButtons();
+ }
+
+ void IdentityDialog::moveNicknameDown()
+ {
+ uint current = m_nicknameLBox->currentItem();
+
+ if(current < (m_nicknameLBox->count() - 1))
+ {
+ QString txt = m_nicknameLBox->text(current);
+ m_nicknameLBox->removeItem(current);
+ m_nicknameLBox->insertItem(txt, current + 1);
+ m_nicknameLBox->setCurrentItem(current + 1);
+ }
+
+ updateButtons();
+ }
+
+ void IdentityDialog::refreshCurrentIdentity()
+ {
+ if(!m_currentIdentity)
+ {
+ return;
+ }
+
+ m_currentIdentity->setRealName(m_realNameEdit->text());
+ QStringList nicks;
+
+ for(unsigned int i = 0; i < m_nicknameLBox->count(); ++i)
+ {
+ nicks.append(m_nicknameLBox->text(i));
+ }
+
+ m_currentIdentity->setNicknameList(nicks);
+ m_currentIdentity->setBot(m_botEdit->text());
+ m_currentIdentity->setPassword(m_passwordEdit->text());
+
+ m_currentIdentity->setInsertRememberLineOnAway(m_insertRememberLineOnAwayChBox->isChecked());
+ m_currentIdentity->setAwayNick(m_awayNickEdit->text());
+ m_currentIdentity->setShowAwayMessage(m_awayMessageGBox->isChecked());
+ m_currentIdentity->setAwayMessage(m_awayEdit->text());
+ m_currentIdentity->setReturnMessage(m_unAwayEdit->text());
+ m_currentIdentity->setAutomaticAway(m_automaticAwayGBox->isChecked());
+ m_currentIdentity->setAwayInactivity(m_awayInactivitySpin->value());
+ m_currentIdentity->setAutomaticUnaway(m_automaticUnawayChBox->isChecked());
+
+ m_currentIdentity->setShellCommand(m_sCommandEdit->text());
+ m_currentIdentity->setCodecName(Konversation::IRCCharsets::self()->availableEncodingShortNames()[m_codecCBox->currentItem()]);
+ m_currentIdentity->setIdent(m_loginEdit->text());
+ m_currentIdentity->setQuitReason(m_quitEdit->text());
+ m_currentIdentity->setPartReason(m_partEdit->text());
+ m_currentIdentity->setKickReason(m_kickEdit->text());
+ }
+
+ void IdentityDialog::slotOk()
+ {
+ if(m_nicknameLBox->count() == 0)
+ {
+ KMessageBox::error(this, i18n("You must add at least one nick to the identity."));
+ m_identityCBox->setCurrentText(m_currentIdentity->getName());
+ return;
+ }
+
+ if(m_realNameEdit->text().isEmpty())
+ {
+ KMessageBox::error(this, i18n("Please enter a real name."));
+ m_identityCBox->setCurrentText(m_currentIdentity->getName());
+ return;
+ }
+
+ refreshCurrentIdentity();
+ Preferences::setIdentityList(m_identityList);
+ static_cast<KonversationApplication*>(kapp)->saveOptions(true);
+ emit identitiesChanged();
+ accept();
+ }
+
+ void IdentityDialog::newIdentity()
+ {
+ bool ok = false;
+ QString txt = KInputDialog::getText(i18n("Add Identity"), i18n("Identity name:"), QString(), &ok, this);
+
+ if(ok && !txt.isEmpty())
+ {
+ KUser user(KUser::UseRealUserID);
+ IdentityPtr identity = new Identity;
+ identity->setName(txt);
+ identity->setIdent(user.loginName());
+ m_identityList.append(identity);
+ m_identityCBox->insertItem(txt);
+ m_identityCBox->setCurrentItem(m_identityCBox->count() - 1);
+ updateIdentity(m_identityCBox->currentItem());
+ }
+ else if(ok && txt.isEmpty())
+ {
+ KMessageBox::error(this, i18n("You need to give the identity a name."));
+ newIdentity();
+ }
+ updateButtons();
+ }
+
+ void IdentityDialog::renameIdentity()
+ {
+ bool ok = false;
+ QString currentTxt = m_identityCBox->currentText();
+ QString txt = KInputDialog::getText(i18n("Rename Identity"), i18n("Identity name:"), currentTxt, &ok, this);
+
+ if(ok && !txt.isEmpty())
+ {
+ m_currentIdentity->setName(txt);
+ m_identityCBox->changeItem(txt, m_identityCBox->currentItem());
+ }
+ else if(ok && txt.isEmpty())
+ {
+ KMessageBox::error(this, i18n("You need to give the identity a name."));
+ renameIdentity();
+ }
+ }
+
+ void IdentityDialog::deleteIdentity()
+ {
+ int current = m_identityCBox->currentItem();
+
+ if(current <= 0)
+ {
+ return;
+ }
+
+ ServerGroupList serverGroups = Preferences::serverGroupList();
+ ServerGroupList::iterator it = serverGroups.begin();
+ bool found = false;
+
+ while((it != serverGroups.end()) && !found)
+ {
+ if((*it)->identityId() == m_currentIdentity->id())
+ {
+ found = true;
+ }
+
+ ++it;
+ }
+
+ QString warningTxt;
+
+ if(found)
+ {
+ warningTxt = i18n("This identity is in use, if you remove it the network settings using it will"
+ " fall back to the default identity. Should it be deleted anyway?");
+ }
+ else
+ {
+ warningTxt = i18n("Are you sure you want to delete all information for this identity?");
+ }
+
+ if(KMessageBox::warningContinueCancel(this, warningTxt, i18n("Delete Identity"),
+ KGuiItem(i18n("Delete"), "editdelete")) == KMessageBox::Continue)
+ {
+ m_identityCBox->removeItem(current);
+ m_identityList.remove(m_currentIdentity);
+ m_currentIdentity = 0;
+ updateIdentity(m_identityCBox->currentItem());
+ updateButtons();
+ }
+ }
+
+ void IdentityDialog::copyIdentity()
+ {
+ bool ok = false;
+ QString currentTxt = m_identityCBox->currentText();
+ QString txt = KInputDialog::getText(i18n("Duplicate Identity"), i18n("Identity name:"), currentTxt, &ok, this);
+
+ if(ok && !txt.isEmpty())
+ {
+ IdentityPtr identity = new Identity;
+ identity->copy(*m_currentIdentity);
+ identity->setName(txt);
+ m_identityList.append(identity);
+ m_identityCBox->insertItem(txt);
+ m_identityCBox->setCurrentItem(m_identityCBox->count() - 1);
+ updateIdentity(m_identityCBox->currentItem());
+ }
+ else if(ok && txt.isEmpty())
+ {
+ KMessageBox::error(this, i18n("You need to give the identity a name."));
+ renameIdentity();
+ }
+ }
+
+ void IdentityDialog::setCurrentIdentity(int index)
+ {
+ if (index >= m_identityCBox->count())
+ index = 0;
+
+ m_identityCBox->setCurrentItem(index);
+ updateIdentity(index);
+ }
+
+ IdentityPtr IdentityDialog::setCurrentIdentity(IdentityPtr identity)
+ {
+ int index = Preferences::identityList().findIndex(identity);
+ setCurrentIdentity(index);
+
+ return m_currentIdentity;
+ }
+
+ IdentityPtr IdentityDialog::currentIdentity() const
+ {
+ return m_currentIdentity;
+ }
+}
+
+#include "identitydialog.moc"
diff --git a/konversation/src/identitydialog.h b/konversation/src/identitydialog.h
new file mode 100644
index 0000000..e58438f
--- /dev/null
+++ b/konversation/src/identitydialog.h
@@ -0,0 +1,99 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#ifndef KONVERSATIONIDENTITYDIALOG_H
+#define KONVERSATIONIDENTITYDIALOG_H
+
+#include "identity.h"
+
+#include <kdialogbase.h>
+
+
+class KComboBox;
+class KLineEdit;
+class QCheckBox;
+class QSpinBox;
+class QListBox;
+class QGroupBox;
+class QToolButton;
+
+namespace Konversation
+{
+
+ class IdentityDialog : public KDialogBase
+ {
+ Q_OBJECT
+ public:
+ explicit IdentityDialog(QWidget *parent = 0, const char *name = 0);
+ ~IdentityDialog();
+ void setCurrentIdentity(int index);
+ IdentityPtr setCurrentIdentity(IdentityPtr identity);
+ IdentityPtr currentIdentity() const;
+
+
+ signals:
+ void identitiesChanged();
+
+
+ protected slots:
+ void updateIdentity(int index);
+
+ void addNickname();
+ void editNickname();
+ void deleteNickname();
+ void updateButtons();
+ void moveNicknameUp();
+ void moveNicknameDown();
+
+ void refreshCurrentIdentity();
+
+ void slotOk();
+
+ void newIdentity();
+ void renameIdentity();
+ void deleteIdentity();
+ void copyIdentity();
+
+
+ private:
+ KComboBox* m_identityCBox;
+ KLineEdit* m_realNameEdit;
+ KLineEdit* m_sCommandEdit;
+ KLineEdit* m_loginEdit;
+ KComboBox* m_codecCBox;
+ KLineEdit* m_botEdit;
+ KLineEdit* m_passwordEdit;
+ KLineEdit* m_quitEdit;
+ KLineEdit* m_partEdit;
+ KLineEdit* m_kickEdit;
+ KLineEdit* m_awayEdit;
+ KLineEdit* m_unAwayEdit;
+ KLineEdit* m_awayNickEdit;
+ QCheckBox* m_insertRememberLineOnAwayChBox;
+ QListBox* m_nicknameLBox;
+ QGroupBox* m_awayMessageGBox;
+ QGroupBox* m_automaticAwayGBox;
+ QSpinBox* m_awayInactivitySpin;
+ QCheckBox* m_automaticUnawayChBox;
+ QToolButton* m_editBtn;
+ QToolButton* m_delBtn;
+ QToolButton* m_upNicknameBtn;
+ QToolButton* m_downNicknameBtn;
+ QPushButton* m_addNicknameBtn;
+ QPushButton* m_changeNicknameBtn;
+ QPushButton* m_removeNicknameBtn;
+
+ IdentityList m_identityList;
+ IdentityPtr m_currentIdentity;
+ };
+
+}
+#endif
diff --git a/konversation/src/ignore.cpp b/konversation/src/ignore.cpp
new file mode 100644
index 0000000..8ff8304
--- /dev/null
+++ b/konversation/src/ignore.cpp
@@ -0,0 +1,30 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Mon Jun 24 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "ignore.h"
+
+
+Ignore::Ignore(const QString &newName,int newFlags)
+{
+ setFlags(newFlags);
+ setName(newName);
+}
+
+Ignore::~Ignore()
+{
+}
+
+void Ignore::setName(const QString &newName) { name=newName; }
+void Ignore::setFlags(int newFlags) { flags=newFlags; }
+QString Ignore::getName() { return name; }
+int Ignore::getFlags() { return flags; }
diff --git a/konversation/src/ignore.h b/konversation/src/ignore.h
new file mode 100644
index 0000000..ec64c9c
--- /dev/null
+++ b/konversation/src/ignore.h
@@ -0,0 +1,46 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Mon Jun 24 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef IGNORE_H
+#define IGNORE_H
+
+#include <qstring.h>
+
+
+class Ignore
+{
+ public:
+ enum Type
+ {
+ Channel=1,
+ Query=2,
+ Notice=4,
+ CTCP=8,
+ DCC=16,
+ Exception=32,
+ All=31
+ };
+
+ Ignore(const QString &name,int flags);
+ ~Ignore();
+
+ void setName(const QString &newName);
+ void setFlags(int newFlags);
+ QString getName();
+ int getFlags();
+
+ protected:
+ QString name;
+ int flags;
+};
+#endif
diff --git a/konversation/src/ignore_preferences.cpp b/konversation/src/ignore_preferences.cpp
new file mode 100644
index 0000000..825cfb7
--- /dev/null
+++ b/konversation/src/ignore_preferences.cpp
@@ -0,0 +1,219 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#include "ignore_preferences.h"
+#include "ignorelistviewitem.h"
+#include "ignore.h"
+#include "preferences.h"
+
+#include <klocale.h>
+#include <klistview.h>
+#include <qlistview.h>
+#include <qlineedit.h>
+#include <qheader.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kpushbutton.h>
+#include <qcheckbox.h>
+
+
+Ignore_Config::Ignore_Config( QWidget* parent, const char* name, WFlags fl )
+ : Ignore_ConfigUI( parent, name, fl )
+{
+ connect(newButton,SIGNAL(clicked()),
+ this,SLOT(newIgnore()));
+ connect(removeButton,SIGNAL(clicked()),
+ this,SLOT(removeIgnore()));
+ connect(removeAllButton,SIGNAL(clicked()),
+ this,SLOT(removeAllIgnore()));
+ connect(ignoreListView,SIGNAL(selectionChanged(QListViewItem*)),
+ this,SLOT(select(QListViewItem*)));
+ connect(chkChannel, SIGNAL(clicked()), this, SLOT(flagCheckboxChanged()));
+ connect(chkQuery, SIGNAL(clicked()), this, SLOT(flagCheckboxChanged()));
+ connect(chkNotice, SIGNAL(clicked()), this, SLOT(flagCheckboxChanged()));
+ connect(chkCTCP, SIGNAL(clicked()), this, SLOT(flagCheckboxChanged()));
+ connect(chkDCC, SIGNAL(clicked()), this, SLOT(flagCheckboxChanged()));
+ connect(txtPattern, SIGNAL(textChanged(const QString &)), this, SLOT(flagCheckboxChanged()));
+// connect(chkException, SIGNAL(clicked()), this, SLOT(flagCheckboxChanged()));
+ loadSettings();
+
+ ignoreListView->header()->setMovingEnabled(false);
+}
+
+Ignore_Config::~Ignore_Config()
+{
+
+}
+
+void Ignore_Config::newIgnore()
+{
+ ignoreListView->setSelected(new IgnoreListViewItem(ignoreListView,
+ "new!new@new.new",
+ Ignore::Channel |
+ Ignore::Query |
+ Ignore::Notice |
+ Ignore::CTCP |
+ Ignore::DCC), true);
+ txtPattern->setFocus();
+ txtPattern->selectAll();
+
+ updateEnabledness();
+ emit modified();
+}
+void Ignore_Config::removeAllIgnore()
+{
+ ignoreListView->clear();
+ updateEnabledness();
+ emit modified();
+}
+void Ignore_Config::removeIgnore()
+{
+ delete ignoreListView->selectedItem();
+ updateEnabledness();
+ emit modified();
+}
+
+QPtrList<Ignore> Ignore_Config::getIgnoreList()
+{
+ QPtrList<Ignore> newList;
+
+ IgnoreListViewItem* item=static_cast<IgnoreListViewItem*>(ignoreListView->firstChild());
+ while(item)
+ {
+ Ignore* newItem=new Ignore(item->text(0),item->getFlags());
+ newList.append(newItem);
+ item=item->itemBelow();
+ }
+
+ return newList;
+}
+
+// returns the currently visible ignore list as QStringList to make comparing easy
+QStringList Ignore_Config::currentIgnoreList()
+{
+ QStringList newList;
+
+ IgnoreListViewItem* item=static_cast<IgnoreListViewItem*>(ignoreListView->firstChild());
+ while(item)
+ {
+ newList.append(item->text(0)+' '+item->getFlags());
+ item=item->itemBelow();
+ }
+
+ return newList;
+}
+
+// checks if the currently visible ignore list differs from the currently saved one
+bool Ignore_Config::hasChanged()
+{
+ return(m_oldIgnoreList!=currentIgnoreList());
+}
+
+void Ignore_Config::restorePageToDefaults()
+{
+ if(ignoreListView->childCount() != 0) {
+ ignoreListView->clear();
+ updateEnabledness();
+ emit modified();
+ }
+}
+void Ignore_Config::saveSettings()
+{
+ Preferences::setIgnoreList(getIgnoreList());
+ // remember the list for hasChanged()
+ m_oldIgnoreList=currentIgnoreList();
+}
+
+void Ignore_Config::loadSettings()
+{
+ QPtrList<Ignore> ignoreList=Preferences::ignoreList();
+ // Insert Ignore items backwards to get them sorted properly
+ Ignore* item=ignoreList.last();
+ ignoreListView->clear();
+ while(item)
+ {
+ new IgnoreListViewItem(ignoreListView,item->getName(),item->getFlags());
+ item=ignoreList.prev();
+ }
+ // remember the list for hasChanged()
+ m_oldIgnoreList=currentIgnoreList();
+ updateEnabledness();
+}
+
+void Ignore_Config::updateEnabledness()
+{
+ IgnoreListViewItem* selectedItem=static_cast<IgnoreListViewItem*>(ignoreListView->selectedItem());
+
+ chkChannel->setEnabled(selectedItem != NULL);
+ chkQuery->setEnabled(selectedItem != NULL);
+ chkNotice->setEnabled(selectedItem != NULL);
+ chkCTCP->setEnabled(selectedItem != NULL);
+ chkDCC->setEnabled(selectedItem != NULL);
+// chkExceptions->setEnabled(selectedItem != NULL);
+ txtPattern->setEnabled(selectedItem != NULL);
+ removeButton->setEnabled(selectedItem != NULL);
+ removeAllButton->setEnabled(ignoreListView->childCount() > 0);
+
+}
+
+void Ignore_Config::select(QListViewItem* item)
+{
+ updateEnabledness();
+ // FIXME: Cast to IgnoreListViewItem, maybe derive from KListView some day
+ IgnoreListViewItem* selectedItem=static_cast<IgnoreListViewItem*>(item);
+
+ if(selectedItem)
+ {
+ int flags = selectedItem->getFlags();
+ chkChannel->setChecked(flags & Ignore::Channel);
+ chkQuery->setChecked(flags & Ignore::Query);
+ chkNotice->setChecked(flags & Ignore::Notice);
+ chkCTCP->setChecked(flags & Ignore::CTCP);
+ chkDCC->setChecked(flags & Ignore::DCC);
+ txtPattern->blockSignals(true);
+ txtPattern->setText(selectedItem->getName());
+ txtPattern->blockSignals(false);
+
+// chkExceptions->setChecked(flags & Ignore::Exception) ;
+ }
+}
+
+void Ignore_Config::flagCheckboxChanged()
+{
+ int flags = 0;
+ if(chkChannel->isChecked()) flags |= Ignore::Channel;
+ if(chkQuery->isChecked()) flags |= Ignore::Query;
+ if(chkNotice->isChecked()) flags |= Ignore::Notice;
+ if(chkCTCP->isChecked()) flags |= Ignore::CTCP;
+ if(chkDCC->isChecked()) flags |= Ignore::DCC;
+
+// if(chkExceptions->isChecked()) flags |= Ignore::Exceptions;
+ IgnoreListViewItem* selectedItem=static_cast<IgnoreListViewItem*>(ignoreListView->selectedItem());
+ if(selectedItem) {
+ selectedItem->setFlags(flags);
+ selectedItem->setName(txtPattern->text());
+ }
+ emit modified();
+}
+
+/*
+ * Sets the strings of the subwidgets using the current
+ * language.
+ */
+void Ignore_Config::languageChange()
+{
+ loadSettings();
+}
+
+#include "ignore_preferences.moc"
diff --git a/konversation/src/ignore_preferences.h b/konversation/src/ignore_preferences.h
new file mode 100644
index 0000000..2b90bce
--- /dev/null
+++ b/konversation/src/ignore_preferences.h
@@ -0,0 +1,58 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef IGNORE_CONFIG_H
+#define IGNORE_CONFIG_H
+
+#include "ignore_preferencesui.h"
+#include "konvisettingspage.h"
+
+#include <qptrlist.h>
+
+
+class Ignore;
+class Ignore_Config : public Ignore_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit Ignore_Config( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ ~Ignore_Config();
+ QString flagNames;
+
+ virtual void restorePageToDefaults();
+ virtual void saveSettings();
+ virtual void loadSettings();
+
+ virtual bool hasChanged();
+
+ private:
+ QStringList m_oldIgnoreList;
+
+ QStringList currentIgnoreList(); // in hasChanged() format
+ QPtrList<Ignore> getIgnoreList(); // in prefs format
+ void updateEnabledness();
+
+ public slots:
+ virtual void languageChange();
+
+ protected slots:
+ void newIgnore();
+ void removeIgnore();
+ void flagCheckboxChanged();
+ void select(QListViewItem* item);
+ void removeAllIgnore();
+ signals:
+ void modified();
+};
+
+#endif // IGNORE_CONFIG_H
diff --git a/konversation/src/ignore_preferencesui.ui b/konversation/src/ignore_preferencesui.ui
new file mode 100644
index 0000000..bc33198
--- /dev/null
+++ b/konversation/src/ignore_preferencesui.ui
@@ -0,0 +1,219 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Ignore_ConfigUI</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</comment>
+<author>Copyright (C) 2005 Peter Simonsson</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Ignore_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>374</width>
+ <height>310</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>300</width>
+ <height>200</height>
+ </size>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Pattern</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>ignoreListView</cstring>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Pattern:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>txtPattern</cstring>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>grpMessageTypes</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Message Types</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>chkCTCP</cstring>
+ </property>
+ <property name="text">
+ <string>CTCP</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>chkChannel</cstring>
+ </property>
+ <property name="text">
+ <string>Channel</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>chkDCC</cstring>
+ </property>
+ <property name="text">
+ <string>DCC</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>chkNotice</cstring>
+ </property>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>chkQuery</cstring>
+ </property>
+ <property name="text">
+ <string>Query</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="2" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>layout20</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>newButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeAllButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove &amp;All</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>ignoreListView</tabstop>
+ <tabstop>txtPattern</tabstop>
+ <tabstop>chkChannel</tabstop>
+ <tabstop>chkQuery</tabstop>
+ <tabstop>chkNotice</tabstop>
+ <tabstop>chkCTCP</tabstop>
+ <tabstop>chkDCC</tabstop>
+ <tabstop>newButton</tabstop>
+ <tabstop>removeButton</tabstop>
+ <tabstop>removeAllButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/ignorelistviewitem.cpp b/konversation/src/ignorelistviewitem.cpp
new file mode 100644
index 0000000..c7ba20a
--- /dev/null
+++ b/konversation/src/ignorelistviewitem.cpp
@@ -0,0 +1,55 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Die Jun 25 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "ignorelistviewitem.h"
+#include "ignore.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+IgnoreListViewItem::IgnoreListViewItem(QListView* parent,const QString& name,int newFlags):
+KListViewItem(parent,name)
+{
+ setFlags(newFlags);
+}
+
+IgnoreListViewItem::~IgnoreListViewItem()
+{
+}
+
+void IgnoreListViewItem::setFlag(int flag,bool active)
+{
+ if(active) m_flags|=flag;
+ else m_flags &= ~flag; //any bits that are set in flag will cause those bits in flags to be set to 0
+ setFlags(m_flags);
+}
+
+void IgnoreListViewItem::setFlags(int newFlags)
+{
+ m_flags=newFlags;
+
+ QString flagsStr;
+ if(m_flags & Ignore::Channel) flagsStr += i18n("Channel") + ' ';
+ if(m_flags & Ignore::Query) flagsStr += i18n("Query") + ' ';
+ if(m_flags & Ignore::Notice) flagsStr += i18n("Notice") + ' ';
+ if(m_flags & Ignore::CTCP) flagsStr += i18n("CTCP") + ' ';
+ if(m_flags & Ignore::DCC) flagsStr += i18n("DCC") + ' ';
+ if(m_flags & Ignore::Exception) flagsStr += i18n("Exception") + ' ';
+ setText(1,flagsStr);
+}
+
+IgnoreListViewItem* IgnoreListViewItem::itemBelow()
+{
+ return (IgnoreListViewItem*) QListViewItem::itemBelow();
+}
diff --git a/konversation/src/ignorelistviewitem.h b/konversation/src/ignorelistviewitem.h
new file mode 100644
index 0000000..c99242a
--- /dev/null
+++ b/konversation/src/ignorelistviewitem.h
@@ -0,0 +1,39 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Die Jun 25 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef IGNORELISTVIEWITEM_H
+#define IGNORELISTVIEWITEM_H
+
+#include <qstring.h>
+
+#include <klistview.h>
+
+
+class IgnoreListViewItem : public KListViewItem
+{
+ public:
+ IgnoreListViewItem(QListView* parent,const QString& name,int flags);
+ ~IgnoreListViewItem();
+
+ void setFlag(int flag,bool active);
+ bool getFlag(int flag) { return m_flags & flag; };
+ QString getName() { return text(0); };
+ void setName(QString name) { setText(0, name); };
+ int getFlags() { return m_flags; };
+ IgnoreListViewItem* itemBelow();
+
+ void setFlags(int flags);
+ protected:
+ int m_flags;
+};
+#endif
diff --git a/konversation/src/images.cpp b/konversation/src/images.cpp
new file mode 100644
index 0000000..d97f9c7
--- /dev/null
+++ b/konversation/src/images.cpp
@@ -0,0 +1,319 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005-2006 Eike Hein <hein@kde.org>
+*/
+
+#include "images.h"
+#include "common.h"
+#include "konversationapplication.h"
+
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qstringlist.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+
+
+using namespace Konversation;
+
+Images::Images()
+{
+ initializeLeds();
+ initializeNickIcons();
+ initializeKimifaceIcons();
+
+ m_closeIcon = KGlobal::iconLoader()->loadIcon("fileclose",KIcon::Small);
+ m_disabledCloseIcon = KGlobal::iconLoader()->loadIconSet("fileclose",KIcon::Small).pixmap(QIconSet::Small, false);
+}
+
+Images::~Images()
+{
+}
+
+QIconSet Images::getKimproxyAway() const { return kimproxyAway; }
+QIconSet Images::getKimproxyOnline() const { return kimproxyOnline; }
+QIconSet Images::getKimproxyOffline() const { return kimproxyOffline; }
+
+QPixmap Images::getNickIcon(NickPrivilege privilege,bool isAway) const
+{
+ return nickIcons[privilege][isAway?1:0];
+}
+
+void Images::initializeLeds()
+{
+ m_serverColor = "steelblue";
+ m_systemColor = Preferences::tabNotificationsSystemColor();
+ m_msgsColor = Preferences::tabNotificationsMsgsColor();
+ m_privateColor = Preferences::tabNotificationsPrivateColor();
+ m_eventsColor = Preferences::tabNotificationsEventsColor();
+ m_nickColor = Preferences::tabNotificationsNickColor();
+ m_highlightsColor = Preferences::tabNotificationsHighlightsColor();
+
+ // m_serverLedOn = getLed(m_serverColor,true);
+ m_serverLedOff = getLed(m_serverColor,false);
+ m_systemLedOn = getLed(m_systemColor,true);
+ m_systemLedOff = getLed(m_systemColor,false);
+ m_msgsLedOn = getLed(m_msgsColor,true);
+ m_msgsLedOff = getLed(m_msgsColor,false);
+ m_privateLedOn = getLed(m_privateColor,true);
+ m_privateLedOff = getLed(m_privateColor,false);
+ m_eventsLedOn = getLed(m_eventsColor,true);
+ m_nickLedOn = getLed(m_nickColor,true);
+ m_highlightsLedOn = getLed(m_highlightsColor,true);
+}
+
+void Images::initializeKimifaceIcons()
+{
+ kimproxyAway = KGlobal::iconLoader()->loadIconSet("kimproxyaway",KIcon::Small);
+ kimproxyOnline = KGlobal::iconLoader()->loadIconSet("kimproxyonline",KIcon::Small);
+ kimproxyOffline = KGlobal::iconLoader()->loadIconSet("kimproxyoffline",KIcon::Small);
+}
+
+// NickIcons
+
+void Images::initializeNickIcons()
+{
+
+ QString iconTheme = Preferences::iconTheme();
+ QStringList icons = KGlobal::dirs()->findAllResources("data","konversation/themes/"+iconTheme+"/*.png");
+
+ if( icons.count() < 7 ) // Sanity
+ icons = KGlobal::dirs()->findAllResources("data","konversation/themes/default/*.png");
+
+ icons.sort();
+ QStringList::ConstIterator it = icons.begin();
+
+ /* The list is sorted alphabetically. */
+
+ QPixmap elementAdmin(*it);
+ ++it;
+ QPixmap elementAway(*it);
+ ++it;
+ QPixmap elementHalfOp(*it);
+ ++it;
+ QPixmap elementNormal(*it);
+ ++it;
+ QPixmap elementOp(*it);
+ ++it;
+ QPixmap elementOwner(*it);
+ ++it;
+ QPixmap elementVoice(*it);
+
+ nickIcons[Normal][0] = elementNormal;
+ nickIcons[Normal][1] = overlayPixmaps( nickIcons[Normal][0], elementAway );
+
+ nickIcons[Voice][0] = overlayPixmaps( elementNormal, elementVoice );
+ nickIcons[Voice][1] = overlayPixmaps( nickIcons[Voice][0], elementAway );
+
+ nickIcons[HalfOp][0] = overlayPixmaps( elementNormal, elementHalfOp );
+ nickIcons[HalfOp][1] = overlayPixmaps( nickIcons[HalfOp][0], elementAway );
+
+ nickIcons[Op][0] = overlayPixmaps( elementNormal, elementOp );
+ nickIcons[Op][1] = overlayPixmaps( nickIcons[Op][0], elementAway );
+
+ nickIcons[Owner][0] = overlayPixmaps( elementNormal, elementOwner );
+ nickIcons[Owner][1] = overlayPixmaps( nickIcons[Owner][0], elementAway );
+
+ nickIcons[Admin][0] = overlayPixmaps( elementNormal, elementAdmin );
+ nickIcons[Admin][1] = overlayPixmaps( nickIcons[Admin][0], elementAway );
+
+ /*
+ // why doesn't it work?
+ nickIcons[Op][0] = elementNormal;
+ bitBlt( &nickIcons[Op][0], 0, 0, &elementOp, 0, 0, -1, -1, Qt::CopyROP );
+ nickIcons[Op][1] = nickIcons[Op][0];
+ bitBlt( &nickIcons[Op][1], 0, 0, &elementAway, 0, 0, -1, -1, Qt::CopyROP );
+ */
+}
+
+void Images::updateIcons()
+{
+ m_closeIcon = KGlobal::iconLoader()->loadIcon("fileclose",KIcon::Small);
+ m_disabledCloseIcon = KGlobal::iconLoader()->loadIconSet("fileclose",KIcon::Small).pixmap(QIconSet::Small, false);
+}
+
+QIconSet Images::getLed(QColor col,bool state)
+{
+ QColor color;
+ QPainter paint;
+ QBrush brush;
+ QPen pen;
+
+ if (state==false)
+ color = col.dark(180);
+ else
+ color = col;
+
+ int scale = 3;
+ int width = 12 * scale;
+
+ QPixmap *tmpMap = 0;
+
+ tmpMap = new QPixmap(width + 6, width + 6);
+ QWidget tmpWidget;
+ tmpMap->fill(tmpWidget.paletteBackgroundColor());
+ paint.begin(tmpMap);
+
+ // Set the brush to SolidPattern, this fills the entire area
+ // of the ellipse which is drawn first
+ brush.setStyle( QBrush::SolidPattern );
+ brush.setColor( color );
+ paint.setBrush( brush );
+
+ // Draw a "flat" LED with the given color
+ paint.drawEllipse( scale, scale, width - scale*2, width - scale*2 );
+
+ // Setting the new width of the pen is essential to avoid "pixelized"
+ // shadow like it can be observed with the old LED code
+ pen.setWidth( 2 * scale );
+
+ // shrink the light on the LED to a size about 2/3 of the complete LED
+ int pos = width/5 + 1;
+ int light_width = width;
+ light_width *= 2;
+ light_width /= 3;
+
+ // Calculate the LEDs "light factor"
+ int light_quote = (130*2/(light_width?light_width:1))+100;
+
+ // Draw the bright spot on the LED
+ while (light_width)
+ {
+ color = color.light( light_quote ); // make color lighter
+ pen.setColor( color ); // set color as pen color
+ paint.setPen( pen ); // select the pen for drawing
+ paint.drawEllipse( pos, pos, light_width, light_width ); // draw the ellipse (circle)
+ light_width--;
+ if (!light_width)
+ break;
+ paint.drawEllipse( pos, pos, light_width, light_width );
+ light_width--;
+ if (!light_width)
+ break;
+ paint.drawEllipse( pos, pos, light_width, light_width );
+ pos++; light_width--;
+ }
+
+ // Draw border
+ pen.setWidth( scale + 1 );
+ color = QColor("#7D7D7D");
+ pen.setColor( color ); // Set the pen accordingly
+ paint.setPen( pen ); // Select pen for drawing
+ brush.setStyle( QBrush::NoBrush ); // Switch off the brush
+ paint.setBrush( brush ); // This avoids filling of the ellipse
+ paint.drawEllipse( 2, 2, width, width );
+ paint.end();
+
+ tmpMap->setMask(tmpMap->createHeuristicMask(true));
+
+ // painting done
+ QImage i = tmpMap->convertToImage();
+ delete tmpMap;
+
+ i.setAlphaBuffer(true);
+ width /= 3;
+ i = i.smoothScale(width, width);
+
+ QPixmap dest = i;
+
+ QIconSet result;
+ result.setPixmap(dest,QIconSet::Automatic);
+ return dest;
+}
+
+QIconSet Images::getServerLed(bool state)
+{
+ if (state)
+ return m_serverLedOn;
+ else
+ return m_serverLedOff;
+}
+
+QIconSet Images::getSystemLed(bool state)
+{
+ if (Preferences::tabNotificationsSystemColor()!=m_systemColor)
+ {
+ if (state)
+ return getLed(Preferences::tabNotificationsSystemColor(),true);
+ else
+ return getLed(Preferences::tabNotificationsSystemColor(),false);
+ }
+ else
+ {
+ if (state)
+ return m_systemLedOn;
+ else
+ return m_systemLedOff;
+ }
+}
+
+QIconSet Images::getMsgsLed(bool state)
+{
+ if (Preferences::tabNotificationsMsgsColor()!=m_msgsColor)
+ {
+ if (state)
+ return getLed(Preferences::tabNotificationsMsgsColor(),true);
+ else
+ return getLed(Preferences::tabNotificationsMsgsColor(),false);
+ }
+ else
+ {
+ if (state)
+ return m_msgsLedOn;
+ else
+ return m_msgsLedOff;
+ }
+}
+
+QIconSet Images::getPrivateLed(bool state)
+{
+ if (Preferences::tabNotificationsPrivateColor()!=m_privateColor)
+ {
+ if (state)
+ return getLed(Preferences::tabNotificationsPrivateColor(),true);
+ else
+ return getLed(Preferences::tabNotificationsPrivateColor(),false);
+ }
+ else
+ {
+ if (state)
+ return m_privateLedOn;
+ else
+ return m_privateLedOff;
+ }
+}
+
+QIconSet Images::getEventsLed()
+{
+ if (Preferences::tabNotificationsEventsColor()!=m_eventsColor)
+ return getLed(Preferences::tabNotificationsEventsColor(),true);
+ else
+ return m_eventsLedOn;
+}
+
+QIconSet Images::getNickLed()
+{
+ if (Preferences::tabNotificationsNickColor()!=m_nickColor)
+ return getLed(Preferences::tabNotificationsNickColor(),true);
+ else
+ return m_nickLedOn;
+}
+
+QIconSet Images::getHighlightsLed()
+{
+ if (Preferences::tabNotificationsHighlightsColor()!=m_highlightsColor)
+ return getLed(Preferences::tabNotificationsHighlightsColor(),true);
+ else
+ return m_highlightsLedOn;
+}
+
+#include "images.moc"
diff --git a/konversation/src/images.h b/konversation/src/images.h
new file mode 100644
index 0000000..119c048
--- /dev/null
+++ b/konversation/src/images.h
@@ -0,0 +1,102 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005-2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef IMAGES_H
+#define IMAGES_H
+
+#include <qiconset.h>
+#include <qpixmap.h>
+#include <qobject.h>
+
+
+/**
+ * Do not create an instance of this class yourself.
+ * use KonversationApplication::instance()->images().
+ */
+
+class Images : public QObject
+{
+ Q_OBJECT
+
+ public:
+ enum NickPrivilege
+ {
+ Normal=0,
+ Voice,
+ HalfOp,
+ Op,
+ Owner,
+ Admin,
+ _NickPrivilege_COUNT
+ };
+
+ Images();
+ virtual ~Images();
+
+ QPixmap getCloseIcon() { return m_closeIcon; }
+ QPixmap getDisabledCloseIcon() { return m_disabledCloseIcon; }
+
+ QIconSet getLed(QColor col,bool state = true);
+
+ QIconSet getServerLed(bool state);
+ QIconSet getSystemLed(bool state);
+ QIconSet getMsgsLed(bool state);
+ QIconSet getPrivateLed(bool state);
+ QIconSet getEventsLed();
+ QIconSet getNickLed();
+ QIconSet getHighlightsLed();
+
+ QIconSet getKimproxyAway() const;
+ QIconSet getKimproxyOnline() const;
+ QIconSet getKimproxyOffline() const;
+
+ QPixmap getNickIcon(NickPrivilege privilege,bool isAway=false) const;
+ void initializeNickIcons();
+
+ public slots:
+ void updateIcons();
+
+ protected:
+ void initializeLeds();
+ void initializeKimifaceIcons();
+
+ QPixmap m_closeIcon;
+ QPixmap m_disabledCloseIcon;
+
+ QIconSet m_serverLedOn;
+ QIconSet m_serverLedOff;
+ QIconSet m_systemLedOn;
+ QIconSet m_systemLedOff;
+ QIconSet m_msgsLedOn;
+ QIconSet m_msgsLedOff;
+ QIconSet m_privateLedOn;
+ QIconSet m_privateLedOff;
+ QIconSet m_eventsLedOn;
+ QIconSet m_nickLedOn;
+ QIconSet m_highlightsLedOn;
+
+ QColor m_serverColor;
+ QColor m_systemColor;
+ QColor m_msgsColor;
+ QColor m_privateColor;
+ QColor m_eventsColor;
+ QColor m_nickColor;
+ QColor m_highlightsColor;
+
+ QIconSet kimproxyAway;
+ QIconSet kimproxyOnline;
+ QIconSet kimproxyOffline;
+
+ // [privilege][away]
+ QPixmap nickIcons[_NickPrivilege_COUNT][2];
+};
+#endif
diff --git a/konversation/src/inputfilter.cpp b/konversation/src/inputfilter.cpp
new file mode 100644
index 0000000..badbe66
--- /dev/null
+++ b/konversation/src/inputfilter.cpp
@@ -0,0 +1,1985 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "inputfilter.h"
+#include "server.h"
+#include "replycodes.h"
+#include "konversationapplication.h"
+#include "commit.h"
+#include "version.h"
+#include "query.h"
+#include "channel.h"
+#include "statuspanel.h"
+#include "common.h"
+#include "notificationhandler.h"
+
+#include <qdatastream.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kdeversion.h>
+#include <kstringhandler.h>
+#include <config.h>
+#include <kdebug.h>
+#include <kresolver.h>
+
+InputFilter::InputFilter()
+{
+ m_connecting = false;
+}
+
+InputFilter::~InputFilter()
+{
+}
+
+void InputFilter::setServer(Server* newServer)
+{
+ server=newServer;
+}
+
+void InputFilter::parseLine(const QString& a_newLine)
+{
+ QString trailing;
+ QString newLine(a_newLine);
+
+ // Remove white spaces at the end and beginning
+ newLine = newLine.stripWhiteSpace();
+ // Find end of middle parameter list
+ int pos = newLine.find(" :");
+ // Was there a trailing parameter?
+ if(pos != -1)
+ {
+ // Copy trailing parameter
+ trailing = newLine.mid(pos + 2);
+ // Cut trailing parameter from string
+ newLine = newLine.left(pos);
+ }
+ // Remove all unnecessary white spaces to make parsing easier
+ newLine = newLine.simplifyWhiteSpace();
+
+ QString prefix;
+
+ // Do we have a prefix?
+ if(newLine[0] == ':')
+ {
+ // Find end of prefix
+ pos = newLine.find(' ');
+ // Copy prefix
+ prefix = newLine.mid(1, pos - 1);
+ // Remove prefix from line
+ newLine = newLine.mid(pos + 1);
+ }
+
+ // Find end of command
+ pos = newLine.find(' ');
+ // Copy command (all lowercase to make parsing easier)
+ QString command = newLine.left(pos).lower();
+ // Are there parameters left in the string?
+ QStringList parameterList;
+
+ if(pos != -1)
+ {
+ // Cut out the command
+ newLine = newLine.mid(pos + 1);
+ // The rest of the string will be the parameter list
+ parameterList = QStringList::split(" ", newLine);
+ }
+
+ Q_ASSERT(server);
+
+ // Server command, if no "!" was found in prefix
+ if((prefix.find('!') == -1) && (prefix != server->getNickname()))
+ {
+
+ parseServerCommand(prefix, command, parameterList, trailing);
+ }
+ else
+ {
+ parseClientCommand(prefix, command, parameterList, trailing);
+ }
+}
+
+void InputFilter::parseClientCommand(const QString &prefix, const QString &command, const QStringList &parameterList, const QString &_trailing)
+{
+ KonversationApplication* konv_app = static_cast<KonversationApplication *>(KApplication::kApplication());
+ Q_ASSERT(konv_app);
+ Q_ASSERT(server);
+ // Extract nickname from prefix
+ int pos = prefix.find("!");
+ QString sourceNick = prefix.left(pos);
+ QString sourceHostmask = prefix.mid(pos + 1);
+ // remember hostmask for this nick, it could have changed
+ server->addHostmaskToNick(sourceNick,sourceHostmask);
+ QString trailing = _trailing;
+
+ if(command=="privmsg")
+ {
+ bool isChan = isAChannel(parameterList[0]);
+ // CTCP message?
+ if(server->identifyMsg() && (trailing.at(0) == '+' || trailing.at(0) == '-')) {
+ trailing = trailing.mid(1);
+ }
+
+ if(trailing.at(0)==QChar(0x01))
+ {
+ // cut out the CTCP command
+ QString ctcp = trailing.mid(1,trailing.find(1,1)-1);
+
+ QString ctcpCommand=ctcp.left(ctcp.find(" ")).lower();
+ QString ctcpArgument=ctcp.mid(ctcp.find(" ")+1);
+ ctcpArgument=static_cast<KonversationApplication*>(kapp)->doAutoreplace(ctcpArgument,false);
+
+ // If it was a ctcp action, build an action string
+ if(ctcpCommand=="action" && isChan)
+ {
+ if(!isIgnore(prefix,Ignore::Channel))
+ {
+ Channel* channel = server->getChannelByName( parameterList[0] );
+
+ if(!channel) {
+ kdError() << "Didn't find the channel " << parameterList[0] << endl;
+ return;
+ }
+
+ channel->appendAction(sourceNick,ctcpArgument);
+
+ if(sourceNick != server->getNickname())
+ {
+ if(ctcpArgument.lower().find(QRegExp("(^|[^\\d\\w])"
+ + QRegExp::escape(server->loweredNickname())
+ + "([^\\d\\w]|$)")) !=-1 )
+ {
+ konv_app->notificationHandler()->nick(channel, sourceNick, ctcpArgument);
+ }
+ else
+ {
+ konv_app->notificationHandler()->message(channel, sourceNick, ctcpArgument);
+ }
+ }
+ }
+ }
+ // If it was a ctcp action, build an action string
+ else if(ctcpCommand=="action" && !isChan)
+ {
+ // Check if we ignore queries from this nick
+ if(!isIgnore(prefix,Ignore::Query))
+ {
+ NickInfoPtr nickinfo = server->obtainNickInfo(sourceNick);
+ nickinfo->setHostmask(sourceHostmask);
+
+ // create new query (server will check for dupes)
+ query = server->addQuery(nickinfo, false /* we didn't initiate this*/ );
+
+ // send action to query
+ query->appendAction(sourceNick,ctcpArgument);
+
+ if(sourceNick != server->getNickname() && query)
+ {
+ konv_app->notificationHandler()->queryMessage(query, sourceNick, ctcpArgument);
+ }
+ }
+ }
+
+ // Answer ping requests
+ else if(ctcpCommand=="ping")
+ {
+ if(!isIgnore(prefix,Ignore::CTCP))
+ {
+ if(isChan)
+ {
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received CTCP-PING request from %1 to channel %2, sending answer.")
+ .arg(sourceNick).arg(parameterList[0])
+ );
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received CTCP-%1 request from %2, sending answer.")
+ .arg("PING").arg(sourceNick)
+ );
+ }
+ server->ctcpReply(sourceNick,QString("PING %1").arg(ctcpArgument));
+ }
+ }
+
+ // Maybe it was a version request, so act appropriately
+ else if(ctcpCommand=="version")
+ {
+ if(!isIgnore(prefix,Ignore::CTCP))
+ {
+ if (isChan)
+ {
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received Version request from %1 to channel %2.")
+ .arg(sourceNick).arg(parameterList[0])
+ );
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received Version request from %1.")
+ .arg(sourceNick)
+ );
+ }
+
+ QString reply;
+ if(Preferences::customVersionReplyEnabled())
+ {
+ reply = Preferences::customVersionReply().stripWhiteSpace();
+ }
+ else
+ {
+ // Do not internationalize the below version string
+ reply = QString("Konversation %1 (C) 2002-2008 by the Konversation team")
+ .arg(QString(KONVI_VERSION));
+ }
+ server->ctcpReply(sourceNick,"VERSION "+reply);
+ }
+ }
+ // DCC request?
+ else if(ctcpCommand=="dcc" && !isChan)
+ {
+ if(!isIgnore(prefix,Ignore::DCC))
+ {
+ // Extract DCC type and argument list
+ QString dccType=ctcpArgument.lower().section(' ',0,0);
+
+ // Support file names with spaces
+ QString dccArguments = ctcpArgument.mid(ctcpArgument.find(" ")+1);
+ QStringList dccArgumentList;
+
+ if ((dccArguments.contains('\"') >= 2) && (dccArguments.startsWith("\""))) {
+ int lastQuotePos = dccArguments.findRev("\"");
+ if (dccArguments[lastQuotePos+1] == ' ') {
+ QString fileName = dccArguments.mid(1, lastQuotePos-1);
+ dccArguments = dccArguments.mid(lastQuotePos+2);
+
+ dccArgumentList.append(fileName);
+ }
+ }
+ dccArgumentList += QStringList::split(' ', dccArguments);
+
+ if(dccType=="send")
+ {
+ if(dccArgumentList.count()==4)
+ {
+ // incoming file
+ konv_app->notificationHandler()->dccIncoming(server->getStatusView(), sourceNick);
+ emit addDccGet(sourceNick,dccArgumentList);
+ }
+ else if(dccArgumentList.count()==5)
+ {
+ if(dccArgumentList[2]=="0")
+ {
+ // incoming file (Reverse DCC)
+ konv_app->notificationHandler()->dccIncoming(server->getStatusView(), sourceNick);
+ emit addDccGet(sourceNick,dccArgumentList);
+ }
+ else
+ {
+ // the receiver accepted the offer for Reverse DCC
+ emit startReverseDccSendTransfer(sourceNick,dccArgumentList);
+ }
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("DCC"),
+ i18n("Received invalid DCC SEND request from %1.")
+ .arg(sourceNick)
+ );
+ }
+ }
+ else if(dccType=="accept")
+ {
+ // resume request was accepted
+ if(dccArgumentList.count()==3)
+ {
+ emit resumeDccGetTransfer(sourceNick,dccArgumentList);
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("DCC"),
+ i18n("Received invalid DCC ACCEPT request from %1.")
+ .arg(sourceNick)
+ );
+ }
+ }
+ // Remote client wants our sent file resumed
+ else if(dccType=="resume")
+ {
+ if(dccArgumentList.count()==3)
+ {
+ emit resumeDccSendTransfer(sourceNick,dccArgumentList);
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("DCC"),
+ i18n("Received invalid DCC RESUME request from %1.")
+ .arg(sourceNick)
+ );
+ }
+ }
+ else if(dccType=="chat")
+ {
+
+ if(dccArgumentList.count()==3)
+ {
+ // will be connected via Server to KonversationMainWindow::addDccChat()
+ emit addDccChat(server->getNickname(),sourceNick,dccArgumentList,false);
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("DCC"),
+ i18n("Received invalid DCC CHAT request from %1.")
+ .arg(sourceNick)
+ );
+ }
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("DCC"),
+ i18n("Unknown DCC command %1 received from %2.")
+ .arg(ctcpArgument).arg(sourceNick)
+ );
+ }
+ }
+ }
+ else if (ctcpCommand=="clientinfo" && !isChan)
+ {
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received CTCP-%1 request from %2, sending answer.")
+ .arg("CLIENTINFO").arg(sourceNick)
+ );
+ server->ctcpReply(sourceNick,QString("CLIENTINFO ACTION CLIENTINFO DCC PING TIME VERSION"));
+ }
+ else if(ctcpCommand=="time" && !isChan)
+ {
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received CTCP-%1 request from %2, sending answer.")
+ .arg("TIME").arg(sourceNick)
+ );
+ server->ctcpReply(sourceNick,QString("TIME ")+QDateTime::currentDateTime().toString());
+ }
+
+ // No known CTCP request, give a general message
+ else
+ {
+ if(!isIgnore(prefix,Ignore::CTCP))
+ {
+ if (isChan)
+ server->appendServerMessageToChannel(
+ parameterList[0],
+ "CTCP",
+ i18n("Received unknown CTCP-%1 request from %2 to Channel %3.")
+ .arg(ctcp).arg(sourceNick).arg(parameterList[0])
+ );
+ else
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received unknown CTCP-%1 request from %2.")
+ .arg(ctcp).arg(sourceNick)
+ );
+ }
+ }
+ }
+ // No CTCP, so it's an ordinary channel or query message
+ else
+ {
+ parsePrivMsg (prefix, parameterList, trailing);
+ }
+ }
+ else if(command=="notice")
+ {
+ if(!isIgnore(prefix,Ignore::Notice))
+ {
+ // Channel notice?
+ if(isAChannel(parameterList[0]))
+ {
+ if(server->identifyMsg())
+ {
+ trailing = trailing.mid(1);
+ }
+
+ server->appendServerMessageToChannel(parameterList[0], i18n("Notice"),
+ i18n("-%1 to %2- %3")
+ .arg(sourceNick).arg(parameterList[0]).arg(trailing)
+ );
+ }
+ // Private notice
+ else
+ {
+ // Was this a CTCP reply?
+ if(trailing.at(0)==QChar(0x01))
+ {
+ // cut 0x01 bytes from trailing string
+ QString ctcp(trailing.mid(1,trailing.length()-2));
+ QString replyReason(ctcp.section(' ',0,0));
+ QString reply(ctcp.section(' ',1));
+
+ // pong reply, calculate turnaround time
+ if(replyReason.lower()=="ping")
+ {
+ int dateArrived=QDateTime::currentDateTime().toTime_t();
+ int dateSent=reply.toInt();
+ int time = dateArrived-dateSent;
+ QString unit = "seconds";
+
+ if (time==1)
+ unit = "second";
+
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received CTCP-PING reply from %1: %2 %3.")
+ .arg(sourceNick)
+ .arg(time)
+ .arg(unit)
+ );
+ }
+ // all other ctcp replies get a general message
+ else
+ {
+ server->appendMessageToFrontmost(i18n("CTCP"),
+ i18n("Received CTCP-%1 reply from %2: %3.")
+ .arg(replyReason).arg(sourceNick).arg(reply)
+ );
+ }
+ }
+ // No, so it was a normal notice
+ else
+ {
+ // Nickserv
+ if (trailing.startsWith("If this is your nick"))
+ {
+ // Identify command if specified
+ server->registerWithServices();
+ }
+ else if (server->identifyMsg())
+ trailing = trailing.mid(1);
+
+ if(trailing.lower() == "password accepted - you are now recognized"
+ || trailing.lower() == "you have already identified")
+ {
+ NickInfoPtr nickInfo = server->getNickInfo(server->getNickname());
+ if(nickInfo)
+ nickInfo->setIdentified(true);
+ }
+ server->appendMessageToFrontmost(i18n("Notice"), i18n("-%1- %2").arg(sourceNick).arg(trailing));
+ }
+ }
+ }
+ }
+ else if(command=="join")
+ {
+ QString channelName(trailing);
+ // Sometimes JOIN comes without ":" in front of the channel name
+ if(channelName.isEmpty())
+ channelName=parameterList[parameterList.count()-1];
+
+ // Did we join the channel, or was it someone else?
+ if(server->isNickname(sourceNick))
+ {
+ /*
+ QString key;
+ // TODO: Try to remember channel keys for autojoins and manual joins, so
+ // we can get %k to work
+
+ if(channelName.find(' ')!=-1)
+ {
+ key=channelName.section(' ',1,1);
+ channelName=channelName.section(' ',0,0);
+ }
+ */
+
+ // Join the channel
+ server->joinChannel(channelName, sourceHostmask);
+
+ server->resetNickList(channelName);
+
+ // Upon JOIN we're going to receive some NAMES input from the server which
+ // we need to be able to tell apart from manual invocations of /names
+ setAutomaticRequest("NAMES",channelName,true);
+
+ server->getChannelByName(channelName)->clearModeList();
+
+ // Request modes for the channel
+ server->queue("MODE "+channelName, Server::LowPriority);
+
+ // Initiate channel ban list
+ server->getChannelByName(channelName)->clearBanList();
+ setAutomaticRequest("BANLIST",channelName,true);
+ server->queue("MODE "+channelName+" +b", Server::LowPriority);
+ }
+ else
+ {
+ Channel* channel = server->nickJoinsChannel(channelName,sourceNick,sourceHostmask);
+ konv_app->notificationHandler()->join(channel, sourceNick);
+ }
+ }
+ else if(command=="kick")
+ {
+ server->nickWasKickedFromChannel(parameterList[0],parameterList[1],sourceNick,trailing);
+ }
+ else if(command=="part")
+ {
+ /* FIXME: Ugly workaround for a version of the PART line encountered on ircu:
+ * :Nick!user@host PART :#channel
+ * Quote: "The final colon is specified as a "last argument" designator, and
+ * is always valid before the final argument."
+ */
+
+ QString channel;
+ QString reason;
+
+ if (parameterList[0].isEmpty())
+ {
+ channel = trailing;
+ }
+ else
+ {
+ channel = parameterList[0];
+ reason = trailing;
+ }
+
+ Channel* channelPtr = server->removeNickFromChannel(channel,sourceNick,reason);
+
+ if(sourceNick != server->getNickname())
+ {
+ konv_app->notificationHandler()->part(channelPtr, sourceNick);
+ }
+ }
+ else if(command=="quit")
+ {
+ server->removeNickFromServer(sourceNick,trailing);
+ if(sourceNick != server->getNickname())
+ {
+ konv_app->notificationHandler()->quit(server->getStatusView(), sourceNick);
+ }
+ }
+ else if(command=="nick")
+ {
+ QString newNick(trailing);
+
+ // Message may not include ":" in front of the new nickname
+ if (newNick.isEmpty())
+ newNick=parameterList[parameterList.count()-1];
+
+ server->renameNick(sourceNick,newNick);
+
+ if (sourceNick != server->getNickname())
+ {
+ konv_app->notificationHandler()->nickChange(server->getStatusView(), sourceNick, newNick);
+ }
+ }
+ else if(command=="topic")
+ {
+ server->setChannelTopic(sourceNick,parameterList[0],trailing);
+ }
+ else if(command=="mode") // mode #channel -/+ mmm params
+ {
+ QStringList params=parameterList;
+ if (!trailing.isEmpty())
+ params << trailing;
+ parseModes(sourceNick, params);
+ Channel* channel = server->getChannelByName(parameterList[0]);
+ if(sourceNick != server->getNickname())
+ {
+ konv_app->notificationHandler()->mode(channel, sourceNick);
+ }
+ }
+ else if(command=="invite")
+ {
+ QString channel;
+
+ if (parameterList.count() > 1)
+ channel = parameterList[1];
+ else
+ channel = trailing;
+
+ server->appendMessageToFrontmost(i18n("Invite"),
+ i18n("%1 invited you to channel %2.")
+ .arg(sourceNick).arg(channel)
+ );
+ emit invitation(sourceNick,channel);
+ }
+ else
+ {
+ server->appendMessageToFrontmost(command,parameterList.join(" ")+' '+trailing);
+ }
+}
+
+void InputFilter::parseServerCommand(const QString &prefix, const QString &command, const QStringList &parameterList, const QString &trailing)
+{
+ bool isNumeric;
+ int numeric = command.toInt(&isNumeric);
+
+ Q_ASSERT(server); if(!server) return;
+
+ if(!isNumeric)
+ {
+ if(command=="ping")
+ {
+ QString text;
+ text = (!trailing.isEmpty()) ? trailing : parameterList.join(" ");
+
+ if(!trailing.isEmpty())
+ {
+ text = prefix + " :" + text;
+ }
+
+ if(!text.startsWith(" "))
+ {
+ text.prepend(' ');
+ }
+
+ // queue the reply to send it as soon as possible
+ server->queue("PONG"+text, Server::HighPriority);
+
+ }
+ else if(command=="error :closing link:")
+ {
+ kdDebug() << "link closed" << endl;
+ }
+ else if(command=="pong")
+ {
+ // double check if we are in lag measuring mode since some servers fail to send
+ // the LAG cookie back in PONG
+ if(trailing.startsWith("LAG") || getLagMeasuring())
+ {
+ server->pongReceived();
+ }
+ }
+ else if(command=="mode")
+ {
+ QStringList params=parameterList;
+ if (!trailing.isEmpty())
+ params << trailing;
+ parseModes(prefix, params);
+ }
+ else if(command=="notice")
+ {
+ server->appendStatusMessage(i18n("Notice"),i18n("-%1- %2").arg(prefix).arg(trailing));
+ }
+ else if(command=="kick")
+ {
+ server->nickWasKickedFromChannel(parameterList[0],parameterList[1],prefix,trailing);
+ }
+ else if(command == "privmsg")
+ {
+ parsePrivMsg(prefix, parameterList, trailing);
+ }
+ // All yet unknown messages go into the frontmost window unaltered
+ else
+ {
+ server->appendMessageToFrontmost(command,parameterList.join(" ")+' '+trailing);
+ }
+ }
+ else
+ {
+ switch (numeric)
+ {
+ case RPL_WELCOME:
+ case RPL_YOURHOST:
+ case RPL_CREATED:
+ {
+ if(numeric==RPL_WELCOME)
+ {
+ QString host;
+
+ if(trailing.contains("@"))
+ host = trailing.section("@",1);
+
+ // re-set nickname, since the server may have truncated it
+ if(parameterList[0]!=server->getNickname())
+ server->renameNick(server->getNickname(), parameterList[0]);
+
+ // Send the welcome signal, so the server class knows we are connected properly
+ emit welcome(host);
+ m_connecting = true;
+ }
+ server->appendStatusMessage(i18n("Welcome"),trailing);
+ break;
+ }
+ case RPL_MYINFO:
+ {
+ server->appendStatusMessage(i18n("Welcome"),
+ i18n("Server %1 (Version %2), User modes: %3, Channel modes: %4")
+ .arg(parameterList[1])
+ .arg(parameterList[2])
+ .arg(parameterList[3])
+ .arg(parameterList[4])
+ );
+ server->setAllowedChannelModes(parameterList[4]);
+ break;
+ }
+ //case RPL_BOUNCE: // RFC 1459 name, now seems to be obsoleted by ...
+ case RPL_ISUPPORT: // ... DALnet RPL_ISUPPORT
+ {
+ server->appendStatusMessage(i18n("Support"),parameterList.join(" "));
+
+ // The following behavoiur is neither documented in RFC 1459 nor in 2810-2813
+ // Nowadays, most ircds send server capabilities out via 005 (BOUNCE).
+ // refer to http://www.irc.org/tech_docs/005.html for a kind of documentation.
+ // More on http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
+
+ QStringList::const_iterator it = parameterList.begin();
+ // don't want the user name
+ ++it;
+ for (; it != parameterList.end(); ++it )
+ {
+ QString property, value;
+ int pos;
+ if ((pos=(*it).find( '=' )) !=-1)
+ {
+ property = (*it).left(pos);
+ value = (*it).mid(pos+1);
+ }
+ else
+ {
+ property = *it;
+ }
+ if (property=="PREFIX")
+ {
+ pos = value.find(')',1);
+ if(pos==-1)
+ {
+ server->setPrefixes(QString(), value);
+ // XXX if ) isn't in the string, NOTHING should be there. anyone got a server
+ if (value.length() || property.length())
+ server->appendStatusMessage("","XXX Server sent bad PREFIX in RPL_ISUPPORT, please report.");
+ }
+ else
+ {
+ server->setPrefixes (value.mid(1, pos-1), value.mid(pos+1));
+ }
+ }
+ else if (property=="CHANTYPES")
+ {
+ server->setChannelTypes(value);
+ }
+ else if (property == "CAPAB")
+ {
+ // Disable as we don't use this for anything yet
+ //server->queue("CAPAB IDENTIFY-MSG");
+ }
+ else
+ {
+ //kdDebug() << "Ignored server-capability: " << property << " with value '" << value << "'" << endl;
+ }
+ } // endfor
+ break;
+ }
+ case RPL_UMODEIS:
+ {
+ QString message=QString("%1 %2").arg(i18n("Your personal modes are:")).arg(parameterList.join(" ").section(' ',1) + ' '+trailing);
+ server->appendMessageToFrontmost("Info", message);
+ break;
+ }
+ case RPL_CHANNELMODEIS:
+ {
+ const QString modeString=parameterList[2];
+ // This is the string the user will see
+ QString modesAre;
+ QString message = i18n("Channel modes: ") + modeString;
+
+ for(unsigned int index=0;index<modeString.length();index++)
+ {
+ QString parameter;
+ int parameterCount=3;
+ char mode=modeString[index];
+ if(mode!='+')
+ {
+ if(!modesAre.isEmpty())
+ modesAre+=", ";
+ if(mode=='t')
+ modesAre+=i18n("topic protection");
+ else if(mode=='n')
+ modesAre+=i18n("no messages from outside");
+ else if(mode=='s')
+ modesAre+=i18n("secret");
+ else if(mode=='i')
+ modesAre+=i18n("invite only");
+ else if(mode=='p')
+ modesAre+=i18n("private");
+ else if(mode=='m')
+ modesAre+=i18n("moderated");
+ else if(mode=='k')
+ {
+ parameter=parameterList[parameterCount++];
+ message += ' ' + parameter;
+ modesAre+=i18n("password protected");
+ }
+ else if(mode=='a')
+ modesAre+=i18n("anonymous");
+ else if(mode=='r')
+ modesAre+=i18n("server reop");
+ else if(mode=='c')
+ modesAre+=i18n("no colors allowed");
+ else if(mode=='l')
+ {
+ parameter=parameterList[parameterCount++];
+ message += ' ' + parameter;
+ modesAre+=i18n("limited to %n user", "limited to %n users", parameter.toInt());
+ }
+ else
+ {
+ modesAre+=mode;
+ }
+ server->updateChannelModeWidgets(parameterList[1],mode,parameter);
+ }
+ } // endfor
+ if(!modesAre.isEmpty() && Preferences::useLiteralModes())
+ {
+ server->appendCommandMessageToChannel(parameterList[1],i18n("Mode"),message);
+ }
+ else
+ {
+ server->appendCommandMessageToChannel(parameterList[1],i18n("Mode"),
+ i18n("Channel modes: ") + modesAre
+ );
+ }
+ break;
+ }
+ case RPL_CHANNELURLIS:
+ {// :niven.freenode.net 328 argonel #channel :http://www.buggeroff.com/
+ server->appendCommandMessageToChannel(parameterList[1], i18n("URL"),
+ i18n("Channel URL: %1").arg(trailing));
+ break;
+ }
+ case RPL_CHANNELCREATED:
+ {
+ QDateTime when;
+ when.setTime_t(parameterList[2].toUInt());
+ server->appendCommandMessageToChannel(parameterList[1],i18n("Created"),
+ i18n("This channel was created on %1.")
+ .arg(when.toString(Qt::LocalDate))
+ );
+
+ if(Preferences::autoWhoContinuousEnabled())
+ {
+ emit endOfWho(parameterList[1]);
+ }
+
+ break;
+ }
+ case RPL_WHOISACCOUNT:
+ {
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),i18n("%1 is logged in as %2.").arg(parameterList[1]).arg(parameterList[2]));
+ }
+ break;
+ }
+ case RPL_NAMREPLY:
+ {
+ QStringList nickList;
+
+ if(!trailing.isEmpty())
+ {
+ nickList = QStringList::split(" ", trailing);
+ }
+ else if(parameterList.count() > 3)
+ {
+ for(uint i = 3; i < parameterList.count(); i++) {
+ nickList.append(parameterList[i]);
+ }
+ }
+ else
+ {
+ kdDebug() << "Hmm seems something is broken... can't get to the names!" << endl;
+ }
+
+ // send list to channel
+ server->addPendingNickList(parameterList[2], nickList);
+
+ // Display message only if this was not an automatic request.
+ if(!getAutomaticRequest("NAMES",parameterList[2])==1)
+ {
+ server->appendMessageToFrontmost(i18n("Names"),trailing);
+ }
+ break;
+ }
+ case RPL_ENDOFNAMES:
+ {
+ if(getAutomaticRequest("NAMES",parameterList[1])==1)
+ {
+ // This code path was taken for the automatic NAMES input on JOIN, upcoming
+ // NAMES input for this channel will be manual invocations of /names
+ setAutomaticRequest("NAMES",parameterList[1],false);
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("Names"),i18n("End of NAMES list."));
+ }
+ break;
+ }
+ // Topic set messages
+ case RPL_NOTOPIC:
+ {
+ server->appendMessageToFrontmost(i18n("TOPIC"),i18n("The channel %1 has no topic set.").arg(parameterList[1]) /*.arg(parameterList[2])*/); //FIXME ok, whats the second parameter supposed to be?
+
+ break;
+ }
+ case RPL_TOPIC:
+ {
+ QString topic = Konversation::removeIrcMarkup(trailing);
+
+ // FIXME: This is an abuse of the automaticRequest system: We're
+ // using it in an inverted manner, i.e. the automaticRequest is
+ // set to true by a manual invocation of /topic. Bad bad bad -
+ // needs rethinking of automaticRequest.
+ if(getAutomaticRequest("TOPIC",parameterList[1])==0)
+ {
+ // Update channel window
+ server->setChannelTopic(parameterList[1],topic);
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("Topic"),i18n("The channel topic for %1 is: \"%2\"").arg(parameterList[1]).arg(topic));
+ }
+
+ break;
+ }
+ case RPL_TOPICSETBY:
+ {
+ // Inform user who set the topic and when
+ QDateTime when;
+ when.setTime_t(parameterList[3].toUInt());
+
+ // See FIXME in RPL_TOPIC
+ if(getAutomaticRequest("TOPIC",parameterList[1])==0)
+ {
+ server->appendCommandMessageToChannel(parameterList[1],i18n("Topic"),
+ i18n("The topic was set by %1 on %2.")
+ .arg(parameterList[2]).arg(when.toString(Qt::LocalDate)),
+ false);
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("Topic"),i18n("The topic for %1 was set by %2 on %3.")
+ .arg(parameterList[1])
+ .arg(parameterList[2])
+ .arg(when.toString(Qt::LocalDate))
+ );
+ setAutomaticRequest("TOPIC",parameterList[1],false);
+ }
+ emit topicAuthor(parameterList[1], parameterList[2], when);
+
+ break;
+ }
+ case RPL_WHOISACTUALLY:
+ {
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),i18n("%1 is actually using the host %2.").arg(parameterList[1]).arg(parameterList[2]));
+ }
+ break;
+ }
+ case ERR_NOSUCHNICK:
+ {
+ // Display slightly different error message in case we performed a WHOIS for
+ // IP resolve purposes, and clear it from the automaticRequest list
+ if(getAutomaticRequest("DNS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("%1: No such nick/channel.").arg(parameterList[1]));
+ }
+ else if(getAutomaticRequest("WHOIS",parameterList[1])==0) //Display message only if this was not an automatic request.
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("No such nick: %1.").arg(parameterList[1]));
+ setAutomaticRequest("DNS", parameterList[1], false);
+ }
+
+ break;
+ }
+ case ERR_NOSUCHCHANNEL:
+ {
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("%1: No such channel.").arg(parameterList[1]));
+ }
+ break;
+ }
+ // Nick already on the server, so try another one
+ case ERR_NICKNAMEINUSE:
+ {
+ // if we are already connected, don't try tro find another nick ourselves
+ if(server->isConnected())
+ { // Show message
+ server->appendMessageToFrontmost(i18n("Nick"),i18n("Nickname already in use, try a different one."));
+ }
+ else // not connected yet, so try to find a nick that's not in use
+ {
+ // Get the next nick from the list or ask for a new one
+ QString newNick = server->getNextNickname();
+
+ // The user chose to disconnect
+ if (newNick.isNull())
+ {
+ server->disconnect();
+ }
+ else
+ {
+ // Update Server window
+ server->obtainNickInfo(server->getNickname()) ;
+ server->renameNick(server->getNickname(), newNick);
+ // Show message
+ server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use. Trying %1.").arg(newNick));
+ // Send nickchange request to the server
+ server->queue("NICK "+newNick);
+ }
+ }
+ break;
+ }
+ case ERR_ERRONEUSNICKNAME:
+ {
+ if(server->isConnected())
+ { // We are already connected. Just print the error message
+ server->appendMessageToFrontmost(i18n("Nick"), trailing);
+ }
+ else // Find a new nick as in ERR_NICKNAMEINUSE
+ {
+ QString newNick = server->getNextNickname();
+
+ // The user chose to disconnect
+ if (newNick.isNull())
+ {
+ server->disconnect();
+ }
+ else
+ {
+ server->obtainNickInfo(server->getNickname()) ;
+ server->renameNick(server->getNickname(), newNick);
+ server->appendMessageToFrontmost(i18n("Nick"), i18n("Erroneus nickname. Changing nick to %1." ).arg(newNick)) ;
+ server->queue("NICK "+newNick);
+ }
+ }
+ break;
+ }
+ case ERR_NOTONCHANNEL:
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("You are not on %1.").arg(parameterList[1]));
+
+ break;
+ }
+ case RPL_MOTDSTART:
+ {
+ if(!m_connecting || !Preferences::skipMOTD())
+ server->appendStatusMessage(i18n("MOTD"),i18n("Message of the day:"));
+ break;
+ }
+ case RPL_MOTD:
+ {
+ if(!m_connecting || !Preferences::skipMOTD())
+ server->appendStatusMessage(i18n("MOTD"),trailing);
+ break;
+ }
+ case RPL_ENDOFMOTD:
+ {
+ if(!m_connecting || !Preferences::skipMOTD())
+ server->appendStatusMessage(i18n("MOTD"),i18n("End of message of the day"));
+
+ if(m_connecting)
+ server->autoCommandsAndChannels();
+
+ m_connecting = false;
+ break;
+ }
+ case ERR_NOMOTD:
+ {
+ if(m_connecting)
+ server->autoCommandsAndChannels();
+
+ m_connecting = false;
+ break;
+ }
+ case RPL_YOUREOPER:
+ {
+ server->appendMessageToFrontmost(i18n("Notice"),i18n("You are now an IRC operator on this server."));
+
+ break;
+ }
+ case RPL_GLOBALUSERS: // Current global users: 589 Max: 845
+ {
+ QString current(trailing.section(' ',3));
+ //QString max(trailing.section(' ',5,5));
+ server->appendStatusMessage(i18n("Users"),i18n("Current users on the network: %1").arg(current));
+ break;
+ }
+ case RPL_LOCALUSERS: // Current local users: 589 Max: 845
+ {
+ QString current(trailing.section(' ',3));
+ //QString max(trailing.section(' ',5,5));
+ server->appendStatusMessage(i18n("Users"),i18n("Current users on %1: %2.").arg(prefix).arg(current));
+ break;
+ }
+ case RPL_ISON:
+ {
+ // Tell server to start the next notify timer round
+ emit notifyResponse(trailing);
+ break;
+ }
+ case RPL_AWAY:
+ {
+ NickInfo* nickInfo = server->getNickInfo(parameterList[1]);
+ if(nickInfo)
+ {
+ nickInfo->setAway(true);
+ if( nickInfo->getAwayMessage() == trailing )
+ break;
+ nickInfo->setAwayMessage(trailing);
+ }
+
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Away"),i18n("%1 is away: %2")
+ .arg(parameterList[1]).arg(trailing)
+ );
+ }
+
+ break;
+ }
+ case RPL_INVITING:
+ {
+ server->appendMessageToFrontmost(i18n("Invite"),
+ i18n("You invited %1 to channel %2.")
+ .arg(parameterList[1]).arg(parameterList[2])
+ );
+ break;
+ }
+ //Sample WHOIS response
+ //"/WHOIS psn"
+ //[19:11] :zahn.freenode.net 311 PhantomsDad psn ~psn h106n2fls23o1068.bredband.comhem.se * :Peter Simonsson
+ //[19:11] :zahn.freenode.net 319 PhantomsDad psn :#kde-devel #koffice
+ //[19:11] :zahn.freenode.net 312 PhantomsDad psn irc.freenode.net :http://freenode.net/
+ //[19:11] :zahn.freenode.net 301 PhantomsDad psn :away
+ //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user
+ //[19:11] :zahn.freenode.net 317 PhantomsDad psn 4921 1074973024 :seconds idle, signon time
+ //[19:11] :zahn.freenode.net 318 PhantomsDad psn :End of /WHOIS list.
+ case RPL_WHOISUSER:
+ {
+ NickInfo* nickInfo = server->getNickInfo(parameterList[1]);
+ if(nickInfo)
+ {
+ nickInfo->setHostmask(i18n("%1@%2").arg(parameterList[2]).arg(parameterList[3]));
+ nickInfo->setRealName(trailing);
+ }
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ // escape html tags
+ QString escapedRealName(trailing);
+ escapedRealName.replace("<","&lt;").replace(">","&gt;");
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is %2@%3 (%4)")
+ .arg(parameterList[1])
+ .arg(parameterList[2])
+ .arg(parameterList[3])
+ .arg(escapedRealName), false); // Don't parse any urls
+ }
+ else
+ {
+ // This WHOIS was requested by Server for DNS resolve purposes; try to resolve the host
+ if(getAutomaticRequest("DNS",parameterList[1])==1)
+ {
+ KNetwork::KResolverResults resolved = KNetwork::KResolver::resolve(parameterList[3],"");
+ if(resolved.error() == KResolver::NoError && resolved.size() > 0)
+ {
+ QString ip = resolved.first().address().nodeName();
+ server->appendMessageToFrontmost(i18n("DNS"),
+ i18n("Resolved %1 (%2) to address: %3")
+ .arg(parameterList[1])
+ .arg(parameterList[3])
+ .arg(ip)
+ );
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("Error"),
+ i18n("Unable to resolve address for %1 (%2)")
+ .arg(parameterList[1])
+ .arg(parameterList[3])
+ );
+ }
+
+ // Clear this from the automaticRequest list so it works repeatedly
+ setAutomaticRequest("DNS", parameterList[1], false);
+ }
+ }
+ break;
+ }
+ // From a WHOIS.
+ //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user
+ case RPL_WHOISIDENTIFY:
+ case RPL_IDENTIFIED:
+ {
+ NickInfo* nickInfo = server->getNickInfo(parameterList[1]);
+ if(nickInfo)
+ {
+ nickInfo->setIdentified(true);
+ }
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ // Prints "psn is an identified user"
+ //server->appendStatusMessage(i18n("Whois"),parameterList.join(" ").section(' ',1)+' '+trailing);
+ // The above line works fine, but can't be i18n'ised. So use the below instead.. I hope this is okay.
+ server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an identified user.").arg(parameterList[1]));
+ }
+ break;
+ }
+ // Sample WHO response
+ //"/WHO #lounge"
+ //[21:39] [352] #lounge jasmine bots.worldforge.org irc.worldforge.org jasmine H 0 jasmine
+ //[21:39] [352] #lounge ~Nottingha worldforge.org irc.worldforge.org SherwoodSpirit H 0 Arboreal Entity
+ case RPL_WHOREPLY:
+ {
+ NickInfo* nickInfo = server->getNickInfo(parameterList[5]);
+ // G=away G@=away,op G+=away,voice
+ bool bAway = parameterList[6].upper().startsWith("G");
+ if(nickInfo)
+ {
+ nickInfo->setHostmask(i18n("%1@%2").arg(parameterList[2]).arg(parameterList[3]));
+ //Strip off the "0 "
+ nickInfo->setRealName(trailing.section(" ", 1));
+ nickInfo->setAway(bAway);
+ if(!bAway)
+ {
+ nickInfo->setAwayMessage(QString());
+ }
+ }
+ // Display message only if this was not an automatic request.
+ if(!whoRequestList.isEmpty()) // for safe
+ {
+ if(getAutomaticRequest("WHO",whoRequestList.front())==0)
+ {
+ server->appendMessageToFrontmost(i18n("Who"),
+ i18n("%1 is %2@%3 (%4)%5").arg(parameterList[5])
+ .arg(parameterList[2])
+ .arg(parameterList[3])
+ .arg(trailing.section(" ", 1))
+ .arg(bAway?i18n(" (Away)"):QString())
+ , false); // Don't parse as url
+ }
+ }
+ break;
+ }
+ case RPL_ENDOFWHO:
+ {
+ if(!whoRequestList.isEmpty())
+ { // for safety
+ QStringList::iterator it = whoRequestList.find(parameterList[1].lower());
+
+ if(it != whoRequestList.end())
+ {
+ if(getAutomaticRequest("WHO", *it) == 0)
+ {
+ server->appendMessageToFrontmost(i18n("Who"),
+ i18n("End of /WHO list for %1")
+ .arg(parameterList[1]));
+ }
+ else
+ {
+ setAutomaticRequest("WHO", *it, false);
+ }
+
+ whoRequestList.remove(it);
+ }
+ else
+ {
+ // whoReauestList seems to be broken.
+ kdDebug() << "InputFilter::parseServerCommand(): RPL_ENDOFWHO: malformed ENDOFWHO. retrieved: "
+ << parameterList[1] << " expected: " << whoRequestList.front()
+ << endl;
+ whoRequestList.clear();
+ }
+ }
+ else
+ {
+ kdDebug() << "InputFilter::parseServerCommand(): RPL_ENDOFWHO: unexpected ENDOFWHO. retrieved: "
+ << parameterList[1]
+ << endl;
+ }
+
+ emit endOfWho(parameterList[1]);
+ break;
+ }
+ case RPL_WHOISCHANNELS:
+ {
+ QStringList userChannels,voiceChannels,opChannels,halfopChannels,ownerChannels,adminChannels;
+
+ // get a list of all channels the user is in
+ QStringList channelList=QStringList::split(' ',trailing);
+ channelList.sort();
+
+ // split up the list in channels where they are operator / user / voice
+ for(unsigned int index=0; index < channelList.count(); index++)
+ {
+ QString lookChannel=channelList[index];
+ if(lookChannel.startsWith("*") || lookChannel.startsWith("&"))
+ {
+ adminChannels.append(lookChannel.mid(1));
+ server->setChannelNick(lookChannel.mid(1), parameterList[1], 16);
+ }
+ // See bug #97354 part 2
+ else if((lookChannel.startsWith("!") || lookChannel.startsWith("~")) && server->isAChannel(lookChannel.mid(1)))
+ {
+ ownerChannels.append(lookChannel.mid(1));
+ server->setChannelNick(lookChannel.mid(1), parameterList[1], 8);
+ }
+ // See bug #97354 part 1
+ else if(lookChannel.startsWith("@+"))
+ {
+ opChannels.append(lookChannel.mid(2));
+ server->setChannelNick(lookChannel.mid(2), parameterList[1], 4);
+ }
+ else if(lookChannel.startsWith("@"))
+ {
+ opChannels.append(lookChannel.mid(1));
+ server->setChannelNick(lookChannel.mid(1), parameterList[1], 4);
+ }
+ else if(lookChannel.startsWith("%"))
+ {
+ halfopChannels.append(lookChannel.mid(1));
+ server->setChannelNick(lookChannel.mid(1), parameterList[1], 2);
+ }
+ else if(lookChannel.startsWith("+"))
+ {
+ voiceChannels.append(lookChannel.mid(1));
+ server->setChannelNick(lookChannel.mid(1), parameterList[1], 1);
+ }
+ else
+ {
+ userChannels.append(lookChannel);
+ server->setChannelNick(lookChannel, parameterList[1], 0);
+ }
+ } // endfor
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ if(userChannels.count())
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is a user on channels: %2")
+ .arg(parameterList[1])
+ .arg(userChannels.join(" "))
+ );
+ }
+ if(voiceChannels.count())
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 has voice on channels: %2")
+ .arg(parameterList[1]).arg(voiceChannels.join(" "))
+ );
+ }
+ if(halfopChannels.count())
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is a halfop on channels: %2")
+ .arg(parameterList[1]).arg(halfopChannels.join(" "))
+ );
+ }
+ if(opChannels.count())
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is an operator on channels: %2")
+ .arg(parameterList[1]).arg(opChannels.join(" "))
+ );
+ }
+ if(ownerChannels.count())
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is owner of channels: %2")
+ .arg(parameterList[1]).arg(ownerChannels.join(" "))
+ );
+ }
+ if(adminChannels.count())
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is admin on channels: %2")
+ .arg(parameterList[1]).arg(adminChannels.join(" "))
+ );
+ }
+ }
+ break;
+ }
+ case RPL_WHOISSERVER:
+ {
+ NickInfo* nickInfo = server->getNickInfo(parameterList[1]);
+ if(nickInfo)
+ {
+ nickInfo->setNetServer(parameterList[2]);
+ nickInfo->setNetServerInfo(trailing);
+ // Clear the away state on assumption that if nick is away, this message will be followed
+ // by a 301 RPL_AWAY message. Not necessary a invalid assumption, but what can we do?
+ nickInfo->setAway(false);
+ nickInfo->setAwayMessage(QString());
+ }
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is online via %2 (%3).").arg(parameterList[1])
+ .arg(parameterList[2]).arg(trailing)
+ );
+ }
+ break;
+ }
+ case RPL_WHOISHELPER:
+ {
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 is available for help.")
+ .arg(parameterList[1])
+ );
+ }
+ break;
+ }
+ case RPL_WHOISOPERATOR:
+ {
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ if (trailing.lower().simplifyWhiteSpace().startsWith("is an irc operator"))
+ server->appendMessageToFrontmost(i18n("Whois"),i18n("%1 is an IRC Operator.").arg(parameterList[1]));
+ else
+ server->appendMessageToFrontmost(i18n("Whois"),QString("%1 %2").arg(parameterList[1]).arg(trailing));
+ }
+ break;
+ }
+ case RPL_WHOISIDLE:
+ {
+ // get idle time in seconds
+ long seconds=parameterList[2].toLong();
+ long minutes=seconds/60;
+ long hours =minutes/60;
+ long days =hours/24;
+
+ // if idle time is longer than a day
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ if(days)
+ {
+ const QString daysString = i18n("1 day", "%n days", days);
+ const QString hoursString = i18n("1 hour", "%n hours", (hours % 24));
+ const QString minutesString = i18n("1 minute", "%n minutes", (minutes % 60));
+ const QString secondsString = i18n("1 second", "%n seconds", (seconds % 60));
+
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 = name of person, %2 = (x days), %3 = (x hours), %4 = (x minutes), %5 = (x seconds)",
+ "%1 has been idle for %2, %3, %4, and %5.")
+ .arg(parameterList[1])
+ .arg(daysString).arg(hoursString).arg(minutesString).arg(secondsString)
+ );
+ // or longer than an hour
+ }
+ else if(hours)
+ {
+ const QString hoursString = i18n("1 hour", "%n hours", hours);
+ const QString minutesString = i18n("1 minute", "%n minutes", (minutes % 60));
+ const QString secondsString = i18n("1 second", "%n seconds", (seconds % 60));
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 = name of person, %2 = (x hours), %3 = (x minutes), %4 = (x seconds)",
+ "%1 has been idle for %2, %3, and %4.")
+ .arg(parameterList[1])
+ .arg(hoursString).arg(minutesString).arg(secondsString)
+ );
+ // or longer than a minute
+ }
+ else if(minutes)
+ {
+ const QString minutesString = i18n("1 minute", "%n minutes", minutes);
+ const QString secondsString = i18n("1 second", "%n seconds", (seconds % 60));
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 = name of person, %2 = (x minutes), %3 = (x seconds)",
+ "%1 has been idle for %2 and %3.")
+ .arg(parameterList[1])
+ .arg(minutesString).arg(secondsString)
+ );
+ // or just some seconds
+ }
+ else
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 has been idle for 1 second.", "%1 has been idle for %n seconds.", seconds)
+ .arg(parameterList[1])
+ );
+ }
+ }
+
+ if(parameterList.count()==4)
+ {
+ QDateTime when;
+ when.setTime_t(parameterList[3].toUInt());
+ NickInfo* nickInfo = server->getNickInfo(parameterList[1]);
+ if(nickInfo)
+ {
+ nickInfo->setOnlineSince(when);
+ }
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),
+ i18n("%1 has been online since %2.")
+ .arg(parameterList[1]).arg(when.toString(Qt::LocalDate))
+ );
+ }
+ }
+ break;
+ }
+ case RPL_ENDOFWHOIS:
+ {
+ //NickInfo* nickInfo = server->getNickInfo(parameterList[1]);
+ // Display message only if this was not an automatic request.
+ if(getAutomaticRequest("WHOIS",parameterList[1])==0)
+ {
+ server->appendMessageToFrontmost(i18n("Whois"),i18n("End of WHOIS list."));
+ }
+ // was this an automatic request?
+ if(getAutomaticRequest("WHOIS",parameterList[1])!=0)
+ {
+ setAutomaticRequest("WHOIS",parameterList[1],false);
+ }
+ break;
+ }
+ case RPL_USERHOST:
+ {
+ // iterate over all nick/masks in reply
+ QStringList uhosts=QStringList::split(" ",trailing);
+
+ for(unsigned int index=0;index<uhosts.count();index++)
+ {
+ // extract nickname and hostmask from reply
+ QString nick(uhosts[index].section('=',0,0));
+ QString mask(uhosts[index].section('=',1));
+
+ // get away and IRC operator flags
+ bool away=(mask[0]=='-');
+ bool ircOp=(nick[nick.length()-1]=='*');
+
+ // cut flags from nick/hostmask
+ mask=mask.mid(1);
+ if(ircOp)
+ {
+ nick=nick.left(nick.length()-1);
+ }
+
+ // inform server of this user's data
+ emit userhost(nick,mask,away,ircOp);
+
+ // display message only if this was no automatic request
+ if(getAutomaticRequest("USERHOST",nick)==0)
+ {
+ server->appendMessageToFrontmost(i18n("Userhost"),
+ i18n("%1 = nick, %2 = shows if nick is op, %3 = hostmask, %4 = shows away", "%1%2 is %3%4.")
+ .arg(nick)
+ .arg((ircOp) ? i18n(" (IRC Operator)") : QString())
+ .arg(mask)
+ .arg((away) ? i18n(" (away)") : QString()));
+ }
+
+ // was this an automatic request?
+ if(getAutomaticRequest("USERHOST",nick)!=0)
+ {
+ setAutomaticRequest("USERHOST",nick,false);
+ }
+ } // for
+ break;
+ }
+ case RPL_LISTSTART: //FIXME This reply is obsolete!!!
+ {
+ if(getAutomaticRequest("LIST",QString())==0)
+ {
+ server->appendMessageToFrontmost(i18n("List"),i18n("List of channels:"));
+ }
+ break;
+ }
+ case RPL_LIST:
+ {
+ if(getAutomaticRequest("LIST",QString())==0)
+ {
+ QString message;
+ message=i18n("%1 (%n user): %2", "%1 (%n users): %2", parameterList[2].toInt());
+ server->appendMessageToFrontmost(i18n("List"),message.arg(parameterList[1]).arg(trailing));
+ }
+ else // send them to /LIST window
+ {
+ emit addToChannelList(parameterList[1],parameterList[2].toInt(),trailing);
+ }
+
+ break;
+ }
+ case RPL_LISTEND:
+ {
+ // was this an automatic request?
+ if(getAutomaticRequest("LIST",QString())==0)
+ {
+ server->appendMessageToFrontmost(i18n("List"),i18n("End of channel list."));
+ }
+ else
+ {
+ setAutomaticRequest("LIST",QString(),false);
+ }
+ break;
+ }
+ case RPL_NOWAWAY:
+ {
+ NickInfo* nickInfo = server->getNickInfo(parameterList[0]);
+ if (nickInfo) nickInfo->setAway(true);
+
+ server->setAway(true);
+
+ break;
+ }
+ case RPL_UNAWAY:
+ {
+ NickInfo* nickInfo = server->getNickInfo(parameterList[0]);
+
+ if (nickInfo)
+ {
+ nickInfo->setAway(false);
+ nickInfo->setAwayMessage(QString());
+ }
+
+ server->setAway(false);
+
+ break;
+ }
+ case RPL_BANLIST:
+ {
+ if (getAutomaticRequest("BANLIST", parameterList[1]))
+ {
+ server->addBan(parameterList[1], parameterList.join(" ").section(' ', 2, 4));
+ } else {
+ QDateTime when;
+ when.setTime_t(parameterList[4].toUInt());
+
+ server->appendMessageToFrontmost(i18n("BanList:%1").arg(parameterList[1]), i18n("BanList message: e.g. *!*@aol.com set by MrGrim on <date>", "%1 set by %2 on %3").arg(parameterList[2]).arg(parameterList[3].section('!', 0, 0)).arg(when.toString(Qt::LocalDate)));
+ }
+ break;
+ }
+ case RPL_ENDOFBANLIST:
+ {
+ if (getAutomaticRequest("BANLIST", parameterList[1]))
+ {
+ setAutomaticRequest("BANLIST", parameterList[1], false);
+ } else {
+ server->appendMessageToFrontmost(i18n("BanList:%1").arg(parameterList[1]), i18n("End of Ban List."));
+ }
+ break;
+ }
+ case ERR_NOCHANMODES:
+ {
+ ChatWindow *chatwindow = server->getChannelByName(parameterList[1]);
+ if(chatwindow)
+ {
+ chatwindow->appendServerMessage(i18n("Channel"), trailing);
+ }
+ else // We couldn't join the channel , so print the error. with [#channel] : <Error Message>
+ {
+ server->appendMessageToFrontmost(i18n("Channel"), trailing);
+ }
+ break;
+ }
+ case ERR_NOSUCHSERVER:
+ {
+ //Some servers don't know their name, so they return an error instead of the PING data
+ if (getLagMeasuring() && trailing.startsWith(prefix))
+ {
+ server->pongReceived();
+ }
+ break;
+ }
+ case ERR_UNAVAILRESOURCE:
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("%1 is currently unavailable.").arg(parameterList[1]));
+
+ break;
+ }
+ case RPL_HIGHCONNECTCOUNT:
+ case RPL_LUSERCLIENT:
+ case RPL_LUSEROP:
+ case RPL_LUSERUNKNOWN:
+ case RPL_LUSERCHANNELS:
+ case RPL_LUSERME:
+ {
+ server->appendStatusMessage(i18n("Users"), parameterList.join(" ").section(' ',1) + ' '+trailing);
+ break;
+ }
+ case ERR_UNKNOWNCOMMAND:
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("%1: Unknown command.").arg(parameterList[1]));
+
+ break;
+ }
+ case ERR_NOTREGISTERED:
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("Not registered."));
+
+ break;
+ }
+ case ERR_NEEDMOREPARAMS:
+ {
+ server->appendMessageToFrontmost(i18n("Error"),i18n("%1: This command requires more parameters.").arg(parameterList[1]));
+
+ break;
+ }
+ case RPL_CAPAB: // Special freenode reply afaik
+ {
+ // Disable as we don't use this for anything yet
+ if(trailing.contains("IDENTIFY-MSG"))
+ {
+ server->enableIdentifyMsg(true);
+ break;
+ }
+
+ /* don't break; - this is also used as RPL_DATASTR on ircu and some others */
+ }
+ // FALLTHROUGH to default to let the error display otherwise
+ default:
+ {
+ // All yet unknown messages go into the frontmost window without the
+ // preceding nickname
+ server->appendMessageToFrontmost(command, parameterList.join(" ").section(' ',1) + ' '+trailing);
+ }
+ } // end of numeric switch
+ }
+}
+
+void InputFilter::parseModes(const QString &sourceNick, const QStringList &parameterList)
+{
+ const QString modestring=parameterList[1];
+
+ if (!isAChannel(parameterList[0]))
+ {
+ QString message;
+ if (parameterList[0] == server->getNickname())
+ {
+ if (sourceNick == server->getNickname())
+ {
+ //XXX someone might care about the potentially unnecessary plural here
+ message = i18n("You have set personal modes: ") + modestring;
+ }
+ else
+ { //XXX someone might care about the potentially unnecessary plural here
+ message = QString("%1 %2 %3").arg(sourceNick).arg(i18n("has changed your personal modes:")).arg(modestring);
+ }
+ }
+ if (!message.isEmpty())
+ server->appendStatusMessage(i18n("Mode"), message);
+ return;
+ }
+
+ bool plus=false;
+ int parameterIndex=0;
+ // List of modes that need a parameter (note exception with -k and -l)
+ // Mode q is quiet on freenode and acts like b... if this is a channel mode on other
+ // networks then more logic is needed here. --MrGrim
+ QString parameterModes="aAoOvhkbleIq";
+ QString message = sourceNick + i18n(" sets mode: ") + modestring;
+
+ for(unsigned int index=0;index<modestring.length();index++)
+ {
+ unsigned char mode=modestring[index];
+ QString parameter;
+
+ // Check if this is a mode or a +/- qualifier
+ if(mode=='+' || mode=='-')
+ {
+ plus=(mode=='+');
+ }
+ else
+ {
+ // Check if this was a parameter mode
+ if(parameterModes.find(mode)!=-1)
+ {
+ // Check if the mode actually wants a parameter. -k and -l do not!
+ if(plus || (!plus && (mode!='k') && (mode!='l')))
+ {
+ // Remember the mode parameter
+ parameter=parameterList[2+parameterIndex];
+ message += ' ' + parameter;
+ // Switch to next parameter
+ ++parameterIndex;
+ }
+ }
+ // Let the channel update its modes
+ if(parameter.isEmpty()) // XXX Check this to ensure the braces are in the correct place
+ {
+ kdDebug() << "in updateChannelMode. sourceNick: '" << sourceNick << "' parameterlist: '"
+ << parameterList.join(", ") << "'"
+ << endl;
+ }
+ server->updateChannelMode(sourceNick,parameterList[0],mode,plus,parameter);
+ }
+ } // endfor
+
+ if (Preferences::useLiteralModes())
+ {
+ server->appendCommandMessageToChannel(parameterList[0],i18n("Mode"),message);
+ }
+}
+
+// # & + and ! are *often*, but not necessarily, Channel identifiers. + and ! are non-RFC,
+// so if a server doesn't offer 005 and supports + and ! channels, I think thats broken behaviour
+// on their part - not ours. --Argonel
+bool InputFilter::isAChannel(const QString &check)
+{
+ Q_ASSERT(server);
+ // if we ever see the assert, we need the ternary
+ return server? server->isAChannel(check) : QString("#&").contains(check.at(0));
+}
+
+bool InputFilter::isIgnore(const QString &sender, Ignore::Type type)
+{
+ bool doIgnore = false;
+
+ QPtrList<Ignore> list = Preferences::ignoreList();
+
+ for(unsigned int index =0; index<list.count(); index++)
+ {
+ Ignore* item = list.at(index);
+ QRegExp ignoreItem(QRegExp::escape(item->getName()).replace("\\*", "(.*)"),false);
+ if (ignoreItem.exactMatch(sender) && (item->getFlags() & type))
+ doIgnore = true;
+ if (ignoreItem.exactMatch(sender) && (item->getFlags() & Ignore::Exception))
+ return false;
+ }
+
+ return doIgnore;
+}
+
+void InputFilter::reset()
+{
+ automaticRequest.clear();
+ whoRequestList.clear();
+}
+
+void InputFilter::setAutomaticRequest(const QString& command, const QString& name, bool yes)
+{
+ automaticRequest[command][name.lower()] += (yes) ? 1 : -1;
+ if(automaticRequest[command][name.lower()]<0)
+ {
+ kdDebug() << "InputFilter::automaticRequest( " << command << ", " << name
+ << " ) was negative! Resetting!"
+ << endl;
+ automaticRequest[command][name.lower()]=0;
+ }
+}
+
+int InputFilter::getAutomaticRequest(const QString& command, const QString& name)
+{
+ return automaticRequest[command][name.lower()];
+}
+
+void InputFilter::addWhoRequest(const QString& name) { whoRequestList << name.lower(); }
+
+bool InputFilter::isWhoRequestUnderProcess(const QString& name) { return (whoRequestList.contains(name.lower())>0); }
+
+void InputFilter::setLagMeasuring(bool state) { lagMeasuring=state; }
+
+bool InputFilter::getLagMeasuring() { return lagMeasuring; }
+
+void InputFilter::parsePrivMsg(const QString& prefix,
+ const QStringList& parameterList,
+ const QString& trailing)
+{
+ int pos = prefix.find("!");
+ QString source;
+ QString sourceHostmask;
+ QString message(trailing);
+
+ if(pos > 0)
+ {
+ source = prefix.left(pos);
+ sourceHostmask = prefix.mid(pos + 1);
+ }
+ else
+ {
+ source = prefix;
+ }
+
+ KonversationApplication* konv_app = static_cast<KonversationApplication*>(kapp);
+ message = konv_app->doAutoreplace(message, false);
+
+ if(isAChannel(parameterList[0]))
+ {
+ if(!isIgnore(prefix, Ignore::Channel))
+ {
+ Channel* channel = server->getChannelByName(parameterList[0]);
+ if(channel)
+ {
+ channel->append(source, message);
+
+ if(source != server->getNickname())
+ {
+ QRegExp regexp("(^|[^\\d\\w])" +
+ QRegExp::escape(server->loweredNickname()) +
+ "([^\\d\\w]|$)");
+ regexp.setCaseSensitive(false);
+ if(message.find(regexp) !=-1 )
+ {
+ konv_app->notificationHandler()->nick(channel,
+ source, message);
+ }
+ else
+ {
+ konv_app->notificationHandler()->message(channel,
+ source, message);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if(!isIgnore(prefix,Ignore::Query))
+ {
+ NickInfoPtr nickinfo = server->obtainNickInfo(source);
+ nickinfo->setHostmask(sourceHostmask);
+
+ // Create a new query (server will check for dupes)
+ query = server->addQuery(nickinfo, false /*we didn't initiate this*/ );
+
+ // send action to query
+ query->appendQuery(source, message);
+
+ if(source != server->getNickname() && query)
+ {
+ QRegExp regexp("(^|[^\\d\\w])" +
+ QRegExp::escape(server->loweredNickname()) +
+ "([^\\d\\w]|$)");
+ regexp.setCaseSensitive(false);
+ if(message.find(regexp) !=-1 )
+ {
+ konv_app->notificationHandler()->nick(query,
+ source, message);
+ }
+ else
+ {
+ konv_app->notificationHandler()->queryMessage(query,
+ source, message);
+ }
+ }
+ }
+ }
+}
+
+#include "inputfilter.moc"
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/inputfilter.h b/konversation/src/inputfilter.h
new file mode 100644
index 0000000..2edb985
--- /dev/null
+++ b/konversation/src/inputfilter.h
@@ -0,0 +1,94 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef INPUTFILTER_H
+#define INPUTFILTER_H
+
+#include "ignore.h"
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+
+class Server;
+class QWidget;
+class Query;
+class QDateTime;
+
+class InputFilter : public QObject
+{
+ Q_OBJECT
+
+ public:
+ InputFilter();
+ ~InputFilter();
+
+ void setServer(Server* newServer);
+ void parseLine(const QString &line);
+
+ void reset(); // reset AutomaticRequest, WhoRequestList
+
+ // use this when the client does automatics, like userhost for finding hostmasks
+ void setAutomaticRequest(const QString& command, const QString& name, bool yes);
+ int getAutomaticRequest(const QString& command, const QString& name);
+ void addWhoRequest(const QString& name); // called from Server::send()
+ // to avoid duplicate requests
+ bool isWhoRequestUnderProcess(const QString& name);
+ void setLagMeasuring(bool yes);
+ bool getLagMeasuring();
+
+ signals:
+ void welcome(const QString& ownHost);
+ void notifyResponse(const QString &nicksOnline);
+ // will be connected to Server::startReverseDccSendTransfer()
+ void startReverseDccSendTransfer(const QString &sourceNick, const QStringList &dccArgument);
+ // will be connected to Server::addDccGet()
+ void addDccGet(const QString &sourceNick, const QStringList &dccArgument);
+ // will be connected to Server::resumeDccGetTransfer()
+ void resumeDccGetTransfer(const QString &sourceNick, const QStringList &dccArgument);
+ // will be connected to Server::resumeDccSendTransfer()
+ void resumeDccSendTransfer(const QString &sourceNick, const QStringList &dccArgument);
+ // will be connected to Server::userhost()
+ void userhost(const QString& nick,const QString& hostmask,bool away,bool ircOp);
+ // will be connected to Server::setTopicAuthor()
+ void topicAuthor(const QString& channel, const QString& author, QDateTime t);
+ void endOfWho(const QString& target); // for scheduling auto /WHO
+ void addChannelListPanel();
+ void addToChannelList(const QString& channel,int users,const QString& topic);
+ void invitation(const QString& nick,const QString& channel);
+
+ void addDccChat(const QString& myNick,const QString& nick,const QStringList& arguments,bool listen);
+
+ protected:
+ void parseClientCommand(const QString &prefix, const QString &command, const QStringList &parameterList, const QString &trailing);
+ void parseServerCommand(const QString &prefix, const QString &command, const QStringList &parameterList, const QString &trailing);
+ void parseModes(const QString &sourceNick, const QStringList &parameterList);
+ void parsePrivMsg(const QString& prefix, const QStringList& parameterList, const QString& trailing);
+
+ bool isAChannel(const QString &check);
+ bool isIgnore(const QString &pattern, Ignore::Type type);
+
+ Server* server;
+ // automaticRequest[command][channel or nick]=count
+ QMap< QString, QMap<QString,int> > automaticRequest;
+ QStringList whoRequestList;
+ int lagMeasuring;
+
+ Query* query;
+
+ int m_debugCount;
+
+ /// Used when handling MOTD
+ bool m_connecting;
+};
+#endif
diff --git a/konversation/src/insertchardialog.cpp b/konversation/src/insertchardialog.cpp
new file mode 100644
index 0000000..3b5c080
--- /dev/null
+++ b/konversation/src/insertchardialog.cpp
@@ -0,0 +1,57 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#include "insertchardialog.h"
+
+#include <kcharselect.h>
+#include <klocale.h>
+#include <kguiitem.h>
+
+
+namespace Konversation
+{
+
+ InsertCharDialog::InsertCharDialog(const QString& font, QWidget *parent, const char *name)
+ : KDialogBase(parent, name, false, i18n("Insert Character"),
+ KDialogBase::Ok | KDialogBase::Close,
+ KDialogBase::Ok, false)
+ {
+ setButtonOK(KGuiItem(i18n("&Insert"), "ok", i18n("Insert a character")));
+
+ m_charTable = new KCharSelect(this, "charTable", font);
+ m_charTable->enableFontCombo(false);
+ setMainWidget(m_charTable);
+
+ connect(m_charTable, SIGNAL(doubleClicked()), this, SLOT(slotOk()));
+ }
+
+ InsertCharDialog::~InsertCharDialog()
+ {
+ }
+
+ void InsertCharDialog::setFont(const QFont &font)
+ {
+ m_charTable->setFont(font.family());
+ }
+
+ QChar InsertCharDialog::chr()
+ {
+ return m_charTable->chr();
+ }
+
+ void InsertCharDialog::slotOk()
+ {
+ emit insertChar(m_charTable->chr());
+ }
+
+}
+
+#include "insertchardialog.moc"
diff --git a/konversation/src/insertchardialog.h b/konversation/src/insertchardialog.h
new file mode 100644
index 0000000..a2c0001
--- /dev/null
+++ b/konversation/src/insertchardialog.h
@@ -0,0 +1,46 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#ifndef KONVERSATIONINSERTCHARDIALOG_H
+#define KONVERSATIONINSERTCHARDIALOG_H
+
+#include <kdialogbase.h>
+
+
+class KCharSelect;
+class QChar;
+
+namespace Konversation
+{
+
+ class InsertCharDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+ explicit InsertCharDialog(const QString& font = QString(), QWidget *parent = 0, const char *name = 0);
+ ~InsertCharDialog();
+
+ void setFont(const QFont &font);
+ QChar chr();
+
+ protected slots:
+ virtual void slotOk();
+
+ signals:
+ void insertChar(const QChar&);
+
+ private:
+ KCharSelect* m_charTable;
+ };
+
+}
+#endif
diff --git a/konversation/src/irccharsets.cpp b/konversation/src/irccharsets.cpp
new file mode 100644
index 0000000..89fdf4f
--- /dev/null
+++ b/konversation/src/irccharsets.cpp
@@ -0,0 +1,177 @@
+// A wrapper for KCharsets
+// Copyright (C) 2004, 2006 Shintaro Matsuoka <shin@shoegazed.org>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#include "irccharsets.h"
+
+#include <qglobal.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <kcharsets.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstaticdeleter.h>
+
+#if QT_VERSION < 0x030300
+#include <klocale.h>
+#else
+#include <qlocale.h>
+#endif
+
+
+namespace Konversation
+{
+
+ IRCCharsets* IRCCharsets::s_self = 0;
+ static KStaticDeleter<IRCCharsets> staticIRCCharsetDeleter;
+
+ IRCCharsets *IRCCharsets::self()
+ {
+ if(!s_self)
+ staticIRCCharsetDeleter.setObject(s_self, new IRCCharsets());
+ return s_self;
+ }
+
+ QStringList IRCCharsets::availableEncodingShortNames()
+ {
+ return m_shortNames;
+ }
+
+ QStringList IRCCharsets::availableEncodingDescriptiveNames()
+ {
+ return m_descriptiveNames;
+ }
+
+ int IRCCharsets::availableEncodingsCount()
+ {
+ return m_shortNames.count();
+ }
+
+ QString IRCCharsets::shortNameToDescriptiveName( const QString& shortName )
+ {
+ return m_descriptiveNames[ shortNameToIndex( shortName ) ];
+ }
+
+ QString descriptiveNameToShortName( const QString& descriptiveName )
+ {
+ return KGlobal::charsets()->encodingForName( descriptiveName );
+ }
+
+ QString IRCCharsets::ambiguousNameToShortName( const QString& ambiguousName )
+ {
+ // simplify ambiguousName
+ QString simplifiedAmbiguousName( ambiguousName.lower() );
+ simplifiedAmbiguousName.replace( QRegExp( "[^a-z0-9]" ), "" );
+
+ // search m_simplifiedShortNames
+ int index = 0;
+ for ( QStringList::iterator it = m_simplifiedShortNames.begin() ; it != m_simplifiedShortNames.end() ; ++it )
+ {
+ if ( (*it) == simplifiedAmbiguousName )
+ return m_shortNames[index];
+ ++index;
+ }
+
+ // search m_shortNameAliases
+ if ( m_shortNameAliases.contains( simplifiedAmbiguousName ) )
+ return m_shortNameAliases[ simplifiedAmbiguousName ];
+
+ // failed
+ return QString();
+ }
+
+ int IRCCharsets::shortNameToIndex( const QString& shortName )
+ {
+ int index = 0;
+ for ( QStringList::iterator it = m_shortNames.begin() ; it != m_shortNames.end() ; ++it )
+ {
+ if ( (*it) == shortName )
+ return index;
+ ++index;
+ }
+ return -1;
+ }
+
+ bool IRCCharsets::isValidEncoding( const QString& shortName )
+ {
+ return ( m_shortNames.contains( shortName ) > 0 );
+ }
+
+ QString IRCCharsets::encodingForLocale()
+ {
+ #if QT_VERSION < 0x030300
+ QString locale = KLocale::defaultLanguage();
+ #else
+ QString locale = QLocale::system().name();
+ #endif
+
+ // Special cases
+ // don't add conditions for the languages for which QTextCodec::codecForLocale() returns a correct codec.
+ if ( locale == "ja_JP" )
+ return "jis7";
+
+ // it's a little hacky..
+ for ( QStringList::iterator it = m_shortNames.begin() ; it != m_shortNames.end() ; ++it )
+ if ( QTextCodec::codecForName( (*it).ascii() ) == QTextCodec::codecForLocale() )
+ return *it;
+
+ return "utf8";
+ }
+
+ QTextCodec* IRCCharsets::codecForName( const QString& shortName )
+ {
+ if(shortName == "iso-2022-jp")
+ return QTextCodec::codecForName( "jis7" );
+ else
+ return QTextCodec::codecForName( shortName.ascii() );
+ }
+
+ IRCCharsets::IRCCharsets()
+ {
+ s_self = this;
+
+ // setup m_shortNameAliases
+ // use only [a-z0-9] for keys!
+ m_shortNameAliases["unicode"] = "utf8";
+ m_shortNameAliases["latin1"] = "iso-8859-1";
+
+ // setup m_shortNames, m_descriptiveNames, m_simplifiedShortNames
+ QRegExp reSimplify( "[^a-zA-Z0-9]" );
+ m_descriptiveNames = KGlobal::charsets()->descriptiveEncodingNames();
+ QStringList::Iterator it = m_descriptiveNames.begin();
+ while ( it != m_descriptiveNames.end() )
+ {
+ QString encodingName = KGlobal::charsets()->encodingForName( *it );
+ // exclude encodings which are not supported on IRC
+ if ( encodingName == "iso-10646-ucs-2" ||
+ encodingName == "ucs2" ||
+ encodingName == "utf16" ||
+ encodingName == "utf7" )
+ {
+ it = m_descriptiveNames.remove( it );
+ }
+ else
+ {
+ m_shortNames.append( encodingName );
+ m_simplifiedShortNames.append( encodingName.replace( reSimplify, "" ) );
+
+ if(encodingName == "jis7") // Add iso-2022-jp which is same as jis7 but not in Qt
+ {
+ it = m_descriptiveNames.insert(it, "Japanese ( iso-2022-jp )");
+ m_shortNames.append( "iso-2022-jp" );
+ m_simplifiedShortNames.append( "ISO-2022-JP" );
+ ++it;
+ }
+ ++it;
+ }
+
+ }
+ }
+
+}
diff --git a/konversation/src/irccharsets.h b/konversation/src/irccharsets.h
new file mode 100644
index 0000000..1f7facd
--- /dev/null
+++ b/konversation/src/irccharsets.h
@@ -0,0 +1,106 @@
+// A wrapper for KCharsets
+// Copyright (C) 2004 Shintaro Matsuoka <shin@shoegazed.org>
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#ifndef KONVERSATION_IRCCHARSETS_H
+#define KONVERSATION_IRCCHARSETS_H
+
+#include <qmap.h>
+#include <qstringlist.h>
+
+
+namespace Konversation
+{
+
+ class IRCCharsets
+ {
+ private:
+ IRCCharsets();
+
+ public:
+ static IRCCharsets *self();
+
+ /**
+ * Lists all available encoding names.
+ * e.g. "utf8", "iso 8859-1"
+ * Encodings which don't work on IRC are excluded. (e.g. utf16)
+ * @note It's guaranteed that the order of this list is same with that of @ref availableEncodingDescriptiveNames() .
+ */
+ QStringList availableEncodingShortNames();
+
+ /**
+ * Lists all available encoding descriptions.
+ * e.g. "Unicode ( utf8 )", "Western European ( iso 8859-1 )"
+ * Encodings which don't work on IRC are excluded. (e.g. utf16)
+ */
+ QStringList availableEncodingDescriptiveNames();
+
+ int availableEncodingsCount();
+
+ QString shortNameToDescriptiveName( const QString& shortName );
+ QString descriptiveNameToShortName( const QString& descriptiveName );
+
+ /**
+ * Converts the ambiguous encoding name to a short encoding name
+ * Like : iso8859-9 -> iso 8859-9, iso-8859-9 -> iso 8859-9
+ * If the ambiguous name is invalid, returns QString:null.
+ * @return a short encoding name or QString::null
+ */
+ QString ambiguousNameToShortName( const QString& ambiguousName );
+
+ /**
+ * Returns the encoding index in the short names list or the descriptions list.
+ * If the encoding name is invalid, returns -1.
+ * @return an index number of the encoding
+ */
+ int shortNameToIndex( const QString& shortName );
+
+ /**
+ * Checks if the encoding name is in the short encoding names.
+ * @see availableEncodingShortNames()
+ */
+ bool isValidEncoding( const QString& shortName );
+
+ /**
+ * Returns the short name of the most suitable encoding for this locale.
+ * @return a short encoding name
+ */
+ QString encodingForLocale();
+
+ QTextCodec* codecForName( const QString& shortName );
+
+ private:
+ QMap<QString,QString> m_shortNameAliases;
+
+ /**
+ * short names list
+ * you can get this list with @ref availableEncodingShortNames()
+ * e.g. iso 8859-1
+ */
+ QStringList m_shortNames;
+
+ /**
+ * descriptive names list
+ * you can get this list with @ref availableEncodingDescriptiveNames();
+ * e.g. Western European ( iso 8859-1 )
+ */
+ QStringList m_descriptiveNames;
+
+ /**
+ * simplified short names list (for internal use)
+ * e.g. iso88591
+ * used in @ref ambiguousNameToShortName()
+ */
+ QStringList m_simplifiedShortNames;
+
+ static IRCCharsets *s_self;
+ };
+
+}
+#endif // KONVERSATION_IRCCHARSETS_H
diff --git a/konversation/src/irccolorchooser.cpp b/konversation/src/irccolorchooser.cpp
new file mode 100644
index 0000000..3cf6ccf
--- /dev/null
+++ b/konversation/src/irccolorchooser.cpp
@@ -0,0 +1,83 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ dialog used to add irc colors to your messages
+ begin: Wed 9 July 2003
+ copyright: (C) 2003 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#include "irccolorchooser.h"
+#include "irccolorchooserui.h"
+#include "config/preferences.h"
+
+#include <qlabel.h>
+#include <qpixmap.h>
+
+#include <klocale.h>
+#include <kcombobox.h>
+
+
+IRCColorChooser::IRCColorChooser(QWidget* parent, const char* name)
+: KDialogBase(parent, name, true, i18n("IRC Color Chooser"), Ok|Cancel, Ok)
+{
+ m_view = new IRCColorChooserUI(this);
+ setMainWidget(m_view);
+ initColors(m_view->m_fgColorCBox);
+ initColors(m_view->m_bgColorCBox);
+ m_view->m_bgColorCBox->insertItem(i18n("None"), 0);
+
+ connect(m_view->m_fgColorCBox, SIGNAL(activated(int)), this, SLOT(updatePreview()));
+ connect(m_view->m_bgColorCBox, SIGNAL(activated(int)), this, SLOT(updatePreview()));
+ m_view->m_fgColorCBox->setCurrentItem(1);
+ m_view->m_bgColorCBox->setCurrentItem(0);
+ updatePreview();
+}
+
+QString IRCColorChooser::color()
+{
+ QString s;
+ s = "%C" + QString::number(m_view->m_fgColorCBox->currentItem());
+
+ if(m_view->m_bgColorCBox->currentItem() > 0)
+ {
+ s += ',' + QString::number(m_view->m_bgColorCBox->currentItem() - 1);
+ }
+
+ return s;
+}
+
+void IRCColorChooser::updatePreview()
+{
+ QColor bgc;
+
+ if(m_view->m_bgColorCBox->currentItem() > 0)
+ {
+ bgc = Preferences::ircColorCode(m_view->m_bgColorCBox->currentItem() - 1);
+ }
+ else
+ {
+ bgc = Preferences::color(Preferences::TextViewBackground);
+ }
+
+ m_view->m_previewLbl->setBackgroundColor(bgc);
+ m_view->m_previewLbl->setPaletteForegroundColor(Preferences::ircColorCode(m_view->m_fgColorCBox->currentItem()));
+}
+
+void IRCColorChooser::initColors(KComboBox* combo)
+{
+ QPixmap pix(width(), combo->fontMetrics().height() + 4);
+
+ for (int i =0; i < 15; i++)
+ {
+ pix.fill(Preferences::ircColorCode(i));
+ combo->insertItem(pix, i);
+ }
+}
+
+#include "irccolorchooser.moc"
diff --git a/konversation/src/irccolorchooser.h b/konversation/src/irccolorchooser.h
new file mode 100644
index 0000000..d1357fa
--- /dev/null
+++ b/konversation/src/irccolorchooser.h
@@ -0,0 +1,41 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ dialog used to add irc colors to your messages
+ begin: Wed 9 July 2003
+ copyright: (C) 2003 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#ifndef IRCCOLORCHOOSER_H
+#define IRCCOLORCHOOSER_H
+
+#include <kdialogbase.h>
+
+
+class IRCColorChooserUI;
+class KComboBox;
+class Preferences;
+
+class IRCColorChooser : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ explicit IRCColorChooser(QWidget* parent, const char* name = 0);
+ QString color();
+
+ protected slots:
+ void updatePreview();
+
+ protected:
+ void initColors(KComboBox* combo);
+
+ protected:
+ IRCColorChooserUI* m_view;
+};
+#endif
diff --git a/konversation/src/irccolorchooserui.ui b/konversation/src/irccolorchooserui.ui
new file mode 100644
index 0000000..386824c
--- /dev/null
+++ b/konversation/src/irccolorchooserui.ui
@@ -0,0 +1,148 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>IRCColorChooserUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>IRCColorChooserUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>249</width>
+ <height>122</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_previewLbl</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>Preview</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>This is how your message will look with these colors</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;This is a preview of how your readers may see view your message if you select these colors.&lt;br&gt;
+&lt;b&gt;Note: Not all clients support this, and some users may have chosen to ignore your color changes.&lt;/b&gt;
+&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>m_fgColorCBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The chosen text color is added to the input line.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Here you choose what color you want you the text in the next message you send to be. If you choose a color and click Ok, the chosen color is added to the Input Line. Any text written after this will be in the chosen color, until you change the color again.&lt;br&gt;
+&lt;b&gt;Note: Not all users turn on the option to see this.&lt;/b&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_fgColorLbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Foreground color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_fgColorCBox</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The chosen text color is added to the input line.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Here you choose what color you want you the text in the next message you send to be. If you choose a color and click Ok, the chosen color is added to the Input Line. Any text written after this will be in the chosen color, until you change the color again.&lt;br&gt;
+&lt;b&gt;Note: Not all users turn on the option to see this.&lt;/b&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>m_bgColorCBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The chosen text-background color is added to the input line.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Here you choose what color you want you background for the text in the next message you send to be.
+If you choose a color and click Ok, the chosen color is added to the Input Line.
+Any text written after this will have the chosen color, until you change the color again.&lt;br&gt;
+Choosing the default "&lt;i&gt;None&lt;/i&gt;" will not change the background color of your message, so your readers will view your message with their normal background text color.&lt;br&gt;
+&lt;b&gt;Note: Not all clients support this, and not all users turn on the option to see this.&lt;/b&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>m_bgColorLbl</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Background color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_bgColorCBox</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The chosen text-background color is added to the input line.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Here you choose what color you want you background for the text in the next message you send to be.
+If you choose a color and click Ok, the chosen color is added to the Input Line.
+Any text written after this will have the chosen color, until you change the color again.&lt;br&gt;
+Choosing the default "&lt;i&gt;None&lt;/i&gt;" will not change the background color of your message, so your readers will view your message with their normal background text color.&lt;br&gt;
+&lt;b&gt;Note: Not all clients support this, and not all users turn on the option to see this.&lt;/b&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/ircinput.cpp b/konversation/src/ircinput.cpp
new file mode 100644
index 0000000..b69c787
--- /dev/null
+++ b/konversation/src/ircinput.cpp
@@ -0,0 +1,551 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ The line input widget with chat enhanced functions
+ begin: Tue Mar 5 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "ircinput.h"
+#include "konversationapplication.h"
+#include "multilineedit.h"
+#include "chatwindow.h"
+#include "ircview.h"
+
+#include <private/qrichtext_p.h>
+#include <qclipboard.h>
+#include <qregexp.h>
+#include <qdom.h>
+#include <qevent.h>
+#include <qobject.h>
+#include <qwhatsthis.h>
+#include <qpopupmenu.h>
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kcompletionbox.h>
+
+#define MAXHISTORY 100
+#define RICHTEXT 0
+
+
+IRCInput::IRCInput(QWidget* parent) : KTextEdit(parent)
+{
+ m_lastHeight=document()->height();
+
+ //I am not terribly interested in finding out where this value comes from
+ //nor in compensating for it if my guess is incorrect. so, cache it.
+ m_qtBoxPadding=m_lastHeight-fontMetrics().lineSpacing();
+
+ connect(KApplication::kApplication(), SIGNAL(appearanceChanged()), this, SLOT(updateAppearance()));
+ m_multiRow = Preferences::useMultiRowInputBox();
+
+ m_useSelection = false;
+
+ // connect history signal
+ connect(this,SIGNAL (history(bool)) ,this,SLOT (getHistory(bool)) );
+ // add one empty line to the history (will be overwritten with newest entry)
+ historyList.prepend(QString());
+ // reset history line counter
+ lineNum=0;
+ // reset completion mode
+ setCompletionMode('\0');
+ completionBox = new KCompletionBox(this);
+ connect(completionBox, SIGNAL(activated(const QString&)), this, SLOT(insertCompletion(const QString&)));
+
+ // widget may not be resized vertically
+ setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::Fixed));
+
+ //NoWrap coupled with the size policy constrains the line edit to be one row high
+ setWordWrap(m_multiRow ? WidgetWidth : NoWrap);
+
+ setHScrollBarMode(AlwaysOff);
+ setVScrollBarMode(AlwaysOff);
+ #if RICHTEXT == 1
+ setAutoFormatting(QTextEdit::AutoNone);
+ setTextFormat(RichText);
+ #else
+ setTextFormat(PlainText);
+ #endif
+
+ QWhatsThis::add(this, i18n("<qt>The input line is where you type messages to be sent the channel, query, or server. A message sent to a channel is seen by everyone on the channel, whereas a message in a query is sent only to the person in the query with you.<p>To automatically complete the nickname you began typing, press Tab. If you have not begun typing, the last successfully completed nickname will be used.<p>You can also send special commands:<br><table><tr><th>/me <i>action</i></th><td>shows up as an action in the channel or query. For example: <em>/me sings a song</em> will show up in the channel as 'Nick sings a song'.</td></tr><tr><th>/whois <i>nickname</i></th><td>shows information about this person, including what channels they are in.</td></tr></table><p>For more commands, see the Konversation Handbook.<p>A message cannot contain multiple lines.</qt>"));
+
+ m_disableSpellCheckTimer = new QTimer(this);
+ connect(m_disableSpellCheckTimer, SIGNAL(timeout()), this, SLOT(disableSpellChecking()));
+}
+
+IRCInput::~IRCInput()
+{
+}
+
+void IRCInput::showEvent(QShowEvent* /* e */)
+{
+ m_disableSpellCheckTimer->stop();
+ setCheckSpellingEnabled(Preferences::spellChecking());
+}
+
+void IRCInput::hideEvent(QHideEvent* /* event */)
+{
+ Preferences::setSpellChecking(checkSpellingEnabled());
+
+ // If we disable spell-checking here immediately, tab switching will
+ // be very slow. If we delay it by five seconds, a user would have to
+ // need more than five seconds to switch between all his tabs before
+ // the slowdown starts to occur (show event stops the timer, i.e. wrap-
+ // around is not an issue). Unless he has unlikely amounts of channels,
+ // needing more than five seconds indicates very slow switching speed,
+ // which makes the delay a non-issue to begin with. Hence this fixes
+ // the problem on the surface. In the KDE 4 version, we want to look
+ // into having only one spell-checker instance instead of starting and
+ // stopping at all.
+ m_disableSpellCheckTimer->start(5000, true);
+}
+
+void IRCInput::disableSpellChecking()
+{
+ setCheckSpellingEnabled(false);
+}
+void IRCInput::slotSpellCheckDone(const QString& s)
+{
+ // NOTE: kdelibs 3.5's KSpell stupidly adds newlines to its
+ // buffer at some point for god-knows-what-reason, and for-
+ // gets to remove them again before handing the result back.
+ // There's a FIXME to the effect in KSpell::check. This is
+ // a workaround.
+
+ if (s == text() || s == (text() + '\n'+'\n'))
+ return;
+
+ setText(s.simplifyWhiteSpace());
+}
+
+void IRCInput::updateAppearance()
+{
+ m_multiRow = Preferences::useMultiRowInputBox();
+ setWordWrap(m_multiRow ? WidgetWidth : NoWrap);
+ m_lastHeight=heightForWidth(sizeHint().width());
+ ensureCursorVisible(); //appears to trigger updateGeometry
+}
+
+void IRCInput::resizeContents( int w, int h )
+{
+ if (document()->height() != m_lastHeight) {
+ m_lastHeight=document()->height();
+ updateGeometry();
+ }
+ KTextEdit::resizeContents(w,h);
+}
+
+// widget must be only one line high - luckily QT will enforce this via wrappping policy
+QSize IRCInput::sizeHint() const
+{
+ constPolish();
+
+ int ObscurePadding = 4;
+ int f=2*frameWidth();
+ int w=12 * (kMax(fontMetrics().lineSpacing(),14) + f + ObscurePadding);
+ int h=m_lastHeight - m_qtBoxPadding + f + ObscurePadding;
+ return QSize(w,h);
+}
+
+QPopupMenu *IRCInput::createPopupMenu( const QPoint &pos )
+{
+ QPopupMenu *menu=KTextEdit::createPopupMenu(pos);
+ menu->removeItemAt(menu->count()-1);
+ menu->removeItemAt(menu->count()-1);
+ return menu;
+}
+
+QString IRCInput::text() const
+{
+ #if RICHTEXT == 1
+ QString content=KTextEdit::text();
+
+ QDomDocument document;
+
+ document.setContent(content,false);
+ QDomNodeList nodes=document.elementsByTagName("p");
+ if(nodes.count())
+ {
+ QDomElement node=nodes.item(0).toElement();
+ return node.text();
+ }
+ return QString();
+
+ #else
+ return KTextEdit::text();
+ #endif
+}
+
+void IRCInput::setText(const QString& text)
+{
+ // reimplemented to set cursor at the end of the new text
+ KTextEdit::setText(text);
+ setCursorPosition(0,text.length()+1);
+}
+
+// FIXME - find a better way to do this. eventfilters introduce nebulous behaviour
+//take text events from IRCView and TopicLabel
+bool IRCInput::eventFilter(QObject *object,QEvent *event)
+{
+ if (object->isA("IRCView") || object->isA("Konversation::TopicLabel"))
+ {
+ if (event->type() == QEvent::KeyPress)
+ {
+ QKeyEvent* ke = static_cast<QKeyEvent*>(event);
+
+ // Allow tab to be handled naturally by the widget.
+ // Once it runs out of links it goes to the next control.
+ if (ke->key() == Key_Tab && (ke->state() == 0 || ke->state() == Qt::ShiftButton))
+ return false;
+
+ if (!ke->text().isEmpty() && ((ke->state() & (Qt::ShiftButton|Qt::Keypad)) || ke->state() == 0))
+ {
+ setFocus();
+ KonversationApplication::sendEvent(this,event);
+ return true;
+ }
+ }
+ }
+
+ return KTextEdit::eventFilter(object,event);
+}
+
+// Take care of Tab, Cursor and so on
+void IRCInput::keyPressEvent(QKeyEvent* e)
+{
+ switch(e->key())
+ {
+ case Key_Tab:
+ emit nickCompletion();
+ return;
+ break;
+
+ case Key_Up:
+ if (m_multiRow && (e->state() != (Qt::ShiftButton|Qt::ControlButton)))
+ break;
+ emit history(true);
+ return;
+ break;
+
+ case Key_Down:
+ if (m_multiRow && (e->state() != (Qt::ShiftButton|Qt::ControlButton)))
+ break;
+ emit history(false);
+ return;
+ break;
+
+ case Key_Enter:
+ case Key_Return:
+ {
+ if(text().length()) addHistory(text());
+ if(completionBox->isHidden())
+ {
+ // Reset completion mode
+ setCompletionMode('\0');
+
+ // Ctrl+Enter is a special case in which commands should be send as normal messages
+ if ( e->state() & ControlButton )
+ {
+ emit envelopeCommand();
+ }
+ else
+ {
+ setText(static_cast<KonversationApplication*>(kapp)->doAutoreplace(text(),true));
+ emit submit();
+ }
+ }
+ else
+ {
+ insertCompletion(completionBox->currentText());
+ completionBox->hide();
+ }
+ // prevent widget from adding lines
+ return;
+ }
+ break;
+
+ default:
+ // Check if the keystroke actually produced text. If not it was just a qualifier.
+ if(!e->text().isEmpty() || ((e->key() >= Qt::Key_Home) && (e->key() <= Qt::Key_Down)))
+ {
+ if(getCompletionMode()!='\0')
+ {
+ setCompletionMode('\0');
+ emit endCompletion();
+ }
+
+ completionBox->hide();
+ }
+
+ // support ASCII BEL
+ if(e->ascii() == 7)
+ insert("%G");
+ // support ^U (delete text in input box)
+ else if(e->ascii() == 21)
+ setText("");
+ }
+
+ KTextEdit::keyPressEvent(e);
+}
+
+void IRCInput::addHistory(const QString& line)
+{
+ // Only add line if it's not the same as the last was
+ if(historyList[1]!=line)
+ {
+ // Replace empty first entry with line
+ historyList[0]=line;
+ // Add new empty entry to history
+ historyList.prepend(QString());
+ // Remove oldest line in history, if the list grows beyond MAXHISTORY
+ if(historyList.count()>MAXHISTORY) historyList.remove(historyList.last());
+ }
+ // Reset history counter
+ lineNum=0;
+}
+
+void IRCInput::getHistory(bool up)
+{
+ // preserve text
+ historyList[lineNum]=text();
+ // Did the user press cursor up?
+ if(up)
+ {
+ // increment the line counter
+ lineNum++;
+ // if we are past the end of the list, go to the last entry
+ if(lineNum==historyList.count()) lineNum--;
+ }
+ // no, it was cursor down
+ else
+ {
+ // If we are at the top of the lest, arrow-down shall add the text to the history and clear the field for new input
+ if(lineNum==0)
+ {
+ if(text().length()) addHistory(text());
+ setText("");
+ }
+ // If we aren't at the top of the list, decrement the line counter
+ else
+ {
+ lineNum--;
+ }
+ }
+ // replace the text in the input field with history
+ setText(historyList[lineNum]);
+}
+
+/**
+ * Work around the fact that while QTextEdit::paste() is virtual, whether we are
+ * pasting from middle button or control-V is PRIVATE and NO ACCESSOR is given.
+ */
+void IRCInput::contentsMouseReleaseEvent( QMouseEvent *ev)
+{
+ if (ev->button() == Qt::MidButton)
+ {
+ m_useSelection=true;
+ }
+
+ // Reset completion
+ setCompletionMode('\0');
+ emit endCompletion();
+
+ KTextEdit::contentsMouseReleaseEvent(ev);
+ m_useSelection=false;
+}
+
+void IRCInput::paste(bool useSelection)
+{
+ m_useSelection = useSelection;
+ paste();
+ m_useSelection = false;
+}
+
+void IRCInput::paste()
+{
+ QClipboard *cb = KApplication::kApplication()->clipboard();
+ setFocus();
+
+ // Copy text from the clipboard (paste)
+ QString pasteText;
+ if(m_useSelection)
+ {
+ pasteText = cb->text( QClipboard::Selection);
+ }
+ else
+ {
+ pasteText = cb->text( QClipboard::Clipboard);
+ }
+
+ // is there any text in the clipboard?
+ if(!pasteText.isEmpty())
+ {
+ //End completion on paste
+ setCompletionMode('\0');
+ emit endCompletion();
+
+ bool signal=false;
+
+ // replace \r with \n to make xterm pastes happy
+ pasteText.replace("\r","\n");
+ // remove blank lines
+ while(pasteText.contains("\n\n"))
+ pasteText.replace("\n\n","\n");
+
+ QRegExp reTopSpace("^ *\n");
+ while(pasteText.contains(reTopSpace))
+ pasteText.remove(reTopSpace);
+
+ QRegExp reBottomSpace("\n *$");
+ while(pasteText.contains(reBottomSpace))
+ pasteText.remove(reBottomSpace);
+
+ // Escape % when var expansion is enabled
+ if (!Preferences::disableExpansion())
+ {
+ pasteText.replace ('%', "%%");
+ }
+
+ // does the text contain at least one newline character?
+ if(pasteText.find('\n')!=-1)
+ {
+ // make comparisons easier (avoid signed / unsigned warnings)
+ unsigned int pos=pasteText.find('\n');
+ unsigned int rpos=pasteText.findRev('\n');
+
+ // emit the signal if there's a line break in the middle of the text
+ if(pos>0 && pos!=(pasteText.length()-1))
+ signal=true;
+ // emit the signal if there's more than one line break in the text
+ if(pos!=rpos)
+ signal=true;
+
+ // Remove the \n from end of the line if there's only one \n
+ if(!signal)
+ pasteText.remove('\n');
+ }
+ else
+ {
+ insert(pasteText);
+ return;
+ }
+
+ // should we signal the application due to newlines in the paste?
+ if(signal)
+ {
+ // if there is text in the input line
+ if(!text().isEmpty())
+ {
+ // prepend text to the paste
+ pasteText=text()+'\n'+pasteText;
+ }
+ // ask the user on long pastes
+ if(checkPaste(pasteText))
+ {
+ // signal pasted text
+ emit textPasted(pasteText);
+ // remember old line, in case the user does not paste eventually
+ addHistory(pasteText);
+ // delete input text
+ setText("");
+ }
+ }
+ // otherwise let the KLineEdit handle the pasting
+ else KTextEdit::paste();
+ }
+}
+
+bool IRCInput::checkPaste(QString& text)
+{
+ int doPaste=KMessageBox::Yes;
+
+ //text is now preconditioned when you get here
+ int lines=text.contains('\n');
+
+ if(text.length()>256 || lines)
+ {
+ doPaste=KMessageBox::warningYesNoCancel
+ (this,
+ i18n("<qt>You are attempting to paste a large portion of text (%1 bytes or %2 lines) into "
+ "the chat. This can cause connection resets or flood kills. "
+ "Do you really want to continue?</qt>").arg(text.length()).arg(lines+1),
+ i18n("Large Paste Warning"),
+ i18n("Paste"),
+ i18n("&Edit..."),
+ "LargePaste",
+ KMessageBox::Dangerous);
+ }
+
+ if (doPaste==KMessageBox::No)
+ {
+ QString ret(MultilineEdit::edit(this,text));
+ if (ret.isEmpty())
+ return false;
+ text=ret;
+ return true;
+ }
+
+ return (doPaste==KMessageBox::Yes);
+}
+
+void IRCInput::showCompletionList(const QStringList& nicks)
+{
+ completionBox->setItems(nicks);
+ completionBox->popup();
+}
+
+void IRCInput::insertCompletion(const QString& nick)
+{
+ int pos; // = cursorPosition();
+ int oldPos; // = cursorPosition();
+
+ getCursorPosition(&oldPos,&pos);
+ oldPos=pos;
+
+ QString line = text();
+
+ while(pos && line[pos-1] != ' ') pos--;
+
+ line.remove(pos, oldPos - pos);
+
+ // did we find the nick in the middle of the line?
+ if(pos)
+ {
+ QString addMiddle(Preferences::nickCompleteSuffixMiddle());
+ line.insert(pos, nick + addMiddle);
+ pos += nick.length() + addMiddle.length();
+ }
+ // no, it was at the beginning
+ else
+ {
+ setLastCompletion(nick);
+ QString addStart(Preferences::nickCompleteSuffixStart());
+ line.insert(pos, nick + addStart);
+ pos += nick.length() + addStart.length();
+ }
+
+ setText(line);
+ setCursorPosition(0,pos);
+}
+
+void IRCInput::setLastCompletion(const QString& completion)
+{
+ m_lastCompletion = completion;
+}
+
+// Accessor methods
+
+void IRCInput::setCompletionMode(char mode) { completionMode=mode; }
+char IRCInput::getCompletionMode() { return completionMode; }
+void IRCInput::setOldCursorPosition(int pos) { oldPos=pos; }
+int IRCInput::getOldCursorPosition() { return oldPos; }
+
+#include "ircinput.moc"
diff --git a/konversation/src/ircinput.h b/konversation/src/ircinput.h
new file mode 100644
index 0000000..85e42c1
--- /dev/null
+++ b/konversation/src/ircinput.h
@@ -0,0 +1,91 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ The line input widget with chat enhanced functions
+ begin: Tue Mar 5 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef IRCINPUT_H
+#define IRCINPUT_H
+
+#include <qstringlist.h>
+
+#include <ktextedit.h>
+
+
+class KCompletionBox;
+class QMouseEvent;
+
+class IRCInput : public KTextEdit
+{
+ Q_OBJECT
+
+ public:
+ explicit IRCInput(QWidget* parent);
+ ~IRCInput();
+
+ void setCompletionMode(char mode);
+ char getCompletionMode();
+ void setOldCursorPosition(int pos);
+ int getOldCursorPosition();
+ QString lastCompletion() const { return m_lastCompletion; }
+
+ virtual QSize sizeHint() const;
+ QString text() const;
+
+ signals:
+ void nickCompletion();
+ void endCompletion(); // tell channel that completion phase is over
+ void history(bool up);
+ void textPasted(const QString& text);
+ void submit();
+ void envelopeCommand();
+
+ public slots:
+ void paste();
+ void paste(bool useSelection);
+ void showCompletionList(const QStringList& nicks);
+ void setText(const QString& text);
+ void setLastCompletion(const QString& completion);
+ virtual void setOverwriteMode(bool) { }
+ virtual void resizeContents( int w, int h );
+ virtual void updateAppearance();
+
+ protected slots:
+ void getHistory(bool up);
+ void insertCompletion(const QString& nick);
+ void disableSpellChecking();
+ virtual void slotSpellCheckDone(const QString& s);
+
+ protected:
+ bool eventFilter(QObject *object,QEvent *event);
+ void addHistory(const QString& text);
+ bool checkPaste(QString& text);
+ void contentsMouseReleaseEvent(QMouseEvent *);
+
+ virtual void keyPressEvent(QKeyEvent* e);
+ virtual QPopupMenu *createPopupMenu( const QPoint& pos );
+ virtual void showEvent(QShowEvent* e);
+ virtual void hideEvent(QHideEvent* e);
+
+ QStringList historyList;
+ unsigned int lineNum;
+ unsigned int oldPos;
+ char completionMode;
+ KCompletionBox* completionBox;
+ QString m_lastCompletion;
+ bool m_useSelection;
+ bool m_multiRow;
+ int m_lastHeight; //essentially corresponds to qtextbrowser::doc->height()
+ int m_qtBoxPadding; //see comment in constructor
+
+ QTimer* m_disableSpellCheckTimer;
+};
+#endif
diff --git a/konversation/src/ircqueue.cpp b/konversation/src/ircqueue.cpp
new file mode 100644
index 0000000..c80ceca
--- /dev/null
+++ b/konversation/src/ircqueue.cpp
@@ -0,0 +1,209 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) version 2.
+*/
+
+/*
+ Copyright (C) 2008 Eli J. MacKenzie <argonel at gmail.com>
+*/
+
+#include <qdatetime.h>
+#include <qtimer.h>
+#include <qstring.h>
+
+#include "ircqueue.h"
+#include "server.h"
+
+//#include "/home/ejm/argnl.h"
+
+IRCQueue::EmptyingRate staticrates[Server::Howmanyqueuesdoweneedanywayquestionmark]; /*=
+ {
+ IRCQueue::EmptyingRate(6,60000)
+ ,IRCQueue::EmptyingRate(20,60000)
+ ,IRCQueue::EmptyingRate(1,1000)//,IRCQueue::EmptyingRate::Bytes)
+ };
+*/
+
+int IRCQueue::EmptyingRate::nextInterval(int, int elapsed)
+{
+ if (!isValid())
+ return 0;
+ //KX << _S(m_interval) << endl;
+ if (m_type == Lines)
+ {
+ int i = m_interval/m_rate;
+ //KX << _S(i) << endl;
+ if (i<elapsed) {
+ //KX << _S(i) << _S(elapsed) << endl;
+ return 0;
+ }
+ else
+ {
+ //KX << _S(i) << endl;
+ return i;
+ }
+ }
+ else
+ {
+ //TODO write this...
+ return 0;
+ }
+return 0;
+}
+
+IRCQueue::EmptyingRate& IRCQueue::getRate()
+{
+ return m_rate;
+}
+
+
+IRCQueue::IRCQueue(Server *server, EmptyingRate& rate, int ind) :
+ m_rate(rate), m_blocked(true), m_server(server),
+ m_linesSent(0), m_globalLinesSent(0),
+ m_bytesSent(0), m_globalBytesSent(0), m_lastWait(0), m_myIndex(ind)
+{
+ //KX << _S(m_rate.m_rate) << _S(m_rate.m_interval) << _S(m_rate.m_type) << endl;
+ m_timer=new QTimer(this);
+ connect(m_timer, SIGNAL(timeout()), SLOT(sendNow()));
+ if (server)
+ {
+ connect(server, SIGNAL(serverOnline(bool)), SLOT(serverOnline(bool)));
+ connect(server, SIGNAL(sentStat(int, int, IRCQueue*)), SLOT(sent(int, int, IRCQueue*)));
+ m_blocked=!(m_server->isConnected());
+ }
+}
+
+IRCQueue::~IRCQueue()
+{
+ kdDebug() << "~IRCQueue" << endl;
+}
+
+QString IRCQueue::pop()
+{
+ if (m_pending.isEmpty())
+ return QString("");
+
+ IRCMessage msg=m_pending.first();
+ m_pending.pop_front();
+ m_lastWait=msg.age();
+ m_lastSent=QTime::currentTime();
+ return msg.text();
+}
+
+int IRCQueue::nextSize()
+{
+ if (m_pending.isEmpty())
+ return 0;
+ return m_pending.first().text().length();
+}
+
+int IRCQueue::currentWait()
+{
+ if (m_pending.isEmpty())
+ return 0;
+ return m_pending.first().age();
+}
+
+int IRCQueue::elapsed()
+{
+ if (m_startedAt.isNull())
+ return 0;
+ else
+ return m_startedAt.elapsed(); //FIXME if its been more than a day since this queue was used, this breaks
+}
+
+int IRCQueue::linesSent() const
+{
+ return m_linesSent;
+}
+
+int IRCQueue::bytesSent() const
+{
+ return m_bytesSent;
+}
+
+///Feedback indicating size of data sent to update statistics. Not necessarily data from this queue!!!
+void IRCQueue::sent(int, int e, IRCQueue *wq)
+{
+ //KX << k_funcinfo << _S(m_mine) << endl;
+ m_globalLinesSent++;
+ m_globalBytesSent+=e; // we don't care about the unencoded bytes, we want what went to the server
+ if (wq == this) {
+ m_linesSent++;
+ m_bytesSent+=e;
+ }
+}
+
+void IRCQueue::enqueue(QString line)
+{
+ m_pending.append(IRCMessage(line));
+ if (!m_timer->isActive())
+ adjustTimer();
+}
+
+//starts timer if stopped, adjusts interval if necessary
+void IRCQueue::adjustTimer()
+{
+ int msec;
+ msec=getRate().nextInterval(nextSize(), elapsed());
+ //if (m_myIndex == 0)
+ // KX << _S(msec) << endl;
+ m_timer->start(msec,true);
+ m_startedAt.start();
+ return;
+}
+
+bool IRCQueue::doSend()
+{
+ bool p=!m_pending.isEmpty();
+ if (p)
+ {
+ // int plw = m_lastWait;
+ QString s=pop();
+ //if (m_myIndex == 0)
+ // KX << _S(plw) << _S(m_lastWait) << endl;
+ m_server->toServer(s, this);
+ m_startedAt.start();
+ }
+ return p;//if we sent something, fire the timer again
+}
+
+///it would probably be better to delete and recreate the queue.
+void IRCQueue::reset()
+{
+ // KX << k_funcinfo << endl;
+ m_timer->stop();
+ m_lastWait=0;
+ if (m_server)
+ m_blocked=!(m_server->isConnected()); //FIXME (maybe) "we can't do this anymore because blocked can't correspond to whether the server is online, instead must correspond to whether the socket has become writable (readyWrite)"
+
+ m_startedAt=m_globalLastSent=m_lastSent=QTime();
+ m_pending.clear();
+ m_linesSent=m_bytesSent=m_globalBytesSent=m_globalLinesSent=0;
+}
+
+//called when the timer fires.
+void IRCQueue::sendNow()
+{
+ if (doSend())
+ adjustTimer();
+ //else //its a single-shot timer so if we don't adjust it, it won't run :)
+}
+
+///lets us know we should block output
+void IRCQueue::serverOnline(bool on)
+{
+ if (m_blocked!=on)
+ return;
+ m_blocked=!on;
+ if (m_blocked && m_timer->isActive())
+ reset();
+ else if (!m_blocked && !m_timer->isActive() && nextSize())
+ {
+ adjustTimer();
+ }
+}
+
+#include "ircqueue.moc"
diff --git a/konversation/src/ircqueue.h b/konversation/src/ircqueue.h
new file mode 100644
index 0000000..c80c329
--- /dev/null
+++ b/konversation/src/ircqueue.h
@@ -0,0 +1,137 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) version 2.
+*/
+
+/*
+ Copyright (C) 2008 Eli J. MacKenzie <argonel at gmail.com>
+*/
+
+
+#ifndef IRCQUEUE_H
+#define IRCQUEUE_H
+
+class Server;
+
+//channel.cpp, outputfilter.cpp, query.cpp, server.cpp, statuspanel.cpp
+
+/**
+ * A message from or to an IRC server.
+ *
+ * Contains all we know about the message, which currently consists of the text, the time it was created,
+ * and its original encoding. (Since currently these objects are only used internally, we know the message
+ * is Unicode.)
+ */
+struct IRCMessage
+{
+ IRCMessage() : t(QTime::currentTime()) //, codec(QTextCodec::codecForName("utf8"))
+ {} ///< this constructor required for QValueList, do not use
+
+ /**
+ Make a new IRCMessage with timestamp of QTime::currentTime().
+
+ Note the constructor takes a QString, not a const QString& or a QString *. If you want to modify the
+ contained text, put it back with setText.
+ */
+ IRCMessage(QString i) : s(i), t(QTime::currentTime()) //, codec(QTextCodec::codecForName("utf8"))
+ {}
+
+ QString text() { return s; }
+ int age() { return t.elapsed(); }
+ QTime time() { return t; }
+ void setText(QString text) { s=text; }
+private:
+ QString s;
+ QTime t;
+
+ //FIXME wire this up
+ //QTextCodec* codec;
+ //operator const char * () const { return codec->fromUnicode(text()); }
+
+};
+
+/**
+* Provides a self-sending queue of IRCMessages.
+*
+* Messages enqueued in this server can only be erased via reset() or sent to the attached server.
+* The server and the emptying rates cannot be changed, if you want to do that construct a new queue.
+
+*/
+class IRCQueue: public QObject
+{
+ Q_OBJECT
+
+public:
+ struct EmptyingRate
+ {
+ enum RateType {
+ Lines, ///< Lines per interval.
+ Bytes ///< Bytes per interval. Not implemented. FIXME
+ };
+ EmptyingRate(int rate=6, int msec_interval=50000, RateType type=Lines):
+ m_rate(rate), m_interval(msec_interval), m_type(type)
+ {
+ }
+
+ int nextInterval(int byte_size, int msec_since_last);
+
+ int m_rate;
+ int m_interval;
+ RateType m_type;
+ bool isValid() { return m_rate > 0; }
+ };
+
+ IRCQueue(Server *server, EmptyingRate& rate, int myindex=0);
+ ~IRCQueue();
+
+ void enqueue(QString line);
+ void reset();
+ EmptyingRate& getRate();// { return &m_rate; }
+
+ bool isValid() { return m_rate.isValid(); }
+ bool isEmpty() { return m_pending.isEmpty(); }
+
+ //WTF? why are there two of these.
+ //These are decoupled for a reason, what is it?
+ int currentWait(); ///< Time in ms that the front has been waiting
+ int elapsed(); ///< How long has the queue been running since it was last started?
+
+ int nextSize(); ///< Current size of front
+ int pendingMessages() { return m_pending.count(); }
+ int linesSent() const; ///< count of lines sent by this queue
+ int bytesSent() const; ///< count of bytes sent by this queue
+
+ ///Time in milliseconds that the previous message waited
+ int lastWait() { return m_lastWait; }
+
+public slots:
+ void sent(int bytes, int encodedBytes, IRCQueue *); ///< feedback statistics
+ void sendNow(); ///< dumps a line to the socket
+ void serverOnline(bool on); ///< server tells us that the socket is ready
+
+protected:
+ QString pop(); ///< pops front, sets statistics
+ void adjustTimer(); ///< sets the next timer interval
+ bool doSend(); ///< pops front and tells the server to send it. returns true if we sent something
+ EmptyingRate& m_rate;
+
+private:
+ QValueList<IRCMessage> m_pending;
+ QTimer *m_timer;
+ bool m_blocked;
+ bool m_online;
+ Server *m_server;
+
+ QTime m_startedAt;
+ QTime m_lastSent, m_globalLastSent;
+ int m_linesSent, m_globalLinesSent;
+ int m_bytesSent, m_globalBytesSent;
+ int m_lastWait;
+ int m_myIndex;
+};
+
+extern IRCQueue::EmptyingRate staticrates[];
+
+#endif
diff --git a/konversation/src/ircview.cpp b/konversation/src/ircview.cpp
new file mode 100644
index 0000000..3b4108a
--- /dev/null
+++ b/konversation/src/ircview.cpp
@@ -0,0 +1,1791 @@
+// -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*-
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005-2007 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "ircview.h"
+#include "channel.h"
+#include "dccchat.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "viewcontainer.h"
+#include "connectionmanager.h"
+#include "highlight.h"
+#include "server.h"
+#include "konversationsound.h"
+#include "common.h"
+#include "emoticon.h"
+#include "notificationhandler.h"
+
+#include <private/qrichtext_p.h>
+#include <qstylesheet.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+#include <qtextbrowser.h>
+#include <qclipboard.h>
+#include <qbrush.h>
+#include <qevent.h>
+#include <qdragobject.h>
+#include <qpopupmenu.h>
+#include <qwhatsthis.h>
+#include <qmap.h>
+#include <qcolor.h>
+#include <qscrollbar.h>
+#include <qcursor.h>
+
+#include <dcopref.h>
+#include <dcopclient.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kurldrag.h>
+#include <kbookmark.h>
+#include <kbookmarkmanager.h>
+#include <kdeversion.h>
+#include <kstandarddirs.h>
+#include <krun.h>
+#include <kprocess.h>
+#include <kiconloader.h>
+#include <kshell.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kglobalsettings.h>
+#include <kdebug.h>
+#include <kmenubar.h>
+#include <kfiledialog.h>
+#include <kio/job.h>
+#include <kstdaccel.h>
+#include <kglobal.h>
+
+
+IRCView::IRCView(QWidget* parent, Server* newServer) : KTextBrowser(parent)
+{
+ m_copyUrlMenu = false;
+ m_resetScrollbar = true;
+ m_offset = 0;
+ m_mousePressed = false;
+ m_isOnNick = false;
+ m_isOnChannel = false;
+ m_chatWin = 0;
+ m_findParagraph=0;
+ m_findIndex=0;
+ m_nickPopup = 0;
+ m_channelPopup = 0;
+
+ m_rememberLineParagraph = -1;
+ m_rememberLineDirtyBit = false;
+
+ m_disableEnsureCursorVisible = false;
+ m_wasPainted = false;
+
+ setAutoFormatting(QTextEdit::AutoNone);
+ setUndoRedoEnabled(0);
+ setLinkUnderline(false);
+ setVScrollBarMode(AlwaysOn);
+ setHScrollBarMode(AlwaysOff);
+ setWrapPolicy(QTextEdit::AtWordOrDocumentBoundary);
+ setNotifyClick(true);
+ setFocusPolicy(QWidget::ClickFocus);
+
+ // set basic style sheet for <p> to make paragraph spacing possible
+ QStyleSheet* sheet=new QStyleSheet(this,"ircview_style_sheet");
+ new QStyleSheetItem(sheet,"p");
+ setStyleSheet(sheet);
+
+ m_popup = new QPopupMenu(this,"ircview_context_menu");
+ toggleMenuBarSeparator = m_popup->insertSeparator();
+ m_popup->setItemVisible(toggleMenuBarSeparator, false);
+ copyUrlMenuSeparator = m_popup->insertSeparator();
+ m_popup->setItemVisible(copyUrlMenuSeparator, false);
+ m_popup->insertItem(SmallIconSet("editcopy"),i18n("&Copy"),Copy);
+ m_popup->insertItem(i18n("Select All"),SelectAll);
+ m_popup->insertItem(SmallIcon("find"),i18n("Find Text..."),Search);
+
+ setServer(newServer);
+
+ setViewBackground(Preferences::color(Preferences::TextViewBackground),QString());
+
+ if (Preferences::customTextFont())
+ setFont(Preferences::textFont());
+ else
+ setFont(KGlobalSettings::generalFont());
+
+ if (Preferences::useParagraphSpacing()) enableParagraphSpacing();
+
+ connect(this, SIGNAL(highlighted(const QString&)), this, SLOT(highlightedSlot(const QString&)));
+}
+
+IRCView::~IRCView()
+{
+ delete m_popup;
+}
+
+void IRCView::enableParagraphSpacing()
+{
+ // Set style sheet for <p> to define paragraph spacing.
+ QStyleSheet* sheet = styleSheet();
+
+ if (!sheet) return;
+
+ QStyleSheetItem* style = sheet->item("p");
+
+ if (!style)
+ {
+ kdDebug() << "IRCView::updateStyleSheet(): style == 0!" << endl;
+
+ return;
+ }
+
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setMargin(QStyleSheetItem::MarginVertical, Preferences::paragraphSpacing());
+ style->setSelfNesting(false);
+}
+
+void IRCView::setViewBackground(const QColor& backgroundColor, const QString& pixmapName)
+{
+ QPixmap backgroundPixmap;
+ backgroundPixmap.load(pixmapName);
+
+ if(backgroundPixmap.isNull())
+ {
+ setPaper(backgroundColor);
+ }
+ else
+ {
+ QBrush backgroundBrush;
+ backgroundBrush.setColor(backgroundColor);
+ backgroundBrush.setPixmap(backgroundPixmap);
+ setPaper(backgroundBrush);
+ }
+}
+
+void IRCView::setServer(Server* newServer)
+{
+ m_server = newServer;
+
+ if (newServer)
+ {
+ KAction *action = newServer->getViewContainer()->actionCollection()->action("open_logfile");
+ Q_ASSERT(action);
+ if(!action) return;
+ m_popup->insertSeparator();
+ action->plug(m_popup);
+ }
+
+}
+
+const QString& IRCView::getContextNick() const
+{
+ return m_currentNick;
+}
+
+void IRCView::clearContextNick()
+{
+ m_currentNick = QString();
+}
+
+void IRCView::clear()
+{
+ m_buffer = QString();
+ KTextBrowser::setText("");
+ wipeLineParagraphs();
+}
+
+void IRCView::highlightedSlot(const QString& _link)
+{
+ QString link = _link;
+ // HACK Replace % with \x03 in the url to keep Qt from doing stupid things
+ link = link.replace ('\x03', "%");
+ //Hack to handle the fact that we get a decoded url
+ link = KURL::fromPathOrURL(link).url();
+
+ // HACK:Use space as a placeholder for \ as Qt tries to be clever and does a replace to / in urls in QTextEdit
+ if(link.startsWith("#"))
+ {
+ link = link.replace(' ', "\\");
+ }
+
+ //we just saw this a second ago. no need to reemit.
+ if (link == m_lastStatusText && !link.isEmpty())
+ return;
+
+ // remember current URL to overcome link clicking problems in QTextBrowser
+ m_highlightedURL = link;
+
+ if (link.isEmpty())
+ {
+ if (!m_lastStatusText.isEmpty())
+ {
+ emit clearStatusBarTempText();
+ m_lastStatusText = QString();
+ }
+ } else
+ {
+ m_lastStatusText = link;
+ }
+
+ if(!link.startsWith("#"))
+ {
+ m_isOnNick = false;
+ m_isOnChannel = false;
+
+ if (!link.isEmpty()) {
+ //link therefore != m_lastStatusText so emit with this new text
+ emit setStatusBarTempText(link);
+ }
+ if (link.isEmpty() && m_copyUrlMenu)
+ {
+ m_popup->removeItem(CopyUrl);
+ m_popup->removeItem(Bookmark);
+ m_popup->removeItem(SaveAs);
+ m_popup->setItemVisible(copyUrlMenuSeparator, false);
+ m_copyUrlMenu = false;
+
+ }
+ else if (!link.isEmpty() && !m_copyUrlMenu)
+ {
+ m_popup->setItemVisible(copyUrlMenuSeparator, true);
+ m_popup->insertItem(SmallIcon("editcopy"), i18n("Copy URL to Clipboard"), CopyUrl, 1);
+ m_popup->insertItem(SmallIcon("bookmark"), i18n("Add to Bookmarks"), Bookmark, 2);
+ m_popup->insertItem(SmallIcon("filesaveas"), i18n("Save Link As..."), SaveAs, 3);
+ m_copyUrlMenu = true;
+ m_urlToCopy = link;
+ }
+ }
+ else if (link.startsWith("#") && !link.startsWith("##"))
+ {
+ m_currentNick = link.mid(1);
+ m_nickPopup->changeTitle(m_nickPopupId,m_currentNick);
+ m_isOnNick = true;
+ emit setStatusBarTempText(i18n("Open a query with %1").arg(m_currentNick));
+ }
+ else
+ {
+ // link.startsWith("##")
+ m_currentChannel = link.mid(1);
+
+ QString prettyId = m_currentChannel;
+
+ if (prettyId.length()>15)
+ {
+ prettyId.truncate(15);
+ prettyId.append("...");
+ }
+
+ m_channelPopup->changeTitle(m_channelPopupId,prettyId);
+ m_isOnChannel = true;
+ emit setStatusBarTempText(i18n("Join the channel %1").arg(m_currentChannel));
+ }
+}
+
+void IRCView::openLink(const QString& url, bool newTab)
+{
+ if (!url.isEmpty() && !url.startsWith("#"))
+ {
+ if (url.startsWith("irc://"))
+ {
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ konvApp->getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, url);
+ }
+ else if (!Preferences::useCustomBrowser() || url.startsWith("mailto:"))
+ {
+ if(newTab && !url.startsWith("mailto:"))
+ {
+ QCString foundApp, foundObj;
+ QByteArray data;
+ QDataStream str(data, IO_WriteOnly);
+ if( KApplication::dcopClient()->findObject("konqueror*", "konqueror-mainwindow*",
+ "windowCanBeUsedForTab()", data, foundApp, foundObj, false, 3000))
+ {
+ DCOPRef ref(foundApp, foundObj);
+ ref.call("newTab", url);
+ } else
+ new KRun(KURL::fromPathOrURL(url));
+ } else
+ new KRun(KURL::fromPathOrURL(url));
+ }
+ else
+ {
+ QString cmd = Preferences::webBrowserCmd();
+ cmd.replace("%u", url);
+ KProcess *proc = new KProcess;
+ QStringList cmdAndArgs = KShell::splitArgs(cmd);
+ *proc << cmdAndArgs;
+ // This code will also work, but starts an extra shell process.
+ // kdDebug() << "IRCView::urlClickSlot(): cmd = " << cmd << endl;
+ // *proc << cmd;
+ // proc->setUseShell(true);
+ proc->start(KProcess::DontCare);
+ delete proc;
+ }
+ }
+ //FIXME: Don't do channel links in DCC Chats to begin with since they don't have a server.
+ else if (url.startsWith("##") && m_server && m_server->isConnected())
+ {
+ QString channel(url);
+ channel.replace("##", "#");
+ m_server->sendJoinCommand(channel);
+ }
+ //FIXME: Don't do user links in DCC Chats to begin with since they don't have a server.
+ else if (url.startsWith("#") && m_server && m_server->isConnected())
+ {
+ QString recipient(url);
+ recipient.remove("#");
+ NickInfoPtr nickInfo = m_server->obtainNickInfo(recipient);
+ m_server->addQuery(nickInfo, true /*we initiated*/);
+ }
+}
+
+void IRCView::replaceDecoration(QString& line, char decoration, char replacement)
+{
+ int pos;
+ bool decorated = false;
+
+ while((pos=line.find(decoration))!=-1)
+ {
+ line.replace(pos,1,(decorated) ? QString("</%1>").arg(replacement) : QString("<%1>").arg(replacement));
+ decorated = !decorated;
+ }
+}
+
+QString IRCView::filter(const QString& line, const QString& defaultColor, const QString& whoSent,
+bool doHighlight, bool parseURL, bool self)
+{
+ QString filteredLine(line);
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ //Since we can't turn off whitespace simplification withouteliminating text wrapping,
+ // if the line starts with a space turn it into a non-breaking space.
+ // (which magically turns back into a space on copy)
+
+ if (filteredLine[0]==' ')
+ filteredLine[0]='\xA0';
+
+ // TODO: Use QStyleSheet::escape() here
+ // Replace all < with &lt;
+ filteredLine.replace("<","\x0blt;");
+ // Replace all > with &gt;
+ filteredLine.replace(">", "\x0bgt;");
+
+ #if 0
+ if(!Preferences::disableExpansion())
+ {
+ QRegExp boldRe("\\*\\*([a-zA-Z0-9]+)\\*\\*");
+ QRegExp underRe("\\_\\_([a-zA-Z0-9]+)\\_\\_");
+ int position = 0;
+ QString replacement;
+
+ while( position >= 0)
+ {
+ position = boldRe.search(filteredLine, position);
+ if( position > -1)
+ {
+ replacement = boldRe.cap(1);
+ replacement = "\x02"+replacement+"\x02";
+ filteredLine.replace(position,replacement.length()+2,replacement);
+ }
+ position += boldRe.matchedLength();
+ }
+
+ position = 0;
+ while( position >= 0)
+ {
+ position = underRe.search(filteredLine, position);
+ if( position > -1)
+ {
+ replacement = underRe.cap(1);
+ replacement = "\x1f"+replacement+"\x1f";
+ filteredLine.replace(position,replacement.length()+2,replacement);
+ }
+ position += underRe.matchedLength();
+ }
+ }
+ #endif
+
+ if(filteredLine.find("\x07") != -1)
+ {
+ if(Preferences::beep())
+ {
+ kapp->beep();
+ }
+ }
+
+ // replace \003 and \017 codes with rich text color codes
+ // captures 1 2 23 4 4 3 1
+ QRegExp colorRegExp("(\003([0-9]|0[0-9]|1[0-5]|)(,([0-9]|0[0-9]|1[0-5])|,|)|\017)");
+
+ int pos;
+ bool allowColors = Preferences::allowColorCodes();
+ bool firstColor = true;
+ QString colorString;
+
+ while((pos=colorRegExp.search(filteredLine))!=-1)
+ {
+ if(!allowColors)
+ {
+ colorString = QString();
+ }
+ else
+ {
+ colorString = (firstColor) ? QString::null : QString("</font>");
+
+ // reset colors on \017 to default value
+ if(colorRegExp.cap(1) == "\017")
+ colorString += "<font color=\""+defaultColor+"\">";
+ else
+ {
+ if(!colorRegExp.cap(2).isEmpty())
+ {
+ int foregroundColor = colorRegExp.cap(2).toInt();
+ colorString += "<font color=\"" + Preferences::ircColorCode(foregroundColor).name() + "\">";
+ }
+ else
+ {
+ colorString += "<font color=\""+defaultColor+"\">";
+ }
+ }
+
+ firstColor = false;
+ }
+
+ filteredLine.replace(pos, colorRegExp.cap(0).length(), colorString);
+ }
+
+ if(!firstColor)
+ filteredLine+="</font>";
+
+ // Replace all text decorations
+ // TODO: \017 should reset all textt decorations to plain text
+ replaceDecoration(filteredLine,'\x02','b');
+ replaceDecoration(filteredLine,'\x09','i');
+ replaceDecoration(filteredLine,'\x13','s');
+ replaceDecoration(filteredLine,'\x15','u');
+ replaceDecoration(filteredLine,'\x16','b'); // should be inverse
+ replaceDecoration(filteredLine,'\x1f','u');
+
+ if(parseURL)
+ {
+ filteredLine = Konversation::tagURLs(filteredLine, whoSent);
+ }
+ else
+ {
+ // Change & to &amp; to prevent html entities to do strange things to the text
+ filteredLine.replace('&', "&amp;");
+ filteredLine.replace("\x0b", "&");
+ }
+
+ filteredLine = Konversation::EmotIcon::filter(filteredLine, fontMetrics());
+
+ // Highlight
+ QString ownNick;
+
+ if (m_server)
+ {
+ ownNick = m_server->getNickname();
+ }
+ else if (m_chatWin->getType() == ChatWindow::DccChat)
+ {
+ ownNick = static_cast<DccChat*>(m_chatWin)->getOwnNick();
+ }
+
+ if(doHighlight && (whoSent != ownNick) && !self)
+ {
+ QString highlightColor;
+
+ if(Preferences::highlightNick() &&
+ filteredLine.lower().find(QRegExp("(^|[^\\d\\w])" +
+ QRegExp::escape(ownNick.lower()) +
+ "([^\\d\\w]|$)")) != -1)
+ {
+ // highlight current nickname
+ highlightColor = Preferences::highlightNickColor().name();
+ m_tabNotification = Konversation::tnfNick;
+ }
+ else
+ {
+ QPtrList<Highlight> highlightList = Preferences::highlightList();
+ QPtrListIterator<Highlight> it(highlightList);
+ Highlight* highlight = it.current();
+ bool patternFound = false;
+ int index = 0;
+
+ QStringList captures;
+ while(highlight)
+ {
+ if(highlight->getRegExp())
+ {
+ QRegExp needleReg=highlight->getPattern();
+ needleReg.setCaseSensitive(false);
+ // highlight regexp in text
+ patternFound = ((filteredLine.find(needleReg) != -1) ||
+ // highlight regexp in nickname
+ (whoSent.find(needleReg) != -1));
+
+ // remember captured patterns for later
+ captures=needleReg.capturedTexts();
+
+ }
+ else
+ {
+ QString needle=highlight->getPattern();
+ // highlight patterns in text
+ patternFound = ((filteredLine.find(needle, 0, false) != -1) ||
+ // highlight patterns in nickname
+ (whoSent.find(needle, 0, false) != -1));
+ }
+
+ if(!patternFound)
+ {
+ ++it;
+ highlight = it.current();
+ ++index;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if(patternFound)
+ {
+ highlightColor = highlight->getColor().name();
+ m_highlightColor = highlightColor;
+ m_tabNotification = Konversation::tnfHighlight;
+
+ if(Preferences::highlightSoundsEnabled() && m_chatWin->notificationsEnabled())
+ {
+ konvApp->sound()->play(highlight->getSoundURL());
+ }
+
+ konvApp->notificationHandler()->highlight(m_chatWin, whoSent, line);
+ m_autoTextToSend = highlight->getAutoText();
+
+ // replace %0 - %9 in regex groups
+ for(unsigned int capture=0;capture<captures.count();capture++)
+ {
+ m_autoTextToSend.replace(QString("%%1").arg(capture),captures[capture]);
+ }
+ m_autoTextToSend.replace(QRegExp("%[0-9]"),QString());
+ }
+ }
+
+ // apply found highlight color to line
+ if(!highlightColor.isEmpty())
+ {
+ filteredLine = "<font color=\"" + highlightColor + "\">" + filteredLine + "</font>";
+ }
+ }
+ else if(doHighlight && (whoSent == ownNick) && Preferences::highlightOwnLines())
+ {
+ // highlight own lines
+ filteredLine = "<font color=\"" + Preferences::highlightOwnLinesColor().name() +
+ "\">" + filteredLine + "</font>";
+ }
+
+ // Replace pairs of spaces with "<space>&nbsp;" to preserve some semblance of text wrapping
+ filteredLine.replace(" "," \xA0");
+ return filteredLine;
+}
+
+QString IRCView::createNickLine(const QString& nick, bool encapsulateNick, bool privMsg)
+{
+ QString nickLine = "%2";
+
+ if(Preferences::useClickableNicks())
+ {
+ // HACK:Use space as a placeholder for \ as Qt tries to be clever and does a replace to / in urls in QTextEdit
+ nickLine = "<a href=\"#" + QString(nick).replace('\\', " ") + "\">%2</a>";
+ }
+
+ if(privMsg)
+ {
+ nickLine.prepend ("-&gt; ");
+ }
+
+ if(encapsulateNick)
+ nickLine = "&lt;" + nickLine + "&gt;";
+
+ if(Preferences::useColoredNicks() && m_server)
+ {
+ QString nickColor;
+
+ if (nick != m_server->getNickname())
+ nickColor = Preferences::nickColor(m_server->obtainNickInfo(nick)->getNickColor()).name();
+ else
+ nickColor = Preferences::nickColor(8).name();
+
+ if(nickColor == "#000000")
+ {
+ nickColor = "#000001"; // HACK Working around QTextBrowser's auto link coloring
+ }
+
+ nickLine = "<font color=\"" + nickColor + "\">"+nickLine+"</font>";
+ }
+ //FIXME: Another last-minute hack to get DCC Chat colored nicknames
+ // working. We can't use NickInfo::getNickColor() because we don't
+ // have a server.
+ else if (Preferences::useColoredNicks() && m_chatWin->getType() == ChatWindow::DccChat)
+ {
+ QString ownNick = static_cast<DccChat*>(m_chatWin)->getOwnNick();
+ QString nickColor;
+
+ if (nick != ownNick)
+ {
+ int nickvalue = 0;
+
+ for (uint index = 0; index < nick.length(); index++)
+ {
+ nickvalue += nick[index].unicode();
+ }
+
+ nickColor = Preferences::nickColor((nickvalue % 8)).name();
+ }
+ else
+ nickColor = Preferences::nickColor(8).name();
+
+ if(nickColor == "#000000")
+ {
+ nickColor = "#000001"; // HACK Working around QTextBrowser's auto link coloring
+ }
+
+ nickLine = "<font color=\"" + nickColor + "\">"+nickLine+"</font>";
+ }
+
+ if(Preferences::useBoldNicks())
+ nickLine = "<b>" + nickLine + "</b>";
+
+ return nickLine;
+}
+
+void IRCView::append(const QString& nick,const QString& message)
+{
+ QString channelColor = Preferences::color(Preferences::ChannelMessage).name();
+
+ if(channelColor == "#000000")
+ {
+ channelColor = "#000001"; // HACK Working around QTextBrowser's auto link coloring
+ }
+
+ QString line;
+ m_tabNotification = Konversation::tnfNormal;
+
+ QString nickLine = createNickLine(nick);
+
+ if(basicDirection(message) == QChar::DirR)
+ {
+ line = RLE;
+ line += LRE;
+ line += "<p><font color=\"" + channelColor + "\">" + nickLine + " %1" + PDF + RLM + " %3</font></p>\n";
+ }
+ else
+ {
+ line = "<p><font color=\"" + channelColor + "\">%1" + nickLine + " %3</font></p>\n";
+ }
+
+ line = line.arg(timeStamp(), nick, filter(message, channelColor, nick, true));
+
+ emit textToLog(QString("<%1>\t%2").arg(nick).arg(message));
+
+ doAppend(line);
+}
+
+void IRCView::insertRememberLine()
+{
+ m_rememberLineDirtyBit = true;
+
+ if (!Preferences::automaticRememberLineOnlyOnTextChange())
+ appendRememberLine();
+}
+
+void IRCView::cancelRememberLine()
+{
+ m_rememberLineDirtyBit = false;
+}
+
+void IRCView::appendRememberLine()
+{
+ m_rememberLineDirtyBit = false;
+
+ if (m_rememberLineParagraph == paragraphs() - 1)
+ return;
+
+ if (m_rememberLineParagraph > -1)
+ {
+ removeParagraph(m_rememberLineParagraph);
+
+ QValueList<int> newList;
+ QValueList<int>::ConstIterator it;
+
+ for (it = m_markerLineParagraphs.begin(); it != m_markerLineParagraphs.end(); ++it)
+ {
+ if ((*it) < m_rememberLineParagraph)
+ newList << (*it);
+ else if ((*it) > m_rememberLineParagraph)
+ newList << (*it) - 1;
+ }
+
+ m_markerLineParagraphs = newList;
+ }
+
+ repaintChanged();
+
+ appendLine(Preferences::color(Preferences::CommandMessage).name());
+
+ m_rememberLineParagraph = paragraphs() - 1;
+}
+
+void IRCView::insertMarkerLine()
+{
+ qHeapSort(m_markerLineParagraphs);
+
+ if (m_markerLineParagraphs.last() == paragraphs() - 1)
+ return;
+
+ bool rememberLineDirtyBit = m_rememberLineDirtyBit;
+ m_rememberLineDirtyBit = false;
+
+ appendLine(Preferences::color(Preferences::ActionMessage).name());
+
+ m_rememberLineDirtyBit = rememberLineDirtyBit;
+
+ m_markerLineParagraphs.append(paragraphs() - 1);
+}
+
+void IRCView::appendLine(const QString& color)
+{
+ QColor channelColor = Preferences::color(Preferences::ChannelMessage);
+
+ QString line = "<p><font style=\"font-size:1pt;\"><br><br><hr color=\""+color+"\" noshade></font></p>\n";
+
+ doAppend(line, true);
+}
+
+void IRCView::clearLines()
+{
+ if (m_rememberLineParagraph > -1)
+ m_markerLineParagraphs.append(m_rememberLineParagraph);
+
+ if (m_markerLineParagraphs.count() > 0)
+ {
+ qHeapSort(m_markerLineParagraphs);
+
+ QValueList<int>::ConstIterator it;
+ int removeCounter = 0;
+
+ for (it = m_markerLineParagraphs.begin(); it != m_markerLineParagraphs.end(); ++it)
+ {
+ removeParagraph((*it) - removeCounter);
+ removeCounter++;
+ }
+
+ wipeLineParagraphs();
+
+ repaintChanged();
+ }
+}
+
+bool IRCView::hasLines()
+{
+ if (m_rememberLineParagraph > -1 || m_markerLineParagraphs.count() > 0)
+ return true;
+
+ return false;
+}
+
+void IRCView::updateLineParagraphs(int numRemoved)
+{
+ if (m_rememberLineParagraph - numRemoved < 0)
+ m_rememberLineParagraph = -1;
+ else
+ m_rememberLineParagraph -= numRemoved;
+
+ if (!m_markerLineParagraphs.isEmpty())
+ {
+ QValueList<int> newMarkerLineParagraphs;
+
+ QValueList<int>::const_iterator it;
+
+ for (it = m_markerLineParagraphs.begin(); it != m_markerLineParagraphs.end(); ++it)
+ {
+ if ((*it) - numRemoved >= 0)
+ newMarkerLineParagraphs << ((*it) - numRemoved);
+ }
+
+ m_markerLineParagraphs = newMarkerLineParagraphs;
+ }
+}
+
+void IRCView::wipeLineParagraphs()
+{
+ m_markerLineParagraphs.clear();
+ m_rememberLineParagraph = -1;
+}
+
+void IRCView::appendRaw(const QString& message, bool suppressTimestamps, bool self)
+{
+ QColor channelColor=Preferences::color(Preferences::ChannelMessage);
+ QString line;
+ m_tabNotification = Konversation::tnfNone;
+
+ if(suppressTimestamps)
+ {
+ line = QString("<p><font color=\"" + channelColor.name() + "\">" + message + "</font></p>\n");
+ }
+ else
+ {
+ line = QString("<p>" + timeStamp() + " <font color=\"" + channelColor.name() + "\">" + message + "</font></p>\n");
+ }
+
+ doAppend(line, self);
+}
+
+void IRCView::appendQuery(const QString& nick, const QString& message, bool inChannel)
+{
+ QString queryColor=Preferences::color(Preferences::QueryMessage).name();
+
+ if(queryColor == "#000000")
+ {
+ queryColor = "#000001"; // HACK Working around QTextBrowser's auto link coloring
+ }
+
+ QString line;
+ m_tabNotification = Konversation::tnfPrivate;
+
+ QString nickLine = createNickLine(nick, true, inChannel);
+
+ if(basicDirection(message) == QChar::DirR)
+ {
+ line = RLE;
+ line += LRE;
+ line += "<p><font color=\"" + queryColor + "\">" + nickLine + " %1" + PDF + " %3</font></p>\n";
+ }
+ else
+ {
+ line = "<p><font color=\"" + queryColor + "\">%1 " + nickLine + " %3</font></p>\n";
+ }
+
+ line = line.arg(timeStamp(), nick, filter(message, queryColor, nick, true));
+
+ emit textToLog(QString("<%1>\t%2").arg(nick).arg(message));
+
+ doAppend(line);
+}
+
+void IRCView::appendChannelAction(const QString& nick,const QString& message)
+{
+ m_tabNotification = Konversation::tnfNormal;
+
+ appendAction(nick, message);
+}
+
+
+void IRCView::appendQueryAction(const QString& nick,const QString& message)
+{
+ m_tabNotification = Konversation::tnfPrivate;
+
+ appendAction(nick, message);
+}
+
+void IRCView::appendAction(const QString& nick,const QString& message)
+{
+ QString actionColor=Preferences::color(Preferences::ActionMessage).name();
+
+ // HACK Working around QTextBrowser's auto link coloring
+ if (actionColor == "#000000") actionColor = "#000001";
+
+ QString line;
+ QString nickLine = createNickLine(nick, false);
+
+ if (basicDirection(message) == QChar::DirR)
+ {
+ line = RLE;
+ line += LRE;
+ line += "<p><font color=\"" + actionColor + "\">" + nickLine + " * %1" + PDF + " %3</font></p>\n";
+ }
+ else
+ {
+ line = "<p><font color=\"" + actionColor + "\">%1 * " + nickLine + " %3</font></p>\n";
+ }
+
+ line = line.arg(timeStamp(), nick, filter(message, actionColor, nick, true));
+
+ emit textToLog(QString("\t * %1 %2").arg(nick).arg(message));
+
+ doAppend(line);
+}
+
+void IRCView::appendServerMessage(const QString& type, const QString& message, bool parseURL)
+{
+ QString serverColor = Preferences::color(Preferences::ServerMessage).name();
+ m_tabNotification = Konversation::tnfControl;
+
+ // Fixed width font option for MOTD
+ QString fixed;
+ if(Preferences::fixedMOTD() && !m_fontDataBase.isFixedPitch(font().family()))
+ {
+ if(type == i18n("MOTD"))
+ fixed=" face=\"" + KGlobalSettings::fixedFont().family() + "\"";
+ }
+
+ QString line;
+
+ if(basicDirection(message) == QChar::DirR)
+ {
+ line = RLE;
+ line += LRE;
+ line += "<p><font color=\"" + serverColor + "\"" + fixed + "><b>[</b>%2<b>]</b> %1" + PDF + " %3</font></p>\n";
+ }
+ else
+ {
+ line = "<p><font color=\"" + serverColor + "\"" + fixed + ">%1 <b>[</b>%2<b>]</b> %3</font></p>\n";
+ }
+
+ if(type != i18n("Notify"))
+ line = line.arg(timeStamp(), type, filter(message, serverColor, 0 , true, parseURL));
+ else
+ line = "<font color=\"" + serverColor + "\">"+line.arg(timeStamp(), type, message)+"</font>";
+
+ emit textToLog(QString("%1\t%2").arg(type).arg(message));
+
+ doAppend(line);
+}
+
+void IRCView::appendCommandMessage(const QString& type,const QString& message, bool important, bool parseURL, bool self)
+{
+ if (Preferences::hideUnimportantEvents() && !important)
+ return;
+
+ QString commandColor = Preferences::color(Preferences::CommandMessage).name();
+ QString line;
+ QString prefix="***";
+ m_tabNotification = Konversation::tnfControl;
+
+ if(type == i18n("Join"))
+ {
+ prefix="-->";
+ parseURL=false;
+ }
+ else if(type == i18n("Part") || type == i18n("Quit"))
+ {
+ prefix="<--";
+ }
+
+ prefix=QStyleSheet::escape(prefix);
+
+ if(basicDirection(message) == QChar::DirR)
+ {
+ line = RLE;
+ line += LRE;
+ line += "<p><font color=\"" + commandColor + "\">%2 %1" + PDF + " %3</font></p>\n";
+ }
+ else
+ {
+ line = "<p><font color=\"" + commandColor + "\">%1 %2 %3</font></p>\n";
+ }
+
+ line = line.arg(timeStamp(), prefix, filter(message, commandColor, 0, true, parseURL, self));
+
+ emit textToLog(QString("%1\t%2").arg(type).arg(message));
+
+ doAppend(line, self);
+}
+
+void IRCView::appendBacklogMessage(const QString& firstColumn,const QString& rawMessage)
+{
+ QString time;
+ QString message = rawMessage;
+ QString nick = firstColumn;
+ QString backlogColor = Preferences::color(Preferences::BacklogMessage).name();
+ m_tabNotification = Konversation::tnfNone;
+
+ time = nick.section(' ', 0, 4);
+ nick = nick.section(' ', 5);
+
+ if(!nick.isEmpty() && !nick.startsWith("<") && !nick.startsWith("*"))
+ {
+ nick = '|' + nick + '|';
+ }
+
+ // Nicks are in "<nick>" format so replace the "<>"
+ nick.replace("<","&lt;");
+ nick.replace(">","&gt;");
+
+ QString line;
+
+ if(basicDirection(message) == QChar::DirR)
+ {
+ line = "<p><font color=\"" + backlogColor + "\">%2 %1 %3</font></p>\n";
+ }
+ else
+ {
+ line = "<p><font color=\"" + backlogColor + "\">%1 %2 %3</font></p>\n";
+ }
+
+ line = line.arg(time, nick, filter(message, backlogColor, NULL, false, false));
+
+ doAppend(line);
+}
+
+//without any display update stuff that freaks out the scrollview
+void IRCView::removeSelectedText( int selNum )
+{
+ QTextDocument* doc=document();
+
+ for ( int i = 0; i < (int)doc->numSelections(); ++i )
+ {
+ if ( i == selNum )
+ continue;
+ doc->removeSelection( i );
+ }
+ // ...snip...
+
+ doc->removeSelectedText( selNum, QTextEdit::textCursor() );
+
+ // ...snip...
+}
+
+void IRCView::scrollToBottom()
+{
+ // QTextEdit::scrollToBottom does sync() too, but we don't want it because its slow
+ setContentsPos( contentsX(), contentsHeight() - visibleHeight() );
+}
+
+void IRCView::ensureCursorVisible()
+{
+ if (!m_disableEnsureCursorVisible)
+ QTextEdit::ensureCursorVisible();
+}
+
+void IRCView::doAppend(const QString& newLine, bool self)
+{
+ if (m_rememberLineDirtyBit) appendRememberLine();
+
+ // Add line to buffer
+ QString line(newLine);
+
+ if (!self && m_chatWin) m_chatWin->activateTabNotification(m_tabNotification);
+
+ // scroll view only if the scroll bar is already at the bottom
+ bool doScroll = ( KTextBrowser::verticalScrollBar()->value() == KTextBrowser::verticalScrollBar()->maxValue());
+
+ line.remove('\n'); // TODO why have newlines? we get <p>, so the \n are unnecessary...
+
+ bool up = KTextBrowser::viewport()->isUpdatesEnabled();
+
+ KTextBrowser::viewport()->setUpdatesEnabled(false);
+ int paraFrom, indexFrom, paraTo, indexTo;
+ bool textselected = hasSelectedText();
+
+ // Remember the selection so we don't loose it when adding the new line
+ if(textselected)
+ getSelection(&paraFrom, &indexFrom, &paraTo, &indexTo);
+
+ document()->lastParagraph()->format();
+
+ KTextBrowser::append(line);
+
+ document()->lastParagraph()->format();
+
+ // get maximum number of lines we want to have in the scollback buffer
+ int sbm = Preferences::scrollbackMax();
+
+ // Explanation: the scrolling mechanism cannot handle the buffer changing when the scrollbar is not
+ // at an end, so the scrollbar wets its pants and forgets who it is for ten minutes
+ // Also make sure not to delete any lines if maximum lines of scrollback is set to 0 (unlimited)
+ if (sbm && doScroll)
+ {
+ int numRemoved = paragraphs() - sbm;
+
+ if (numRemoved > 0)
+ {
+ for (int index = numRemoved; index > 0; --index)
+ {
+ removeParagraph(0);
+ }
+
+ updateLineParagraphs(numRemoved);
+
+ if (textselected)
+ {
+ paraFrom -= numRemoved;
+ paraTo -= numRemoved;
+ }
+ }
+ }
+
+ resizeContents(contentsWidth(), document()->height());
+ KTextBrowser::viewport()->setUpdatesEnabled(up);
+
+ // Restore selection
+ if(textselected && paraFrom >= 0 && paraTo >= 0) {
+ // HACK: do not change scrollback position: setSelection() calls
+ // ensureCursorVisible()
+ m_disableEnsureCursorVisible = true;
+ setSelection(paraFrom, indexFrom, paraTo, indexTo);
+ m_disableEnsureCursorVisible = false;
+ }
+
+ if (doScroll) updateScrollBarPos();
+
+ //FIXME: Disable auto-text for DCC Chats since we don't have a server
+ // to parse wildcards.
+ if (!m_autoTextToSend.isEmpty() && m_server)
+ {
+ // replace placeholders in autoText
+ QString sendText = m_server->parseWildcards(m_autoTextToSend,m_server->getNickname(),
+ QString(), QString(), QString(), QString());
+ // avoid recursion due to signalling
+ m_autoTextToSend = QString();
+ // send signal only now
+ emit autoText(sendText);
+ }
+ else
+ {
+ m_autoTextToSend = QString();
+ }
+
+ if (!m_lastStatusText.isEmpty()) emit clearStatusBarTempText();
+}
+
+// remember if scrollbar was positioned at the end of the text or not
+void IRCView::hideEvent(QHideEvent* /* event */) {
+ m_resetScrollbar = ((contentsHeight()-visibleHeight()) == contentsY());
+}
+
+// Workaround to scroll to the end of the TextView when it's shown
+void IRCView::showEvent(QShowEvent* event) {
+ Q_UNUSED(event);
+ // did the user scroll the view to the end of the text before hiding?
+ if (m_resetScrollbar)
+ {
+ // NOTE: when the view has not been painted yet, contentsHeight() might
+ // not return accurate information and hence this code won't reset
+ // scrollback properly in such a case. See paintEvent() hack below.
+ moveCursor(MoveEnd,false);
+ ensureVisible(0,contentsHeight());
+ }
+}
+
+void IRCView::paintEvent(QPaintEvent* event)
+{
+ // HACK: if the widget is being painted for the first time, call
+ // showEvent() which will reset scrollback position as needed. It seems
+ // that at the time this event is triggered, QTextEdit provides more
+ // accurate information than when showEvent() is triggered.
+ if (!m_wasPainted)
+ {
+ m_wasPainted = true;
+ showEvent(0);
+ }
+ KTextBrowser::paintEvent(event);
+}
+
+void IRCView::contentsMouseReleaseEvent(QMouseEvent *ev)
+{
+ if (ev->button() == Qt::MidButton)
+ {
+ if(m_copyUrlMenu)
+ {
+ openLink(m_urlToCopy,true);
+ return;
+ }
+ else
+ {
+ emit textPasted(true);
+ return;
+ }
+ }
+
+ if (ev->button() == QMouseEvent::LeftButton)
+ {
+ if (m_mousePressed)
+ {
+ if (ev->state() == (Qt::LeftButton|Qt::ShiftButton))
+ saveLinkAs(m_highlightedURL);
+ else
+ openLink(m_highlightedURL);
+
+ m_mousePressed = false;
+ return;
+ }
+ }
+
+ KTextBrowser::contentsMouseReleaseEvent(ev);
+}
+
+void IRCView::contentsMousePressEvent(QMouseEvent* ev)
+{
+ if (ev->button() == QMouseEvent::LeftButton)
+ {
+ m_urlToDrag = m_highlightedURL;
+
+ if (!m_urlToDrag.isNull())
+ {
+ m_mousePressed = true;
+ m_pressPosition = ev->pos();
+ return;
+ }
+ }
+
+ KTextBrowser::contentsMousePressEvent(ev);
+}
+
+void IRCView::contentsMouseMoveEvent(QMouseEvent* ev)
+{
+ if (m_mousePressed && (m_pressPosition - ev->pos()).manhattanLength() > QApplication::startDragDistance())
+ {
+ m_mousePressed = false;
+ removeSelection();
+ KURL ux = KURL::fromPathOrURL(m_urlToDrag);
+
+ if (m_server && m_urlToDrag.startsWith("##"))
+ {
+ //FIXME consistent IRC URL serialization
+ ux = QString("irc://%1:%2/%3").arg(m_server->getServerName()).arg(m_server->getPort()).arg(m_urlToDrag.mid(2));
+ }
+ else if (m_urlToDrag.startsWith("#"))
+ {
+ ux = m_urlToDrag.mid(1);
+ }
+
+ KURLDrag* u = new KURLDrag(ux, viewport());
+ u->drag();
+ return;
+ }
+
+ KTextBrowser::contentsMouseMoveEvent(ev);
+}
+
+void IRCView::contentsContextMenuEvent(QContextMenuEvent* ev)
+{
+ bool block = contextMenu(ev);
+
+ // HACK Replace % with \x03 in the url to keep Qt from doing stupid things
+ m_highlightedURL = anchorAt(viewportToContents(mapFromGlobal(QCursor::pos())));
+ m_highlightedURL = m_highlightedURL.replace('\x03', "%");
+ // Hack to counter the fact that we're given an decoded url
+ m_highlightedURL = KURL::fromPathOrURL(m_highlightedURL).url();
+
+ if (m_highlightedURL.isEmpty()) viewport()->setCursor(Qt::ArrowCursor);
+
+ if(m_highlightedURL.startsWith("#"))
+ {
+ // HACK:Use space as a placeholder for \ as Qt tries to be clever and does a replace to / in urls in QTextEdit
+ m_highlightedURL = m_highlightedURL.replace(' ', "\\");
+ }
+
+ if (!block)
+ KTextBrowser::contentsContextMenuEvent(ev);
+}
+
+bool IRCView::contextMenu(QContextMenuEvent* ce)
+{
+ if (m_server && m_isOnNick && m_nickPopup->isEnabled())
+ {
+ updateNickMenuEntries(m_nickPopup, getContextNick());
+
+ if(m_nickPopup->exec(ce->globalPos()) == -1)
+ clearContextNick();
+
+ m_isOnNick = false;
+ }
+ else if (m_server && m_isOnChannel && m_channelPopup->isEnabled())
+ {
+ m_channelPopup->exec(ce->globalPos());
+ m_isOnChannel = false;
+ }
+ else
+ {
+ KActionCollection* actionCollection = KonversationApplication::instance()->getMainWindow()->actionCollection();
+ KToggleAction* toggleMenuBarAction = static_cast<KToggleAction*>(actionCollection->action("options_show_menubar"));
+
+ if (toggleMenuBarAction && !toggleMenuBarAction->isChecked())
+ {
+ toggleMenuBarAction->plug(m_popup, 0);
+ m_popup->setItemVisible(toggleMenuBarSeparator, true);
+ }
+
+ m_popup->setItemEnabled(Copy,(hasSelectedText()));
+
+ KAction* channelSettingsAction = 0;
+
+ if (m_chatWin->getType() == ChatWindow::Channel)
+ {
+ channelSettingsAction = KonversationApplication::instance()->getMainWindow()->actionCollection()->action("channel_settings");
+ if (channelSettingsAction) channelSettingsAction->plug(m_popup);
+ }
+
+ if (m_chatWin->getType() == ChatWindow::Query)
+ {
+ updateNickMenuEntries(m_popup, m_chatWin->getName());
+ clearContextNick();
+ }
+
+ int r = m_popup->exec(ce->globalPos());
+
+ switch (r)
+ {
+ case -1:
+ // dummy. -1 means, no entry selected. we don't want -1to go in default, so
+ // we catch it here
+ break;
+ case Copy:
+ copy();
+ break;
+ case CopyUrl:
+ {
+ QClipboard *cb = KApplication::kApplication()->clipboard();
+ cb->setText(m_urlToCopy,QClipboard::Selection);
+ cb->setText(m_urlToCopy,QClipboard::Clipboard);
+ break;
+ }
+ case SelectAll:
+ selectAll();
+ break;
+ case Search:
+ search();
+ break;
+ case SendFile:
+ emit sendFile();
+ break;
+ case Bookmark:
+ {
+ KBookmarkManager* bm = KBookmarkManager::userBookmarksManager();
+ KBookmarkGroup bg = bm->addBookmarkDialog(m_urlToCopy, QString());
+ bm->save();
+ bm->emitChanged(bg);
+ break;
+ }
+ case SaveAs:
+ saveLinkAs(m_urlToCopy);
+ break;
+ default:
+ emit extendedPopup(r);
+ }
+
+ if (toggleMenuBarAction)
+ {
+ toggleMenuBarAction->unplug(m_popup);
+ m_popup->setItemVisible(toggleMenuBarSeparator, false);
+ }
+
+ if (channelSettingsAction) channelSettingsAction->unplug(m_popup);
+ }
+ return true;
+}
+
+void IRCView::setupNickPopupMenu()
+{
+ m_nickPopup = new KPopupMenu(this,"nicklist_context_menu");
+ m_modes = new KPopupMenu(this,"nicklist_modes_context_submenu");
+ m_kickban = new KPopupMenu(this,"nicklist_kick_ban_context_submenu");
+ m_nickPopupId= m_nickPopup->insertTitle(m_currentNick);
+
+ m_nickPopup->insertItem(i18n("&Whois"),Konversation::Whois);
+ m_nickPopup->insertItem(i18n("&Version"),Konversation::Version);
+ m_nickPopup->insertItem(i18n("&Ping"),Konversation::Ping);
+
+ m_nickPopup->insertSeparator();
+
+ m_modes->insertItem(i18n("Give Op"),Konversation::GiveOp);
+ m_modes->insertItem(i18n("Take Op"),Konversation::TakeOp);
+ m_modes->insertItem(i18n("Give Voice"),Konversation::GiveVoice);
+ m_modes->insertItem(i18n("Take Voice"),Konversation::TakeVoice);
+ m_nickPopup->insertItem(i18n("Modes"),m_modes,Konversation::ModesSub);
+
+ m_kickban->insertItem(i18n("Kick"),Konversation::Kick);
+ m_kickban->insertItem(i18n("Kickban"),Konversation::KickBan);
+ m_kickban->insertItem(i18n("Ban Nickname"),Konversation::BanNick);
+ m_kickban->insertSeparator();
+ m_kickban->insertItem(i18n("Ban *!*@*.host"),Konversation::BanHost);
+ m_kickban->insertItem(i18n("Ban *!*@domain"),Konversation::BanDomain);
+ m_kickban->insertItem(i18n("Ban *!user@*.host"),Konversation::BanUserHost);
+ m_kickban->insertItem(i18n("Ban *!user@domain"),Konversation::BanUserDomain);
+ m_kickban->insertSeparator();
+ m_kickban->insertItem(i18n("Kickban *!*@*.host"),Konversation::KickBanHost);
+ m_kickban->insertItem(i18n("Kickban *!*@domain"),Konversation::KickBanDomain);
+ m_kickban->insertItem(i18n("Kickban *!user@*.host"),Konversation::KickBanUserHost);
+ m_kickban->insertItem(i18n("Kickban *!user@domain"),Konversation::KickBanUserDomain);
+ m_nickPopup->insertItem(i18n("Kick / Ban"),m_kickban,Konversation::KickBanSub);
+
+ m_nickPopup->insertItem(i18n("Ignore"), Konversation::IgnoreNick);
+ m_nickPopup->insertItem(i18n("Unignore"), Konversation::UnignoreNick);
+ m_nickPopup->setItemVisible(Konversation::IgnoreNick, false);
+ m_nickPopup->setItemVisible(Konversation::UnignoreNick, false);
+
+ m_nickPopup->insertSeparator();
+
+ m_nickPopup->insertItem(i18n("Open Query"),Konversation::OpenQuery);
+ if (kapp->authorize("allow_downloading"))
+ {
+ m_nickPopup->insertItem(SmallIcon("2rightarrow"),i18n("Send &File..."),Konversation::DccSend);
+ }
+ m_nickPopup->insertSeparator();
+
+ m_nickPopup->insertItem(i18n("Add to Watched Nicks"), Konversation::AddNotify);
+
+ connect(m_nickPopup, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+ connect(m_modes, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+ connect(m_kickban, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+}
+
+void IRCView::updateNickMenuEntries(QPopupMenu* popup, const QString& nickname)
+{
+ if (popup)
+ {
+ if (Preferences::isIgnored(nickname))
+ {
+ popup->setItemVisible(Konversation::UnignoreNick, true);
+ popup->setItemVisible(Konversation::IgnoreNick, false);
+ }
+ else
+ {
+ popup->setItemVisible(Konversation::IgnoreNick, true);
+ popup->setItemVisible(Konversation::UnignoreNick, false);
+ }
+
+ if (!m_server || !m_server->getServerGroup())
+ popup->setItemEnabled(Konversation::AddNotify, false);
+ else if (!m_server->isConnected())
+ popup->setItemEnabled(Konversation::AddNotify, false);
+ else if (!Preferences::hasNotifyList(m_server->getServerGroup()->id()))
+ popup->setItemEnabled(Konversation::AddNotify, false);
+ else if (Preferences::isNotify(m_server->getServerGroup()->id(), nickname))
+ popup->setItemEnabled(Konversation::AddNotify, false);
+ else
+ popup->setItemEnabled(Konversation::AddNotify, true);
+ }
+}
+
+void IRCView::setupQueryPopupMenu()
+{
+ m_nickPopup = new KPopupMenu(this,"query_context_menu");
+ m_nickPopupId = m_nickPopup->insertTitle(m_currentNick);
+ m_nickPopup->insertItem(i18n("&Whois"),Konversation::Whois);
+ m_nickPopup->insertItem(i18n("&Version"),Konversation::Version);
+ m_nickPopup->insertItem(i18n("&Ping"),Konversation::Ping);
+ m_nickPopup->insertSeparator();
+
+ m_nickPopup->insertItem(i18n("Ignore"), Konversation::IgnoreNick);
+ m_nickPopup->insertItem(i18n("Unignore"), Konversation::UnignoreNick);
+ m_nickPopup->setItemVisible(Konversation::IgnoreNick, false);
+ m_nickPopup->setItemVisible(Konversation::UnignoreNick, false);
+
+ if (kapp->authorize("allow_downloading"))
+ m_nickPopup->insertItem(SmallIcon("2rightarrow"),i18n("Send &File..."),Konversation::DccSend);
+
+ m_nickPopup->insertItem(i18n("Add to Watched Nicks"), Konversation::AddNotify);
+
+ connect(m_nickPopup, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+}
+
+void IRCView::setupChannelPopupMenu()
+{
+ m_channelPopup = new KPopupMenu(this,"channel_context_menu");
+ m_channelPopupId = m_channelPopup->insertTitle(m_currentChannel);
+ m_channelPopup->insertItem(i18n("&Join"),Konversation::Join);
+ m_channelPopup->insertItem(i18n("Get &user list"),Konversation::Names);
+ m_channelPopup->insertItem(i18n("Get &topic"),Konversation::Topic);
+
+ connect(m_channelPopup, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+}
+
+void IRCView::setNickAndChannelContextMenusEnabled(bool enable)
+{
+ if (m_nickPopup) m_nickPopup->setEnabled(enable);
+ if (m_channelPopup) m_channelPopup->setEnabled(enable);
+}
+
+void IRCView::search()
+{
+ emit doSearch();
+}
+
+void IRCView::searchAgain()
+{
+ if (m_pattern.isEmpty())
+ {
+ emit doSearch();
+ }
+ else
+ {
+ // next search must begin one index before / after the last search
+ // depending on the search direction.
+ if(m_forward)
+ {
+ ++m_findIndex;
+ if(m_findIndex == paragraphLength(m_findParagraph))
+ {
+ m_findIndex = 0;
+ ++m_findParagraph;
+ }
+ }
+ else
+ {
+ if(m_findIndex)
+ {
+ --m_findIndex;
+ }
+ else
+ {
+ --m_findParagraph;
+ m_findIndex = paragraphLength(m_findParagraph);
+ }
+ }
+
+ if(!find(m_pattern, m_caseSensitive, m_wholeWords, m_forward, &m_findParagraph, &m_findIndex))
+ {
+ KMessageBox::information(this,i18n("No matches found for \"%1\".").arg(m_pattern),i18n("Information"));
+ }
+
+ }
+}
+
+bool IRCView::search(const QString& pattern, bool caseSensitive,
+bool wholeWords, bool forward, bool fromCursor)
+{
+ m_pattern = pattern;
+ m_caseSensitive = caseSensitive;
+ m_wholeWords = wholeWords;
+ m_forward = forward;
+ m_fromCursor = fromCursor;
+
+ if (m_pattern.isEmpty())
+ return true;
+
+ if (!m_fromCursor)
+ {
+ if(m_forward)
+ {
+ m_findParagraph = 1;
+ m_findIndex = 1;
+ }
+ else
+ {
+ m_findParagraph = paragraphs();
+ m_findIndex = paragraphLength(paragraphs());
+ }
+ }
+
+ return searchNext();
+}
+
+bool IRCView::searchNext(bool reversed)
+{
+ if (m_pattern.isEmpty())
+ return true;
+
+ bool fwd = (reversed ? !m_forward : m_forward);
+ // next search must begin one index before / after the last search
+ // depending on the search direction.
+ if (fwd)
+ {
+ ++m_findIndex;
+ if(m_findIndex == paragraphLength(m_findParagraph))
+ {
+ m_findIndex = 0;
+ ++m_findParagraph;
+ }
+ }
+ else
+ {
+ if (m_findIndex)
+ {
+ --m_findIndex;
+ }
+ else
+ {
+ --m_findParagraph;
+ m_findIndex = paragraphLength(m_findParagraph);
+ }
+ }
+
+ return find(m_pattern, m_caseSensitive, m_wholeWords, fwd,
+ &m_findParagraph, &m_findIndex);
+}
+
+// other windows can link own menu entries here
+QPopupMenu* IRCView::getPopup() const
+{
+ return m_popup;
+}
+
+// for more information about these RTFM
+// http://www.unicode.org/reports/tr9/
+// http://www.w3.org/TR/unicode-xml/
+QChar IRCView::LRM = (ushort)0x200e; // Right-to-Left Mark
+QChar IRCView::RLM = (ushort)0x200f; // Left-to-Right Mark
+QChar IRCView::LRE = (ushort)0x202a; // Left-to-Right Embedding
+QChar IRCView::RLE = (ushort)0x202b; // Right-to-Left Embedding
+QChar IRCView::RLO = (ushort)0x202e; // Right-to-Left Override
+QChar IRCView::LRO = (ushort)0x202d; // Left-to-Right Override
+QChar IRCView::PDF = (ushort)0x202c; // Previously Defined Format
+
+QChar::Direction IRCView::basicDirection(const QString &string)
+{
+ // The following code decides between LTR or RTL direction for
+ // a line based on the amount of each type of characters pre-
+ // sent. It does so by counting, but stops when one of the two
+ // counters becomes higher than half of the string length to
+ // avoid unnecessary work.
+
+ unsigned int pos = 0;
+ unsigned int rtl_chars = 0;
+ unsigned int ltr_chars = 0;
+ unsigned int str_len = string.length();
+ unsigned int str_half_len = str_len/2;
+
+ for(pos=0; pos < str_len; pos++)
+ {
+ if (!(string[pos].isNumber() || string[pos].isSymbol() ||
+ string[pos].isSpace() || string[pos].isPunct() ||
+ string[pos].isMark()))
+ {
+ switch(string[pos].direction())
+ {
+ case QChar::DirL:
+ case QChar::DirLRO:
+ case QChar::DirLRE:
+ ltr_chars++;
+ break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirRLO:
+ case QChar::DirRLE:
+ rtl_chars++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ltr_chars > str_half_len)
+ return QChar::DirL;
+ else if (rtl_chars > str_half_len)
+ return QChar::DirR;
+ }
+
+ if (rtl_chars > ltr_chars)
+ return QChar::DirR;
+ else
+ return QChar::DirL;
+}
+
+void IRCView::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if(acceptDrops() && QUriDrag::canDecode(e))
+ e->accept();
+}
+
+void IRCView::contentsDropEvent(QDropEvent *e)
+{
+ QStrList s;
+ if(QUriDrag::decode(e,s))
+ emit filesDropped(s);
+}
+
+QString IRCView::timeStamp()
+{
+ if(Preferences::timestamping())
+ {
+ QTime time = QTime::currentTime();
+ QString timeColor = Preferences::color(Preferences::Time).name();
+ QString timeFormat = Preferences::timestampFormat();
+ QString timeString;
+
+ if(!Preferences::showDate())
+ {
+ timeString = QString("<font color=\"" + timeColor + "\">[%1]</font> ").arg(time.toString(timeFormat));
+ }
+ else
+ {
+ QDate date = QDate::currentDate();
+ timeString = QString("<font color=\"" +
+ timeColor + "\">[%1 %2]</font> ")
+ .arg(KGlobal::locale()->formatDate(date, true /*short format*/),
+ time.toString(timeFormat));
+ }
+
+ return timeString;
+ }
+
+ return QString();
+}
+
+void IRCView::setChatWin(ChatWindow* chatWin)
+{
+ m_chatWin = chatWin;
+
+ if(m_chatWin->getType()==ChatWindow::Channel)
+ setupNickPopupMenu();
+ else
+ setupQueryPopupMenu();
+
+ setupChannelPopupMenu();
+}
+
+void IRCView::keyPressEvent(QKeyEvent* e)
+{
+ KKey key(e);
+
+ if (KStdAccel::copy().contains(key))
+ {
+ copy();
+ e->accept();
+ return;
+ }
+ else if (KStdAccel::paste().contains(key))
+ {
+ emit textPasted(false);
+ e->accept();
+ return;
+ }
+
+ KTextBrowser::keyPressEvent(e);
+}
+
+void IRCView::resizeEvent(QResizeEvent* e)
+{
+ bool doScroll = ( KTextBrowser::verticalScrollBar()->value() == KTextBrowser::verticalScrollBar()->maxValue());
+ KTextBrowser::resizeEvent(e);
+
+ if(doScroll)
+ {
+ QTimer::singleShot(0, this, SLOT(updateScrollBarPos()));
+ }
+}
+
+void IRCView::updateScrollBarPos()
+{
+ ensureVisible(contentsX(), contentsHeight());
+ repaintContents(false);
+}
+
+void IRCView::saveLinkAs(const QString& url)
+{
+ KURL source(url);
+ KFileDialog dialog(":SaveLinkAs", QString (), this, "savelinkdia", true);
+ dialog.setCaption(i18n("Save Link As"));
+ dialog.setSelection(source.fileName());
+
+ if(dialog.exec() == QDialog::Rejected)
+ return;
+
+ KURL destination = dialog.selectedURL();
+ KIO::copyAs(source, destination);
+}
+
+#include "ircview.moc"
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/ircview.h b/konversation/src/ircview.h
new file mode 100644
index 0000000..39e3ff0
--- /dev/null
+++ b/konversation/src/ircview.h
@@ -0,0 +1,242 @@
+#ifndef IRCVIEW_H
+#define IRCVIEW_H
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005-2006 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "common.h"
+
+#include <qmap.h>
+#include <qfontdatabase.h>
+
+#include <ktextbrowser.h>
+
+
+class QPixmap;
+class QStrList;
+class QDropEvent;
+class QDragEnterEvent;
+class QEvent;
+
+class KPopupMenu;
+
+class Server;
+class ChatWindow;
+class SearchBar;
+
+class IRCView : public KTextBrowser
+{
+ Q_OBJECT
+
+ public:
+ IRCView(QWidget* parent,Server* newServer);
+ ~IRCView();
+
+ void clear();
+ void setViewBackground(const QColor& backgroundColor, const QString& pixmapName);
+ void setServer(Server* server);
+
+ // Returns the current nick under context menu.
+ const QString& getContextNick() const;
+
+ void enableParagraphSpacing();
+
+ QPopupMenu* getPopup() const;
+
+ enum PopupIDs
+ {
+ Copy,CopyUrl,SelectAll,
+ Search,
+ SendFile,
+ Bookmark,
+ SaveAs
+ };
+
+ void setChatWin(ChatWindow* chatWin);
+
+ bool search(const QString& pattern, bool caseSensitive,
+ bool wholeWords, bool forward, bool fromCursor);
+ bool searchNext(bool reversed = false);
+
+ QColor highlightColor() { return m_highlightColor; }
+ QString currentChannel() { return m_currentChannel; }
+
+ void setNickAndChannelContextMenusEnabled(bool enable);
+
+ bool hasLines();
+
+
+ signals:
+ // Notify container of new text and highlight state
+ void gotFocus(); // So we can set focus to input line
+ void textToLog(const QString& text);
+ void sendFile();
+ void extendedPopup(int id);
+ void autoText(const QString& text);
+ void textPasted(bool useSelection);
+ void popupCommand(int);
+ void filesDropped(const QStrList&);
+ void doSearch();
+
+ void setStatusBarTempText(const QString&);
+ void clearStatusBarTempText();
+
+ public slots:
+ void insertRememberLine();
+ void cancelRememberLine();
+ void insertMarkerLine();
+ void clearLines();
+
+ void append(const QString& nick, const QString& message);
+ void appendRaw(const QString& message, bool suppressTimestamps=false, bool self = false);
+ void appendQuery(const QString& nick, const QString& message, bool inChannel = false);
+ void appendQueryAction(const QString& nick, const QString& message);
+ void appendChannelAction(const QString& nick, const QString& message);
+ void appendServerMessage(const QString& type, const QString& message, bool parseURL = true);
+ void appendCommandMessage(const QString& command, const QString& message, bool important,
+ bool parseURL=true, bool self=false);
+ void appendBacklogMessage(const QString& firstColumn, const QString& message);
+ void search();
+ void searchAgain();
+
+ void setCurrentChannel(const QString& channel) { m_currentChannel = channel; }
+
+ virtual void removeSelectedText(int selNum=0);
+
+ virtual void scrollToBottom(); // Overwritten for internal reasons
+
+ // Clears context nick
+ void clearContextNick();
+
+ // Updates the scrollbar position
+ void updateScrollBarPos();
+
+ // Override ensureCursorVisible() to have an ability to disable this call
+ virtual void ensureCursorVisible();
+
+ protected slots:
+ void highlightedSlot(const QString& link);
+ void saveLinkAs(const QString& url);
+
+ protected:
+ void openLink(const QString &url, bool newTab=false);
+ QString filter(const QString& line, const QString& defaultColor, const QString& who=NULL,
+ bool doHighlight=true, bool parseURL=true, bool self=false);
+ void appendAction(const QString& nick, const QString& message);
+ void doAppend(const QString& line, bool self=false);
+ void replaceDecoration(QString& line,char decoration,char replacement);
+ virtual void contentsDragMoveEvent(QDragMoveEvent* e);
+ virtual void contentsDropEvent(QDropEvent* e);
+ virtual void contentsMouseReleaseEvent(QMouseEvent* ev);
+ virtual void contentsMousePressEvent(QMouseEvent* ev);
+ virtual void contentsMouseMoveEvent(QMouseEvent* ev);
+ virtual void contentsContextMenuEvent(QContextMenuEvent* ev);
+
+ virtual void keyPressEvent(QKeyEvent* e);
+ virtual void resizeEvent(QResizeEvent* e);
+
+ void hideEvent(QHideEvent* event);
+ void showEvent(QShowEvent* event);
+ void paintEvent(QPaintEvent* event);
+
+ bool contextMenu(QContextMenuEvent* ce);
+
+ void setupNickPopupMenu();
+ void updateNickMenuEntries(QPopupMenu* popup, const QString& nickname);
+ void setupQueryPopupMenu();
+ void setupChannelPopupMenu();
+
+ QChar::Direction basicDirection(const QString &string);
+
+ /// Returns a formated timestamp if timestamps are enabled else it returns QString::null
+ QString timeStamp();
+
+ /// Returns a formated nick string
+ QString createNickLine(const QString& nick, bool encapsulateNick = true, bool privMsg = false);
+
+ // used by search function
+ int m_findParagraph;
+ int m_findIndex;
+
+ void appendLine(const QString& color);
+ void appendRememberLine();
+
+ void updateLineParagraphs(int numRemoved);
+ void wipeLineParagraphs();
+
+ int m_rememberLineParagraph;
+ bool m_rememberLineDirtyBit;
+
+ QValueList<int> m_markerLineParagraphs;
+
+ // This is set to what we last sent status text to the statusbar. Empty if we have sent clearStatusBarTempText() string
+ QString m_lastStatusText;
+ // decide if we should place the scrollbar at the bottom on show()
+ bool m_resetScrollbar;
+
+ QString m_autoTextToSend;
+ Konversation::TabNotifyType m_tabNotification;
+ bool m_copyUrlMenu;
+ QString m_urlToCopy;
+
+ QString m_buffer;
+ Server* m_server;
+ QPopupMenu* m_popup;
+ int toggleMenuBarSeparator;
+ int copyUrlMenuSeparator;
+ KPopupMenu* m_nickPopup;
+ KPopupMenu* m_channelPopup;
+ KPopupMenu* m_modes;
+ KPopupMenu* m_kickban;
+
+ static QChar LRM;
+ static QChar RLM;
+ static QChar LRE;
+ static QChar RLE;
+ static QChar RLO;
+ static QChar LRO;
+ static QChar PDF;
+
+ bool m_caseSensitive;
+ bool m_wholeWords;
+ bool m_forward;
+ bool m_fromCursor;
+ QString m_pattern;
+ QColor m_highlightColor;
+
+ uint m_offset;
+ QStringList m_colorList;
+
+ QString m_highlightedURL; // the URL we're currently hovering on with the mouse
+ QString m_currentNick;
+ QString m_currentChannel;
+ bool m_isOnNick;
+ bool m_isOnChannel;
+ bool m_mousePressed;
+ QString m_urlToDrag;
+ QPoint m_pressPosition;
+ int m_nickPopupId;
+ int m_channelPopupId;
+
+ QFontDatabase m_fontDataBase;
+
+ // Provide ability to disable parent ensureCursorVisible() call
+ bool m_disableEnsureCursorVisible;
+ // If this widget was painted at least once. Needed for scrollbar
+ // reset HACK as a precaution.
+ bool m_wasPainted;
+
+ ChatWindow* m_chatWin;
+ friend class IRCStyleSheet;
+};
+#endif
diff --git a/konversation/src/ircviewbox.cpp b/konversation/src/ircviewbox.cpp
new file mode 100644
index 0000000..817f7d9
--- /dev/null
+++ b/konversation/src/ircviewbox.cpp
@@ -0,0 +1,141 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Renchi Raju <renchi@pooh.tam.uiuc.edu>
+*/
+
+#include "ircviewbox.h"
+#include "ircview.h"
+#include "searchbar.h"
+
+#include <klocale.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <qpixmap.h>
+
+
+static QPixmap getIcon(const QString& name)
+{
+ KIconLoader* iconLoader = kapp->iconLoader();
+ return iconLoader->loadIcon(name, KIcon::Toolbar, 16);
+}
+
+IRCViewBox::IRCViewBox(QWidget* parent, Server* newServer)
+: QVBox(parent)
+{
+ m_ircView = new IRCView(this, newServer);
+ m_searchBar = new SearchBar(this);
+ m_searchBar->hide();
+ m_matchedOnce = false;
+
+ connect(m_searchBar, SIGNAL(signalSearchChanged(const QString&)),
+ this, SLOT(slotSearchChanged(const QString&)));
+ connect(m_searchBar, SIGNAL(signalSearchNext()),
+ this, SLOT(slotSearchNext()));
+ connect(m_searchBar, SIGNAL(signalSearchPrevious()),
+ this, SLOT(slotSearchPrevious()));
+ connect(m_ircView, SIGNAL(doSearch()),
+ SLOT(slotSearch()));
+ connect(m_searchBar, SIGNAL(hidden()), m_ircView, SIGNAL(gotFocus()));
+}
+
+IRCViewBox::~IRCViewBox()
+{
+ delete m_ircView;
+ delete m_searchBar;
+}
+
+IRCView* IRCViewBox::ircView() const
+{
+ return m_ircView;
+}
+
+void IRCViewBox::slotSearch()
+{
+ if (m_searchBar->isVisible())
+ {
+ m_searchBar->hide();
+ return;
+ }
+
+ m_searchBar->show();
+ m_searchBar->setFocus();
+}
+
+void IRCViewBox::slotSearchNext()
+{
+ searchNext(false);
+}
+
+void IRCViewBox::slotSearchPrevious()
+{
+ searchNext(true);
+}
+
+void IRCViewBox::searchNext(bool reversed)
+{
+ bool match = m_ircView->searchNext(reversed);
+
+ if (match)
+ {
+ m_searchBar->setHasMatch(true);
+ m_searchBar->setStatus(QPixmap(), "");
+ return;
+ }
+
+ if (!m_matchedOnce)
+ {
+ m_searchBar->setHasMatch(false);
+ m_searchBar->setStatus(getIcon("messagebox_warning"),
+ i18n("Phrase not found"));
+ return;
+ }
+
+ match = m_ircView->search(m_searchBar->pattern(),
+ m_searchBar->caseSensitive(),
+ m_searchBar->wholeWords(),
+ m_searchBar->searchForward(),
+ false);
+
+ if (!match)
+ {
+ m_searchBar->setHasMatch(false);
+ m_searchBar->setStatus(getIcon("messagebox_warning"),
+ i18n("Phrase not found"));
+ return;
+ }
+
+ m_searchBar->setHasMatch(true);
+ m_searchBar->setStatus(getIcon("messagebox_info"),
+ i18n("Wrapped search"));
+}
+
+void IRCViewBox::slotSearchChanged(const QString& pattern)
+{
+ bool match = m_ircView->search(pattern,
+ m_searchBar->caseSensitive(),
+ m_searchBar->wholeWords(),
+ m_searchBar->searchForward(),
+ m_searchBar->fromCursor());
+
+ if (match)
+ {
+ m_searchBar->setHasMatch(true);
+ m_searchBar->setStatus(QPixmap(), "");
+ }
+ else
+ {
+ m_searchBar->setHasMatch(false);
+ m_searchBar->setStatus(getIcon("messagebox_warning"),
+ i18n("Phrase not found"));
+ }
+
+ m_matchedOnce = match;
+}
+
+#include "ircviewbox.moc"
diff --git a/konversation/src/ircviewbox.h b/konversation/src/ircviewbox.h
new file mode 100644
index 0000000..321e293
--- /dev/null
+++ b/konversation/src/ircviewbox.h
@@ -0,0 +1,49 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Renchi Raju <renchi@pooh.tam.uiuc.edu>
+*/
+
+#ifndef IRCVIEWBOX_H
+#define IRCVIEWBOX_H
+
+#include <qvbox.h>
+
+
+class IRCView;
+class SearchBar;
+class Server;
+
+class IRCViewBox : public QVBox
+{
+ Q_OBJECT
+
+ public:
+
+ IRCViewBox(QWidget* parent, Server* newServer);
+ ~IRCViewBox();
+
+ IRCView* ircView() const;
+
+ public slots:
+
+ void slotSearch();
+ void slotSearchNext();
+ void slotSearchPrevious();
+ void slotSearchChanged(const QString& pattern);
+
+ protected:
+ void searchNext(bool reversed = false);
+
+ private:
+
+ IRCView* m_ircView;
+ SearchBar* m_searchBar;
+ bool m_matchedOnce;
+};
+#endif /* IRCVIEWBOX_H */
diff --git a/konversation/src/joinchanneldialog.cpp b/konversation/src/joinchanneldialog.cpp
new file mode 100644
index 0000000..4799da2
--- /dev/null
+++ b/konversation/src/joinchanneldialog.cpp
@@ -0,0 +1,104 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 by Peter Simonsson <psn@linux.se>
+*/
+
+#include "joinchanneldialog.h"
+#include "joinchannelui.h"
+#include "server.h"
+#include "channel.h"
+#include "servergroupsettings.h"
+
+#include <qlabel.h>
+
+#include <klocale.h>
+#include <kcombobox.h>
+#include <klineedit.h>
+
+
+namespace Konversation
+{
+
+ JoinChannelDialog::JoinChannelDialog(Server* server, QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Join Channel on %1").arg(server->getDisplayName()), Ok|Cancel, Ok)
+ {
+ m_server = server;
+ m_widget = new JoinChannelUI(this);
+ setMainWidget(m_widget);
+
+ m_widget->serverLbl->setText(server->getDisplayName());
+
+ if (m_server->getServerGroup())
+ {
+ ChannelList history = server->getServerGroup()->channelHistory();
+ ChannelList::iterator endIt = history.end();
+ const QPtrList<Channel> &channels = server->getChannelList();
+ QPtrListIterator<Channel> chanIt(channels);
+ Channel* chan = 0;
+ bool joined = false;
+
+ for(ChannelList::iterator it = history.begin(); it != endIt; ++it)
+ {
+ chan = chanIt.toFirst();
+ joined = false;
+
+ while(chan)
+ {
+ if(chan->getName() == (*it).name())
+ {
+ joined = true;
+ }
+
+ ++chanIt;
+ chan = chanIt.current();
+ }
+
+ if(!joined)
+ {
+ m_widget->channelCombo->addToHistory((*it).name());
+ }
+ }
+ }
+
+ m_widget->channelCombo->setCurrentText("");
+ }
+
+ JoinChannelDialog::~JoinChannelDialog()
+ {
+ }
+
+ QString JoinChannelDialog::channel() const
+ {
+ QString channel = m_widget->channelCombo->currentText();
+
+ if(!m_server->isAChannel(channel))
+ {
+ channel = '#' + channel;
+ }
+
+ return channel;
+ }
+
+ QString JoinChannelDialog::password() const
+ {
+ return m_widget->passwordEdit->text();
+ }
+
+ void JoinChannelDialog::slotOk()
+ {
+ // If the channel already exist in the history only the password will be updated.
+ if (m_server->getServerGroup())
+ m_server->getServerGroup()->appendChannelHistory(ChannelSettings(channel(), password()));
+
+ accept();
+ }
+
+}
+
+#include "joinchanneldialog.moc"
diff --git a/konversation/src/joinchanneldialog.h b/konversation/src/joinchanneldialog.h
new file mode 100644
index 0000000..7cad840
--- /dev/null
+++ b/konversation/src/joinchanneldialog.h
@@ -0,0 +1,44 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+*/
+
+#ifndef KONVERSATIONJOINCHANNELDIALOG_H
+#define KONVERSATIONJOINCHANNELDIALOG_H
+
+#include <kdialogbase.h>
+
+
+class Server;
+
+namespace Konversation
+{
+
+ class JoinChannelUI;
+
+ class JoinChannelDialog : public KDialogBase
+ {
+ Q_OBJECT
+ public:
+ explicit JoinChannelDialog(Server* server, QWidget *parent = 0, const char *name = 0);
+ ~JoinChannelDialog();
+
+ QString channel() const;
+ QString password() const;
+
+ protected slots:
+ virtual void slotOk();
+
+ private:
+ JoinChannelUI* m_widget;
+ Server* m_server;
+ };
+
+}
+#endif
diff --git a/konversation/src/joinchannelui.ui b/konversation/src/joinchannelui.ui
new file mode 100644
index 0000000..7863331
--- /dev/null
+++ b/konversation/src/joinchannelui.ui
@@ -0,0 +1,110 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Konversation::JoinChannelUI</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</comment>
+<author>(C) 2004 Peter Simonsson</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Konversation::JoinChannelUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>507</width>
+ <height>209</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>channelLabel</cstring>
+ </property>
+ <property name="text">
+ <string>C&amp;hannel:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>channelCombo</cstring>
+ </property>
+ </widget>
+ <widget class="KHistoryCombo" row="1" column="1">
+ <property name="name">
+ <cstring>channelCombo</cstring>
+ </property>
+ <property name="autoCompletion">
+ <bool>true</bool>
+ </property>
+ <property name="contextMenuEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>passwordEdit</cstring>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>passwordLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwordEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Server:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>serverLbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/konsolepanel.cpp b/konversation/src/konsolepanel.cpp
new file mode 100644
index 0000000..2fe5b99
--- /dev/null
+++ b/konversation/src/konsolepanel.cpp
@@ -0,0 +1,75 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2003 Mickael Marchand <marchand@kde.org>
+*/
+
+#include "konsolepanel.h"
+#include "common.h"
+#include "viewcontainer.h"
+
+#include <kdebug.h>
+#include <klibloader.h>
+#include <klocale.h>
+
+
+KonsolePanel::KonsolePanel(QWidget *p) : ChatWindow( p ), k_part (0)
+{
+ setName(i18n("Konsole"));
+ setType(ChatWindow::Konsole);
+ KLibFactory *fact = KLibLoader::self()->factory("libkonsolepart");
+ if (!fact) return;
+
+ k_part = (KParts::ReadOnlyPart *) fact->create(this);
+ if (!k_part) return;
+
+ k_part->widget()->setFocusPolicy(QWidget::WheelFocus);
+ setFocusProxy(k_part->widget());
+ k_part->widget()->setFocus();
+
+ connect(k_part, SIGNAL(destroyed()), this, SLOT(partDestroyed()));
+ connect(k_part, SIGNAL(receivedData(const QString&)), this, SLOT(konsoleChanged(const QString&)));
+}
+
+KonsolePanel::~KonsolePanel()
+{
+ kdDebug() << "KonsolePanel::~KonsolePanel()" << endl;
+ if ( k_part )
+ {
+ // make sure to prevent partDestroyed() signals from being sent
+ disconnect(k_part, SIGNAL(destroyed()), this, SLOT(partDestroyed()));
+ delete k_part;
+ }
+}
+
+QWidget* KonsolePanel::getWidget()
+{
+ if (k_part)
+ return k_part->widget();
+ else
+ return 0;
+}
+
+void KonsolePanel::childAdjustFocus()
+{
+ if (k_part) k_part->widget()->setFocus();
+}
+
+void KonsolePanel::partDestroyed()
+{
+ k_part = 0;
+
+ emit closeView(this);
+}
+
+void KonsolePanel::konsoleChanged(const QString& /* data */)
+{
+ activateTabNotification(Konversation::tnfSystem);
+}
+
+#include "konsolepanel.moc"
diff --git a/konversation/src/konsolepanel.h b/konversation/src/konsolepanel.h
new file mode 100644
index 0000000..ad9d1c0
--- /dev/null
+++ b/konversation/src/konsolepanel.h
@@ -0,0 +1,47 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2003 Mickael Marchand <marchand@kde.org>
+*/
+
+#ifndef KONSOLE_PANEL_H
+#define KONSOLE_PANEL_H
+
+#include "chatwindow.h"
+
+#include <kparts/part.h>
+
+
+class KonsolePanel : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit KonsolePanel(QWidget *p);
+ ~KonsolePanel();
+
+ virtual void setName(const QString& newName) { ChatWindow::setName(newName); }
+
+ QWidget* getWidget();
+
+ signals:
+ void closeView(ChatWindow* view);
+
+ public slots:
+ void partDestroyed();
+
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ protected slots:
+ void konsoleChanged(const QString& data);
+
+ private:
+ KParts::ReadOnlyPart *k_part;
+};
+#endif /* KONSOLE_PANEL_H */
diff --git a/konversation/src/konvdcop.cpp b/konversation/src/konvdcop.cpp
new file mode 100644
index 0000000..e7abda9
--- /dev/null
+++ b/konversation/src/konvdcop.cpp
@@ -0,0 +1,359 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ The konversation DCOP interface class
+ begin: Mar 7 2003
+ copyright: (C) 2003 by Alex Zepeda
+ email: zipzippy@sonic.net
+*/
+
+#include "konvdcop.h"
+#include "konversationapplication.h"
+#include "connectionmanager.h"
+#include "awaymanager.h"
+#include "channel.h"
+#include "identity.h"
+#include "server.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <dcopclient.h>
+#include <klocale.h>
+
+
+KonvDCOP::KonvDCOP() : DCOPObject("irc"), QObject(0, "irc")
+{
+ connectDCOPSignal("kdesktop", "KScreensaverIface", "KDE_start_screensaver()", "setScreenSaverStarted()", false);
+ connectDCOPSignal("kdesktop", "KScreensaverIface", "KDE_stop_screensaver()", "setScreenSaverStopped()", false);
+}
+
+void KonvDCOP::raw(const QString& server,const QString& command)
+{
+ kdDebug() << "KonvDCOP::raw()" << endl;
+ // send raw IRC protocol data
+ emit dcopRaw(server,command);
+}
+
+QStringList KonvDCOP::listServers()
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ QStringList hosts;
+ QPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
+ Server* server;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ if (server) hosts << server->getServerName();
+
+ return hosts;
+}
+
+QStringList KonvDCOP::listConnectedServers()
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ QStringList connectedHosts;
+ QPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
+ Server* server;
+
+ for (server = serverList.first(); server; server = serverList.next())
+ if (server && server->isConnected()) connectedHosts << server->getServerName();
+
+ return connectedHosts;
+}
+
+void KonvDCOP::setAway(const QString& awaymessage)
+{
+ static_cast<KonversationApplication*>(kapp)->getAwayManager()->requestAllAway(awaymessage);
+}
+
+void KonvDCOP::setBack()
+{
+ static_cast<KonversationApplication*>(kapp)->getAwayManager()->requestAllUnaway();
+}
+
+void KonvDCOP::setScreenSaverStarted()
+{
+ static_cast<KonversationApplication*>(kapp)->getAwayManager()->setManagedIdentitiesAway();
+}
+
+void KonvDCOP::setScreenSaverStopped()
+{
+ static_cast<KonversationApplication*>(kapp)->getAwayManager()->setManagedIdentitiesUnaway();
+}
+
+void KonvDCOP::sayToAll(const QString &message)
+{
+ emit dcopMultiServerRaw("msg " + message);
+}
+
+void KonvDCOP::actionToAll(const QString &message)
+{
+ emit dcopMultiServerRaw("me " + message);
+}
+
+void KonvDCOP::say(const QString& _server,const QString& _target,const QString& _command)
+{
+ //Sadly, copy on write doesn't exist with QString::replace
+ QString server(_server), target(_target), command(_command);
+
+ // TODO: this just masks a greater problem - Server::addQuery will return a query for '' --argonel
+ // TODO: other DCOP calls need argument checking too --argonel
+ if (server.isEmpty() || target.isEmpty() || command.isEmpty())
+ kdDebug() << "KonvDCOP::say() requires 3 arguments." << endl;
+ else
+ {
+ command.replace('\n',"\\n");
+ command.replace('\r',"\\r");
+ target.remove('\n');
+ target.remove('\r');
+ server.remove('\n');
+ server.remove('\r');
+ // Act as if the user typed it
+ emit dcopSay(server,target,command);
+ }
+}
+
+void KonvDCOP::info(const QString& string)
+{
+ kdDebug() << "KonvDCOP::info()" << endl;
+ emit dcopInfo(string);
+}
+
+void KonvDCOP::debug(const QString& string)
+{
+ kdDebug() << "KonvDCOP::debug()" << endl;
+ emit dcopInfo(QString("Debug: %1").arg(string));
+}
+
+void KonvDCOP::error(const QString& string)
+{
+ kdDebug() << "KonvDCOP::error()" << endl;
+ emit dcopInfo(QString("Error: %1").arg(string));
+}
+
+void KonvDCOP::insertMarkerLine()
+{
+ emit dcopInsertMarkerLine();
+}
+
+void KonvDCOP::connectToServer(const QString& address, int port, const QString& channel, const QString& password)
+{
+ emit connectTo(Konversation::SilentlyReuseConnection, address, QString::number(port), password, "", channel);
+}
+
+QString KonvDCOP::getNickname(const QString& serverName)
+{
+ Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
+
+ if (!server)
+ {
+ error( i18n( "getNickname: Server %1 is not found." ).arg( serverName ) );
+ return QString();
+ }
+
+ return server->getNickname();
+}
+
+QString KonvDCOP::getAnyNickname()
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ Server* server = konvApp->getConnectionManager()->getAnyServer();
+
+ if (server) return server->getNickname();
+
+ return QString();
+}
+
+QString KonvDCOP::getChannelEncoding(const QString& server, const QString& channel)
+{
+ return Preferences::channelEncoding(server,channel);
+}
+
+// Identity stuff
+KonvIdentDCOP::KonvIdentDCOP()
+: DCOPObject("identity"),
+QObject(0, "identity")
+{
+}
+
+QStringList KonvIdentDCOP::listIdentities()
+{
+ QStringList identities;
+ IdentityList ids = Preferences::identityList();
+ for(IdentityList::ConstIterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ identities.append((*it)->getName());
+ }
+ return identities;
+}
+
+void KonvIdentDCOP::setrealName(const QString &id_name, const QString& name)
+{
+ IdentityList ids = Preferences::identityList();
+
+ for(IdentityList::iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ if ((*it)->getName() == id_name)
+ {
+ (*it)->setRealName(name);
+ return;
+ }
+ }
+
+}
+
+QString KonvIdentDCOP::getrealName(const QString &id_name)
+{
+ IdentityList ids = Preferences::identityList();
+
+ for(IdentityList::ConstIterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ if ((*it)->getName() == id_name)
+ {
+ return (*it)->getRealName();
+ }
+ }
+
+ return QString();
+}
+
+void KonvIdentDCOP::setIdent(const QString &/*identity*/, const QString& /*ident*/)
+{
+ //Preferences::identityByName(identity)->.setIdent(;
+}
+
+QString KonvIdentDCOP::getIdent(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getIdent();
+}
+
+void KonvIdentDCOP::setNickname(const QString &identity, int index,const QString& nick)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setNickname(index, nick);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getNickname(const QString &identity, int index)
+{
+ return Preferences::identityByName(identity)->getNickname(index);
+}
+
+void KonvIdentDCOP::setBot(const QString &identity, const QString& bot)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setBot(bot);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getBot(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getBot();
+}
+
+void KonvIdentDCOP::setPassword(const QString &identity, const QString& password)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setPassword(password);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getPassword(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getPassword();
+}
+
+void KonvIdentDCOP::setNicknameList(const QString &identity, const QStringList& newList)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setNicknameList(newList);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QStringList KonvIdentDCOP::getNicknameList(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getNicknameList();
+}
+
+void KonvIdentDCOP::setQuitReason(const QString &identity, const QString& reason)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setQuitReason(reason);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getQuitReason(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getQuitReason();
+}
+
+
+void KonvIdentDCOP::setPartReason(const QString &identity, const QString& reason)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setPartReason(reason);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getPartReason(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getPartReason();
+}
+
+void KonvIdentDCOP::setKickReason(const QString &identity, const QString& reason)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setKickReason(reason);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getKickReason(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getKickReason();
+}
+
+void KonvIdentDCOP::setShowAwayMessage(const QString &identity, bool state)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setShowAwayMessage(state);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+bool KonvIdentDCOP::getShowAwayMessage(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getShowAwayMessage();
+}
+
+void KonvIdentDCOP::setAwayMessage(const QString &identity, const QString& message)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setAwayMessage(message);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getAwayMessage(const QString &identity)
+{
+ const QString f = Preferences::identityByName(identity)->getAwayMessage();
+ return f;
+}
+
+void KonvIdentDCOP::setReturnMessage(const QString &identity, const QString& message)
+{
+ const Identity *i = Preferences::identityByName(identity);
+ const_cast<Identity *>(i)->setReturnMessage(message);
+ static_cast<KonversationApplication *>(kapp)->saveOptions(true);
+}
+
+QString KonvIdentDCOP::getReturnMessage(const QString &identity)
+{
+ return Preferences::identityByName(identity)->getReturnMessage();
+}
+
+#include "konvdcop.moc"
diff --git a/konversation/src/konvdcop.h b/konversation/src/konvdcop.h
new file mode 100644
index 0000000..3f513fb
--- /dev/null
+++ b/konversation/src/konvdcop.h
@@ -0,0 +1,110 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ The konversation DCOP interface class
+ begin: Mar 7 2003
+ copyright: (C) 2003 by Alex Zepeda
+ email: zipzippy@sonic.net
+*/
+
+#ifndef KONV_DCOP_H
+#define KONV_DCOP_H
+
+#include "konviface.h"
+#include "common.h"
+
+#include <qobject.h>
+#include <dcopobject.h>
+
+
+class KonvDCOP : public QObject, virtual public KonvIface
+{
+ Q_OBJECT
+
+ public:
+ KonvDCOP();
+
+ QString getNickname (const QString &server);
+ QString getAnyNickname ();
+ QString getChannelEncoding(const QString& server, const QString& channel);
+
+ signals:
+ void dcopSay(const QString& server,const QString& target,const QString& command);
+ void dcopInfo(const QString& string);
+ void dcopInsertMarkerLine();
+ void dcopRaw(const QString& server, const QString& command);
+ void dcopMultiServerRaw(const QString& command);
+
+ void connectTo(Konversation::ConnectionFlag flag,
+ const QString& hostName,
+ const QString& port = "",
+ const QString& password = "",
+ const QString& nick = "",
+ const QString& channel = "",
+ bool useSSL = false);
+
+ public slots:
+ void setAway(const QString &awaymessage);
+ void setBack();
+ void sayToAll(const QString &message);
+ void actionToAll(const QString &message);
+ void raw(const QString& server,const QString& command);
+ void say(const QString& server,const QString& target,const QString& command);
+ void info(const QString& string);
+ void debug(const QString& string);
+ void error(const QString& string);
+ void insertMarkerLine();
+ void connectToServer(const QString& adress, int port, const QString& channel, const QString& password);
+ QStringList listServers();
+ QStringList listConnectedServers();
+
+ void setScreenSaverStarted();
+ void setScreenSaverStopped();
+};
+
+class KonvIdentDCOP : public QObject, virtual public KonvIdentityIface
+{
+ Q_OBJECT
+
+ public:
+ KonvIdentDCOP();
+
+ void setrealName(const QString &identity, const QString& name);
+ QString getrealName(const QString &identity);
+ void setIdent(const QString &identity, const QString& ident);
+ QString getIdent(const QString &identity);
+
+ void setNickname(const QString &identity, int index,const QString& nick);
+ QString getNickname(const QString &identity, int index);
+
+ void setBot(const QString &identity, const QString& bot);
+ QString getBot(const QString &identity);
+ void setPassword(const QString &identity, const QString& password);
+ QString getPassword(const QString &identity);
+
+ void setNicknameList(const QString &identity, const QStringList& newList);
+ QStringList getNicknameList(const QString &identity);
+
+ void setQuitReason(const QString &identity, const QString& reason);
+ QString getQuitReason(const QString &identity);
+ void setPartReason(const QString &identity, const QString& reason);
+ QString getPartReason(const QString &identity);
+ void setKickReason(const QString &identity, const QString& reason);
+ QString getKickReason(const QString &identity);
+
+ void setShowAwayMessage(const QString &identity, bool state);
+ bool getShowAwayMessage(const QString &identity);
+
+ void setAwayMessage(const QString &identity, const QString& message);
+ QString getAwayMessage(const QString &identity);
+ void setReturnMessage(const QString &identity, const QString& message);
+ QString getReturnMessage(const QString &identity);
+
+ QStringList listIdentities();
+};
+#endif
diff --git a/konversation/src/konversation-0.19-appearance.pl b/konversation/src/konversation-0.19-appearance.pl
new file mode 100755
index 0000000..ba440ac
--- /dev/null
+++ b/konversation/src/konversation-0.19-appearance.pl
@@ -0,0 +1,52 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $currentGroup = "";
+my $key;
+my $value;
+my $i;
+my $out;
+my @irccolors;
+my @nickcolors;
+
+while (<>)
+{
+ chomp;
+ if ( /^\[/ )
+ {
+ $currentGroup = $_;
+ next;
+ }
+ ($key, $value) = ($_ =~ /([^=]+)=[ \t]*([^\n]+)/);
+ if ($_ =~ /IRCColors/)
+ {
+ print("# DELETE $currentGroup$key\n");
+ if ($value ne "#ffffff,#000000,#000080,#008000,#ff0000,#a52a2a,#800080,#ff8000,#808000,#00ff00,#008080,#00ffff,#0000ff,#ffc0cb,#a0a0a0,#c0c0c0")
+ {
+ $i = 0;
+ @irccolors = split(/,/, $value);
+ foreach $out (@irccolors)
+ {
+ $out =~ /([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/;
+ printf("IrcColorCode$i=%d,%d,%d\n",hex($1),hex($2),hex($3));
+ $i++;
+ }
+ }
+ }
+ elsif ($_ =~ /NickColors/)
+ {
+ print("# DELETE $currentGroup$key\n");
+ if ($value ne "#E90E7F,#8E55E9,#B30E0E,#18B33C,#58ADB3,#9E54B3,#0FB39A,#3176B3,#000000")
+ {
+ $i = 0;
+ @nickcolors = split(/,/, $value);
+ foreach $out (@nickcolors)
+ {
+ $out =~ /([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/;
+ printf("NickColor$i=%d,%d,%d\n",hex($1),hex($2),hex($3));
+ $i++;
+ }
+ }
+ }
+}
diff --git a/konversation/src/konversation-0.19-colorcodes.pl b/konversation/src/konversation-0.19-colorcodes.pl
new file mode 100644
index 0000000..5016c79
--- /dev/null
+++ b/konversation/src/konversation-0.19-colorcodes.pl
@@ -0,0 +1,26 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $currentGroup = "";
+my $key;
+my $value;
+
+while (<>)
+{
+ chomp;
+ if ( /^\[/ )
+ {
+ $currentGroup = $_;
+ next;
+ }
+ ($key, $value) = ($_ =~ /([^=]+)=[ \t]*([^\n]+)/);
+ if ($_ =~ /FilterColorCodes/)
+ {
+ print("# DELETE $currentGroup$key\n");
+ if ($value eq "true")
+ {
+ print("AllowColorCodes=false\n");
+ }
+ }
+}
diff --git a/konversation/src/konversation-0.19-colors.pl b/konversation/src/konversation-0.19-colors.pl
new file mode 100755
index 0000000..34a4918
--- /dev/null
+++ b/konversation/src/konversation-0.19-colors.pl
@@ -0,0 +1,23 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $currentGroup = "";
+my $key;
+my $value;
+
+while (<>)
+{
+ chomp;
+ if ( /^\[/ )
+ {
+ $currentGroup = $_;
+ next;
+ }
+ ($key, $value) = ($_ =~ /([^=]+)=[ \t]*([^\n]+)/);
+ if ($value =~ /([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/)
+ {
+ print("# DELETE $currentGroup$key\n");
+ printf("$key=%d,%d,%d\n",hex($1),hex($2),hex($3));
+ }
+}
diff --git a/konversation/src/konversation-0.19-custombrowser.pl b/konversation/src/konversation-0.19-custombrowser.pl
new file mode 100644
index 0000000..f63d0f2
--- /dev/null
+++ b/konversation/src/konversation-0.19-custombrowser.pl
@@ -0,0 +1,26 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $currentGroup = "";
+my $key;
+my $value;
+
+while (<>)
+{
+ chomp;
+ if ( /^\[/ )
+ {
+ $currentGroup = $_;
+ next;
+ }
+ ($key, $value) = ($_ =~ /([^=]+)=[ \t]*([^\n]+)/);
+ if ($_ =~ /UseKdeDefault/)
+ {
+ print("# DELETE $currentGroup$key\n");
+ if ($value eq "false")
+ {
+ print("UseCustomBrowser=true\n");
+ }
+ }
+}
diff --git a/konversation/src/konversation-0.19-notifylists.pl b/konversation/src/konversation-0.19-notifylists.pl
new file mode 100644
index 0000000..42bfe9d
--- /dev/null
+++ b/konversation/src/konversation-0.19-notifylists.pl
@@ -0,0 +1,43 @@
+#! /usr/bin/perl
+
+use strict;
+
+my($out);
+my($group);
+my($gotgroup);
+my(%group2groupno);
+my(%key2value);
+my(@split);
+my(%saw);
+
+while (<>)
+{
+ if ($_ =~ /^\[ServerGroup ([0-9]+)\]/)
+ {
+ $group = $1;
+ $gotgroup = 1;
+ }
+ elsif ($_ =~ /^Name=(.+)/ && $gotgroup)
+ {
+ $group2groupno{$group} = $1;
+ $gotgroup = 0;
+ }
+ elsif ($_ =~ /^(.+)=(.+)/)
+ {
+ $key2value{$1} = $2;
+ }
+}
+
+foreach $out (keys %group2groupno)
+{
+ @split = split(" ",$key2value{$group2groupno{$out}});
+
+ if (@split)
+ {
+ undef %saw;
+ @saw{@split} = ();
+ @split = keys %saw;
+
+ print "[ServerGroup $out]\nNotifyList=@split\n";
+ }
+}
diff --git a/konversation/src/konversation-0.19-sortorder.pl b/konversation/src/konversation-0.19-sortorder.pl
new file mode 100755
index 0000000..6777a91
--- /dev/null
+++ b/konversation/src/konversation-0.19-sortorder.pl
@@ -0,0 +1,69 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $currentGroup = "";
+my $name;
+my $value;
+my $out;
+my %list;
+
+while (<>)
+{
+ chomp;
+ if ( /^\[/ )
+ {
+ $currentGroup = $_;
+ next;
+ }
+ elsif ($_ =~ /AdminValue/)
+ {
+ ($name,$value) = split("=",$_);
+ $list{"$value"} = "p";
+ print("# DELETE $currentGroup$name\n");
+ }
+ elsif ($_ =~ /HalfopValue/)
+ {
+ ($name,$value) = split("=",$_);
+ $list{"$value"} = "h";
+ print("# DELETE $currentGroup$name\n");
+ }
+ elsif ($_ =~ /NoRightsValue/)
+ {
+ ($name,$value) = split("=",$_);
+ $list{"$value"} = "-";
+ print("# DELETE $currentGroup$name\n");
+ }
+ elsif ($_ =~ /OperatorValue/)
+ {
+ ($name,$value) = split("=",$_);
+ $list{"$value"} = "o";
+ print("# DELETE $currentGroup$name\n");
+ }
+ elsif ($_ =~ /OwnerValue/)
+ {
+ ($name,$value) = split("=",$_);
+ $list{"$value"} = "q";
+ print("# DELETE $currentGroup$name\n");
+ }
+ elsif ($_ =~ /VoiceValue/)
+ {
+ ($name,$value) = split("=",$_);
+ $list{"$value"} = "v";
+ print("# DELETE $currentGroup$name\n");
+ }
+ elsif ($_ =~ /AwayValue/)
+ {
+ ($name,$value) = split("=",$_);
+ print("# DELETE $currentGroup$name\n");
+ }
+}
+
+print "SortOrder=";
+
+foreach $out (reverse sort { $a <=> $b } keys %list)
+{
+ print $list{$out};
+}
+
+print "\n"; \ No newline at end of file
diff --git a/konversation/src/konversation-0.19-tabplacement.pl b/konversation/src/konversation-0.19-tabplacement.pl
new file mode 100755
index 0000000..91d9570
--- /dev/null
+++ b/konversation/src/konversation-0.19-tabplacement.pl
@@ -0,0 +1,26 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $currentGroup = "";
+my $key;
+my $value;
+
+while (<>)
+{
+ chomp;
+ if ( /^\[/ )
+ {
+ $currentGroup = $_;
+ next;
+ }
+ ($key, $value) = ($_ =~ /([^=]+)=[ \t]*([^\n]+)/);
+ if ($_ =~ /TabPlacement/)
+ {
+ if ($value eq "0")
+ {
+ print("# DELETE $currentGroup$key\n");
+ print("$key=Top\n");
+ }
+ }
+}
diff --git a/konversation/src/konversation-0.20-customfonts.pl b/konversation/src/konversation-0.20-customfonts.pl
new file mode 100644
index 0000000..21f3ae0
--- /dev/null
+++ b/konversation/src/konversation-0.20-customfonts.pl
@@ -0,0 +1,21 @@
+#! /usr/bin/perl
+
+use strict;
+
+
+while (<>)
+{
+ chomp;
+ if ($_ =~ /^TextFont/)
+ {
+ print("CustomTextFont=true\n");
+ }
+ if ($_ =~ /^ListFont/)
+ {
+ print("CustomListFont=true\n");
+ }
+ if ($_ =~ /^TabFont/)
+ {
+ print("CustomTabFont=true\n");
+ }
+}
diff --git a/konversation/src/konversation-0.20-quickbuttons.pl b/konversation/src/konversation-0.20-quickbuttons.pl
new file mode 100644
index 0000000..c5b49bc
--- /dev/null
+++ b/konversation/src/konversation-0.20-quickbuttons.pl
@@ -0,0 +1,137 @@
+#! /usr/bin/perl
+
+use strict;
+
+my $button0 = "";
+my $button1 = "";
+my $button2 = "";
+my $button3 = "";
+my $button4 = "";
+my $button5 = "";
+my $button6 = "";
+my $button7 = "";
+
+while (<>)
+{
+ chomp;
+ if ($_ =~ /^Button0/)
+ {
+ $button0 = $_;
+ }
+ if ($_ =~ /^Button1/)
+ {
+ $button1 = $_;
+ }
+ if ($_ =~ /^Button2/)
+ {
+ $button2 = $_;
+ }
+ if ($_ =~ /^Button3/)
+ {
+ $button3 = $_;
+ }
+ if ($_ =~ /^Button4/)
+ {
+ $button4 = $_;
+ }
+ if ($_ =~ /^Button5/)
+ {
+ $button5 = $_;
+ }
+ if ($_ =~ /^Button6/)
+ {
+ $button6 = $_;
+ }
+ if ($_ =~ /^Button7/)
+ {
+ $button7 = $_;
+ }
+}
+
+if ($button0 ne "")
+{
+ print("# DELETE [Button List]Button0\n");
+ print("$button0\n");
+}
+else
+{
+ print("# DELETE [Button List]Button0\n");
+ print("Button0=Op,/OP %u%n\n");
+}
+
+if ($button1 ne "")
+{
+ print("# DELETE [Button List]Button1\n");
+ print("$button1\n");
+}
+else
+{
+ print("# DELETE [Button List]Button1\n");
+ print("Button1=DeOp,/DEOP %u%n\n");
+}
+
+if ($button2 ne "")
+{
+ print("# DELETE [Button List]Button2\n");
+ print("$button2\n");
+}
+else
+{
+ print("# DELETE [Button List]Button2\n");
+ print("Button2=WhoIs,/WHOIS %s,%%u%n\n");
+}
+
+if ($button3 ne "")
+{
+ print("# DELETE [Button List]Button3\n");
+ print("$button3\n");
+}
+else
+{
+ print("# DELETE [Button List]Button3\n");
+ print("Button3=Version,/CTCP %s,%%u VERSION%n\n");
+}
+
+if ($button4 ne "")
+{
+ print("# DELETE [Button List]Button4\n");
+ print("$button4\n");
+}
+else
+{
+ print("# DELETE [Button List]Button4\n");
+ print("Button4=Kick,/KICK %u%n\n");
+}
+
+if ($button5 ne "")
+{
+ print("# DELETE [Button List]Button5\n");
+ print("$button5\n");
+}
+else
+{
+ print("# DELETE [Button List]Button5\n");
+ print("Button5=Ban,/BAN %u%n\n");
+}
+
+if ($button6 ne "")
+{
+ print("# DELETE [Button List]Button6\n");
+ print("$button6\n");
+}
+else
+{
+ print("# DELETE [Button List]Button6\n");
+ print("Button6=Part,/PART %c Leaving...%n\n");
+}
+
+if ($button7 ne "")
+{
+ print("# DELETE [Button List]Button7\n");
+ print("$button7\n");
+}
+else
+{
+ print("# DELETE [Button List]Button7\n");
+ print("Button7=Quit,/QUIT Leaving...%n\n");
+} \ No newline at end of file
diff --git a/konversation/src/konversation.desktop b/konversation/src/konversation.desktop
new file mode 100644
index 0000000..110292b
--- /dev/null
+++ b/konversation/src/konversation.desktop
@@ -0,0 +1,47 @@
+[Desktop Entry]
+Type=Application
+Exec=konversation -caption "%c" %i
+Icon=konversation
+X-DocPath=konversation/index.html
+GenericName=IRC Client
+GenericName[ar]=زبون IRC
+GenericName[bg]=IRC клиент
+GenericName[br]=Kliant IRC
+GenericName[ca]=Client IRC
+GenericName[cs]=IRC klient
+GenericName[cy]=Dibynnydd IRC
+GenericName[da]=IRC-klient
+GenericName[de]=IRC-Programm
+GenericName[el]=Πελάτης IRC
+GenericName[es]=Cliente de IRC
+GenericName[et]=IRC klient
+GenericName[ga]=Cliant IRC
+GenericName[gl]=Cliente IRC
+GenericName[he]=לקוח IRC
+GenericName[it]=Client IRC
+GenericName[ja]=IRC クライアント
+GenericName[ka]=IRC საუბარი
+GenericName[lt]=IRC klientas
+GenericName[ms]=Klien IRC
+GenericName[nl]=IRC-client
+GenericName[pa]=IRC ਕਲਾਂਇਟ
+GenericName[pt]=Cliente de IRC
+GenericName[pt_BR]=Cliente IRC
+GenericName[sr]=IRC клијент
+GenericName[sr@Latn]=IRC klijent
+GenericName[sv]=IRC-klient
+GenericName[tr]=IRC İstemcisi
+GenericName[uk]=Клієнт IRC
+GenericName[xx]=xxIRC Clientxx
+GenericName[zh_CN]=IRC 客户端
+GenericName[zh_TW]=IRC 客戶端程式
+Terminal=false
+Name=Konversation
+Name[hi]=कनवर्सेसन
+Name[pa]=ਗੱਲਬਾਤ
+Name[ta]=உரையாடல்
+Name[xx]=xxKonversationxx
+Categories=Qt;KDE;Network;IRCClient;
+ServiceTypes=DCOP/InstantMessenger;DCOP/Unique
+X-DCOP-ServiceName=konversation
+X-DCOP-ServiceType=Unique
diff --git a/konversation/src/konversation.upd b/konversation/src/konversation.upd
new file mode 100644
index 0000000..0909314
--- /dev/null
+++ b/konversation/src/konversation.upd
@@ -0,0 +1,144 @@
+Id=1.1-rememberline
+File=konversationrc
+Group=General Options
+Key=ShowRememberLineInAllWindows,MarkerLineInAllViews
+RemoveKey=ShowRememberLineInAllWindows
+
+Id=1.1-markerlines
+File=konversationrc
+Group=Appearance,General Options
+Key=AutoInsertRememberLineAfterMinimizing,AutomaticRememberLine
+RemoveKey=AutoInsertRememberLineAfterMinimizing
+
+Id=0.20-customfonts
+File=konversationrc
+Group=Appearance
+Script=konversation-0.20-customfonts.pl,perl
+
+Id=0.20-quickbuttons
+File=konversationrc
+Group=Button List
+Script=konversation-0.20-quickbuttons.pl,perl
+
+Id=0.19-notifylists
+File=konversationrc
+Script=konversation-0.19-notifylists.pl,perl
+RemoveGroup=Notify Group Lists
+
+Id=0.19-appearance
+File=konversationrc
+Group=Appearance
+Key=CloseButtonsOnTabs,CloseButtons
+RemoveKey=CloseButtonsOnTabs
+RemoveKey=QuickButtons
+RemoveKey=Codec
+RemoveKey=FilterColorCodes
+Script=konversation-0.19-appearance.pl,perl
+Script=konversation-0.19-colorcodes.pl,perl
+
+Id=0.19-browser
+File=konversationrc
+Group=Web Browser Settings
+Script=konversation-0.19-custombrowser.pl,perl
+
+Id=0.19-flags
+File=konversationrc
+Group=Flags
+RemoveKey=BlinkingTabs
+Script=konversation-0.19-tabplacement.pl,perl
+
+Id=0.19-highlightcolors
+File=konversationrc
+Group=Highlight List
+Script=konversation-0.19-colors.pl,perl
+
+# Convert OSD color storage format
+Id=0.19-osdcolors
+File=konversationrc
+Group=OSD
+Script=konversation-0.19-colors.pl,perl
+
+# Convert nick list sort order storage format
+Id=0.19-sortorder
+File=konversationrc
+Group=Sort Nicknames
+Script=konversation-0.19-sortorder.pl,perl
+
+# Convert color storage format
+Id=0.19-colors
+File=konversationrc
+Group=Message Text Colors
+Script=konversation-0.19-colors.pl,perl
+
+Id=0.19-generaloptions
+File=konversationrc
+Group=General Options
+Key=RedirectServerAndAppMsgToStatusPane,AndAppMsgToStatusPane
+Key=ClickableNicks,UseClickableNicks
+RemoveKey=RedirectServerAndAppMsgToStatusPane
+RemoveKey=ClickableNicks
+RemoveKey=ColorConfigurationGeometry
+RemoveKey=ButtonGeometry
+RemoveKey=Geometry
+RemoveKey=HilightGeometry
+RemoveKey=IgnoreGeometry
+RemoveKey=KickReason
+RemoveKey=LogfileGeometry
+RemoveKey=PartReason
+RemoveKey=ServerWindowStatusBarStatus
+RemoveKey=ServerWindowToolBarIconSize
+RemoveKey=ServerWindowToolBarIconText
+RemoveKey=ServerWindowToolBarPos
+RemoveKey=ServerWindowToolBarStatus
+RemoveKey=ToolBarPos
+RemoveKey=VersionReply
+RemoveKey=MaximumLag
+
+Id=0.19-cleanup-themes
+File=konversationrc
+Group=Themes
+RemoveKey=IconThemeIndex
+RemoveKey=IconThemeName
+
+Id=0.19-cleanup-notifylist
+File=konversationrc
+Group=Notify List
+RemoveKey=NotifyList
+
+Id=0.19-cleanup-dcc
+File=konversationrc
+Group=DCC Settings
+RemoveKey=GetIpFromServer
+RemoveKey=Rollback
+
+Id=0.19-cleanup-colors
+File=konversationrc
+Group=Colors
+RemoveGroup=Colors
+
+Id=0.19-cleanup-hilightlist
+File=konversationrc
+Group=Hilight List
+RemoveGroup=Hilight List
+
+Id=0.19-cleanup-notificationmessages
+File=konversationrc
+Group=Notification Messages
+RemoveGroup=Notification Messages
+
+Id=0.19-cleanup-ledcolors
+File=konversationrc
+Group=Led Colors
+RemoveGroup=Led Colors
+
+Id=0.19-cleanup-kmditaskbar
+File=konversationrc
+Group=MainWindow Toolbar KMdiTaskBar
+RemoveGroup=MainWindow Toolbar KMdiTaskBar
+
+# Rename ChannelSplitter
+Id=0.19-channelsplitter
+File=konversationrc
+Group=Appearance
+Key=ChannelSplitter,ChannelSplitterSizes
+RemoveKey=ChannelSplitter
diff --git a/konversation/src/konversationapplication.cpp b/konversation/src/konversationapplication.cpp
new file mode 100644
index 0000000..fd0afa7
--- /dev/null
+++ b/konversation/src/konversationapplication.cpp
@@ -0,0 +1,888 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "connectionmanager.h"
+#include "awaymanager.h"
+#include "dcctransfermanager.h"
+#include "viewcontainer.h"
+#include "highlight.h"
+#include "server.h"
+#include "konversationsound.h"
+#include "quickconnectdialog.h"
+#include "servergroupsettings.h"
+#include "serversettings.h"
+#include "channel.h"
+#include "images.h"
+#include "notificationhandler.h"
+#include "commit.h"
+#include "version.h"
+
+#include <qtextcodec.h>
+#include <qregexp.h>
+#include <qfileinfo.h>
+
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <dcopclient.h>
+#include <kdeversion.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+
+
+KonversationApplication::KonversationApplication()
+: KUniqueApplication(true, true, true)
+{
+ mainWindow = 0;
+ m_connectionManager = 0;
+ m_awayManager = 0;
+ quickConnectDialog = 0;
+ osd = 0;
+}
+
+KonversationApplication::~KonversationApplication()
+{
+ kdDebug() << k_funcinfo << endl;
+ Server::_stashRates();
+ Preferences::writeConfig();
+ saveOptions(false);
+
+ delete m_images;
+ delete dcopObject;
+ //delete prefsDCOP;
+ delete identDCOP;
+ delete osd;
+ osd = 0;
+}
+
+int KonversationApplication::newInstance()
+{
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+ QCString url;
+ if (args->count() > 0) url = args->arg(0);
+
+ if (!mainWindow)
+ {
+ connect(this, SIGNAL(shutDown()), this, SLOT(prepareShutdown()));
+
+ m_connectionManager = new ConnectionManager(this);
+
+ m_awayManager = new AwayManager(this);
+
+ connect(m_connectionManager, SIGNAL(identityOnline(int)), m_awayManager, SLOT(identityOnline(int)));
+ connect(m_connectionManager, SIGNAL(identityOffline(int)), m_awayManager, SLOT(identityOffline(int)));
+ connect(m_connectionManager, SIGNAL(identityOffline(int)), m_awayManager, SLOT(identityOffline(int)));
+ connect(m_connectionManager, SIGNAL(connectionChangedAwayState(bool)), m_awayManager, SLOT(updateGlobalAwayAction(bool)));
+
+ // an instance of DccTransferManager needs to be created before GUI class instances' creation.
+ m_dccTransferManager = new DccTransferManager(this);
+
+ // make sure all vars are initialized properly
+ quickConnectDialog = 0;
+
+ // Sound object used to play sound...
+ m_sound = new Konversation::Sound(this);
+
+ // initialize OSD display here, so we can read the Preferences::properly
+ osd = new OSDWidget( "Konversation" );
+
+ Preferences::self();
+ readOptions();
+
+ // Images object providing LEDs, NickIcons
+ m_images = new Images();
+ connect(this, SIGNAL(iconChanged(int)), m_images, SLOT(updateIcons()));
+
+ // Auto-alias scripts. This adds any missing aliases
+ QStringList aliasList(Preferences::aliasList());
+ QStringList scripts(Preferences::defaultAliasList());
+ bool changed = false;
+ for ( QStringList::ConstIterator it = scripts.begin(); it != scripts.end(); ++it )
+ {
+ if(!aliasList.contains(*it)) {
+ changed = true;
+ aliasList.append(*it);
+ }
+ }
+ if(changed)
+ Preferences::setAliasList(aliasList);
+
+ // Setup system codec
+ // TODO: check if this works now as intended
+ // QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
+
+ // open main window
+ mainWindow = new KonversationMainWindow();
+ setMainWidget(mainWindow);
+
+ connect(mainWindow, SIGNAL(showQuickConnectDialog()), this, SLOT(openQuickConnectDialog()) );
+ connect(Preferences::self(), SIGNAL(updateTrayIcon()), mainWindow, SLOT(updateTrayIcon()) );
+ connect(osd, SIGNAL(hidden()), mainWindow, SIGNAL(endNotification()));
+ // take care of user style changes, setting back colors and stuff
+
+ // apply GUI settings
+ emit appearanceChanged();
+
+ if (Preferences::showTrayIcon() && (Preferences::hiddenToTray() || Preferences::hideToTrayOnStartup()))
+ mainWindow->hide();
+ else
+ mainWindow->show();
+
+ bool openServerList = Preferences::showServerList();
+
+ // handle autoconnect on startup
+ Konversation::ServerGroupList serverGroups = Preferences::serverGroupList();
+
+ if (url.isEmpty() && !args->isSet("server"))
+ {
+ for (Konversation::ServerGroupList::iterator it = serverGroups.begin(); it != serverGroups.end(); ++it)
+ {
+ if ((*it)->autoConnectEnabled())
+ {
+ openServerList = false;
+ m_connectionManager->connectTo(Konversation::CreateNewConnection, (*it)->id());
+ }
+ }
+ }
+
+ if (openServerList) mainWindow->openServerList();
+
+ connect(this, SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)), this, SLOT(saveOptions()));
+
+ // prepare dcop interface
+ dcopObject = new KonvDCOP;
+ kapp->dcopClient()->setDefaultObject(dcopObject->objId());
+ identDCOP = new KonvIdentDCOP;
+
+ if (dcopObject)
+ {
+ connect(dcopObject,SIGNAL (dcopMultiServerRaw(const QString&)),
+ this,SLOT (dcopMultiServerRaw(const QString&)) );
+ connect(dcopObject,SIGNAL (dcopRaw(const QString&,const QString&)),
+ this,SLOT (dcopRaw(const QString&,const QString&)) );
+ connect(dcopObject,SIGNAL (dcopSay(const QString&,const QString&,const QString&)),
+ this,SLOT (dcopSay(const QString&,const QString&,const QString&)) );
+ connect(dcopObject,SIGNAL (dcopInfo(const QString&)),
+ this,SLOT (dcopInfo(const QString&)) );
+ connect(dcopObject,SIGNAL (dcopInsertMarkerLine()),
+ mainWindow,SIGNAL(insertMarkerLine()));
+ connect(dcopObject, SIGNAL(connectTo(Konversation::ConnectionFlag, const QString&, const QString&, const QString&, const QString&, const QString&, bool)),
+ m_connectionManager, SLOT(connectTo(Konversation::ConnectionFlag, const QString&, const QString&, const QString&, const QString&, const QString&, bool)));
+ }
+
+ m_notificationHandler = new Konversation::NotificationHandler(this);
+ }
+
+ if (!url.isEmpty())
+ getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, url);
+ else if (args->isSet("server"))
+ {
+ getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection,
+ args->getOption("server"),
+ args->getOption("port"),
+ args->getOption("password"),
+ args->getOption("nick"),
+ args->getOption("channel"),
+ args->isSet("ssl"));
+ }
+
+ return KUniqueApplication::newInstance();
+}
+
+KonversationApplication* KonversationApplication::instance()
+{
+ return static_cast<KonversationApplication*>(KApplication::kApplication());
+}
+
+void KonversationApplication::prepareShutdown()
+{
+ if (mainWindow->isHidden() && Preferences::showTrayIcon())
+ Preferences::setHiddenToTray(true);
+ else
+ Preferences::setHiddenToTray(false);
+
+ mainWindow->getViewContainer()->prepareShutdown();
+
+ m_awayManager->blockSignals(true);
+ delete m_awayManager;
+
+ m_connectionManager->quitServers();
+ m_connectionManager->blockSignals(true);
+ delete m_connectionManager;
+}
+
+void KonversationApplication::showQueueTuner(bool p)
+{
+ getMainWindow()->getViewContainer()->showQueueTuner(p);
+}
+
+void KonversationApplication::dcopMultiServerRaw(const QString &command)
+{
+ sendMultiServerCommand(command.section(' ', 0,0), command.section(' ', 1));
+}
+
+void KonversationApplication::dcopRaw(const QString& connection, const QString &command)
+{
+ Server* server = 0;
+
+ bool conversion = false;
+ int connectionId = connection.toInt(&conversion);
+
+ if (conversion) server = getConnectionManager()->getServerByConnectionId(connectionId);
+
+ if (!server) server = getConnectionManager()->getServerByName(connection);
+
+ if (server) server->dcopRaw(command);
+}
+
+
+void KonversationApplication::dcopSay(const QString& connection, const QString& target, const QString& command)
+{
+ Server* server = 0;
+
+ bool conversion = false;
+ int connectionId = connection.toInt(&conversion);
+
+ if (conversion) server = getConnectionManager()->getServerByConnectionId(connectionId);
+
+ if (!server) server = getConnectionManager()->getServerByName(connection);
+
+ if (server) server->dcopSay(target, command);
+}
+
+void KonversationApplication::dcopInfo(const QString& string)
+{
+ mainWindow->getViewContainer()->appendToFrontmost(i18n("DCOP"), string, 0);
+}
+
+void KonversationApplication::readOptions()
+{
+ // get standard config file
+ KConfig* config=kapp->config();
+
+ // read nickname sorting order for channel nick lists
+ config->setGroup("Sort Nicknames");
+ QString sortOrder=config->readEntry("SortOrder");
+ QStringList sortOrderList=QStringList::split("",sortOrder);
+ sortOrderList.sort();
+ if (sortOrderList.join("")!="-hopqv")
+ {
+ sortOrder=Preferences::defaultNicknameSortingOrder();
+ Preferences::setSortOrder(sortOrder);
+ }
+
+ // Identity list
+ QStringList identityList=config->groupList().grep(QRegExp("Identity [0-9]+"));
+ if(!identityList.isEmpty())
+ {
+ Preferences::clearIdentityList();
+
+ for(unsigned int index=0;index<identityList.count();index++)
+ {
+ IdentityPtr newIdentity=new Identity();
+
+ config->setGroup(identityList[index]);
+
+ newIdentity->setName(config->readEntry("Name"));
+
+ newIdentity->setIdent(config->readEntry("Ident"));
+ newIdentity->setRealName(config->readEntry("Realname"));
+
+ newIdentity->setNicknameList(config->readListEntry("Nicknames"));
+
+ newIdentity->setBot(config->readEntry("Bot"));
+ newIdentity->setPassword(config->readEntry("Password"));
+
+ newIdentity->setInsertRememberLineOnAway(config->readBoolEntry("InsertRememberLineOnAway"));
+ newIdentity->setShowAwayMessage(config->readBoolEntry("ShowAwayMessage"));
+ newIdentity->setAwayMessage(config->readEntry("AwayMessage"));
+ newIdentity->setReturnMessage(config->readEntry("ReturnMessage"));
+ newIdentity->setAutomaticAway(config->readBoolEntry("AutomaticAway", false));
+ newIdentity->setAwayInactivity(config->readNumEntry("AwayInactivity", 10));
+ newIdentity->setAutomaticUnaway(config->readBoolEntry("AutomaticUnaway", false));
+
+ newIdentity->setQuitReason(config->readEntry("QuitReason"));
+ newIdentity->setPartReason(config->readEntry("PartReason"));
+ newIdentity->setKickReason(config->readEntry("KickReason"));
+
+ newIdentity->setShellCommand(config->readEntry("PreShellCommand"));
+
+ newIdentity->setCodecName(config->readEntry("Codec"));
+
+ newIdentity->setAwayNick(config->readEntry("AwayNick"));
+
+ Preferences::addIdentity(newIdentity);
+
+ }
+
+ }
+
+ osd->setEnabled(Preferences::useOSD());
+
+ //How to load the font from the text?
+ osd->setFont(Preferences::oSDFont());
+
+ osd->setDuration(Preferences::oSDDuration());
+ osd->setScreen(Preferences::oSDScreen());
+ osd->setShadow(Preferences::oSDDrawShadow());
+
+ osd->setOffset(Preferences::oSDOffsetX(), Preferences::oSDOffsetY());
+ osd->setAlignment((OSDWidget::Alignment)Preferences::oSDAlignment());
+
+ if(Preferences::oSDUseCustomColors())
+ {
+ osd->setTextColor(Preferences::oSDTextColor());
+ osd->setBackgroundColor(Preferences::oSDBackgroundColor());
+ }
+
+ // Check if there is old server list config
+ config->setGroup("Server List");
+
+ // Read the new server settings
+ QStringList groups = config->groupList().grep(QRegExp("ServerGroup [0-9]+"));
+ QMap<int,QStringList> notifyList;
+
+ if(!groups.isEmpty())
+ {
+ Konversation::ServerGroupList serverGroups;
+ QStringList::iterator it;
+ QStringList tmp1;
+ QStringList::iterator it2;
+ Konversation::ChannelList channelHistory;
+ Konversation::ServerSettings server;
+ Konversation::ChannelSettings channel;
+
+ for(it = groups.begin(); it != groups.end(); ++it)
+ {
+ config->setGroup((*it));
+ Konversation::ServerGroupSettingsPtr serverGroup = new Konversation::ServerGroupSettings;
+ serverGroup->setName(config->readEntry("Name"));
+ serverGroup->setIdentityId(Preferences::identityByName(config->readEntry("Identity"))->id());
+ serverGroup->setConnectCommands(config->readEntry("ConnectCommands"));
+ serverGroup->setAutoConnectEnabled(config->readBoolEntry("AutoConnect"));
+ serverGroup->setNotificationsEnabled(config->readBoolEntry("EnableNotifications", true));
+ serverGroup->setExpanded(config->readBoolEntry("Expanded", false));
+
+ notifyList.insert((*serverGroup).id(),QStringList::split(' ',config->readEntry("NotifyList")));
+
+ tmp1 = config->readListEntry("ServerList");
+ for(it2 = tmp1.begin(); it2 != tmp1.end(); ++it2)
+ {
+ config->setGroup((*it2));
+ server.setHost(config->readEntry("Server"));
+ server.setPort(config->readNumEntry("Port"));
+ server.setPassword(config->readEntry("Password"));
+ server.setSSLEnabled(config->readBoolEntry("SSLEnabled"));
+ serverGroup->addServer(server);
+ }
+
+ config->setGroup((*it));
+ tmp1 = config->readListEntry("AutoJoinChannels");
+
+ for(it2 = tmp1.begin(); it2 != tmp1.end(); ++it2)
+ {
+ config->setGroup((*it2));
+
+ if(!config->readEntry("Name").isEmpty())
+ {
+ channel.setName(config->readEntry("Name"));
+ channel.setPassword(config->readEntry("Password"));
+ serverGroup->addChannel(channel);
+ }
+ }
+
+ config->setGroup((*it));
+ tmp1 = config->readListEntry("ChannelHistory");
+ channelHistory.clear();
+
+ for(it2 = tmp1.begin(); it2 != tmp1.end(); ++it2)
+ {
+ config->setGroup((*it2));
+
+ if(!config->readEntry("Name").isEmpty())
+ {
+ channel.setName(config->readEntry("Name"));
+ channel.setPassword(config->readEntry("Password"));
+ channel.setNotificationsEnabled(config->readBoolEntry("EnableNotifications", true));
+ channelHistory.append(channel);
+ }
+ }
+
+ serverGroup->setChannelHistory(channelHistory);
+
+ serverGroups.append(serverGroup);
+ }
+
+ Preferences::setServerGroupList(serverGroups);
+ }
+
+ // Notify Settings and lists. Must follow Server List.
+ Preferences::setNotifyList(notifyList);
+ Preferences::setNotifyDelay(Preferences::notifyDelay());
+ Preferences::setUseNotify(Preferences::useNotify());
+
+ // Quick Buttons List
+
+ // if there are button definitions in the config file, remove default buttons
+ if(config->hasGroup("Button List")) Preferences::clearQuickButtonList();
+ config->setGroup("Button List");
+ // Read all default buttons
+ QStringList buttonList(Preferences::quickButtonList());
+ // Read all quick buttons
+ int index=0;
+ while(config->hasKey(QString("Button%1").arg(index)))
+ {
+ buttonList.append(config->readEntry(QString("Button%1").arg(index++)));
+ } // while
+ // Put back the changed button list
+ Preferences::setQuickButtonList(buttonList);
+
+ // Autoreplace List
+
+ // if there are autoreplace definitions in the config file, remove default entries
+ if(config->hasGroup("Autoreplace List")) Preferences::clearAutoreplaceList();
+ config->setGroup("Autoreplace List");
+ // Read all default entries
+ QStringList autoreplaceList(Preferences::autoreplaceList());
+ // Read all entries
+ index=0;
+ while(config->hasKey(QString("Autoreplace%1").arg(index)))
+ {
+ // read entry and get length of the string
+ QString entry=config->readEntry(QString("Autoreplace%1").arg(index++));
+ unsigned int length=entry.length()-1;
+ // if there's a "#" in the end, strip it (used to preserve blanks at the end of the replacement text)
+ // there should always be one, but older versions did not do it, so we check first
+ if(entry.at(length)=='#') entry=entry.left(length);
+ // add entry to internal list
+ autoreplaceList.append(entry);
+ } // while
+ // Put back the changed autoreplace list
+ Preferences::setAutoreplaceList(autoreplaceList);
+
+ // Highlight List
+ if(config->hasKey("Highlight")) // Stay compatible with versions < 0.14
+ {
+ QString highlight=config->readEntry("Highlight");
+ QStringList hiList=QStringList::split(' ',highlight);
+
+ unsigned int hiIndex;
+ for(hiIndex=0;hiIndex<hiList.count();hiIndex+=2)
+ {
+ Preferences::addHighlight(hiList[hiIndex],false,'#'+hiList[hiIndex+1],QString(),QString());
+ }
+
+ config->deleteEntry("Highlight");
+ }
+ else
+ {
+ int i = 0;
+
+ while(config->hasGroup(QString("Highlight%1").arg(i)))
+ {
+ config->setGroup(QString("Highlight%1").arg(i));
+ Preferences::addHighlight(config->readEntry("Pattern"),
+ config->readBoolEntry("RegExp"),
+ config->readColorEntry("Color"),
+ config->readPathEntry("Sound"),
+ config->readEntry("AutoText"));
+ i++;
+ }
+ }
+
+ // Ignore List
+ config->setGroup("Ignore List");
+ // Remove all default entries if there is at least one Ignore in the Preferences::file
+ if(config->hasKey("Ignore0")) Preferences::clearIgnoreList();
+ // Read all ignores
+ index=0;
+ while(config->hasKey(QString("Ignore%1").arg(index)))
+ {
+ Preferences::addIgnore(config->readEntry(QString("Ignore%1").arg(index++)));
+ }
+
+ // Aliases
+ config->setGroup("Aliases");
+ QStringList newList=config->readListEntry("AliasList");
+ if(!newList.isEmpty()) Preferences::setAliasList(newList);
+
+ // Channel Encodings
+ QMap<QString,QString> channelEncodingEntries=config->entryMap("Channel Encodings");
+ QRegExp re("^(.+) ([^\\s]+)$");
+ QStringList channelEncodingEntryKeys=channelEncodingEntries.keys();
+ QStringList::iterator itStr=channelEncodingEntryKeys.begin();
+ for(; itStr != channelEncodingEntryKeys.end(); ++itStr)
+ {
+ if(re.search(*itStr) > -1)
+ {
+ int serverGroupId = Preferences::serverGroupIdByName(re.cap(1));
+ if(serverGroupId != -1)
+ Preferences::setChannelEncoding(serverGroupId,re.cap(2),channelEncodingEntries[*itStr]);
+ }
+ }
+
+ // O, what a tangled web
+ Server::_fetchRates();
+}
+
+void KonversationApplication::saveOptions(bool updateGUI)
+{
+ KConfig* config=kapp->config();
+
+// Should be handled in NicklistBehaviorConfigController now
+// config->setGroup("Sort Nicknames");
+
+ // Clean up identity list
+ QStringList identities=config->groupList().grep(QRegExp("Identity [0-9]+"));
+ if(identities.count())
+ {
+ // remove old identity list from Preferences::file to keep numbering under control
+ for(unsigned int index=0;index<identities.count();index++)
+ config->deleteGroup(identities[index]);
+ }
+
+ IdentityList identityList = Preferences::identityList();
+ int index = 0;
+
+ for(IdentityList::ConstIterator it = identityList.begin(); it != identityList.end(); ++it)
+ {
+ IdentityPtr identity = (*it);
+ config->setGroup(QString("Identity %1").arg(index));
+
+ config->writeEntry("Name",identity->getName());
+ config->writeEntry("Ident",identity->getIdent());
+ config->writeEntry("Realname",identity->getRealName());
+ config->writeEntry("Nicknames",identity->getNicknameList());
+ config->writeEntry("Bot",identity->getBot());
+ config->writeEntry("Password",identity->getPassword());
+ config->writeEntry("InsertRememberLineOnAway", identity->getInsertRememberLineOnAway());
+ config->writeEntry("ShowAwayMessage",identity->getShowAwayMessage());
+ config->writeEntry("AwayMessage",identity->getAwayMessage());
+ config->writeEntry("ReturnMessage",identity->getReturnMessage());
+ config->writeEntry("AutomaticAway", identity->getAutomaticAway());
+ config->writeEntry("AwayInactivity", identity->getAwayInactivity());
+ config->writeEntry("AutomaticUnaway", identity->getAutomaticUnaway());
+ config->writeEntry("QuitReason",identity->getQuitReason());
+ config->writeEntry("PartReason",identity->getPartReason());
+ config->writeEntry("KickReason",identity->getKickReason());
+ config->writeEntry("PreShellCommand",identity->getShellCommand());
+ config->writeEntry("Codec",identity->getCodecName());
+ config->writeEntry("AwayNick", identity->getAwayNick());
+ index++;
+ } // endfor
+
+ // Remove the old servergroups from the config
+ QStringList groups = config->groupList().grep(QRegExp("ServerGroup [0-9]+"));
+ if(groups.count())
+ {
+ QStringList::iterator it;
+ for(it = groups.begin(); it != groups.end(); ++it)
+ {
+ config->deleteGroup((*it));
+ }
+ }
+
+ // Remove the old servers from the config
+ groups = config->groupList().grep(QRegExp("Server [0-9]+"));
+ if(groups.count())
+ {
+ QStringList::iterator it;
+ for(it = groups.begin(); it != groups.end(); ++it)
+ {
+ config->deleteGroup((*it));
+ }
+ }
+
+ // Remove the old channels from the config
+ groups = config->groupList().grep(QRegExp("Channel [0-9]+"));
+ if(groups.count())
+ {
+ QStringList::iterator it;
+ for(it = groups.begin(); it != groups.end(); ++it)
+ {
+ config->deleteGroup((*it));
+ }
+ }
+
+ // Add the new servergroups to the config
+ Konversation::ServerGroupList serverGroupList = Preferences::serverGroupList();
+ Konversation::ServerGroupList::iterator it;
+ index = 0;
+ int index2 = 0;
+ int index3 = 0;
+ int width = QString::number(serverGroupList.count()).length();
+ QString groupName;
+ QStringList servers;
+ Konversation::ServerList::iterator it2;
+ Konversation::ServerList serverlist;
+ Konversation::ChannelList channelList;
+ Konversation::ChannelList::iterator it3;
+ QStringList channels;
+ QStringList channelHistory;
+
+ for(it = serverGroupList.begin(); it != serverGroupList.end(); ++it)
+ {
+ serverlist = (*it)->serverList();
+ servers.clear();
+
+ for(it2 = serverlist.begin(); it2 != serverlist.end(); ++it2)
+ {
+ groupName = QString("Server %1").arg(index2);
+ servers.append(groupName);
+ config->setGroup(groupName);
+ config->writeEntry("Server", (*it2).host());
+ config->writeEntry("Port", (*it2).port());
+ config->writeEntry("Password", (*it2).password());
+ config->writeEntry("SSLEnabled", (*it2).SSLEnabled());
+ index2++;
+ }
+
+ channelList = (*it)->channelList();
+ channels.clear();
+
+ for(it3 = channelList.begin(); it3 != channelList.end(); ++it3)
+ {
+ groupName = QString("Channel %1").arg(index3);
+ channels.append(groupName);
+ config->setGroup(groupName);
+ config->writeEntry("Name", (*it3).name());
+ config->writeEntry("Password", (*it3).password());
+ index3++;
+ }
+
+ channelList = (*it)->channelHistory();
+ channelHistory.clear();
+
+ for(it3 = channelList.begin(); it3 != channelList.end(); ++it3)
+ {
+ groupName = QString("Channel %1").arg(index3);
+ channelHistory.append(groupName);
+ config->setGroup(groupName);
+ config->writeEntry("Name", (*it3).name());
+ config->writeEntry("Password", (*it3).password());
+ config->writeEntry("EnableNotifications", (*it3).enableNotifications());
+ index3++;
+ }
+
+ config->setGroup(QString("ServerGroup %1").arg(QString::number(index).rightJustify(width,'0')));
+ config->writeEntry("Name", (*it)->name());
+ config->writeEntry("Identity", (*it)->identity()->getName());
+ config->writeEntry("ServerList", servers);
+ config->writeEntry("AutoJoinChannels", channels);
+ config->writeEntry("ConnectCommands", (*it)->connectCommands());
+ config->writeEntry("AutoConnect", (*it)->autoConnectEnabled());
+ config->writeEntry("ChannelHistory", channelHistory);
+ config->writeEntry("EnableNotifications", (*it)->enableNotifications());
+ config->writeEntry("Expanded", (*it)->expanded());
+ config->writeEntry("NotifyList",Preferences::notifyStringByGroupName((*it)->name()));
+ index++;
+ }
+
+ config->deleteGroup("Server List");
+
+ // Ignore List
+ config->deleteGroup("Ignore List");
+ config->setGroup("Ignore List");
+ QPtrList<Ignore> ignoreList=Preferences::ignoreList();
+ Ignore* item=ignoreList.first();
+ index=0;
+ while(item)
+ {
+ config->writeEntry(QString("Ignore%1").arg(index),QString("%1,%2").arg(item->getName()).arg(item->getFlags()));
+ item=ignoreList.next();
+ index++;
+ }
+
+ // Channel Encodings
+ // remove all entries once
+ config->deleteGroup("Channel Encodings");
+ config->setGroup("Channel Encodings");
+ QValueList<int> encServers=Preferences::channelEncodingsServerGroupIdList();
+ //i have no idea these would need to be sorted //encServers.sort();
+ QValueList<int>::iterator encServer;
+ for ( encServer = encServers.begin(); encServer != encServers.end(); ++encServer )
+ {
+ Konversation::ServerGroupSettingsPtr sgsp = Preferences::serverGroupById(*encServer);
+ if ( sgsp ) // sgsp == 0 when the entry is of QuickConnect or something?
+ {
+ QStringList encChannels=Preferences::channelEncodingsChannelList(*encServer);
+ //ditto //encChannels.sort();
+ QStringList::iterator encChannel;
+ for ( encChannel = encChannels.begin(); encChannel != encChannels.end(); ++encChannel )
+ {
+ QString enc = Preferences::channelEncoding(*encServer, *encChannel);
+ QString key = sgsp->name() + ' ' + (*encChannel);
+ config->writeEntry(key, enc);
+ }
+ }
+ }
+
+ config->sync();
+
+ if(updateGUI)
+ emit appearanceChanged();
+}
+
+// FIXME: use KURL maybe?
+void KonversationApplication::storeUrl(const QString& who,const QString& newUrl)
+{
+ QString url(newUrl);
+ // clean up URL to help KRun() in URL catcher interface
+ if(url.startsWith("www.")) url="http://"+url;
+ else if(url.startsWith("ftp.")) url="ftp://"+url;
+
+ url=url.replace("&amp;","&");
+
+ // check that we don't add the same URL twice
+ deleteUrl(who,url);
+ urlList.append(who+' '+url);
+ emit catchUrl(who,url);
+}
+
+const QStringList& KonversationApplication::getUrlList()
+{
+ return urlList;
+}
+
+void KonversationApplication::deleteUrl(const QString& who,const QString& url)
+{
+ urlList.remove(who+' '+url);
+}
+
+void KonversationApplication::clearUrlList()
+{
+ urlList.clear();
+}
+
+void KonversationApplication::openQuickConnectDialog()
+{
+ quickConnectDialog = new QuickConnectDialog(mainWindow);
+ connect(quickConnectDialog, SIGNAL(connectClicked(Konversation::ConnectionFlag, const QString&, const QString&,
+ const QString&, const QString&, const QString&, bool)),
+ m_connectionManager, SLOT(connectTo(Konversation::ConnectionFlag, const QString&, const QString&,
+ const QString&, const QString&, const QString&, bool)));
+ quickConnectDialog->show();
+}
+
+void KonversationApplication::sendMultiServerCommand(const QString& command, const QString& parameter)
+{
+ QPtrList<Server> serverList = getConnectionManager()->getServerList();
+
+ for (Server* server = serverList.first(); server; server = serverList.next())
+ server->executeMultiServerCommand(command, parameter);
+}
+
+void KonversationApplication::splitNick_Server(const QString& nick_server, QString &ircnick, QString &serverOrGroup)
+{
+ //kaddresbook uses the utf separator 0xE120, so treat that as a separator as well
+ QString nickServer = nick_server;
+ nickServer.replace(QChar(0xE120), "@");
+ ircnick = nickServer.section("@",0,0);
+ serverOrGroup = nickServer.section("@",1);
+}
+
+NickInfoPtr KonversationApplication::getNickInfo(const QString &ircnick, const QString &serverOrGroup)
+{
+ QPtrList<Server> serverList = getConnectionManager()->getServerList();
+ NickInfoPtr nickInfo;
+ QString lserverOrGroup = serverOrGroup.lower();
+ for(Server* lookServer = serverList.first(); lookServer; lookServer = serverList.next())
+ {
+ if(lserverOrGroup.isEmpty()
+ || lookServer->getServerName().lower()==lserverOrGroup
+ || lookServer->getDisplayName().lower()==lserverOrGroup)
+ {
+ nickInfo = lookServer->getNickInfo(ircnick);
+ if(nickInfo) return nickInfo; //If we found one
+ }
+ }
+ return 0;
+}
+
+// auto replace on input/output
+QString KonversationApplication::doAutoreplace(const QString& text,bool output)
+{
+ // get autoreplace list
+ QStringList autoreplaceList=Preferences::autoreplaceList();
+ // working copy
+ QString line=text;
+
+ // loop through the list of replacement patterns
+ for(unsigned int index=0;index<autoreplaceList.count();index++)
+ {
+ // get autoreplace definition
+ QString definition=autoreplaceList[index];
+ // split definition in parts
+ QString regex=definition.section(',',0,0);
+ QString direction=definition.section(',',1,1);
+ QString pattern=definition.section(',',2,2);
+ QString replacement=definition.section(',',3);
+
+ QString isDirection=output ? "o" : "i";
+
+ // only replace if this pattern is for the specific direction or both directions
+ if(direction==isDirection || direction=="io")
+ {
+ // regular expression pattern?
+ if(regex=="1")
+ {
+ // create regex from pattern
+ QRegExp needleReg=pattern;
+ // set pattern case insensitive
+ needleReg.setCaseSensitive(true);
+ int index = 0;
+
+ do {
+ replacement = definition.section(',',3);
+ // find matches
+ index = line.find(needleReg, index);
+
+ if(index != -1)
+ {
+ // remember captured patterns
+ QStringList captures = needleReg.capturedTexts();
+
+ // replace %0 - %9 in regex groups
+ for(unsigned int capture=0;capture<captures.count();capture++)
+ {
+ replacement.replace(QString("%%1").arg(capture),captures[capture]);
+ }
+ replacement.replace(QRegExp("%[0-9]"),QString());
+ // replace input with replacement
+ line.replace(index, captures[0].length(), replacement);
+ index += replacement.length();
+ }
+ } while(index >= 0 && index < (int)line.length());
+ }
+ else
+ {
+ QRegExp needleReg("\\b" + QRegExp::escape(pattern) + "\\b");
+ needleReg.setCaseSensitive(false);
+ line.replace(needleReg,replacement);
+ }
+ }
+ }
+
+ return line;
+}
+
+#include "konversationapplication.moc"
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/konversationapplication.h b/konversation/src/konversationapplication.h
new file mode 100644
index 0000000..3464406
--- /dev/null
+++ b/konversation/src/konversationapplication.h
@@ -0,0 +1,169 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONVERSATIONAPPLICATION_H
+#define KONVERSATIONAPPLICATION_H
+
+#include "preferences.h"
+#include "osd.h"
+#include "konvdcop.h"
+#include "identity.h"
+#include "nickinfo.h"
+
+#include <kuniqueapplication.h>
+
+
+class QCString;
+
+class ConnectionManager;
+class AwayManager;
+class DccTransferManager;
+class KonversationMainWindow;
+class KonvDCOP;
+class Server;
+class QuickConnectDialog;
+class Images;
+class ServerGroupSettings;
+
+namespace Konversation
+{
+ class Sound;
+ class NotificationHandler;
+
+ // Shared between NickListView and IRCView
+ enum PopupIDs
+ {
+ ModesSub,GiveOp,TakeOp,GiveHalfOp,TakeHalfOp,GiveVoice,TakeVoice,
+ KickBanSub,IgnoreNick,UnignoreNick,
+ Kick,KickBan,BanNick,BanHost,BanDomain,BanUserHost,BanUserDomain,
+ KickBanHost,KickBanDomain,KickBanUserHost,KickBanUserDomain,
+ Whois,Version,Ping,OpenQuery,DccSend,Join,Names,Topic,
+ CustomID, AddressbookSub, AddressbookChange, AddressbookNew, AddressbookDelete,
+ AddressbookEdit, SendEmail, StartDccChat, AddNotify
+ };
+
+}
+
+class KonversationApplication : public KUniqueApplication
+{
+ Q_OBJECT
+
+ public:
+ /** This function in general shouldn't be called, because in the future there
+ * may be multiple windows.
+ * However, in some situations we have messageboxes that aren't targeted for
+ * any particular main window, such as general errors from dcop calls.
+ *
+ * Note to any MDI developer - get this to return any of the windows, or some
+ * 'main' one.
+ */
+ KonversationMainWindow* getMainWindow() { return mainWindow; }
+
+ ConnectionManager* getConnectionManager() { return m_connectionManager; }
+ AwayManager* getAwayManager() { return m_awayManager; }
+ DccTransferManager* getDccTransferManager() { return m_dccTransferManager; }
+
+ // HACK
+ void showQueueTuner(bool);
+
+ // URL-Catcher
+ void storeUrl(const QString& who,const QString& url);
+ const QStringList& getUrlList();
+
+ KonversationApplication();
+ ~KonversationApplication();
+
+ static KonversationApplication* instance();
+
+ /** For dcop and addressbook, a user can be specified as user@irc.server.net
+ * or user\@servergroup or using the unicode separator symbol 0xE120 instead
+ * of the "@". This function takes a string like the above examples, and
+ * modifies ircnick and serverOrGroup to contain the split up string. If
+ * the string doesn't have an @ or 0xE120, ircnick is set to the
+ * nick_server, and serverOrGroup is set to empty.
+ * Behaviour is undefined for serverOrGroup if multiple @ or 0xE120 are found.
+ * @param nick_server A string containting ircnick and possibly servername or server group
+ * @param ircnick This is modified to contain the ircnick
+ * @param serverOrGroup This is modified to contain the servername, servergroup or an empty string.
+ */
+ static void splitNick_Server(const QString& nick_server, QString &ircnick, QString &serverOrGroup);
+
+ /** Tries to find a nickinfo for a given ircnick on a given ircserver.
+ * @param ircnick The case-insensitive ircnick of the person you want to find. e.g. "johnflux"
+ * @param serverOrGroup The case-insensitive server name (e.g. "irc.kde.org") or server group name (e.g. "freenode"), or null to search all servers
+ * @return A nickinfo for this user and server if one is found.
+ */
+ NickInfoPtr getNickInfo(const QString &ircnick, const QString &serverOrGroup);
+
+ OSDWidget* osd;
+
+ Konversation::Sound* sound() { return m_sound; }
+
+ Images* images() { return m_images; }
+
+ Konversation::NotificationHandler* notificationHandler() const { return m_notificationHandler; }
+
+ // auto replacement for input or output lines
+ QString doAutoreplace(const QString& text,bool output);
+
+ int newInstance();
+
+
+ signals:
+ void catchUrl(const QString& who,const QString& url);
+ void serverGroupsChanged(const Konversation::ServerGroupSettings* serverGroup);
+
+
+ public slots:
+ void readOptions();
+ void saveOptions(bool updateGUI=true);
+
+ void deleteUrl(const QString& who,const QString& url);
+ void clearUrlList();
+
+
+ protected slots:
+ void prepareShutdown();
+
+ void openQuickConnectDialog();
+
+ void dcopMultiServerRaw(const QString &command);
+ void dcopRaw(const QString& connection, const QString &command);
+ void dcopSay(const QString& connection, const QString& target, const QString& command);
+ void dcopInfo(const QString& string);
+ void sendMultiServerCommand(const QString& command, const QString& parameter);
+
+
+ private:
+ ConnectionManager* m_connectionManager;
+ AwayManager* m_awayManager;
+ DccTransferManager* m_dccTransferManager;
+ QStringList urlList;
+ KonvDCOP* dcopObject;
+ KonvIdentDCOP* identDCOP;
+ KonversationMainWindow* mainWindow;
+ Konversation::Sound* m_sound;
+ QuickConnectDialog* quickConnectDialog;
+ Images* m_images;
+
+ Konversation::NotificationHandler* m_notificationHandler;
+
+ QStringList colorList;
+};
+
+#endif
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/konversationmainwindow.cpp b/konversation/src/konversationmainwindow.cpp
new file mode 100644
index 0000000..a85c5fd
--- /dev/null
+++ b/konversation/src/konversationmainwindow.cpp
@@ -0,0 +1,654 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "konversationmainwindow.h"
+#include "konversationapplication.h"
+#include "linkaddressbook/addressbook.h"
+#include "konvisettingsdialog.h"
+#include "viewcontainer.h"
+#include "konversationstatusbar.h"
+#include "konvibookmarkhandler.h"
+#include "trayicon.h"
+#include "serverlistdialog.h"
+#include "identitydialog.h"
+#include "notificationhandler.h"
+#include "irccharsets.h"
+#include "connectionmanager.h"
+#include "awaymanager.h"
+#include "dcctransfermanager.h"
+
+#include <qnamespace.h>
+#include <qwhatsthis.h>
+#include <qsignalmapper.h>
+#include <qobjectlist.h>
+
+#include <kaccel.h>
+#include <kaccelmanager.h>
+#include <kstdaction.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmenubar.h>
+#include <kkeydialog.h>
+#include <kdeversion.h>
+#include <kedittoolbar.h>
+#include <kpopupmenu.h>
+#include <kwin.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <dcopclient.h>
+#include <scriptmanager.h>
+#include <kabc/addressbook.h>
+#include <kabc/errorhandler.h>
+
+#include <config.h>
+#ifdef USE_KNOTIFY
+#include <knotifydialog.h>
+#endif
+
+
+KonversationMainWindow::KonversationMainWindow() : KMainWindow(0,"main_window", WStyle_ContextHelp | WType_TopLevel | WDestructiveClose)
+{
+ m_hasDirtySettings = false;
+ m_closeApp = false;
+ m_serverListDialog = 0;
+ m_settingsDialog = NULL;
+
+ m_viewContainer = new ViewContainer(this);
+ setCentralWidget(m_viewContainer->getWidget());
+
+ //used for event compression. See header file for resetHasDirtySettings()
+ connect(KonversationApplication::instance(), SIGNAL(appearanceChanged()), this, SLOT(resetHasDirtySettings()));
+ connect(KonversationApplication::instance(), SIGNAL(appearanceChanged()), this, SLOT(updateTrayIcon()));
+
+
+ // Set up view container
+ connect(KonversationApplication::instance(), SIGNAL(appearanceChanged()), m_viewContainer, SLOT(updateAppearance()));
+ connect(KonversationApplication::instance(), SIGNAL(iconChanged(int)), m_viewContainer, SLOT(updateViewIcons()));
+ connect(KonversationApplication::instance(), SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)),
+ m_viewContainer, SLOT(updateViews(const Konversation::ServerGroupSettings*)));
+ connect(m_viewContainer, SIGNAL(autoJoinToggled(const Konversation::ServerGroupSettings*)),
+ KonversationApplication::instance(), SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)));
+ connect(m_viewContainer, SIGNAL(setWindowCaption(const QString&)), this, SLOT(setCaption(const QString&)));
+ connect(KonversationApplication::instance()->getConnectionManager(),
+ SIGNAL(connectionChangedState(Server*, Konversation::ConnectionState)),
+ m_viewContainer, SLOT(connectionStateChanged(Server*, Konversation::ConnectionState)));
+ connect(this, SIGNAL(triggerRememberLine()), m_viewContainer, SLOT(insertRememberLine()));
+ connect(this, SIGNAL(triggerRememberLines(Server*)), m_viewContainer, SLOT(insertRememberLines(Server*)));
+ connect(this, SIGNAL(cancelRememberLine()), m_viewContainer, SLOT(cancelRememberLine()));
+ connect(this, SIGNAL(insertMarkerLine()), m_viewContainer, SLOT(insertMarkerLine()));
+
+ // Set up status bar
+ m_statusBar = new KonversationStatusBar(this);
+ connect(KonversationApplication::instance(), SIGNAL(appearanceChanged()), m_statusBar, SLOT(updateAppearance()));
+
+ createStandardStatusBarAction();
+
+ connect(actionCollection(), SIGNAL(actionStatusText(const QString&)), m_statusBar, SLOT(setMainLabelTempText(const QString&)));
+ connect(actionCollection(), SIGNAL(clearStatusText()), m_statusBar, SLOT(clearMainLabelTempText()));
+ actionCollection()->setHighlightingEnabled(true);
+
+ connect(m_viewContainer, SIGNAL(resetStatusBar()), m_statusBar, SLOT(resetStatusBar()));
+ connect(m_viewContainer, SIGNAL(setStatusBarTempText(const QString&)), m_statusBar, SLOT(setMainLabelTempText(const QString&)));
+ connect(m_viewContainer, SIGNAL(clearStatusBarTempText()), m_statusBar, SLOT(clearMainLabelTempText()));
+ connect(m_viewContainer, SIGNAL(setStatusBarInfoLabel(const QString&)), m_statusBar, SLOT(updateInfoLabel(const QString&)));
+ connect(m_viewContainer, SIGNAL(clearStatusBarInfoLabel()), m_statusBar, SLOT(clearInfoLabel()));
+ connect(m_viewContainer, SIGNAL(setStatusBarLagLabelShown(bool)), m_statusBar, SLOT(setLagLabelShown(bool)));
+ connect(m_viewContainer, SIGNAL(updateStatusBarLagLabel(Server*, int)), m_statusBar, SLOT(updateLagLabel(Server*, int)));
+ connect(m_viewContainer, SIGNAL(resetStatusBarLagLabel()), m_statusBar, SLOT(resetLagLabel()));
+ connect(m_viewContainer, SIGNAL(setStatusBarLagLabelTooLongLag(Server*, int)), m_statusBar, SLOT(setTooLongLag(Server*, int)));
+ connect(m_viewContainer, SIGNAL(updateStatusBarSSLLabel(Server*)), m_statusBar, SLOT(updateSSLLabel(Server*)));
+ connect(m_viewContainer, SIGNAL(removeStatusBarSSLLabel()), m_statusBar, SLOT(removeSSLLabel()));
+
+
+ // Actions
+ KStdAction::quit(this,SLOT(quitProgram()),actionCollection());
+
+ hideMenuBarAction = KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection());
+
+ setStandardToolBarMenuEnabled(true);
+ KStdAction::configureToolbars(this, SLOT(configureToolbar()), actionCollection());
+
+ KStdAction::keyBindings(this, SLOT(openKeyBindings()), actionCollection());
+ KAction *preferencesAction = KStdAction::preferences(this, SLOT(openPrefsDialog()), actionCollection());
+
+#ifdef USE_KNOTIFY // options_configure_notifications
+ KAction *configureNotificationsAction = KStdAction::configureNotifications(this,SLOT(openNotifications()), actionCollection());
+#endif
+
+ KAction* action;
+
+ (new KAction(i18n("&Server List..."), "server", KShortcut("F2"), this, SLOT(openServerList()),
+ actionCollection(), "open_server_list"))->setToolTip(i18n("Manage networks and servers"));
+ (new KAction(i18n("Quick &Connect..."), "connect_creating", KShortcut("F7"), this, SLOT(openQuickConnectDialog()),
+ actionCollection(), "quick_connect_dialog"))->setToolTip(i18n("Type in the address of a new IRC server to connect to"));
+
+ action = new KAction(i18n("&Reconnect"), "connect_creating", 0, m_viewContainer, SLOT(reconnectFrontServer()), actionCollection(), "reconnect_server");
+ action->setEnabled(false);
+ action->setToolTip(i18n("Reconnect to the current server."));
+
+ action = new KAction(i18n("&Disconnect"), "connect_no", 0, m_viewContainer, SLOT(disconnectFrontServer()), actionCollection(), "disconnect_server");
+ action->setEnabled(false);
+ action->setToolTip(i18n("Disconnect from the current server."));
+
+ (new KAction(i18n("&Identities..."), "identity", KShortcut("F8"), this, SLOT(openIdentitiesDialog()),
+ actionCollection(), "identities_dialog"))->setToolTip(i18n("Manage your nick, away and other identity settings"));
+
+ new KToggleAction(i18n("&Watched Nicks Online"), "kontact_contacts", KShortcut("F4"), m_viewContainer, SLOT(openNicksOnlinePanel()), actionCollection(), "open_nicksonline_window");
+ new KToggleAction(i18n("&DCC Status"), "2rightarrow", KShortcut("F9"), m_viewContainer, SLOT(toggleDccPanel()), actionCollection(), "open_dccstatus_window");
+ action = new KAction(i18n("&Open Logfile"), "history", KShortcut("Ctrl+O"), m_viewContainer, SLOT(openLogFile()), actionCollection(), "open_logfile");
+ action->setEnabled(false);
+ action->setToolTip(i18n("Open the known history for this channel in a new tab"));
+
+ action = new KAction(i18n("&Channel Settings..."), "edit", m_viewContainer, SLOT(openChannelSettings()), actionCollection(), "channel_settings");
+ action->setEnabled(false);
+ action->setToolTip(i18n("Open the channel settings dialog for this tab"));
+
+ KToggleAction* channelListAction = new KToggleAction(i18n("Channel &List"), "view_text", KShortcut("F5"), m_viewContainer, SLOT(openChannelList()), actionCollection(), "open_channel_list");
+ channelListAction->setEnabled(false);
+ channelListAction->setToolTip(i18n("Show a list of all the known channels on this server"));
+
+ action = new KToggleAction(i18n("&URL Catcher"), "enhanced_browsing", KShortcut("F6"), m_viewContainer, SLOT(addUrlCatcher()), actionCollection(), "open_url_catcher");
+ action->setToolTip(i18n("List all URLs that have been mentioned recently in a new tab"));
+
+ if (kapp->authorize("shell_access"))
+ {
+ action = new KAction(i18n("New &Konsole"), "openterm", 0, m_viewContainer, SLOT(addKonsolePanel()), actionCollection(), "open_konsole");
+ action->setToolTip(i18n("Open a terminal in a new tab"));
+ }
+
+ // Actions to navigate through the different pages
+ KShortcut nextShortcut = KStdAccel::tabNext();
+ nextShortcut.setSeq(1, KKeySequence("Alt+Right"));
+ KShortcut prevShortcut = KStdAccel::tabPrev();
+ prevShortcut.setSeq(1, KKeySequence("Alt+Left"));
+ action = new KAction(i18n("&Next Tab"), QApplication::reverseLayout() ? "previous" : "next",
+ QApplication::reverseLayout() ? prevShortcut : nextShortcut,
+ m_viewContainer, SLOT(showNextView()), actionCollection(), "next_tab");
+ action->setEnabled(false);
+ action = new KAction(i18n("&Previous Tab"), QApplication::reverseLayout() ? "next" : "previous",
+ QApplication::reverseLayout() ? nextShortcut : prevShortcut,
+ m_viewContainer, SLOT(showPreviousView()),actionCollection(),"previous_tab");
+ action->setEnabled(false);
+ action = new KAction(i18n("Close &Tab"),"tab_remove",KShortcut("Ctrl+w"), m_viewContainer, SLOT(closeCurrentView()),actionCollection(),"close_tab");
+ action->setEnabled(false);
+ action = new KAction(i18n("Next Active Tab"), 0, KShortcut("Ctrl+Alt+Space"), m_viewContainer, SLOT(showNextActiveView()),
+ actionCollection(), "next_active_tab");
+ action->setEnabled(false);
+
+ if (Preferences::tabPlacement()==Preferences::Left)
+ {
+ action = new KAction(i18n("Move Tab Up"), "1uparrow", KShortcut("Alt+Shift+Left"),
+ m_viewContainer, SLOT(moveViewLeft()), actionCollection(), "move_tab_left");
+ action->setEnabled(false);
+ action->setToolTip("Move this tab");
+ action = new KAction(i18n("Move Tab Down"), "1downarrow", KShortcut("Alt+Shift+Right"),
+ m_viewContainer, SLOT(moveViewRight()), actionCollection(), "move_tab_right");
+ action->setEnabled(false);
+ action->setToolTip("Move this tab");
+ }
+ else
+ {
+ KAction* action2;
+ if (QApplication::reverseLayout())
+ {
+ action2 = new KAction(i18n("Move Tab Right"), "1rightarrow", KShortcut("Alt+Shift+Right"),
+ m_viewContainer, SLOT(moveViewLeft()), actionCollection(), "move_tab_left");
+ action = new KAction(i18n("Move Tab Left"), "1leftarrow", KShortcut("Alt+Shift+Left"),
+ m_viewContainer, SLOT(moveViewRight()), actionCollection(), "move_tab_right");
+ }
+ else
+ {
+ action = new KAction(i18n("Move Tab Left"), "1leftarrow", KShortcut("Alt+Shift+Left"),
+ m_viewContainer, SLOT(moveViewLeft()), actionCollection(), "move_tab_left");
+ action2 = new KAction(i18n("Move Tab Right"), "1rightarrow", KShortcut("Alt+Shift+Right"),
+ m_viewContainer, SLOT(moveViewRight()), actionCollection(), "move_tab_right");
+ }
+
+ action->setEnabled(false);
+ action->setToolTip("Move this tab");
+ action2->setEnabled(false);
+ action2->setToolTip("Move this tab");
+ }
+
+ action = new KAction(i18n("Rejoin Channel"), 0, m_viewContainer, SLOT(rejoinChannel()), actionCollection(), "rejoin_channel");
+ action->setEnabled(false);
+
+ action = new KToggleAction(i18n("Enable Notifications"), 0, 0, m_viewContainer, SLOT(toggleViewNotifications()), actionCollection(), "tab_notifications");
+ action->setEnabled(false);
+
+ action = new KToggleAction(i18n("Join on Connect"), 0, 0, m_viewContainer, SLOT(toggleAutoJoin()), actionCollection(), "tab_autojoin");
+ action->setEnabled(false);
+
+ KSelectAction* selectAction = new KSelectAction(i18n("Set Encoding"), "charset", 0, actionCollection(), "tab_encoding");
+ selectAction->setEditable(false);
+ QStringList encodingDescs = Konversation::IRCCharsets::self()->availableEncodingDescriptiveNames();
+ encodingDescs.prepend(i18n("Default"));
+ selectAction->setItems(encodingDescs);
+ selectAction->setEnabled(false);
+ connect(selectAction, SIGNAL(activated(int)), m_viewContainer, SLOT(changeViewCharset(int)));
+
+ QSignalMapper* tabSelectionMapper = new QSignalMapper(this);
+ connect(tabSelectionMapper, SIGNAL(mapped(int)), m_viewContainer, SLOT(goToView(int)));
+
+ for (uint i = 1; i <= 10; ++i)
+ {
+ KAction* tabSelectionAction = new KAction(i18n("Go to Tab %1").arg(i), 0, KShortcut(QString("Alt+%1").arg(i%10)),
+ tabSelectionMapper, SLOT(map()), actionCollection(), QString("go_to_tab_%1").arg(i).local8Bit());
+ tabSelectionMapper->setMapping( tabSelectionAction, i-1);
+ }
+
+ action = new KAction(i18n("Clear &Marker Lines"), 0, KShortcut("CTRL+SHIFT+R"), m_viewContainer, SLOT(clearViewLines()),actionCollection(),"clear_lines");
+ action->setToolTip(i18n("Clear marker lines in the current tab"));
+ action->setEnabled(false);
+ action = new KAction(i18n("&Clear Window"), 0, KShortcut("Ctrl+L"), m_viewContainer, SLOT(clearView()),actionCollection(),"clear_window");
+ action->setToolTip(i18n("Clear the contents of the current tab"));
+ action->setEnabled(false);
+ action = new KAction(i18n("Clear &All Windows"),0,KShortcut("CTRL+SHIFT+L"), m_viewContainer, SLOT(clearAllViews()),actionCollection(),"clear_tabs");
+ action->setToolTip(i18n("Clear the contents of all open tabs"));
+ action->setEnabled(false);
+
+ KToggleAction* awayAction = new KToggleAction(i18n("Global Away"), KShortcut("Ctrl+Shift+A"), actionCollection(), "toggle_away");
+ connect(awayAction, SIGNAL(toggled(bool)), static_cast<KonversationApplication*>(kapp)->getAwayManager(), SLOT(toggleGlobalAway(bool)));
+ awayAction->setEnabled(false);
+
+ action = new KAction(i18n("&Join Channel..."), "add", KShortcut("Ctrl+J"), m_viewContainer, SLOT(showJoinChannelDialog()), actionCollection(), "join_channel");
+ action->setEnabled(false);
+ action->setToolTip("Join a new channel on this server");
+
+ action = KStdAction::find(m_viewContainer, SLOT(findText()), actionCollection());
+ action->setEnabled(false);
+ action = KStdAction::findNext(m_viewContainer, SLOT(findNextText()), actionCollection());
+ action->setEnabled(false);
+ action = KStdAction::findPrev(m_viewContainer, SLOT(findPrevText()), actionCollection());
+ action->setEnabled(false);
+
+ action = new KAction(i18n("&IRC Color..."), "colorize", CTRL+Key_K, m_viewContainer, SLOT(insertIRCColor()), actionCollection(), "irc_colors");
+ action->setToolTip(i18n("Set the color of your current IRC message"));
+ action->setEnabled(false);
+ action = new KAction(i18n("&Marker Line"), 0, KShortcut("Ctrl+R") , m_viewContainer, SLOT(insertMarkerLine()), actionCollection(), "insert_marker_line");
+ action->setToolTip(i18n("Insert a horizontal line into the current tab that only you can see"));
+ action->setEnabled(false);
+ action = new KAction(i18n("Special &Character..."), "char", KShortcut("Alt+Shift+C"), m_viewContainer, SLOT(insertCharacter()), actionCollection(), "insert_character");
+ action->setToolTip(i18n("Insert any character into your current IRC message"));
+ action->setEnabled(false);
+
+ action = new KAction(i18n("Close &All Open Queries"), 0, KShortcut("F11"), m_viewContainer, SLOT(closeQueries()), actionCollection(), "close_queries");
+ action->setEnabled(false);
+
+ KToggleAction* toggleChannelNickListsAction = new KToggleAction(i18n("Hide Nicklist"), 0,
+ KShortcut("Ctrl+H"), m_viewContainer, SLOT(toggleChannelNicklists()), actionCollection(), "hide_nicknamelist");
+ if (!Preferences::showNickList())
+ toggleChannelNickListsAction->setChecked(true);
+
+ // set up system tray
+ m_trayIcon = new Konversation::TrayIcon(this);
+ connect(this, SIGNAL(endNotification()), m_trayIcon, SLOT(endNotification()));
+ connect(KonversationApplication::instance(), SIGNAL(iconChanged(int)), m_trayIcon, SLOT(updateAppearance()));
+ connect(m_trayIcon, SIGNAL(quitSelected()), this, SLOT(quitProgram()));
+ KPopupMenu *trayMenu = m_trayIcon->contextMenu();
+ #ifdef USE_KNOTIFY
+ configureNotificationsAction->plug(trayMenu);
+ #endif
+ preferencesAction->plug(trayMenu);
+ awayAction->plug(trayMenu);
+
+ // decide whether to show the tray icon or not
+ updateTrayIcon();
+
+ createGUI(NULL, false);
+
+ resize(700, 500); // Give the app a sane default size
+ setAutoSaveSettings();
+
+ // Apply menubar show/hide pref
+ hideMenuBarAction->setChecked(Preferences::showMenuBar());
+ toggleMenubar(true);
+
+ // Bookmarks
+ m_bookmarkHandler = new KonviBookmarkHandler(this);
+
+ // set up KABC with a nice gui error dialog
+ KABC::GuiErrorHandler *m_guiErrorHandler = new KABC::GuiErrorHandler(this);
+ kapp->dcopClient()->setAcceptCalls( false );
+ Konversation::Addressbook::self()->getAddressBook()->setErrorHandler(m_guiErrorHandler);
+ kapp->dcopClient()->setAcceptCalls( true );
+
+ if (Preferences::useNotify() && Preferences::openWatchedNicksAtStartup())
+ m_viewContainer->openNicksOnlinePanel();
+
+}
+
+KonversationMainWindow::~KonversationMainWindow()
+{
+ delete m_viewContainer;
+}
+
+int KonversationMainWindow::confirmQuit()
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ if (konvApp->getConnectionManager()->connectionCount() == 0)
+ return KMessageBox::Continue;
+
+ int result = KMessageBox::Cancel;
+
+ if (!KMessageBox::shouldBeShownContinue("systemtrayquitKonversation")
+ && konvApp->getDccTransferManager()->hasActiveTransfers())
+ {
+ result = KMessageBox::warningContinueCancel(
+ this,
+ i18n("<qt>You have active DCC file transfers. Are you sure you want to quit <b>Konversation</b>?</qt>"),
+ i18n("Confirm Quit"),
+ i18n("Quit"),
+ "QuitWithActiveDccTransfers");
+ }
+ else
+ {
+ result = KMessageBox::warningContinueCancel(
+ this,
+ i18n("<qt>Are you sure you want to quit <b>Konversation</b>?</qt>"),
+ i18n("Confirm Quit"),
+ i18n("Quit"),
+ "systemtrayquitKonversation");
+ }
+
+ return result;
+}
+
+void KonversationMainWindow::quitProgram()
+{
+ if (Preferences::showTrayIcon() &&
+ sender() != m_trayIcon &&
+ confirmQuit() == KMessageBox::Cancel) return;
+
+ // will call queryClose()
+ m_closeApp = true;
+ close();
+}
+
+bool KonversationMainWindow::queryClose()
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ if (!konvApp->sessionSaving())
+ {
+ if (sender() == m_trayIcon)
+ m_closeApp = true;
+
+ if (Preferences::showTrayIcon() && !m_closeApp)
+ {
+ KMessageBox::information( this,
+ i18n("<p>Closing the main window will keep Konversation running in the system tray. "
+ "Use <b>Quit</b> from the <b>Konversation</b> menu to quit the application.</p>"),
+ i18n( "Docking in System Tray" ), "HideOnCloseInfo" );
+ hide();
+
+ return false;
+ }
+
+ if (!Preferences::showTrayIcon() && confirmQuit() == KMessageBox::Cancel)
+ return false;
+ }
+
+ return true;
+}
+
+void KonversationMainWindow::hideEvent(QHideEvent *e)
+{
+ emit triggerRememberLine();
+
+ m_statusBar->clearMainLabelTempText();
+
+ KMainWindow::hideEvent(e);
+}
+
+void KonversationMainWindow::showEvent(QShowEvent *e)
+{
+ emit cancelRememberLine();
+
+ KMainWindow::showEvent(e);
+}
+
+void KonversationMainWindow::leaveEvent(QEvent* e)
+{
+ m_statusBar->clearMainLabelTempText();
+
+ KMainWindow::leaveEvent(e);
+}
+
+bool KonversationMainWindow::event(QEvent* e)
+{
+ if (e->type() == QEvent::WindowActivate)
+ {
+ emit endNotification();
+ emit cancelRememberLine();
+ }
+ else if(e->type() == QEvent::WindowDeactivate)
+ {
+ m_statusBar->clearMainLabelTempText();
+
+ if (kapp->activeModalWidget() == 0)
+ emit triggerRememberLine();
+ }
+
+ return KMainWindow::event(e);
+}
+
+void KonversationMainWindow::settingsChangedSlot()
+{
+ // This is for compressing the events. m_hasDirtySettings is set to true
+ // when the settings have changed, then set to false when the app reacts to it
+ // via the appearanceChanged signal. This prevents a series of settingsChanged signals
+ // causing the app expensively rereading its settings many times.
+ // The appearanceChanged signal is connected to resetHasDirtySettings to reset this bool
+ if (!m_hasDirtySettings)
+ {
+ QTimer::singleShot(0, KonversationApplication::instance(), SIGNAL(appearanceChanged()));
+ m_hasDirtySettings = true;
+ }
+}
+
+void KonversationMainWindow::resetHasDirtySettings()
+{
+ m_hasDirtySettings = false;
+}
+
+void KonversationMainWindow::updateTrayIcon()
+{
+ m_trayIcon->setNotificationEnabled(Preferences::trayNotify());
+
+ if (Preferences::showTrayIcon())
+ m_trayIcon->show();
+ else
+ m_trayIcon->hide();
+}
+
+void KonversationMainWindow::toggleMenubar(bool dontShowWarning)
+{
+ if (hideMenuBarAction->isChecked())
+ menuBar()->show();
+ else
+ {
+ if (!dontShowWarning)
+ {
+ QString accel = hideMenuBarAction->shortcut().toString();
+ KMessageBox::information(this,
+ i18n("<qt>This will hide the menu bar completely. You can show it again by typing %1.</qt>").arg(accel),
+ "Hide menu bar","HideMenuBarWarning");
+ }
+ menuBar()->hide();
+ }
+
+ Preferences::setShowMenuBar(hideMenuBarAction->isChecked());
+}
+
+int KonversationMainWindow::configureToolbar()
+{
+ saveMainWindowSettings(KGlobal::config());
+ KEditToolbar dlg(actionCollection(), xmlFile(), true, this);
+ connect(&dlg, SIGNAL(newToolbarConfig()), SLOT(saveToolbarConfig()));
+ return dlg.exec();
+}
+
+void KonversationMainWindow::saveToolbarConfig()
+{
+ createGUI(xmlFile(), false);
+ applyMainWindowSettings(KGlobal::config());
+}
+
+void KonversationMainWindow::focusAndShowErrorMessage(const QString &errorMsg)
+{
+ show();
+ KWin::demandAttention(winId());
+ KWin::activateWindow(winId());
+ KMessageBox::error(this, errorMsg);
+}
+
+void KonversationMainWindow::openPrefsDialog()
+{
+ //An instance of your dialog could be already created and could be cached,
+ //in which case you want to display the cached dialog instead of creating
+ //another one
+ if (!m_settingsDialog)
+ {
+ m_settingsDialog = new KonviSettingsDialog(this);
+ //User edited the configuration - update your local copies of the
+ //configuration data
+ connect(m_settingsDialog, SIGNAL(settingsChanged()), this, SLOT(settingsChangedSlot()));
+ }
+ m_settingsDialog->show();
+}
+
+void KonversationMainWindow::openKeyBindings()
+{
+ // Change a number of action names to make them friendlier for the shortcut list.
+ actionCollection()->action("tab_notifications")->setText(i18n("Toggle Notifications"));
+ actionCollection()->action("toggle_away")->setText(i18n("Toggle Away Globally"));
+ actionCollection()->action("irc_colors")->setText(i18n("Insert &IRC Color..."));
+ actionCollection()->action("insert_character")->setText(i18n("Insert Special &Character..."));
+ actionCollection()->action("insert_marker_line")->setText(i18n("Insert &Marker Line"));
+ QString openChannelListString = actionCollection()->action("open_channel_list")->text();
+ actionCollection()->action("open_channel_list")->setText(i18n("&Channel List"));
+ QString openLogFileString = actionCollection()->action("open_logfile")->text();
+ actionCollection()->action("open_logfile")->setText(i18n("&Open Logfile"));
+
+ // Open shortcut configuration dialog.
+ KKeyDialog::configure(actionCollection());
+
+ // Reset action names.
+ actionCollection()->action("tab_notifications")->setText(i18n("Enable Notifications"));
+ actionCollection()->action("toggle_away")->setText(i18n("Set &Away Globally"));
+ actionCollection()->action("irc_colors")->setText(i18n("&IRC Color..."));
+ actionCollection()->action("insert_character")->setText(i18n("Special &Character..."));
+ actionCollection()->action("insert_marker_line")->setText(i18n("&Marker Line"));
+ actionCollection()->action("open_channel_list")->setText(openChannelListString);
+ actionCollection()->action("open_logfile")->setText(openLogFileString);
+}
+
+void KonversationMainWindow::openServerList()
+{
+ if (!m_serverListDialog)
+ {
+ m_serverListDialog = new Konversation::ServerListDialog(this);
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ connect(m_serverListDialog, SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)),
+ konvApp, SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)));
+ connect(konvApp, SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)),
+ m_serverListDialog, SLOT(updateServerList()));
+ connect(m_serverListDialog, SIGNAL(connectTo(Konversation::ConnectionFlag, int)),
+ konvApp->getConnectionManager(), SLOT(connectTo(Konversation::ConnectionFlag, int)));
+ connect(m_serverListDialog, SIGNAL(connectTo(Konversation::ConnectionFlag, ConnectionSettings&)),
+ konvApp->getConnectionManager(), SLOT(connectTo(Konversation::ConnectionFlag, ConnectionSettings&)));
+ connect(konvApp->getConnectionManager(), SIGNAL(closeServerList()), m_serverListDialog, SLOT(slotClose()));
+ }
+
+ m_serverListDialog->show();
+}
+
+void KonversationMainWindow::openQuickConnectDialog()
+{
+ emit showQuickConnectDialog();
+}
+
+// open the preferences dialog and show the watched nicknames page
+void KonversationMainWindow::openNotify()
+{
+ openPrefsDialog();
+ if (m_settingsDialog) m_settingsDialog->openWatchedNicknamesPage();
+}
+
+void KonversationMainWindow::openIdentitiesDialog()
+{
+ Konversation::IdentityDialog dlg(this);
+
+ if (dlg.exec() == KDialog::Accepted)
+ {
+ if (m_serverListDialog)
+ m_serverListDialog->updateServerList();
+ m_viewContainer->updateViewEncoding(m_viewContainer->getFrontView());
+ }
+}
+
+IdentityPtr KonversationMainWindow::editIdentity(IdentityPtr identity)
+{
+ IdentityPtr newIdentity;
+
+ Konversation::IdentityDialog dlg(this);
+ newIdentity = dlg.setCurrentIdentity(identity);
+
+ if ((dlg.exec() == KDialog::Accepted) && m_serverListDialog)
+ {
+ m_serverListDialog->updateServerList();
+ return newIdentity;
+ }
+ else
+ return 0;
+}
+
+void KonversationMainWindow::openNotifications()
+{
+ #ifdef USE_KNOTIFY
+ (void) KNotifyDialog::configure(this);
+ #endif
+}
+
+void KonversationMainWindow::notifyAction(const QString& serverName, const QString& nick)
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ Server* server = konvApp->getConnectionManager()->getServerByName(serverName);
+ if (server) server->notifyAction(nick);
+}
+
+// TODO: Let an own class handle notify things
+void KonversationMainWindow::setOnlineList(Server* notifyServer,const QStringList& /*list*/, bool /*changed*/)
+{
+ emit nicksNowOnline(notifyServer);
+ // FIXME if (changed && nicksOnlinePanel) newText(nicksOnlinePanel, QString::null, true);
+}
+
+QString KonversationMainWindow::currentURL(bool passNetwork)
+{
+ return m_viewContainer->currentViewURL(passNetwork);
+}
+
+QString KonversationMainWindow::currentTitle()
+{
+ return m_viewContainer->currentViewTitle();
+}
+
+#include "konversationmainwindow.moc"
diff --git a/konversation/src/konversationmainwindow.h b/konversation/src/konversationmainwindow.h
new file mode 100644
index 0000000..5781e9f
--- /dev/null
+++ b/konversation/src/konversationmainwindow.h
@@ -0,0 +1,148 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef _KONVERSATIONMAINWINDOW_H_
+#define _KONVERSATIONMAINWINDOW_H_
+
+#include "channel.h"
+#include "preferences.h"
+#include "ssllabel.h"
+#include "nickinfo.h"
+#include "server.h"
+
+#include <qstringlist.h>
+
+#include <kmainwindow.h>
+#include <kaction.h>
+
+
+class KToggleAction;
+class KScriptManager;
+
+class KonviBookmarkHandler;
+class Ignore;
+class NicksOnline;
+class QuickButtonsDialog;
+class KonviSettingsDialog;
+class ViewContainer;
+class KonversationStatusBar;
+
+namespace Konversation
+{
+ class ServerListDialog;
+ class TrayIcon;
+}
+
+class KonversationMainWindow : public KMainWindow
+{
+ Q_OBJECT
+
+ public:
+ KonversationMainWindow();
+ ~KonversationMainWindow();
+
+ ViewContainer* getViewContainer() { return m_viewContainer; }
+ Konversation::TrayIcon* systemTrayIcon() const { return m_trayIcon; }
+
+ /** Some errors need to be shown, even when konversation is minimized.
+ * For example, when a kimiface call is received to query a person,
+ * (e.g. the user choses "Chat with X" in kmail) but that person isn't
+ * recognised, we need to give immediate feedback to the user.
+ */
+ void focusAndShowErrorMessage(const QString &errorMsg);
+
+ QString currentURL(bool passNetwork);
+ QString currentTitle();
+
+ signals:
+ void startNotifyTimer(int msec);
+ void showQuickConnectDialog();
+ void nicksNowOnline(Server*);
+ void endNotification();
+ void serverStateChanged(Server* server, Konversation::ConnectionState state);
+ void triggerRememberLine();
+ void triggerRememberLines(Server*);
+ void cancelRememberLine();
+ void insertMarkerLine();
+
+ public slots:
+ void updateTrayIcon();
+
+ void openServerList();
+
+ void openIdentitiesDialog();
+ IdentityPtr editIdentity(IdentityPtr identity);
+
+ void setOnlineList(Server* notifyServer,const QStringList& list, bool changed);
+
+ protected slots:
+ /** This is connected to the preferences settingsChanged signal and acts to compress
+ * multiple successively settingsChanged() signals into a single output
+ * appearanceChanged() signal.
+ *
+ * Do not connect to the settingsChanged signal elsewhere. If you want to know when
+ * the settings have changed, connect to:
+ * KonversationApplication::instance(), SIGNAL(appearanceChanged())
+ */
+ void settingsChangedSlot();
+
+ /** This is connected to the appearanceChanged signal.
+ * @see settingsChangedSlot()
+ */
+ void resetHasDirtySettings();
+
+ void toggleMenubar(bool dontShowWarning = false);
+
+ int configureToolbar();
+ void saveToolbarConfig();
+
+ void openPrefsDialog();
+ void openKeyBindings();
+ void openQuickConnectDialog();
+
+ void openNotify();
+ // it seems that moc does not honor #ifs in compile so we create an
+ // empty slot in our .cpp file rather than #if this slot out
+ void openNotifications();
+ void notifyAction(const QString& serverName,const QString& nick);
+
+ void quitProgram();
+ void showEvent(QShowEvent* e);
+ void hideEvent(QHideEvent* e);
+ void leaveEvent(QEvent* e);
+
+
+ protected:
+ int confirmQuit();
+ bool queryClose();
+ virtual bool event(QEvent* e);
+
+ ViewContainer* m_viewContainer;
+ KonversationStatusBar* m_statusBar;
+ Konversation::TrayIcon* m_trayIcon;
+
+ KToggleAction* hideMenuBarAction;
+
+ KPopupMenu* m_bookmarks;
+ KonviBookmarkHandler* m_bookmarkHandler;
+ KonviSettingsDialog *m_settingsDialog;
+ Konversation::ServerListDialog* m_serverListDialog;
+
+ /** @see settingsChangedSlot() */
+ bool m_hasDirtySettings;
+ bool m_closeApp;
+};
+
+#endif
diff --git a/konversation/src/konversationsound.cpp b/konversation/src/konversationsound.cpp
new file mode 100644
index 0000000..7eb6466
--- /dev/null
+++ b/konversation/src/konversationsound.cpp
@@ -0,0 +1,41 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Thu Jan 29 2004
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#include "konversationsound.h"
+
+#include <config.h>
+#include <kurl.h>
+
+#ifdef USE_KNOTIFY
+#include <knotifyclient.h>
+#endif
+
+
+namespace Konversation
+{
+ Sound::Sound(QObject* parent, const char* name)
+ : QObject(parent, name)
+ {}
+
+ Sound::~Sound()
+ {}
+
+ void Sound::play(const KURL& url)
+ {
+ #ifdef USE_KNOTIFY
+ KNotifyClient::userEvent(0,QString(),1,1,url.path());
+ #endif
+ }
+}
+
+#include "konversationsound.moc"
diff --git a/konversation/src/konversationsound.h b/konversation/src/konversationsound.h
new file mode 100644
index 0000000..145b75b
--- /dev/null
+++ b/konversation/src/konversationsound.h
@@ -0,0 +1,41 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Thu Jan 29 2004
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#ifndef KONVERSATIONKONVERSATIONSOUND_H
+#define KONVERSATIONKONVERSATIONSOUND_H
+
+#include <qobject.h>
+
+
+class KURL;
+
+namespace Konversation
+{
+
+ /**
+ Class that handles sounds
+ */
+ class Sound : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ explicit Sound(QObject *parent = 0, const char *name = 0);
+ ~Sound();
+
+ public slots:
+ void play(const KURL& url);
+
+ };
+}
+#endif
diff --git a/konversation/src/konversationstatusbar.cpp b/konversation/src/konversationstatusbar.cpp
new file mode 100644
index 0000000..db29a06
--- /dev/null
+++ b/konversation/src/konversationstatusbar.cpp
@@ -0,0 +1,242 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "konversationstatusbar.h"
+#include "konversationmainwindow.h"
+#include "viewcontainer.h"
+#include "ssllabel.h"
+
+#include <qwhatsthis.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <ksqueezedtextlabel.h>
+
+
+KonversationStatusBar::KonversationStatusBar(KonversationMainWindow* window)
+{
+ m_window = window;
+
+ // Initialize status bar.
+ m_window->statusBar();
+
+ m_mainLabel = new KSqueezedTextLabel(m_window->statusBar(),"mainLabel");
+ setMainLabelText(i18n("Ready."));
+ m_mainLabel->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ m_mainLabel->setMinimumWidth(0);
+
+ // KSqueezedLabel calculates the wrong height. Popular workaround.
+ int height = m_window->fontMetrics().height()+2;
+ m_mainLabel->setFixedHeight(height);
+
+ m_infoLabel = new QLabel(m_window->statusBar(), "infoLabel");
+ m_infoLabel->hide();
+ QWhatsThis::add(m_infoLabel, i18n("<qt>This shows the number of users in the channel, and the number of those that are operators (ops).<p>A channel operator is a user that has special privileges, such as the ability to kick and ban users, change the channel modes, make other users operators</qt>"));
+
+ m_lagLabel = new QLabel(i18n("Lag: Unknown"), m_window->statusBar(), "lagLabel");
+ m_lagLabel->hide();
+
+ m_sslLabel = new SSLLabel(m_window->statusBar(),"sslLabel");
+ m_sslLabel->setPixmap(SmallIcon("encrypted"));
+ m_sslLabel->hide();
+ QWhatsThis::add(m_sslLabel, i18n("All communication with the server is encrypted. This makes it harder for someone to listen in on your communications."));
+
+ m_window->statusBar()->addWidget(m_mainLabel, 1, false);
+ m_window->statusBar()->addWidget(m_infoLabel, 0, true);
+ m_window->statusBar()->addWidget(m_lagLabel, 0, true);
+ m_window->statusBar()->addWidget(m_sslLabel, 0, true);
+
+ QWhatsThis::add(m_window->statusBar(), i18n("<qt>The status bar shows various messages, including any problems connecting to the server. On the far right the current delay to the server is shown. The delay is the time it takes for messages from you to reach the server, and from the server back to you.</qt>"));
+}
+
+KonversationStatusBar::~KonversationStatusBar()
+{
+}
+
+void KonversationStatusBar::updateAppearance()
+{
+ // KSqueezedLabel calculates the wrong height. Popular workaround.
+ int height = m_window->fontMetrics().height()+2;
+ m_mainLabel->setFixedHeight(height);
+}
+
+void KonversationStatusBar::resetStatusBar()
+{
+ setMainLabelText(i18n("Ready."));
+ setInfoLabelShown(false);
+ setLagLabelShown(false);
+ clearInfoLabel();
+ resetLagLabel();
+}
+
+void KonversationStatusBar::setMainLabelText(const QString& text)
+{
+ m_oldMainLabelText = text;
+
+ // Don't overwrite the temp text if there is any.
+ if (m_tempMainLabelText.isEmpty())
+ m_mainLabel->setText(text);
+}
+
+void KonversationStatusBar::setMainLabelTempText(const QString& text)
+{
+ if (!text.isEmpty())
+ {
+ m_tempMainLabelText = text;
+ m_mainLabel->setText(text);
+ }
+ else
+ clearMainLabelTempText();
+}
+
+void KonversationStatusBar::clearMainLabelTempText()
+{
+ // Unset the temp text so the next setMainLabelText won't fail.
+ m_tempMainLabelText = QString();
+
+ m_mainLabel->setText(m_oldMainLabelText);
+}
+
+void KonversationStatusBar::setInfoLabelShown(bool shown)
+{
+ if (shown)
+ m_infoLabel->show();
+ else
+ m_infoLabel->hide();
+}
+
+void KonversationStatusBar::updateInfoLabel(const QString& text)
+{
+ QString formatted = Konversation::removeIrcMarkup(text);
+ m_infoLabel->setText(formatted);
+
+ if (m_infoLabel->isHidden()) m_infoLabel->show();
+}
+
+void KonversationStatusBar::clearInfoLabel()
+{
+ m_infoLabel->setText(QString());
+}
+
+void KonversationStatusBar::setLagLabelShown(bool shown)
+{
+ if (shown)
+ m_lagLabel->show();
+ else
+ m_lagLabel->hide();
+}
+
+void KonversationStatusBar::updateLagLabel(Server* lagServer, int msec)
+{
+ if (lagServer==m_window->getViewContainer()->getFrontServer())
+ {
+ setMainLabelText(i18n("Ready."));
+
+ QString lagString = lagServer->getServerName() + " - ";
+
+ if (msec == -1)
+ lagString += i18n("Lag: Unknown");
+ else if (msec < 1000)
+ lagString += i18n("Lag: %1 ms").arg(msec);
+ else
+ lagString += i18n("Lag: %1 s").arg(msec / 1000);
+
+ m_lagLabel->setText(lagString);
+
+ if (m_lagLabel->isHidden()) m_lagLabel->show();
+ }
+}
+
+void KonversationStatusBar::resetLagLabel()
+{
+ m_lagLabel->setText(i18n("Lag: Unknown"));
+}
+
+void KonversationStatusBar::setTooLongLag(Server* lagServer, int msec)
+{
+ if ((msec % 5000)==0)
+ {
+ int seconds = msec/1000;
+ int minutes = seconds/60;
+ int hours = minutes/60;
+ int days = hours/24;
+ QString lagString;
+
+ if (days)
+ {
+ const QString daysString = i18n("1 day", "%n days", days);
+ const QString hoursString = i18n("1 hour", "%n hours", (hours % 24));
+ const QString minutesString = i18n("1 minute", "%n minutes", (minutes % 60));
+ const QString secondsString = i18n("1 second", "%n seconds", (seconds % 60));
+ lagString = i18n("%1 = name of server, %2 = (x days), %3 = (x hours), %4 = (x minutes), %5 = (x seconds)", "No answer from server %1 for more than %2, %3, %4, and %5.").arg(lagServer->getServerName())
+ .arg(daysString).arg(hoursString).arg(minutesString).arg(secondsString);
+ // or longer than an hour
+ }
+ else if (hours)
+ {
+ const QString hoursString = i18n("1 hour", "%n hours", hours);
+ const QString minutesString = i18n("1 minute", "%n minutes", (minutes % 60));
+ const QString secondsString = i18n("1 second", "%n seconds", (seconds % 60));
+ lagString = i18n("%1 = name of server, %2 = (x hours), %3 = (x minutes), %4 = (x seconds)", "No answer from server %1 for more than %2, %3, and %4.").arg(lagServer->getServerName())
+ .arg(hoursString).arg(minutesString).arg(secondsString);
+ // or longer than a minute
+ }
+ else if (minutes)
+ {
+ const QString minutesString = i18n("1 minute", "%n minutes", minutes);
+ const QString secondsString = i18n("1 second", "%n seconds", (seconds % 60));
+ lagString = i18n("%1 = name of server, %2 = (x minutes), %3 = (x seconds)", "No answer from server %1 for more than %2 and %3.").arg(lagServer->getServerName())
+ .arg(minutesString).arg(secondsString);
+ // or just some seconds
+ }
+ else
+ {
+ lagString = i18n("No answer from server %1 for more than 1 second.", "No answer from server %1 for more than %n seconds.", seconds).arg(lagServer->getServerName());
+ }
+
+ setMainLabelText(lagString);
+ }
+
+ if (lagServer==m_window->getViewContainer()->getFrontServer())
+ {
+ QString lagString = lagServer->getServerName() + " - ";
+ lagString.append(i18n("Lag: %1 s").arg(msec/1000));
+
+ if (m_lagLabel->isHidden()) m_lagLabel->show();
+ m_lagLabel->setText(lagString);
+ }
+}
+
+void KonversationStatusBar::updateSSLLabel(Server* server)
+{
+ if (server == m_window->getViewContainer()->getFrontServer()
+ && server->getUseSSL() && server->isConnected())
+ {
+ disconnect(m_sslLabel,0,0,0);
+ connect(m_sslLabel,SIGNAL(clicked()),server,SLOT(showSSLDialog()));
+ QToolTip::remove(m_sslLabel);
+ QToolTip::add(m_sslLabel,server->getSSLInfo());
+ m_sslLabel->show();
+ }
+ else
+ m_sslLabel->hide();
+}
+
+void KonversationStatusBar::removeSSLLabel()
+{
+ disconnect(m_sslLabel,0,0,0);
+ m_sslLabel->hide();
+}
+
+#include "konversationstatusbar.moc"
diff --git a/konversation/src/konversationstatusbar.h b/konversation/src/konversationstatusbar.h
new file mode 100644
index 0000000..931f7d6
--- /dev/null
+++ b/konversation/src/konversationstatusbar.h
@@ -0,0 +1,67 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONVERSATIONSTATUSBAR_H
+#define KONVERSATIONSTATUSBAR_H
+
+#include <qobject.h>
+
+
+class QLabel;
+
+class KonversationMainWindow;
+class KSqueezedTextLabel;
+class SSLLabel;
+class Server;
+
+class KonversationStatusBar : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit KonversationStatusBar(KonversationMainWindow* parent);
+ ~KonversationStatusBar();
+
+ public slots:
+ void updateAppearance();
+
+ void resetStatusBar();
+
+ void setMainLabelText(const QString& text);
+
+ void setMainLabelTempText(const QString& text);
+ void clearMainLabelTempText();
+
+ void setInfoLabelShown(bool shown);
+ void updateInfoLabel(const QString& text);
+ void clearInfoLabel();
+
+ void setLagLabelShown(bool shown);
+ void updateLagLabel(Server* lagServer, int msec);
+ void resetLagLabel();
+ void setTooLongLag(Server* lagServer, int msec);
+
+ void updateSSLLabel(Server* server);
+ void removeSSLLabel();
+
+ private:
+ KonversationMainWindow* m_window;
+
+ KSqueezedTextLabel* m_mainLabel;
+ QLabel* m_infoLabel;
+ QLabel* m_lagLabel;
+ SSLLabel* m_sslLabel;
+
+ QString m_oldMainLabelText;
+ QString m_tempMainLabelText;
+};
+
+#endif
diff --git a/konversation/src/konversationui.rc b/konversation/src/konversationui.rc
new file mode 100644
index 0000000..66d3945
--- /dev/null
+++ b/konversation/src/konversationui.rc
@@ -0,0 +1,82 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="konversation" version="51">
+
+ <MenuBar>
+ <Menu name="file">
+ <Action name="open_server_list" />
+ <Action name="quick_connect_dialog" />
+ <Separator />
+ <Action name="disconnect_server" />
+ <Action name="reconnect_server" />
+ <Separator />
+ <Action name="join_channel" />
+ <Separator />
+ <Action name="toggle_away" />
+ </Menu>
+ <Menu name="edit">
+ <Action name="clear_lines" />
+ <Separator />
+ <Action name="clear_window" />
+ <Action name="clear_tabs" />
+ </Menu>
+ <Menu name="insert">
+ <Text>&amp;Insert</Text>
+ <Action name="irc_colors" />
+ <Action name="insert_marker_line" />
+ <Action name="insert_character" />
+ </Menu>
+ <Menu name="bookmarks">
+ <Text>&amp;Bookmarks</Text>
+ </Menu>
+ <Menu name="settings">
+ <Action name="hide_nicknamelist" />
+ <Separator />
+ <Action name="identities_dialog" append="save_merge" />
+ </Menu>
+ <Menu name="windows" append="settings_merge">
+ <Text>&amp;Window</Text>
+ <Action name="previous_tab" />
+ <Action name="next_tab" />
+ <Action name="next_active_tab" />
+ <Separator />
+ <Action name="move_tab_left" />
+ <Action name="move_tab_right" />
+ <Action name="close_tab" />
+ <Action name="close_queries" />
+ <Separator />
+ <Action name="tab_notifications" />
+ <Action name="tab_encoding" />
+ <Separator />
+ <Action name="open_nicksonline_window" />
+ <Action name="open_url_catcher" />
+ <Action name="open_dccstatus_window" />
+ <Action name="open_konsole" />
+ <Separator />
+ <Action name="open_channel_list" />
+ <Action name="open_logfile" />
+ <Action name="channel_settings" />
+ </Menu>
+ </MenuBar>
+
+ <ToolBar fullWidth="true" hidden="true" name="mainToolBar">
+ <Action name="open_server_list" />
+ <Action name="quick_connect_dialog" />
+ <Separator />
+ <Action name="toggle_away" />
+ <Separator />
+ <Action name="irc_colors" />
+ </ToolBar>
+
+ <Menu name="tabContextMenu">
+ <Action name="tab_notifications" />
+ <Action name="tab_encoding" />
+ <Separator />
+ <ActionList name="server_actions" />
+ <Separator />
+ <Action name="move_tab_left" />
+ <Action name="move_tab_right" />
+ <Action name="close_tab" />
+</Menu>
+
+</kpartgui>
+
diff --git a/konversation/src/konvibookmarkhandler.cpp b/konversation/src/konvibookmarkhandler.cpp
new file mode 100644
index 0000000..3258cba
--- /dev/null
+++ b/konversation/src/konvibookmarkhandler.cpp
@@ -0,0 +1,96 @@
+/*
+ Copyright (c) 2005 by İsmail Dönmez <ismail@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+Based on the code by:
+Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+*/
+
+#include "konvibookmarkhandler.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "connectionmanager.h"
+#include "konvibookmarkmenu.h"
+
+#include <qstring.h>
+
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+
+
+KonviBookmarkHandler::KonviBookmarkHandler(KonversationMainWindow* mainWindow)
+: QObject( mainWindow, "KonviBookmarkHandler" ),
+KBookmarkOwner(),
+m_mainWindow(mainWindow)
+{
+ m_menu = static_cast<KPopupMenu*>(mainWindow->factory()->container("bookmarks", mainWindow));
+
+ m_file = locate( "data", "konversation/bookmarks.xml" );
+
+ if ( m_file.isEmpty() )
+ m_file = locateLocal( "data", "konversation/bookmarks.xml" );
+
+ if(!m_menu)
+ {
+ m_bookmarkMenu = 0;
+ return;
+ }
+
+ KBookmarkManager *manager = KBookmarkManager::managerForFile( m_file, false);
+ manager->setEditorOptions(kapp->caption(), false);
+ manager->setUpdate( true );
+ manager->setShowNSBookmarks( false );
+
+ connect( manager, SIGNAL(changed(const QString &,const QString &)), SLOT(slotBookmarksChanged(const QString &,const QString &)));
+
+ m_bookmarkMenu = new KonviBookmarkMenu( manager, this, m_menu, NULL, true );
+}
+
+KonviBookmarkHandler::~KonviBookmarkHandler()
+{
+ delete m_bookmarkMenu;
+}
+
+void KonviBookmarkHandler::slotEditBookmarks()
+{
+ KProcess proc;
+ proc << QString::fromLatin1("keditbookmarks");
+ proc << "--nobrowser";
+ proc << "--caption" << i18n("Konversation Bookmarks Editor");
+ proc << m_file;
+ proc.start(KProcess::DontCare);
+}
+
+void KonviBookmarkHandler::slotBookmarksChanged( const QString &,
+const QString &)
+{
+ // This is called when someone changes bookmarks in konversation
+ m_bookmarkMenu->slotBookmarksChanged("");
+}
+
+void KonviBookmarkHandler::openBookmarkURL(const QString& url, const QString& /* title */)
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ konvApp->getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, url);
+}
+
+QString KonviBookmarkHandler::currentURL() const
+{
+ return m_mainWindow->currentURL(true);
+}
+
+QString KonviBookmarkHandler::currentTitle() const
+{
+ return m_mainWindow->currentTitle();
+}
+
+#include "konvibookmarkhandler.moc"
diff --git a/konversation/src/konvibookmarkhandler.h b/konversation/src/konvibookmarkhandler.h
new file mode 100644
index 0000000..0071195
--- /dev/null
+++ b/konversation/src/konvibookmarkhandler.h
@@ -0,0 +1,55 @@
+#ifndef KONVIBOOKMARKHANDLER_H
+#define KONVIBOOKMARKHANDLER_H
+
+/*
+ Copyright (c) 2005 by İsmail Dönmez <ismail@kde.org.tr>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+Based on the code by :
+Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+*/
+
+#include <kbookmarkmanager.h>
+
+
+class QString;
+class KPopupMenu;
+class KonversationMainWindow;
+class KonviBookmarkMenu;
+
+class KonviBookmarkHandler : public QObject, public KBookmarkOwner
+{
+ Q_OBJECT
+
+ public:
+ explicit KonviBookmarkHandler(KonversationMainWindow *mainWindow);
+ ~KonviBookmarkHandler();
+
+ KPopupMenu* popupMenu();
+
+ // KBookmarkOwner interface:
+ virtual void openBookmarkURL(const QString& url, const QString& title);
+ virtual QString currentURL() const;
+ virtual QString currentTitle() const;
+
+ private slots:
+ void slotBookmarksChanged(const QString &, const QString & caller);
+ void slotEditBookmarks();
+
+
+ private:
+ KonversationMainWindow* m_mainWindow;
+ KPopupMenu *m_menu;
+ KonviBookmarkMenu *m_bookmarkMenu;
+ QString m_file;
+};
+#endif // KONVIBOOKMARKHANDLER_H
diff --git a/konversation/src/konvibookmarkmenu.cpp b/konversation/src/konvibookmarkmenu.cpp
new file mode 100644
index 0000000..4474f6a
--- /dev/null
+++ b/konversation/src/konvibookmarkmenu.cpp
@@ -0,0 +1,154 @@
+/*
+ Copyright (c) 2005 by İsmail Dönmez <ismail@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+Based on the code by:
+Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+*/
+
+#include "konvibookmarkmenu.h"
+#include "konvibookmarkhandler.h"
+
+#include <qregexp.h>
+
+#include <kpopupmenu.h>
+#include <kaction.h>
+
+
+KonviBookmarkMenu::KonviBookmarkMenu( KBookmarkManager* mgr,
+KonviBookmarkHandler * _owner, KPopupMenu * _parentMenu,
+KActionCollection *collec, bool _isRoot, bool _add,
+const QString & parentAddress )
+: KBookmarkMenu( mgr, _owner, _parentMenu, collec, _isRoot, _add, parentAddress),
+m_kOwner(_owner)
+{
+ /*
+ * First, we disconnect KBookmarkMenu::slotAboutToShow()
+ * Then, we connect KonviBookmarkMenu::slotAboutToShow().
+ * They are named differently because the SLOT() macro thinks we want
+ * KonviBookmarkMenu::KBookmarkMenu::slotAboutToShow()
+ * Could this be solved if slotAboutToShow() is virtual in KBookmarMenu?
+ */
+ disconnect(_parentMenu, SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow()));
+ connect(_parentMenu, SIGNAL(aboutToShow()), SLOT(slotAboutToShow2()));
+}
+
+/*
+ * Duplicate this exactly because KBookmarkMenu::slotBookmarkSelected can't
+ * be overrided. I would have preferred to NOT have to do this.
+ *
+ * Why did I do this?
+ * - when KBookmarkMenu::fillbBookmarkMenu() creates sub-KBookmarkMenus.
+ * - when ... adds KActions, it uses KBookmarkMenu::slotBookmarkSelected()
+ * instead of KonviBookmarkMenu::slotBookmarkSelected().
+ */
+void KonviBookmarkMenu::slotAboutToShow2()
+{
+ // Did the bookmarks change since the last time we showed them ?
+ if ( m_bDirty )
+ {
+ m_bDirty = false;
+ refill();
+ }
+}
+
+void KonviBookmarkMenu::refill()
+{
+ m_lstSubMenus.clear();
+ QPtrListIterator<KAction> it( m_actions );
+ for (; it.current(); ++it )
+ it.current()->unplug( m_parentMenu );
+ m_parentMenu->clear();
+ m_actions.clear();
+ fillBookmarkMenu();
+ m_parentMenu->adjustSize();
+}
+
+void KonviBookmarkMenu::fillBookmarkMenu()
+{
+ if ( m_bIsRoot )
+ {
+ if ( m_bAddBookmark )
+ addAddBookmark();
+
+ addEditBookmarks();
+
+ if ( m_bAddBookmark )
+ addNewFolder();
+ }
+
+ KBookmarkGroup parentBookmark = m_pManager->findByAddress( m_parentAddress ).toGroup();
+ Q_ASSERT(!parentBookmark.isNull());
+ bool separatorInserted = false;
+ for ( KBookmark bm = parentBookmark.first(); !bm.isNull();
+ bm = parentBookmark.next(bm) )
+ {
+ QString text = bm.text();
+ text.replace( '&', "&&" );
+ if ( !separatorInserted && m_bIsRoot) // inserted before the first konq bookmark, to avoid the separator if no konq bookmark
+ {
+ m_parentMenu->insertSeparator();
+ separatorInserted = true;
+ }
+ if ( !bm.isGroup() )
+ {
+ if ( bm.isSeparator() )
+ {
+ m_parentMenu->insertSeparator();
+ }
+ else
+ {
+ // kdDebug(1203) << "Creating URL bookmark menu item for " << bm.text() << endl;
+ // create a normal URL item, with ID as a name
+ KAction * action = new KAction( text, bm.icon(), 0,
+ this, SLOT( slotBookmarkSelected() ),
+ m_actionCollection, bm.url().url().utf8() );
+
+ action->setStatusText( bm.url().prettyURL() );
+
+ action->plug( m_parentMenu );
+ m_actions.append( action );
+ }
+ }
+ else
+ {
+ // kdDebug(1203) << "Creating bookmark submenu named " << bm.text() << endl;
+ KActionMenu * actionMenu = new KActionMenu( text, bm.icon(),
+ m_actionCollection, 0L );
+ actionMenu->plug( m_parentMenu );
+ m_actions.append( actionMenu );
+ KonviBookmarkMenu *subMenu = new KonviBookmarkMenu( m_pManager,
+ m_kOwner, actionMenu->popupMenu(),
+ m_actionCollection, false,
+ m_bAddBookmark, bm.address() );
+ m_lstSubMenus.append( subMenu );
+ }
+ }
+
+ if ( !m_bIsRoot && m_bAddBookmark )
+ {
+ if ( m_parentMenu->count() > 0 )
+ m_parentMenu->insertSeparator();
+ addAddBookmark();
+ addNewFolder();
+ }
+}
+
+void KonviBookmarkMenu::slotBookmarkSelected()
+{
+ if ( !m_pOwner ) return; // this view doesn't handle bookmarks...
+ /* URL */
+ m_kOwner->openBookmarkURL( QString::fromUtf8(sender()->name()),
+ ( (KAction *)sender() )->text() /* Title */ );
+}
+
+#include "konvibookmarkmenu.moc"
diff --git a/konversation/src/konvibookmarkmenu.h b/konversation/src/konvibookmarkmenu.h
new file mode 100644
index 0000000..f7921c2
--- /dev/null
+++ b/konversation/src/konvibookmarkmenu.h
@@ -0,0 +1,65 @@
+#ifndef KONVIBOOKMARKMENU_H
+#define KONVIBOOKMARKMENU_H
+
+/*
+ Copyright (c) 2005 by İsmail Dönmez <ismail@kde.org.tr>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+Based on the code by:
+Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+*/
+
+#include <qobject.h>
+
+#include <kbookmarkmenu.h>
+
+
+class QString;
+class KBookmark;
+class KAction;
+class KActionMenu;
+class KActionCollection;
+class KBookmarkOwner;
+class KBookmarkMenu;
+class KPopupMenu;
+class KonviBookmarkHandler;
+class KonviBookmarkMenu;
+class KonviBookmarkMenuPrivate;
+
+class KonviBookmarkMenu : public KBookmarkMenu
+{
+ Q_OBJECT
+
+ public:
+ KonviBookmarkMenu( KBookmarkManager* mgr,
+ KonviBookmarkHandler * _owner, KPopupMenu * _parentMenu,
+ KActionCollection *collec, bool _isRoot,
+ bool _add = true, const QString & parentAddress = "");
+
+ void fillBookmarkMenu();
+
+ public slots:
+
+ private:
+
+ protected slots:
+ void slotAboutToShow2();
+ void slotBookmarkSelected();
+
+ protected:
+ void refill();
+
+ private:
+ KonviBookmarkHandler * m_kOwner;
+ KonviBookmarkMenuPrivate *d;
+};
+#endif // KONVIBOOKMARKMENU_H
diff --git a/konversation/src/konviconfigdialog.cpp b/konversation/src/konviconfigdialog.cpp
new file mode 100644
index 0000000..386236d
--- /dev/null
+++ b/konversation/src/konviconfigdialog.cpp
@@ -0,0 +1,284 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net)
+ * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+ * Copyright (C) 2004 Michael Brade <brade@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * KConfigDialog derivative allowing for a multi-level hierarchical TreeList.
+ * Differences from KConfigDialog:
+ * - Use QStringList instead of QString for the item name(s) in addPage and
+ * addPageInternal, thus calling the respective KDialogBase methods which
+ * allow specifying a path from which the TreeList hierarchy is constructed.
+ * - Use 16x16 icons in the TreeList.
+ * - Fill a new int m_lastAddedIndex with the pageIndex() of a new page added
+ * with addPageInternal, and offer a public interface int lastAddedIndex().
+ * See the KConfigDialog reference for detailed documentation.
+ *
+ * begin: Nov 22 2005
+ * copyright: (C) 2005-2006 by Eike Hein, KConfigDialog developers
+ * email: hein@kde.org
+ */
+
+#include "konviconfigdialog.h"
+
+#include <kconfigskeleton.h>
+#include <kconfigdialogmanager.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+
+#include <qlayout.h>
+#include <qvbox.h>
+#include <qmap.h>
+
+
+QAsciiDict<KonviConfigDialog> KonviConfigDialog::openDialogs;
+
+// This class is here purly so we don't break binary compatibility down the road.
+class KonviConfigDialog::KConfigDialogPrivate
+{
+ public:
+ KConfigDialogPrivate(KDialogBase::DialogType t) : shown(false), type(t), manager(0) { }
+
+ bool shown;
+ KDialogBase::DialogType type;
+ KConfigDialogManager *manager;
+ QMap<QWidget *, KConfigDialogManager *> managerForPage;
+};
+
+KonviConfigDialog::KonviConfigDialog( QWidget *parent, const char *name,
+ KConfigSkeleton *config,
+ DialogType dialogType,
+ int dialogButtons,
+ ButtonCode defaultButton,
+ bool modal ) :
+ KDialogBase( dialogType, Qt::WStyle_DialogBorder,
+ parent, name, modal, i18n("Configure"), dialogButtons, defaultButton ),
+ d(new KConfigDialogPrivate(dialogType))
+{
+ if ( name )
+ {
+ openDialogs.insert(name, this);
+ }
+ else
+ {
+ QCString genericName;
+ genericName.sprintf("SettingsDialog-%p", this);
+ openDialogs.insert(genericName, this);
+ setName(genericName);
+ }
+
+ connect(this, SIGNAL(okClicked()), this, SLOT(updateSettings()));
+ connect(this, SIGNAL(applyClicked()), this, SLOT(updateSettings()));
+ connect(this, SIGNAL(applyClicked()), this, SLOT(updateButtons()));
+ connect(this, SIGNAL(defaultClicked()), this, SLOT(updateWidgetsDefault()));
+ connect(this, SIGNAL(defaultClicked()), this, SLOT(updateButtons()));
+
+ d->manager = new KConfigDialogManager(this, config);
+ setupManagerConnections(d->manager);
+
+ enableButton(Apply, false);
+}
+
+KonviConfigDialog::~KonviConfigDialog()
+{
+ openDialogs.remove(name());
+ delete d;
+}
+
+void KonviConfigDialog::addPage(QWidget *page,
+ const QStringList &items,
+ const QString &pixmapName,
+ const QString &header,
+ bool manage)
+{
+ addPageInternal(page, items, pixmapName, header);
+ if(manage)
+ d->manager->addWidget(page);
+}
+
+void KonviConfigDialog::addPage(QWidget *page,
+ KConfigSkeleton *config,
+ const QStringList &items,
+ const QString &pixmapName,
+ const QString &header)
+{
+ addPageInternal(page, items, pixmapName, header);
+ d->managerForPage[page] = new KConfigDialogManager(page, config);
+ setupManagerConnections(d->managerForPage[page]);
+}
+
+void KonviConfigDialog::addPageInternal(QWidget *page,
+ const QStringList &items,
+ const QString &pixmapName,
+ const QString &header)
+{
+ if(d->shown)
+ {
+ kdDebug(240) << "KonviConfigDialog::addPage: can not add a page after the dialog has been shown.";
+ return;
+ }
+ switch(d->type)
+ {
+ case TreeList:
+ case IconList:
+ case Tabbed:
+ {
+ QVBox *frame = addVBoxPage(items, header, SmallIcon(pixmapName, 16));
+ frame->setSpacing( 0 );
+ frame->setMargin( 0 );
+ page->reparent(((QWidget*)frame), 0, QPoint());
+ m_lastAddedIndex = pageIndex(frame);
+ }
+ break;
+
+ case Swallow:
+ {
+ page->reparent(this, 0, QPoint());
+ setMainWidget(page);
+ }
+ break;
+
+ case Plain:
+ {
+ QFrame *main = plainPage();
+ QVBoxLayout *topLayout = new QVBoxLayout( main, 0, 0 );
+ page->reparent(((QWidget*)main), 0, QPoint());
+ topLayout->addWidget( page );
+ }
+ break;
+
+ default:
+ kdDebug(240) << "KonviConfigDialog::addpage: unknown type.";
+ }
+}
+
+void KonviConfigDialog::setupManagerConnections(KConfigDialogManager *manager)
+{
+ connect(manager, SIGNAL(settingsChanged()), this, SLOT(settingsChangedSlot()));
+ connect(manager, SIGNAL(widgetModified()), this, SLOT(updateButtons()));
+
+ connect(this, SIGNAL(okClicked()), manager, SLOT(updateSettings()));
+ connect(this, SIGNAL(applyClicked()), manager, SLOT(updateSettings()));
+ connect(this, SIGNAL(defaultClicked()), manager, SLOT(updateWidgetsDefault()));
+}
+
+KonviConfigDialog* KonviConfigDialog::exists(const char* name)
+{
+ return openDialogs.find(name);
+}
+
+bool KonviConfigDialog::showDialog(const char* name)
+{
+ KonviConfigDialog *dialog = exists(name);
+ if(dialog)
+ dialog->show();
+ return (dialog != NULL);
+}
+
+void KonviConfigDialog::updateButtons()
+{
+ static bool only_once = false;
+ if (only_once) return;
+ only_once = true;
+
+ QMap<QWidget *, KConfigDialogManager *>::iterator it;
+
+ bool has_changed = d->manager->hasChanged() || hasChanged();
+ for (it = d->managerForPage.begin();
+ it != d->managerForPage.end() && !has_changed;
+ ++it)
+ {
+ has_changed |= (*it)->hasChanged();
+ }
+
+ enableButton(Apply, has_changed);
+
+ bool is_default = d->manager->isDefault() && isDefault();
+ for (it = d->managerForPage.begin();
+ it != d->managerForPage.end() && is_default;
+ ++it)
+ {
+ is_default &= (*it)->isDefault();
+ }
+
+ enableButton(Default, !is_default);
+
+ emit widgetModified();
+ only_once = false;
+}
+
+void KonviConfigDialog::settingsChangedSlot()
+{
+ // Update the buttons
+ updateButtons();
+ emit settingsChanged();
+ emit settingsChanged(name());
+}
+
+void KonviConfigDialog::show()
+{
+ QMap<QWidget *, KConfigDialogManager *>::iterator it;
+
+ updateWidgets();
+ d->manager->updateWidgets();
+ for (it = d->managerForPage.begin(); it != d->managerForPage.end(); ++it)
+ (*it)->updateWidgets();
+
+ bool has_changed = d->manager->hasChanged() || hasChanged();
+ for (it = d->managerForPage.begin();
+ it != d->managerForPage.end() && !has_changed;
+ ++it)
+ {
+ has_changed |= (*it)->hasChanged();
+ }
+
+ enableButton(Apply, has_changed);
+
+ bool is_default = d->manager->isDefault() && isDefault();
+ for (it = d->managerForPage.begin();
+ it != d->managerForPage.end() && is_default;
+ ++it)
+ {
+ is_default &= (*it)->isDefault();
+ }
+
+ enableButton(Default, !is_default);
+ d->shown = true;
+ KDialogBase::show();
+}
+
+int KonviConfigDialog::lastAddedIndex()
+{
+ return m_lastAddedIndex;
+}
+
+void KonviConfigDialog::updateSettings()
+{
+}
+
+void KonviConfigDialog::updateWidgets()
+{
+}
+
+void KonviConfigDialog::updateWidgetsDefault()
+{
+}
+
diff --git a/konversation/src/konviconfigdialog.h b/konversation/src/konviconfigdialog.h
new file mode 100644
index 0000000..b53262b
--- /dev/null
+++ b/konversation/src/konviconfigdialog.h
@@ -0,0 +1,124 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net)
+ * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+ * Copyright (C) 2004 Michael Brade <brade@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * KConfigDialog derivative allowing for a multi-level hierarchical TreeList.
+ * Differences from KConfigDialog:
+ * - Use QStringList instead of QString for the item name(s) in addPage and
+ * addPageInternal, thus calling the respective KDialogBase methods which
+ * allow specifying a path from which the TreeList hierarchy is constructed.
+ * - Use 16x16 icons in the TreeList.
+ * - Fill a new int m_lastAddedIndex with the pageIndex() of a new page added
+ * with addPageInternal, and offer a public interface int lastAddedIndex().
+ * See the KConfigDialog reference for detailed documentation.
+ *
+ * begin: Nov 22 2005
+ * copyright: (C) 2005-2006 by Eike Hein, KConfigDialog developers
+ * email: hein@kde.org
+ */
+
+#ifndef KONVICONFIGDIALOG_H
+#define KONVICONFIGDIALOG_H
+
+#include <qasciidict.h>
+
+#include <kdialogbase.h>
+
+
+class KConfig;
+class KConfigSkeleton;
+class KConfigDialogManager;
+
+class KonviConfigDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ signals:
+ void widgetModified();
+
+ void settingsChanged();
+
+ void settingsChanged(const char *dialogName);
+
+ void sigUpdateWidgets();
+
+ public:
+ KonviConfigDialog( QWidget *parent, const char *name,
+ KConfigSkeleton *config,
+ DialogType dialogType = IconList,
+ int dialogButtons = Default|Ok|Apply|Cancel|Help,
+ ButtonCode defaultButton = Ok,
+ bool modal=false );
+
+ ~KonviConfigDialog();
+
+ void addPage( QWidget *page, const QStringList &items,
+ const QString &pixmapName,
+ const QString &header=QString(),
+ bool manage=true );
+
+ void addPage( QWidget *page, KConfigSkeleton *config,
+ const QStringList &items,
+ const QString &pixmapName,
+ const QString &header=QString() );
+
+ static KonviConfigDialog* exists( const char* name );
+
+ static bool showDialog( const char* name );
+
+ virtual void show();
+
+ int lastAddedIndex();
+
+ protected slots:
+ virtual void updateSettings();
+
+ virtual void updateWidgets();
+
+ virtual void updateWidgetsDefault();
+
+ protected:
+ virtual bool hasChanged() { return false; }
+
+ virtual bool isDefault() { return true; }
+
+ protected slots:
+ void updateButtons();
+
+ void settingsChangedSlot();
+
+ private:
+ void addPageInternal(QWidget *page, const QStringList &items,
+ const QString &pixmapName, const QString &header);
+
+ void setupManagerConnections(KConfigDialogManager *manager);
+
+ private:
+ static QAsciiDict<KonviConfigDialog> openDialogs;
+
+ class KConfigDialogPrivate;
+
+ KConfigDialogPrivate *d;
+
+ int m_lastAddedIndex;
+};
+#endif //KONVICONFIGDIALOG_H
diff --git a/konversation/src/konviface.h b/konversation/src/konviface.h
new file mode 100644
index 0000000..945bd33
--- /dev/null
+++ b/konversation/src/konviface.h
@@ -0,0 +1,89 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2007-2007 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONV_IFACE_H
+#define KONV_IFACE_H
+
+#include "ignore.h"
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+#include <dcopobject.h>
+
+
+class KonvIface : virtual public DCOPObject
+{
+ K_DCOP
+
+ k_dcop:
+ virtual void setAway(const QString &awaymessage) = 0;
+ virtual void setBack() = 0;
+ virtual void sayToAll(const QString &message) = 0;
+ virtual void actionToAll(const QString &message) = 0;
+
+ virtual void raw(const QString& server,const QString& command) = 0;
+ virtual void say(const QString& server,const QString& target,const QString& command) = 0;
+ virtual void info(const QString& string) = 0;
+ virtual void debug(const QString& string) = 0;
+ virtual void error(const QString& string) = 0;
+ virtual void insertMarkerLine() = 0;
+ virtual void connectToServer(const QString& address, int port, const QString& channel, const QString& password) = 0;
+ virtual QString getNickname (const QString &serverid) = 0;
+ virtual QString getAnyNickname () = 0;
+ virtual QStringList listServers() = 0;
+ virtual QStringList listConnectedServers() = 0;
+ virtual QString getChannelEncoding(const QString& server, const QString& channel) = 0;
+
+ virtual void setScreenSaverStarted() = 0;
+ virtual void setScreenSaverStopped() = 0;
+};
+
+class KonvIdentityIface : virtual public DCOPObject
+{
+ K_DCOP
+ k_dcop:
+
+ virtual void setrealName(const QString &identity, const QString& name) = 0;
+ virtual QString getrealName(const QString &identity) = 0;
+ virtual void setIdent(const QString &identity, const QString& ident) = 0;
+ virtual QString getIdent(const QString &identity) = 0;
+
+ virtual void setNickname(const QString &identity, int index,const QString& nick) = 0;
+ virtual QString getNickname(const QString &identity, int index) = 0;
+
+ virtual void setBot(const QString &identity, const QString& bot) = 0;
+ virtual QString getBot(const QString &identity) = 0;
+ virtual void setPassword(const QString &identity, const QString& password) = 0;
+ virtual QString getPassword(const QString &identity) = 0;
+
+ virtual void setNicknameList(const QString &identity, const QStringList& newList) = 0;
+ virtual QStringList getNicknameList(const QString &identity) = 0;
+
+ virtual void setQuitReason(const QString &identity, const QString& reason) = 0;
+ virtual QString getQuitReason(const QString &identity) = 0;
+ virtual void setPartReason(const QString &identity, const QString& reason) = 0;
+ virtual QString getPartReason(const QString &identity) = 0;
+ virtual void setKickReason(const QString &identity, const QString& reason) = 0;
+ virtual QString getKickReason(const QString &identity) = 0;
+
+ virtual void setShowAwayMessage(const QString &identity, bool state) = 0;
+ virtual bool getShowAwayMessage(const QString &identity) = 0;
+
+ virtual void setAwayMessage(const QString &identity, const QString& message) = 0;
+ virtual QString getAwayMessage(const QString &identity) = 0;
+ virtual void setReturnMessage(const QString &identity, const QString& message) = 0;
+ virtual QString getReturnMessage(const QString &identity) = 0;
+
+ virtual QStringList listIdentities() = 0;
+};
+#endif
diff --git a/konversation/src/konvirc.protocol b/konversation/src/konvirc.protocol
new file mode 100644
index 0000000..78c00b8
--- /dev/null
+++ b/konversation/src/konvirc.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=konversation %u
+protocol=irc
+input=none
+output=none
+helper=true
+listing=false
+reading=false
+writing=false
+makedir=false
+deleting=false
diff --git a/konversation/src/konvirc6.protocol b/konversation/src/konvirc6.protocol
new file mode 100644
index 0000000..6d885cb
--- /dev/null
+++ b/konversation/src/konvirc6.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=konversation %u
+protocol=irc6
+input=none
+output=none
+helper=true
+listing=false
+reading=false
+writing=false
+makedir=false
+deleting=false
diff --git a/konversation/src/konvisettingsdialog.cpp b/konversation/src/konvisettingsdialog.cpp
new file mode 100644
index 0000000..0e00a35
--- /dev/null
+++ b/konversation/src/konvisettingsdialog.cpp
@@ -0,0 +1,316 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "konvisettingsdialog.h"
+#include "konviconfigdialog.h"
+#include "config/preferences.h"
+#include "chatwindowappearance_preferences.h"
+#include "connectionbehavior_preferences.h"
+#include "highlight_preferences.h"
+#include "warnings_preferences.h"
+#include "log_preferences.h"
+#include "quickbuttons_preferences.h"
+#include "autoreplace_preferences.h"
+#include "chatwindowbehaviour_preferences.h"
+#include "fontappearance_preferences.h"
+#include "nicklistbehavior_preferences.h"
+#include "tabs_preferences.h"
+#include "colorsappearance_preferences.h"
+#include "generalbehavior_preferences.h"
+#include "dcc_preferences.h"
+#include "osd_preferences.h"
+#include "theme_preferences.h"
+#include "alias_preferences.h"
+#include "ignore_preferences.h"
+#include "watchednicknames_preferences.h"
+#include "tabnotifications_preferences.h"
+
+#include <qsplitter.h>
+#include <qcombobox.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klistview.h>
+
+
+KonviSettingsDialog::KonviSettingsDialog( QWidget *parent) :
+ KonviConfigDialog( parent, "settings", Preferences::self(), KDialogBase::TreeList)
+{
+ m_modified = false;
+ setShowIconsInTreeList(true);
+
+ QStringList iconPath;
+
+ iconPath << i18n("Interface");
+ setFolderIcon( iconPath, SmallIcon("looknfeel") );
+
+ iconPath.clear();
+ iconPath << i18n("Behavior");
+ setFolderIcon( iconPath, SmallIcon("configure") );
+
+ iconPath.clear();
+ iconPath<< i18n("Behavior");
+ setFolderIcon( iconPath, SmallIcon("configure") );
+
+ iconPath.clear();
+ iconPath<< i18n("Notifications");
+ setFolderIcon( iconPath, SmallIcon("playsound") );
+
+ QStringList pagePath;
+
+ //Interface/Chat Window
+ m_confChatWindowAppearanceWdg = new ChatWindowAppearance_Config( 0, "ChatWindowAppearance" );
+ m_confChatWindowAppearanceWdg->kcfg_TimestampFormat->insertItem("hh:mm");
+ m_confChatWindowAppearanceWdg->kcfg_TimestampFormat->insertItem("hh:mm:ss");
+ m_confChatWindowAppearanceWdg->kcfg_TimestampFormat->insertItem("h:m ap");
+ pagePath.clear();
+ pagePath << i18n("Interface") << i18n("Chat Window");
+ addPage ( m_confChatWindowAppearanceWdg, pagePath, "view_text", i18n("Chat Window") );
+
+ //Interface/Themes
+ m_confThemeWdg = new Theme_Config( this, "Theme" );
+ pagePath.clear();
+ pagePath << i18n("Interface") << i18n("Nicklist Themes");
+ addPage ( m_confThemeWdg, pagePath, "iconthemes", i18n("Nicklist Themes") );
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confThemeWdg);
+ connect(m_confThemeWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+
+ //Interface/Colors
+ m_confColorsAppearanceWdg = new ColorsAppearance_Config( this, "ColorsAppearance" );
+ pagePath.clear();
+ pagePath << i18n("Interface") << i18n("Colors");
+ addPage ( m_confColorsAppearanceWdg, pagePath, "colorize", i18n("Colors") );
+
+ //Interface/Fonts
+ m_confFontAppearanceWdg = new FontAppearance_Config( this, "FontAppearance" );
+ pagePath.clear();
+ pagePath << i18n("Interface") << i18n("Fonts");
+ addPage ( m_confFontAppearanceWdg, pagePath, "fonts", i18n("Fonts") );
+
+ //Interface/Quick Buttons
+ m_confQuickButtonsWdg = new QuickButtons_Config( this, "QuickButtons" );
+ pagePath.clear();
+ pagePath << i18n("Interface") << i18n("Quick Buttons");
+ addPage ( m_confQuickButtonsWdg, pagePath, "keyboard", i18n("Quick Buttons") );
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confQuickButtonsWdg);
+ connect(m_confQuickButtonsWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+
+ //Interface/Tabs
+ m_confTabBarWdg = new Tabs_Config( this, "TabBar" );
+ pagePath.clear();
+ pagePath << i18n("Interface") << i18n("Tabs");
+ addPage ( m_confTabBarWdg, pagePath, "tab_new", i18n("Tabs") );
+
+ //Behavior/General
+ m_confGeneralBehaviorWdg = new GeneralBehavior_Config( this, "GeneralBehavior" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("General");
+ addPage ( m_confGeneralBehaviorWdg, pagePath, "exec", i18n("General") );
+
+ //Behavior/Connection
+ m_confConnectionBehaviorWdg = new ConnectionBehavior_Config( this, "ConnectionBehavior" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("Connection");
+ addPage ( m_confConnectionBehaviorWdg, pagePath, "connect_creating", i18n("Connection") );
+
+ //Behaviour/Chat Window
+ m_confChatwindowBehaviourWdg = new ChatwindowBehaviour_Config( this, "ChatwindowBehaviour" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("Chat Window");
+ addPage ( m_confChatwindowBehaviourWdg, pagePath, "view_text", i18n("Chat Window") );
+
+ //Behaviour/Nickname List
+ m_confNicklistBehaviorWdg = new NicklistBehavior_Config( this, "NicklistBehavior" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("Nickname List");
+ addPage ( m_confNicklistBehaviorWdg, pagePath, "player_playlist", i18n("Nickname List") );
+ connect(m_confNicklistBehaviorWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confNicklistBehaviorWdg);
+
+ //Behaviour/Command Aliases
+ m_confAliasWdg = new Alias_Config( this, "Alias" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("Command Aliases");
+ addPage ( m_confAliasWdg, pagePath, "editcopy", i18n("Command Aliases") );
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confAliasWdg);
+ connect(m_confAliasWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+
+ //Behaviour/Auto Replace
+ m_confAutoreplaceWdg = new Autoreplace_Config( this, "Autoreplace" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("Auto Replace");
+ addPage ( m_confAutoreplaceWdg, pagePath, "kview", i18n("Auto Replace") );
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confAutoreplaceWdg);
+ connect(m_confAutoreplaceWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+
+ //Behaviour/Ignore
+ m_confIgnoreWdg = new Ignore_Config(this, "Ignore");
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("Ignore");
+ addPage ( m_confIgnoreWdg, pagePath, "stop", i18n("Ignore") );
+ connect(m_confIgnoreWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confIgnoreWdg);
+
+ //Behaviour/Logging
+ m_confLogWdg = new Log_Config( this, "Log" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("Logging");
+ addPage ( m_confLogWdg, pagePath, "log", i18n("Logging") );
+
+ m_confDCCWdg = new DCC_Config( this, "DCC" );
+ pagePath.clear();
+ pagePath << i18n("Behavior") << i18n("DCC");
+ addPage ( m_confDCCWdg, pagePath, "2rightarrow", i18n("DCC") );
+
+ //Notifications/Tab Bar
+ m_confTabNotificationsWdg = new TabNotifications_Config( this, "TabBar" );
+ pagePath.clear();
+ pagePath << i18n("Notifications") << i18n("Tabs");
+ addPage ( m_confTabNotificationsWdg, pagePath, "tab_new", i18n("Tabs") );
+
+ //Notification/Highlighting
+ m_confHighlightWdg = new Highlight_Config( this, "Highlight" );
+ pagePath.clear();
+ pagePath << i18n("Notifications") << i18n("Highlight");
+ addPage ( m_confHighlightWdg, pagePath, "paintbrush", i18n("Highlight") );
+ connect(m_confHighlightWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confHighlightWdg);
+
+ //Notification/Watched Nicknames
+ m_confWatchedNicknamesWdg = new WatchedNicknames_Config( this, "WatchedNicknames" );
+ pagePath.clear();
+ pagePath << i18n("Notifications") << i18n("Watched Nicknames");
+ addPage ( m_confWatchedNicknamesWdg, pagePath, "kfind", i18n("Watched Nicknames") );
+ // remember index so we can open this page later from outside
+ m_watchedNicknamesIndex=lastAddedIndex();
+ connect(m_confWatchedNicknamesWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confWatchedNicknamesWdg);
+
+ //Notification/On Screen Display
+ m_confOSDWdg = new OSD_Config( this, "OSD" );
+ pagePath.clear();
+ pagePath << i18n("Notifications") << i18n("On Screen Display");
+ addPage ( m_confOSDWdg, pagePath, "tv", i18n("On Screen Display") );
+ //no modified connection needed - it's all kcfg widgets
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confOSDWdg);
+
+ //Notification/Warning Dialogs
+ m_confWarningsWdg = new Warnings_Config( this, "Warnings" );
+ pagePath.clear();
+ pagePath << i18n("Notifications") << i18n("Warning Dialogs");
+ addPage ( m_confWarningsWdg, pagePath, "messagebox_warning", i18n("Warning Dialogs") );
+ m_indexToPageMapping.insert(lastAddedIndex(), m_confWarningsWdg);
+ connect(m_confWarningsWdg, SIGNAL(modified()), this, SLOT(modifiedSlot()));
+
+ unfoldTreeList();
+}
+
+void KonviSettingsDialog::showEvent(QShowEvent* e)
+{
+ KonviConfigDialog::showEvent(e);
+
+ QSplitter* splitter = ((QSplitter*)child(0, "QSplitter", true));
+ KListView* listView = ((KListView*)child(0, "KListView", true));
+
+ if (splitter && listView)
+ {
+ int visible = listView->visibleWidth();
+ int content = listView->contentsWidth();
+
+ if (visible < content)
+ {
+ int shiftSplitterBy = content - visible;
+ resize(width()+shiftSplitterBy, height());
+ QValueList<int> oldSizes = splitter->sizes();
+ QValueList<int> newSizes;
+ newSizes << oldSizes[0] + shiftSplitterBy << oldSizes[1] - shiftSplitterBy;
+ splitter->setSizes(newSizes);
+ }
+ }
+}
+
+void KonviSettingsDialog::modifiedSlot()
+{
+ // this is for the non KConfigXT parts to tell us, if the user actually changed
+ // something or went back to the old settings
+// kdDebug() << "KonviSettingsDialog::modifiedSlot()" << endl;
+ m_modified = false;
+ QIntDictIterator<KonviSettingsPage> it( m_indexToPageMapping );
+ for ( ; it.current(); ++it )
+ {
+ if ( (*it).hasChanged() )
+ {
+ m_modified = true;
+// kdDebug() << "KonviSettingsDialog::modifiedSlot(): modified!" << endl;
+ break;
+ }
+ }
+ updateButtons();
+}
+
+KonviSettingsDialog::~KonviSettingsDialog()
+{
+}
+
+void KonviSettingsDialog::updateSettings()
+{
+ QIntDictIterator<KonviSettingsPage> it( m_indexToPageMapping );
+ for ( ; it.current(); ++it )
+ {
+ // this is for the non KConfigXT parts to update the UI (like quick buttons)
+ (*it).saveSettings();
+ }
+ m_modified = false;
+ emit settingsChanged();
+}
+
+void KonviSettingsDialog::updateWidgets()
+{
+ QIntDictIterator<KonviSettingsPage> it( m_indexToPageMapping );
+ for ( ; it.current(); ++it )
+ {
+ (*it).loadSettings();
+ }
+ m_modified = false;
+}
+
+void KonviSettingsDialog::updateWidgetsDefault()
+{
+ QIntDictIterator<KonviSettingsPage> it( m_indexToPageMapping );
+ for ( ; it.current(); ++it )
+ {
+ (*it).restorePageToDefaults();
+ }
+ m_modified = true;
+}
+
+void KonviSettingsDialog::openWatchedNicknamesPage()
+{
+ // page index has been calculated in the constructor
+ showPage(m_watchedNicknamesIndex);
+}
+
+// accessor method - will be used by KonviConfigDialog::updateButtons()
+bool KonviSettingsDialog::hasChanged()
+{
+ return m_modified;
+}
+
+// accessor method - will be used by KonviConfigDialog::updateButtons()
+bool KonviSettingsDialog::isDefault()
+{
+ return true;
+}
+
+#include "konvisettingsdialog.moc"
+
diff --git a/konversation/src/konvisettingsdialog.h b/konversation/src/konvisettingsdialog.h
new file mode 100644
index 0000000..b9b0878
--- /dev/null
+++ b/konversation/src/konvisettingsdialog.h
@@ -0,0 +1,95 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONVISETTINGSDIALOG_H
+#define KONVISETTINGSDIALOG_H
+
+#include "konviconfigdialog.h"
+#include "konvisettingspage.h"
+
+#include <kdialogbase.h>
+#include <qasciidict.h>
+#include <qintdict.h>
+
+
+class Warnings_Config;
+class ChatWindowAppearance_Config;
+class FontAppearance_Config;
+class Theme_Config;
+class ColorsAppearance_Config;
+class GeneralBehavior_Config;
+class ConnectionBehavior_Config;
+class ChatwindowBehaviour_Config;
+class NicklistBehavior_Config;
+class Tabs_Config;
+class Alias_Config;
+class QuickButtons_Config;
+class Autoreplace_Config;
+class Log_Config;
+class DCC_Config;
+class WatchedNicknames_Config;
+class Highlight_Config;
+class OSD_Config;
+class Ignore_Config;
+class TabNotifications_Config;
+
+class KDEUI_EXPORT KonviSettingsDialog : public KonviConfigDialog
+{
+ Q_OBJECT
+
+ protected:
+ Warnings_Config* m_confWarningsWdg;
+ ChatWindowAppearance_Config* m_confChatWindowAppearanceWdg;
+ FontAppearance_Config* m_confFontAppearanceWdg;
+ Theme_Config* m_confThemeWdg;
+ ColorsAppearance_Config* m_confColorsAppearanceWdg;
+ GeneralBehavior_Config* m_confGeneralBehaviorWdg;
+ ConnectionBehavior_Config* m_confConnectionBehaviorWdg;
+ ChatwindowBehaviour_Config* m_confChatwindowBehaviourWdg;
+ NicklistBehavior_Config* m_confNicklistBehaviorWdg;
+ Tabs_Config* m_confTabBarWdg;
+ Alias_Config* m_confAliasWdg;
+ QuickButtons_Config* m_confQuickButtonsWdg;
+ Autoreplace_Config* m_confAutoreplaceWdg;
+ Log_Config* m_confLogWdg;
+ DCC_Config* m_confDCCWdg;
+ WatchedNicknames_Config* m_confWatchedNicknamesWdg;
+ Highlight_Config* m_confHighlightWdg;
+ OSD_Config* m_confOSDWdg;
+ Ignore_Config* m_confIgnoreWdg;
+ TabNotifications_Config* m_confTabNotificationsWdg;
+
+ bool m_modified;
+
+ public:
+ explicit KonviSettingsDialog(QWidget *parent);
+ ~KonviSettingsDialog();
+
+ void openWatchedNicknamesPage();
+
+ protected slots:
+ virtual void updateSettings();
+ virtual void updateWidgets();
+ virtual void updateWidgetsDefault();
+ void modifiedSlot();
+
+ protected:
+ virtual bool hasChanged();
+ virtual bool isDefault();
+ virtual void showEvent(QShowEvent* e);
+
+ // remember page index
+ unsigned int m_watchedNicknamesIndex;
+ QIntDict<KonviSettingsPage> m_indexToPageMapping;
+};
+
+#endif //KONVISETTINGSDIALOG_H
diff --git a/konversation/src/konvisettingspage.h b/konversation/src/konvisettingspage.h
new file mode 100644
index 0000000..e7c6aad
--- /dev/null
+++ b/konversation/src/konvisettingspage.h
@@ -0,0 +1,27 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef KONVISETTINGSPAGE_H
+#define KONVISETTINGSPAGE_H
+
+class KonviSettingsPage
+{
+ public:
+ virtual void restorePageToDefaults() = 0; // function called when the user klicks "Default"
+ virtual void saveSettings() = 0; // function called when the user klicks "Ok" or "Apply"
+ virtual void loadSettings() = 0; // function called when the user opens the page
+
+ virtual bool hasChanged() = 0; // is to return if any non-KConfigXT settings have changed
+};
+
+#endif
+
+
diff --git a/konversation/src/linkaddressbook/Makefile.am b/konversation/src/linkaddressbook/Makefile.am
new file mode 100644
index 0000000..266c2c6
--- /dev/null
+++ b/konversation/src/linkaddressbook/Makefile.am
@@ -0,0 +1,13 @@
+METASOURCES = AUTO
+
+INCLUDES = -I$(srcdir)/.. -I.. -I$(srcdir)/../config -I../config
+AM_CPPFLAGS = $(all_includes) -I$(top_builddir)/konversation/src
+
+noinst_LTLIBRARIES = liblinkaddressbookui.la
+noinst_HEADERS = linkaddressbookui.h kimiface.h
+
+liblinkaddressbookui_la_SOURCES = linkaddressbookui_base.ui linkaddressbookui.cpp addressbook_base.cpp addressbook.cpp nicklisttooltip.cpp nicksonlinetooltip.cpp kimiface.skel addresseeitem.cpp
+kimiface_DIR = $(kde_includes)
+liblinkaddressbookui_la_LDFLAGS = $(all_libraries) -no-undefined
+
+# vim: set noet:
diff --git a/konversation/src/linkaddressbook/addressbook.cpp b/konversation/src/linkaddressbook/addressbook.cpp
new file mode 100644
index 0000000..c5608e6
--- /dev/null
+++ b/konversation/src/linkaddressbook/addressbook.cpp
@@ -0,0 +1,339 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ This class gives function that interact with kaddressbook.
+ begin: Fri 2004-07-23
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#include "addressbook.h"
+#include "../viewcontainer.h"
+#include "../konversationmainwindow.h"
+#include "../server.h"
+#include "../channel.h"
+#include "../konversationapplication.h"
+
+#include <qstringlist.h>
+#include "qwidget.h"
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kwin.h>
+
+
+namespace Konversation
+{
+
+ Addressbook *Addressbook::m_instance=0L;
+
+ Addressbook::Addressbook() : DCOPObject( "KIMIface")
+ {
+ addressBook = KABC::StdAddressBook::self(true);
+ m_ticket=NULL;
+ }
+ Addressbook::~Addressbook()
+ {
+ if (m_instance == this)
+ sd.setObject(m_instance, 0, false);
+ }
+
+ Addressbook *Addressbook::self()
+ {
+ if (!m_instance) { sd.setObject(m_instance, new Addressbook()); }
+ return m_instance;
+ }
+
+ QStringList Addressbook::allContacts()
+ {
+ QStringList contactUIDS;
+ for( KABC::AddressBook::Iterator it = addressBook->begin(); it != addressBook->end(); ++it )
+ if(hasAnyNicks(*it)) contactUIDS.append((*it).uid());
+ return contactUIDS;
+ }
+ //Produces a string list of all the irc nicks that are known.
+ QStringList Addressbook::allContactsNicks()
+ {
+ QStringList contacts;
+ for( KABC::AddressBook::Iterator it = addressBook->begin(); it != addressBook->end(); ++it )
+ contacts += QStringList::split( QChar( 0xE000 ), (*it).custom("messaging/irc", "All") );
+ return contacts;
+ }
+
+ QStringList Addressbook::onlineContacts()
+ {
+ QStringList contactUIDS;
+ for( KABC::AddressBook::Iterator it = addressBook->begin(); it != addressBook->end(); ++it )
+ if(isOnline(*it)) contactUIDS.append((*it).uid());
+
+ return contactUIDS;
+ }
+ QStringList Addressbook::reachableContacts()
+ {
+ return onlineContacts();
+ }
+ QStringList Addressbook::fileTransferContacts()
+ {
+ return onlineContacts();
+ }
+ bool Addressbook::isPresent(const QString &uid)
+ {
+ return hasAnyNicks(addressBook->findByUid(uid));
+ }
+ QString Addressbook::displayName(const QString &uid)
+ {
+ return getBestNick(addressBook->findByUid(uid));
+ }
+ QString Addressbook::presenceString(const QString &uid)
+ {
+ if(uid.isEmpty())
+ {
+ kdDebug() << "Addressbook::presenceString() called with an empty uid" << endl;
+ return QString("Error");
+ }
+ switch( presenceStatus(uid))
+ {
+ case 0:
+ return "";
+ case 1:
+ return i18n("Offline");
+ case 2:
+ return i18n("Connecting"); //Shouldn't happen - not supported.
+ case 3:
+ return i18n("Away");
+ case 4:
+ return i18n("Online");
+ }
+ return QString("Error");
+ }
+ int Addressbook::presenceStatus(const QString &uid)
+ {
+ return presenceStatusByAddressee(addressBook->findByUid(uid));
+ }
+
+ bool Addressbook::canReceiveFiles(const QString &uid)
+ {
+ if(uid.isEmpty())
+ {
+ kdDebug() << "Addressbook::canReceiveFiles() called with empty uid" << endl;
+ return false;
+ }
+ int presence = presenceStatus(uid);
+
+ return (presence == 4) || (presence == 3);
+ }
+ bool Addressbook::canRespond(const QString &uid)
+ {
+ if(uid.isEmpty())
+ {
+ kdDebug() << "Addressbook::canRespond called with empty uid" << endl;
+ return false;
+ }
+ //this should return false if they are offline.
+ int result = presenceStatus(uid);
+ if(result == 3 || result == 4) return true;
+ return false;
+ }
+ QString Addressbook::locate(const QString &contactId, const QString &protocol)
+ {
+ if(contactId.isEmpty())
+ {
+ kdDebug() << "Addressbook::locate called with empty contactId" << endl;
+ return QString();
+ }
+ if(protocol != "messaging/irc")
+ return QString();
+
+ return getKABCAddresseeFromNick(contactId).uid();
+ }
+ QPixmap Addressbook::icon(const QString &uid)
+ {
+
+ Images* icons = KonversationApplication::instance()->images();
+ QIconSet currentIcon;
+ if(!isPresent(uid))
+ return QPixmap();
+
+ switch(presenceStatus(uid))
+ {
+ case 0: //Unknown
+ case 1: //Offline
+ case 2: //connecting - invalid for us?
+ currentIcon = icons->getKimproxyOffline();
+ break;
+ case 3: //Away
+ currentIcon = icons->getKimproxyAway();
+ break;
+ case 4: //Online
+ currentIcon = icons->getKimproxyOnline();
+ break;
+ default:
+ //error
+ kdDebug() << "Unknown status " << uid << endl;
+ return QPixmap();
+ }
+
+ QPixmap joinedIcon = currentIcon.pixmap(QIconSet::Automatic, QIconSet::Active, QIconSet::On);
+ return joinedIcon;
+ }
+ QString Addressbook::context(const QString &uid)
+ {
+ if(uid.isEmpty())
+ {
+ kdDebug() << "Addressbook::contact called with empty uid" << endl;
+ return QString();
+ }
+ QString context;
+ return context;
+ }
+ QStringList Addressbook::protocols()
+ {
+ QStringList protocols;
+ protocols.append("messaging/irc");
+ return protocols;
+ }
+
+ // ACTORS
+ /**
+ * Send a single message to the specified addressee
+ * Any response will be handled by the IM client as a normal
+ * conversation.
+ * @param uid the KABC uid you want to chat with.
+ * @param message the message to send them.
+ */
+ void Addressbook::messageContact( const QString &uid, const QString& message )
+ {
+ if(uid.isEmpty())
+ {
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation for instant messaging, but did not specify any contact to send the message to. This is probably a bug in the other application."));
+ return;
+ }
+ KABC::Addressee addressee = addressBook->findByUid(uid);
+ if(addressee.isEmpty())
+ {
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation for instant messaging, but Konversation could not find the specified contact in the KDE address book."));
+ return;
+ }
+ NickInfoPtr nickInfo = getNickInfo(addressee);
+ if(!nickInfo)
+ {
+ QString user = addressee.fullEmail();
+ if(!user.isEmpty()) user = " (" + user + ')';
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation for instant messaging, but the requested user%1 is not online.").arg(user));
+ return;
+ }
+
+ nickInfo->getServer()->dcopSay(nickInfo->getNickname(), message);
+ }
+
+ /**
+ * Open a chat to a contact, and optionally set some initial text
+ */
+ void Addressbook::messageNewContact( const QString &contactId, const QString &/*protocol*/ ) {
+ if(contactId.isEmpty() )
+ {
+ kdDebug() << "Addressbook::messageNewContact called with empty contactid" << endl;
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation for instant messaging, but did not specify any contact to send the message to. This is probably a bug in the other application."));
+ return;
+ }
+ messageContact(contactId, QString());
+}
+
+/**
+ * Start a chat session with the specified addressee
+ * @param uid the KABC uid you want to chat with.
+ */
+void Addressbook::chatWithContact( const QString &uid )
+{
+ if(uid.isEmpty())
+ {
+ kdDebug() << "Addressbook::chatWithContact called with empty uid" << endl;
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation for instant messaging, but did not specify any contact to send the message to. This is probably a bug in the other application."));
+ return;
+ }
+ messageContact(uid, QString());
+}
+
+/**
+ * Send the file to the contact
+ * @param uid the KABC uid you are sending to.
+ * @param sourceURL a KURL to send.
+ * @param altFileName an alternate filename describing the file
+ * @param fileSize file size in bytes
+ */
+void Addressbook::sendFile(const QString &uid, const KURL &sourceURL, const QString &altFileName, uint fileSize)
+{
+ if(uid.isEmpty())
+ {
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation to send a file to a contact, but did not specify any contact to send the file to. This is probably a bug in the other application."));
+ return;
+ }
+ KABC::Addressee addressee = addressBook->findByUid(uid);
+ if(addressee.isEmpty())
+ {
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation to send a file to a contact, but Konversation could not find the specified contact in the KDE address book."));
+ return;
+ }
+ NickInfoPtr nickInfo = getNickInfo(addressee);
+ if(!nickInfo)
+ {
+ QString user = addressee.fullEmail();
+ if(!user.isEmpty()) user = " (" + user + ')';
+ focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation to send a file to a contact, but the requested user%1 is not currently online.").arg(user));
+ return;
+ }
+ nickInfo->getServer()->addDccSend(nickInfo->getNickname(), sourceURL, altFileName, fileSize);
+ QWidget *widget = nickInfo->getServer()->getViewContainer()->getWindow();
+ KWin::demandAttention(widget->winId()); //If activeWindow request is denied, at least demand attention!
+ KWin::activateWindow(widget->winId()); //May or may not work, depending on focus stealing prevention.
+
+}
+
+// MUTATORS
+// Contact list
+/**
+ * Add a contact to the contact list
+ * @param contactId the protocol specific identifier for the contact, eg UIN for ICQ, screenname for AIM, nick for IRC.
+ * @param protocolId the protocol, eg one of "AIMProtocol", "MSNProtocol", "ICQProtocol", ...
+ * @return whether the add succeeded. False may signal already present, protocol not supported, or add operation not supported.
+ */
+bool Addressbook::addContact( const QString &/*contactId*/, const QString &/*protocolId*/ ) {
+focusAndShowErrorMessage(i18n("Another KDE application tried to use Konversation to add a contact. Konversation does support this."));
+return false;
+//Nicks are auto added if they are put in the addressbook - I don' think there is anything useful I can do.
+}
+
+void Addressbook::emitContactPresenceChanged(const QString &uid, int presence)
+{
+ if(uid.isEmpty())
+ {
+ //This warning below is annoying. FIXME - disabled because it's too verbose
+ // kdDebug() << "Addressbook::emitContactPresenceChanged was called with empty uid" << endl;
+ return;
+ }
+ Q_ASSERT(kapp->dcopClient());
+ emit contactPresenceChanged(uid, kapp->dcopClient()->appId(), presence);
+ // kdDebug() << "Presence changed for uid " << uid << " to " << presence << endl;
+}
+
+void Addressbook::emitContactPresenceChanged(const QString &uid)
+{
+ if(uid.isEmpty())
+ {
+ kdDebug() << "Addressbook::emitContactPresenceChanged was called with empty uid" << endl;
+ return;
+ };
+
+ emitContactPresenceChanged(uid, presenceStatus(uid));
+}
+
+} //NAMESPACE
+
+#include "addressbook.moc"
diff --git a/konversation/src/linkaddressbook/addressbook.h b/konversation/src/linkaddressbook/addressbook.h
new file mode 100644
index 0000000..9b906f5
--- /dev/null
+++ b/konversation/src/linkaddressbook/addressbook.h
@@ -0,0 +1,99 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ This class contains functions that interact with kaddressbook.
+ begin: Fri 2004-07-23
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <kabc/addressbook.h>
+#include <kabc/stdaddressbook.h>
+
+#include <kstaticdeleter.h>
+#include <qobject.h>
+#include <qregexp.h>
+#include <config.h>
+#include "kimiface.h"
+
+#include "../images.h"
+#include "../nickinfo.h"
+#include "addressbook_base.h"
+
+namespace Konversation
+{
+ class Addressbook : public AddressbookBase
+ {
+ Q_OBJECT
+ public:
+
+ virtual ~Addressbook(); // This needs to be public so it can be deleted by our static pointer
+ static Addressbook *self();
+ QStringList allContactsNicks();
+ QStringList allContacts();
+ QStringList reachableContacts();
+ QStringList onlineContacts();
+ QStringList fileTransferContacts();
+ bool isPresent( const QString &uid );
+ QString displayName( const QString &uid );
+ QString presenceString( const QString &uid );
+ bool canReceiveFiles( const QString &uid );
+ bool canRespond( const QString &uid );
+ QString locate( const QString &contactId, const QString &protocol );
+ // metadata
+ QPixmap icon( const QString &uid );
+ QString context( const QString &uid );
+ virtual int presenceStatus(const QString &uid);
+ // App capabilities
+ QStringList protocols();
+
+ /**
+ * Message a contact by their metaContactId, aka their uid in KABC.
+ */
+ void messageContact( const QString &uid, const QString& message );
+
+ /**
+ * Open a chat to a contact, and optionally set some initial text
+ */
+ void messageNewContact( const QString &contactId, const QString &protocolId );
+
+ /**
+ * Message a contact by their metaContactId, aka their uid in KABC.
+ */
+ void chatWithContact( const QString &uid );
+
+ /**
+ * Send the file to the contact
+ */
+ void sendFile(const QString &uid, const KURL &sourceURL,
+ const QString &altFileName = QString(), uint fileSize = 0);
+
+ /**
+ * Lets outsiders tell us to emit presenceChanged signal.
+ */
+ void emitContactPresenceChanged( const QString &uid, int presence);
+ /**
+ * Lets outsiders tell us to emit presenceChanged signal.
+ */
+ void emitContactPresenceChanged(const QString &uid);
+
+ bool addContact( const QString &contactId, const QString &protocolId );
+
+ protected:
+ Addressbook();
+
+ static Addressbook *m_instance;
+ };
+
+ static KStaticDeleter<Addressbook> sd;
+
+}
+#endif
diff --git a/konversation/src/linkaddressbook/addressbook_base.cpp b/konversation/src/linkaddressbook/addressbook_base.cpp
new file mode 100644
index 0000000..b8f7243
--- /dev/null
+++ b/konversation/src/linkaddressbook/addressbook_base.cpp
@@ -0,0 +1,516 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ addressbook.cpp - This class gives function that interact with kaddressbook.
+ begin: Fri 2004-07-23
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#include "addressbook.h"
+#include "../server.h"
+#include "../konversationapplication.h"
+#include "../konversationmainwindow.h"
+#include "../channel.h"
+
+#include <qstringlist.h>
+
+#include <klocale.h>
+#include <kstringhandler.h>
+#include <krun.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kmessagebox.h>
+
+namespace Konversation
+{
+
+ AddressbookBase::AddressbookBase()
+ {
+ KABC::StdAddressBook::setAutomaticSave( false );
+ m_ticket=NULL;
+ }
+
+ AddressbookBase::~AddressbookBase()
+ {
+ }
+
+ KABC::AddressBook *AddressbookBase::getAddressBook() { return addressBook; }
+
+ KABC::Addressee AddressbookBase::getKABCAddresseeFromNick(const QString &ircnick, const QString &servername, const QString &servergroup)
+ {
+ KABC::AddressBook::Iterator it;
+
+ for( it = addressBook->begin(); it != addressBook->end(); ++it )
+ {
+ if(hasNick(*it, ircnick, servername, servergroup))
+ return (*it);
+ }
+ return KABC::Addressee();
+ }
+ KABC::Addressee AddressbookBase::getKABCAddresseeFromNick(const QString &nick_server)
+ {
+ KABC::AddressBook::Iterator it;
+
+ for( it = addressBook->begin(); it != addressBook->end(); ++it )
+ {
+ if(hasNick(*it, nick_server))
+ return (*it);
+ }
+ return KABC::Addressee();
+ }
+
+ bool AddressbookBase::hasNick(const KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup)
+ {
+
+ QString lnick = ircnick.lower();
+ QString lnick_servername;
+ QString lnick_servergroup;
+ if(!servername.isEmpty())
+ lnick_servername = lnick + QChar(0xE120) + servername.lower();
+ if(!servergroup.isEmpty())
+ lnick_servergroup = lnick + QChar(0xE120) + servergroup.lower();
+
+ QString lit;
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
+ QStringList::iterator end = addresses.end();
+ for ( QStringList::iterator it = addresses.begin(); it != end; ++it )
+ {
+ lit = (*it).lower();
+ if(lit == lnick || lit == lnick_servername || lit == lnick_servergroup)
+ return true;
+ }
+ return false;
+
+ }
+
+ QStringList AddressbookBase::allContactsNicksForServer(const QString &servername, const QString &servergroup)
+ {
+ QStringList contacts;
+ for( KABC::AddressBook::Iterator it = addressBook->begin(); it != addressBook->end(); ++it )
+ contacts += getNicks(*it, servername, servergroup);
+ return contacts;
+
+ }
+
+ QStringList AddressbookBase::getNicks(const KABC::Addressee &addressee, const QString &servername, const QString &servergroup)
+ {
+ QStringList nicks;
+
+ QString lservername = servername.lower();
+ QString lservergroup = servergroup.lower();
+
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
+ QStringList::iterator end = addresses.end();
+ for ( QStringList::iterator it = addresses.begin(); it != end; ++it )
+ {
+ if(!(*it).contains(QChar( 0xE120)))
+ nicks.append(*it);
+ else
+ {
+ QString it_server = (*it).section(QChar( 0xE120), 0,0).lower();
+ if(it_server == lservername || it_server == lservergroup)
+ nicks.append((*it).section(QChar( 0xE120 ), 1,1));
+ }
+ }
+ return nicks;
+ }
+
+ bool AddressbookBase::hasNick(const KABC::Addressee &addressee, const QString &nick_server)
+ {
+ QString lnick_server = nick_server.lower();
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
+ QStringList::iterator end = addresses.end();
+ for ( QStringList::iterator it = addresses.begin(); it != end; ++it )
+ {
+ QString it_server = (*it).section(QChar( 0xE120), 0,0).lower();
+ if(it_server ==lnick_server)
+ return true;
+ }
+ return false;
+
+ }
+
+ QString AddressbookBase::getBestNick(const KABC::Addressee &addressee)
+ {
+ //Look for a nickinfo for this nick, and use that. That way we turn a nick that is online.
+ NickInfoPtr nickInfo = getNickInfo(addressee);
+ if(nickInfo)
+ return nickInfo->getNickname();
+ //No online nickinfo - not connected to server maybe. just return the first nick.
+
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
+ if(!addresses.empty())
+ return addresses.first();
+ //No irc nicks- nothing left to try - return null
+ return QString();
+ }
+
+ NickInfoPtr AddressbookBase::getNickInfo(const KABC::Addressee &addressee)
+ {
+ NickInfoPtr lastNickInfo;
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
+ QStringList::iterator end = addresses.end();
+ for ( QStringList::iterator it = addresses.begin(); it != end; ++it )
+ {
+ QString ircnick;
+ QString serverOrGroup;
+ KonversationApplication::splitNick_Server(*it, ircnick, serverOrGroup);
+ NickInfoPtr nickInfo =
+ dynamic_cast<KonversationApplication*>(kapp)->getNickInfo(ircnick, serverOrGroup);
+ if(nickInfo)
+ {
+ if(!nickInfo->isAway())
+ return nickInfo;
+ //This nick is away. Keep looking, but use it if we can't find one that's on
+ lastNickInfo = nickInfo;
+ }
+ }
+ //Use a nick that's either away, or non-existant.
+ return lastNickInfo;
+ }
+
+ bool AddressbookBase::hasAnyNicks(const KABC::Addressee &addressee)
+ {
+ return !addressee.custom("messaging/irc", "All").isEmpty();
+ }
+ void AddressbookBase::unassociateNick(KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup)
+ {
+
+ kdDebug() << "in unassociatenick for '" << ircnick << endl;
+ if(ircnick.isEmpty()) return;
+
+ QString lnick = ircnick.lower();
+ QString lnick_servername;
+ QString lnick_servergroup;
+ if(!servername.isEmpty())
+ lnick_servername = lnick + QChar(0xE120) + servername.lower();
+ if(!servergroup.isEmpty())
+ lnick_servergroup = lnick + QChar(0xE120) + servergroup.lower();
+
+ //We should now have lnick = ircnick, and versions with servername and servergroup -
+ // like johnflux, johnflux@freenode, or johnflux@irc.kde.org except with the unicode
+ // separator char 0xe120 instead of the @
+
+ kdDebug() << "nick" << ircnick<< endl;
+ bool changed = false;
+ if(addressee.isEmpty())
+ {
+ kdDebug() << "Ignoring unassociation command for empty addressee for nick " << ircnick << endl;
+ }
+ QString lit;
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
+ QStringList::iterator it = addresses.begin();
+ while(it != addresses.end())
+ {
+ lit = (*it).lower();
+ if(lit == lnick || lit == lnick_servername || lit == lnick_servergroup)
+ {
+ changed = true;
+ it = addresses.remove(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+ if(!changed)
+ return;
+
+ //if(!getAndCheckTicket())
+ // return;
+ QString new_custom = addresses.join( QChar( 0xE000 ));
+ if(new_custom.isEmpty())
+ addressee.removeCustom("messaging/irc", "All");
+ else
+ addressee.insertCustom("messaging/irc", "All", new_custom);
+
+ addressBook->insertAddressee(addressee);
+ //saveTicket();
+ }
+ void AddressbookBase::focusAndShowErrorMessage(const QString &errorMsg)
+ {
+ static_cast<KonversationApplication *>(kapp)->getMainWindow()->focusAndShowErrorMessage(errorMsg);
+ }
+ /**For a given contact, adds the ircnick if they don't already have it. If you pass an addressBook, the contact is inserted
+ * if it has changed. */
+ void AddressbookBase::associateNick(KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup)
+ {
+ //It's easiest to just remove it from the list if it's there already
+ unassociateNick(addressee, ircnick, servername, servergroup);
+ QString nick_server = ircnick;
+ if(!servergroup.isEmpty())
+ nick_server += QChar(0xE120) + servergroup;
+ else if(!servername.isEmpty())
+ nick_server += QChar(0xE120) + servername;
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
+ //For sanity reasons, don't let the number of irc nicks go above 10.
+ //To do this, just remove the irc nick at the end of the list.
+ if(addresses.count() >= 10)
+ addresses.pop_back();
+ addresses.append(nick_server);
+ addressee.insertCustom("messaging/irc", "All", addresses.join( QChar( 0xE000 )));
+
+ addressBook->insertAddressee(addressee);
+ }
+ /** This function associates the nick for a person, then iterates over all the contacts unassociating the nick from everyone else. It saves the addressses that have changed.*/
+ bool AddressbookBase::associateNickAndUnassociateFromEveryoneElse(KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup)
+ {
+ for( KABC::AddressBook::Iterator it = addressBook->begin(); it != addressBook->end(); ++it )
+ if((*it).uid() != addressee.uid())
+ unassociateNick(*it, ircnick, servername, servergroup);
+ associateNick(addressee, ircnick, servername, servergroup);
+ return true;;
+ }
+
+ bool AddressbookBase::getAndCheckTicket()
+ {
+ if(m_ticket)
+ {
+ kdError() << "Internal error - getting new ticket without saving old" << endl;
+ return false;
+ }
+ m_ticket = addressBook->requestSaveTicket();
+ if ( !m_ticket )
+ {
+ kdError() << "Resource is locked by other application!" << endl;
+ //emit error
+ return false;
+ }
+ kdDebug() << "gotTicketSuccessfully" << endl;
+ return true;
+ }
+ void AddressbookBase::releaseTicket()
+ {
+ addressBook->releaseSaveTicket(m_ticket);
+ m_ticket = NULL;
+ }
+
+ bool AddressbookBase::saveTicket()
+ {
+ if(!addressBook->save( m_ticket) )
+ {
+ kdError() << "Saving failed!" << endl;
+ addressBook->releaseSaveTicket(m_ticket);
+ m_ticket = NULL;
+ return false;
+ }
+ kdDebug() << "saveTicket() was successful" << endl;
+ m_ticket= NULL;
+ emit addresseesChanged();
+ return true;
+ }
+
+ bool AddressbookBase::saveAddressbook()
+ {
+ if(m_ticket)
+ {
+ kdError() << "Internal error - getting new ticket without saving old" << endl;
+ return false;
+ }
+ m_ticket = addressBook->requestSaveTicket();
+ if ( !m_ticket )
+ {
+ kdError() << "Resource is locked by other application!" << endl;
+ return false;
+ }
+ else
+ {
+ if ( !addressBook->save( m_ticket ) )
+ {
+ kdError() << "Saving failed!" << endl;
+ addressBook->releaseSaveTicket(m_ticket);
+ m_ticket = NULL;
+ return false;
+ }
+ }
+ kdDebug() << "Save was successful" << endl;
+ m_ticket = NULL;
+ emit addresseesChanged();
+ return true;
+ }
+
+ bool AddressbookBase::saveAddressee(KABC::Addressee &addressee)
+ {
+ Q_ASSERT(&addressee);
+ addressBook->insertAddressee(addressee);
+ bool success = saveAddressbook();
+ if(success)
+ emitContactPresenceChanged(addressee.uid(), presenceStatusByAddressee(addressee));
+ return success;
+ }
+
+ /**
+ * Indicate the presence as a number. Checks all the nicks that that person has.
+ * If we find them, return 4 (online).
+ * If we are connected to any of the servers that they are on, and we don't find them, return 1 (offline)
+ * If we find them, but they are set to away then return 3 (away)
+ * If we are connected to none of the servers that they are on, return 0 (unknown)
+ * @param addressee Addressbook contact you want to know of the presence of
+ * @return 0 (unknown), 1 (offline), 3 (away), 4 (online)
+ */
+ int AddressbookBase::presenceStatusByAddressee(const KABC::Addressee &addressee)
+ {
+ Q_ASSERT(&addressee);
+ NickInfoPtr nickInfo = getNickInfo(addressee);
+
+ if(!nickInfo)
+ {
+ if(hasAnyNicks(addressee))
+ return 1; //either offline, or we aren't on the same server.
+ else
+ return 0; //Not known to us
+ }
+ if(nickInfo->isAway()) return 3;
+ return 4;
+
+ }
+ bool AddressbookBase::isOnline(KABC::Addressee &addressee)
+ {
+ return !!getNickInfo(addressee);
+ }
+
+ bool AddressbookBase::editAddressee(const QString &uid)
+ {
+ Q_ASSERT(!uid.isEmpty());
+ //FIXME: This hack can be removed. I fixed the below mentioned bug in kde 3.4 cvs - 2005-01-02
+ //
+ //Because of stupid bugs in kaddressbook, first load kaddressbook using startServiceByDesktopPath
+ // then call it on the command line to actually put it in edit mode. This is stupid :(
+ kapp->startServiceByDesktopName( "kaddressbook" );
+
+ KProcess *proc = new KProcess;
+ *proc << "kaddressbook";
+ *proc << "--editor-only" << "--uid" << uid;
+ kdDebug() << "running kaddressbook --editor-only --uid " << uid << endl;
+ if(!proc->start())
+ {
+ KMessageBox::error(0, i18n("Could not run your addressbook program (kaddressbook). This is most likely because it is not installed. Please install the 'kdepim' packages."));
+ return false;
+ }
+ return true;
+ }
+
+ bool AddressbookBase::sendEmail(const KABC::Addressee &addressee)
+ {
+ if(addressee.preferredEmail().isEmpty())
+ {
+ KMessageBox::sorry(0, i18n("The contact that you have selected does not have an email address associated with them. "), i18n("Cannot Send Email"));
+ return false;
+ }
+ return runEmailProgram(addressee.fullEmail());
+ }
+
+ bool AddressbookBase::runEmailProgram(const QString &mailtoaddress)
+ {
+ KRun *proc = new KRun(KURL(QString("mailto:") + KStringHandler::from8Bit(mailtoaddress.ascii())));
+ kdDebug() << "Sending email to " << mailtoaddress << endl;
+ if(proc->hasError())
+ {
+ KMessageBox::error(0, i18n("Could not run your email program. This is possibly because one is not installed. To install the KDE email program (kmail) please install the 'kdepim' packages."));
+ return false;
+ }
+ return true;
+
+ }
+ bool AddressbookBase::sendEmail(const ChannelNickList &nickList)
+ {
+ if(nickList.isEmpty()) return false;
+ QString mailto;
+
+ QStringList nicksWithoutAddressee;
+ QStringList nicksWithoutEmails;
+ QStringList nicksWithEmails;
+ for(ChannelNickList::ConstIterator it=nickList.begin();it!=nickList.end();++it)
+ {
+ NickInfoPtr nickInfo = (*it)->getNickInfo();
+ Q_ASSERT(nickInfo); if(!nickInfo) return false;
+ KABC::Addressee addr = nickInfo->getAddressee();
+ if(addr.isEmpty())
+ {
+ nicksWithoutAddressee.append(nickInfo->getNickname());
+ }
+ else if(addr.preferredEmail().isEmpty())
+ {
+ nicksWithoutEmails.append(nickInfo->getNickname());
+ }
+ else
+ {
+ nicksWithEmails.append(nickInfo->getNickname());
+ if(!mailto.isEmpty())
+ mailto += ", ";
+ mailto += addr.fullEmail();
+
+ }
+ }
+ if(!nicksWithoutAddressee.isEmpty() || !nicksWithoutEmails.isEmpty())
+ {
+ QString message;
+ if(nicksWithoutEmails.isEmpty())
+ {
+ if(nicksWithEmails.isEmpty())
+ {
+ if(nicksWithoutAddressee.count() > 1)
+ message = i18n("None of the contacts that you have selected were associated with an addressbook contacts. ");
+ else
+ message = i18n("The contact that you have selected is not associated with an addressbook contact. ");
+ }
+ else
+ {
+ if(nicksWithoutAddressee.count() > 1)
+ message = i18n("Some of the contacts (%1) that you have selected are not associated with addressbook contacts. ").arg(nicksWithoutAddressee.join(", "));
+ else
+ message = i18n("One of the contacts (%1) that you have selected is not associated with an addressbook contact. ").arg(nicksWithoutAddressee.join(", "));
+ }
+ message += i18n("You can right click on a contact, and choose to edit the Addressbook Associations to link them to a contact in your addressbook.");
+ }
+ else if(nicksWithoutAddressee.isEmpty())
+ {
+ if(nicksWithEmails.isEmpty())
+ {
+ if(nicksWithoutEmails.count() > 1)
+ message = i18n("None of the contacts that you have selected have an email address associated with them. ");
+ else
+ message = i18n("The contact that you have selected does not have an email address associated with them. ");
+ }
+ else
+ {
+ if(nicksWithoutEmails.count() > 1)
+ message = i18n("Some of the contacts (%1) that you have selected do not have an email address associated with them. ").arg(nicksWithoutEmails.join(", "));
+ else
+ message = i18n("One of the contacts (%1) that you have selected does not have an email address associated with them. ").arg(nicksWithoutEmails.join(", "));
+ }
+ message += i18n("You can right click on a contact, and choose to edit the addressbook contact, adding an email for them.");
+ }
+ else
+ {
+ message = i18n("Some of the contacts (%1) that you have selected are not associated with addressbook contacts, and some of the contacts (%2) do not have an email address associated with them. ").arg(nicksWithoutAddressee.join(", ").arg(nicksWithoutEmails.join(", ")));
+ message += i18n("You can right click on a contact, and choose to edit the Addressbook Associations to link them to a contact in your addressbook, and choose to edit the addressbook contact, adding an email for them.");
+ }
+ if(nicksWithEmails.isEmpty())
+ {
+ KMessageBox::sorry(0, message, i18n("Cannot Send Email"));
+ return false;
+ }
+ else
+ {
+ message += i18n("\nDo you want to send an email anyway to the nicks that do have an email address?");
+ int result = KMessageBox::questionYesNo(0, message, i18n("Send Email"), KGuiItem(i18n("&Send Email...")), KGuiItem(i18n("&Cancel")));
+ if(result == KMessageBox::No)
+ {
+ return false;
+ }
+ }
+ }
+
+ return runEmailProgram(mailto);
+ }
+
+} // NAMESPACE
+
+#include "addressbook_base.moc"
diff --git a/konversation/src/linkaddressbook/addressbook_base.h b/konversation/src/linkaddressbook/addressbook_base.h
new file mode 100644
index 0000000..d7db6c8
--- /dev/null
+++ b/konversation/src/linkaddressbook/addressbook_base.h
@@ -0,0 +1,128 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ addressbook.cpp - This class contains functions that interact with kaddressbook.
+ begin: Fri 2004-07-23
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#ifndef ADDRESSBOOKBASE_H
+#define ADDRESSBOOKBASE_H
+
+#include "../images.h"
+#include "../nickinfo.h"
+#include "../channelnick.h"
+
+#include <kstaticdeleter.h>
+#include <qobject.h>
+#include <qregexp.h>
+#include <config.h>
+
+
+#include <kabc/addressbook.h>
+#include <kabc/stdaddressbook.h>
+
+
+namespace Konversation
+{
+ class AddressbookBase : public QObject, public KIMIface
+ {
+ Q_OBJECT
+ public:
+ virtual ~AddressbookBase(); // This needs to be public so it can be deleted by our static pointer
+ KABC::Addressee getKABCAddresseeFromNick(const QString &ircnick, const QString &servername, const QString &servergroup);
+ KABC::Addressee getKABCAddresseeFromNick(const QString &nick_server);
+ bool hasNick(const KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup);
+ bool hasNick(const KABC::Addressee &addressee, const QString &nick_server);
+
+ /** For a given contact, remove the ircnick if they have it. If you
+ * pass an addressBook, the contact is inserted if it has changed.
+ */
+ void unassociateNick(KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup);
+ void associateNick(KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup);
+ bool associateNickAndUnassociateFromEveryoneElse(KABC::Addressee &addressee, const QString &ircnick, const QString &servername, const QString &servergroup);
+ /** If this user is online, return one of the nicks that they are
+ * using. Otherwise return the first nick listed.
+ * If there are multiple matches, it will prefer ones that are not set to away.
+ * @return online nick, first nick, or QString::null if they aren't known at all.
+ */
+ QString getBestNick(const KABC::Addressee &addressee);
+ bool hasAnyNicks(const KABC::Addressee &addresse);
+ int presenceStatusByAddressee(const KABC::Addressee &addressee);
+ bool isOnline(KABC::Addressee &addressee);
+ bool getAndCheckTicket();
+ bool saveTicket();
+ void releaseTicket();
+ bool saveAddressee(KABC::Addressee &addressee);
+ bool saveAddressbook();
+
+ QStringList allContactsNicksForServer(const QString &servername, const QString &servergroup);
+ QStringList getNicks(const KABC::Addressee &addressee, const QString &servername, const QString &servergroup);
+
+ KABC::AddressBook *getAddressBook();
+
+ /** Return an online NickInfo for this addressee.
+ * If there are multiple matches, it tries to pick one that is not away.
+ * Note: No NickInfo is returned if the addressee is offline.
+ * NickInfo's are for online and away nicks only.
+ * @param addressee The addressee to get a nickInfo for
+ * @return A nickInfo. It tries hard to return a nickInfo that is not away if one exists.
+ */
+ static NickInfoPtr getNickInfo(const KABC::Addressee &addressee);
+ /**
+ * Lets outsiders tell us to emit presenceChanged signal.
+ */
+ virtual void emitContactPresenceChanged(const QString &uid, int presence) = 0;
+ /**
+ * Lets outsiders tell us to emit presenceChanged signal.
+ */
+ virtual void emitContactPresenceChanged(const QString &uid) = 0;
+
+ /**
+ * Run kmail (or whatever the users email client is)
+ * to create a single email addressed to all of the nicks passed in.
+ * Gives an error dialog to the user if any of the contacts don't have an
+ * email address associated, and gives the user the option to continue
+ * with the contacts that did have email addresses.
+ */
+ bool sendEmail(const ChannelNickList &nicklist);
+ /**
+ * Run kmail (or whatever the users email client is)
+ * to create a single email addressed to the addressee passed in.
+ * Gives an error dialog to the user if the addressee doesn't have an email address associated.
+ */
+ bool sendEmail(const KABC::Addressee &addressee);
+ /**
+ * Run kaddressbook to edit the addressee passed in.
+ */
+ bool editAddressee(const QString &uid);
+ /**
+ * Run the users email program (e.g. kmail) passing "mailto:" + mailtoaddress.
+ * Note that mailto: will be prepended for you.
+ * @param mailtoaddress A comma delimited set of email address topass as "mailto:"
+ * @return True if there were no problems running the email program. An error will be shown to the user if there was.
+ */
+ bool runEmailProgram(const QString &mailtoaddress);
+
+ /** Just calls KonversationMainWindow::focusAndShowErrorMessage(const QString *errorMsg)
+ *
+ * @see KonversationMainWindow::focusAndShowErrorMessage(const QString *errorMsg)
+ */
+ void focusAndShowErrorMessage(const QString &errorMsg);
+ signals:
+ void addresseesChanged();
+
+ protected:
+ AddressbookBase();
+ KABC::AddressBook* addressBook;
+ KABC::Ticket *m_ticket;
+ };
+
+} //NAMESPACE
+#endif
diff --git a/konversation/src/linkaddressbook/addresseeitem.cpp b/konversation/src/linkaddressbook/addresseeitem.cpp
new file mode 100644
index 0000000..9c9c6b4
--- /dev/null
+++ b/konversation/src/linkaddressbook/addresseeitem.cpp
@@ -0,0 +1,63 @@
+/*
+ Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "addresseeitem.h"
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+AddresseeItem::AddresseeItem( QListView *parent, const KABC::Addressee &addressee) :
+KListViewItem( parent ),
+mAddressee( addressee )
+{
+ //We can't save showphoto because we don't have a d pointer
+ KABC::Picture pic = mAddressee.photo();
+ if(!pic.isIntern())
+ pic = mAddressee.logo();
+ if(pic.isIntern())
+ {
+ //60 pixels seems okay.. kmail uses 60 btw
+ QPixmap qpixmap( pic.data().scaleWidth(60) );
+ setPixmap( Photo,qpixmap );
+ }
+
+ setText( Name, addressee.realName() );
+ setText( Email, addressee.preferredEmail() );
+}
+
+QString AddresseeItem::key( int column, bool ) const
+{
+ if (column == Email)
+ {
+ QString value = text(Email);
+ QRegExp emailRe("<\\S*>");
+ int match = emailRe.search(value);
+ if (match > -1)
+ value = value.mid(match + 1, emailRe.matchedLength() - 2);
+
+ return value.lower();
+ }
+
+ return text(column).lower();
+}
diff --git a/konversation/src/linkaddressbook/addresseeitem.h b/konversation/src/linkaddressbook/addresseeitem.h
new file mode 100644
index 0000000..2e2aec3
--- /dev/null
+++ b/konversation/src/linkaddressbook/addresseeitem.h
@@ -0,0 +1,66 @@
+/*
+ This file is part of libkabc.
+ Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef KABC_ADDRESSEEDIALOG_H
+#define KABC_ADDRESSEEDIALOG_H
+
+#include <qdict.h>
+
+#include <kdialogbase.h>
+#include <klineedit.h>
+#include <klistview.h>
+
+#include <kabc/addressbook.h>
+
+/**
+ @short Special ListViewItem
+*/
+class AddresseeItem : public KListViewItem
+{
+ public:
+
+ /**
+ Type of column
+ @li @p Name - Name in Addressee
+ @li @p Email - Email in Addressee
+ */
+ enum columns { Photo =0, Name = 1, Email = 2 };
+
+ /**
+ Constructor.
+
+ @param parent The parent listview.
+ @param addressee The associated addressee.
+ */
+ AddresseeItem( QListView *parent, const KABC::Addressee &addressee );
+
+ /**
+ Returns the addressee.
+ */
+ KABC::Addressee addressee() const { return mAddressee; }
+
+ /**
+ Method used by QListView to sort the items.
+ */
+ virtual QString key( int column, bool ascending ) const;
+
+ private:
+ KABC::Addressee mAddressee;
+};
+#endif
diff --git a/konversation/src/linkaddressbook/kimiface.h b/konversation/src/linkaddressbook/kimiface.h
new file mode 100644
index 0000000..990f980
--- /dev/null
+++ b/konversation/src/linkaddressbook/kimiface.h
@@ -0,0 +1,191 @@
+/*
+ kimiface.h - KDE Instant Messenger DCOP Interface
+
+ Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef KIMIFACE_H
+#define KIMIFACE_H
+
+#include <qpixmap.h>
+#include <dcopobject.h>
+#include <qstringlist.h>
+#include <kurl.h>
+
+/**
+ * Generic DCOP interface for KDE instant messenger applications
+ * Note one omission of this interface is the lack of control over the range of values used for protocols' names.
+ * @author Will Stephenson <lists@stevello.free-online.co.uk>
+ */
+class KIMIface : virtual public DCOPObject
+{
+ K_DCOP
+
+ k_dcop:
+ // ACCESSORS
+ // contact list
+ /**
+ * Obtain a list of IM-contactable entries in the KDE
+ * address book.
+ * @return a list of KABC uids.
+ */
+ virtual QStringList allContacts() = 0;
+ /**
+ * Obtain a list of KDE address book entries who are
+ * currently reachable.
+ * @return a list of KABC uids who can receive a message, even if online.
+ */
+ virtual QStringList reachableContacts() = 0;
+ /**
+ * Obtain a list of KDE address book entries who are
+ * currently online.
+ * @return a list of KABC uids who are online with unspecified presence.
+ */
+ virtual QStringList onlineContacts() = 0;
+ /**
+ * Obtain a list of KDE address book entries who may
+ * receive file transfers.
+ * @return a list of KABC uids capable of file transfer.
+ */
+ virtual QStringList fileTransferContacts() = 0;
+
+ // individual
+ /**
+ * Confirm if a given KABC uid is known to KIMProxy
+ * @param uid the KABC uid you are interested in.
+ * @return whether one of the chat programs KIMProxy talks to knows of this KABC uid.
+ */
+ virtual bool isPresent( const QString & uid ) = 0;
+ /**
+ * Obtain the IM app's idea of the contact's display name
+ * Useful if KABC lookups may be too slow
+ * @param uid KABC uid.
+ * @return The corresponding display name.
+ */
+ virtual QString displayName( const QString & uid ) = 0;
+ /**
+ * Obtain the IM presence as a i18ned string for the specified addressee
+ * @param uid the KABC uid you want the presence for.
+ * @return the i18ned string describing presence.
+ */
+ virtual QString presenceString( const QString & uid ) = 0;
+ /**
+ * Obtain the IM presence as a number (see KIMIface) for the specified addressee
+ * @param uid the KABC uid you want the presence for.
+ * @return a numeric representation of presence - currently one of 0 (Unknown), 1 (Offline), 2 (Connecting), 3 (Away), 4 (Online)
+ */
+ virtual int presenceStatus( const QString & uid ) = 0;
+ /**
+ * Indicate if a given uid can receive files
+ * @param uid the KABC uid you are interested in.
+ * @return Whether the specified addressee can receive files.
+ */
+ virtual bool canReceiveFiles( const QString & uid ) = 0;
+ /**
+ * Some media are unidirectional (eg, sending SMS via a web interface).
+ * @param uid the KABC uid you are interested in.
+ * @return Whether the specified addressee can respond.
+ */
+ virtual bool canRespond( const QString & uid ) = 0;
+ /**
+ * Get the KABC uid corresponding to the supplied IM address
+ * Protocols should be
+ * @param contactId the protocol specific identifier for the contact, eg UIN for ICQ, screenname for AIM, nick for IRC.
+ * @param protocol the protocol, eg one of "AIMProtocol", "MSNProtocol", "ICQProtocol",
+ * @return a KABC uid or null if none found/
+ */
+ virtual QString locate( const QString & contactId, const QString & protocol ) = 0;
+ // metadata
+ /**
+ * Obtain the icon representing IM presence for the specified addressee
+ * @param uid the KABC uid you want the presence for.
+ * @return a pixmap representing the uid's presence.
+ */
+ virtual QPixmap icon( const QString & uid ) = 0;
+ /**
+ * Get the supplied addressee's current context (home, work, or any).
+ * @param uid the KABC uid you want the context for.
+ * @return A QString describing the context, or null if not supported.
+ */
+ virtual QString context( const QString & uid ) = 0;
+ // App capabilities
+ /**
+ * Discover what protocols the application supports
+ * @return the set of protocols that the application supports
+ */
+ virtual QStringList protocols() = 0;
+
+ // ACTORS
+ /**
+ * Send a single message to the specified addressee
+ * Any response will be handled by the IM client as a normal
+ * conversation.
+ * @param uid the KABC uid you want to chat with.
+ * @param message the message to send them.
+ */
+ virtual void messageContact( const QString &uid, const QString& message ) = 0;
+
+ /**
+ * Open a chat to a contact, and optionally set some initial text
+ */
+ virtual void messageNewContact( const QString &contactId, const QString &protocol ) = 0;
+
+ /**
+ * Start a chat session with the specified addressee
+ * @param uid the KABC uid you want to chat with.
+ */
+ virtual void chatWithContact( const QString &uid ) = 0;
+
+ /**
+ * Send the file to the contact
+ * @param uid the KABC uid you are sending to.
+ * @param sourceURL a KURL to send.
+ * @param altFileName an alternate filename describing the file
+ * @param fileSize file size in bytes
+ */
+ virtual void sendFile(const QString &uid, const KURL &sourceURL,
+ const QString &altFileName = QString(), uint fileSize = 0) = 0;
+
+ // MUTATORS
+ // Contact list
+ /**
+ * Add a contact to the contact list
+ * @param contactId the protocol specific identifier for the contact, eg UIN for ICQ, screenname for AIM, nick for IRC.
+ * @param protocol the protocol, eg one of "AIMProtocol", "MSNProtocol", "ICQProtocol", ...
+ * @return whether the add succeeded. False may signal already present, protocol not supported, or add operation not supported.
+ */
+ virtual bool addContact( const QString &contactId, const QString &protocol ) = 0;
+ // SIGNALS
+ k_dcop_signals:
+ /**
+ * Indicates that a contact's presence has changed
+ * @param uid the contact whose presence changed.
+ * @param appId the dcop application id of the program the signal originates from.
+ * @param presence the new numeric presence @ref presenceStatus
+ */
+ void contactPresenceChanged( QString uid, QCString appId, int presence );
+};
+#endif
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/konversation/src/linkaddressbook/linkaddressbookui.cpp b/konversation/src/linkaddressbook/linkaddressbookui.cpp
new file mode 100644
index 0000000..9347075
--- /dev/null
+++ b/konversation/src/linkaddressbook/linkaddressbookui.cpp
@@ -0,0 +1,191 @@
+/*
+ Kontact's Link IRC Nick to Addressbook contact Wizard
+
+ This code was shamelessly stolen from kopete's add new contact wizard.
+
+ Copyright (c) 2004 by John Tapsell <john@geola.co.uk>
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+*************************************************************************
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+*************************************************************************
+*/
+
+#include "linkaddressbookui.h"
+#include "addressbook.h"
+#include "linkaddressbookui_base.h"
+#include "addresseeitem.h"
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdeversion.h>
+#include <kinputdialog.h>
+#include <kpushbutton.h>
+#include <kactivelabel.h>
+#include <kdebug.h>
+#include <klistview.h>
+#include <klistviewsearchline.h>
+#include <kabc/addressbook.h>
+#include <kabc/stdaddressbook.h>
+
+
+LinkAddressbookUI::LinkAddressbookUI( QWidget *parent, const char *name, const QString &ircnick, const QString &servername, const QString &servergroup, const QString &suggested_realname )
+: KDialogBase(Plain, i18n("Link IRC Nick to Addressbook Contact"), Ok|Cancel|Help, Ok, parent, name)
+{
+ QFrame* page = plainPage();
+ QGridLayout* pageLayout = new QGridLayout(page, 1, 1, 0, 0);
+ m_mainWidget = new LinkAddressbookUI_Base(page);
+ pageLayout->addWidget(m_mainWidget, 0, 0);
+
+ enableButtonOK(false);
+ setHelp("linkaddressbook");
+ m_addressBook = Konversation::Addressbook::self()->getAddressBook();
+
+ // Addressee validation connections
+ connect( m_mainWidget->addAddresseeButton, SIGNAL( clicked() ), SLOT( slotAddAddresseeClicked() ) );
+ connect( m_mainWidget->addresseeListView, SIGNAL( clicked(QListViewItem * ) ),
+ SLOT( slotAddresseeListClicked( QListViewItem * ) ) );
+ connect( m_mainWidget->addresseeListView, SIGNAL( selectionChanged( QListViewItem * ) ),
+ SLOT( slotAddresseeListClicked( QListViewItem * ) ) );
+ connect( m_mainWidget->addresseeListView, SIGNAL( spacePressed( QListViewItem * ) ),
+ SLOT( slotAddresseeListClicked( QListViewItem * ) ) );
+
+ connect( m_addressBook, SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( slotLoadAddressees() ) );
+ connect( Konversation::Addressbook::self(), SIGNAL(addresseesChanged()), this, SLOT(slotLoadAddressees()));
+
+ //We should add a clear KAction here. But we can't really do that with a designer file :\ this sucks
+
+ m_ircnick = ircnick;
+ m_lower_ircnick = m_ircnick.lower();
+ m_servername = servername;
+ m_servergroup = servergroup;
+ m_suggested_realname = suggested_realname;
+
+ m_mainWidget->addresseeListView->setColumnText(2, SmallIconSet("email"), i18n("Email") );
+
+ if(m_suggested_realname.isEmpty()) m_suggested_realname = suggested_realname;
+ Q_ASSERT(!ircnick.isEmpty());
+ m_mainWidget->kListViewSearchLine->setListView(m_mainWidget->addresseeListView);
+ slotLoadAddressees();
+
+ m_mainWidget->addresseeListView->setColumnWidthMode(0, QListView::Manual);
+ //Photo is 60, and it's nice to have a small gap, imho
+ m_mainWidget->addresseeListView->setColumnWidth(0, 63);
+
+}
+
+LinkAddressbookUI::~LinkAddressbookUI()
+{
+}
+
+/** Read in contacts from addressbook, and select the contact that is for our nick. */
+void LinkAddressbookUI::slotLoadAddressees()
+{
+ m_mainWidget->addresseeListView->clear();
+
+ QString realname;
+ int num_contacts_with_nick=0; //There shouldn't be more than 1 contact with this irc nick. Warn the user if there is.
+
+ KABC::AddressBook::Iterator it;
+ for( it = m_addressBook->begin(); it != m_addressBook->end(); ++it )
+ if(Konversation::Addressbook::self()->hasNick(*it, m_lower_ircnick, m_servername, m_servergroup))
+ {
+ realname = (*it).realName();
+ num_contacts_with_nick++;
+ (new AddresseeItem( m_mainWidget->addresseeListView, (*it) ))->setSelected(true);
+ } else
+ /*AddresseeItem *item =*/ new AddresseeItem( m_mainWidget->addresseeListView, (*it));
+
+ if(num_contacts_with_nick == 0)
+ m_mainWidget->lblHeader->setText(i18n("Choose the person who '%1' is.").arg(m_ircnick));
+ else if(num_contacts_with_nick == 1 && realname.isEmpty())
+ m_mainWidget->lblHeader->setText(i18n("Currently '%1' is associated with a contact.").arg(m_ircnick));
+ else if(num_contacts_with_nick == 1 && !realname.isEmpty())
+ m_mainWidget->lblHeader->setText(i18n("Currently '%1' is associated with contact '%2'.").arg(m_ircnick).arg(realname));
+ else
+ m_mainWidget->lblHeader->setText(i18n("<qt><b>Warning:</b> '%1' is currently being listed as belonging to multiple contacts. Please select the correct contact.</qt>").arg(m_ircnick));
+
+}
+
+void LinkAddressbookUI::slotAddAddresseeClicked()
+{
+ // Pop up add addressee dialog
+ if(!Konversation::Addressbook::self()->getAndCheckTicket()) return;
+ QString addresseeName = KInputDialog::getText( i18n( "New Address Book Entry" ),
+ i18n( "Name the new entry:" ),
+ m_suggested_realname, 0, this );
+
+ if ( !addresseeName.isEmpty() )
+ {
+ KABC::Addressee addr;
+ addr.setNameFromString( addresseeName );
+ m_addressBook->insertAddressee(addr);
+ Konversation::Addressbook::self()->saveTicket();
+ slotLoadAddressees();
+ }
+ else
+ {
+ Konversation::Addressbook::self()->releaseTicket();
+ }
+}
+
+void LinkAddressbookUI::slotAddresseeListClicked( QListViewItem *addressee )
+{
+ // enable ok if a valid addressee is selected
+ enableButtonOK(addressee ? addressee->isSelected() : false);
+}
+
+void LinkAddressbookUI::slotOk()
+{
+ //// set the KABC uid in the metacontact
+ AddresseeItem *item = 0L;
+ item = static_cast<AddresseeItem *>( m_mainWidget->addresseeListView->selectedItem() );
+
+ KABC::Addressee addr;
+ if ( item )
+ {
+
+ addr = item->addressee();
+ if(!Konversation::Addressbook::self()->getAndCheckTicket())
+ {
+ return;
+ }
+ Konversation::Addressbook::self()->associateNickAndUnassociateFromEveryoneElse(addr, m_ircnick, m_servername, m_servergroup);
+ if(!Konversation::Addressbook::self()->saveTicket())
+ {
+ return;
+ }
+ }
+ disconnect( m_addressBook, SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( slotLoadAddressees() ) );
+ deleteLater();
+ accept();
+}
+
+void LinkAddressbookUI::slotCancel()
+{
+ disconnect( m_addressBook, SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( slotLoadAddressees() ) );
+ deleteLater();
+ reject();
+}
+
+#include "linkaddressbookui.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/konversation/src/linkaddressbook/linkaddressbookui.h b/konversation/src/linkaddressbook/linkaddressbookui.h
new file mode 100644
index 0000000..87ebd52
--- /dev/null
+++ b/konversation/src/linkaddressbook/linkaddressbookui.h
@@ -0,0 +1,68 @@
+/*
+ linkaddressbookwizard.h - Kontact's Link IRC Nick to Addressbook contact Wizard
+
+ This code was shamelessly stolen from kopete's add new contact wizard.
+
+ Copyright (c) 2004 by John Tapsell <john@geola.co.uk>
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+*************************************************************************
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+*************************************************************************
+*/
+
+#ifndef LINKADDRESSBOOKUI_H
+#define LINKADDRESSBOOKUI_H
+
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+#include <kdialogbase.h>
+#include <kdebug.h>
+#include <kabc/addressbook.h>
+
+class QCheckListItem;
+class LinkAddressbookUI_Base;
+
+class LinkAddressbookUI : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ LinkAddressbookUI( QWidget *parent, const char *name, const QString &ircnick, const QString &servername, const QString &servergroup, const QString &suggested_realname);
+ ~LinkAddressbookUI();
+
+ private:
+ KABC::AddressBook* m_addressBook;
+ QString m_ircnick;
+ QString m_lower_ircnick; //Same as above, but in lower case, for comparisons.
+ QString m_servername;
+ QString m_servergroup;
+ QString m_suggested_realname;
+ LinkAddressbookUI_Base* m_mainWidget;
+
+ protected slots:
+ virtual void slotOk();
+ virtual void slotCancel();
+ void slotAddAddresseeClicked();
+ void slotAddresseeListClicked( QListViewItem *addressee );
+ /**
+ * Utility function, populates the addressee list
+ */
+ void slotLoadAddressees();
+
+};
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/konversation/src/linkaddressbook/linkaddressbookui_base.ui b/konversation/src/linkaddressbook/linkaddressbookui_base.ui
new file mode 100644
index 0000000..6e82416
--- /dev/null
+++ b/konversation/src/linkaddressbook/linkaddressbookui_base.ui
@@ -0,0 +1,174 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>LinkAddressbookUI_Base</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>LinkAddressbookUI_Base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>596</width>
+ <height>572</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>405</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="3" column="0">
+ <property name="name">
+ <cstring>addAddresseeButton</cstring>
+ </property>
+ <property name="text">
+ <string>Create New &amp;Entry...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Create a new entry in your address book</string>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lblHeader</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KListView" row="2" column="0" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Photo</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Email</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>addresseeListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>10</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Select the contact you want to communicate with via Instant Messaging</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblSearch</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>S&amp;earch:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kListViewSearchLine</cstring>
+ </property>
+ </widget>
+ <widget class="KListViewSearchLine">
+ <property name="name">
+ <cstring>kListViewSearchLine</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kactivelabel.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistviewsearchline.h</includehint>
+</includehints>
+<includes>
+ <include>klistviewsearchline.h</include>
+</includes>
+</UI>
diff --git a/konversation/src/linkaddressbook/nicklisttooltip.cpp b/konversation/src/linkaddressbook/nicklisttooltip.cpp
new file mode 100644
index 0000000..c2fcde7
--- /dev/null
+++ b/konversation/src/linkaddressbook/nicklisttooltip.cpp
@@ -0,0 +1,68 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ The class that controls what the tooltip looks like when you hover over a person in the nicklistview. This is used to show contact information about the person from the addressbook.
+ begin: Sun 25 July 2004
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#include "nicklisttooltip.h"
+#include "../nick.h"
+#include "../nicklistview.h"
+#include "../nickinfo.h"
+#include "../common.h"
+
+#include <klocale.h>
+#include <qtooltip.h>
+#include <qlistview.h>
+
+
+class NickListView;
+
+namespace Konversation
+{
+ KonversationNickListViewToolTip::KonversationNickListViewToolTip(QWidget *parent, NickListView *lv) : QToolTip(parent)
+ {
+ m_listView = lv;
+ }
+
+ KonversationNickListViewToolTip::~KonversationNickListViewToolTip()
+ {
+ }
+
+ void KonversationNickListViewToolTip::maybeTip( const QPoint &pos )
+ {
+ if( !parentWidget() || !m_listView )
+ return;
+
+ QListViewItem *item = m_listView->itemAt( pos );
+
+ if( !item )
+ return;
+
+ Nick *nick = dynamic_cast<Nick*>( item );
+
+ if(! nick )
+ return;
+
+ QString toolTip;
+ QRect itemRect = m_listView->itemRect( item );
+
+ uint leftMargin = m_listView->treeStepSize() *
+ ( item->depth() + ( m_listView->rootIsDecorated() ? 1 : 0 ) ) +
+ m_listView->itemMargin();
+ uint xAdjust = itemRect.left() + leftMargin;
+ uint yAdjust = itemRect.top();
+ QPoint relativePos( pos.x() - xAdjust, pos.y() - yAdjust );
+ toolTip = Konversation::removeIrcMarkup(nick->getChannelNick()->tooltip());
+ if(!toolTip.isEmpty())
+ tip(itemRect, toolTip);
+ }
+
+} // namespace Konversation
diff --git a/konversation/src/linkaddressbook/nicklisttooltip.h b/konversation/src/linkaddressbook/nicklisttooltip.h
new file mode 100644
index 0000000..068850c
--- /dev/null
+++ b/konversation/src/linkaddressbook/nicklisttooltip.h
@@ -0,0 +1,45 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ nicklisttooltip.h - The class that controls what the tooltip looks like when you hover over a person in the nicklistview. This is used to show contact information about the person from the addressbook.
+ begin: Sun 25 July 2004
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#ifndef KONVERSATIONNICKLISTTOOLTIP_H
+#define KONVERSATIONNICKLISTTOOLTIP_H
+
+#include "addressbook.h"
+
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qpoint.h>
+#include <qstring.h>
+
+#include <klocale.h>
+
+
+class NickListView;
+
+namespace Konversation
+{
+
+ class KonversationNickListViewToolTip : public QToolTip
+ {
+ public:
+ KonversationNickListViewToolTip(QWidget *parent, NickListView *lv);
+ virtual ~KonversationNickListViewToolTip();
+
+ void maybeTip( const QPoint &pos );
+
+ private:
+ NickListView *m_listView;
+ };
+}
+#endif
diff --git a/konversation/src/linkaddressbook/nicksonlinetooltip.cpp b/konversation/src/linkaddressbook/nicksonlinetooltip.cpp
new file mode 100644
index 0000000..b89cab6
--- /dev/null
+++ b/konversation/src/linkaddressbook/nicksonlinetooltip.cpp
@@ -0,0 +1,67 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ The class that controls what the tooltip looks like when you hover over a person in the nicklistview. This is used to show contact information about the person from the addressbook.
+ begin: Sun 25 July 2004
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+
+#include "nicklisttooltip.h"
+#include "../nick.h"
+#include "../nicklistview.h"
+#include "../nickinfo.h"
+#include "../nicksonline.h"
+
+#include <klocale.h>
+#include <qtooltip.h>
+#include <qlistview.h>
+#include <klistview.h>
+
+
+class NickListView;
+
+namespace Konversation
+{
+ KonversationNicksOnlineToolTip::KonversationNicksOnlineToolTip(QWidget *parent, NicksOnline *nicksOnline) : QToolTip(parent)
+ {
+ m_nicksOnline = nicksOnline;
+ }
+
+ KonversationNicksOnlineToolTip::~KonversationNicksOnlineToolTip()
+ {
+ }
+
+ void KonversationNicksOnlineToolTip::maybeTip( const QPoint &pos )
+ {
+ if( !parentWidget() || !m_nicksOnline || !m_nicksOnline->getNickListView() )
+ return;
+ KListView *m_listView = m_nicksOnline->getNickListView();
+ QListViewItem *item = m_listView->itemAt( pos );
+ if( !item )
+ return;
+ NickInfoPtr nickInfo = m_nicksOnline->getNickInfo(item);
+
+ if(!nickInfo )
+ return;
+ QString toolTip;
+ QRect itemRect = m_listView->itemRect( item );
+
+ uint leftMargin = m_listView->treeStepSize() *
+ ( item->depth() + ( m_listView->rootIsDecorated() ? 1 : 0 ) ) +
+ m_listView->itemMargin();
+ uint xAdjust = itemRect.left() + leftMargin;
+ uint yAdjust = itemRect.top();
+ QPoint relativePos( pos.x() - xAdjust, pos.y() - yAdjust );
+ toolTip = nickInfo->tooltip();
+ if(!toolTip.isEmpty())
+ tip(itemRect, toolTip);
+ }
+
+} // namespace Konversation
diff --git a/konversation/src/linkaddressbook/nicksonlinetooltip.h b/konversation/src/linkaddressbook/nicksonlinetooltip.h
new file mode 100644
index 0000000..79f0888
--- /dev/null
+++ b/konversation/src/linkaddressbook/nicksonlinetooltip.h
@@ -0,0 +1,45 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ nicklisttooltip.h - The class that controls what the tooltip looks like when you hover over a person in the nicklistview. This is used to show contact information about the person from the addressbook.
+ begin: Sun 25 July 2004
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#ifndef KONVERSATIONNICKSONLINETOOLTIP_H
+#define KONVERSATIONNICKSONLINETOOLTIP_H
+
+#include "addressbook.h"
+
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qpoint.h>
+#include <qstring.h>
+
+#include <klocale.h>
+
+
+class NicksOnline;
+
+namespace Konversation
+{
+
+ class KonversationNicksOnlineToolTip : public QToolTip
+ {
+ public:
+ KonversationNicksOnlineToolTip(QWidget *parent, NicksOnline *nicksOnline);
+ virtual ~KonversationNicksOnlineToolTip();
+
+ void maybeTip( const QPoint &pos );
+
+ private:
+ NicksOnline *m_nicksOnline;
+ };
+}
+#endif
diff --git a/konversation/src/log_preferences.ui b/konversation/src/log_preferences.ui
new file mode 100644
index 0000000..3af0047
--- /dev/null
+++ b/konversation/src/log_preferences.ui
@@ -0,0 +1,116 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Log_Config</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.</comment>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Log_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>528</width>
+ <height>310</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_Log</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Enable Logging</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_LowerLog</cstring>
+ </property>
+ <property name="text">
+ <string>Enable &amp;lower case logfile names</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_AddHostnameToLog</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Append hostname to logfile names</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Logfile &amp;path:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_LogfilePath</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="3" column="1">
+ <property name="name">
+ <cstring>kcfg_LogfilePath</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="mode">
+ <number>26</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>kcfg_LowerLog</tabstop>
+ <tabstop>kcfg_AddHostnameToLog</tabstop>
+ <tabstop>kcfg_LogfilePath</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/logfilereader.cpp b/konversation/src/logfilereader.cpp
new file mode 100644
index 0000000..22357f4
--- /dev/null
+++ b/konversation/src/logfilereader.cpp
@@ -0,0 +1,187 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Shows the content of a log file
+ begin: Fri Dec 5 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "logfilereader.h"
+#include "konversationapplication.h"
+#include "ircview.h"
+#include "ircviewbox.h"
+
+#include <qlayout.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdockarea.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qregexp.h>
+#include <qspinbox.h>
+#include <qstylesheet.h>
+#include <qwhatsthis.h>
+
+#include <kdialog.h>
+#include <ktoolbar.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kio/jobclasses.h>
+
+
+LogfileReader::LogfileReader(QWidget* parent, const QString& log) : ChatWindow(parent)
+{
+ setType(ChatWindow::LogFileReader);
+
+ fileName = log;
+ QDockArea* toolBarDock = new QDockArea(Qt::Horizontal,QDockArea::Normal,this,"logfile_toolbar_dock");
+ toolBar = new KToolBar(toolBarDock,"logfile_toolbar",true,true);
+
+ toolBar->insertButton("filesaveas",0,SIGNAL(clicked()),this,SLOT(saveLog()),true,i18n("Save As..."));
+
+ new QLabel(i18n("Show last:"),toolBar,"logfile_size_label");
+ sizeSpin = new QSpinBox(10,1000,10,toolBar,"logfile_size_spinbox");
+ QWhatsThis::add(sizeSpin, i18n("Use this box to set the maximum size of the log file. This setting does not take effect until you restart Konversation. Each log file may have a separate setting."));
+ sizeSpin->setValue(Preferences::logfileBufferSize());
+ sizeSpin->setSuffix(i18n(" KB"));
+ sizeSpin->installEventFilter(this);
+
+ toolBar->insertButton("reload",0,SIGNAL(clicked()),this,SLOT(updateView()),true,i18n("Reload"));
+ toolBar->insertButton("editdelete",0,SIGNAL(clicked()),this,SLOT(clearLog()),true,i18n("Clear Logfile"));
+
+ IRCViewBox* ircBox = new IRCViewBox(this, 0);
+ setTextView(ircBox->ircView());
+ QWhatsThis::add(getTextView(), i18n("The messages in the log file are displayed here. The oldest messages are at the top and the most recent are at the bottom."));
+
+ updateView();
+ resize(Preferences::logfileReaderSize());
+ ircBox->ircView()->setFocusPolicy(QWidget::StrongFocus);
+ setFocusPolicy(QWidget::StrongFocus);
+ setFocusProxy(ircBox->ircView());
+
+ connect(getTextView(), SIGNAL(gotFocus()), getTextView(), SLOT(setFocus()));
+}
+
+LogfileReader::~LogfileReader()
+{
+ Preferences::setLogfileReaderSize(size());
+ Preferences::setLogfileBufferSize(sizeSpin->value());
+
+ delete toolBar;
+}
+
+bool LogfileReader::eventFilter(QObject* /* watched */, QEvent* e)
+{
+ if (e->type() == QEvent::KeyPress)
+ {
+ QKeyEvent* ke = static_cast<QKeyEvent*>(e);
+
+ if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)
+ {
+ updateView();
+
+ return true;
+ }
+ else
+ return false;
+ }
+
+ return false;
+}
+
+void LogfileReader::updateView()
+{
+ // get maximum size of logfile to display
+ unsigned long pos=sizeSpin->value()*1024;
+ getTextView()->clear();
+
+ QFile file(fileName);
+
+ if(file.open(IO_ReadOnly))
+ {
+ QTextStream stream(&file);
+ stream.setEncoding(QTextStream::UnicodeUTF8);
+
+ // Set file pointer to <pos> bytes from the end
+ if(stream.device()->size()>pos)
+ stream.device()->at(stream.device()->size()-pos);
+ // Skip first line, since it may be incomplete
+ stream.readLine();
+ QString str;
+
+ while(!stream.eof())
+ {
+ str = QStyleSheet::escape(stream.readLine());
+ getTextView()->appendRaw(str, true);
+ }
+
+ stream.unsetDevice();
+ file.close();
+ }
+}
+
+void LogfileReader::clearLog()
+{
+ if(KMessageBox::warningContinueCancel(this,
+ i18n("Do you really want to permanently discard all log information of this file?"),
+ i18n("Clear Logfile"),
+ KStdGuiItem::del(),
+ "ClearLogfileQuestion")==KMessageBox::Continue)
+ {
+ QFile::remove(fileName);
+ updateView();
+ }
+}
+
+void LogfileReader::saveLog()
+{
+ KMessageBox::information(this,
+ i18n("Note: By saving the logfile you will save all data in the file, not only the part you can see in this viewer."),
+ i18n("Save Logfile"),
+ "SaveLogfileNote");
+
+ QString destination=KFileDialog::getSaveFileName(fileName,
+ QString(),
+ this,
+ i18n("Choose Destination Folder"));
+ if(!destination.isEmpty())
+ {
+ // replace # with %25 to make it URL conforming
+ KIO::Job* job=KIO::copy(KURL(fileName.replace("#","%23")),
+ KURL(destination),
+ true);
+
+ connect(job,SIGNAL(result(KIO::Job*)),this,SLOT(copyResult(KIO::Job*)));
+ }
+}
+
+void LogfileReader::copyResult(KIO::Job* job)
+{
+ if(job->error()) job->showErrorDialog(this);
+
+ job->deleteLater();
+}
+
+void LogfileReader::closeLog()
+{
+ delete this;
+}
+
+void LogfileReader::childAdjustFocus()
+{
+ getTextView()->setFocus();
+}
+
+int LogfileReader::margin() { return KDialog::marginHint(); }
+int LogfileReader::spacing() { return KDialog::spacingHint(); }
+bool LogfileReader::searchView() { return true; }
+
+#include "logfilereader.moc"
diff --git a/konversation/src/logfilereader.h b/konversation/src/logfilereader.h
new file mode 100644
index 0000000..0327c74
--- /dev/null
+++ b/konversation/src/logfilereader.h
@@ -0,0 +1,59 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Shows the content of a log file
+ begin: Fri Dec 5 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef LOGFILEREADER_H
+#define LOGFILEREADER_H
+
+#include "chatwindow.h"
+
+#include <kio/job.h>
+
+
+class KToolBar;
+class QSpinBox;
+
+class LogfileReader : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ LogfileReader(QWidget* parent, const QString& log);
+ ~LogfileReader();
+
+ virtual bool closeYourself() { closeLog(); return true; }
+ virtual bool searchView();
+
+ virtual bool eventFilter(QObject* watched, QEvent* e);
+
+
+ protected slots:
+ void updateView();
+ void clearLog();
+ void saveLog();
+ void closeLog();
+ void copyResult(KIO::Job* job);
+
+
+ protected:
+ int margin();
+ int spacing();
+
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ KToolBar* toolBar;
+ QSpinBox* sizeSpin;
+ QString fileName;
+};
+#endif
diff --git a/konversation/src/main.cpp b/konversation/src/main.cpp
new file mode 100644
index 0000000..2deede7
--- /dev/null
+++ b/konversation/src/main.cpp
@@ -0,0 +1,103 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Where it all began ...
+ begin: Die Jan 15 05:59:05 CET 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <kdebug.h>
+
+#include "konversationapplication.h"
+#include "version.h"
+#include "commit.h"
+
+/*
+ Don't use i18n() here, use I18N_NOOP() instead!
+ i18n() will only work as soon as a kapplication object was made.
+*/
+
+static const char* shortDescription=I18N_NOOP("A user friendly IRC client");
+
+static const KCmdLineOptions options[] =
+{
+ { "+[url]", I18N_NOOP("irc:// URL or server hostname"), 0 },
+ { "server <server>", I18N_NOOP("Server to connect"), 0 },
+ { "port <port>", I18N_NOOP("Port to use"), "6667"},
+ { "channel <channel>", I18N_NOOP("Channel to join after connection"), ""},
+ { "nick <nickname>", I18N_NOOP("Nickname to use"),""},
+ { "password <password>", I18N_NOOP("Password for connection"),""},
+ { "ssl", I18N_NOOP("Use SSL for connection"),"false"},
+ KCmdLineLastOption
+};
+
+int main(int argc, char* argv[])
+{
+ KAboutData aboutData("konversation",
+ I18N_NOOP("Konversation"),
+ KONVI_VERSION,
+ shortDescription,
+ KAboutData::License_GPL,
+ I18N_NOOP("(C) 2002-2008 by the Konversation team"),
+ I18N_NOOP("Konversation is a client for the Internet Relay Chat (IRC) protocol.\n"
+ "Meet friends on the net, make new acquaintances and lose yourself in\n"
+ "talk about your favorite subject."),
+ "http://konversation.kde.org/");
+
+ aboutData.addAuthor("Dario Abatianni",I18N_NOOP("Original Author, Project Founder"),"eisfuchs@tigress.com");
+ aboutData.addAuthor("Peter Simonsson",I18N_NOOP("Maintainer"),"psn@linux.se");
+ aboutData.addAuthor("Eike Hein",I18N_NOOP("Maintainer, Release Manager, User interface, Protocol handling"),"hein@kde.org");
+ aboutData.addAuthor("Shintaro Matsuoka",I18N_NOOP("DCC, Encoding handling, OSD positioning"),"shin@shoegazed.org");
+ aboutData.addAuthor("Eli MacKenzie",I18N_NOOP("Protocol handling, Input line"),"argonel@gmail.com");
+ aboutData.addAuthor("İsmail Dönmez",I18N_NOOP("Blowfish, SSL support, KNetwork port, Colored nicks, Nicklist themes"),"ismail@kde.org");
+ aboutData.addAuthor("John Tapsell",I18N_NOOP("Refactoring, KAddressBook/Kontact integration"), "john@geola.co.uk");
+
+ aboutData.addCredit("Olivier Bédard",I18N_NOOP("Website hosting"));
+ aboutData.addCredit("Jędrzej Lisowski",I18N_NOOP("Website maintenance"),"yesoos@gmail.com");
+ aboutData.addCredit("Christian Muehlhaeuser",I18N_NOOP("Multiple modes extension, Close widget placement, OSD functionality"),"chris@chris.de");
+ aboutData.addCredit("Gary Cramblitt",I18N_NOOP("Documentation, Watched nicks online improvements, Custom web browser extension"),"garycramblitt@comcast.net");
+ aboutData.addCredit("Matthias Gierlings",I18N_NOOP("Color configurator, Highlight dialog"),"gismore@users.sourceforge.net");
+ aboutData.addCredit("Alex Zepeda",I18N_NOOP("DCOP interface"),"garbanzo@hooked.net");
+ aboutData.addCredit("Stanislav Karchebny",I18N_NOOP("Non-Latin1-Encodings"),"berkus@users.sourceforge.net");
+ aboutData.addCredit("Mickael Marchand",I18N_NOOP("Konsole part view"),"marchand@kde.org");
+ aboutData.addCredit("Michael Goettsche",I18N_NOOP("Quick connect, Ported new OSD, other features and bugfixes"),"michael.goettsche@kdemail.net");
+ aboutData.addCredit("Benjamin Meyer",I18N_NOOP("A Handful of fixes and code cleanup"),"ben+konversation@meyerhome.net");
+ aboutData.addCredit("Jakub Stachowski",I18N_NOOP("Drag&Drop improvements"),"qbast@go2.pl");
+ aboutData.addCredit("Sebastian Sariego",I18N_NOOP("Artwork"),"segfault@kde.cl");
+ aboutData.addCredit("Renchi Raju",I18N_NOOP("Firefox style searchbar"));
+ aboutData.addCredit("Michael Kreitzer",I18N_NOOP("Raw modes, Tab grouping per server, Ban list"),"mrgrim@gr1m.org");
+ aboutData.addCredit("Frauke Oster",I18N_NOOP("System tray patch"),"frauke@frsv.de");
+ aboutData.addCredit("Lucijan Busch",I18N_NOOP("Bug fixes"),"lucijan@kde.org");
+ aboutData.addCredit("Sascha Cunz",I18N_NOOP("Extended user modes patch"),"mail@sacu.de");
+ aboutData.addCredit("Steve Wollkind",I18N_NOOP("Close visible tab with shortcut patch"),"steve@njord.org");
+ aboutData.addCredit("Thomas Nagy",I18N_NOOP("Cycle tabs with mouse scroll wheel"),"thomas.nagy@eleve.emn.fr");
+ aboutData.addCredit("Tobias Olry",I18N_NOOP("Channel ownership mode patch"),"tobias.olry@web.de");
+ aboutData.addCredit("Ruud Nabben",I18N_NOOP("Option to enable IRC color filtering"),"r.nabben@gawab.com");
+ aboutData.addCredit("Lothar Braun",I18N_NOOP("Bug fixes"),"mail@lobraun.de");
+ aboutData.addCredit("Ivor Hewitt",I18N_NOOP("Bug fixes, OSD work, clearing topics"),"ivor@ivor.org");
+ aboutData.addCredit("Emil Obermayr",I18N_NOOP("Sysinfo script"),"nobs@tigress.com");
+ aboutData.addCredit("Stanislav Nikolov",I18N_NOOP("Bug fixes"),"valsinats@gmail.com");
+ aboutData.addCredit("Juan Carlos Torres",I18N_NOOP("Auto-join context menu"),"carlosdgtorres@gmail.com");
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(options);
+ KApplication::addCmdLineOptions();
+
+ if (!KUniqueApplication::start()) return 0;
+
+ KonversationApplication app;
+
+ return app.exec();
+}
diff --git a/konversation/src/modebutton.cpp b/konversation/src/modebutton.cpp
new file mode 100644
index 0000000..445db55
--- /dev/null
+++ b/konversation/src/modebutton.cpp
@@ -0,0 +1,44 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Wed Feb 6 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "modebutton.h"
+
+
+ModeButton::ModeButton(const QString& label,QWidget* parent,int newId) :
+QPushButton::QPushButton(label,parent)
+{
+ id=newId;
+ on=false;
+ setToggleButton(true);
+ connect(this,SIGNAL (clicked()),this,SLOT (wasClicked()) );
+}
+
+ModeButton::~ModeButton()
+{
+}
+
+void ModeButton::setOn(bool state)
+{
+ on=state;
+ QPushButton::setOn(state);
+}
+
+void ModeButton::wasClicked()
+{
+ emit clicked(id,!on);
+ // Keep button in old state, since we don't know if mode change will
+ // eventually work. If we aren't channel operator, it won't.
+ setOn(on);
+}
+
+#include "modebutton.moc"
diff --git a/konversation/src/modebutton.h b/konversation/src/modebutton.h
new file mode 100644
index 0000000..e8895d6
--- /dev/null
+++ b/konversation/src/modebutton.h
@@ -0,0 +1,40 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Wed Feb 6 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef MODEBUTTON_H
+#define MODEBUTTON_H
+
+#include <qpushbutton.h>
+
+
+class ModeButton : public QPushButton
+{
+ Q_OBJECT
+
+ public:
+ ModeButton(const QString& label,QWidget* parent,int id);
+ ~ModeButton();
+
+ void setOn(bool state);
+
+ signals:
+ void clicked(int id,bool on);
+
+ public slots:
+ void wasClicked();
+
+ protected:
+ int id;
+ bool on;
+};
+#endif
diff --git a/konversation/src/multilineedit.cpp b/konversation/src/multilineedit.cpp
new file mode 100644
index 0000000..19e1eef
--- /dev/null
+++ b/konversation/src/multilineedit.cpp
@@ -0,0 +1,79 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#include "multilineedit.h"
+#include "multilinetextedit.h"
+#include "konversationapplication.h"
+
+#include <qlayout.h>
+
+#include <klocale.h>
+
+
+QString MultilineEdit::returnText; // static
+
+MultilineEdit::MultilineEdit(QWidget* parent, const QString& text) :
+KDialogBase(parent,"multiline_edit_dialog",true,i18n("Edit Multiline Paste"),
+KDialogBase::User1 | KDialogBase::Ok | KDialogBase::Cancel,KDialogBase::Ok,true,
+KGuiItem(i18n("Add &Quotation Indicators")))
+{
+ // Create the top level widget
+ QWidget* page=new QWidget(this);
+ setMainWidget(page);
+ // Add the layout to the widget
+ QVBoxLayout* dialogLayout=new QVBoxLayout(page);
+ dialogLayout->setSpacing(spacingHint());
+ // add the text editor
+ textEditor=new MultilineTextEdit(page,"multiline_text_editor");
+ textEditor->setTextFormat(PlainText);
+ textEditor->setText(text);
+ returnText=text;
+
+ dialogLayout->addWidget(textEditor);
+
+ setInitialSize(Preferences::multilineEditSize());
+ show();
+}
+
+MultilineEdit::~MultilineEdit()
+{
+ Preferences::setMultilineEditSize(size());
+}
+
+void MultilineEdit::slotCancel()
+{
+ returnText=QString();
+ KDialogBase::slotCancel();
+}
+
+void MultilineEdit::slotOk()
+{
+ returnText=textEditor->text();
+ KDialogBase::slotOk();
+}
+
+void MultilineEdit::slotUser1()
+{
+ QStringList lines=QStringList::split("\n",textEditor->text(),true);
+ for( QStringList::iterator it=lines.begin() ; it!=lines.end() ; ++it )
+ (*it) = "> " + (*it);
+ textEditor->setText(lines.join("\n"));
+}
+
+QString MultilineEdit::edit(QWidget* parent, const QString& text)
+{
+ MultilineEdit dlg(parent,text);
+ dlg.exec();
+
+ return returnText;
+}
+
+#include "multilineedit.moc"
diff --git a/konversation/src/multilineedit.h b/konversation/src/multilineedit.h
new file mode 100644
index 0000000..db86cc0
--- /dev/null
+++ b/konversation/src/multilineedit.h
@@ -0,0 +1,40 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#ifndef MULTILINEEDIT_H
+#define MULTILINEEDIT_H
+
+#include <kdialogbase.h>
+
+
+class QWidget;
+class MultilineTextEdit;
+
+class MultilineEdit : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ MultilineEdit(QWidget* parent, const QString& text);
+ ~MultilineEdit();
+
+ static QString edit(QWidget* parent, const QString& text);
+
+ protected slots:
+ void slotOk();
+ void slotCancel();
+ void slotUser1();
+
+ protected:
+ MultilineTextEdit* textEditor;
+ static QString returnText;
+};
+#endif
diff --git a/konversation/src/multilinetextedit.cpp b/konversation/src/multilinetextedit.cpp
new file mode 100644
index 0000000..d5f7869
--- /dev/null
+++ b/konversation/src/multilinetextedit.cpp
@@ -0,0 +1,175 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#include "multilinetextedit.h"
+
+#include <qpainter.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+
+
+MultilineTextEdit::MultilineTextEdit(QWidget* parent,const char* name) : QTextEdit(parent,name)
+{
+ // make sure, our whitespace highlighting gets called whenever needed
+ connect(this,SIGNAL(textChanged()),this,SLOT(drawWhitespaces()));
+ connect(this,SIGNAL(cursorPositionChanged(int,int)),this,SLOT(cursorChanged(int,int)));
+}
+
+MultilineTextEdit::~MultilineTextEdit()
+{
+}
+
+void MultilineTextEdit::drawContents(QPainter* p,int clipx,int clipy,int clipw,int cliph)
+{
+ // redraw text
+ QTextEdit::drawContents(p,clipx,clipy,clipw,cliph);
+ // overlay whitespace markup
+ drawWhitespaces();
+}
+
+void MultilineTextEdit::drawWhitespaces()
+{
+ // prepare a rectangle to store the width of the whitespace found
+ QRect space;
+ // get the painter for the text area
+ QPainter pa(viewport());
+
+ // get a sane color
+ QColor col=colorGroup().link();
+ // and a brush of the same color
+ QBrush fillBrush(col);
+ // use it for line drawing
+ pa.setPen(col);
+ // and for filling
+ pa.setBrush(fillBrush);
+
+ // prepare the carriage return coordinates array
+ QPointArray cr(4);
+ // and the tabulator arrow coordinate array
+ QPointArray tab(7);
+
+ // whitespace expression
+ QRegExp regex("\\s");
+
+ // line buffer
+ QString line;
+
+ int x,y,pos,paragraph;
+ // start looking in every paragraph
+ for(paragraph=0;paragraph<paragraphs();paragraph++)
+ {
+ // get paragraph text
+ line=text(paragraph);
+ // start looking for whitespaces from the beginning
+ pos=0;
+ while((pos=line.find(regex,pos))!=-1)
+ {
+ // whitespace found is not the carriage return at the end of the line?
+ if(pos<((int)line.length()-1))
+ {
+ // get whitespace rectangle
+ space=mapToView(paragraph,pos);
+ // extract x/y coordinates
+ x=space.width()/2-1+space.x();
+ y=space.height()/2-1+space.y();
+
+ // if it was a regular blank ...
+ if(regex.cap(0)==" ")
+ {
+ // dras a simple small square
+ pa.drawRect(x-1,y,2,2);
+ }
+ // if it was a tabulator
+ else if(regex.cap(0)=="\t")
+ {
+ // calculate arrow points and draw them filled
+ tab.putPoints(0,7, x-5,y-1, x,y-1, x,y-3, x+3,y, x,y+3, x,y+1, x-5,y+1);
+ pa.drawPolygon(tab);
+ }
+ }
+ // go to next position and resume looking for more whitespaces
+ pos++;
+ } // while
+
+ // end of line, get carriage return position
+ space=mapToView(paragraph,line.length()-1);
+ // extract x/y positions
+ x=space.width()/2-1+space.x();
+ y=space.height()/2-1+space.y();
+ // calculate carriage return triangle coordinates and draw them filled
+ cr.putPoints(0,4, x,y, x,y+1, x+4, y+5, x+4, y-4);
+ pa.drawPolygon(cr);
+ } // for
+}
+
+void MultilineTextEdit::cursorChanged(int /* p */ ,int /* i */)
+{
+ // update markup, since cursor destroys it
+ drawWhitespaces();
+}
+
+// code below from kbabel and adapted by me (Eisfuchs). Thanks, Guys!
+
+QRect MultilineTextEdit::mapToView(int para,int index)
+{
+ if( para < 0 || para > paragraphs() ||
+ index < 0 || index > paragraphLength(para) )
+ return QRect(); //invalid rectangle
+
+ const QFontMetrics& fm = fontMetrics();
+ const QString& paratext = text(para);
+
+ // Find index of the first character on the same line as parameter
+ // 'index' using binary search. Very fast, even for long texts.
+ int linestart = 0;
+ int indexline = lineOfChar( para, index );
+ if ( indexline > 0 )
+ {
+ int min = 0, max = index;
+ int i = (min + max)/2;
+ int iline = lineOfChar( para, i );
+ while ( iline != indexline-1 ||
+ lineOfChar( para, i+1 ) != indexline )
+ {
+ Q_ASSERT( min != max && min != i && max != i );
+ if ( iline < indexline )
+ min = i;
+ else
+ max = i;
+ i = (min + max)/2;
+ iline = lineOfChar( para, i );
+ }
+ linestart = i+1;
+ }
+ Q_ASSERT( linestart >= 0 );
+
+ int linewidth = fm.size(ExpandTabs, paratext.mid( linestart, index-linestart )).width();
+ int linewidth2 = fm.size(ExpandTabs, paratext.mid( linestart, index-linestart+1 )).width();
+
+ // FIXME as soon as it's possible to ask real margins from QTextEdit:
+ const int left_margin = 4;
+ // const int top_margin = 4;
+
+ QPainter painter( viewport());
+ const QRect& linerect = paragraphRect(para);
+
+ return QRect(
+ contentsToViewport( QPoint(
+ left_margin + linerect.left() + linewidth ,
+ /*top_margin + */linerect.top() + indexline * fm.lineSpacing() + fm.leading())),
+ QSize(
+ linewidth2-linewidth,
+ fm.lineSpacing()
+ ));
+}
+
+#include "multilinetextedit.moc"
diff --git a/konversation/src/multilinetextedit.h b/konversation/src/multilinetextedit.h
new file mode 100644
index 0000000..e0f71aa
--- /dev/null
+++ b/konversation/src/multilinetextedit.h
@@ -0,0 +1,41 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#ifndef MULTILINETEXTEDIT_H
+#define MULTILINETEXTEDIT_H
+
+#include <qtextedit.h>
+
+
+class QPaintEvent;
+class QWheelEvent;
+
+class MultilineTextEdit : public QTextEdit
+{
+ Q_OBJECT
+
+ public:
+ explicit MultilineTextEdit(QWidget* parent=0,const char* name=0);
+ ~MultilineTextEdit();
+
+ protected slots:
+ void drawWhitespaces();
+ void cursorChanged(int,int);
+
+ protected:
+ // reimplemented
+ virtual void drawContents(QPainter* p,int clipx,int clipy,int clipw,int cliph);
+
+ // the stuff below is copied from kbabel. Thanks, guys!
+ QRect mapToView(int paragraph,int index);
+};
+
+#endif
diff --git a/konversation/src/nick.cpp b/konversation/src/nick.cpp
new file mode 100644
index 0000000..1ea7ffc
--- /dev/null
+++ b/konversation/src/nick.cpp
@@ -0,0 +1,262 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Fri Jan 25 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "nick.h"
+#include "addressbook.h"
+
+#include <qtextstream.h>
+
+#include <kdebug.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kabc/phonenumber.h>
+
+#include "konversationapplication.h"
+#include "preferences.h"
+
+Nick::Nick(KListView *listView,
+const ChannelNickPtr& channelnick)
+ : QObject (),
+ KListViewItem (listView, listView->lastItem(), QString::null,
+ channelnick->getNickname(), channelnick->getHostmask())
+{
+ m_channelnickptr = channelnick;
+
+ Q_ASSERT(channelnick);
+ if(!channelnick) return;
+
+ m_flags = 0;
+ m_height = height();
+
+ refresh();
+
+ connect(this, SIGNAL(refreshed()), listView, SLOT(startResortTimer()));
+ connect(getChannelNick(), SIGNAL(channelNickChanged()), SLOT(refresh()));
+ connect(getChannelNick()->getNickInfo(), SIGNAL(nickInfoChanged()), SLOT(refresh()));
+}
+
+Nick::~Nick()
+{
+}
+
+ChannelNickPtr Nick::getChannelNick() const
+{
+ Q_ASSERT(m_channelnickptr);
+ return m_channelnickptr;
+}
+
+void Nick::refresh()
+{
+ int flags = 0;
+ NickInfo* nickInfo = getChannelNick()->getNickInfo();
+ bool away = false;
+
+ if ( nickInfo )
+ away = nickInfo->isAway();
+
+ if(away)
+ flags=1;
+
+ Images* images = KonversationApplication::instance()->images();
+ QPixmap icon;
+
+ if ( getChannelNick()->isOwner() )
+ {
+ flags += 64;
+ icon = images->getNickIcon( Images::Owner, away );
+ }
+ else if ( getChannelNick()->isAdmin() )
+ {
+ flags += 128;
+ icon = images->getNickIcon( Images::Admin, away );
+ }
+ else if ( getChannelNick()->isOp() )
+ {
+ flags += 32;
+ icon = images->getNickIcon( Images::Op, away );
+ }
+ else if ( getChannelNick()->isHalfOp() )
+ {
+ flags += 16;
+ icon = images->getNickIcon( Images::HalfOp, away );
+ }
+ else if ( getChannelNick()->hasVoice() )
+ {
+ flags += 8;
+ icon = images->getNickIcon( Images::Voice, away );
+ }
+ else
+ {
+ flags += 4;
+ icon = images->getNickIcon( Images::Normal, away );
+ }
+
+ setPixmap( 0, icon );
+
+ KABC::Picture pic = nickInfo->getAddressee().photo();
+
+ if(!pic.isIntern())
+ {
+ pic = nickInfo->getAddressee().logo();
+ }
+
+ if(pic.isIntern())
+ {
+ QPixmap qpixmap(pic.data().scaleHeight(m_height));
+ setPixmap(1,qpixmap);
+ }
+
+ QString newtext1 = calculateLabel1();
+ if(newtext1 != text(1))
+ {
+ setText(1, newtext1);
+ flags += 2;
+ }
+
+ setText(2, calculateLabel2());
+ repaint();
+
+ if(m_flags != flags)
+ {
+ m_flags = flags;
+ emit refreshed(); // Resort nick list
+ }
+}
+
+QString Nick::calculateLabel1()
+{
+ NickInfoPtr nickinfo = getChannelNick()->getNickInfo();
+ KABC::Addressee addressee = nickinfo->getAddressee();
+
+ if(!addressee.realName().isEmpty()) //if no addressee, realName will be empty
+ {
+ return nickinfo->getNickname() + " (" + addressee.realName() + ')';
+ }
+ else if(Preferences::showRealNames() && !nickinfo->getRealName().isEmpty())
+ {
+ return nickinfo->getNickname() + " (" + nickinfo->getRealName() + ')';
+ }
+
+ return nickinfo->getNickname();
+}
+
+QString Nick::calculateLabel2()
+{
+ return getChannelNick()->getNickInfo()->getHostmask();
+}
+
+int Nick::compare(QListViewItem* item,int col,bool ascending) const
+{
+ Nick* otherItem = static_cast<Nick*>(item);
+
+ if(Preferences::sortByActivity())
+ {
+ uint thisRecentActivity = getChannelNick()->recentActivity();
+ uint otherRecentActivity = otherItem->getChannelNick()->recentActivity();
+ if(thisRecentActivity > otherRecentActivity)
+ {
+ return -1;
+ }
+ if(thisRecentActivity < otherRecentActivity)
+ {
+ return 1;
+ }
+ uint thisTimestamp = getChannelNick()->timeStamp();
+ uint otherTimestamp = otherItem->getChannelNick()->timeStamp();
+ if(thisTimestamp > otherTimestamp)
+ {
+ return -1;
+ }
+ if(thisTimestamp < otherTimestamp)
+ {
+ return 1;
+ }
+ }
+
+ if(Preferences::sortByStatus())
+ {
+ int thisFlags = getSortingValue();
+ int otherFlags = otherItem->getSortingValue();
+
+ if(thisFlags > otherFlags)
+ {
+ return 1;
+ }
+ if(thisFlags < otherFlags)
+ {
+ return -1;
+ }
+ }
+
+ QString thisKey;
+ QString otherKey;
+
+ if(col > 1) //the reason we need this: enabling hostnames adds another column
+ {
+ if(Preferences::sortCaseInsensitive())
+ {
+ thisKey = thisKey.lower();
+ otherKey = otherKey.lower();
+ }
+ else
+ {
+ thisKey = key(col, ascending);
+ otherKey = otherItem->key(col, ascending);
+ }
+ }
+ else if(col == 1)
+ {
+ if(Preferences::sortCaseInsensitive())
+ {
+ thisKey = getChannelNick()->loweredNickname();
+ otherKey = otherItem->getChannelNick()->loweredNickname();
+ }
+ else
+ {
+ thisKey = key(col, ascending);
+ otherKey = otherItem->key(col, ascending);
+ }
+ }
+
+ return thisKey.compare(otherKey);
+}
+
+void Nick::paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align )
+{
+ QColorGroup cg2 = cg;
+ NickInfo* nickInfo = getChannelNick()->getNickInfo();
+
+ if(nickInfo->isAway())
+ {
+ cg2.setColor(QColorGroup::Text, kapp->palette(listView()).disabled().text());
+ }
+
+ KListViewItem::paintCell(p,cg2,column,width,align);
+}
+
+int Nick::getSortingValue() const
+{
+ int flags;
+ QString sortingOrder = Preferences::sortOrder();
+
+ if(getChannelNick()->isOwner()) flags=sortingOrder.find('q');
+ else if(getChannelNick()->isAdmin()) flags=sortingOrder.find('p');
+ else if(getChannelNick()->isOp() ) flags=sortingOrder.find('o');
+ else if(getChannelNick()->isHalfOp()) flags=sortingOrder.find('h');
+ else if(getChannelNick()->hasVoice()) flags=sortingOrder.find('v');
+ else flags=sortingOrder.find('-');
+
+ return flags;
+}
+
+#include "nick.moc"
diff --git a/konversation/src/nick.h b/konversation/src/nick.h
new file mode 100644
index 0000000..1a5ae59
--- /dev/null
+++ b/konversation/src/nick.h
@@ -0,0 +1,55 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Fri Jan 25 2002
+ copyright: (C) 2002 by Dario Abatianni
+ (C) 2007 Peter Simonsson <peter.simonsson@gmail.com>
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef NICK_H
+#define NICK_H
+
+#include <qobject.h>
+#include <klistview.h>
+
+#include "channelnick.h"
+
+class Nick : public QObject, public KListViewItem
+{
+ Q_OBJECT
+ public:
+ Nick(KListView *listView,
+ const ChannelNickPtr& channelnick);
+ ~Nick();
+
+ ChannelNickPtr getChannelNick() const;
+ int getSortingValue() const;
+
+ virtual void paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align);
+ virtual int compare(QListViewItem* item,int col,bool ascending) const;
+
+ public slots:
+ void refresh();
+
+ signals:
+ void refreshed();
+
+ protected:
+ QString calculateLabel1();
+ QString calculateLabel2();
+
+ protected:
+ ChannelNickPtr m_channelnickptr;
+
+ QString label;
+ int m_height;
+ int m_flags;
+ bool m_away;
+};
+#endif
diff --git a/konversation/src/nickinfo.cpp b/konversation/src/nickinfo.cpp
new file mode 100644
index 0000000..72853aa
--- /dev/null
+++ b/konversation/src/nickinfo.cpp
@@ -0,0 +1,445 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Nick Information
+ begin: Sat Jan 17 2004
+ copyright: (C) 2004 by Gary Cramblitt
+ email: garycramblitt@comcast.net
+*/
+
+/*
+ The NickInfo object is a data container for information about a single nickname.
+ It is owned by the Server object and should NOT be deleted by anything other than Server.
+ Store a pointer to this with NickInfoPtr
+*/
+
+#include "nickinfo.h"
+#include "konversationapplication.h"
+#include "linkaddressbook/addressbook.h"
+#include "linkaddressbook/linkaddressbookui.h"
+#include "konversationmainwindow.h"
+#include "viewcontainer.h"
+#include "server.h"
+
+#include <qtooltip.h>
+
+#include <klocale.h>
+
+
+NickInfo::NickInfo(const QString& nick, Server* server): KShared()
+{
+ m_addressee=Konversation::Addressbook::self()->getKABCAddresseeFromNick(nick, server->getServerName(), server->getDisplayName());
+ m_nickname = nick;
+ m_loweredNickname = nick.lower();
+ m_owningServer = server;
+ m_away = false;
+ m_notified = false;
+ m_identified = false;
+ m_printedOnline = true;
+
+ if(!m_addressee.isEmpty())
+ Konversation::Addressbook::self()->emitContactPresenceChanged(m_addressee.uid(), 4);
+
+ connect( Konversation::Addressbook::self()->getAddressBook(), SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( refreshAddressee() ) );
+ connect( Konversation::Addressbook::self(), SIGNAL(addresseesChanged()), this, SLOT(refreshAddressee()));
+
+ m_changedTimer = new QTimer( this);
+ connect(m_changedTimer, SIGNAL( timeout()), SLOT(emitNickInfoChanged()));
+
+ // reset nick color
+ m_nickColor = 0;
+}
+
+NickInfo::~NickInfo()
+{
+ if(!m_addressee.isEmpty())
+ Konversation::Addressbook::self()->emitContactPresenceChanged(m_addressee.uid(), 1);
+}
+
+// Get properties of NickInfo object.
+QString NickInfo::getNickname() const { return m_nickname; }
+QString NickInfo::loweredNickname() const { return m_loweredNickname; }
+QString NickInfo::getHostmask() const { return m_hostmask; }
+
+bool NickInfo::isAway() const { return m_away; }
+QString NickInfo::getAwayMessage() const { return m_awayMessage; }
+QString NickInfo::getIdentdInfo() const { return m_identdInfo; }
+QString NickInfo::getVersionInfo() const { return m_versionInfo; }
+bool NickInfo::isNotified() const { return m_notified; }
+QString NickInfo::getRealName() const { return m_realName; }
+QString NickInfo::getNetServer() const { return m_netServer; }
+QString NickInfo::getNetServerInfo() const { return m_netServerInfo; }
+QDateTime NickInfo::getOnlineSince() const { return m_onlineSince; }
+
+uint NickInfo::getNickColor()
+{
+ // do we already have a color?
+ if(!m_nickColor)
+ {
+ int nickvalue = 0;
+
+ for (uint index = 0; index < m_nickname.length(); index++)
+ {
+ nickvalue += m_nickname[index].unicode();
+ }
+
+ m_nickColor = (nickvalue % 8) + 1;
+ }
+ // return color offset -1 (since we store it +1 for 0 checking)
+ return m_nickColor-1;
+}
+
+bool NickInfo::isIdentified() const { return m_identified; }
+
+QString NickInfo::getPrettyOnlineSince() const
+{
+ QString prettyOnlineSince;
+ int daysto = m_onlineSince.date().daysTo( QDate::currentDate());
+ if(daysto == 0) prettyOnlineSince = i18n("Today");
+ else if(daysto == 1) prettyOnlineSince = i18n("Yesterday");
+ else prettyOnlineSince = m_onlineSince.toString("ddd d MMMM yyyy");
+ //TODO - we should use KLocale for this
+ prettyOnlineSince += ", " + m_onlineSince.toString("h:mm ap");
+
+ return prettyOnlineSince;
+}
+
+// Return the Server object that owns this NickInfo object.
+Server* NickInfo::getServer() const { return m_owningServer; }
+
+// Set properties of NickInfo object.
+void NickInfo::setNickname(const QString& newNickname)
+{
+ Q_ASSERT(!newNickname.isEmpty());
+ if(newNickname == m_nickname) return;
+
+ KABC::Addressee newaddressee = Konversation::Addressbook::self()->getKABCAddresseeFromNick(newNickname, m_owningServer->getServerName(), m_owningServer->getDisplayName());
+ //We now know who this person is
+ if(m_addressee.isEmpty() && !newaddressee.isEmpty())
+ {
+ //Associate the old nickname with new contact
+ Konversation::Addressbook::self()->associateNick(newaddressee,m_nickname, m_owningServer->getServerName(), m_owningServer->getDisplayName());
+ Konversation::Addressbook::self()->saveAddressee(newaddressee);
+ }
+ else if(!m_addressee.isEmpty() && newaddressee.isEmpty())
+ {
+ Konversation::Addressbook::self()->associateNick(m_addressee, newNickname, m_owningServer->getServerName(), m_owningServer->getDisplayName());
+ Konversation::Addressbook::self()->saveAddressee(newaddressee);
+ newaddressee = m_addressee;
+ }
+
+ m_addressee = newaddressee;
+ m_nickname = newNickname;
+ m_loweredNickname = newNickname.lower();
+
+ QString realname = m_addressee.realName();
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::emitNickInfoChanged()
+{
+ m_owningServer->emitNickInfoChanged(this);
+ emit nickInfoChanged();
+}
+
+void NickInfo::startNickInfoChangedTimer()
+{
+ if(!m_changedTimer->isActive())
+ m_changedTimer->start(3000, true /*single shot*/);
+}
+
+void NickInfo::setHostmask(const QString& newMask)
+{
+ if (newMask.isEmpty() || newMask == m_hostmask) return;
+ m_hostmask = newMask;
+
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setAway(bool state)
+{
+ if(state == m_away) return;
+ m_away = state;
+
+ startNickInfoChangedTimer();
+ if(!m_addressee.isEmpty())
+ Konversation::Addressbook::self()->emitContactPresenceChanged(m_addressee.uid());
+}
+
+void NickInfo::setIdentified(bool identified)
+{
+ if(identified == m_identified) return;
+ m_identified = identified;
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setAwayMessage(const QString& newMessage)
+{
+ if(m_awayMessage == newMessage) return;
+ m_awayMessage = newMessage;
+
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setIdentdInfo(const QString& newIdentdInfo)
+{
+ if(m_identdInfo == newIdentdInfo) return;
+ m_identdInfo = newIdentdInfo;
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setVersionInfo(const QString& newVersionInfo)
+{
+ if(m_versionInfo == newVersionInfo) return;
+ m_versionInfo = newVersionInfo;
+
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setNotified(bool state)
+{
+ if(state == m_notified) return;
+ m_notified = state;
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setRealName(const QString& newRealName)
+{
+ if (newRealName.isEmpty() || m_realName == newRealName) return;
+ m_realName = newRealName;
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setNetServer(const QString& newNetServer)
+{
+ if (newNetServer.isEmpty() || m_netServer == newNetServer) return;
+ m_netServer = newNetServer;
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setNetServerInfo(const QString& newNetServerInfo)
+{
+ if (newNetServerInfo.isEmpty() || newNetServerInfo == m_netServerInfo) return;
+ m_netServerInfo = newNetServerInfo;
+ startNickInfoChangedTimer();
+}
+
+void NickInfo::setOnlineSince(const QDateTime& datetime)
+{
+ if (datetime.isNull() || datetime == m_onlineSince) return;
+ m_onlineSince = datetime;
+
+ startNickInfoChangedTimer();
+}
+
+KABC::Addressee NickInfo::getAddressee() const { return m_addressee;}
+
+void NickInfo::refreshAddressee()
+{
+ //m_addressee might not have changed, but information inside it may have.
+ KABC::Addressee addressee=Konversation::Addressbook::self()->getKABCAddresseeFromNick(m_nickname, m_owningServer->getServerName(), m_owningServer->getDisplayName());
+ if(!addressee.isEmpty() && addressee.uid() != m_addressee.uid())
+ {
+ //This nick now belongs to a different addressee. We need to update the status for both the old and new addressees.
+ Konversation::Addressbook::self()->emitContactPresenceChanged(addressee.uid());
+ }
+ m_addressee = addressee;
+
+ startNickInfoChangedTimer();
+
+ if(!m_addressee.isEmpty())
+ Konversation::Addressbook::self()->emitContactPresenceChanged(m_addressee.uid());
+}
+
+QString NickInfo::tooltip() const
+{
+
+ QString strTooltip;
+ QTextStream tooltip( &strTooltip, IO_WriteOnly );
+ tooltip << "<qt>";
+
+ tooltip << "<table cellspacing=\"0\" cellpadding=\"0\">";
+ tooltipTableData(tooltip);
+ tooltip << "</table></qt>";
+ return strTooltip;
+}
+
+QString NickInfo::getBestAddresseeName()
+{
+ if(!m_addressee.formattedName().isEmpty())
+ {
+ return m_addressee.formattedName();
+ }
+ else if(!m_addressee.realName().isEmpty())
+ {
+ return m_addressee.realName();
+ }
+ else if(!getRealName().isEmpty())
+ {
+ return getRealName();
+ }
+ else
+ {
+ return getNickname();
+ }
+
+}
+
+void NickInfo::tooltipTableData(QTextStream &tooltip) const
+{
+ tooltip << "<tr><td colspan=\"2\" valign=\"top\">";
+
+ bool dirty = false;
+ KABC::Picture photo = m_addressee.photo();
+ KABC::Picture logo = m_addressee.logo();
+ bool isimage=false;
+ if(photo.isIntern())
+ {
+ QMimeSourceFactory::defaultFactory()->setImage( "photo", photo.data() );
+ tooltip << "<img src=\"photo\">";
+ dirty=true;
+ isimage=true;
+ }
+ else if(!photo.url().isEmpty())
+ {
+ //JOHNFLUX FIXME TODO:
+ //Are there security problems with this? loading from an external refrence?
+ //Assuming not.
+ tooltip << "<img src=\"" << photo.url() << "\">";
+ dirty=true;
+ isimage=true;
+ }
+ if(logo.isIntern())
+ {
+ QMimeSourceFactory::defaultFactory()->setImage( "logo", logo.data() );
+ tooltip << "<img src=\"logo\">";
+ dirty=true;
+ isimage=true;
+ }
+ else if(!logo.url().isEmpty())
+ {
+ //JOHNFLUX FIXME TODO:
+ //Are there security problems with this? loading from an external refrence?
+ //Assuming not.
+ tooltip << "<img src=\"" << logo.url() << "\">";
+ dirty=true;
+ isimage=true;
+ }
+ tooltip << "<b>" << (isimage?"":"<center>");
+ if(!m_addressee.formattedName().isEmpty())
+ {
+ tooltip << m_addressee.formattedName();
+ dirty = true;
+ }
+ else if(!m_addressee.realName().isEmpty())
+ {
+ tooltip << m_addressee.realName();
+ dirty = true;
+ }
+ else if(!getRealName().isEmpty() && getRealName().lower() != loweredNickname())
+ {
+ QString escapedRealName( getRealName() );
+ escapedRealName.replace("<","&lt;").replace(">","&gt;");
+ tooltip << escapedRealName;
+ dirty = true;
+ }
+ else
+ {
+ tooltip << getNickname();
+ //Don't set dirty if all we have is their nickname
+ }
+ if(m_identified) tooltip << i18n(" (identified)");
+ tooltip << (isimage?"":"</center>") << "</b>";
+
+ tooltip << "</td></tr>";
+ if(!m_addressee.emails().isEmpty())
+ {
+ tooltip << "<tr><td><b>" << i18n("Email") << ": </b></td><td>";
+ tooltip << m_addressee.emails().join(", ");
+ tooltip << "</td></tr>";
+ dirty=true;
+ }
+
+ if(!m_addressee.organization().isEmpty())
+ {
+ tooltip << "<tr><td><b>" << m_addressee.organizationLabel() << ": </b></td><td>" << m_addressee.organization() << "</td></tr>";
+ dirty=true;
+ }
+ if(!m_addressee.role().isEmpty())
+ {
+ tooltip << "<tr><td><b>" << m_addressee.roleLabel() << ": </b></td><td>" << m_addressee.role() << "</td></tr>";
+ dirty=true;
+ }
+ KABC::PhoneNumber::List numbers = m_addressee.phoneNumbers();
+ for( KABC::PhoneNumber::List::ConstIterator it = numbers.begin(); it != numbers.end(); ++it)
+ {
+ tooltip << "<tr><td><b>" << (*it).label() << ": </b></td><td>" << (*it).number() << "</td></tr>";
+ dirty=true;
+ }
+ if(!m_addressee.birthday().toString().isEmpty() )
+ {
+ tooltip << "<tr><td><b>" << m_addressee.birthdayLabel() << ": </b></td><td>" << m_addressee.birthday().toString("ddd d MMMM yyyy") << "</td></tr>";
+ dirty=true;
+ }
+ if(!getHostmask().isEmpty())
+ {
+ tooltip << "<tr><td><b>" << i18n("Hostmask:") << " </b></td><td>" << getHostmask() << "</td></tr>";
+ dirty=true;
+ }
+ if(isAway())
+ {
+ tooltip << "<tr><td><b>" << i18n("Away Message:") << " </b></td><td>";
+ if(!getAwayMessage().isEmpty())
+ tooltip << getAwayMessage();
+ else
+ tooltip << i18n("(unknown)");
+ tooltip << "</td></tr>";
+ dirty=true;
+ }
+ if(!getOnlineSince().toString().isEmpty())
+ {
+ tooltip << "<tr><td><b>" << i18n("Online Since:") << " </b></td><td>" << getPrettyOnlineSince() << "</td></tr>";
+ dirty=true;
+ }
+
+}
+
+void NickInfo::showLinkAddressbookUI()
+{
+ LinkAddressbookUI *linkaddressbookui = new LinkAddressbookUI(m_owningServer->getViewContainer()->getWindow(), NULL, m_nickname, m_owningServer->getServerName(), m_owningServer->getDisplayName(), m_realName);
+ linkaddressbookui->show();
+
+}
+
+bool NickInfo::editAddressee() const
+{
+ if(m_addressee.isEmpty()) return false;
+
+ Konversation::Addressbook::self()->editAddressee(m_addressee.uid());
+ return true;
+}
+
+bool NickInfo::sendEmail() const
+{
+ return Konversation::Addressbook::self()->sendEmail(m_addressee);
+}
+
+void NickInfo::setPrintedOnline(bool printed)
+{
+ m_printedOnline=printed;
+}
+
+bool NickInfo::getPrintedOnline()
+{
+ if(this)
+ return m_printedOnline;
+ else
+ return false;
+}
+
+#include "nickinfo.moc"
diff --git a/konversation/src/nickinfo.h b/konversation/src/nickinfo.h
new file mode 100644
index 0000000..f6e9a9b
--- /dev/null
+++ b/konversation/src/nickinfo.h
@@ -0,0 +1,185 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Nick Information
+ begin: Sat Jan 17 2004
+ copyright: (C) 2004 by Gary Cramblitt
+ email: garycramblitt@comcast.net
+*/
+
+#ifndef NICKINFO_H
+#define NICKINFO_H
+
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <ksharedptr.h>
+
+#include <kabc/addressbook.h>
+
+
+class Server;
+class QTimer;
+
+/**
+ The NickInfo object is a data container for information about a single nickname.
+ It is owned by the Server object and should NOT be deleted by anything other than Server.
+
+ A NickInfo is _only_ for online (or away) nicks. Not for offline nicks.
+ Offline (but watched or in addressbook) nicks are stored in the Server object.
+
+*/
+class NickInfo : public QObject, public KShared
+{
+ Q_OBJECT
+
+ public:
+ NickInfo(const QString& nick, Server* server);
+ ~NickInfo();
+
+ // Get properties of NickInfo object.
+ QString getNickname() const;
+ QString loweredNickname() const;
+ QString getHostmask() const;
+ /** Currently return whether the user has set themselves to away with /away.
+ * May be changed in the future to parse the nick string and see if it contains
+ * "|away" or "|afk" or something.
+ */
+ bool isAway() const;
+ QString getAwayMessage() const;
+ QString getIdentdInfo() const;
+ QString getVersionInfo() const;
+ bool isNotified() const;
+ QString getRealName() const;
+ QString getNetServer() const;
+ QString getNetServerInfo() const;
+ QDateTime getOnlineSince() const;
+ uint getNickColor();
+ /** Whether this user is identified with nickserv.
+ * Found only by doing /whois nick
+ */
+ bool isIdentified() const;
+ /** This returns a string of the date and time that the user has been online since.
+ * It will return null if a /whois hasn't been issued yet for this nickinfo
+ * @return a date-string in the form of "Today, 4:23pm", "Yesterday, 12:32pm" or "Mon 3 Mar 2004, 8:02am"
+ */
+ QString getPrettyOnlineSince() const;
+ /// Return the Server object that owns this NickInfo object.
+ Server* getServer() const;
+
+ /// Return the kabc (kaddressbook) contact for this nick
+ KABC::Addressee getAddressee() const;
+
+ /** Set properties of NickInfo object. */
+ void setNickname(const QString& newNickname);
+ /** Set properties of NickInfo object. Ignores the request is newmask is empty.*/
+ void setHostmask(const QString& newMask);
+ /** Set properties of NickInfo object. */
+ void setAway(bool state);
+ /** Set properties of NickInfo object. */
+ void setAwayMessage(const QString& newMessage);
+ /** Set properties of NickInfo object. */
+ void setIdentdInfo(const QString& newIdentdInfo);
+ /** Set properties of NickInfo object. */
+ void setVersionInfo(const QString& newVersionInfo);
+ /** Set properties of NickInfo object. */
+ void setNotified(bool state);
+ /** Set properties of NickInfo object. */
+ void setRealName(const QString& newRealName);
+ /** Set properties of NickInfo object. */
+ void setNetServer(const QString& newNetServer);
+ /** Set properties of NickInfo object. */
+ void setNetServerInfo(const QString& newNetServerInfo);
+ /** Whether this user is identified with nickserv.
+ * Found only by doing /whois nick
+ */
+ void setIdentified(bool identified);
+ /** Updates the time online since.
+ * This will be called from the results of a /whois
+ * This function also calculates and sets prettyOnlineSince
+ * @see getPrettyOnlineSince()
+ */
+ void setOnlineSince(const QDateTime& datetime);
+ /** Returns html describing this nickInfo - useful for tooltips when hovering over this nick.
+ */
+ QString tooltip() const;
+ /** Returns just the <tr><td>.. data for a tooltip.
+ * Used so that channelNick->tooltip() can call this, then append on its own information.
+ */
+ void tooltipTableData(QTextStream &tooltip) const;
+
+ /** Returns a full name for this contact. Tries to use the name out of addressbook.
+ * If that is empty, uses the real name from whois. If that fails, use nickname.
+ *
+ * @return A string to show the user for the name of this contact
+ */
+ QString getBestAddresseeName();
+
+ /** Open this contact up in a "edit addresee association" window
+ */
+ void showLinkAddressbookUI();
+ /** Edit the contact in kaddressbook
+ */
+ bool editAddressee() const;
+ /** Run kmail for this contact
+ */
+ bool sendEmail() const;
+
+ void setPrintedOnline(bool printed);
+ bool getPrintedOnline();
+
+ private:
+ /** After calling, emitNickInfoChanged is guaranteed to be called _within_ 1 second.
+ * Used to consolidate changed signals.
+ */
+ void startNickInfoChangedTimer();
+ QString m_nickname;
+ QString m_loweredNickname;
+ Server* m_owningServer;
+ QString m_hostmask;
+ bool m_away;
+ QString m_awayMessage;
+ QString m_identdInfo;
+ QString m_versionInfo;
+ bool m_notified;
+ QString m_realName;
+ /** The server they are connected to. */
+ QString m_netServer;
+ QString m_netServerInfo;
+ QDateTime m_onlineSince;
+ KABC::Addressee m_addressee;
+ /** Whether this user is identified with nickserv.
+ * Found only by doing /whois nick
+ */
+ bool m_identified;
+ QTimer *m_changedTimer;
+ /* True if "foo is online" message is printed */
+ bool m_printedOnline;
+ /* The color index for lookup on Preferences::NickColor(index).name()
+ Internally stored as index-1 to allow checking for 0 */
+ uint m_nickColor;
+
+ private slots:
+ void refreshAddressee();
+ /** emits NickInfoChanged for this object, and calls the server emitNickInfoChanged.
+ * Called when the m_changedTimer activates.
+ */
+ void emitNickInfoChanged();
+ signals:
+ void nickInfoChanged(void);
+};
+
+/** A NickInfoPtr is a pointer to a NickInfo object. Since it is a KSharedPtr, the NickInfo
+ * object is automatically destroyed when all references are destroyed.
+ */
+typedef KSharedPtr<NickInfo> NickInfoPtr;
+/** A NickInfoMap is a list of NickInfo objects, indexed and sorted by lowercase nickname.
+ */
+typedef QMap<QString,NickInfoPtr> NickInfoMap;
+
+typedef QValueList<NickInfoPtr> NickInfoList;
+#endif
diff --git a/konversation/src/nicklistbehavior_preferences.cpp b/konversation/src/nicklistbehavior_preferences.cpp
new file mode 100644
index 0000000..8bb3bde
--- /dev/null
+++ b/konversation/src/nicklistbehavior_preferences.cpp
@@ -0,0 +1,108 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "nicklistbehavior_preferences.h"
+#include "valuelistviewitem.h"
+#include "config/preferences.h"
+
+#include <qheader.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+
+
+NicklistBehavior_Config::NicklistBehavior_Config(QWidget *parent, const char *name)
+ : NicklistBehavior_ConfigUI(parent, name)
+{
+ // get page widget and populate listview
+ loadSettings();
+
+ // make items react to drag & drop
+ sortOrder->setSorting(-1,false);
+ sortOrder->header()->setMovingEnabled(false);
+
+ connect(sortOrder,SIGNAL (moved()),this,SIGNAL (modified()) );
+}
+
+NicklistBehavior_Config::~NicklistBehavior_Config()
+{
+}
+
+void NicklistBehavior_Config::restorePageToDefaults()
+{
+ setNickList(Preferences::defaultNicknameSortingOrder());
+}
+
+void NicklistBehavior_Config::loadSettings()
+{
+ // get sorting order string from preferences
+ setNickList(Preferences::sortOrder());
+ m_oldSortingOrder=currentSortingOrder();
+}
+
+void NicklistBehavior_Config::setNickList(const QString &sortingOrder)
+{
+ sortOrder->clear();
+ // loop through the sorting order string, insert the matching descriptions in reverse order
+ // to keep the correct sorting
+ for(unsigned int index=sortingOrder.length();index!=0;index--)
+ {
+ // get next mode char
+ QChar mode=sortingOrder[index-1];
+ // find appropriate description
+ if(mode=='-') new KListViewItem(sortOrder,mode,i18n("Normal Users"));
+ if(mode=='v') new KListViewItem(sortOrder,mode,i18n("Voice (+v)"));
+ if(mode=='h') new KListViewItem(sortOrder,mode,i18n("Halfops (+h)"));
+ if(mode=='o') new KListViewItem(sortOrder,mode,i18n("Operators (+o)"));
+ if(mode=='p') new KListViewItem(sortOrder,mode,i18n("Channel Admins (+p)"));
+ if(mode=='q') new KListViewItem(sortOrder,mode,i18n("Channel Owners (+q)"));
+ }
+}
+
+QString NicklistBehavior_Config::currentSortingOrder()
+{
+ // get the uppermost entry of the sorting list
+ QListViewItem* item=sortOrder->firstChild();
+ // prepare the new sorting order string
+ QString currentSortingOrder;
+ // iterate through all items of the listview
+ while(item)
+ {
+ // add mode char to the sorting order string
+ currentSortingOrder+=item->text(0);
+ // go to next item in the listview
+ item=item->itemBelow();
+ } // while
+
+ return currentSortingOrder;
+}
+
+// save settings permanently
+void NicklistBehavior_Config::saveSettings()
+{
+ // get the current sorting order
+ QString newSortingOrder=currentSortingOrder();
+
+ // update sorting order on in-memory preferences
+ Preferences::setSortOrder(newSortingOrder);
+
+ // save current sorting order as a reference to hasChanged()
+ m_oldSortingOrder=currentSortingOrder();
+}
+
+bool NicklistBehavior_Config::hasChanged()
+{
+ return(m_oldSortingOrder!=currentSortingOrder());
+}
+
+#include "nicklistbehavior_preferences.moc"
diff --git a/konversation/src/nicklistbehavior_preferences.h b/konversation/src/nicklistbehavior_preferences.h
new file mode 100644
index 0000000..5eda68d
--- /dev/null
+++ b/konversation/src/nicklistbehavior_preferences.h
@@ -0,0 +1,46 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef NICKLISTBEHAVIOR_Config_H
+#define NICKLISTBEHAVIOR_Config_H
+
+#include "konvisettingspage.h"
+#include "nicklistbehavior_preferencesui.h"
+
+#include <qobject.h>
+
+
+class NicklistBehavior_Config : public NicklistBehavior_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit NicklistBehavior_Config(QWidget *parent = 0, const char *name = 0);
+ ~NicklistBehavior_Config();
+
+ virtual void saveSettings();
+ virtual void loadSettings();
+ virtual void restorePageToDefaults();
+
+ virtual bool hasChanged();
+
+ private:
+ void setNickList(const QString &sortingOrder);
+ QString currentSortingOrder();
+
+ QString m_oldSortingOrder;
+
+ signals:
+ void modified();
+};
+
+#endif
diff --git a/konversation/src/nicklistbehavior_preferencesui.ui b/konversation/src/nicklistbehavior_preferencesui.ui
new file mode 100644
index 0000000..2e84d89
--- /dev/null
+++ b/konversation/src/nicklistbehavior_preferencesui.ui
@@ -0,0 +1,160 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>NicklistBehavior_ConfigUI</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</comment>
+<author>Copyright (C) 2005 Peter Simonsson</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>NicklistBehavior_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>692</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Command to be executed on double click:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ChannelDoubleClickAction</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_SortCaseInsensitive</cstring>
+ </property>
+ <property name="text">
+ <string>Sort case &amp;insensitive</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_SortByActivity</cstring>
+ </property>
+ <property name="text">
+ <string>Sort by &amp;activity</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_SortByStatus</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Sort b&amp;y user status</string>
+ </property>
+ </widget>
+ <widget class="KListView" row="4" column="0" rowspan="1" colspan="3">
+ <column>
+ <property name="text">
+ <string>Mode</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Explanation</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>sortOrder</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="shadeSortColumn">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="2">
+ <property name="name">
+ <cstring>kcfg_ChannelDoubleClickAction</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>orderHintLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>(Reorder nick sorting order by drag and drop)</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_SortByStatus</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>sortOrder</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_SortByStatus</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>orderHintLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_ChannelDoubleClickAction</tabstop>
+ <tabstop>kcfg_SortCaseInsensitive</tabstop>
+ <tabstop>kcfg_SortByStatus</tabstop>
+ <tabstop>sortOrder</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/nicklistview.cpp b/konversation/src/nicklistview.cpp
new file mode 100644
index 0000000..e6e2bf2
--- /dev/null
+++ b/konversation/src/nicklistview.cpp
@@ -0,0 +1,336 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ This is the class that shows the channel nick list
+ begin: Fre Jun 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "nicklistview.h"
+#include "konversationapplication.h"
+#include "images.h"
+#include "linkaddressbook/addressbook.h"
+
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <qtooltip.h>
+#include <qtimer.h>
+#include <kiconloader.h>
+#include <qwhatsthis.h>
+#include <qdragobject.h>
+
+
+NickListView::NickListView(QWidget* parent, Channel *chan) :
+KListView(parent)
+{
+ KListView::setSorting(-1);
+ setWhatsThis();
+ channel=chan;
+ popup=new KPopupMenu(this,"nicklist_context_menu");
+ modes=new KPopupMenu(this,"nicklist_modes_context_submenu");
+ kickban=new KPopupMenu(this,"nicklist_kick_ban_context_submenu");
+ addressbook= new KPopupMenu(this,"nicklist_addressbook_context_submenu");
+ setAcceptDrops(true);
+ setDropHighlighter(true);
+ setDropVisualizer(false);
+
+ if (popup)
+ {
+ popup->insertItem(i18n("&Whois"),Konversation::Whois);
+ popup->insertItem(i18n("&Version"),Konversation::Version);
+ popup->insertItem(i18n("&Ping"),Konversation::Ping);
+
+ popup->insertSeparator();
+
+ if (modes)
+ {
+ modes->insertItem(i18n("Give Op"),Konversation::GiveOp);
+ modes->insertItem(i18n("Take Op"),Konversation::TakeOp);
+ modes->insertItem(i18n("Give HalfOp"),Konversation::GiveHalfOp);
+ modes->insertItem(i18n("Take HalfOp"),Konversation::TakeHalfOp);
+ modes->insertItem(i18n("Give Voice"),Konversation::GiveVoice);
+ modes->insertItem(i18n("Take Voice"),Konversation::TakeVoice);
+ popup->insertItem(i18n("Modes"),modes,Konversation::ModesSub);
+ }
+
+ if (kickban)
+ {
+
+ kickban->insertItem(i18n("Kick"),Konversation::Kick);
+ kickban->insertItem(i18n("Kickban"),Konversation::KickBan);
+ kickban->insertItem(i18n("Ban Nickname"),Konversation::BanNick);
+ kickban->insertSeparator();
+ kickban->insertItem(i18n("Ban *!*@*.host"),Konversation::BanHost);
+ kickban->insertItem(i18n("Ban *!*@domain"),Konversation::BanDomain);
+ kickban->insertItem(i18n("Ban *!user@*.host"),Konversation::BanUserHost);
+ kickban->insertItem(i18n("Ban *!user@domain"),Konversation::BanUserDomain);
+ kickban->insertSeparator();
+ kickban->insertItem(i18n("Kickban *!*@*.host"),Konversation::KickBanHost);
+ kickban->insertItem(i18n("Kickban *!*@domain"),Konversation::KickBanDomain);
+ kickban->insertItem(i18n("Kickban *!user@*.host"),Konversation::KickBanUserHost);
+ kickban->insertItem(i18n("Kickban *!user@domain"),Konversation::KickBanUserDomain);
+ popup->insertItem(i18n("Kick / Ban"),kickban,Konversation::KickBanSub);
+ }
+
+ popup->insertItem(i18n("Ignore"), Konversation::IgnoreNick);
+ popup->insertItem(i18n("Unignore"), Konversation::UnignoreNick);
+
+ popup->insertSeparator();
+
+ int newitem;
+ newitem = popup->insertItem(i18n("Open &Query"),Konversation::OpenQuery);
+ popup->setWhatsThis(newitem, "<qt>Start a private chat between you and this person.<p/><em>Technical note:</em><br>The conversation between you and this person will be sent via the server. This means that the conversation will be affected by server lag, server stability, and will be terminated when you disconnect from the server.</qt>");
+ newitem = popup->insertItem(i18n("Open DCC &Chat"),Konversation::StartDccChat);
+ popup->setWhatsThis(newitem, "<qt>Start a private <em>D</em>irect <em>C</em>lient <em>C</em>onnection chat between you and this person.<p/><em>Technical note:</em><br />The conversation between you and this person will be sent directly. This means it is independent from the server - so if the server connection fails, or use disconnect, your DCC Chat will be unaffected. It also means that no irc server admin can view or spy on this chat.</qt>");
+
+ if (kapp->authorize("allow_downloading"))
+ {
+ newitem = popup->insertItem(SmallIcon("2rightarrow"),i18n("Send &File..."),Konversation::DccSend);
+ popup->setWhatsThis(newitem, "<qt>Send a file to this person. If you are having problem sending files, or they are sending slowly, see the Konversation Handbook and DCC preferences page.</qt>");
+ }
+ popup->insertItem(SmallIconSet("mail_generic"),i18n("&Send Email..."), Konversation::SendEmail);
+
+ popup->insertSeparator();
+
+ if (addressbook)
+ popup->insertItem(i18n("Addressbook Associations"), addressbook, Konversation::AddressbookSub);
+
+ popup->insertItem(i18n("Add to Watched Nicks"), Konversation::AddNotify);
+
+ connect (popup, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+ connect (modes, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+ connect (kickban, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+ connect (addressbook, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+
+ }
+ else
+ {
+ kdWarning() << "NickListView::NickListView(): Could not create popup!" << endl;
+ }
+
+ #if KDE_IS_VERSION(3,3,90)
+ setShadeSortColumn(false);
+ #endif
+
+ // We have our own tooltips, don't use the default QListView ones
+ setShowToolTips(false);
+ m_tooltip = new Konversation::KonversationNickListViewToolTip(viewport(), this);
+
+ m_resortTimer = new QTimer(this);
+ connect(m_resortTimer, SIGNAL(timeout()), SLOT(resort()));
+}
+
+NickListView::~NickListView()
+{
+ delete m_tooltip;
+ m_tooltip = 0;
+}
+
+void NickListView::setWhatsThis()
+{
+ Images* images = KonversationApplication::instance()->images();
+
+ if(images->getNickIcon( Images::Normal, false).isNull())
+ {
+ QWhatsThis::add(this, i18n("<qt>This shows all the people in the channel. The nick for each person is shown.<br>Usually an icon is shown showing the status of each person, but you do not seem to have any icon theme installed. See the Konversation settings - <i>Configure Konversation</i> under the <i>Settings</i> menu. Then view the page for <i>Themes</i> under <i>Appearence</i>.</qt>"));
+ }
+ else
+ {
+ QMimeSourceFactory::defaultFactory()->setImage( "admin", images->getNickIcon( Images::Admin, false ).convertToImage() );
+ QMimeSourceFactory::defaultFactory()->setImage( "owner", images->getNickIcon( Images::Owner, false ).convertToImage());
+ QMimeSourceFactory::defaultFactory()->setImage( "op", images->getNickIcon( Images::Op, false ).convertToImage() );
+ QMimeSourceFactory::defaultFactory()->setImage( "halfop", images->getNickIcon( Images::HalfOp, false ).convertToImage() );
+ QMimeSourceFactory::defaultFactory()->setImage( "voice", images->getNickIcon( Images::Voice, false ).convertToImage() );
+ QMimeSourceFactory::defaultFactory()->setImage( "normal", images->getNickIcon( Images::Normal, false ).convertToImage() );
+ QMimeSourceFactory::defaultFactory()->setImage( "normalaway", images->getNickIcon( Images::Normal, true).convertToImage() );
+
+ QWhatsThis::add(this, i18n("<qt>This shows all the people in the channel. The nick for each person is shown, with a picture showing their status.<p>"
+ "<table>"
+
+ "<tr><th><img src=\"admin\"></th><td>This person has administrator privileges.</td></tr>"
+ "<tr><th><img src=\"owner\"></th><td>This person is a channel owner.</td></tr>"
+ "<tr><th><img src=\"op\"></th><td>This person is a channel operator.</td></tr>"
+ "<tr><th><img src=\"halfop\"></th><td>This person is a channel half-operator.</td></tr>"
+ "<tr><th><img src=\"voice\"></th><td>This person has voice, and can therefore talk in a moderated channel.</td></tr>"
+ "<tr><th><img src=\"normal\"></th><td>This person does not have any special privileges.</td></tr>"
+ "<tr><th><img src=\"normalaway\"></th><td>This indicates that this person is currently away.</td></tr>"
+ "</table><p>"
+ "The meaning of admin, owner and halfop varies between different IRC servers.<p>"
+ "Hovering over any nick shows their current status, as well as any information in the addressbook for this person. See the Konversation Handbook for more information."
+ "</qt>"
+ ));
+ }
+
+}
+
+void NickListView::refresh()
+{
+ QListViewItemIterator it(this);
+
+ while (it.current())
+ {
+ static_cast<Nick*>(it.current())->refresh();
+ ++it;
+ }
+
+ setWhatsThis();
+}
+
+void NickListView::startResortTimer()
+{
+ if(!m_resortTimer->isActive())
+ m_resortTimer->start(3000, true /*single shot*/);
+}
+
+void NickListView::resort()
+{
+ KListView::setSorting(m_column, m_ascending);
+ sort();
+ KListView::setSorting(-1);
+}
+
+void NickListView::contextMenuEvent(QContextMenuEvent* ce)
+{
+ ce->accept();
+
+ if (selectedItems().count())
+ {
+ insertAssociationSubMenu();
+ updateActions();
+ popup->popup(ce->globalPos());
+ }
+}
+
+void NickListView::updateActions()
+{
+ int ignoreCounter = 0;
+ int unignoreCounter = 0;
+ int notifyCounter = 0;
+
+ int serverGroupId = -1;
+
+ if (channel->getServer()->getServerGroup())
+ serverGroupId = channel->getServer()->getServerGroup()->id();
+
+ ChannelNickList nickList=channel->getSelectedChannelNicks();
+ ChannelNickList::ConstIterator it;
+
+ for (it = nickList.begin(); it != nickList.end(); ++it)
+ {
+ if (Preferences::isIgnored((*it)->getNickname()))
+ ++unignoreCounter;
+ else
+ ++ignoreCounter;
+
+ if (serverGroupId != -1 && Preferences::isNotify(serverGroupId, (*it)->getNickname()))
+ ++notifyCounter;
+ }
+
+ if (ignoreCounter)
+ popup->setItemVisible(Konversation::IgnoreNick, true);
+ else
+ popup->setItemVisible(Konversation::IgnoreNick, false);
+
+ if (unignoreCounter)
+ popup->setItemVisible(Konversation::UnignoreNick, true);
+ else
+ popup->setItemVisible(Konversation::UnignoreNick, false);
+
+ if (notifyCounter || !Preferences::hasNotifyList(serverGroupId))
+ popup->setItemEnabled(Konversation::AddNotify, false);
+ else
+ popup->setItemEnabled(Konversation::AddNotify, true);
+}
+
+void NickListView::insertAssociationSubMenu()
+{
+
+ bool existingAssociation = false;
+ bool noAssociation = false;
+ bool emailAddress = false;
+
+ addressbook->clear();
+
+ ChannelNickList nickList=channel->getSelectedChannelNicks();
+ for(ChannelNickList::ConstIterator it=nickList.begin();it!=nickList.end();++it)
+ {
+ KABC::Addressee addr = (*it)->getNickInfo()->getAddressee();
+ if(addr.isEmpty())
+ {
+ noAssociation=true;
+ if(existingAssociation && emailAddress) break;
+ }
+ else
+ {
+ if(!emailAddress && !addr.preferredEmail().isEmpty())
+ emailAddress = true;
+ existingAssociation=true;
+ if(noAssociation && emailAddress) break;
+ }
+ }
+
+ if(!noAssociation && existingAssociation)
+ {
+ addressbook->insertItem(SmallIcon("contents"), i18n("Edit Contact..."), Konversation::AddressbookEdit);
+ addressbook->insertSeparator();
+ }
+
+ if(noAssociation && existingAssociation)
+ addressbook->insertItem(i18n("Choose/Change Associations..."), Konversation::AddressbookChange);
+ else if(noAssociation)
+ addressbook->insertItem(i18n("Choose Contact..."), Konversation::AddressbookChange);
+ else
+ addressbook->insertItem(i18n("Change Association..."), Konversation::AddressbookChange);
+
+ if(noAssociation && !existingAssociation)
+ addressbook->insertItem(i18n("Create New Contact..."), Konversation::AddressbookNew);
+
+ if(existingAssociation)
+ addressbook->insertItem(SmallIcon("editdelete"), i18n("Delete Association"), Konversation::AddressbookDelete);
+
+ if(!emailAddress)
+ popup->setItemEnabled(Konversation::SendEmail, false);
+ else
+ popup->setItemEnabled(Konversation::SendEmail, true);
+
+}
+
+void NickListView::setSorting(int column, bool ascending)
+{
+ m_column = column;
+ m_ascending = ascending;
+}
+
+bool NickListView::acceptDrag (QDropEvent* event) const
+{
+ if (event->provides("text/uri-list"))
+ {
+ if (event->source())
+ {
+ QStrList uris;
+
+ if (QUriDrag::decode(event,uris))
+ {
+ QString first = uris.first();
+
+ if (first.startsWith("irc://") || channel->getNickList().containsNick(first))
+ return false;
+ }
+ else
+ return false;
+ }
+
+ return true;
+ }
+ else
+ return false;
+}
+
+#include "nicklistview.moc"
diff --git a/konversation/src/nicklistview.h b/konversation/src/nicklistview.h
new file mode 100644
index 0000000..ac6ac80
--- /dev/null
+++ b/konversation/src/nicklistview.h
@@ -0,0 +1,77 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Channel Nick List, including context menu
+ begin: Fre Jun 7 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef NICKLISTVIEW_H
+#define NICKLISTVIEW_H
+
+#include "channel.h"
+#include "nicklisttooltip.h"
+#include "images.h"
+#include "common.h"
+
+#include <klistview.h>
+
+
+class QPopupMenu;
+class QContextMenuEvent;
+class QTimer;
+
+class NickListView : public KListView
+{
+ Q_OBJECT
+
+ public:
+ NickListView(QWidget* parent, Channel *chan);
+ ~NickListView();
+
+ /** Call when the icons have been changed.
+ */
+ void refresh();
+ void setWhatsThis();
+
+ virtual void setSorting(int column, bool ascending);
+
+ public slots:
+ /** When this is called, resort is guaranteed to be called within a hard-coded time (a few seconds).
+ * This prevents lots of calls to resort.
+ */
+ void startResortTimer();
+
+ /** Resort the listview.
+ * It is better to call startResortTimer() which will resort with a minimum of a
+ * 1 second delay.
+ */
+ void resort();
+
+ signals:
+ /* Will be connected to Channel::popupCommand(int) */
+ void popupCommand(int id);
+
+ protected:
+ void contextMenuEvent(QContextMenuEvent* ce);
+ virtual bool acceptDrag (QDropEvent* event) const;
+ void insertAssociationSubMenu();
+ void updateActions();
+ Konversation::KonversationNickListViewToolTip *m_tooltip;
+ QPopupMenu* popup;
+ QPopupMenu* modes;
+ QPopupMenu* kickban;
+ QPopupMenu* addressbook;
+ Channel *channel;
+ QTimer *m_resortTimer;
+
+ int m_column;
+ bool m_ascending;
+};
+#endif
diff --git a/konversation/src/nicksonline.cpp b/konversation/src/nicksonline.cpp
new file mode 100644
index 0000000..85361ff
--- /dev/null
+++ b/konversation/src/nicksonline.cpp
@@ -0,0 +1,975 @@
+// -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*-
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ shows a user tree of friends per server
+ begin: Sam Aug 31 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "nicksonline.h"
+#include "channel.h"
+#include "server.h"
+#include "konversationapplication.h"
+#include "connectionmanager.h"
+#include "images.h"
+#include "query.h"
+#include "linkaddressbook/linkaddressbookui.h"
+#include "linkaddressbook/addressbook.h"
+#include "linkaddressbook/nicksonlinetooltip.h"
+#include "konversationmainwindow.h"
+#include "viewcontainer.h"
+#include "nicksonlineitem.h"
+
+#include <qlayout.h>
+#include <qstringlist.h>
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qpopupmenu.h>
+#include <qptrlist.h>
+#include <qwhatsthis.h>
+
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kdialog.h>
+#include <klistview.h>
+#include <kiconloader.h>
+#include <kprocess.h>
+#include <kmessagebox.h>
+
+
+NicksOnline::NicksOnline(QWidget* parent): ChatWindow(parent)
+{
+ setName(i18n("Watched Nicks Online"));
+ setType(ChatWindow::NicksOnline);
+
+ m_nickListView=new KListView(this);
+
+ // Set to false every 8 seconds to permit a whois on watched nicks lacking information.
+ // Remove when server or addressbook does this automatically.
+ m_whoisRequested = true;
+
+ m_nickListView->addColumn(i18n("Network/Nickname/Channel"));
+ m_kabcIconSet = KGlobal::iconLoader()->loadIconSet("kaddressbook",KIcon::Small);
+ m_nickListView->addColumn(i18n("Additional Information"));
+ m_nickListView->setFullWidth(true);
+ m_nickListView->setRootIsDecorated(true);
+ m_nickListView->setShowToolTips(false);
+ m_nickListView->setShadeSortColumn(true);
+ m_nickListView->setShowSortIndicator(true);
+
+ QString nickListViewWT = i18n(
+ "<p>These are all the nicknames on your Nickname Watch list, listed under the "
+ "server network they are connected to. The list also includes the nicknames "
+ "in KAddressBook associated with the server network.</p>"
+ "<p>The <b>Additional Information</b> column shows the information known "
+ "for each nickname.</p>"
+ "<p>The channels the nickname has joined are listed underneath each nickname.</p>"
+ "<p>Nicknames appearing under <b>Offline</b> are not connected to any of the "
+ "servers in the network.</p>"
+ "<p>Right-click with the mouse on a nickname to perform additional functions.</p>");
+ QWhatsThis::add(m_nickListView, nickListViewWT);
+
+ m_tooltip = new Konversation::KonversationNicksOnlineToolTip(m_nickListView->viewport(), this);
+
+ setMargin(margin());
+ setSpacing(spacing());
+
+ QHBox* buttonBox=new QHBox(this);
+ buttonBox->setSpacing(spacing());
+ QPushButton* editButton=new QPushButton(i18n("&Edit Watch List..."),
+ buttonBox,"edit_notify_button");
+ QString editButtonWT = i18n(
+ "Click to edit the list of nicknames that appear on this screen.");
+ QWhatsThis::add(editButton, editButtonWT);
+
+ connect(editButton, SIGNAL(clicked()), SIGNAL(editClicked()) );
+ connect(m_nickListView, SIGNAL(doubleClicked(QListViewItem*)),
+ this,SLOT(processDoubleClick(QListViewItem*)));
+
+ QLabel* addressbookLabel = new QLabel(i18n("Address book:"),
+ buttonBox, "nicksonline_addressbook_label");
+ QString addressbookLabelWT = i18n(
+ "When you select a nickname in the list above, the buttons here are used "
+ "to associate the nickname with an entry in KAddressBook.");
+ QWhatsThis::add(addressbookLabel, addressbookLabelWT);
+ addressbookLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_editContactButton = new QPushButton(i18n("Edit C&ontact..."),
+ buttonBox, "nicksonline_editcontact_button");
+ QString editContactButtonWT = i18n(
+ "Click to create, view, or edit the KAddressBook entry associated with the nickname "
+ "selected above.");
+ QWhatsThis::add(m_editContactButton, editContactButtonWT);
+ m_editContactButton->setIconSet(m_kabcIconSet);
+ m_changeAssociationButton = new QPushButton(i18n("&Change Association..."),
+ buttonBox, "nicksonline_changeassociation_button");
+ QString changeAssociationButtonWT = i18n(
+ "Click to associate the nickname selected above with an entry in KAddressBook.");
+ QWhatsThis::add(m_changeAssociationButton, changeAssociationButtonWT);
+ m_changeAssociationButton->setIconSet(m_kabcIconSet);
+ m_deleteAssociationButton = new QPushButton(i18n("&Delete Association"),
+ buttonBox, "nicksonline_deleteassociation_button");
+ QString deleteAssociationButtonWT = i18n(
+ "Click to remove the association between the nickname selected above and a "
+ "KAddressBook entry.");
+ QWhatsThis::add(m_deleteAssociationButton, deleteAssociationButtonWT);
+ m_deleteAssociationButton->setIconSet(m_kabcIconSet);
+
+ connect(m_editContactButton, SIGNAL(clicked()),
+ this, SLOT(slotEditContactButton_Clicked()));
+ connect(m_changeAssociationButton, SIGNAL(clicked()),
+ this, SLOT(slotChangeAssociationButton_Clicked()));
+ connect(m_deleteAssociationButton, SIGNAL(clicked()),
+ this, SLOT(slotDeleteAssociationButton_Clicked()));
+ connect(m_nickListView, SIGNAL(selectionChanged()),
+ this, SLOT(slotNickListView_SelectionChanged()));
+
+ setupAddressbookButtons(nsNotANick);
+
+ // Create context menu. Individual menu entries are created in rightButtonClicked slot.
+ m_popupMenu = new QPopupMenu(this,"nicksonline_context_menu");
+ connect(m_nickListView, SIGNAL(rightButtonClicked(QListViewItem *, const QPoint &, int )),
+ this, SLOT(slotNickListView_RightButtonClicked(QListViewItem*, const QPoint &)));
+ connect(m_popupMenu, SIGNAL(activated(int)),
+ this, SLOT(slotPopupMenu_Activated(int)));
+
+ // Display info for all currently-connected servers.
+ refreshAllServerOnlineLists();
+
+ // Connect and start refresh timer.
+ m_timer = new QTimer(this, "nicksOnlineTimer");
+ connect(m_timer, SIGNAL (timeout()), this, SLOT(timerFired()));
+ // TODO: User preference for refresh interval.
+ m_timer->start(8000);
+}
+
+NicksOnline::~NicksOnline()
+{
+ m_timer->stop();
+ delete m_timer;
+ delete m_nickListView;
+}
+
+KListView* NicksOnline::getNickListView()
+{
+ return m_nickListView;
+}
+
+/**
+ * Returns the named child of parent item in a NicksOnlineItem
+ * @param parent Pointer to a NicksOnlineItem.
+ * @param name The name in the desired child QListViewItem, must be in column 0.
+ * @param type The type of entry to be found
+ * @return Pointer to the child QListViewItem or 0 if not found.
+ */
+QListViewItem* NicksOnline::findItemChild(const QListViewItem* parent, const QString& name, NicksOnlineItem::NickListViewColumn type)
+{
+ if (!parent) return 0;
+ QListViewItem* child;
+ for (child = parent->firstChild(); (child) ; child = child->nextSibling())
+ {
+ if(static_cast<NicksOnlineItem*>(child)->type() == type && child->text(0) == name) return child;
+ }
+ return 0;
+}
+
+/**
+ * Returns the first occurrence of a child item of a given type in a NicksOnlineItem
+ * @param parent Pointer to a NicksOnlineItem.
+ * @param type The type of entry to be found
+ * @return Pointer to the child QListViewItem or 0 if not found.
+ */
+QListViewItem* NicksOnline::findItemType(const QListViewItem* parent, NicksOnlineItem::NickListViewColumn type)
+{
+ if (!parent) return 0;
+ QListViewItem* child;
+ for (child = parent->firstChild(); (child) ; child = child->nextSibling())
+ {
+ if(static_cast<NicksOnlineItem*>(child)->type() == type) return child;
+ }
+ return 0;
+}
+
+/**
+ * Returns a pointer to the network QListViewItem with the given name.
+ * @param name The name of the network, assumed to be in column 0 of the item.
+ * @return Pointer to the QListViewItem or 0 if not found.
+ */
+QListViewItem* NicksOnline::findNetworkRoot(const QString& name)
+{
+ QListViewItem* child;
+ for (child = getNickListView()->firstChild(); (child) ; child = child->nextSibling())
+ {
+ if (child->text(0) == name) return child;
+ }
+ return 0;
+}
+
+/**
+ * Return a string containing formatted additional information about a nick.
+ * @param nickInfo A pointer to NickInfo structure for the nick. May be Null.
+ * @param addressee Addressbook entry for the nick. May be empty.
+ * @return A string formatted for display containing the information
+ * about the nick.
+ * @return needWhois True if a WHOIS needs to be performed on the nick
+ * to get additional information.
+ */
+QString NicksOnline::getNickAdditionalInfo(NickInfoPtr nickInfo, KABC::Addressee addressee,
+bool& needWhois)
+{
+ QString info;
+ if (!addressee.isEmpty())
+ {
+ if (addressee.fullEmail().isEmpty())
+ info += addressee.realName();
+ else
+ info += addressee.fullEmail();
+ }
+ QString niInfo;
+ if (nickInfo)
+ {
+ if (nickInfo->isAway())
+ {
+ niInfo += i18n("Away");
+ if (!nickInfo->getAwayMessage().isEmpty())
+ niInfo += '(' + nickInfo->getAwayMessage() + ')';
+ }
+ if (!nickInfo->getHostmask().isEmpty())
+ niInfo += ' ' + nickInfo->getHostmask();
+ if (!nickInfo->getRealName().isEmpty())
+ niInfo += " (" + nickInfo->getRealName() + ')';
+ if (!nickInfo->getNetServer().isEmpty())
+ {
+ niInfo += i18n( " online via %1" ).arg( nickInfo->getNetServer() );
+ if (!nickInfo->getNetServerInfo().isEmpty())
+ niInfo += " (" + nickInfo->getNetServerInfo() + ')';
+ }
+ if (!nickInfo->getOnlineSince().isNull())
+ niInfo += i18n( " since %1" ).arg( nickInfo->getPrettyOnlineSince() );
+ }
+ needWhois = niInfo.isEmpty();
+ if (!info.isEmpty() && !needWhois) info += ' ';
+ return info + niInfo;
+}
+
+/**
+ * Refresh the nicklistview for a single server.
+ * @param server The server to be refreshed.
+ */
+void NicksOnline::updateServerOnlineList(Server* servr)
+{
+ bool newNetworkRoot = false;
+ QString serverName = servr->getServerName();
+ QString networkName = servr->getDisplayName();
+ QListViewItem* networkRoot = findNetworkRoot(networkName);
+ // If network is not in our list, add it.
+ if (!networkRoot)
+ {
+ networkRoot = new NicksOnlineItem(NicksOnlineItem::NetworkRootItem,m_nickListView,networkName);
+ newNetworkRoot = true;
+ }
+ // Store server name in hidden column.
+ // Note that there could be more than one server in the network connected,
+ // but it doesn't matter because all the servers in a network have the same
+ // watch list.
+ networkRoot->setText(nlvcServerName, serverName);
+ // Update list of servers in the network that are connected.
+ QStringList serverList = QStringList::split(",", networkRoot->text(nlvcAdditionalInfo));
+ if (!serverList.contains(serverName)) serverList.append(serverName);
+ networkRoot->setText(nlvcAdditionalInfo, serverList.join(","));
+ // Get item in nicklistview for the Offline branch.
+ QListViewItem* offlineRoot = findItemType(networkRoot, NicksOnlineItem::OfflineItem);
+ if (!offlineRoot)
+ {
+ offlineRoot = new NicksOnlineItem(NicksOnlineItem::OfflineItem,networkRoot,i18n("Offline"));
+ offlineRoot->setText(nlvcServerName, serverName);
+ }
+
+ // Get watch list.
+ QStringList watchList = servr->getWatchList();
+ QStringList::iterator itEnd = watchList.end();
+ QString nickname;
+
+ for (QStringList::iterator it = watchList.begin(); it != itEnd; ++it)
+ {
+ nickname = (*it);
+ NickInfoPtr nickInfo = getOnlineNickInfo(networkName, nickname);
+
+ if (nickInfo && nickInfo->getPrintedOnline())
+ {
+ // Nick is online.
+ // Which server did NickInfo come from?
+ Server* server=nickInfo->getServer();
+ // Get addressbook entry (if any) for the nick.
+ KABC::Addressee addressee = nickInfo->getAddressee();
+ // Construct additional information string for nick.
+ bool needWhois = false;
+ QString nickAdditionalInfo = getNickAdditionalInfo(nickInfo, addressee, needWhois);
+ // Remove from offline branch if present.
+ QListViewItem* item = findItemChild(offlineRoot, nickname, NicksOnlineItem::NicknameItem);
+ if (item) delete item;
+ // Add to network if not already added.
+ QListViewItem* nickRoot = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem);
+ if (!nickRoot) nickRoot = new NicksOnlineItem(NicksOnlineItem::NicknameItem,networkRoot, nickname, nickAdditionalInfo);
+ nickRoot->setText(nlvcAdditionalInfo, nickAdditionalInfo);
+ nickRoot->setText(nlvcServerName, serverName);
+ // If no additional info available, request a WHOIS on the nick.
+ if (!m_whoisRequested)
+ {
+ if (needWhois)
+ {
+ requestWhois(networkName, nickname);
+ m_whoisRequested = true;
+ }
+ }
+ // Set Kabc icon if the nick is associated with an addressbook entry.
+ if (!addressee.isEmpty())
+ nickRoot->setPixmap(nlvcKabc, m_kabcIconSet.pixmap(
+ QIconSet::Small, QIconSet::Normal, QIconSet::On));
+ else
+ nickRoot->setPixmap(nlvcKabc, m_kabcIconSet.pixmap(
+ QIconSet::Small, QIconSet::Disabled, QIconSet::Off));
+
+ QStringList channelList = server->getNickChannels(nickname);
+ QStringList::iterator itEnd2 = channelList.end();
+
+ for (QStringList::iterator it2 = channelList.begin(); it2 != itEnd2; ++it2)
+ {
+ // Known channels where nickname is online and mode in each channel.
+ // FIXME: If user connects to multiple servers in same network, the
+ // channel info will differ between the servers, resulting in inaccurate
+ // mode and led info displayed.
+
+ QString channelName = (*it2);
+
+ ChannelNickPtr channelNick = server->getChannelNick(channelName, nickname);
+ QString nickMode;
+ if (channelNick->hasVoice()) nickMode = nickMode + i18n(" Voice");
+ if (channelNick->isHalfOp()) nickMode = nickMode + i18n(" HalfOp");
+ if (channelNick->isOp()) nickMode = nickMode + i18n(" Operator");
+ if (channelNick->isOwner()) nickMode = nickMode + i18n(" Owner");
+ if (channelNick->isAdmin()) nickMode = nickMode + i18n(" Admin");
+ QListViewItem* channelItem = findItemChild(nickRoot, channelName, NicksOnlineItem::ChannelItem);
+ if (!channelItem) channelItem = new NicksOnlineItem(NicksOnlineItem::ChannelItem,nickRoot,
+ channelName, nickMode);
+ channelItem->setText(nlvcAdditionalInfo, nickMode);
+
+ // Icon for mode of nick in each channel.
+ Images::NickPrivilege nickPrivilege = Images::Normal;
+ if (channelNick->hasVoice()) nickPrivilege = Images::Voice;
+ if (channelNick->isHalfOp()) nickPrivilege = Images::HalfOp;
+ if (channelNick->isOp()) nickPrivilege = Images::Op;
+ if (channelNick->isOwner()) nickPrivilege = Images::Owner;
+ if (channelNick->isAdmin()) nickPrivilege = Images::Admin;
+ if (server->getJoinedChannelMembers(channelName) != 0)
+ channelItem->setPixmap(nlvcChannel,
+ KonversationApplication::instance()->images()->getNickIcon(nickPrivilege, false));
+ else
+ channelItem->setPixmap(nlvcChannel,
+ KonversationApplication::instance()->images()->getNickIcon(nickPrivilege, true));
+ }
+ // Remove channel if nick no longer in it.
+ QListViewItem* child = nickRoot->firstChild();
+ while (child)
+ {
+ QListViewItem* nextChild = child->nextSibling();
+ if (channelList.find(child->text(nlvcNick)) == channelList.end())
+ delete child;
+ child = nextChild;
+ }
+ }
+ else
+ {
+ // Nick is offline.
+ // Remove from online nicks, if present.
+ QListViewItem* item = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem);
+ if (item) delete item;
+ // Add to offline list if not already listed.
+ QListViewItem* nickRoot = findItemChild(offlineRoot, nickname, NicksOnlineItem::NicknameItem);
+ if (!nickRoot) nickRoot = new NicksOnlineItem(NicksOnlineItem::NicknameItem,offlineRoot, nickname);
+ nickRoot->setText(nlvcServerName, serverName);
+ // Get addressbook entry for the nick.
+ KABC::Addressee addressee = servr->getOfflineNickAddressee(nickname);
+ // Format additional information for the nick.
+ bool needWhois = false;
+ QString nickAdditionalInfo = getNickAdditionalInfo(0, addressee, needWhois);
+ nickRoot->setText(nlvcAdditionalInfo, nickAdditionalInfo);
+ // Set Kabc icon if the nick is associated with an addressbook entry.
+ if (!addressee.isEmpty())
+ nickRoot->setPixmap(nlvcKabc, m_kabcIconSet.pixmap(
+ QIconSet::Small, QIconSet::Normal, QIconSet::On));
+ else
+ nickRoot->setPixmap(nlvcKabc, m_kabcIconSet.pixmap(
+ QIconSet::Small, QIconSet::Disabled, QIconSet::Off));
+ }
+ }
+ // Erase nicks no longer being watched.
+ QListViewItem* item = networkRoot->firstChild();
+ while (item)
+ {
+ QListViewItem* nextItem = item->nextSibling();
+ if (static_cast<NicksOnlineItem*>(item)->type() != NicksOnlineItem::OfflineItem)
+ {
+ QString nickname = item->text(nlvcNick);
+ if ((watchList.find(nickname) == watchList.end()) &&
+ (serverName == item->text(nlvcServerName))) delete item;
+ }
+ item = nextItem;
+ }
+ item = offlineRoot->firstChild();
+
+ if(item) {
+ while (item)
+ {
+ QListViewItem* nextItem = item->nextSibling();
+ QString nickname = item->text(nlvcNick);
+ if ((watchList.find(nickname) == watchList.end()) &&
+ (serverName == item->text(nlvcServerName))) delete item;
+ item = nextItem;
+ }
+ }
+ else
+ {
+ delete offlineRoot;
+ }
+ // Expand server if newly added to list.
+ if (newNetworkRoot)
+ {
+ networkRoot->setOpen(true);
+ // Connect server NickInfo updates.
+ connect (servr, SIGNAL(nickInfoChanged(Server*, const NickInfoPtr)),
+ this, SLOT(slotNickInfoChanged(Server*, const NickInfoPtr)));
+ }
+}
+
+/**
+ * Determines if a nick is online in any of the servers in a network and returns
+ * a NickInfo if found, otherwise 0.
+ * @param networkName Server network name.
+ * @param nickname Nick name.
+ * @return NickInfo if nick is online in any server, otherwise 0.
+ */
+NickInfoPtr NicksOnline::getOnlineNickInfo(QString& networkName, QString& nickname)
+{
+ // Get list of pointers to all servers.
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ QPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
+ for (Server* server = serverList.first(); server; server = serverList.next())
+ {
+ if (server->getDisplayName() == networkName)
+ {
+ NickInfoPtr nickInfo = server->getNickInfo(nickname);
+ if (nickInfo) return nickInfo;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Requests a WHOIS for a specified server network and nickname.
+ * The request is sent to the first server found in the network.
+ * @param groupName Server group name.
+ * @param nickname Nick name.
+ */
+void NicksOnline::requestWhois(QString& networkName, QString& nickname)
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ QPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
+ for (Server* server = serverList.first(); server; server = serverList.next())
+ {
+ if (server->getDisplayName() == networkName)
+ {
+ server->requestWhois(nickname);
+ return;
+ }
+ }
+}
+
+/**
+ * Refresh the nicklistview for all servers.
+ */
+void NicksOnline::refreshAllServerOnlineLists()
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ QPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
+ Server* server;
+ // Remove servers no longer connected.
+ QListViewItem* child = m_nickListView->firstChild();
+ while (child)
+ {
+ QListViewItem* nextChild = child->nextSibling();
+ QString networkName = child->text(nlvcNetwork);
+ QStringList serverNameList = QStringList::split(",", child->text(nlvcAdditionalInfo));
+ QStringList::Iterator itEnd = serverNameList.end();
+ QStringList::Iterator it = serverNameList.begin();
+ while (it != itEnd)
+ {
+ QString serverName = *it;
+ // Locate server in server list.
+ bool found = false;
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ if ((server->getServerName() == serverName) &&
+ (server->getDisplayName() == networkName)) found = true;
+ }
+ if (!found)
+ it = serverNameList.remove(it);
+ else
+ ++it;
+ }
+ // Remove Networks with no servers connected, otherwise update list of connected
+ // servers.
+ if (serverNameList.empty())
+ delete child;
+ else
+ child->setText(nlvcAdditionalInfo, serverNameList.join(","));
+ child = nextChild;
+ }
+ // Display info for all currently-connected servers.
+ for (server = serverList.first(); server; server = serverList.next())
+ {
+ updateServerOnlineList(server);
+ }
+ // Refresh addressbook buttons.
+ slotNickListView_SelectionChanged();
+}
+
+void NicksOnline::timerFired()
+{
+ // Allow one WHOIS request per cycle.
+ m_whoisRequested = false;
+ refreshAllServerOnlineLists();
+}
+
+/**
+ * When a user double-clicks a nickname in the nicklistview, let server know so that
+ * it can perform the user's chosen default action for that.
+ */
+void NicksOnline::processDoubleClick(QListViewItem* item)
+{
+ // Only emit signal when the user double clicked a nickname rather than
+ // a server name or channel name.
+ QString serverName;
+ QString nickname;
+ if (getItemServerAndNick(item, serverName, nickname))
+ emit doubleClicked(serverName, nickname);
+}
+
+/**
+ * Returns the server name and nickname of the specified nicklistview item.
+ * @param item The nicklistview item.
+ * @return serverName Name of the server for the nick at the item, or Null if not a nick.
+ * @return nickname The nickname at the item.
+ */
+bool NicksOnline::getItemServerAndNick(const QListViewItem* item, QString& serverName, QString& nickname)
+{
+ if (!item) return false;
+ // convert into NicksOnlineItem
+ const NicksOnlineItem* nlItem=static_cast<const NicksOnlineItem*>(item);
+ // If on a network, return false;
+ if (nlItem->type() == NicksOnlineItem::NetworkRootItem) return false;
+ // get server name
+ serverName = item->text(nlvcServerName);
+ // If on a channel, move up to the nickname.
+ if (nlItem->type() == NicksOnlineItem::ChannelItem)
+ {
+ item = item->parent();
+ serverName = item->text(nlvcServerName);
+ }
+ nickname = item->text(nlvcNick);
+ // offline columns are not nick names
+ if (nlItem->type() == NicksOnlineItem::OfflineItem) return false;
+ return true;
+}
+
+NickInfoPtr NicksOnline::getNickInfo(const QListViewItem* item)
+{
+ QString serverName;
+ QString nickname;
+
+ getItemServerAndNick(item, serverName, nickname);
+
+ if (!serverName || !nickname)
+ return 0;
+
+ Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
+
+ if (server) return server->getNickInfo(nickname);
+
+ return 0;
+}
+
+/**
+ * Given a server name and nickname, returns the item in the Nick List View displaying
+ * the nick.
+ * @param serverName Name of server.
+ * @param nickname Nick name.
+ * @return Pointer to QListViewItem displaying the nick, or 0 if not found.
+ *
+ * @see getItemServerAndNick
+ */
+QListViewItem* NicksOnline::getServerAndNickItem(const QString& serverName,
+const QString& nickname)
+{
+ Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
+ if (!server) return 0;
+ QString networkName = server->getDisplayName();
+ QListViewItem* networkRoot = m_nickListView->findItem(networkName, nlvcNetwork);
+ if (!networkRoot) return 0;
+ QListViewItem* nickRoot = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem);
+ return nickRoot;
+}
+
+/**
+ * Perform an addressbook command (edit contact, create new contact,
+ * change/delete association.)
+ * @param id The command id. @ref CommandIDs.
+ *
+ * The operation is performed on the nickname at the currently-selected item in
+ * the nicklistview.
+ *
+ * Also refreshes the nicklistview display to reflect the new addressbook state
+ * for the nick.
+ */
+void NicksOnline::doCommand(int id)
+{
+ if(id < 0)
+ {
+ return;
+ }
+
+ QString serverName;
+ QString nickname;
+ QListViewItem* item = m_nickListView->selectedItem();
+
+ if(!getItemServerAndNick(item, serverName, nickname))
+ {
+ return;
+ }
+
+ // Get the server object corresponding to the server name.
+ Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
+
+ if (!server) return;
+
+ // Get NickInfo object corresponding to the nickname.
+ NickInfoPtr nickInfo = server->getNickInfo(nickname);
+ // Get addressbook entry for the nick.
+ KABC::Addressee addressee;
+
+ if(nickInfo)
+ {
+ addressee = nickInfo->getAddressee();
+ }
+ else
+ {
+ addressee = server->getOfflineNickAddressee(nickname);
+ }
+
+ switch(id)
+ {
+ case ciSendEmail:
+ Konversation::Addressbook::self()->sendEmail(addressee);
+ return; //no need to refresh item
+ case ciAddressbookEdit:
+ Konversation::Addressbook::self()->editAddressee(addressee.uid());
+ return; //no need to refresh item - nickinfo changed will be called anyway.
+ case ciAddressbookChange:
+ if(nickInfo)
+ {
+ nickInfo->showLinkAddressbookUI();
+ }
+ else
+ {
+ LinkAddressbookUI *linkaddressbookui = new LinkAddressbookUI(server->getViewContainer()->getWindow(), NULL, nickname, server->getServerName(), server->getDisplayName(), addressee.realName());
+ linkaddressbookui->show();
+ }
+ break;
+ case ciAddressbookNew:
+ case ciAddressbookDelete:
+ {
+ Konversation::Addressbook *addressbook = Konversation::Addressbook::self();
+
+ if(addressbook && addressbook->getAndCheckTicket())
+ {
+ if(id == ciAddressbookDelete)
+ {
+ if (addressee.isEmpty())
+ {
+ return;
+ }
+
+ addressbook->unassociateNick(addressee, nickname, server->getServerName(), server->getDisplayName());
+ }
+ else
+ {
+ addressee.setGivenName(nickname);
+ addressee.setNickName(nickname);
+ addressbook->associateNickAndUnassociateFromEveryoneElse(addressee, nickname, server->getServerName(), server->getDisplayName());
+ }
+ if(addressbook->saveTicket())
+ {
+ //saveTicket will refresh the addressees for us.
+ if(id == ciAddressbookNew)
+ {
+ Konversation::Addressbook::self()->editAddressee(addressee.uid());
+ }
+ }
+ }
+ break;
+ }
+ case ciJoinChannel:
+ {
+ // only join real channels
+ if (static_cast<NicksOnlineItem*>(m_nickListView->selectedItem())->type() == NicksOnlineItem::ChannelItem)
+ {
+ QString contactChannel = m_nickListView->selectedItem()->text(nlvcChannel);
+ server->queue( "JOIN "+contactChannel );
+ }
+ break;
+ }
+ case ciWhois:
+ server->queue("WHOIS "+nickname);
+ return;
+ case ciOpenQuery:
+ NickInfoPtr nickInfo = server->obtainNickInfo(nickname);
+ class Query* query = server->addQuery(nickInfo, true /*we initiated*/);
+ emit showView(query);
+ return;
+ }
+
+ refreshItem(item);
+}
+
+/**
+ * Get the addressbook state of the nickname at the specified nicklistview item.
+ * @param item Item of the nicklistview.
+ * @return Addressbook state.
+ * 0 = not a nick, 1 = nick has no addressbook association, 2 = nick has association
+ */
+int NicksOnline::getNickAddressbookState(QListViewItem* item)
+{
+ int nickState = nsNotANick;
+ QString serverName;
+ QString nickname;
+ if (getItemServerAndNick(item, serverName, nickname))
+ {
+ Server *server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
+ if (!server) return nsNotANick;
+ NickInfoPtr nickInfo = server->getNickInfo(nickname);
+ if (nickInfo)
+ {
+ if (nickInfo->getAddressee().isEmpty())
+ nickState = nsNoAddress;
+ else
+ nickState = nsHasAddress;
+ }
+ else
+ {
+ if (server->getOfflineNickAddressee(nickname).isEmpty())
+ nickState = nsNoAddress;
+ else
+ nickState = nsHasAddress;
+ }
+ }
+ return nickState;
+}
+
+/**
+ * Sets the enabled/disabled state and labels of the addressbook buttons
+ * based on the given nick addressbook state.
+ * @param nickState The state of the nick. 1 = not associated with addressbook,
+ * 2 = associated with addressbook. @ref getNickAddressbookState.
+ */
+void NicksOnline::setupAddressbookButtons(int nickState)
+{
+ switch (nickState)
+ {
+ case nsNotANick:
+ {
+ m_editContactButton->setEnabled(false);
+ m_changeAssociationButton->setEnabled(false);
+ m_deleteAssociationButton->setEnabled(false);
+ break;
+ }
+ case nsNoAddress:
+ {
+ m_editContactButton->setText(i18n("Create New C&ontact..."));
+ m_editContactButton->setEnabled(true);
+ m_changeAssociationButton->setText(i18n("&Choose Association..."));
+ m_changeAssociationButton->setEnabled(true);
+ m_deleteAssociationButton->setEnabled(false);
+ break;
+ }
+ case nsHasAddress:
+ {
+ m_editContactButton->setText(i18n("Edit C&ontact..."));
+ m_editContactButton->setEnabled(true);
+ m_changeAssociationButton->setText(i18n("&Change Association..."));
+ m_changeAssociationButton->setEnabled(true);
+ m_deleteAssociationButton->setEnabled(true);
+ break;
+ }
+ }
+}
+
+/**
+ * Received when user clicks the Edit Contact (or New Contact) button.
+ */
+void NicksOnline::slotEditContactButton_Clicked()
+{
+ switch (getNickAddressbookState(m_nickListView->selectedItem()))
+ {
+ case nsNotANick: break;
+ case nsNoAddress: { doCommand(ciAddressbookNew); break; }
+ case nsHasAddress: { doCommand(ciAddressbookEdit); break; }
+ }
+}
+
+/**
+ * Received when user clicks the Change Association button.
+ */
+void NicksOnline::slotChangeAssociationButton_Clicked() { doCommand(ciAddressbookChange); }
+/**
+ * Received when user clicks the Delete Association button.
+ */
+void NicksOnline::slotDeleteAssociationButton_Clicked() { doCommand(ciAddressbookDelete); }
+/**
+ * Received when user selects a different item in the nicklistview.
+ */
+void NicksOnline::slotNickListView_SelectionChanged()
+{
+ QListViewItem* item = m_nickListView->selectedItem();
+ int nickState = getNickAddressbookState(item);
+ setupAddressbookButtons(nickState);
+}
+
+/**
+ * Received when right-clicking an item in the NickListView.
+ */
+void NicksOnline::slotNickListView_RightButtonClicked(QListViewItem* item, const QPoint& pt)
+{
+ if (!item) return;
+ m_popupMenu->clear();
+ int nickState = getNickAddressbookState(item);
+ switch (nickState)
+ {
+ case nsNotANick:
+ {
+ break;
+ }
+ case nsNoAddress:
+ {
+ m_popupMenu->insertItem(i18n("&Choose Association..."), ciAddressbookChange);
+ m_popupMenu->insertItem(i18n("Create New C&ontact..."), ciAddressbookNew);
+ m_popupMenu->insertSeparator();
+ m_popupMenu->insertItem(i18n("&Whois"), ciWhois);
+ m_popupMenu->insertItem(i18n("Open &Query"), ciOpenQuery);
+ if (item->text(nlvcServerName).isEmpty())
+ m_popupMenu->insertItem(i18n("&Join Channel"), ciJoinChannel);
+ break;
+ }
+ case nsHasAddress:
+ {
+ m_popupMenu->insertItem(SmallIcon("mail_generic"), i18n("&Send Email..."), ciSendEmail);
+ m_popupMenu->insertSeparator();
+ m_popupMenu->insertItem(SmallIcon("contents"), i18n("Edit C&ontact..."), ciAddressbookEdit);
+ m_popupMenu->insertSeparator();
+ m_popupMenu->insertItem(i18n("&Change Association..."), ciAddressbookChange);
+ m_popupMenu->insertItem(SmallIconSet("editdelete"), i18n("&Delete Association"), ciAddressbookDelete);
+ m_popupMenu->insertSeparator();
+ m_popupMenu->insertItem(i18n("&Whois"), ciWhois);
+ m_popupMenu->insertItem(i18n("Open &Query"), ciOpenQuery);
+ if (item->text(nlvcServerName).isEmpty())
+ m_popupMenu->insertItem(i18n("&Join Channel"), ciJoinChannel);
+ break;
+ }
+ }
+ if (nickState != nsNotANick)
+ m_popupMenu->popup(pt);
+}
+
+/**
+ * Received from popup menu when user chooses something.
+ */
+void NicksOnline::slotPopupMenu_Activated(int id)
+{
+ doCommand(id);
+}
+
+/**
+ * Received from server when a NickInfo changes its information.
+ */
+void NicksOnline::slotNickInfoChanged(Server* server, const NickInfoPtr nickInfo)
+{
+ if (!nickInfo) return;
+ QString nickname = nickInfo->getNickname();
+
+ if (!server) return;
+ QString serverName = server->getServerName();
+ QListViewItem* item = getServerAndNickItem(serverName, nickname);
+ refreshItem(item);
+}
+
+/**
+ * Refreshes the information for the given item in the list.
+ * @param item Pointer to listview item.
+ */
+void NicksOnline::refreshItem(QListViewItem* item)
+{
+ if (!item) return;
+ QString serverName;
+ QString nickname;
+ if (getItemServerAndNick(item, serverName, nickname))
+ {
+ Server *server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
+ if (server)
+ {
+ NickInfoPtr nickInfo = server->getNickInfo(nickname);
+ KABC::Addressee addressee;
+ if (nickInfo)
+ addressee = nickInfo->getAddressee();
+ else
+ addressee = server->getOfflineNickAddressee(nickname);
+ int nickState = 2;
+ if (addressee.isEmpty()) nickState = 1;
+ switch (nickState)
+ {
+ case nsNotANick:
+ break;
+ case nsNoAddress:
+ {
+ item->setPixmap(nlvcKabc, m_kabcIconSet.pixmap(
+ QIconSet::Small, QIconSet::Disabled, QIconSet::Off)); break;
+ }
+ case nsHasAddress:
+ {
+ item->setPixmap(nlvcKabc, m_kabcIconSet.pixmap(
+ QIconSet::Small, QIconSet::Normal, QIconSet::On)); break;
+ }
+ }
+ QString nickAdditionalInfo;
+ bool needWhois = false;
+ if (nickInfo) nickAdditionalInfo = getNickAdditionalInfo(nickInfo, addressee,
+ needWhois);
+ item->setText(nlvcAdditionalInfo, nickAdditionalInfo);
+ if (item == m_nickListView->selectedItem()) setupAddressbookButtons(nickState);
+ }
+ }
+}
+
+void NicksOnline::childAdjustFocus() {}
+
+#include "nicksonline.moc"
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/nicksonline.h b/konversation/src/nicksonline.h
new file mode 100644
index 0000000..6243b78
--- /dev/null
+++ b/konversation/src/nicksonline.h
@@ -0,0 +1,261 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ shows a user tree of friends per server
+ begin: Sam Aug 31 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef NICKSONLINE_H
+#define NICKSONLINE_H
+
+#include "nickinfo.h"
+#include "nicksonlineitem.h"
+#include "chatwindow.h"
+#include "linkaddressbook/nicksonlinetooltip.h"
+
+#include <qvbox.h>
+#include <qiconset.h>
+#include <qpair.h>
+
+
+class KListView;
+class QPushButton;
+class QPopupMenu;
+
+class ChatWindow;
+
+class NicksOnline : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ // Columns of the NickListView.
+ enum NickListViewColumn
+ {
+ nlvcNetworkNickChannel = 0,
+ nlvcNetwork = 0,
+ nlvcNick = 0,
+ nlvcChannel = 0,
+ nlvcKabc = 1,
+ nlvcAdditionalInfo = 1,
+ nlvcServerName = 2 // hidden
+ };
+ // Ids associated with menu/button commands.
+ enum CommandIDs
+ {
+ ciAddressbookChange, ciAddressbookNew, ciAddressbookDelete, ciAddressbookEdit,
+ ciSendEmail, ciWhois, ciJoinChannel, ciOpenQuery
+ };
+ enum NickState
+ {
+ nsNotANick = 0, // User didn't click on a nickname.
+ nsNoAddress = 1, // Nick does not have an addressbook association.
+ nsHasAddress = 2 // Nick has an associated addressbook entry.
+ };
+
+ explicit NicksOnline(QWidget* parent);
+ ~NicksOnline();
+
+ // These are here for the benefit of NicksOnlineTooltip.
+ KListView* getNickListView();
+ NickInfoPtr getNickInfo(const QListViewItem* item);
+
+ virtual bool canBeFrontView() { return true; }
+
+ signals:
+ /**
+ * Emitted when user clicks Edit Watch List button.
+ */
+ void editClicked();
+ /**
+ * Emitted whenever user double-clicks a nick in the Nicks Online tab.
+ */
+ void doubleClicked(const QString& server,const QString& nick);
+
+ void showView(ChatWindow* view);
+
+ public slots:
+
+ /**
+ * Refresh the nicklistview for a single server.
+ * @param server The server to be refreshed.
+ */
+ void updateServerOnlineList(Server* server);
+
+ protected slots:
+ /**
+ * When a user double-clicks a nickname in the nicklistview, let server know so that
+ * it can perform the user's chosen default action for that.
+ */
+ void processDoubleClick(QListViewItem* item);
+ /**
+ * Timer used to refresh display.
+ */
+ void timerFired();
+ /**
+ * Received when user clicks the Edit Contact (or New Contact) button.
+ */
+ void slotEditContactButton_Clicked();
+ /**
+ * Received when user clicks the Change Association button.
+ */
+ void slotChangeAssociationButton_Clicked();
+ /**
+ * Received when user clicks the Delete Association button.
+ */
+ void slotDeleteAssociationButton_Clicked();
+ /**
+ * Received when user selects a different item in the nicklistview.
+ */
+ void slotNickListView_SelectionChanged();
+ /**
+ * Received when right-clicking an item in the NickListView.
+ */
+ void slotNickListView_RightButtonClicked(QListViewItem* item, const QPoint& pt);
+ /**
+ * Received from server when a NickInfo changes its information.
+ */
+ void slotNickInfoChanged(Server* server, const NickInfoPtr nickInfo);
+ /**
+ * Received from popup menu when user chooses something.
+ */
+ void slotPopupMenu_Activated(int id);
+
+ protected:
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ private:
+ /**
+ * Returns the named child of parent item in a NicksOnlineItem
+ * @param parent Pointer to a NicksOnlineItem.
+ * @param name The name in the desired child QListViewItem, must be in column 0.
+ * @param type The type of entry to be found
+ * @return Pointer to the child QListViewItem or 0 if not found.
+ */
+ QListViewItem* findItemChild(const QListViewItem* parent, const QString& name, NicksOnlineItem::NickListViewColumn type);
+ /**
+ * Returns the first occurrence of a child item of a given type in a NicksOnlineItem
+ * @param parent Pointer to a NicksOnlineItem.
+ * @param type The type of entry to be found
+ * @return Pointer to the child QListViewItem or 0 if not found.
+ */
+ QListViewItem* findItemType(const QListViewItem* parent, NicksOnlineItem::NickListViewColumn type);
+ /**
+ * Returns a pointer to the network QListViewItem with the given name.
+ * @param name The name of the network, assumed to be in column 0 of the item.
+ * @return Pointer to the QListViewItem or 0 if not found.
+ */
+ QListViewItem* findNetworkRoot(const QString& name);
+ /**
+ * Refresh the nicklistview for all servers.
+ */
+ void refreshAllServerOnlineLists();
+ /**
+ * Refreshes the information for the given item in the list.
+ * @param item Pointer to listview item.
+ */
+ void refreshItem(QListViewItem* item);
+ /**
+ * Return a string containing formatted additional information about a nick.
+ * @param nickInfo A pointer to NickInfo structure for the nick.
+ * @return A string formatted for display containing the information
+ * about the nick.
+ * @return needWhois True if a WHOIS needs to be performed on the nick
+ * to get additional information.
+ */
+ QString getNickAdditionalInfo(NickInfoPtr nickInfo, KABC::Addressee addressee,
+ bool& needWhois);
+ /**
+ * Invokes the KAddressBook contact editor for the specified contact id.
+ * @param uid Id of the contact.
+ * @return False if unable to invoke the Contact editor.
+ */
+ bool editAddressee(const QString &uid);
+ /**
+ * Returns the server name and nickname of the specified nicklistview item.
+ * @param item The nicklistview item.
+ * @return serverName Name of the server for the nick at the item, or Null if not a nick.
+ * @return nickname The nickname at the item.
+ */
+ bool getItemServerAndNick(const QListViewItem* item, QString& serverName, QString& nickname);
+ /**
+ * Given a server name and nickname, returns the item in the Nick List View displaying
+ * the nick.
+ * @param serverName Name of server.Server
+ * @param nickname Nick name.
+ * @return Pointer to QListViewItem displaying the nick, or 0 if not found.
+ *
+ * @see getItemServerAndNick
+ */
+ QListViewItem* getServerAndNickItem(const QString& serverName, const QString& nickname);
+ /**
+ * Perform an addressbook command (edit contact, create new contact,
+ * change/delete association.)
+ * @param id The command id. @ref CommandIDs.
+ *
+ * The operation is performed on the nickname at the currently-selected item in
+ * the nicklistview.
+ *
+ * Also refreshes the nicklistview display to reflect the new addressbook state
+ * for the nick.
+ */
+ void doCommand(int id);
+ /**
+ * Get the addressbook state of the nickname at the specified nicklistview item.
+ * @param item Item of the nicklistview.
+ * @return Addressbook state.
+ * 0 = not a nick, 1 = nick has no addressbook association, 2 = nick has association
+ */
+ int getNickAddressbookState(QListViewItem* item);
+ /**
+ * Sets the enabled/disabled state and labels of the addressbook buttons
+ * based on the given nick addressbook state.
+ * @param nickState The state of the nick. 1 = not associated with addressbook,
+ * 2 = associated with addressbook. @ref getNickAddressbookState.
+ */
+ void setupAddressbookButtons(int nickState);
+ /**
+ * Determines if a nick is online in any of the servers in a network and returns
+ * a NickInfo if found, otherwise 0.
+ * @param networkName Server network name.
+ * @param nickname Nick name.
+ * @return NickInfo if nick is online in any server, otherwise 0.
+ */
+ NickInfoPtr getOnlineNickInfo(QString& networkName, QString& nickname);
+ /**
+ * Requests a WHOIS for a specified server network and nickname.
+ * The request is sent to the first server found in the network.
+ * @param groupName Server group name.
+ * @param nickname Nick name.
+ */
+ void requestWhois(QString& networkName, QString& nickname);
+
+ // The main display of networks, nicks, and channels.
+ KListView* m_nickListView;
+ // Buttons on screen.
+ QPushButton* m_editContactButton;
+ QPushButton* m_changeAssociationButton;
+ QPushButton* m_deleteAssociationButton;
+ // Context menu when right-clicking a nick.
+ QPopupMenu* m_popupMenu;
+ // Helper to display tooltip information for nicks.
+ Konversation::KonversationNicksOnlineToolTip *m_tooltip;
+ // A string containing the identifier for the "Offline" listview item
+ QString c_offline;
+ // Timer for refreshing display and generating WHOISes.
+ QTimer* m_timer;
+ // Addressbook icon.
+ QIconSet m_kabcIconSet;
+ /* Set to False every 8 seconds so that we generate a WHOIS on watch nicks that
+ lack information.*/
+ bool m_whoisRequested;
+};
+#endif
diff --git a/konversation/src/nicksonlineitem.cpp b/konversation/src/nicksonlineitem.cpp
new file mode 100644
index 0000000..cd8cd1d
--- /dev/null
+++ b/konversation/src/nicksonlineitem.cpp
@@ -0,0 +1,52 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#include "nicksonlineitem.h"
+
+
+NicksOnlineItem::NicksOnlineItem(int type, QListView* parent, const QString& name, const QString& col2) :
+ KListViewItem(parent, name, col2)
+{
+ m_type=type;
+}
+
+NicksOnlineItem::NicksOnlineItem(int type, QListViewItem* parent, const QString& name, const QString& col2) :
+ KListViewItem(parent, name, col2)
+{
+ m_type=type;
+}
+
+/**
+ * Reimplemented to make sure, "Offline" items always get sorted to the bottom of the list
+ * @param i Pointer to the QListViewItem to compare with.
+ * @param col The column to compare
+ * @param ascending Specify sorting direction
+ * @return -1 if this item's value is smaller than i, 0 if they are equal, 1 if it's greater
+ */
+int NicksOnlineItem::compare(QListViewItem* i,int col,bool ascending) const
+{
+ // if we are the Offline item, make sure we get sorted at the end of the list
+ if(m_type==OfflineItem) return ascending ? 1 : -1;
+ // if we are competing with an Offline item, always lose
+ if(static_cast<NicksOnlineItem*>(i)->type()==OfflineItem) return ascending ? -1 : 1;
+
+ // otherwise compare items case-insensitively
+ return key(col,ascending).lower().localeAwareCompare(i->key(col,ascending).lower());
+}
+
+/**
+ * Returns the type of the item.
+ * @return One of the enum NickListViewColumn
+ */
+int NicksOnlineItem::type() const
+{
+ return m_type;
+}
diff --git a/konversation/src/nicksonlineitem.h b/konversation/src/nicksonlineitem.h
new file mode 100644
index 0000000..5711e60
--- /dev/null
+++ b/konversation/src/nicksonlineitem.h
@@ -0,0 +1,58 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#ifndef NICKSONLINEITEM_H
+#define NICKSONLINEITEM_H
+
+#include <klistview.h>
+
+
+class NicksOnlineItem : public KListViewItem
+{
+ public:
+ enum NickListViewColumn
+ {
+ NetworkRootItem=0, // TODO: not used yet
+ NicknameItem=1, // TODO: not used yet
+ ChannelItem=2, // TODO: not used yet
+ OfflineItem=3 // this item is the "Offline" item
+ };
+
+ NicksOnlineItem(int type,
+ QListView* parent,
+ const QString& name,
+ const QString& col2 = QString());
+
+ NicksOnlineItem(int type,
+ QListViewItem* parent,
+ const QString& name,
+ const QString& col2 = QString());
+
+ /**
+ * Reimplemented to make sure, "Offline" items always get sorted to the bottom of the list
+ * @param i Pointer to the QListViewItem to compare with.
+ * @param col The column to compare
+ * @param ascending Specify sorting direction
+ * @return -1 if this item's value is smaller than i, 0 if they are equal, 1 if it's greater
+ */
+ virtual int compare(QListViewItem* i,int col,bool ascending) const;
+
+ /**
+ * Returns the type of the item.
+ * @return One of the enum NickListViewColumn
+ */
+ int type() const;
+
+ protected:
+ int m_type;
+};
+
+#endif
diff --git a/konversation/src/notificationhandler.cpp b/konversation/src/notificationhandler.cpp
new file mode 100644
index 0000000..f8477e9
--- /dev/null
+++ b/konversation/src/notificationhandler.cpp
@@ -0,0 +1,334 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#include "notificationhandler.h"
+#include "common.h"
+#include "chatwindow.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "viewcontainer.h"
+#include "trayicon.h"
+#include "server.h"
+
+#include <qstylesheet.h>
+
+#include <knotifyclient.h>
+#include <kstringhandler.h>
+#include <klocale.h>
+
+
+namespace Konversation
+{
+
+ NotificationHandler::NotificationHandler(KonversationApplication* parent, const char* name)
+ : QObject(parent, name)
+ {
+ m_mainWindow = parent->getMainWindow();
+ }
+
+ NotificationHandler::~NotificationHandler()
+ {
+ }
+
+ void NotificationHandler::message(ChatWindow* chatWin, const QString& fromNick, const QString& message)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ QString cleanedMessage = QStyleSheet::escape(Konversation::removeIrcMarkup(message));
+ QString cutup = addLineBreaks(cleanedMessage);
+
+ KNotifyClient::event(m_mainWindow->winId(), "message", QString("<qt>&lt;%1&gt; %2</qt>").arg(fromNick).arg(cutup));
+
+ if(!Preferences::trayNotifyOnlyOwnNick())
+ {
+ startTrayNotification(chatWin);
+ }
+
+ if(Preferences::oSDShowChannel() &&
+ (!m_mainWindow->isActiveWindow() || (chatWin != m_mainWindow->getViewContainer()->getFrontView())))
+ {
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ konvApp->osd->showOSD('(' + chatWin->getName() + ") <" + fromNick + "> " + cleanedMessage);
+ }
+ }
+
+ void NotificationHandler::nick(ChatWindow* chatWin, const QString& fromNick, const QString& message)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ QString cleanedMessage = QStyleSheet::escape(Konversation::removeIrcMarkup(message));
+ QString cutup = addLineBreaks(cleanedMessage);
+
+ KNotifyClient::event(m_mainWindow->winId(), "nick", QString("<qt>&lt;%1&gt; %2</qt>").arg(fromNick).arg(cutup));
+
+ startTrayNotification(chatWin);
+
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ if((Preferences::oSDShowChannel() || Preferences::oSDShowOwnNick()) &&
+ (!m_mainWindow->isActiveWindow() ||
+ (chatWin != m_mainWindow->getViewContainer()->getFrontView())))
+ {
+ konvApp->osd->showOSD(i18n("[HighLight] (%1) <%2> %3").arg(chatWin->getName()).arg(fromNick).arg(cleanedMessage));
+ }
+ }
+
+ void NotificationHandler::queryMessage(ChatWindow* chatWin,
+ const QString& fromNick, const QString& message)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ QString cleanedMessage = QStyleSheet::escape(Konversation::removeIrcMarkup(message));
+ QString cutup = addLineBreaks(cleanedMessage);
+
+ KNotifyClient::event(m_mainWindow->winId(), "queryMessage", QString("<qt>&lt;%1&gt; %2</qt>").arg(fromNick).arg(cutup));
+
+ startTrayNotification(chatWin);
+
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+
+ if(Preferences::oSDShowQuery() && (!m_mainWindow->isActiveWindow() ||
+ (chatWin != m_mainWindow->getViewContainer()->getFrontView())))
+ {
+ konvApp->osd->showOSD(i18n("[Query] <%1> %2").arg(fromNick).arg(cleanedMessage));
+ }
+ }
+
+ void NotificationHandler::startTrayNotification(ChatWindow* chatWin)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (!chatWin->getServer() || (Preferences::disableNotifyWhileAway() && chatWin->getServer()->isAway()))
+ return;
+
+ if (!m_mainWindow->isActiveWindow() && chatWin->getServer()->isConnected())
+ m_mainWindow->systemTrayIcon()->startNotification();
+ }
+
+ void NotificationHandler::join(ChatWindow* chatWin, const QString& nick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "join", i18n("%1 joined %2").arg(nick, chatWin->getName()));
+
+ // OnScreen Message
+ if(Preferences::oSDShowChannelEvent() &&
+ (!m_mainWindow->isActiveWindow() || (chatWin != m_mainWindow->getViewContainer()->getFrontView())))
+ {
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ konvApp->osd->showOSD(i18n("%1 joined %2").arg(nick, chatWin->getName()));
+ }
+ }
+
+ void NotificationHandler::part(ChatWindow* chatWin, const QString& nick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "part", i18n("%1 parted %2").arg(nick, chatWin->getName()));
+
+ // OnScreen Message
+ if(Preferences::oSDShowChannelEvent() &&
+ (!m_mainWindow->isActiveWindow() || (chatWin != m_mainWindow->getViewContainer()->getFrontView())))
+ {
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ konvApp->osd->showOSD(i18n("%1 parted %2").arg(nick, chatWin->getName()));
+ }
+ }
+
+ void NotificationHandler::quit(ChatWindow* chatWin, const QString& nick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "part", i18n("%1 quit %2").arg(nick, chatWin->getServer()->getServerName()));
+ }
+
+ void NotificationHandler::nickChange(ChatWindow* chatWin, const QString& oldNick, const QString& newNick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "nickchange", i18n("%1 changed nickname to %2").arg(oldNick, newNick));
+ }
+
+ void NotificationHandler::dccIncoming(ChatWindow* chatWin, const QString& fromNick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "dcc_incoming", i18n("%1 wants to send a file to you").arg(fromNick));
+ }
+
+ void NotificationHandler::mode(ChatWindow* chatWin, const QString& /*nick*/)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "mode");
+ }
+
+ void NotificationHandler::query(ChatWindow* chatWin, const QString& fromNick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ startTrayNotification(chatWin);
+
+ KNotifyClient::event(m_mainWindow->winId(), "query",
+ i18n("%1 has started a conversation (query) with you.").arg(fromNick));
+ }
+
+ void NotificationHandler::nickOnline(ChatWindow* chatWin, const QString& nick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "notify",
+ i18n("%1 is online (%2).").arg(nick).arg(chatWin->getServer()->getServerName()));
+ }
+
+ void NotificationHandler::nickOffline(ChatWindow* chatWin, const QString& nick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "notify",
+ i18n("%1 went offline (%2).").arg(nick).arg(chatWin->getServer()->getServerName()));
+ }
+
+ void NotificationHandler::kick(ChatWindow* chatWin, const QString& channel,const QString& nick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "kick",
+ i18n("You are kicked by %1 from %2").arg(nick).arg(channel));
+ }
+
+ void NotificationHandler::dccChat(ChatWindow* chatWin, const QString& nick)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "dccChat",
+ i18n("%1 started a dcc chat with you").arg(nick));
+ }
+
+ void NotificationHandler::highlight(ChatWindow* chatWin, const QString& fromNick, const QString& message)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ startTrayNotification(chatWin);
+
+ if(Preferences::oSDShowOwnNick() &&
+ (!m_mainWindow->isActiveWindow() || (chatWin != m_mainWindow->getViewContainer()->getFrontView())))
+ {
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ // if there was no nick associated, this must be a command message, so don't try displaying
+ // an empty nick in <>
+ if(fromNick.isEmpty())
+ konvApp->osd->showOSD(i18n("[HighLight] (%1) *** %2").arg(chatWin->getName()).arg(message));
+ // normal highlight message
+ else
+ konvApp->osd->showOSD(i18n("[HighLight] (%1) <%2> %3").arg(chatWin->getName()).arg(fromNick).arg(message));
+ }
+ }
+
+ void NotificationHandler::connectionFailure(ChatWindow* chatWin, const QString& server)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "connectionFailure",
+ i18n("Failed to connect to %1").arg(server));
+ }
+
+ void NotificationHandler::channelJoin(ChatWindow* chatWin, const QString& channel)
+ {
+ if (!chatWin || !chatWin->notificationsEnabled())
+ return;
+
+ if (Preferences::disableNotifyWhileAway() && chatWin->getServer() && chatWin->getServer()->isAway())
+ return;
+
+ KNotifyClient::event(m_mainWindow->winId(), "channelJoin", i18n("You have joined %1.").arg(channel));
+ }
+
+ QString NotificationHandler::addLineBreaks(const QString& string)
+ {
+ QString cutup = string;
+ int offset = 0;
+
+ for(uint i = 0; i < string.length(); i += 50)
+ {
+ cutup.insert(i + (offset * 4), "<br>");
+ ++offset;
+ }
+
+ return cutup;
+ }
+
+}
+
+#include "notificationhandler.moc"
diff --git a/konversation/src/notificationhandler.h b/konversation/src/notificationhandler.h
new file mode 100644
index 0000000..1f7319c
--- /dev/null
+++ b/konversation/src/notificationhandler.h
@@ -0,0 +1,61 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#ifndef KONVERSATIONNOTIFICATIONHANDLER_H
+#define KONVERSATIONNOTIFICATIONHANDLER_H
+
+#include <qobject.h>
+
+
+class ChatWindow;
+class KonversationApplication;
+class KonversationMainWindow;
+
+namespace Konversation
+{
+
+ class NotificationHandler : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ explicit NotificationHandler(KonversationApplication* parent = 0, const char* name = 0);
+ ~NotificationHandler();
+
+ public slots:
+ void message(ChatWindow* chatWin, const QString& fromNick, const QString& message);
+ void nick(ChatWindow* chatWin, const QString& fromNick, const QString& message);
+ void join(ChatWindow* chatWin, const QString& nick);
+ void part(ChatWindow* chatWin, const QString& nick);
+ void quit(ChatWindow* chatWin, const QString& nick);
+ void nickChange(ChatWindow* chatWin, const QString& oldNick, const QString& newNick);
+ void dccIncoming(ChatWindow* chatWin, const QString& fromNick);
+ void mode(ChatWindow* chatWin, const QString& nick);
+ void query(ChatWindow* chatWin, const QString& fromNick);
+ void queryMessage(ChatWindow* chatWin, const QString& fromNick, const QString& message);
+ void nickOnline(ChatWindow* chatWin, const QString& nick);
+ void nickOffline(ChatWindow* chatWin, const QString& nick);
+ void kick(ChatWindow* chatWin, const QString& channel,const QString& nick);
+ void dccChat(ChatWindow* chatWin, const QString& nick);
+ void highlight(ChatWindow* chatWin, const QString& fromNick, const QString& message);
+ void connectionFailure(ChatWindow* chatWin, const QString& server);
+ void channelJoin(ChatWindow* chatWin, const QString& channel);
+
+ protected:
+ void startTrayNotification(ChatWindow* chatWin);
+ QString addLineBreaks(const QString& string);
+
+ private:
+ KonversationMainWindow* m_mainWindow;
+ };
+
+}
+#endif
diff --git a/konversation/src/osd.cpp b/konversation/src/osd.cpp
new file mode 100644
index 0000000..ce173ab
--- /dev/null
+++ b/konversation/src/osd.cpp
@@ -0,0 +1,446 @@
+/*
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+*/
+
+/*
+ Provides an interface to a plain QWidget, which is independent of KDE (bypassed to X11)
+ begin: Fre Sep 26 2003
+ Copyright (C) 2003 Christian Muehlhaeuser <chris@chris.de>
+ Copyright (C) 2004 Michael Goettsche <michael.goettsche@kdemail.net>
+*/
+
+#include "osd.h"
+#include "konversationapplication.h"
+#include "common.h"
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qregexp.h>
+
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kglobalsettings.h> //unsetColors()
+
+#include <X11/Xlib.h> //reposition()
+
+
+OSDWidget::OSDWidget( const QString &appName, QWidget *parent, const char *name )
+: QWidget( parent, name, WNoAutoErase | WStyle_Customize | WX11BypassWM | WStyle_StaysOnTop | WStyle_Tool )
+, m_appName( appName )
+, m_duration( 5000 )
+, m_shadow( true )
+, m_alignment( Middle )
+, m_screen( 0 )
+, m_y( MARGIN )
+, m_dirty( false )
+{
+ setFocusPolicy( NoFocus );
+ setBackgroundMode( NoBackground );
+ unsetColors();
+
+ connect( &timer, SIGNAL( timeout() ), SLOT( hide() ) );
+ connect( &timerMin, SIGNAL( timeout() ), SLOT( minReached() ) );
+}
+
+void OSDWidget::renderOSDText( const QString &txt )
+{
+ // Escaped text
+ QString text = Konversation::removeIrcMarkup(txt);
+
+ static QBitmap mask;
+
+ //This is various spacings and margins, based on the font to look "just right"
+ const uint METRIC = fontMetrics().width( 'x' );
+
+ // Set a sensible maximum size, don't cover the whole desktop or cross the screen
+ QSize max = QApplication::desktop()->screen( m_screen )->size() - QSize( MARGIN*2 + METRIC*2, 100 );
+ QFont titleFont( "Arial", 12, QFont::Bold );
+ QFontMetrics titleFm( titleFont );
+
+ // The title cannnot be taller than one line
+ // AlignAuto = align Arabic to the right, etc.
+ QRect titleRect = titleFm.boundingRect( 0, 0, max.width() - METRIC, titleFm.height(), AlignAuto, m_appName );
+ // The osd cannot be larger than the screen
+ QRect textRect = fontMetrics().boundingRect( 0, 0, max.width(), max.height(), AlignAuto | WordBreak, text );
+
+ if ( textRect.width() < titleRect.width() )
+ textRect.setWidth( titleRect.width() );
+
+ //this should still be within the screen bounds
+ textRect.addCoords( 0, 0, METRIC*2, titleRect.height() + METRIC );
+
+ osdBuffer.resize( textRect.size() );
+ mask.resize( textRect.size() );
+
+ // Start painting!
+ QPainter bufferPainter( &osdBuffer );
+ QPainter maskPainter( &mask );
+
+ // Draw backing rectangle
+ const uint xround = (METRIC * 200) / textRect.width();
+ const uint yround = (METRIC * 200) / textRect.height();
+
+ bufferPainter.setPen( Qt::black );
+ bufferPainter.setBrush( backgroundColor() );
+ bufferPainter.drawRoundRect( textRect, xround, yround );
+ bufferPainter.setFont( font() );
+
+ const uint w = textRect.width() - 1;
+ const uint h = textRect.height() - 1;
+
+ // Draw the text shadow
+ if ( m_shadow )
+ {
+ bufferPainter.setPen( backgroundColor().dark( 175 ) );
+ bufferPainter.drawText( METRIC + 3, (METRIC/2) + titleFm.height() + 1, w, h, AlignLeft | WordBreak, text );
+ }
+
+ // Draw the text
+ bufferPainter.setPen( foregroundColor() );
+ bufferPainter.drawText( METRIC, (METRIC/2) + titleFm.height() - 1, w, h, AlignLeft | WordBreak, text );
+
+ // Draw the title text
+ bufferPainter.setFont( titleFont );
+ bufferPainter.drawText( METRIC * 2, (METRIC/2), w, h, AlignLeft, m_appName );
+
+ // Masking for transparency
+ mask.fill( Qt::black );
+ maskPainter.setBrush( Qt::white );
+ maskPainter.drawRoundRect( textRect, xround, yround );
+ setMask( mask );
+
+ //do last to reduce noticeable change when showing multiple OSDs in succession
+ reposition( textRect.size() );
+
+ m_currentText = text;
+ m_dirty = false;
+
+ update();
+}
+
+ // slot
+void OSDWidget::showOSD( const QString &text, bool preemptive )
+{
+ if ( isEnabled() && !text.isEmpty() )
+ {
+
+ QString plaintext = text.copy();
+ plaintext.replace(QRegExp("</?(?:font|a|b|i)\\b[^>]*>"), QString(""));
+ plaintext.replace(QString("&lt;"), QString("<"));
+ plaintext.replace(QString("&gt;"), QString(">"));
+ plaintext.replace(QString("&amp;"), QString("&"));
+
+ if ( preemptive || !timerMin.isActive() )
+ {
+ m_currentText = plaintext;
+ m_dirty = true;
+
+ show();
+ }
+ else textBuffer.append( plaintext ); //queue
+ }
+}
+
+void OSDWidget::minReached() //SLOT
+{
+ if ( !textBuffer.isEmpty() )
+ {
+ renderOSDText( textBuffer.front() );
+ textBuffer.pop_front();
+
+ if( m_duration )
+ //timerMin is still running
+ timer.start( m_duration, true );
+ }
+ else timerMin.stop();
+}
+
+void OSDWidget::setDuration( int ms )
+{
+ m_duration = ms;
+
+ if( !m_duration ) timer.stop();
+}
+
+void OSDWidget::setFont( QFont newFont )
+{
+ QWidget::setFont( newFont );
+ refresh();
+}
+
+void OSDWidget::setShadow( bool shadow )
+{
+ m_shadow = shadow;
+ refresh();
+}
+
+void OSDWidget::setTextColor( const QColor &newColor )
+{
+ setPaletteForegroundColor( newColor );
+ refresh();
+}
+
+void OSDWidget::setBackgroundColor( const QColor &newColor )
+{
+ setPaletteBackgroundColor( newColor );
+ refresh();
+}
+
+void OSDWidget::unsetColors()
+{
+ setPaletteForegroundColor( KGlobalSettings::activeTextColor() );
+ setPaletteBackgroundColor( KGlobalSettings::activeTitleColor() );
+
+ refresh();
+}
+
+void OSDWidget::setOffset( int /*x*/, int y )
+{
+ //m_offset = QPoint( x, y );
+ m_y = y;
+ reposition();
+}
+
+void OSDWidget::setAlignment( Alignment a )
+{
+ m_alignment = a;
+ reposition();
+}
+
+void OSDWidget::setScreen( uint screen )
+{
+ const uint n = QApplication::desktop()->numScreens();
+ m_screen = (screen >= n) ? n-1 : (int)screen;
+ reposition();
+}
+
+bool OSDWidget::event( QEvent *e )
+{
+ switch( e->type() )
+ {
+ case QEvent::Paint:
+ bitBlt( this, 0, 0, &osdBuffer );
+ return true;
+
+ default:
+ return QWidget::event( e );
+ }
+}
+
+void OSDWidget::mousePressEvent( QMouseEvent* )
+{
+ hide();
+ emit hidden();
+}
+
+void OSDWidget::show()
+{
+ // Don't show the OSD widget when the desktop is locked
+ if ( isKDesktopLockRunning() == Locked )
+ {
+ minReached(); // don't queue the message
+ return;
+ }
+
+ if ( m_dirty ) renderOSDText( m_currentText );
+
+ QWidget::show();
+
+ if ( m_duration ) //duration 0 -> stay forever
+ {
+ timer.start( m_duration, true ); //calls hide()
+ timerMin.start( 150 ); //calls minReached()
+ }
+}
+
+void OSDWidget::refresh()
+{
+ if ( isVisible() )
+ {
+ //we need to update the buffer
+ renderOSDText( m_currentText );
+ }
+ else m_dirty = true; //ensure we are re-rendered before we are shown
+}
+
+void OSDWidget::reposition( QSize newSize )
+{
+ if( !newSize.isValid() ) newSize = size();
+
+ QPoint newPos( MARGIN, m_y );
+ const QRect screen = QApplication::desktop()->screenGeometry( m_screen );
+
+ //TODO m_y is the middle of the OSD, and don't exceed screen margins
+
+ switch ( m_alignment )
+ {
+ case Left:
+ break;
+
+ case Right:
+ newPos.rx() = screen.width() - MARGIN - newSize.width();
+ break;
+
+ case Center:
+ newPos.ry() = (screen.height() - newSize.height()) / 2;
+
+ //FALL THROUGH
+
+ case Middle:
+ newPos.rx() = (screen.width() - newSize.width()) / 2;
+ break;
+ }
+
+ //ensure we don't dip below the screen
+ if( newPos.y()+newSize.height() > screen.height()-MARGIN ) newPos.ry() = screen.height()-MARGIN-newSize.height();
+
+ // correct for screen position
+ newPos += screen.topLeft();
+
+ //ensure we are painted before we move
+ if( isVisible() ) paintEvent( 0 );
+
+ //fancy X11 move+resize, reduces visual artifacts
+ XMoveResizeWindow( x11Display(), winId(), newPos.x(), newPos.y(), newSize.width(), newSize.height() );
+}
+
+////// OSDPreviewWidget below /////////////////////
+
+#include <kcursor.h> //previewWidget
+#include <klocale.h>
+
+OSDPreviewWidget::OSDPreviewWidget( const QString &appName, QWidget *parent, const char *name )
+: OSDWidget( appName, parent, name )
+, m_dragging( false )
+{
+ m_currentText = i18n( "OSD Preview - drag to reposition" );
+ m_duration = 0;
+}
+
+void OSDPreviewWidget::mousePressEvent( QMouseEvent *event )
+{
+ m_dragOffset = event->pos();
+
+ if ( event->button() == LeftButton && !m_dragging )
+ {
+ grabMouse( KCursor::sizeAllCursor() );
+ m_dragging = true;
+ }
+}
+
+void OSDPreviewWidget::mouseReleaseEvent( QMouseEvent * /*event*/ )
+{
+ if ( m_dragging )
+ {
+ m_dragging = false;
+ releaseMouse();
+
+ // compute current Position && offset
+ QDesktopWidget *desktop = QApplication::desktop();
+ int currentScreen = desktop->screenNumber( pos() );
+
+ if ( currentScreen != -1 )
+ {
+ // set new data
+ m_screen = currentScreen;
+ m_y = QWidget::y();
+
+ emit positionChanged();
+ }
+ }
+}
+
+void OSDPreviewWidget::mouseMoveEvent( QMouseEvent *e )
+{
+ if ( m_dragging && this == mouseGrabber() )
+ {
+ const QRect screen = QApplication::desktop()->screenGeometry( m_screen );
+ const uint hcenter = screen.width() / 2;
+ const uint eGlobalPosX = e->globalPos().x() - screen.left();
+ const uint snapZone = screen.width() / 8;
+
+ QPoint destination = e->globalPos() - m_dragOffset - screen.topLeft();
+ int maxY = screen.height() - height() - MARGIN;
+ if( destination.y() < MARGIN ) destination.ry() = MARGIN;
+ if( destination.y() > maxY ) destination.ry() = maxY;
+
+ if( eGlobalPosX < (hcenter-snapZone) )
+ {
+ m_alignment = Left;
+ destination.rx() = MARGIN;
+ }
+ else if( eGlobalPosX > (hcenter+snapZone) )
+ {
+ m_alignment = Right;
+ destination.rx() = screen.width() - MARGIN - width();
+ }
+ else
+ {
+ const uint eGlobalPosY = e->globalPos().y() - screen.top();
+ const uint vcenter = screen.height()/2;
+
+ destination.rx() = hcenter - width()/2;
+
+ if( eGlobalPosY >= (vcenter-snapZone) && eGlobalPosY <= (vcenter+snapZone) )
+ {
+ m_alignment = Center;
+ destination.ry() = vcenter - height()/2;
+ }
+ else m_alignment = Middle;
+ }
+
+ destination += screen.topLeft();
+
+ move( destination );
+ }
+}
+
+// the code was taken from pilotDaemon.cc in KPilot
+ // static
+OSDWidget::KDesktopLockStatus OSDWidget::isKDesktopLockRunning()
+{
+ if (!Preferences::oSDCheckDesktopLock())
+ return NotLocked;
+
+ DCOPClient *dcopptr = KApplication::kApplication()->dcopClient();
+
+ // Can't tell, very weird
+ if (!dcopptr || !dcopptr->isAttached())
+ {
+ kdWarning() << k_funcinfo << ": Could not make DCOP connection." << endl;
+ return DCOPError;
+ }
+
+ QByteArray data,returnValue;
+ QCString returnType;
+
+ if (!dcopptr->call("kdesktop","KScreensaverIface","isBlanked()",
+ data,returnType,returnValue,true))
+ {
+ // KDesktop is not running. Maybe we are in a KDE4 desktop...
+ kdDebug() << k_funcinfo << ": Check for screensaver failed." << endl;
+ return DCOPError;
+ }
+
+ if (returnType == "bool")
+ {
+ bool b;
+ QDataStream reply(returnValue,IO_ReadOnly);
+ reply >> b;
+ return (b ? Locked : NotLocked);
+ }
+ else
+ {
+ kdWarning() << k_funcinfo << ": Strange return value from screensaver. "
+ << "Assuming screensaver is active." << endl;
+ // Err on the side of safety.
+ return Locked;
+ }
+}
+
+#include "osd.moc"
diff --git a/konversation/src/osd.h b/konversation/src/osd.h
new file mode 100644
index 0000000..997162b
--- /dev/null
+++ b/konversation/src/osd.h
@@ -0,0 +1,124 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Provides an interface to a plain QWidget, which is independent of KDE (bypassed to X11)
+ begin: Fre Sep 26 2003
+ Copyright (C) 2003 Christian Muehlhaeuser <chris@chris.de>
+ Copyright (C) 2004 Michael Goettsche <michael.goettsche@kdemail.net>
+*/
+
+#ifndef OSD_H
+#define OSD_H
+
+#include <qpixmap.h> //stack allocated
+#include <qtimer.h> //stack allocated
+#include <qwidget.h> //baseclass
+
+
+class QFont;
+class QString;
+class QStringList;
+class QTimer;
+class MetaBundle;
+
+class OSDWidget : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ enum Alignment { Left, Middle, Center, Right };
+
+ explicit OSDWidget(const QString &appName, QWidget *parent = 0, const char *name = "osd");
+ void setDuration(int ms);
+ void setFont(QFont newfont);
+ void setShadow(bool shadow);
+ void setTextColor(const QColor &newcolor);
+ void setBackgroundColor(const QColor &newColor);
+ void setOffset( int x, int y );
+ void setAlignment(Alignment);
+ void setScreen(uint screen);
+ void setText(const QString &text) { m_currentText = text; refresh(); }
+
+ void unsetColors();
+
+ int screen() { return m_screen; }
+ int alignment() { return m_alignment; }
+ int y() { return m_y; }
+
+ signals:
+ void hidden();
+
+ public slots:
+ //TODO rename show, scrap removeOSD, just use hide() <- easier to learn
+ void showOSD(const QString&, bool preemptive=false );
+ void removeOSD() //inlined as is convenience function
+ {
+ hide();
+ }
+
+ protected slots:
+ void minReached();
+
+ protected:
+ /* render text into osdBuffer */
+ void renderOSDText(const QString &text);
+ void mousePressEvent( QMouseEvent* );
+ bool event(QEvent*);
+
+ void show();
+
+ /* call to reposition a new OSD text or when position attributes change */
+ void reposition( QSize newSize = QSize() );
+
+ /* called after most set*() calls to update the OSD */
+ void refresh();
+
+ enum KDesktopLockStatus { NotLocked=0, Locked=1, DCOPError=2 };
+ static KDesktopLockStatus isKDesktopLockRunning();
+
+ static const int MARGIN = 15;
+
+ QString m_appName;
+ int m_duration;
+ QTimer timer;
+ QTimer timerMin;
+ QPixmap osdBuffer;
+ QStringList textBuffer;
+ QString m_currentText;
+ bool m_shadow;
+
+ Alignment m_alignment;
+ int m_screen;
+ uint m_y;
+
+ bool m_dirty; //if dirty we will be re-rendered before we are shown
+};
+
+// do not pollute OSDWidget with this preview stuff
+class OSDPreviewWidget : public OSDWidget
+{
+ Q_OBJECT
+
+ public:
+ explicit OSDPreviewWidget( const QString &appName, QWidget *parent = 0, const char *name = "osdpreview" );
+
+ static QPoint m_previewOffset;
+
+ signals:
+ void positionChanged();
+
+ protected:
+ void mousePressEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+ void mouseMoveEvent( QMouseEvent * );
+
+ private:
+ bool m_dragging;
+ QPoint m_dragOffset;
+};
+#endif /*OSD_H*/
diff --git a/konversation/src/osd_preferences.cpp b/konversation/src/osd_preferences.cpp
new file mode 100644
index 0000000..49f223d
--- /dev/null
+++ b/konversation/src/osd_preferences.cpp
@@ -0,0 +1,190 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ivor Hewitt <ivor@ivor.org>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2006 Peter Simonsson <psn@linux.se>
+*/
+
+#include "osd_preferences.h"
+#include "config/preferences.h"
+#include "osd.h"
+#include "konversationapplication.h"
+
+#include <qgroupbox.h>
+#include <qspinbox.h>
+#include <kcombobox.h>
+#include <kcolorbutton.h>
+#include <qcheckbox.h>
+#include <kfontrequester.h>
+
+#include <kconfigdialog.h>
+
+
+OSD_Config::OSD_Config( QWidget* parent, const char* name, WFlags fl )
+ : OSD_ConfigUI( parent, name, fl )
+{
+ bool enableScreenChooser = false;
+ QRect screenRect;
+
+ for(int i = 0; i < QApplication::desktop()->numScreens(); ++i) {
+ kcfg_OSDScreen->insertItem(QString::number(i));
+ screenRect = QApplication::desktop()->screenGeometry(i);
+
+ //Check if we're using xinerama or not
+ if(screenRect.left() != 0 || screenRect.top() != 0) {
+ enableScreenChooser = true;
+ }
+ }
+
+ kcfg_OSDScreen->setEnabled(enableScreenChooser);
+
+ m_pOSDPreview = new OSDPreviewWidget("Konversation");
+ connect(m_pOSDPreview, SIGNAL(positionChanged()), this, SLOT(slotPositionChanged()));
+
+ connect( kcfg_OSDFont, SIGNAL(fontSelected(const QFont&)), this, SLOT(slotUpdateFont(const QFont&)));
+
+ slotOSDEnabledChanged(kcfg_UseOSD->isChecked());
+ slotCustomColorsChanged(kcfg_OSDUseCustomColors->isChecked());
+ slotScreenChanged(Preferences::oSDScreen());
+ slotDrawShadowChanged( kcfg_OSDDrawShadow->isChecked());
+ slotUpdateFont(Preferences::oSDFont());
+
+ kcfg_OSDOffsetX->hide();
+ kcfg_OSDOffsetY->hide();
+ kcfg_OSDAlignment->hide();
+
+ //Connect config page entries to control the OSDPreview
+ connect ( kcfg_UseOSD, SIGNAL( toggled( bool ) ), this, SLOT( slotOSDEnabledChanged(bool) ) );
+ connect ( kcfg_OSDUseCustomColors, SIGNAL(toggled(bool)), this, SLOT(slotCustomColorsChanged(bool)));
+ connect ( kcfg_OSDTextColor, SIGNAL(changed(const QColor&)), this, SLOT(slotTextColorChanged(const QColor&)));
+ connect ( kcfg_OSDBackgroundColor, SIGNAL(changed(const QColor&)), this, SLOT(slotBackgroundColorChanged(const QColor&)));
+ connect ( kcfg_OSDScreen, SIGNAL(activated(int)), this, SLOT(slotScreenChanged(int)));
+ connect ( kcfg_OSDDrawShadow, SIGNAL(toggled(bool)), this, SLOT(slotDrawShadowChanged(bool)));
+}
+
+OSD_Config::~OSD_Config()
+{
+ delete m_pOSDPreview;
+}
+
+void OSD_Config::loadSettings()
+{
+}
+
+void OSD_Config::restorePageToDefaults()
+{
+}
+
+void OSD_Config::saveSettings()
+{
+ //Update the current OSD.
+ KonversationApplication *konvApp=static_cast<KonversationApplication *>(KApplication::kApplication());
+
+ konvApp->osd->setEnabled(kcfg_UseOSD->isChecked());
+ if (kcfg_UseOSD->isChecked())
+ {
+ konvApp->osd->setFont(Preferences::oSDFont());
+ if(kcfg_OSDUseCustomColors->isChecked())
+ {
+ konvApp->osd->setTextColor(kcfg_OSDTextColor->color());
+ konvApp->osd->setBackgroundColor(kcfg_OSDBackgroundColor->color());
+ }
+ else
+ {
+ konvApp->osd->unsetColors();
+ }
+
+ konvApp->osd->setDuration(kcfg_OSDDuration->value());
+ konvApp->osd->setScreen(kcfg_OSDScreen->currentItem());
+ konvApp->osd->setShadow(kcfg_OSDDrawShadow->isChecked());
+
+ //x is ignored anyway, but leave incase we use in future
+ konvApp->osd->setOffset(kcfg_OSDOffsetX->value(), kcfg_OSDOffsetY->value());
+ konvApp->osd->setAlignment((OSDWidget::Alignment)kcfg_OSDAlignment->value());
+ }
+
+}
+
+void OSD_Config::showEvent(QShowEvent*)
+{
+ //Update the preview
+ m_pOSDPreview->setAlignment((OSDWidget::Alignment)( kcfg_OSDAlignment->value() ) );
+ m_pOSDPreview->setOffset(kcfg_OSDOffsetX->value(),kcfg_OSDOffsetY->value());
+
+ m_pOSDPreview->setShown(kcfg_UseOSD->isChecked());
+}
+
+void OSD_Config::hideEvent(QHideEvent*)
+{
+ m_pOSDPreview->setShown(false);
+}
+
+bool OSD_Config::hasChanged()
+{
+ // follow the interface, no Non-KConfigXT settings here, so none have changed
+ return false;
+}
+
+void OSD_Config::slotOSDEnabledChanged(bool on)
+{
+ if ( isVisible() )
+ m_pOSDPreview->setShown(on);
+}
+
+void OSD_Config::slotPositionChanged()
+{
+ kcfg_OSDScreen->setCurrentItem(m_pOSDPreview->screen());
+
+ kcfg_OSDAlignment->setValue( m_pOSDPreview->alignment() );
+ kcfg_OSDOffsetX->setValue( m_pOSDPreview->x());
+ kcfg_OSDOffsetY->setValue( m_pOSDPreview->y());
+}
+
+
+void OSD_Config::slotCustomColorsChanged(bool on)
+{
+ if(on)
+ {
+ m_pOSDPreview->setTextColor(kcfg_OSDTextColor->color());
+ m_pOSDPreview->setBackgroundColor(kcfg_OSDBackgroundColor->color());
+ }
+ else
+ m_pOSDPreview->unsetColors();
+}
+void OSD_Config::slotTextColorChanged(const QColor& color)
+{
+ if(kcfg_OSDUseCustomColors->isChecked())
+ m_pOSDPreview->setTextColor(color);
+}
+
+void OSD_Config::slotBackgroundColorChanged(const QColor& color)
+{
+ if(kcfg_OSDUseCustomColors->isChecked())
+ m_pOSDPreview->setBackgroundColor(color);
+}
+
+void OSD_Config::slotScreenChanged(int index)
+{
+ m_pOSDPreview->setScreen(index);
+}
+
+void OSD_Config::slotDrawShadowChanged(bool on)
+{
+ m_pOSDPreview->setShadow(on);
+}
+
+void OSD_Config::slotUpdateFont(const QFont& font)
+{
+ m_pOSDPreview->setFont(font);
+}
+
+#include "osd_preferences.moc"
+
diff --git a/konversation/src/osd_preferences.h b/konversation/src/osd_preferences.h
new file mode 100644
index 0000000..fe7c38e
--- /dev/null
+++ b/konversation/src/osd_preferences.h
@@ -0,0 +1,57 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ivor Hewitt <ivor@ivor.org>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2006 Peter Simonsson <psn@linux.se>
+*/
+
+#ifndef EXOSDPREFERENCES_H
+#define EXOSDPREFERENCES_H
+
+#include "osd_preferencesui.h"
+#include "konvisettingspage.h"
+
+
+class OSDPreviewWidget;
+
+class OSD_Config : public OSD_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit OSD_Config( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ ~OSD_Config();
+
+ virtual void restorePageToDefaults();
+ virtual void saveSettings();
+ virtual void loadSettings();
+
+ virtual bool hasChanged(); // implement the interface, will not be used here, though
+
+ protected slots:
+ void slotOSDEnabledChanged(bool on);
+ void slotCustomColorsChanged(bool on);
+ void slotTextColorChanged(const QColor& color);
+ void slotBackgroundColorChanged(const QColor& color);
+ void slotScreenChanged(int index);
+ void slotDrawShadowChanged(bool on);
+ void slotUpdateFont(const QFont& font);
+ void slotPositionChanged();
+
+ protected:
+ void showEvent(QShowEvent* event);
+ void hideEvent(QHideEvent* event);
+
+ private:
+ OSDPreviewWidget* m_pOSDPreview;
+};
+
+#endif // EXOSDPREFERENCES_H
diff --git a/konversation/src/osd_preferencesui.ui b/konversation/src/osd_preferencesui.ui
new file mode 100644
index 0000000..3d44f1d
--- /dev/null
+++ b/konversation/src/osd_preferencesui.ui
@@ -0,0 +1,424 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>OSD_ConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>OSD_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>444</width>
+ <height>442</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer row="5" column="0">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_UseOSD</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Enable On Screen Display</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>On &amp;Screen Display</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_OSDDrawShadow</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Draw shadows</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>OSD font:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kFontRequester1</cstring>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_OSDFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="2">
+ <property name="name">
+ <cstring>kcfg_OSDOffsetX</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>2048</number>
+ </property>
+ <property name="minValue">
+ <number>-2048</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="3">
+ <property name="name">
+ <cstring>kcfg_OSDOffsetY</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>2048</number>
+ </property>
+ <property name="minValue">
+ <number>-2048</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="4">
+ <property name="name">
+ <cstring>kcfg_OSDAlignment</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Show &amp;OSD Message</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_OSDShowOwnNick</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;If own nick appears in channel message</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_OSDShowChannel</cstring>
+ </property>
+ <property name="text">
+ <string>On an&amp;y channel message</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_OSDShowQuery</cstring>
+ </property>
+ <property name="text">
+ <string>On &amp;query activity</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_OSDShowChannelEvent</cstring>
+ </property>
+ <property name="text">
+ <string>On &amp;Join/Part events</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_OSDUseCustomColors</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Enable Custom Colors</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Te&amp;xt color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_OSDTextColor</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Background color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_OSDBackgroundColor</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_OSDBackgroundColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_OSDTextColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="1" column="2" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="4" column="0">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>O&amp;ther Settings</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Duration:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_OSDDuration</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;creen:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_OSDScreen</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_OSDDuration</cstring>
+ </property>
+ <property name="suffix">
+ <string> ms</string>
+ </property>
+ <property name="maxValue">
+ <number>10000</number>
+ </property>
+ <property name="minValue">
+ <number>500</number>
+ </property>
+ <property name="lineStep">
+ <number>100</number>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_OSDScreen</cstring>
+ </property>
+ </widget>
+ <spacer row="0" column="2" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_UseOSD</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_OSDDrawShadow</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseOSD</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox3</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseOSD</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_OSDUseCustomColors</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseOSD</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox4</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseOSD</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseOSD</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_OSDFont</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseOSD</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_UseOSD</tabstop>
+ <tabstop>kcfg_OSDDrawShadow</tabstop>
+ <tabstop>kcfg_OSDFont</tabstop>
+ <tabstop>kcfg_OSDOffsetX</tabstop>
+ <tabstop>kcfg_OSDOffsetY</tabstop>
+ <tabstop>kcfg_OSDAlignment</tabstop>
+ <tabstop>kcfg_OSDShowOwnNick</tabstop>
+ <tabstop>kcfg_OSDShowChannel</tabstop>
+ <tabstop>kcfg_OSDShowQuery</tabstop>
+ <tabstop>kcfg_OSDShowChannelEvent</tabstop>
+ <tabstop>kcfg_OSDUseCustomColors</tabstop>
+ <tabstop>kcfg_OSDTextColor</tabstop>
+ <tabstop>kcfg_OSDBackgroundColor</tabstop>
+ <tabstop>kcfg_OSDDuration</tabstop>
+ <tabstop>kcfg_OSDScreen</tabstop>
+ <tabstop>kcfg_OSDUseCustomColors</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/outputfilter.cpp b/konversation/src/outputfilter.cpp
new file mode 100644
index 0000000..40823e2
--- /dev/null
+++ b/konversation/src/outputfilter.cpp
@@ -0,0 +1,1790 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "outputfilter.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "awaymanager.h"
+#include "ignore.h"
+#include "server.h"
+#include "irccharsets.h"
+#include "linkaddressbook/addressbook.h"
+#include "query.h"
+
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qmap.h>
+#include <qvaluelist.h>
+#include <qtextcodec.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kio/passdlg.h>
+#include <kconfig.h>
+#include <kdeversion.h>
+#include <kshell.h>
+#include <ksocketaddress.h>
+#include <kresolver.h>
+#include <kreverseresolver.h>
+#include <kmessagebox.h>
+
+
+namespace Konversation
+{
+ OutputFilter::OutputFilter(Server* server)
+ : QObject(server)
+ {
+ m_server = server;
+ }
+
+ OutputFilter::~OutputFilter()
+ {
+ }
+
+ // replace all aliases in the string and return true if anything got replaced at all
+ bool OutputFilter::replaceAliases(QString& line)
+ {
+ QStringList aliasList=Preferences::aliasList();
+ QString cc(Preferences::commandChar());
+ // check if the line starts with a defined alias
+ for(unsigned int index=0;index<aliasList.count();index++)
+ {
+ // cut alias pattern from definition
+ QString aliasPattern(aliasList[index].section(' ',0,0));
+ // cut first word from command line, so we do not wrongly find an alias
+ // that starts with the same letters, like /m would override /me
+ QString lineStart=line.section(' ',0,0);
+
+ // pattern found?
+ // TODO: cc may be a regexp character here ... we should escape it then
+ if (lineStart==cc+aliasPattern)
+ {
+ QString aliasReplace;
+
+ // cut alias replacement from definition
+ if ( aliasList[index].contains("%p") )
+ aliasReplace = aliasList[index].section(' ',1);
+ else
+ aliasReplace = aliasList[index].section(' ',1 )+' '+line.section(' ',1 );
+
+ // protect "%%"
+ aliasReplace.replace("%%","%\x01");
+ // replace %p placeholder with rest of line
+ aliasReplace.replace("%p",line.section(' ',1));
+ // restore "%<1>" as "%%"
+ aliasReplace.replace("%\x01","%%");
+ // modify line
+ line=aliasReplace;
+ // return "replaced"
+ return true;
+ }
+ } // for
+
+ return false;
+ }
+
+ QStringList OutputFilter::splitForEncoding(const QString& inputLine, uint max)
+ {
+ uint sublen = 0; //The encoded length since the last split
+ int charLength = 0; //the length of this char
+ int lastBreakPoint = 0;
+
+ //FIXME should we run this through the encoder first, checking with "canEncode"?
+ QString text = inputLine; // the text we'll send, currently in Unicode
+ QStringList finals; // The strings we're going to output
+
+ QString channelCodecName=Preferences::channelEncoding(m_server->getDisplayName(), destination);
+ //Get the codec we're supposed to use. This must not fail. (not verified)
+ QTextCodec* codec;
+
+ // I copied this bit straight out of Server::send
+ if (channelCodecName.isEmpty())
+ {
+ codec = m_server->getIdentity()->getCodec();
+ }
+ else
+ {
+ codec = Konversation::IRCCharsets::self()->codecForName(channelCodecName);
+ }
+
+ Q_ASSERT(codec);
+ int index = 0;
+
+ while(text.length() > max)
+ {
+ // The most important bit - turn the current char into a QCString so we can measure it
+ QCString ch = codec->fromUnicode(QString(text[index]));
+ charLength = ch.length();
+
+ // If adding this char puts us over the limit:
+ if (charLength + sublen > max)
+ {
+ if(lastBreakPoint != 0)
+ {
+ finals.append(text.left(lastBreakPoint + 1));
+ text = text.mid(lastBreakPoint + 1);
+ }
+ else
+ {
+ finals.append(text.left(index));
+ text = text.mid(index);
+ }
+
+ lastBreakPoint = 0;
+ sublen = 0;
+ index = 0;
+ }
+ else if (text[index].isSpace() || text[index].isPunct())
+ {
+ lastBreakPoint = index;
+ }
+
+ ++index;
+ sublen += charLength;
+ }
+
+ if (!text.isEmpty())
+ {
+ finals.append(text);
+ }
+
+ return finals;
+ }
+
+ OutputFilterResult OutputFilter::parse(const QString& myNick,const QString& originalLine,const QString& name)
+ {
+ setCommandChar();
+
+ OutputFilterResult result;
+ destination=name;
+
+ QString inputLine(originalLine);
+
+ if(inputLine.isEmpty() || inputLine == "\n")
+ return result;
+
+ //Protect against nickserv auth being sent as a message on the off chance
+ // someone didn't notice leading spaces
+ {
+ QString testNickServ( inputLine.stripWhiteSpace() );
+ if(testNickServ.startsWith(commandChar+"nickserv", false)
+ || testNickServ.startsWith(commandChar+"ns", false))
+ {
+ inputLine = testNickServ;
+ }
+ }
+
+ if(!Preferences::disableExpansion())
+ {
+ // replace placeholders
+ inputLine.replace("%%","%\x01"); // make sure to protect double %%
+ inputLine.replace("%B","\x02"); // replace %B with bold char
+ inputLine.replace("%C","\x03"); // replace %C with color char
+ inputLine.replace("%G","\x07"); // replace %G with ASCII BEL 0x07
+ inputLine.replace("%I","\x09"); // replace %I with italics char
+ inputLine.replace("%O","\x0f"); // replace %O with reset to default char
+ inputLine.replace("%S","\x13"); // replace %S with strikethru char
+ // inputLine.replace(QRegExp("%?"),"\x15");
+ inputLine.replace("%R","\x16"); // replace %R with reverse char
+ inputLine.replace("%U","\x1f"); // replace %U with underline char
+ inputLine.replace("%\x01","%"); // restore double %% as single %
+ }
+
+ QString line=inputLine.lower();
+
+ // Convert double command chars at the beginning to single ones
+ if(line.startsWith(commandChar+commandChar) && !destination.isEmpty())
+ {
+ inputLine=inputLine.mid(1);
+ goto BYPASS_COMMAND_PARSING;
+ }
+ // Server command?
+ else if(line.startsWith(commandChar))
+ {
+ QString command = inputLine.section(' ', 0, 0).mid(1).lower();
+ QString parameter = inputLine.section(' ', 1);
+
+ if (command !="topic")
+ parameter = parameter.stripWhiteSpace();
+
+ if (command == "join") result = parseJoin(parameter);
+ else if(command == "part") result = parsePart(parameter);
+ else if(command == "leave") result = parsePart(parameter);
+ else if(command == "quit") result = parseQuit(parameter);
+ else if(command == "close") result = parseClose(parameter);
+ else if(command == "notice") result = parseNotice(parameter);
+ else if(command == "j") result = parseJoin(parameter);
+ else if(command == "me") result = parseMe(parameter, destination);
+ else if(command == "msg") result = parseMsg(myNick,parameter, false);
+ else if(command == "m") result = parseMsg(myNick,parameter, false);
+ else if(command == "smsg") result = parseSMsg(parameter);
+ else if(command == "query") result = parseMsg(myNick,parameter, true);
+ else if(command == "op") result = parseOp(parameter);
+ else if(command == "deop") result = parseDeop(myNick,parameter);
+ else if(command == "hop") result = parseHop(parameter);
+ else if(command == "dehop") result = parseDehop(myNick,parameter);
+ else if(command == "voice") result = parseVoice(parameter);
+ else if(command == "unvoice") result = parseUnvoice(myNick,parameter);
+ else if(command == "devoice") result = parseUnvoice(myNick,parameter);
+ else if(command == "ctcp") result = parseCtcp(parameter);
+ else if(command == "ping") result = parseCtcp(parameter.section(' ', 0, 0) + " ping");
+ else if(command == "kick") result = parseKick(parameter);
+ else if(command == "topic") result = parseTopic(parameter);
+ else if(command == "away") parseAway(parameter);
+ else if(command == "unaway") parseBack();
+ else if(command == "back") parseBack();
+ else if(command == "invite") result = parseInvite(parameter);
+ else if(command == "exec") result = parseExec(parameter);
+ else if(command == "notify") result = parseNotify(parameter);
+ else if(command == "oper") result = parseOper(myNick,parameter);
+ else if(command == "ban") result = parseBan(parameter);
+ else if(command == "unban") result = parseUnban(parameter);
+ else if(command == "kickban") result = parseBan(parameter,true);
+ else if(command == "ignore") result = parseIgnore(parameter);
+ else if(command == "unignore") result = parseUnignore(parameter);
+ else if(command == "quote") result = parseQuote(parameter);
+ else if(command == "say") result = parseSay(parameter);
+ else if(command == "list") result = parseList(parameter);
+ else if(command == "names") result = parseNames(parameter);
+ else if(command == "raw") result = parseRaw(parameter);
+ else if(command == "dcc") result = parseDcc(parameter);
+ else if(command == "konsole") parseKonsole();
+ else if(command == "aaway") KonversationApplication::instance()->getAwayManager()->requestAllAway(parameter);
+ else if(command == "aunaway") KonversationApplication::instance()->getAwayManager()->requestAllUnaway();
+ else if(command == "aback") KonversationApplication::instance()->getAwayManager()->requestAllUnaway();
+ else if(command == "ame") result = parseAme(parameter);
+ else if(command == "amsg") result = parseAmsg(parameter);
+ else if(command == "omsg") result = parseOmsg(parameter);
+ else if(command == "onotice") result = parseOnotice(parameter);
+ else if(command == "server") parseServer(parameter);
+ else if(command == "reconnect") emit reconnectServer();
+ else if(command == "disconnect") emit disconnectServer();
+ else if(command == "charset") result = parseCharset(parameter);
+ else if(command == "encoding") result = parseCharset(parameter);
+ else if(command == "setkey") result = parseSetKey(parameter);
+ else if(command == "delkey") result = parseDelKey(parameter);
+ else if(command == "showkey") result = parseShowKey(parameter);
+ else if(command == "dns") result = parseDNS(parameter);
+ else if(command == "kill") result = parseKill(parameter);
+ else if(command == "queuetuner") result = parseShowTuner(parameter);
+
+ // Forward unknown commands to server
+ else
+ {
+ result.toServer = inputLine.mid(1);
+ result.type = Message;
+ }
+ }
+ // Ordinary message to channel/query?
+ else if(!destination.isEmpty())
+ {
+ BYPASS_COMMAND_PARSING:
+
+ QStringList outputList=splitForEncoding(inputLine, m_server->getPreLength("PRIVMSG", destination));
+ if (outputList.count() > 1)
+ {
+ result.output=QString();
+ result.outputList=outputList;
+ for ( QStringList::Iterator it = outputList.begin(); it != outputList.end(); ++it )
+ {
+ result.toServerList += "PRIVMSG " + destination + " :" + *it;
+ }
+ }
+ else
+ {
+ result.output = inputLine;
+ result.toServer = "PRIVMSG " + destination + " :" + inputLine;
+ }
+
+ result.type = Message;
+ }
+ // Eveything else goes to the server unchanged
+ else
+ {
+ result.toServer = inputLine;
+ result.output = inputLine;
+ result.typeString = i18n("Raw");
+ result.type = Program;
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseShowTuner(const QString &parameter)
+ {
+ KonversationApplication *konvApp = static_cast<KonversationApplication*>(KApplication::kApplication());
+ OutputFilterResult result;
+
+ if(parameter.isEmpty() || parameter == "on")
+ konvApp->showQueueTuner(true);
+ else if(parameter == "off")
+ konvApp->showQueueTuner(false);
+ else
+ result = usage(i18n("Usage: %1queuetuner [on | off]").arg(commandChar));
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseOp(const QString &parameter)
+ {
+ return changeMode(parameter,'o','+');
+ }
+
+ OutputFilterResult OutputFilter::parseDeop(const QString &ownNick, const QString &parameter)
+ {
+ return changeMode(addNickToEmptyNickList(ownNick,parameter),'o','-');
+ }
+
+ OutputFilterResult OutputFilter::parseHop(const QString &parameter)
+ {
+ return changeMode(parameter, 'h', '+');
+ }
+
+ OutputFilterResult OutputFilter::parseDehop(const QString &ownNick, const QString &parameter)
+ {
+ return changeMode(addNickToEmptyNickList(ownNick,parameter), 'h', '-');
+ }
+
+ OutputFilterResult OutputFilter::parseVoice(const QString &parameter)
+ {
+ return changeMode(parameter,'v','+');
+ }
+
+ OutputFilterResult OutputFilter::parseUnvoice(const QString &ownNick, const QString &parameter)
+ {
+ return changeMode(addNickToEmptyNickList(ownNick,parameter),'v','-');
+ }
+
+ OutputFilterResult OutputFilter::parseJoin(QString& channelName)
+ {
+ OutputFilterResult result;
+
+ if(channelName.contains(",")) // Protect against #foo,0 tricks
+ channelName = channelName.remove(",0");
+ //else if(channelName == "0") // FIXME IRC RFC 2812 section 3.2.1
+
+ if (channelName.isEmpty())
+ {
+ if (destination.isEmpty() || !isAChannel(destination))
+ return usage(i18n("Usage: %1JOIN <channel> [password]").arg(commandChar));
+ channelName=destination;
+ }
+ else if (!isAChannel(channelName))
+ channelName = "#" + channelName.stripWhiteSpace();
+
+ Channel* channel = m_server->getChannelByName(channelName);
+
+ if (channel)
+ {
+ // Note that this relies on the channels-flush-nicklists-on-disconnect behavior.
+ if (!channel->numberOfNicks())
+ result.toServer = "JOIN " + channelName;
+
+ if (channel->joined()) emit showView (channel);
+ }
+ else
+ result.toServer = "JOIN " + channelName;
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseKick(const QString &parameter)
+ {
+ OutputFilterResult result;
+
+ if(isAChannel(destination))
+ {
+ // get nick to kick
+ QString victim = parameter.left(parameter.find(" "));
+
+ if(victim.isEmpty())
+ {
+ result = usage(i18n("Usage: %1KICK <nick> [reason]").arg(commandChar));
+ }
+ else
+ {
+ // get kick reason (if any)
+ QString reason = parameter.mid(victim.length() + 1);
+
+ // if no reason given, take default reason
+ if(reason.isEmpty())
+ {
+ reason = m_server->getIdentity()->getKickReason();
+ }
+
+ result.toServer = "KICK " + destination + ' ' + victim + " :" + reason;
+ }
+ }
+ else
+ {
+ result = error(i18n("%1KICK only works from within channels.").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parsePart(const QString &parameter)
+ {
+ OutputFilterResult result;
+
+ // No parameter, try default part message
+ if(parameter.isEmpty())
+ {
+ // But only if we actually are in a channel
+ if(isAChannel(destination))
+ {
+ result.toServer = "PART " + destination + " :" + m_server->getIdentity()->getPartReason();
+ }
+ else
+ {
+ result = error(i18n("%1PART without parameters only works from within a channel or a query.").arg(commandChar));
+ }
+ }
+ else
+ {
+ // part a given channel
+ if(isAChannel(parameter))
+ {
+ // get channel name
+ QString channel = parameter.left(parameter.find(" "));
+ // get part reason (if any)
+ QString reason = parameter.mid(channel.length() + 1);
+
+ // if no reason given, take default reason
+ if(reason.isEmpty())
+ {
+ reason = m_server->getIdentity()->getPartReason();
+ }
+
+ result.toServer = "PART " + channel + " :" + reason;
+ }
+ // part this channel with a given reason
+ else
+ {
+ if(isAChannel(destination))
+ {
+ result.toServer = "PART " + destination + " :" + parameter;
+ }
+ else
+ {
+ result = error(i18n("%1PART without channel name only works from within a channel.").arg(commandChar));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseTopic(const QString &parameter)
+ {
+ OutputFilterResult result;
+
+ // No parameter, try to get current topic
+ if(parameter.isEmpty())
+ {
+ // But only if we actually are in a channel
+ if(isAChannel(destination))
+ {
+ result.toServer = "TOPIC " + destination;
+ }
+ else
+ {
+ result = error(i18n("%1TOPIC without parameters only works from within a channel.").arg(commandChar));
+ }
+ }
+ else
+ {
+ // retrieve or set topic of a given channel
+ if(isAChannel(parameter))
+ {
+ // get channel name
+ QString channel=parameter.left(parameter.find(" "));
+ // get topic (if any)
+ QString topic=parameter.mid(channel.length()+1);
+ // if no topic given, retrieve topic
+ if(topic.isEmpty())
+ {
+ m_server->requestTopic(channel);
+ }
+ // otherwise set topic there
+ else
+ {
+ result.toServer = "TOPIC " + channel + " :";
+ //If we get passed a ^A as a topic its a sign we should clear the topic.
+ //Used to be a \n, but those get smashed by QStringList::split and readded later
+ //Now is not the time to fight with that. FIXME
+ //If anyone out there *can* actually set the topic to a single ^A, now they have to
+ //specify it twice to get one.
+ if (topic =="\x01\x01")
+ result.toServer += '\x01';
+ else if (topic!="\x01")
+ result.toServer += topic;
+
+ }
+ }
+ // set this channel's topic
+ else
+ {
+ if(isAChannel(destination))
+ {
+ result.toServer = "TOPIC " + destination + " :" + parameter;
+ }
+ else
+ {
+ result = error(i18n("%1TOPIC without channel name only works from within a channel.").arg(commandChar));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ void OutputFilter::parseAway(QString& reason)
+ {
+ if (reason.isEmpty() && m_server->isAway())
+ m_server->requestUnaway();
+ else
+ m_server->requestAway(reason);
+ }
+
+ void OutputFilter::parseBack()
+ {
+ m_server->requestUnaway();
+ }
+
+ OutputFilterResult OutputFilter::parseNames(const QString &parameter)
+ {
+ OutputFilterResult result;
+ result.toServer = "NAMES ";
+ if (parameter.isNull())
+ {
+ return error(i18n("%1NAMES with no target may disconnect you from the server. Specify '*' if you really want this.").arg(commandChar));
+ }
+ else if (parameter != QChar('*'))
+ {
+ result.toServer.append(parameter);
+ }
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseClose(QString parm)
+ {
+ if (parm.isEmpty())
+ parm=destination;
+
+ if (isAChannel(parm) && m_server->getChannelByName(parm))
+ m_server->getChannelByName(parm)->closeYourself(false);
+ else if (m_server->getQueryByName(parm))
+ m_server->getQueryByName(parm)->closeYourself(false);
+ else if (parm.isEmpty()) // this can only mean one thing.. we're in the Server tab
+ m_server->closeYourself(false);
+ else
+ return usage(i18n("Usage: %1close [window] closes the named channel or query tab, or the current tab if none specified.").arg(commandChar));
+ return OutputFilterResult();
+ }
+
+ OutputFilterResult OutputFilter::parseQuit(const QString &reason)
+ {
+ OutputFilterResult result;
+
+ result.toServer = "QUIT :";
+ // if no reason given, take default reason
+ if(reason.isEmpty())
+ result.toServer += m_server->getIdentity()->getQuitReason();
+ else
+ result.toServer += reason;
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseNotice(const QString &parameter)
+ {
+ OutputFilterResult result;
+ QString recipient = parameter.left(parameter.find(" "));
+ QString message = parameter.mid(recipient.length()+1);
+
+ if(parameter.isEmpty() || message.isEmpty())
+ {
+ result = usage(i18n("Usage: %1NOTICE <recipient> <message>").arg(commandChar));
+ }
+ else
+ {
+ result.typeString = i18n("Notice");
+ result.toServer = "NOTICE " + recipient + " :" + message;
+ result.output=i18n("%1 is the message, %2 the recipient nickname","Sending notice \"%2\" to %1.").arg(recipient).arg(message);
+ result.type = Program;
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseMe(const QString &parameter, const QString &destination)
+ {
+ OutputFilterResult result;
+
+ if (!destination.isEmpty() && !parameter.isEmpty())
+ {
+ result.toServer = "PRIVMSG " + destination + " :" + '\x01' + "ACTION " + parameter + '\x01';
+ result.output = parameter;
+ result.type = Action;
+ }
+ else
+ {
+ result = usage(i18n("Usage: %1ME text").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseMsg(const QString &myNick, const QString &parameter, bool isQuery)
+ {
+ OutputFilterResult result;
+ QString recipient = parameter.section(" ", 0, 0, QString::SectionSkipEmpty);
+ QString message = parameter.section(" ", 1);
+ QString output;
+
+ if (recipient.isEmpty())
+ {
+ result = error("Error: You need to specify a recipient.");
+ return result;
+ }
+
+ if (isQuery && m_server->isAChannel(recipient))
+ {
+ result = error("Error: You cannot open queries to channels.");
+ return result;
+ }
+
+ if (message.stripWhiteSpace().isEmpty())
+ {
+ //empty result - we don't want to send any message to the server
+ if (!isQuery)
+ {
+ result = error("Error: You need to specify a message.");
+ return result;
+ }
+ }
+ else if (message.startsWith(commandChar+"me"))
+ {
+ result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01';
+ output = QString("* %1 %2").arg(myNick).arg(message.mid(4));
+ }
+ else
+ {
+ result.toServer = "PRIVMSG " + recipient + " :" + message;
+ output = message;
+ }
+
+ ::Query *query;
+
+ if (isQuery || output.isEmpty())
+ {
+ //if this is a /query, always open a query window.
+ //treat "/msg nick" as "/query nick"
+
+ //Note we have to be a bit careful here.
+ //We only want to create ('obtain') a new nickinfo if we have done /query
+ //or "/msg nick". Not "/msg nick message".
+ NickInfoPtr nickInfo = m_server->obtainNickInfo(recipient);
+ query = m_server->addQuery(nickInfo, true /*we initiated*/);
+ //force focus if the user did not specify any message
+ if (output.isEmpty()) emit showView(query);
+ }
+ else
+ {
+ //We have "/msg nick message"
+ query = m_server->getQueryByName(recipient);
+ }
+
+ if (query && !output.isEmpty())
+ {
+ if (message.startsWith(commandChar+"me"))
+ //log if and only if the query open
+ query->appendAction(m_server->getNickname(), message.mid(4));
+ else
+ //log if and only if the query open
+ query->appendQuery(m_server->getNickname(), output);
+ }
+
+ if (output.isEmpty()) return result; //result should be completely empty;
+ //FIXME - don't do below line if query is focused
+ result.output = output;
+ result.typeString= recipient;
+ result.type = PrivateMessage;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseSMsg(const QString &parameter)
+ {
+ OutputFilterResult result;
+ QString recipient = parameter.left(parameter.find(" "));
+ QString message = parameter.mid(recipient.length() + 1);
+
+ if(message.startsWith(commandChar + "me"))
+ {
+ result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01';
+ }
+ else
+ {
+ result.toServer = "PRIVMSG " + recipient + " :" + message;
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseCtcp(const QString &parameter)
+ {
+ OutputFilterResult result;
+ // who is the recipient?
+ QString recipient = parameter.section(' ', 0, 0);
+ // what is the first word of the ctcp?
+ QString request = parameter.section(' ', 1, 1, QString::SectionSkipEmpty).upper();
+ // what is the complete ctcp command?
+ QString message = parameter.section(' ', 2, 0xffffff, QString::SectionSkipEmpty);
+
+ QString out = request;
+ if (!message.isEmpty())
+ out+= ' ' + message;
+
+ if (request == "PING")
+ {
+ unsigned int time_t = QDateTime::currentDateTime().toTime_t();
+ result.toServer = QString("PRIVMSG %1 :\x01PING %2\x01").arg(recipient).arg(time_t);
+ result.output = i18n("Sending CTCP-%1 request to %2.").arg("PING").arg(recipient);
+ }
+ else
+ {
+ result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + out + '\x01';
+ result.output = i18n("Sending CTCP-%1 request to %2.").arg(out).arg(recipient);
+ }
+
+ result.typeString = i18n("CTCP");
+ result.type = Program;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::changeMode(const QString &parameter,char mode,char giveTake)
+ {
+ OutputFilterResult result;
+ // TODO: Make sure this works with +l <limit> and +k <password> also!
+ QString token;
+ QString tmpToken;
+ QStringList nickList = QStringList::split(' ', parameter);
+
+ if(nickList.count())
+ {
+ // Check if the user specified a channel
+ if(isAChannel(nickList[0]))
+ {
+ token = "MODE " + nickList[0];
+ // remove the first element
+ nickList.remove(nickList.begin());
+ }
+ // Add default destination if it is a channel
+ else if(isAChannel(destination))
+ {
+ token = "MODE " + destination;
+ }
+
+ // Only continue if there was no error
+ if(token.length())
+ {
+ unsigned int modeCount = nickList.count();
+ QString modes;
+ modes.fill(mode, modeCount);
+
+ token += QString(" ") + QChar(giveTake) + modes;
+ tmpToken = token;
+
+ for(unsigned int index = 0; index < modeCount; index++)
+ {
+ if((index % 3) == 0)
+ {
+ result.toServerList.append(token);
+ token = tmpToken;
+ }
+ token += ' ' + nickList[index];
+ }
+
+ if(token != tmpToken)
+ {
+ result.toServerList.append(token);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseDcc(const QString &parameter)
+ {
+ OutputFilterResult result;
+
+ // No parameter, just open DCC panel
+ if(parameter.isEmpty())
+ {
+ emit addDccPanel();
+ }
+ else
+ {
+ QString tmpParameter = parameter;
+ QStringList parameterList = QStringList::split(' ', tmpParameter.replace("\\ ", "%20"));
+
+ QString dccType = parameterList[0].lower();
+
+ if(dccType=="close")
+ {
+ emit closeDccPanel();
+ }
+ else if(dccType=="send")
+ {
+ if(parameterList.count()==1) // DCC SEND
+ {
+ emit requestDccSend();
+ } // DCC SEND <nickname>
+ else if(parameterList.count()==2)
+ {
+ emit requestDccSend(parameterList[1]);
+ } // DCC SEND <nickname> <file> [file] ...
+ else if(parameterList.count()>2)
+ {
+ // TODO: make sure this will work:
+ //output=i18n("Usage: %1DCC SEND nickname [fi6lename] [filename] ...").arg(commandChar);
+ KURL fileURL(parameterList[2]);
+
+ //We could easily check if the remote file exists, but then we might
+ //end up asking for creditionals twice, so settle for only checking locally
+ if(!fileURL.isLocalFile() || QFile::exists( fileURL.path() ))
+ {
+ emit openDccSend(parameterList[1],fileURL);
+ }
+ else
+ {
+ result = error(i18n("File \"%1\" does not exist.").arg(parameterList[2]));
+ }
+ }
+ else // Don't know how this should happen, but ...
+ {
+ result = usage(i18n("Usage: %1DCC [SEND nickname filename]").arg(commandChar));
+ }
+ }
+ // TODO: DCC Chat etc. comes here
+ else if(dccType=="chat")
+ {
+ if(parameterList.count()==2)
+ {
+ emit openDccChat(parameterList[1]);
+ }
+ else
+ {
+ result = usage(i18n("Usage: %1DCC [CHAT nickname]").arg(commandChar));
+ }
+ }
+ else
+ {
+ result = error(i18n("Unrecognized command %1DCC %2. Possible commands are SEND, CHAT, CLOSE.").arg(commandChar).arg(parameterList[0]));
+ }
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::sendRequest(const QString &recipient,const QString &fileName,const QString &address,const QString &port,unsigned long size)
+ {
+ OutputFilterResult result;
+ QString niftyFileName(fileName);
+ /*QFile file(fileName);
+ QFileInfo info(file);*/
+
+ result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND "
+ + fileName
+ + ' ' + address + ' ' + port + ' ' + QString::number(size) + '\x01';
+
+ // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
+ if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
+ niftyFileName = fileName.mid(1, fileName.length()-2);
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::passiveSendRequest(const QString& recipient,const QString &fileName,const QString &address,unsigned long size,const QString &token)
+ {
+ OutputFilterResult result;
+ QString niftyFileName(fileName);
+
+ result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND "
+ + fileName
+ + ' ' + address + " 0 " + QString::number(size) + ' ' + token + '\x01';
+
+ // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
+ if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
+ niftyFileName = fileName.mid(1, fileName.length()-2);
+
+ return result;
+ }
+
+ // Accepting Resume Request
+ OutputFilterResult OutputFilter::acceptResumeRequest(const QString &recipient,const QString &fileName,const QString &port,int startAt)
+ {
+ QString niftyFileName(fileName);
+
+ OutputFilterResult result;
+ result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC ACCEPT " + fileName + ' ' + port
+ + ' ' + QString::number(startAt) + '\x01';
+
+ // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
+ if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
+ niftyFileName = fileName.mid(1, fileName.length()-2);
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::resumeRequest(const QString &sender,const QString &fileName,const QString &port,KIO::filesize_t startAt)
+ {
+ QString niftyFileName(fileName);
+
+ OutputFilterResult result;
+ /*QString newFileName(fileName);
+ newFileName.replace(" ", "_");*/
+ result.toServer = "PRIVMSG " + sender + " :" + '\x01' + "DCC RESUME " + fileName + ' ' + port + ' '
+ + QString::number(startAt) + '\x01';
+
+ // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
+ if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
+ niftyFileName = fileName.mid(1, fileName.length()-2);
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::acceptPassiveSendRequest(const QString& recipient,const QString &fileName,const QString &address,const QString &port,unsigned long size,const QString &token)
+ {
+ OutputFilterResult result;
+ QString niftyFileName(fileName);
+
+ // "DCC SEND" to receive a file sounds weird, but it's ok.
+ result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND "
+ + fileName
+ + ' ' + address + ' ' + port + ' ' + QString::number(size) + ' ' + token + '\x01';
+
+ // Dirty hack to avoid printing ""name with spaces.ext"" instead of "name with spaces.ext"
+ if ((fileName.startsWith("\"")) && (fileName.endsWith("\"")))
+ niftyFileName = fileName.mid(1, fileName.length()-2);
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseInvite(const QString &parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1INVITE <nick> [channel]").arg(commandChar));
+ }
+ else
+ {
+ QString nick = parameter.section(' ', 0, 0, QString::SectionSkipEmpty);
+ QString channel = parameter.section(' ', 1, 1, QString::SectionSkipEmpty);
+
+ if(channel.isEmpty())
+ {
+ if(isAChannel(destination))
+ {
+ channel = destination;
+ }
+ else
+ {
+ result = error(i18n("%1INVITE without channel name works only from within channels.").arg(commandChar));
+ }
+ }
+
+ if(!channel.isEmpty())
+ {
+ if(isAChannel(channel))
+ {
+ result.toServer = "INVITE " + nick + ' ' + channel;
+ }
+ else
+ {
+ result = error(i18n("%1 is not a channel.").arg(channel));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseExec(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1EXEC <script> [parameter list]").arg(commandChar));
+ }
+ else
+ {
+ QStringList parameterList = QStringList::split(' ', parameter);
+
+ if(parameterList[0].find("../") == -1)
+ {
+ emit launchScript(destination, parameter);
+ }
+ else
+ {
+ result = error(i18n("Script name may not contain \"../\"!"));
+ }
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseRaw(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty() || parameter == "open")
+ {
+ emit openRawLog(true);
+ }
+ else if(parameter == "close")
+ {
+ emit closeRawLog();
+ }
+ else
+ {
+ result = usage(i18n("Usage: %1RAW [OPEN | CLOSE]").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseNotify(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ QString groupName = m_server->getDisplayName();
+
+ int serverGroupId = -1;
+
+ if (m_server->getServerGroup())
+ serverGroupId = m_server->getServerGroup()->id();
+
+ if (!parameter.isEmpty() && serverGroupId != -1)
+ {
+ QStringList list = QStringList::split(' ', parameter);
+
+ for(unsigned int index = 0; index < list.count(); index++)
+ {
+ // Try to remove current pattern
+ if(!Preferences::removeNotify(groupName, list[index]))
+ {
+ // If remove failed, try to add it instead
+ if(!Preferences::addNotify(serverGroupId, list[index]))
+ {
+ kdDebug() << "OutputFilter::parseNotify(): Adding failed!" << endl;
+ }
+ }
+ } // endfor
+ }
+
+ // show (new) notify list to user
+ QString list = Preferences::notifyStringByGroupName(groupName) + ' ' + Konversation::Addressbook::self()->allContactsNicksForServer(m_server->getServerName(), m_server->getDisplayName()).join(" ");
+
+ result.typeString = i18n("Notify");
+
+ if(list.isEmpty())
+ result.output = i18n("Current notify list is empty.");
+ else
+ result.output = i18n("Current notify list: %1").arg(list);
+
+ result.type = Program;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseOper(const QString& myNick,const QString& parameter)
+ {
+ OutputFilterResult result;
+ QStringList parameterList = QStringList::split(' ', parameter);
+
+ if(parameter.isEmpty() || parameterList.count() == 1)
+ {
+ QString nick((parameterList.count() == 1) ? parameterList[0] : myNick);
+ QString password;
+ bool keep = false;
+
+ int ret = KIO::PasswordDialog::getNameAndPassword
+ (
+ nick,
+ password,
+ &keep,
+ i18n("Enter user name and password for IRC operator privileges:"),
+ false,
+ i18n("IRC Operator Password")
+ );
+
+ if(ret == KIO::PasswordDialog::Accepted)
+ {
+ result.toServer = "OPER " + nick + ' ' + password;
+ }
+ }
+ else
+ {
+ result.toServer = "OPER " + parameter;
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseBan(const QString& parameter, bool kick)
+ {
+ OutputFilterResult result;
+ // assume incorrect syntax first
+ bool showUsage = true;
+
+ if(!parameter.isEmpty())
+ {
+ QStringList parameterList=QStringList::split(' ',parameter);
+ QString channel;
+ QString option;
+ // check for option
+ QString lowerParameter = parameterList[0].lower();
+ bool host = (lowerParameter == "-host");
+ bool domain = (lowerParameter == "-domain");
+ bool uhost = (lowerParameter == "-userhost");
+ bool udomain = (lowerParameter == "-userdomain");
+
+ // remove possible option
+ if (host || domain || uhost || udomain)
+ {
+ option = parameterList[0].mid(1);
+ parameterList.pop_front();
+ }
+
+ // look for channel / ban mask
+ if (parameterList.count())
+ {
+ // user specified channel
+ if (isAChannel(parameterList[0]))
+ {
+ channel = parameterList[0];
+ parameterList.pop_front();
+ }
+ // no channel, so assume current destination as channel
+ else if (isAChannel(destination))
+ channel = destination;
+ else
+ {
+ // destination is no channel => error
+ if (!kick)
+ result = error(i18n("%1BAN without channel name works only from inside a channel.").arg(commandChar));
+ else
+ result = error(i18n("%1KICKBAN without channel name works only from inside a channel.").arg(commandChar));
+
+ // no usage information after error
+ showUsage = false;
+ }
+ // signal server to ban this user if all went fine
+ if (!channel.isEmpty())
+ {
+ if (kick)
+ {
+ QString victim = parameterList[0];
+ parameterList.pop_front();
+
+ QString reason = parameterList.join(" ");
+
+ result.toServer = "KICK " + channel + ' ' + victim + " :" + reason;
+
+ emit banUsers(QStringList(victim),channel,option);
+ }
+ else
+ {
+ emit banUsers(parameterList,channel,option);
+ }
+
+ // syntax was correct, so reset flag
+ showUsage = false;
+ }
+ }
+ }
+
+ if (showUsage)
+ {
+ if (!kick)
+ result = usage(i18n("Usage: %1BAN [-HOST | -DOMAIN | -USERHOST | -USERDOMAIN] [channel] <user|mask>").arg(commandChar));
+ else
+ result = usage(i18n("Usage: %1KICKBAN [-HOST | -DOMAIN | -USERHOST | -USERDOMAIN] [channel] <user|mask> [reason]").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ // finally set the ban
+ OutputFilterResult OutputFilter::execBan(const QString& mask,const QString& channel)
+ {
+ OutputFilterResult result;
+ result.toServer = "MODE " + channel + " +b " + mask;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseUnban(const QString& parameter)
+ {
+ OutputFilterResult result;
+ // assume incorrect syntax first
+ bool showUsage=true;
+
+ if(!parameter.isEmpty())
+ {
+ QStringList parameterList = QStringList::split(' ', parameter);
+ QString channel;
+ QString mask;
+
+ // if the user specified a channel
+ if(isAChannel(parameterList[0]))
+ {
+ // get channel
+ channel = parameterList[0];
+ // remove channel from parameter list
+ parameterList.pop_front();
+ }
+ // otherwise the current destination must be a channel
+ else if(isAChannel(destination))
+ channel = destination;
+ else
+ {
+ // destination is no channel => error
+ result = error(i18n("%1UNBAN without channel name works only from inside a channel.").arg(commandChar));
+ // no usage information after error
+ showUsage = false;
+ }
+ // if all went good, signal server to unban this mask
+ if(!channel.isEmpty())
+ {
+ emit unbanUsers(parameterList[0], channel);
+ // syntax was correct, so reset flag
+ showUsage = false;
+ }
+ }
+
+ if(showUsage)
+ {
+ result = usage(i18n("Usage: %1UNBAN [channel] pattern").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::execUnban(const QString& mask,const QString& channel)
+ {
+ OutputFilterResult result;
+ result.toServer = "MODE " + channel + " -b " + mask;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseIgnore(const QString& parameter)
+ {
+ OutputFilterResult result;
+ // assume incorrect syntax first
+ bool showUsage = true;
+
+ // did the user give parameters at all?
+ if(!parameter.isEmpty())
+ {
+ QStringList parameterList = QStringList::split(' ', parameter);
+
+ // if nothing else said, only ignore channels and queries
+ int value = Ignore::Channel | Ignore::Query;
+
+ // user specified -all option
+ if(parameterList[0].lower() == "-all")
+ {
+ // ignore everything
+ value = Ignore::All;
+ parameterList.pop_front();
+ }
+
+ // were there enough parameters?
+ if(parameterList.count() >= 1)
+ {
+ for(unsigned int index=0;index<parameterList.count();index++)
+ {
+ if(!parameterList[index].contains('!'))
+ {
+ parameterList[index] += "!*";
+ }
+
+ Preferences::addIgnore(parameterList[index] + ',' + QString::number(value));
+ }
+
+ result.output = i18n("Added %1 to your ignore list.").arg(parameterList.join(", "));
+ result.typeString = i18n("Ignore");
+ result.type = Program;
+
+ // all went fine, so show no error message
+ showUsage = false;
+ }
+ }
+
+ if(showUsage)
+ {
+ result = usage(i18n("Usage: %1IGNORE [ -ALL ] <user 1> <user 2> ... <user n>").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseUnignore(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1UNIGNORE <user 1> <user 2> ... <user n>").arg(commandChar));
+ }
+ else
+ {
+ QString unignore = parameter.simplifyWhiteSpace();
+ QStringList unignoreList = QStringList::split(' ',unignore);
+
+ QStringList succeeded;
+ QStringList failed;
+
+ // Iterate over potential unignores
+ for (QStringList::Iterator it = unignoreList.begin(); it != unignoreList.end(); ++it)
+ {
+ // If pattern looks incomplete, try to complete it
+ if (!(*it).contains('!'))
+ {
+ QString fixedPattern = (*it);
+ fixedPattern += "!*";
+
+ bool success = false;
+
+ // Try to remove completed pattern
+ if (Preferences::removeIgnore(fixedPattern))
+ {
+ succeeded.append(fixedPattern);
+ success = true;
+ }
+
+ // Try to remove the incomplete version too, in case it was added via the GUI ...
+ // FIXME: Validate patterns in GUI?
+ if (Preferences::removeIgnore((*it)))
+ {
+ succeeded.append((*it));
+ success = true;
+ }
+
+ if (!success)
+ failed.append((*it) + "[!*]");
+ }
+ // Try to remove seemingly complete pattern
+ else if (Preferences::removeIgnore((*it)))
+ succeeded.append((*it));
+ // Failed to remove given complete pattern
+ else
+ failed.append((*it));
+ }
+
+ // Print all successful unignores, in case there were any
+ if (succeeded.count()>=1)
+ {
+ m_server->appendMessageToFrontmost(i18n("Ignore"),i18n("Removed %1 from your ignore list.").arg(succeeded.join(", ")));
+ }
+
+ // One failed unignore
+ if (failed.count()==1)
+ {
+ m_server->appendMessageToFrontmost(i18n("Error"),i18n("No such ignore: %1").arg(failed.join(", ")));
+ }
+
+ // Multiple failed unignores
+ if (failed.count()>1)
+ {
+ m_server->appendMessageToFrontmost(i18n("Error"),i18n("No such ignores: %1").arg(failed.join(", ")));
+ }
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseQuote(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1QUOTE command list").arg(commandChar));
+ }
+ else
+ {
+ result.toServer = parameter;
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseSay(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1SAY text").arg(commandChar));
+ }
+ else
+ {
+ result.toServer = "PRIVMSG " + destination + " :" + parameter;
+ result.output = parameter;
+ }
+
+ return result;
+ }
+
+ void OutputFilter::parseKonsole()
+ {
+ emit openKonsolePanel();
+ }
+
+ // Accessors
+
+ void OutputFilter::setCommandChar() { commandChar=Preferences::commandChar(); }
+
+ // # & + and ! are *often*, but not necessarily, channel identifiers. + and ! are non-RFC, so if a server doesn't offer 005 and
+ // supports + and ! channels, I think thats broken behaviour on their part - not ours.
+ bool OutputFilter::isAChannel(const QString &check)
+ {
+ Q_ASSERT(m_server);
+ // XXX if we ever see the assert, we need the ternary
+ return m_server? m_server->isAChannel(check) : QString("#&").contains(check.at(0));
+ }
+
+ OutputFilterResult OutputFilter::usage(const QString& string)
+ {
+ OutputFilterResult result;
+ result.typeString = i18n("Usage");
+ result.output = string;
+ result.type = Program;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::info(const QString& string)
+ {
+ OutputFilterResult result;
+ result.typeString = i18n("Info");
+ result.output = string;
+ result.type = Program;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::error(const QString& string)
+ {
+ OutputFilterResult result;
+ result.typeString = i18n("Error");
+ result.output = string;
+ result.type = Program;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseAme(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1AME text").arg(commandChar));
+ }
+
+ emit multiServerCommand("me", parameter);
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseAmsg(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1AMSG text").arg(commandChar));
+ }
+
+ emit multiServerCommand("msg", parameter);
+ return result;
+ }
+
+ void OutputFilter::parseServer(const QString& parameter)
+ {
+ if (parameter.isEmpty() && !m_server->isConnected() && !m_server->isConnecting())
+ emit reconnectServer();
+ else
+ {
+ QStringList splitted = QStringList::split(" ", parameter);
+ QString host = splitted[0];
+ QString port = "6667";
+ QString password;
+
+ if (splitted.count() == 3)
+ emit connectTo(Konversation::CreateNewConnection, splitted[0], splitted[1], splitted[2]);
+ else if (splitted.count() == 2)
+ {
+ if (splitted[0].contains(QRegExp(":[0-9]+$")))
+ emit connectTo(Konversation::CreateNewConnection, splitted[0], "", splitted[1]);
+ else
+ emit connectTo(Konversation::CreateNewConnection, splitted[0], splitted[1]);
+ }
+ else
+ emit connectTo(Konversation::CreateNewConnection, splitted[0]);
+ }
+ }
+
+ OutputFilterResult OutputFilter::parseOmsg(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(!parameter.isEmpty())
+ {
+ result.toServer = "PRIVMSG @"+destination+" :"+parameter;
+ }
+ else
+ {
+ result = usage(i18n("Usage: %1OMSG text").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseOnotice(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(!parameter.isEmpty())
+ {
+ result.toServer = "NOTICE @"+destination+" :"+parameter;
+ result.typeString = i18n("Notice");
+ result.type = Program;
+ result.output = i18n("Sending notice \"%1\" to %2.").arg(parameter, destination);
+ }
+ else
+ {
+ result = usage(i18n("Usage: %1ONOTICE text").arg(commandChar));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseCharset(const QString& charset)
+ {
+ OutputFilterResult result;
+
+ if (charset.isEmpty ())
+ {
+ result = info (i18n("Current encoding is: %1")
+ .arg(m_server->getIdentity()->getCodec()->name()));
+ return result;
+ }
+
+ QString shortName = Konversation::IRCCharsets::self()->ambiguousNameToShortName(charset);
+
+ if(!shortName.isEmpty())
+ {
+ m_server->getIdentity()->setCodecName(shortName);
+ emit encodingChanged();
+ result = info (i18n("Switched to %1 encoding.").arg(shortName));
+ }
+ else
+ {
+ result = error(i18n("%1 is not a valid encoding.").arg (charset));
+ }
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseSetKey(const QString& parameter)
+ {
+
+ QStringList parms = QStringList::split(" ", parameter);
+
+ if (parms.count() == (0 >> parms.count() > 2))
+ return usage(i18n("Usage: %1setkey [<nick|channel>] <key> sets the encryption key for nick or channel. %2setkey <key> when in a channel or query tab to set the key for it.").arg(commandChar).arg(commandChar) );
+ else if (parms.count() == 1)
+ parms.prepend(destination);
+
+ m_server->setKeyForRecipient(parms[0], parms[1].local8Bit());
+
+ if (isAChannel(parms[0]) && m_server->getChannelByName(parms[0]))
+ m_server->getChannelByName(parms[0])->setEncryptedOutput(true);
+ else if (m_server->getQueryByName(parms[0]))
+ m_server->getQueryByName(parms[0])->setEncryptedOutput(true);
+
+ return info(i18n("The key for %1 has been set.").arg(parms[0]));
+ }
+
+ OutputFilterResult OutputFilter::parseDelKey(const QString& prametr)
+ {
+ QString parameter(prametr.isEmpty()?destination:prametr);
+
+ if(parameter.isEmpty() || parameter.contains(' '))
+ return usage(i18n("Usage: %1delkey <nick> or <channel> deletes the encryption key for nick or channel").arg(commandChar));
+
+ m_server->setKeyForRecipient(parameter, "");
+
+ if (isAChannel(parameter) && m_server->getChannelByName(parameter))
+ m_server->getChannelByName(parameter)->setEncryptedOutput(false);
+ else if (m_server->getQueryByName(parameter))
+ m_server->getQueryByName(parameter)->setEncryptedOutput(false);
+
+ return info(i18n("The key for %1 has been deleted.").arg(parameter));
+ }
+
+ OutputFilterResult OutputFilter::parseShowKey(const QString& prametr)
+ {
+ QString parameter(prametr.isEmpty()?destination:prametr);
+ QString key(m_server->getKeyForRecipient(parameter));
+ QWidget *mw=KonversationApplication::instance()->getMainWindow();
+ if (!key.isEmpty())
+ KMessageBox::information(mw, i18n("The key for %1 is \"%2\".").arg(parameter).arg(key), i18n("Blowfish"));
+ else
+ KMessageBox::information(mw, i18n("No key has been set for %1.").arg(parameter));
+ OutputFilterResult result;
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseList(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ emit openChannelList(parameter, true);
+
+ return result;
+ }
+
+ OutputFilterResult OutputFilter::parseDNS(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1DNS <nick>").arg(commandChar));
+ }
+ else
+ {
+ QStringList splitted = QStringList::split(" ", parameter);
+ QString target = splitted[0];
+
+ KIpAddress address(target);
+
+ // Parameter is an IP address
+ if (address.isIPv4Addr() || address.isIPv6Addr())
+ {
+ // Disable the reverse resolve codepath on older KDE versions due to many
+ // distributions shipping visibility-enabled KDE 3.4 and KNetwork not
+ // coping with it.
+#if KDE_IS_VERSION(3,5,1)
+ KNetwork:: KInetSocketAddress socketAddress(address,0);
+ QString resolvedTarget;
+ QString serv; // We don't need this, but KReverseResolver::resolve does.
+
+ if (KNetwork::KReverseResolver::resolve(socketAddress,resolvedTarget,serv))
+ {
+ result.typeString = i18n("DNS");
+ result.output = i18n("Resolved %1 to: %2").arg(target).arg(resolvedTarget);
+ result.type = Program;
+ }
+ else
+ {
+ result = error(i18n("Unable to resolve %1").arg(target));
+ }
+#else
+ result = error(i18n("Reverse-resolving requires KDE version 3.5.1 or higher."));
+#endif
+ }
+ // Parameter is presumed to be a host due to containing a dot. Yeah, it's dumb.
+ // FIXME: The reason we detect the host by occurrence of a dot is the large penalty
+ // we would incur by using inputfilter to find out if there's a user==target on the
+ // server - once we have a better API for this, switch to it.
+ else if (target.contains('.'))
+ {
+ KNetwork::KResolverResults resolved = KNetwork::KResolver::resolve(target,"");
+ if(resolved.error() == KResolver::NoError && resolved.size() > 0)
+ {
+ QString resolvedTarget = resolved.first().address().nodeName();
+ result.typeString = i18n("DNS");
+ result.output = i18n("Resolved %1 to: %2").arg(target).arg(resolvedTarget);
+ result.type = Program;
+ }
+ else
+ {
+ result = error(i18n("Unable to resolve %1").arg(target));
+ }
+ }
+ // Parameter is either host nor IP, so request a lookup from server, which in
+ // turn lets inputfilter do the job.
+ else
+ {
+ m_server->resolveUserhost(target);
+ }
+ }
+
+ return result;
+ }
+
+
+ QString OutputFilter::addNickToEmptyNickList(const QString& nick, const QString& parameter)
+ {
+ QStringList nickList = QStringList::split(' ', parameter);
+ QString newNickList;
+
+ if (nickList.count() == 0)
+ {
+ newNickList = nick;
+ }
+ // check if list contains only target channel
+ else if (nickList.count() == 1 && isAChannel(nickList[0]))
+ {
+ newNickList = nickList[0] + ' ' + nick;
+ }
+ // list contains at least one nick
+ else
+ {
+ newNickList = parameter;
+ }
+
+ return newNickList;
+ }
+
+ OutputFilterResult OutputFilter::parseKill(const QString& parameter)
+ {
+ OutputFilterResult result;
+
+ if(parameter.isEmpty())
+ {
+ result = usage(i18n("Usage: %1KILL <nick> [comment]").arg(commandChar));
+ }
+ else
+ {
+ QString victim = parameter.section(' ', 0, 0);
+ result.toServer = "KILL " + victim + " :" + parameter.mid(victim.length() + 1);
+ }
+
+ return result;
+ }
+}
+#include "outputfilter.moc"
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/outputfilter.h b/konversation/src/outputfilter.h
new file mode 100644
index 0000000..2ad1a72
--- /dev/null
+++ b/konversation/src/outputfilter.h
@@ -0,0 +1,182 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2005 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef OUTPUTFILTER_H
+#define OUTPUTFILTER_H
+
+#include "identity.h"
+#include "common.h"
+
+#include <qobject.h>
+#include <qstring.h>
+#include <kurl.h>
+#include <kio/global.h>
+
+
+class Server;
+class ChatWindow;
+
+namespace Konversation
+{
+ enum MessageType
+ {
+ Message,
+ Action,
+ Command,
+ Program,
+ PrivateMessage
+ };
+
+ struct OutputFilterResult
+ {
+ QString output;
+ QStringList outputList;
+ QString toServer;
+ QStringList toServerList;
+ QString typeString;
+ MessageType type;
+ };
+
+ class OutputFilter : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ explicit OutputFilter(Server* server);
+ ~OutputFilter();
+
+ QStringList splitForEncoding(const QString& inputLine, uint max);
+ OutputFilterResult parse(const QString& myNick,const QString& line,const QString& name);
+
+ // dcc send
+ OutputFilterResult sendRequest(const QString &recipient,const QString &fileName,const QString &address,const QString &port,unsigned long size);
+ OutputFilterResult passiveSendRequest(const QString& recipient,const QString &fileName,const QString &address,unsigned long size,const QString &token);
+ OutputFilterResult acceptResumeRequest(const QString &recipient,const QString &fileName,const QString &port,int startAt);
+
+ // dcc recv
+ OutputFilterResult resumeRequest(const QString &sender,const QString &fileName,const QString &port,KIO::filesize_t startAt);
+ OutputFilterResult acceptPassiveSendRequest(const QString& recipient,const QString &fileName,const QString &address,const QString &port,unsigned long size,const QString &token);
+
+ bool replaceAliases(QString& line);
+
+ signals:
+ void openDccSend(const QString &recipient, KURL kurl);
+ void requestDccSend(); // Choose Recipient and File from requester
+ // Choose File from requester
+ void requestDccSend(const QString& recipient);
+ void openDccChat(const QString& nick);
+ void addDccPanel();
+ void closeDccPanel();
+ void openRawLog(bool show);
+ void closeRawLog();
+ void openKonsolePanel();
+ void openChannelList(const QString& parameter, bool getList);
+ void sendToAllChannels(const QString& text);
+ void launchScript(const QString& target, const QString& parameter);
+ void banUsers(const QStringList& userList,const QString& channel,const QString& option);
+ void unbanUsers(const QString& mask,const QString& channel);
+ void multiServerCommand(const QString& command, const QString& parameter);
+ void reconnectServer();
+ void disconnectServer();
+
+ void connectTo(Konversation::ConnectionFlag flag,
+ const QString& hostName,
+ const QString& port = "",
+ const QString& password = "",
+ const QString& nick = "",
+ const QString& channel = "",
+ bool useSSL = false
+ );
+
+ void showView(ChatWindow* view);
+ void encodingChanged ();
+
+
+ public slots:
+ void setCommandChar();
+ OutputFilterResult execBan(const QString& mask,const QString& channels);
+ OutputFilterResult execUnban(const QString& mask,const QString& channels);
+
+ protected:
+ OutputFilterResult parseMsg(const QString& myNick,const QString& parameter, bool focusQueryWindow);
+ OutputFilterResult parseSMsg(const QString& parameter);
+ OutputFilterResult parseMe(const QString &parameter, const QString &destination);
+ OutputFilterResult parseDescribe(const QString& parameter);
+ OutputFilterResult parseNotice(const QString& parameter);
+ OutputFilterResult parseJoin(QString& parameter);
+ OutputFilterResult parsePart(const QString& parameter);
+ OutputFilterResult parseQuit(const QString& parameter);
+ OutputFilterResult parseClose(QString parameter);
+ OutputFilterResult parseKick(const QString& parameter);
+ OutputFilterResult parseKickBan(const QString& parameter);
+ OutputFilterResult parseBan(const QString& parameter, bool kick = false);
+ OutputFilterResult parseUnban(const QString& parameter);
+ OutputFilterResult parseNames(const QString& parameter);
+ OutputFilterResult parseList(const QString& parameter);
+ OutputFilterResult parseOp(const QString& parameter);
+ OutputFilterResult parseDeop(const QString& ownNick, const QString& parameter);
+ OutputFilterResult parseHop(const QString& parameter);
+ OutputFilterResult parseDehop(const QString& ownNick, const QString& parameter);
+ OutputFilterResult parseVoice(const QString& parameter);
+ OutputFilterResult parseUnvoice(const QString& ownNick, const QString& parameter);
+ OutputFilterResult parseTopic(const QString& parameter);
+ void parseAway(QString& parameter);
+ void parseBack();
+ OutputFilterResult parseCtcp(const QString& parameter);
+ OutputFilterResult parsePing(const QString& parameter);
+ OutputFilterResult parseVersion(const QString& parameter);
+ void parseServer(const QString& parameter);
+ void parseReconnect();
+ OutputFilterResult parseConnect(const QString& parameter);
+ OutputFilterResult parseInvite(const QString& parameter);
+ OutputFilterResult parseExec(const QString& parameter);
+ OutputFilterResult parseNotify(const QString& parameter);
+ OutputFilterResult parseOper(const QString& myNick,const QString& parameter);
+ OutputFilterResult parseDcc(const QString& parameter);
+ OutputFilterResult parseRaw(const QString& parameter);
+ OutputFilterResult parseIgnore(const QString& parameter);
+ OutputFilterResult parseUnignore(const QString& parameter);
+ OutputFilterResult parseQuote(const QString& parameter);
+ OutputFilterResult parseSay(const QString& parameter);
+ void parseKonsole();
+ OutputFilterResult parseAme(const QString& parameter);
+ OutputFilterResult parseAmsg(const QString& parameter);
+ OutputFilterResult parseOmsg(const QString& parameter);
+ OutputFilterResult parseOnotice(const QString& parameter);
+ OutputFilterResult parseCharset(const QString& charset);
+ void parseCycle();
+ OutputFilterResult parseSetKey(const QString& parameter);
+ OutputFilterResult parseDelKey(const QString& parameter);
+ OutputFilterResult parseShowKey(const QString& parameter);
+ OutputFilterResult parseDNS(const QString& parameter);
+ OutputFilterResult parseKill(const QString& parameter);
+ OutputFilterResult parseShowTuner(const QString &p);
+
+ OutputFilterResult changeMode(const QString& parameter,char mode,char giveTake);
+ bool isAChannel(const QString& check);
+ OutputFilterResult usage(const QString& check);
+ OutputFilterResult info(const QString& check);
+ OutputFilterResult error(const QString& check);
+
+ QString addNickToEmptyNickList(const QString& nick, const QString& parameter);
+
+ private:
+ QString destination;
+ QString commandChar;
+
+ Server* m_server;
+ };
+}
+#endif
diff --git a/konversation/src/query.cpp b/konversation/src/query.cpp
new file mode 100644
index 0000000..e87fbe4
--- /dev/null
+++ b/konversation/src/query.cpp
@@ -0,0 +1,673 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "query.h"
+#include "channel.h"
+#include "server.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "viewcontainer.h"
+#include "ircinput.h"
+#include "ircview.h"
+#include "ircviewbox.h"
+#include "common.h"
+#include "topiclabel.h"
+
+#include <qhbox.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qtextcodec.h>
+#include <qtooltip.h>
+#include <qtextstream.h>
+#include <qwhatsthis.h>
+#include <qsplitter.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kstringhandler.h>
+#include <kpopupmenu.h>
+
+
+Query::Query(QWidget* parent, QString _name) : ChatWindow(parent)
+{
+ name=_name; // need the name a little bit earlier for setServer
+ // don't setName here! It will break logfiles!
+ // setName("QueryWidget");
+ setType(ChatWindow::Query);
+
+ setChannelEncodingSupported(true);
+
+ m_headerSplitter = new QSplitter(Qt::Vertical, this);
+
+ m_initialShow = true;
+ awayChanged=false;
+ awayState=false;
+ QHBox *box = new QHBox(m_headerSplitter);
+ addresseeimage = new QLabel(box, "query_image");
+ addresseeimage->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ addresseeimage->hide();
+ addresseelogoimage = new QLabel(box, "query_logo_image");
+ addresseelogoimage->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ addresseelogoimage->hide();
+
+ queryHostmask=new Konversation::TopicLabel(box, "query_hostmask");
+
+ QString whatsthis = i18n("<qt>Some details of the person you are talking to in this query is shown in this bar. The full name and hostmask is shown, along with any image or logo this person has associated with them in the KDE Addressbook.<p>See the <i>Konversation Handbook</i> for information on associating a nick with a contact in the Addressbook, and for an explanation of what the hostmask is.</qt>");
+ QWhatsThis::add(addresseeimage, whatsthis);
+ QWhatsThis::add(addresseelogoimage, whatsthis);
+ QWhatsThis::add(queryHostmask, whatsthis);
+
+ IRCViewBox* ircBox = new IRCViewBox(m_headerSplitter,0);
+ setTextView(ircBox->ircView()); // Server will be set later in setServer();
+ textView->setAcceptDrops(true);
+ connect(textView,SIGNAL(filesDropped(const QStrList&)),this,SLOT(filesDropped(const QStrList&)));
+ connect(textView,SIGNAL(popupCommand(int)),this,SLOT(popup(int)));
+
+ // link "Whois", "Ignore" ... menu items into ircview popup
+ QPopupMenu* popup=textView->getPopup();
+ popup->insertSeparator();
+ popup->insertItem(i18n("&Whois"),Konversation::Whois);
+ popup->insertItem(i18n("&Version"),Konversation::Version);
+ popup->insertItem(i18n("&Ping"),Konversation::Ping);
+ popup->insertSeparator();
+
+ popup->insertItem(i18n("Ignore"), Konversation::IgnoreNick);
+ popup->insertItem(i18n("Unignore"), Konversation::UnignoreNick);
+ popup->setItemVisible(Konversation::IgnoreNick, false);
+ popup->setItemVisible(Konversation::UnignoreNick, false);
+
+ if (kapp->authorize("allow_downloading"))
+ popup->insertItem(SmallIcon("2rightarrow"),i18n("Send &File..."),Konversation::DccSend);
+
+ popup->insertItem(i18n("Add to Watched Nicks"), Konversation::AddNotify);
+
+ // This box holds the input line
+ QHBox* inputBox=new QHBox(this, "input_log_box");
+ inputBox->setSpacing(spacing());
+
+ awayLabel=new QLabel(i18n("(away)"),inputBox);
+ awayLabel->hide();
+ blowfishLabel = new QLabel(inputBox);
+ blowfishLabel->hide();
+ blowfishLabel->setPixmap(KGlobal::iconLoader()->loadIcon("encrypted", KIcon::Toolbar));
+ queryInput=new IRCInput(inputBox);
+
+ getTextView()->installEventFilter(queryInput);
+ queryInput->installEventFilter(this);
+
+ // connect the signals and slots
+ connect(queryInput,SIGNAL (submit()),this,SLOT (queryTextEntered()) );
+ connect(queryInput,SIGNAL (envelopeCommand()),this,SLOT (queryPassthroughCommand()) );
+ connect(queryInput,SIGNAL (textPasted(const QString&)),this,SLOT (textPasted(const QString&)) );
+ connect(getTextView(), SIGNAL(textPasted(bool)), queryInput, SLOT(paste(bool)));
+ connect(getTextView(),SIGNAL (gotFocus()),queryInput,SLOT (setFocus()) );
+
+ connect(textView,SIGNAL (sendFile()),this,SLOT (sendFileMenu()) );
+ connect(textView,SIGNAL (extendedPopup(int)),this,SLOT (popup(int)) );
+ connect(textView,SIGNAL (autoText(const QString&)),this,SLOT (sendQueryText(const QString&)) );
+
+ updateAppearance();
+
+ setLog(Preferences::log());
+}
+
+Query::~Query()
+{
+}
+
+void Query::setServer(Server* newServer)
+{
+ if (m_server != newServer)
+ connect(newServer, SIGNAL(connectionStateChanged(Server*, Konversation::ConnectionState)),
+ SLOT(connectionStateChanged(Server*, Konversation::ConnectionState)));
+
+ ChatWindow::setServer(newServer);
+
+ if (newServer->getKeyForRecipient(getName()))
+ blowfishLabel->show();
+}
+
+void Query::connectionStateChanged(Server* server, Konversation::ConnectionState state)
+{
+ if (server == m_server)
+ {
+ if (state == Konversation::SSConnected)
+ {
+ //HACK the way the notification priorities work sucks, this forces the tab text color to ungray right now.
+ if (m_currentTabNotify == Konversation::tnfNone || !Preferences::tabNotificationsEvents())
+ KonversationApplication::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
+ }
+ else
+ {
+ //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now.
+ if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl))
+ KonversationApplication::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
+ }
+ }
+}
+
+void Query::setName(const QString& newName)
+{
+ //if(ChatWindow::getName() == newName) return; // no change, so return
+
+ ChatWindow::setName(newName);
+
+ // don't change logfile name if query name changes
+ // This will prevent Nick-Changers to create more than one log file,
+ if (logName.isEmpty())
+ {
+ QString logName = (Preferences::lowerLog()) ? getName().lower() : getName() ;
+
+ if(Preferences::addHostnameToLog())
+ {
+ if(m_nickInfo)
+ logName += m_nickInfo->getHostmask();
+ }
+
+ setLogfileName(logName);
+ }
+}
+
+void Query::setEncryptedOutput(bool e)
+{
+ if (e)
+ blowfishLabel->show();
+ else
+ blowfishLabel->hide();
+}
+
+void Query::queryTextEntered()
+{
+ QString line=queryInput->text();
+ queryInput->setText("");
+ if(line.lower()==Preferences::commandChar()+"clear")
+ {
+ textView->clear();
+ }
+ else if(line.lower()==Preferences::commandChar()+"part")
+ {
+ m_server->closeQuery(getName());
+ }
+ else if(line.length())
+ {
+ sendQueryText(line);
+ }
+}
+
+void Query::queryPassthroughCommand()
+{
+ QString commandChar = Preferences::commandChar();
+ QString line = queryInput->text();
+
+ queryInput->setText("");
+
+ if(!line.isEmpty())
+ {
+ // Prepend commandChar on Ctrl+Enter to bypass outputfilter command recognition
+ if (line.startsWith(commandChar))
+ {
+ line = commandChar + line;
+ }
+ sendQueryText(line);
+ }
+}
+
+void Query::sendQueryText(const QString& sendLine)
+{
+ // create a work copy
+ QString outputAll(sendLine);
+ // replace aliases and wildcards
+ if(m_server->getOutputFilter()->replaceAliases(outputAll))
+ {
+ outputAll = m_server->parseWildcards(outputAll, m_server->getNickname(), getName(), QString(), QString(), QString());
+ }
+
+ // Send all strings, one after another
+ QStringList outList=QStringList::split('\n',outputAll);
+ for(unsigned int index=0;index<outList.count();index++)
+ {
+ QString output(outList[index]);
+
+ // encoding stuff is done in Server()
+ Konversation::OutputFilterResult result = m_server->getOutputFilter()->parse(m_server->getNickname(), output, getName());
+
+ if(!result.output.isEmpty())
+ {
+ if(result.type == Konversation::Action) appendAction(m_server->getNickname(), result.output);
+ else if(result.type == Konversation::Command) appendCommandMessage(result.typeString, result.output);
+ else if(result.type == Konversation::Program) appendServerMessage(result.typeString, result.output);
+ else if(!result.typeString.isEmpty()) appendQuery(result.typeString, result.output);
+ else appendQuery(m_server->getNickname(), result.output);
+ }
+ else if (result.outputList.count())
+ {
+ Q_ASSERT(result.type==Konversation::Message);
+ for ( QStringList::Iterator it = result.outputList.begin(); it != result.outputList.end(); ++it )
+ {
+ append(m_server->getNickname(), *it);
+ }
+ }
+
+ if (!result.toServerList.empty())
+ {
+ m_server->queueList(result.toServerList);
+ }
+ else
+ {
+ m_server->queue(result.toServer);
+ }
+ } // for
+}
+
+void Query::updateAppearance()
+{
+ QColor fg;
+ QColor bg;
+
+ if(Preferences::inputFieldsBackgroundColor())
+ {
+ fg=Preferences::color(Preferences::ChannelMessage);
+ bg=Preferences::color(Preferences::TextViewBackground);
+ }
+ else
+ {
+ fg=colorGroup().foreground();
+ bg=colorGroup().base();
+ }
+
+ queryInput->unsetPalette();
+ queryInput->setPaletteForegroundColor(fg);
+ queryInput->setPaletteBackgroundColor(bg);
+
+ getTextView()->unsetPalette();
+
+ if (Preferences::showBackgroundImage())
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ Preferences::backgroundImage());
+ }
+ else
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ QString());
+ }
+
+ if (Preferences::customTextFont())
+ {
+ getTextView()->setFont(Preferences::textFont());
+ queryInput->setFont(Preferences::textFont());
+ }
+ else
+ {
+ getTextView()->setFont(KGlobalSettings::generalFont());
+ queryInput->setFont(KGlobalSettings::generalFont());
+ }
+
+ ChatWindow::updateAppearance();
+}
+
+void Query::textPasted(const QString& text)
+{
+ if(m_server)
+ {
+ QStringList multiline=QStringList::split('\n',text);
+ for(unsigned int index=0;index<multiline.count();index++)
+ {
+ QString line=multiline[index];
+ QString cChar(Preferences::commandChar());
+ // make sure that lines starting with command char get escaped
+ if(line.startsWith(cChar)) line=cChar+line;
+ sendQueryText(line);
+ }
+ }
+}
+
+void Query::indicateAway(bool show)
+{
+ // QT does not redraw the label properly when they are not on screen
+ // while getting hidden, so we remember the "soon to be" state here.
+ if(isHidden())
+ {
+ awayChanged=true;
+ awayState=show;
+ }
+ else
+ {
+ if(show)
+ awayLabel->show();
+ else
+ awayLabel->hide();
+ }
+}
+
+// fix QTs broken behavior on hidden QListView pages
+void Query::showEvent(QShowEvent*)
+{
+ if(awayChanged)
+ {
+ awayChanged=false;
+ indicateAway(awayState);
+ }
+
+ if(m_initialShow) {
+ m_initialShow = false;
+ QValueList<int> sizes;
+ sizes << queryHostmask->sizeHint().height() << (height() - queryHostmask->sizeHint().height());
+ m_headerSplitter->setSizes(sizes);
+ }
+}
+
+void Query::popup(int id)
+{
+ // get the nickname to the context menu popup
+ QString name = textView->getContextNick();
+ // if there was none (right click into the text view) assume query partner
+ if (name.isEmpty()) name = getName();
+
+ switch (id)
+ {
+ case Konversation::Whois:
+ sendQueryText(Preferences::commandChar()+"WHOIS "+name+' '+name);
+ break;
+
+ case Konversation::IgnoreNick:
+ {
+ if (KMessageBox::warningContinueCancel(this, i18n("Do you want to ignore %1?").arg(name),
+ i18n("Ignore"), i18n("Ignore"), "IgnoreNick") == KMessageBox::Continue)
+ {
+ sendQueryText(Preferences::commandChar()+"IGNORE -ALL "+name);
+
+ int rc = KMessageBox::questionYesNo(this,
+ i18n("Do you want to close this query after ignoring this nickname?"),
+ i18n("Close This Query"),
+ i18n("Close"),
+ i18n("Keep Open"),
+ "CloseQueryAfterIgnore");
+
+ if (rc == KMessageBox::Yes && m_server)
+ QTimer::singleShot(0, this, SLOT(closeWithoutAsking()));
+ }
+
+ break;
+ }
+ case Konversation::UnignoreNick:
+ {
+ QString question = i18n("Do you want to stop ignoring %1?").arg(name);
+
+ if (KMessageBox::warningContinueCancel(this, question, i18n("Unignore"), i18n("Unignore"), "UnignoreNick") ==
+ KMessageBox::Continue)
+ {
+ sendQueryText(Preferences::commandChar()+"UNIGNORE "+name);
+ }
+
+ break;
+ }
+ case Konversation::AddNotify:
+ {
+ if (m_server->getServerGroup())
+ {
+ if (!Preferences::isNotify(m_server->getServerGroup()->id(), name))
+ Preferences::addNotify(m_server->getServerGroup()->id(),name);
+ }
+ break;
+ }
+ case Konversation::DccSend:
+ sendQueryText(Preferences::commandChar()+"DCC SEND "+name);
+ break;
+
+ case Konversation::Version:
+ sendQueryText(Preferences::commandChar()+"CTCP "+name+" VERSION");
+ break;
+
+ case Konversation::Ping:
+ sendQueryText(Preferences::commandChar()+"CTCP "+name+" PING");
+ break;
+
+ case Konversation::Topic:
+ m_server->requestTopic(getTextView()->currentChannel());
+ break;
+ case Konversation::Names:
+ m_server->queue("NAMES " + getTextView()->currentChannel(), Server::LowPriority);
+ break;
+ case Konversation::Join:
+ m_server->queue("JOIN " + getTextView()->currentChannel());
+ break;
+
+ default:
+ kdDebug() << "Query::popup(): Popup id " << id << " does not belong to me!" << endl;
+ break;
+ }
+
+ // delete context menu nickname
+ textView->clearContextNick();
+}
+
+void Query::sendFileMenu()
+{
+ emit sendFile(getName());
+}
+
+void Query::childAdjustFocus()
+{
+ queryInput->setFocus();
+}
+
+void Query::setNickInfo(const NickInfoPtr & nickInfo)
+{
+ if(m_nickInfo)
+ disconnect(m_nickInfo, SIGNAL(nickInfoChanged()), this, SLOT(nickInfoChanged()));
+
+ m_nickInfo = nickInfo;
+ Q_ASSERT(m_nickInfo); if(!m_nickInfo) return;
+ setName(m_nickInfo->getNickname());
+ connect(m_nickInfo, SIGNAL(nickInfoChanged()), this, SLOT(nickInfoChanged()));
+ nickInfoChanged();
+}
+
+void Query::nickInfoChanged()
+{
+ if(m_nickInfo)
+ {
+ setName(m_nickInfo->getNickname());
+ QString text = m_nickInfo->getBestAddresseeName();
+ if(!m_nickInfo->getHostmask().isEmpty() && !text.isEmpty())
+ text += " - ";
+ text += m_nickInfo->getHostmask();
+ if(m_nickInfo->isAway() && !m_nickInfo->getAwayMessage().isEmpty())
+ text += " (" + KStringHandler::rsqueeze(m_nickInfo->getAwayMessage(),100) + ") ";
+ queryHostmask->setText(Konversation::removeIrcMarkup(text));
+
+ KABC::Picture pic = m_nickInfo->getAddressee().photo();
+ if(pic.isIntern())
+ {
+ QPixmap qpixmap(pic.data().scaleHeight(queryHostmask->height()));
+ if(!qpixmap.isNull())
+ {
+ addresseeimage->setPixmap(qpixmap);
+ addresseeimage->show();
+ }
+ else
+ {
+ addresseeimage->hide();
+ }
+ }
+ else
+ {
+ addresseeimage->hide();
+ }
+ KABC::Picture logo = m_nickInfo->getAddressee().logo();
+ if(logo.isIntern())
+ {
+ QPixmap qpixmap(logo.data().scaleHeight(queryHostmask->height()));
+ if(!qpixmap.isNull())
+ {
+ addresseelogoimage->setPixmap(qpixmap);
+ addresseelogoimage->show();
+ }
+ else
+ {
+ addresseelogoimage->hide();
+ }
+ }
+ else
+ {
+ addresseelogoimage->hide();
+ }
+
+ QString strTooltip;
+ QTextStream tooltip( &strTooltip, IO_WriteOnly );
+
+ tooltip << "<qt>";
+
+ tooltip << "<table cellspacing=\"0\" cellpadding=\"0\">";
+
+ m_nickInfo->tooltipTableData(tooltip);
+
+ tooltip << "</table></qt>";
+ QToolTip::add(queryHostmask, strTooltip);
+ QToolTip::add(addresseeimage, strTooltip);
+ QToolTip::add(addresseelogoimage, strTooltip);
+
+ }
+ else
+ {
+ addresseeimage->hide();
+ addresseelogoimage->hide();
+ }
+
+ emit updateQueryChrome(this,getName());
+ emitUpdateInfo();
+}
+
+NickInfoPtr Query::getNickInfo()
+{
+ return m_nickInfo;
+}
+
+QString Query::getTextInLine() { return queryInput->text(); }
+
+bool Query::canBeFrontView() { return true; }
+bool Query::searchView() { return true; }
+
+void Query::appendInputText(const QString& s, bool fromCursor)
+{
+ if(!fromCursor)
+ {
+ queryInput->append(s);
+ }
+ else
+ {
+ int para = 0, index = 0;
+ queryInput->getCursorPosition(&para, &index);
+ queryInput->insertAt(s, para, index);
+ queryInput->setCursorPosition(para, index + s.length());
+ }
+}
+
+ // virtual
+void Query::setChannelEncoding(const QString& encoding)
+{
+ Preferences::setChannelEncoding(m_server->getDisplayName(), getName(), encoding);
+}
+
+QString Query::getChannelEncoding() // virtual
+{
+ return Preferences::channelEncoding(m_server->getDisplayName(), getName());
+}
+
+QString Query::getChannelEncodingDefaultDesc() // virtual
+{
+ return i18n("Identity Default ( %1 )").arg(getServer()->getIdentity()->getCodecName());
+}
+
+bool Query::closeYourself(bool confirm)
+{
+ int result = KMessageBox::Continue;
+ if (confirm)
+ result=KMessageBox::warningContinueCancel(this, i18n("Do you want to close your query with %1?").arg(getName()), i18n("Close Query"), i18n("Close"), "QuitQueryTab");
+
+ if (result == KMessageBox::Continue)
+ {
+ m_server->removeQuery(this);
+ return true;
+ }
+
+ return false;
+}
+
+void Query::closeWithoutAsking()
+{
+ m_server->removeQuery(this);
+}
+
+void Query::filesDropped(const QStrList& files)
+{
+ m_server->sendURIs(files,getName());
+}
+
+void Query::serverOnline(bool online)
+{
+ //queryInput->setEnabled(online);
+ getTextView()->setNickAndChannelContextMenusEnabled(online);
+
+ QPopupMenu* popup = getTextView()->getPopup();
+
+ if (popup)
+ {
+ popup->setItemEnabled(Konversation::Whois, online);
+ popup->setItemEnabled(Konversation::Version, online);
+ popup->setItemEnabled(Konversation::Ping, online);
+ popup->setItemEnabled(Konversation::IgnoreNick, online);
+ popup->setItemEnabled(Konversation::UnignoreNick, online);
+ popup->setItemEnabled(Konversation::AddNotify, online);
+
+ if (kapp->authorize("allow_downloading"))
+ popup->setItemEnabled(Konversation::DccSend, online);
+ }
+}
+
+void Query::emitUpdateInfo()
+{
+ QString info;
+ if(m_nickInfo->loweredNickname() == m_server->loweredNickname())
+ info = i18n("Talking to yourself");
+ else if(m_nickInfo)
+ info = m_nickInfo->getBestAddresseeName();
+ else
+ info = getName();
+
+ emit updateInfo(info);
+}
+
+// show quit message of nick if we see it
+void Query::quitNick(const QString& reason)
+{
+ QString displayReason = reason;
+
+ if (displayReason.isEmpty())
+ {
+ appendCommandMessage(i18n("Quit"),i18n("%1 has left this server.").arg(getName()),false);
+ }
+ else
+ {
+ if (displayReason.find(QRegExp("[\\0000-\\0037]"))!=-1)
+ displayReason+="\017";
+
+ appendCommandMessage(i18n("Quit"),i18n("%1 has left this server (%2).").arg(getName()).arg(displayReason),false);
+ }
+}
+
+#include "query.moc"
diff --git a/konversation/src/query.h b/konversation/src/query.h
new file mode 100644
index 0000000..882e3c7
--- /dev/null
+++ b/konversation/src/query.h
@@ -0,0 +1,118 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef QUERY_H
+#define QUERY_H
+
+#include "chatwindow.h"
+#include "nickinfo.h"
+
+#include <qstring.h>
+
+/* TODO: Idle counter to close query after XXX minutes of inactivity */
+/* TODO: Use /USERHOST to check if queries are still valid */
+
+class QLineEdit;
+class QCheckBox;
+class QLabel;
+class QSplitter;
+
+class IRCInput;
+
+namespace Konversation {
+ class TopicLabel;
+}
+
+class Query : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit Query(QWidget* parent, QString name);
+ virtual void setServer(Server* newServer);
+
+ ~Query();
+
+ /** This will always be called soon after this object is created.
+ * @param nickInfo A nickinfo that must exist.
+ */
+ void setNickInfo(const NickInfoPtr & nickInfo);
+ /** It seems that this does _not_ guaranttee to return non null.
+ * The problem is when you open a query to someone, then the go offline.
+ * This should be fixed maybe? I don't know.
+ */
+ NickInfoPtr getNickInfo();
+ virtual QString getTextInLine();
+ virtual bool closeYourself(bool askForConfirmation=true);
+ virtual bool canBeFrontView();
+ virtual bool searchView();
+
+ virtual void setChannelEncoding(const QString& encoding);
+ virtual QString getChannelEncoding();
+ virtual QString getChannelEncodingDefaultDesc();
+ virtual void emitUpdateInfo();
+
+ virtual bool isInsertSupported() { return true; }
+
+ /** call this when you see a nick quit from the server.
+ * @param reason The quit reason given by that user.
+ */
+ void quitNick(const QString& reason);
+
+ signals:
+ void sendFile(const QString& recipient);
+ void updateQueryChrome(ChatWindow*, const QString&);
+
+ public slots:
+ void sendQueryText(const QString& text);
+ void appendInputText(const QString& s, bool fromCursor);
+ virtual void indicateAway(bool show);
+ void updateAppearance();
+ void setEncryptedOutput(bool);
+ void connectionStateChanged(Server*, Konversation::ConnectionState);
+
+ protected slots:
+ void queryTextEntered();
+ void queryPassthroughCommand();
+ void sendFileMenu();
+ void filesDropped(const QStrList& files);
+ // connected to IRCInput::textPasted() - used to handle large/multiline pastes
+ void textPasted(const QString& text);
+ void popup(int id);
+ void nickInfoChanged();
+ void closeWithoutAsking();
+ virtual void serverOnline(bool online);
+
+ protected:
+ void setName(const QString& newName);
+ void showEvent(QShowEvent* event);
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ bool awayChanged;
+ bool awayState;
+
+ QString queryName;
+ QString buffer;
+
+ QSplitter* m_headerSplitter;
+ Konversation::TopicLabel* queryHostmask;
+ QLabel* addresseeimage;
+ QLabel* addresseelogoimage;
+ QLabel* awayLabel;
+ QLabel* blowfishLabel;
+ IRCInput* queryInput;
+ NickInfoPtr m_nickInfo;
+
+ bool m_initialShow;
+};
+#endif
diff --git a/konversation/src/queuetuner.cpp b/konversation/src/queuetuner.cpp
new file mode 100644
index 0000000..7686ae4
--- /dev/null
+++ b/konversation/src/queuetuner.cpp
@@ -0,0 +1,256 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) version 2.
+*/
+
+/*
+ Copyright (C) 2008 Eli J. MacKenzie <argonel at gmail.com>
+*/
+
+
+
+#include "queuetuner.h"
+#include "server.h"
+#include "ircqueue.h"
+#include "channel.h"
+#include "viewcontainer.h"
+#include "konversationapplication.h"
+
+#include <qtoolbutton.h>
+#include <qspinbox.h>
+#include <qpopupmenu.h>
+#include <qevent.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kstdguiitem.h>
+#include <klocale.h>
+
+
+QueueTuner::QueueTuner(QWidget* parent, ViewContainer *container)
+: QueueTunerBase(parent), m_server(0), m_timer(this, "qTuner"),
+ m_vis(Preferences::self()->showQueueTunerItem()->value())
+{
+ m_closeButton->setIconSet(kapp->iconLoader()->loadIconSet("fileclose", KIcon::Toolbar, 16));
+ connect(m_closeButton, SIGNAL(clicked()), SLOT(close()));
+ connect(container, SIGNAL(frontServerChanging(Server*)), SLOT(setServer(Server*)));
+ connect(&m_timer, SIGNAL(timeout()), SLOT(timerFired()));
+
+ connect(m_slowRate, SIGNAL(valueChanged(int)), SLOT(slowRateChanged(int)));
+ connect(m_slowType, SIGNAL(activated(int)), SLOT(slowTypeChanged(int)));
+ connect(m_slowInterval, SIGNAL(valueChanged(int)), SLOT(slowIntervalChanged(int)));
+
+ connect(m_normalRate, SIGNAL(valueChanged(int)), SLOT(normalRateChanged(int)));
+ connect(m_normalType, SIGNAL(activated(int)), SLOT(normalTypeChanged(int)));
+ connect(m_normalInterval, SIGNAL(valueChanged(int)), SLOT(normalIntervalChanged(int)));
+
+ connect(m_fastRate, SIGNAL(valueChanged(int)), SLOT(fastRateChanged(int)));
+ connect(m_fastType, SIGNAL(activated(int)), SLOT(fastTypeChanged(int)));
+ connect(m_fastInterval, SIGNAL(valueChanged(int)), SLOT(fastIntervalChanged(int)));
+}
+
+QueueTuner::~QueueTuner()
+{
+}
+
+//lps, lpm, bps, kbps
+static void rateToWidget(IRCQueue::EmptyingRate& rate, QSpinBox *r, QComboBox* t, QSpinBox *i)
+{
+ r->setValue(rate.m_rate);
+ t->setCurrentItem(rate.m_type);
+ i->setValue(rate.m_interval/1000);
+}
+
+void QueueTuner::serverDestroyed(QObject* ref)
+{
+ if (ref == m_server)
+ setServer(0);
+}
+
+void QueueTuner::setServer(Server* newServer)
+{
+ const char *w=0;
+ bool toShow=false;
+ if (!m_server && newServer)
+ {
+ toShow=true;
+ w="showing";
+ }
+ else if (!newServer && m_server)
+ {
+ hide();
+ w="hiding";
+ }
+ else
+ w="unchanged";
+ // since this is tied to the new signal, we assume we're only getting called with a change
+
+ m_server = newServer;
+
+ if (toShow)
+ show();
+
+ if (m_server)
+ {
+ connect(m_server, SIGNAL(destroyed(QObject*)), SLOT(serverDestroyed(QObject*)));
+
+ getRates();
+ }
+}
+
+void QueueTuner::getRates()
+{
+ if (!m_server) // you can only get the popup if there is a server, but.. paranoid
+ return;
+
+ rateToWidget(m_server->m_queues[0]->getRate(), m_slowRate, m_slowType, m_slowInterval);
+ rateToWidget(m_server->m_queues[1]->getRate(), m_normalRate, m_normalType, m_normalInterval);
+ rateToWidget(m_server->m_queues[2]->getRate(), m_fastRate, m_fastType, m_fastInterval);
+}
+
+void QueueTuner::timerFired()
+{
+ if (m_server)
+ {
+ IRCQueue *q=0;
+
+ q=m_server->m_queues[0];
+ m_slowAge->setNum(q->currentWait()/1000);
+ m_slowBytes->setNum(q->bytesSent());
+ m_slowCount->setNum(q->pendingMessages());
+ m_slowLines->setNum(q->linesSent());
+
+ q=m_server->m_queues[1];
+ m_normalAge->setNum(q->currentWait()/1000);
+ m_normalBytes->setNum(q->bytesSent());
+ m_normalCount->setNum(q->pendingMessages());
+ m_normalLines->setNum(q->linesSent());
+
+ q=m_server->m_queues[2];
+ m_fastAge->setNum(q->currentWait()/1000);
+ m_fastBytes->setNum(q->bytesSent());
+ m_fastCount->setNum(q->pendingMessages());
+ m_fastLines->setNum(q->linesSent());
+
+ m_srverBytes->setNum(m_server->m_encodedBytesSent);
+ m_globalBytes->setNum(m_server->m_bytesSent);
+ m_globalLines->setNum(m_server->m_linesSent);
+ m_recvBytes->setNum(m_server->m_bytesReceived);
+ }
+}
+
+void QueueTuner::open()
+{
+ Preferences::setShowQueueTuner(true);
+ show();
+}
+
+void QueueTuner::close()
+{
+ Preferences::setShowQueueTuner(false);
+ QueueTunerBase::close();
+}
+
+void QueueTuner::show()
+{
+ if (m_server && Preferences::showQueueTuner())
+ {
+ QueueTunerBase::show();
+ m_timer.start(500);
+
+ }
+}
+
+void QueueTuner::hide()
+{
+ QueueTunerBase::hide();
+ m_timer.stop();
+}
+
+void QueueTuner::slowRateChanged(int v)
+{
+ if (!m_server) return;
+ int &r=m_server->m_queues[0]->getRate().m_rate;
+ r=v;
+}
+
+void QueueTuner::slowTypeChanged(int v)
+{
+ if (!m_server) return;
+ IRCQueue::EmptyingRate::RateType &r=m_server->m_queues[0]->getRate().m_type;
+ r=IRCQueue::EmptyingRate::RateType(v);
+}
+
+void QueueTuner::slowIntervalChanged(int v)
+{
+ if (!m_server) return;
+ int &r=m_server->m_queues[0]->getRate().m_interval;
+ r=v*1000;
+}
+
+void QueueTuner::normalRateChanged(int v)
+{
+ if (!m_server) return;
+ int &r=m_server->m_queues[1]->getRate().m_rate;
+ r=v;
+}
+
+void QueueTuner::normalTypeChanged(int v)
+{
+ if (!m_server) return;
+ IRCQueue::EmptyingRate::RateType &r=m_server->m_queues[1]->getRate().m_type;
+ r=IRCQueue::EmptyingRate::RateType(v);
+}
+
+void QueueTuner::normalIntervalChanged(int v)
+{
+ if (!m_server) return;
+ int &r=m_server->m_queues[1]->getRate().m_interval;
+ r=v*1000;
+}
+
+void QueueTuner::fastRateChanged(int v)
+{
+ if (!m_server) return;
+ int &r=m_server->m_queues[2]->getRate().m_rate;
+ r=v;
+}
+
+void QueueTuner::fastTypeChanged(int v)
+{
+ if (!m_server) return;
+ IRCQueue::EmptyingRate::RateType &r=m_server->m_queues[2]->getRate().m_type;
+ r=IRCQueue::EmptyingRate::RateType(v);
+}
+
+void QueueTuner::fastIntervalChanged(int v)
+{
+ if (!m_server) return;
+ int &r=m_server->m_queues[2]->getRate().m_interval;
+ r=v*1000;
+}
+
+void QueueTuner::contextMenuEvent(QContextMenuEvent* e)
+{
+ QPopupMenu p(this);
+ p.insertItem("Reset...", 1);
+ int id = p.exec(e->globalPos());
+ if (id > 0)
+ {
+
+ QString question(i18n("This cannot be undone, are you sure you wish to reset to default values?"));
+ int x = KMessageBox::warningContinueCancel(this, question, i18n("Reset Values"), KStdGuiItem::reset(), QString::null, KMessageBox::Dangerous);
+ if ( x == KMessageBox::Continue)
+ {
+ Server::_resetRates();
+ getRates();
+ }
+ }
+ e->accept();
+}
+
+#include "queuetuner.moc"
diff --git a/konversation/src/queuetuner.h b/konversation/src/queuetuner.h
new file mode 100644
index 0000000..081572c
--- /dev/null
+++ b/konversation/src/queuetuner.h
@@ -0,0 +1,62 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) version 2.
+*/
+
+/*
+ Copyright (C) 2008 Eli J. MacKenzie <argonel at gmail.com>
+*/
+
+
+#ifndef QUEUETUNER_H
+#define QUEUETUNER_H
+
+#include "queuetunerbase.h"
+
+class Server;
+class ViewContainer;
+class QTimer;
+
+#include <qtimer.h>
+
+class QueueTuner: public QueueTunerBase
+{
+ Q_OBJECT
+
+ public:
+ QueueTuner(QWidget* parent, ViewContainer *container);
+ ~QueueTuner();
+ virtual void contextMenuEvent (QContextMenuEvent*);
+
+ public slots:
+ void setServer(Server* newServer);
+ void getRates();
+ void timerFired();
+ virtual void hide();
+ virtual void show();
+ virtual void open();
+ virtual void close();
+ void slowRateChanged(int);
+ void slowTypeChanged(int);
+ void slowIntervalChanged(int);
+ void normalRateChanged(int);
+ void normalTypeChanged(int);
+ void normalIntervalChanged(int);
+ void fastRateChanged(int);
+ void fastTypeChanged(int);
+ void fastIntervalChanged(int);
+ void serverDestroyed(QObject*);
+
+ signals:
+ void hidden();
+
+ private:
+ Server* m_server;
+ QTimer m_timer;
+ bool &m_vis;
+};
+
+
+#endif
diff --git a/konversation/src/queuetunerbase.ui b/konversation/src/queuetunerbase.ui
new file mode 100644
index 0000000..f11a117
--- /dev/null
+++ b/konversation/src/queuetunerbase.ui
@@ -0,0 +1,767 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>QueueTunerBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>QueueTunerBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>3</y>
+ <width>726</width>
+ <height>91</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout92</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>1</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer34</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>34</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QToolButton">
+ <property name="name">
+ <cstring>m_closeButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ <property name="textPosition">
+ <enum>BesideIcon</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Close</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer35</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>33</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_slowBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="title">
+ <string>Slow Queue</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout164</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_slowRate</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Lines</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Bytes</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_slowType</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_slowInterval</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout242</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="1" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>m_slowAge</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_slowCount</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>m_textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Age:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>m_textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Count:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout236_2_2</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Lines:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Bytes:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_slowLines</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_slowBytes</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_normalBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="title">
+ <string>Normal Queue</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout163</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_normalRate</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Lines</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Bytes</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_normalType</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_normalInterval</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout236_2</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Lines:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5_2</cstring>
+ </property>
+ <property name="text">
+ <string>Bytes:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_normalLines</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_normalBytes</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout240</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_textLabel3_4</cstring>
+ </property>
+ <property name="text">
+ <string>Count:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_normalAge</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_normalCount</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>m_textLabel3_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Age:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_fastBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="title">
+ <string>Fast Queue</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout161</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_fastRate</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Lines</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Bytes</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_fastType</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_fastInterval</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ <property name="lineStep">
+ <number>1</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout237</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>m_textLabel3_2_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Age:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_fastAge</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_fastCount</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_textLabel3_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Count:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout236</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Lines:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Bytes:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_fastLines</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_fastBytes</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_globalBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="title">
+ <string>All Queues</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout88</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel13</cstring>
+ </property>
+ <property name="text">
+ <string>Received:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>m_recvBytes</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_globalLines</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>Bytes (Raw):</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel4_3</cstring>
+ </property>
+ <property name="text">
+ <string>Bytes (Encoded):</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_globalBytes</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>m_srverBytes</cstring>
+ </property>
+ <property name="text">
+ <string>888</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Lines:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/konversation/src/quickbutton.cpp b/konversation/src/quickbutton.cpp
new file mode 100644
index 0000000..004e857
--- /dev/null
+++ b/konversation/src/quickbutton.cpp
@@ -0,0 +1,38 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Wed Feb 6 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "quickbutton.h"
+
+
+QuickButton::QuickButton(const QString &label,const QString &newDefinition,QWidget* parent) :
+QPushButton::QPushButton(label,parent)
+{
+ setDefinition(newDefinition);
+ connect(this,SIGNAL (clicked()),this,SLOT (wasClicked()) );
+}
+
+QuickButton::~QuickButton()
+{
+}
+
+void QuickButton::wasClicked()
+{
+ emit clicked(definition);
+}
+
+void QuickButton::setDefinition(const QString &newDefinition)
+{
+ definition=newDefinition;
+}
+
+#include "quickbutton.moc"
diff --git a/konversation/src/quickbutton.h b/konversation/src/quickbutton.h
new file mode 100644
index 0000000..02a1040
--- /dev/null
+++ b/konversation/src/quickbutton.h
@@ -0,0 +1,42 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Wed Feb 6 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef QUICKBUTTON_H
+#define QUICKBUTTON_H
+
+#include <qpushbutton.h>
+
+
+class QuickButton : public QPushButton
+{
+ Q_OBJECT
+
+ public:
+ QuickButton(const QString &label,const QString &newDefinition,QWidget* parent);
+ ~QuickButton();
+
+ void setDefinition(const QString &newDefinition);
+
+ signals:
+ void clicked(int);
+ void clicked(const QString &definition);
+
+ public slots:
+ void wasClicked();
+
+ protected:
+ int id;
+
+ QString definition;
+};
+#endif
diff --git a/konversation/src/quickbuttons_preferences.cpp b/konversation/src/quickbuttons_preferences.cpp
new file mode 100644
index 0000000..3dabf68
--- /dev/null
+++ b/konversation/src/quickbuttons_preferences.cpp
@@ -0,0 +1,262 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#include "quickbuttons_preferences.h"
+#include "config/preferences.h"
+
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qheader.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <klineedit.h>
+#include <klistview.h>
+
+
+QuickButtons_Config::QuickButtons_Config(QWidget* parent, const char* name)
+ : QuickButtons_ConfigUI(parent, name)
+{
+ // reset flag to defined state (used to block signals when just selecting a new item)
+ m_newItemSelected=false;
+
+ // populate listview
+ loadSettings();
+
+ // make items react to drag & drop
+ buttonListView->setSorting(-1,false);
+ buttonListView->header()->setMovingEnabled(false);
+
+ connect(buttonListView,SIGNAL (selectionChanged(QListViewItem*)),this,SLOT (entrySelected(QListViewItem*)) );
+ connect(buttonListView,SIGNAL (clicked(QListViewItem*)),this,SLOT (entrySelected(QListViewItem*)) );
+ connect(buttonListView,SIGNAL (moved()),this,SIGNAL (modified()) );
+
+ connect(nameInput,SIGNAL (textChanged(const QString&)),this,SLOT (nameChanged(const QString&)) );
+ connect(actionInput,SIGNAL (textChanged(const QString&)),this,SLOT (actionChanged(const QString&)) );
+
+ connect(newButton,SIGNAL (clicked()),this,SLOT (addEntry()));
+ connect(removeButton,SIGNAL (clicked()),this,SLOT (removeEntry()));
+}
+
+QuickButtons_Config::~QuickButtons_Config()
+{
+}
+
+void QuickButtons_Config::loadSettings()
+{
+ setButtonsListView(Preferences::quickButtonList());
+
+ // remember button list for hasChanged()
+ m_oldButtonList=Preferences::quickButtonList();
+}
+
+// fill listview with button definitions
+void QuickButtons_Config::setButtonsListView(const QStringList &buttonList)
+{
+ // clear listView
+ buttonListView->clear();
+ // go through the list
+ for(unsigned int index=buttonList.count();index!=0;index--)
+ {
+ // get button definition
+ QString definition=buttonList[index-1];
+ // cut definition apart in name and action, and create a new listview item
+ new KListViewItem(buttonListView,definition.section(',',0,0),definition.section(',',1));
+ } // for
+ buttonListView->setSelected(buttonListView->firstChild(), true);
+}
+
+// save quick buttons to configuration
+void QuickButtons_Config::saveSettings()
+{
+ // get configuration object
+ KConfig* config=kapp->config();
+
+ // delete all buttons
+ config->deleteGroup("Button List");
+ // create new empty button group
+ config->setGroup("Button List");
+
+ // create empty list
+ QStringList newList=currentButtonList();
+
+ // check if there are any quick buttons in the list view
+ if(newList.count())
+ {
+ // go through all buttons and save them into the configuration
+ for(unsigned int index=0;index<newList.count();index++)
+ {
+ // write the current button's name and definition
+ config->writeEntry(QString("Button%1").arg(index),newList[index]);
+ } // for
+ }
+ // if there were no buttons at all, write a dummy entry to prevent KConfigXT from "optimizing"
+ // the group out, which would in turn make konvi restore the default buttons
+ else
+ config->writeEntry("Empty List",QString());
+
+ // set internal button list
+ Preferences::setQuickButtonList(newList);
+
+ // remember button list for hasChanged()
+ m_oldButtonList=newList;
+}
+
+void QuickButtons_Config::restorePageToDefaults()
+{
+ setButtonsListView(Preferences::defaultQuickButtonList());
+}
+
+QStringList QuickButtons_Config::currentButtonList()
+{
+ // get first item of the button listview
+ QListViewItem* item=buttonListView->firstChild();
+ // create empty list
+ QStringList newList;
+
+ // go through all items and save them into the configuration
+ while(item)
+ {
+ // remember button in internal list
+ newList.append(item->text(0)+','+item->text(1));
+ // get next item in the listview
+ item=item->itemBelow();
+ } // while
+
+ // return list
+ return newList;
+}
+
+bool QuickButtons_Config::hasChanged()
+{
+ return(m_oldButtonList!=currentButtonList());
+}
+
+// slots
+
+// what to do when the user selects an item
+void QuickButtons_Config::entrySelected(QListViewItem* quickButtonEntry)
+{
+ // play it safe, assume disabling all widgets first
+ bool enabled=false;
+
+ // check if there really was an item selected
+ if(quickButtonEntry)
+ {
+ // remember to enable the editing widgets
+ enabled=true;
+
+ // tell the editing widgets not to emit modified() on signals now
+ m_newItemSelected=true;
+ // update editing widget contents
+ nameInput->setText(quickButtonEntry->text(0));
+ actionInput->setText(quickButtonEntry->text(1));
+ // re-enable modified() signal on text changes in edit widgets
+ m_newItemSelected=false;
+ }
+ // enable or disable editing widgets
+ removeButton->setEnabled(enabled);
+ nameLabel->setEnabled(enabled);
+ nameInput->setEnabled(enabled);
+ actionLabel->setEnabled(enabled);
+ actionInput->setEnabled(enabled);
+}
+
+// what to do when the user change the name of a quick button
+void QuickButtons_Config::nameChanged(const QString& newName)
+{
+ // get possible first selected item
+ QListViewItem* item=buttonListView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // rename item
+ item->setText(0,newName);
+ // tell the config system that something has changed
+ if(!m_newItemSelected) emit modified();
+ }
+}
+
+// what to do when the user change the action definition of a quick button
+void QuickButtons_Config::actionChanged(const QString& newAction)
+{
+ // get possible first selected item
+ QListViewItem* item=buttonListView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // rename item
+ item->setText(1,newAction);
+ // tell the config system that something has changed
+ if(!m_newItemSelected) emit modified();
+ }
+}
+
+// add button pressed
+void QuickButtons_Config::addEntry()
+{
+ // add new item at the bottom of list view
+ KListViewItem* newItem=new KListViewItem(buttonListView,buttonListView->lastChild(),i18n("New"),QString());
+ // if successful ...
+ if(newItem)
+ {
+ // select new item and make it the current one
+ buttonListView->setSelected(newItem,true);
+ buttonListView->setCurrentItem(newItem);
+ // set input focus on item name edit
+ nameInput->setFocus();
+ // select all text to make overwriting easier
+ nameInput->selectAll();
+ // tell the config system that something has changed
+ emit modified();
+ }
+}
+
+// remove button pressed
+void QuickButtons_Config::removeEntry()
+{
+ // get possible first selected item
+ QListViewItem* item=buttonListView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // get item below the current one
+ QListViewItem* nextItem=item->itemBelow();
+ // if there was none, get the one above
+ if(!nextItem) nextItem=item->itemAbove();
+
+ // remove the item from the list
+ delete item;
+
+ // check if we found the next item
+ if(nextItem)
+ {
+ // select the item and make it the current ite,
+ buttonListView->setSelected(nextItem,true);
+ buttonListView->setCurrentItem(nextItem);
+ }
+ else
+ {
+ // no next item found, this means the list is empty
+ entrySelected(0);
+ }
+ // tell the config system that somethig has changed
+ emit modified();
+ }
+}
+
+#include "quickbuttons_preferences.moc"
diff --git a/konversation/src/quickbuttons_preferences.h b/konversation/src/quickbuttons_preferences.h
new file mode 100644
index 0000000..12a9ac0
--- /dev/null
+++ b/konversation/src/quickbuttons_preferences.h
@@ -0,0 +1,53 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef QUICKBUTTONSCONFIG_H
+#define QUICKBUTTONSCONFIG_H
+
+#include "quickbuttons_preferencesui.h"
+#include "konvisettingspage.h"
+
+
+class QuickButtons_Config : public QuickButtons_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit QuickButtons_Config(QWidget* parent, const char* name=NULL);
+ ~QuickButtons_Config();
+
+ virtual void saveSettings();
+ virtual void loadSettings();
+ virtual void restorePageToDefaults();
+
+ virtual bool hasChanged();
+
+ signals:
+ void modified();
+
+ protected slots:
+ void entrySelected(QListViewItem* quickButtonEntry);
+ void nameChanged(const QString& newName);
+ void actionChanged(const QString& newAction);
+ void addEntry();
+ void removeEntry();
+
+ protected:
+ void setButtonsListView(const QStringList &buttonList);
+
+ bool m_newItemSelected;
+
+ QStringList m_oldButtonList;
+ QStringList currentButtonList();
+};
+
+#endif
diff --git a/konversation/src/quickbuttons_preferencesui.ui b/konversation/src/quickbuttons_preferencesui.ui
new file mode 100644
index 0000000..64377dd
--- /dev/null
+++ b/konversation/src/quickbuttons_preferencesui.ui
@@ -0,0 +1,222 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>QuickButtons_ConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>QuickButtons_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>439</width>
+ <height>351</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListView" row="0" column="0">
+ <column>
+ <property name="text">
+ <string>Button Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Button Action</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>buttonListView</cstring>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>false</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>actionInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>nameInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>actionLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Button action:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>nameLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Button name:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Available Placeholders</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>placeholderHelpLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>%o: Current nickname
+%c: Current channel
+%K: Server password
+%u: List of selected nicknames
+%s&lt;term&gt;%: term used to separate nicknames in %u
+%n: Send command directly to the server instead of your input line</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>newButton</cstring>
+ </property>
+ <property name="text">
+ <string>New</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>230</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>buttonListView</tabstop>
+ <tabstop>nameInput</tabstop>
+ <tabstop>actionInput</tabstop>
+ <tabstop>newButton</tabstop>
+ <tabstop>removeButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/quickconnectdialog.cpp b/konversation/src/quickconnectdialog.cpp
new file mode 100644
index 0000000..a8aacae
--- /dev/null
+++ b/konversation/src/quickconnectdialog.cpp
@@ -0,0 +1,108 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Dialog for quick connection to an IRC network without adding a server in the Server List.
+ begin: Sat June 05 2004
+ copyright: (C) 2004 by Michael Goettsche
+ email: mail@tuxipuxi.de
+*/
+
+#include "quickconnectdialog.h"
+#include "konversationapplication.h"
+
+#include <qlayout.h>
+#include <qwhatsthis.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+
+#include <klineedit.h>
+#include <klocale.h>
+
+
+QuickConnectDialog::QuickConnectDialog(QWidget *parent)
+:KDialogBase(parent, "quickconnect", true, i18n("Quick Connect"),
+KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true)
+{
+ QWidget* page = new QWidget(this);
+ setMainWidget(page);
+
+ QGridLayout* layout = new QGridLayout(page, 2, 4);
+ layout->setSpacing(spacingHint());
+ layout->setColStretch(1, 10);
+
+ QLabel* hostNameLabel = new QLabel(i18n("&Server host:"), page);
+ QString hostNameWT = i18n("Enter the host of the network here.");
+ QWhatsThis::add(hostNameLabel, hostNameWT);
+ hostNameInput = new KLineEdit(page);
+ QWhatsThis::add(hostNameInput, hostNameWT);
+ hostNameLabel->setBuddy(hostNameInput);
+
+ QLabel* portLabel = new QLabel(i18n("&Port:"), page);
+ QString portWT = i18n("The port that the IRC server is using.");
+ QWhatsThis::add(portLabel, portWT);
+ portInput = new KLineEdit("6667", page );
+ QWhatsThis::add(portInput, portWT);
+ portLabel->setBuddy(portInput);
+
+ QLabel* nickLabel = new QLabel(i18n("&Nick:"), page);
+ QString nickWT = i18n("The nick you want to use.");
+ QWhatsThis::add(nickLabel, nickWT);
+ nickInput = new KLineEdit(Preferences::nickname(0), page);
+ QWhatsThis::add(nickInput, nickWT);
+ nickLabel->setBuddy(nickInput);
+
+ QLabel* passwordLabel = new QLabel(i18n("P&assword:"), page);
+ QString passwordWT = i18n("If the IRC server requires a password, enter it here (most servers do not require a password.)");
+ QWhatsThis::add(passwordLabel, passwordWT);
+ passwordInput = new KLineEdit(page);
+ QWhatsThis::add(passwordInput, passwordWT);
+ passwordLabel->setBuddy(passwordInput);
+
+ sslCheckBox = new QCheckBox(page, "sslCheckBox");
+ sslCheckBox->setText(i18n("&Use SSL"));
+
+ layout->addWidget(hostNameLabel, 0, 0);
+ layout->addWidget(hostNameInput, 0, 1);
+ layout->addWidget(portLabel, 0, 2);
+ layout->addWidget(portInput, 0, 3);
+
+ layout->addWidget(nickLabel, 1, 0);
+ layout->addWidget(nickInput, 1, 1);
+ layout->addWidget(passwordLabel, 1, 2);
+ layout->addWidget(passwordInput, 1, 3);
+
+ layout->addWidget(sslCheckBox, 2, 0);
+
+ hostNameInput->setFocus();
+
+ setButtonOK(KGuiItem(i18n("C&onnect"),"connect_creating",i18n("Connect to the server")));
+}
+
+QuickConnectDialog::~QuickConnectDialog()
+{
+}
+
+void QuickConnectDialog::slotOk()
+{
+ if (!hostNameInput->text().isEmpty() &&
+ !portInput->text().isEmpty() &&
+ !nickInput->text().isEmpty())
+ {
+
+ emit connectClicked(Konversation::PromptToReuseConnection,
+ hostNameInput->text().stripWhiteSpace(),
+ portInput->text(),
+ passwordInput->text(),
+ nickInput->text(),
+ "",
+ sslCheckBox->isChecked());
+ delayedDestruct();
+ }
+}
+
+#include "quickconnectdialog.moc"
diff --git a/konversation/src/quickconnectdialog.h b/konversation/src/quickconnectdialog.h
new file mode 100644
index 0000000..6d96941
--- /dev/null
+++ b/konversation/src/quickconnectdialog.h
@@ -0,0 +1,54 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Dialog for quick connection to an IRC network without adding a server in the Server List.
+ begin: Sat June 05 2004
+ copyright: (C) 2004 by Michael Goettsche
+ email: mail@tuxipuxi.de
+*/
+
+#ifndef QUICKCONNECTDIALOG_H
+#define QUICKCONNECTDIALOG_H
+
+#include "common.h"
+
+#include <kdialogbase.h>
+
+
+class QCheckBox;
+class KLineEdit;
+
+class QuickConnectDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ explicit QuickConnectDialog(QWidget* parent=0);
+ ~QuickConnectDialog();
+
+ signals:
+ void connectClicked(Konversation::ConnectionFlag flag,
+ const QString& hostName,
+ const QString& port,
+ const QString& password,
+ const QString& nick,
+ const QString& channel,
+ bool useSSL
+ );
+
+ protected slots:
+ void slotOk();
+
+ protected:
+ KLineEdit* hostNameInput;
+ KLineEdit* portInput;
+ KLineEdit* passwordInput;
+ KLineEdit* nickInput;
+ QCheckBox* sslCheckBox;
+};
+#endif
diff --git a/konversation/src/rawlog.cpp b/konversation/src/rawlog.cpp
new file mode 100644
index 0000000..30d3d9b
--- /dev/null
+++ b/konversation/src/rawlog.cpp
@@ -0,0 +1,79 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ RawLog.cpp - provides a view to the raw protocol
+ begin: Tue Mar 18 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "rawlog.h"
+#include "channel.h"
+#include "ircview.h"
+#include "ircviewbox.h"
+#include "server.h"
+#include "konversationapplication.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+RawLog::RawLog(QWidget* parent) : ChatWindow(parent)
+{
+ setName(i18n("Raw Log"));
+ setType(ChatWindow::RawLog);
+ IRCViewBox* ircBox = new IRCViewBox(this, 0);
+ setTextView(ircBox->ircView()); // Server will be set later in setServer()
+}
+
+RawLog::~RawLog()
+{
+}
+
+void RawLog::childAdjustFocus()
+{
+}
+
+void RawLog::updateAppearance()
+{
+ getTextView()->unsetPalette();
+
+ if(Preferences::showBackgroundImage())
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ Preferences::backgroundImage());
+ }
+ else
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ QString());
+ }
+
+ if (Preferences::customTextFont())
+ getTextView()->setFont(Preferences::textFont());
+ else
+ getTextView()->setFont(KGlobalSettings::generalFont());
+
+ ChatWindow::updateAppearance();
+}
+
+void RawLog::morphNotification()
+{
+ activateTabNotification(Konversation::tnfSystem);
+}
+
+bool RawLog::closeYourself()
+{
+ // make the server delete us so server can reset the pointer to us
+ m_server->closeRawLog();
+ return true;
+}
+
+bool RawLog::searchView() { return true; }
+
+#include "rawlog.moc"
diff --git a/konversation/src/rawlog.h b/konversation/src/rawlog.h
new file mode 100644
index 0000000..8f3e6e8
--- /dev/null
+++ b/konversation/src/rawlog.h
@@ -0,0 +1,40 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ RawLog.h - provides a view to the raw protocol
+ begin: Die M� 18 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef _RAWLOG_H_
+#define _RAWLOG_H_
+
+#include "chatwindow.h"
+
+
+class RawLog : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit RawLog(QWidget* parent);
+ ~RawLog();
+
+ virtual bool closeYourself();
+ virtual bool searchView();
+
+ public slots:
+ void updateAppearance();
+ void morphNotification();
+
+ protected:
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+};
+#endif
diff --git a/konversation/src/replycodes.h b/konversation/src/replycodes.h
new file mode 100644
index 0000000..df9f1e6
--- /dev/null
+++ b/konversation/src/replycodes.h
@@ -0,0 +1,160 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Fri Jan 25 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef REPLYCODES_H
+#define REPLYCODES_H
+
+#define RPL_WELCOME 001
+#define RPL_YOURHOST 002
+#define RPL_CREATED 003
+#define RPL_MYINFO 004
+#define RPL_BOUNCE 005 // old RFC1459 definition
+#define RPL_ISUPPORT 005 // new DALnet definition
+
+#define RPL_TRACELINK 200
+#define RPL_TRACECONNECTING 201
+#define RPL_TRACEHANDSHAKE 202
+#define RPL_TRACEUNKNOWN 203
+#define RPL_TRACEOPERATOR 204
+#define RPL_TRACEUSER 205
+#define RPL_TRACESERVER 206
+#define RPL_TRACENEWTYPE 208
+#define RPL_STATSLINKINFO 211
+#define RPL_STATSCOMMANDS 212
+#define RPL_STATSCLINE 213
+#define RPL_STATSNLINE 214
+#define RPL_STATSILINE 215
+#define RPL_STATSKLINE 216
+#define RPL_STATSYLINE 218
+#define RPL_ENDOFSTATS 219
+#define RPL_UMODEIS 221
+#define RPL_STATSLLINE 241
+#define RPL_STATSUPTIME 242
+#define RPL_STATSOLINE 243
+#define RPL_STATSHLINE 244
+#define RPL_HIGHCONNECTCOUNT 250
+#define RPL_LUSERCLIENT 251
+#define RPL_LUSEROP 252
+#define RPL_LUSERUNKNOWN 253
+#define RPL_LUSERCHANNELS 254
+#define RPL_LUSERME 255
+#define RPL_ADMINME 256
+#define RPL_ADMINLOC1 257
+#define RPL_ADMINLOC2 258
+#define RPL_ADMINEMAIL 259
+#define RPL_TRACELOG 261
+#define RPL_LOCALUSERS 265
+#define RPL_GLOBALUSERS 266
+#define RPL_CAPAB 290
+
+#define RPL_NONE 300
+#define RPL_AWAY 301
+#define RPL_USERHOST 302
+#define RPL_ISON 303
+#define RPL_UNAWAY 305
+#define RPL_NOWAWAY 306
+#define RPL_WHOISIDENTIFY 307 // DALNet NickServ
+#define RPL_WHOISHELPER 310
+#define RPL_WHOISUSER 311
+#define RPL_WHOISSERVER 312
+#define RPL_WHOISOPERATOR 313
+#define RPL_WHOWASUSER 314
+#define RPL_ENDOFWHO 315
+#define RPL_WHOISIDLE 317
+#define RPL_ENDOFWHOIS 318
+#define RPL_WHOISCHANNELS 319
+#define RPL_IDENTIFIED 320 // when you do /whois you can get [320] JohnFlux is an identified user
+#define RPL_LISTSTART 321
+#define RPL_LIST 322
+#define RPL_LISTEND 323
+#define RPL_CHANNELMODEIS 324
+#define RPL_CHANNELURLIS 328
+#define RPL_CHANNELCREATED 329
+#define RPL_WHOISACCOUNT 330
+#define RPL_NOTOPIC 331
+#define RPL_TOPIC 332
+#define RPL_TOPICSETBY 333 // Extended ircd
+#define RPL_WHOISACTUALLY 338
+#define RPL_INVITING 341
+#define RPL_SUMMONING 342
+#define RPL_VERSION 351
+#define RPL_WHOREPLY 352
+#define RPL_NAMREPLY 353
+#define RPL_LINKS 364
+#define RPL_ENDOFLINKS 365
+#define RPL_ENDOFNAMES 366
+#define RPL_BANLIST 367
+#define RPL_ENDOFBANLIST 368
+#define RPL_ENDOFWHOWAS 369
+#define RPL_INFO 371
+#define RPL_MOTD 372
+#define RPL_ENDOFINFO 374
+#define RPL_MOTDSTART 375
+#define RPL_ENDOFMOTD 376
+#define RPL_YOUREOPER 381
+#define RPL_REHASHING 382
+#define RPL_TIME 391
+#define RPL_USERSSTART 392
+#define RPL_USERS 393
+#define RPL_ENDOFUSERS 394
+#define RPL_NOUSERS 395
+
+#define ERR_NOSUCHNICK 401
+#define ERR_NOSUCHSERVER 402
+#define ERR_NOSUCHCHANNEL 403
+#define ERR_CANNOTSENDTOCHAN 404
+#define ERR_TOOMANYCHANNELS 405
+#define ERR_WASNOSUCHNICK 406
+#define ERR_TOOMANYTARGETS 407
+#define ERR_NOORIGIN 409
+#define ERR_NORECIPIENT 411
+#define ERR_NOTEXTTOSEND 412
+#define ERR_NOTOPLEVEL 413
+#define ERR_WILDTOPLEVEL 414
+#define ERR_UNKNOWNCOMMAND 421
+#define ERR_NOMOTD 422
+#define ERR_NOADMININFO 423
+#define ERR_FILEERROR 424
+#define ERR_NONICKNAMEGIVEN 431
+#define ERR_ERRONEUSNICKNAME 432
+#define ERR_NICKNAMEINUSE 433
+#define ERR_NICKCOLLISION 436
+#define ERR_UNAVAILRESOURCE 437
+#define ERR_USERNOTINCHANNEL 441
+#define ERR_NOTONCHANNEL 442
+#define ERR_USERONCHANNEL 443
+#define ERR_NOLOGIN 444
+#define ERR_SUMMONDISABLED 445
+#define ERR_USERSDISABLED 446
+#define ERR_NOTREGISTERED 451
+#define ERR_NEEDMOREPARAMS 461
+#define ERR_ALREADYREGISTRED 462
+#define ERR_NOPERMFORHOST 463
+#define ERR_PASSWDMISMATCH 464
+#define ERR_YOUREBANNEDCREEP 465
+#define ERR_KEYSET 467
+#define ERR_CHANNELISFULL 471
+#define ERR_UNKNOWNMODE 472
+#define ERR_INVITEONLYCHAN 473
+#define ERR_BANNEDFROMCHAN 474
+#define ERR_BADCHANNELKEY 475
+#define ERR_NOCHANMODES 477
+#define ERR_NOPRIVILEGES 481
+#define ERR_CHANOPRIVSNEEDED 482
+#define ERR_CANTKILLSERVER 483
+#define ERR_NOOPERHOST 491
+#define ERR_UMODEUNKNOWNFLAG 501
+#define ERR_USERSDONTMATCH 502
+
+#endif // REPLYCODES_H
+
diff --git a/konversation/src/scriptlauncher.cpp b/konversation/src/scriptlauncher.cpp
new file mode 100644
index 0000000..a81965e
--- /dev/null
+++ b/konversation/src/scriptlauncher.cpp
@@ -0,0 +1,74 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2003 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+*/
+
+#include "scriptlauncher.h"
+#include "channel.h"
+#include "konversationapplication.h"
+#include "server.h"
+
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kprocess.h>
+#include <dcopclient.h>
+
+
+ScriptLauncher::ScriptLauncher(Server* server)
+: QObject(server)
+{
+ m_server = server;
+}
+
+ScriptLauncher::~ScriptLauncher()
+{
+}
+
+void ScriptLauncher::launchScript(const QString& target, const QString &parameter)
+{
+ KStandardDirs kstddir;
+ // QString scriptPath(kstddir.saveLocation("data",QString("konversation/scripts")));
+ KProcess process;
+
+ // send the script all the information it will need
+ QStringList parameterList=QStringList::split(' ',parameter);
+
+ // find script path (could be installed for all users in $KDEDIR/share/apps/ or
+ // for one user alone in $HOME/.kde/share/apps/
+ QString scriptPath(kstddir.findResource("data","konversation/scripts/"+parameterList[0]));
+
+ process << scriptPath // script path and name
+ << kapp->dcopClient()->appId() // our dcop port
+ << QString::number(m_server->connectionId()) // the server we are connected to
+ << target; // the target where the call came from
+
+ // send remaining parameters to the script
+ for(unsigned int index=1;index<parameterList.count();index++)
+ process << parameterList[index];
+
+ QFileInfo fileInfo(scriptPath);
+
+ process.setWorkingDirectory(fileInfo.dirPath());
+ if(process.start()==false)
+ {
+ QFile file(parameterList[0]);
+ if(!file.exists()) emit scriptNotFound(file.name());
+ else emit scriptExecutionError(file.name());
+ }
+
+ // to free the script's stdin, otherwise backticks won't work
+ process.detach();
+}
+
+#include "scriptlauncher.moc"
diff --git a/konversation/src/scriptlauncher.h b/konversation/src/scriptlauncher.h
new file mode 100644
index 0000000..8350077
--- /dev/null
+++ b/konversation/src/scriptlauncher.h
@@ -0,0 +1,39 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2003 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+*/
+
+#include <qobject.h>
+
+#ifndef SCRIPTLAUNCHER_H
+#define SCRIPTLAUNCHER_H
+
+
+class Server;
+
+class ScriptLauncher : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit ScriptLauncher(Server* server);
+ ~ScriptLauncher();
+
+ signals:
+ void scriptNotFound(const QString& name);
+ void scriptExecutionError(const QString& name);
+
+ public slots:
+ void launchScript(const QString& target, const QString& parameter);
+
+ protected:
+ Server* m_server;
+};
+#endif
diff --git a/konversation/src/searchbar.cpp b/konversation/src/searchbar.cpp
new file mode 100644
index 0000000..992086a
--- /dev/null
+++ b/konversation/src/searchbar.cpp
@@ -0,0 +1,245 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Renchi Raju <renchi@pooh.tam.uiuc.edu>
+ Copyright (C) 2006 Peter Simonsson <psn@linux.se>
+*/
+
+#include "searchbar.h"
+
+#include <qcheckbox.h>
+#include <qtimer.h>
+#include <qpalette.h>
+#include <qaccel.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qobjectlist.h>
+#include <qtoolbutton.h>
+#include <qpopupmenu.h>
+#include <qwidgetstack.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <klocale.h>
+
+#define SEARCH_FORWARD_MENU 1
+#define MATCH_CASE_MENU 2
+#define WHOLE_WORDS_ONLY_MENU 3
+#define FROM_CURSOR_MENU 4
+
+
+SearchBar::SearchBar(QWidget* parent)
+: SearchBarBase(parent)
+{
+ m_searchFoward = false;
+ m_matchCase = false;
+ m_wholeWords = false;
+ m_fromCursor = false;
+
+ setFocusProxy(m_searchEdit);
+ KIconLoader* iconLoader = kapp->iconLoader();
+ m_closeButton->setIconSet(iconLoader->loadIconSet("fileclose", KIcon::Toolbar, 16));
+ m_findNextButton->setIconSet(iconLoader->loadIconSet("up", KIcon::Toolbar, 16));
+ m_findPreviousButton->setIconSet(iconLoader->loadIconSet("down", KIcon::Toolbar, 16));
+ m_statusPixLabel->hide();
+ m_statusTextLabel->hide();
+
+ m_timer = new QTimer(this);
+
+ QAccel* accel = new QAccel(this);
+ accel->connectItem( accel->insertItem(Qt::Key_Escape), this, SLOT(hide()));
+
+ connect(m_timer, SIGNAL(timeout()), SLOT(slotFind()));
+ connect(m_searchEdit, SIGNAL(textChanged(const QString&)), SLOT(slotTextChanged()));
+ connect(m_searchEdit, SIGNAL(returnPressed()), SLOT(slotFindNext()));
+ connect(m_findNextButton, SIGNAL(clicked()), SLOT(slotFindNext()));
+ connect(m_findPreviousButton, SIGNAL(clicked()), SLOT(slotFindPrevious()));
+ connect(m_closeButton, SIGNAL(clicked()), SLOT(hide()));
+ connect(m_optionsButton, SIGNAL(clicked()), this, SLOT(showOptionsMenu()));
+
+ m_optionsMenu = new QPopupMenu(m_optionsButton, "options_menu");
+ m_optionsMenu->setCheckable(true);
+ m_optionsMenu->insertItem(i18n("Find Forward"), this, SLOT(toggleSearchFoward()), 0, SEARCH_FORWARD_MENU);
+ m_optionsMenu->insertItem(i18n("Case Sensitive"), this, SLOT(toggleMatchCase()), 0, MATCH_CASE_MENU);
+ m_optionsMenu->insertItem(i18n("Whole Words Only"), this, SLOT(toggleWholeWords()), 0, WHOLE_WORDS_ONLY_MENU);
+ m_optionsMenu->insertItem(i18n("From Cursor"), this, SLOT(toggleFromCursor()), 0, FROM_CURSOR_MENU);
+
+ m_optionsButton->setPopup(m_optionsMenu);
+}
+
+SearchBar::~SearchBar()
+{
+}
+
+void SearchBar::showEvent(QShowEvent *e)
+{
+ SearchBarBase::showEvent(e);
+ m_searchEdit->selectAll();
+}
+
+bool SearchBar::focusedChild()
+{
+ QObjectList *l = queryList("QWidget", 0,0, true);
+ QObjectListIt it( *l );
+ QObject *obj;
+ bool has=false;
+
+ while ((obj = it.current()) != 0)
+ {
+ ++it;
+ if (((QWidget*)obj)->hasFocus())
+ {
+ has=true;
+ break;
+ }
+ }
+ delete l;
+ return has;
+}
+
+void SearchBar::hide()
+{
+ m_timer->stop();
+ SearchBarBase::hide();
+
+ if (focusedChild())
+ emit hidden();
+}
+
+void SearchBar::slotTextChanged()
+{
+ m_timer->start(50, true);
+}
+
+void SearchBar::slotFind()
+{
+ if (m_searchEdit->text().isEmpty())
+ {
+ m_searchEdit->unsetPalette();
+ m_findNextButton->setEnabled(false);
+ m_findPreviousButton->setEnabled(false);
+ setStatus(QPixmap(), "");
+ return;
+ }
+
+ emit signalSearchChanged(m_searchEdit->text());
+}
+
+void SearchBar::slotFindNext()
+{
+ if (m_searchEdit->text().isEmpty())
+ {
+ m_searchEdit->unsetPalette();
+ m_findNextButton->setEnabled(false);
+ m_findPreviousButton->setEnabled(false);
+ setStatus(QPixmap(), "");
+ return;
+ }
+
+ emit signalSearchNext();
+}
+
+void SearchBar::slotFindPrevious()
+{
+ if (m_searchEdit->text().isEmpty())
+ {
+ m_searchEdit->unsetPalette();
+ m_findNextButton->setEnabled(false);
+ m_findPreviousButton->setEnabled(false);
+ setStatus(QPixmap(), "");
+ return;
+ }
+
+ emit signalSearchPrevious();
+}
+
+void SearchBar::setHasMatch(bool value)
+{
+ QPalette pal = m_searchEdit->palette();
+ pal.setColor(QPalette::Active, QColorGroup::Base, value ? Qt::green : Qt::red);
+ m_searchEdit->setPalette(pal);
+ m_findNextButton->setEnabled(value);
+ m_findPreviousButton->setEnabled(value);
+}
+
+void SearchBar::setStatus(const QPixmap& pix, const QString& text)
+{
+ if(!text.isEmpty()) {
+ m_statusPixLabel->show();
+ m_statusTextLabel->show();
+ } else {
+ m_statusPixLabel->hide();
+ m_statusTextLabel->hide();
+ }
+
+ m_statusPixLabel->setPixmap(pix);
+ m_statusTextLabel->setText(text);
+}
+
+QString SearchBar::pattern() const
+{
+ return m_searchEdit->text();
+}
+
+bool SearchBar::searchForward() const
+{
+ return m_searchFoward;
+}
+
+bool SearchBar::caseSensitive() const
+{
+ return m_matchCase;
+}
+
+bool SearchBar::wholeWords() const
+{
+ return m_wholeWords;
+}
+
+bool SearchBar::fromCursor() const
+{
+ return m_fromCursor;
+}
+
+void SearchBar::toggleSearchFoward()
+{
+ m_searchFoward = !m_searchFoward;
+ m_optionsMenu->setItemChecked(SEARCH_FORWARD_MENU, m_searchFoward);
+ slotTextChanged();
+}
+
+void SearchBar::toggleMatchCase()
+{
+ m_matchCase = !m_matchCase;
+ m_optionsMenu->setItemChecked(MATCH_CASE_MENU, m_matchCase);
+ slotTextChanged();
+}
+
+void SearchBar::toggleWholeWords()
+{
+ m_wholeWords = !m_wholeWords;
+ m_optionsMenu->setItemChecked(WHOLE_WORDS_ONLY_MENU, m_wholeWords);
+ slotTextChanged();
+}
+
+void SearchBar::toggleFromCursor()
+{
+ m_fromCursor = !m_fromCursor;
+ m_optionsMenu->setItemChecked(FROM_CURSOR_MENU, m_fromCursor);
+ slotTextChanged();
+}
+
+void SearchBar::showOptionsMenu()
+{
+ m_optionsButton->openPopup();
+}
+
+#include "searchbar.moc"
diff --git a/konversation/src/searchbar.h b/konversation/src/searchbar.h
new file mode 100644
index 0000000..b831ffe
--- /dev/null
+++ b/konversation/src/searchbar.h
@@ -0,0 +1,83 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Renchi Raju <renchi@pooh.tam.uiuc.edu>
+ Copyright (C) 2006 Peter Simonsson <psn@linux.se>
+*/
+
+#ifndef SEARCHBAR_H
+#define SEARCHBAR_H
+
+#include "searchbarbase.h"
+
+/* TODO:
+ - Changing case-sensitivity and search-forward restarts search from beginning.
+ fix to continue search from current position
+ - figure out what "from cursor" and "whole words" means and is it important for
+ the konvi gods
+ */
+
+class QPopupMenu;
+
+class SearchBar : public SearchBarBase
+{
+ Q_OBJECT
+
+ public:
+ explicit SearchBar(QWidget* parent);
+ ~SearchBar();
+
+ void setHasMatch(bool value);
+ void setStatus(const QPixmap& pix, const QString& text);
+
+ QString pattern() const;
+
+ bool searchForward() const;
+ bool caseSensitive() const;
+ bool wholeWords() const;
+ bool fromCursor() const;
+
+ protected:
+ virtual void showEvent(QShowEvent* e);
+ bool focusedChild();
+
+ public slots:
+ virtual void hide();
+
+ private slots:
+ void slotTextChanged();
+ void slotFind();
+ void slotFindNext();
+ void slotFindPrevious();
+
+ void toggleSearchFoward();
+ void toggleMatchCase();
+ void toggleWholeWords();
+ void toggleFromCursor();
+
+ void showOptionsMenu();
+
+ signals:
+ void signalSearchChanged(const QString& pattern);
+ void signalSearchNext();
+ void signalSearchPrevious();
+ void signalPropertiesChanged();
+ void hidden();
+
+ private:
+ QTimer* m_timer;
+
+ QPopupMenu* m_optionsMenu;
+
+ bool m_searchFoward;
+ bool m_matchCase;
+ bool m_wholeWords;
+ bool m_fromCursor;
+};
+
+#endif /* SEARCHBAR_H */
diff --git a/konversation/src/searchbarbase.ui b/konversation/src/searchbarbase.ui
new file mode 100644
index 0000000..c86f6b1
--- /dev/null
+++ b/konversation/src/searchbarbase.ui
@@ -0,0 +1,131 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>SearchBarBase</class>
+<comment>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+</comment>
+<author>Copyright (C) 2006 Peter Simonsson &lt;psn@linux.se&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SearchBarBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>502</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QToolButton">
+ <property name="name">
+ <cstring>m_closeButton</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ <property name="textPosition">
+ <enum>BesideIcon</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Close</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>m_searchEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_statusPixLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_statusTextLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_findNextButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Find Ne&amp;xt</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_findPreviousButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Find Previous</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QToolButton">
+ <property name="name">
+ <cstring>m_optionsButton</cstring>
+ </property>
+ <property name="text">
+ <string>Options</string>
+ </property>
+ <property name="popupDelay">
+ <number>0</number>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ <property name="textPosition">
+ <enum>BesideIcon</enum>
+ </property>
+ </widget>
+ </hbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="4"/>
+<layoutfunctions spacing="KDialog::spacingHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/server.cpp b/konversation/src/server.cpp
new file mode 100644
index 0000000..8a25b22
--- /dev/null
+++ b/konversation/src/server.cpp
@@ -0,0 +1,3348 @@
+// -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*-
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005-2006 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eli J. MacKenzie <argonel at gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "server.h"
+#include "ircqueue.h"
+#include "query.h"
+#include "channel.h"
+#include "konversationapplication.h"
+#include "connectionmanager.h"
+#include "dcccommon.h"
+#include "dcctransferpanel.h"
+#include "dcctransferpanelitem.h"
+#include "dcctransfersend.h"
+#include "dcctransferrecv.h"
+#include "dccrecipientdialog.h"
+#include "nick.h"
+#include "irccharsets.h"
+#include "viewcontainer.h"
+#include "statuspanel.h"
+#include "rawlog.h"
+#include "channellistpanel.h"
+#include "scriptlauncher.h"
+#include "servergroupsettings.h"
+#include "addressbook.h"
+#include "serverison.h"
+#include "common.h"
+#include "notificationhandler.h"
+#include "blowfish.h"
+#include "dcctransfermanager.h"
+
+#include <qregexp.h>
+#include <qhostaddress.h>
+#include <qtextcodec.h>
+#include <qdatetime.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kresolver.h>
+#include <ksocketdevice.h>
+#include <kaction.h>
+#include <kstringhandler.h>
+#include <kdeversion.h>
+#include <kwin.h>
+#include <config.h>
+
+
+int Server::m_availableConnectionId = 0;
+
+Server::Server(QObject* parent, ConnectionSettings& settings) : QObject(parent)
+{
+ m_connectionId = m_availableConnectionId;
+ m_availableConnectionId++;
+
+ setConnectionSettings(settings);
+
+ m_connectionState = Konversation::SSNeverConnected;
+
+ for (int i=0;i<=_max_queue();i++)
+ {
+ QValueList<int> r=Preferences::queueRate(i);
+ IRCQueue *q=new IRCQueue(this, staticrates[i]); //FIXME these are supposed to be in the rc
+ m_queues.append(q);
+ }
+
+ m_processingIncoming = false;
+ m_identifyMsg = false;
+ m_autoIdentifyLock = false;
+ m_autoJoin = false;
+
+ m_nickIndices.clear();
+ m_nickIndices.append(0);
+
+ m_currentLag = -1;
+ m_rawLog = 0;
+ m_channelListPanel = 0;
+ m_serverISON = 0;
+ m_away = false;
+ m_socket = 0;
+ m_prevISONList = QStringList();
+ m_bytesReceived = 0;
+ m_encodedBytesSent=0;
+ m_bytesSent=0;
+ m_linesSent=0;
+ // TODO fold these into a QMAP, and these need to be reset to RFC values if this server object is reused.
+ m_serverNickPrefixModes = "ovh";
+ m_serverNickPrefixes = "@+%";
+ m_channelPrefixes = "#&";
+
+ setName(QString("server_" + settings.name()).ascii());
+
+ setNickname(settings.initialNick());
+ obtainNickInfo(getNickname());
+
+ m_statusView = getViewContainer()->addStatusView(this);
+
+ if (Preferences::rawLog())
+ addRawLog(false);
+
+ m_inputFilter.setServer(this);
+ m_outputFilter = new Konversation::OutputFilter(this);
+ m_scriptLauncher = new ScriptLauncher(this);
+
+ // don't delete items when they are removed
+ m_channelList.setAutoDelete(false);
+ // For /msg query completion
+ m_completeQueryPosition = 0;
+
+ updateAutoJoin(settings.initialChannel());
+
+ if (!getIdentity()->getShellCommand().isEmpty())
+ QTimer::singleShot(0, this, SLOT(doPreShellCommand()));
+ else
+ QTimer::singleShot(0, this, SLOT(connectToIRCServer()));
+
+ initTimers();
+
+ if (getIdentity()->getShellCommand().isEmpty())
+ connectSignals();
+}
+
+Server::~Server()
+{
+ //send queued messages
+ kdDebug() << "Server::~Server(" << getServerName() << ")" << endl;
+
+ // Delete helper object.
+ delete m_serverISON;
+ m_serverISON = 0;
+
+ // clear nicks online
+ emit nicksNowOnline(this,QStringList(),true);
+
+ // Make sure no signals get sent to a soon to be dying Server Window
+ if (m_socket)
+ {
+ m_socket->blockSignals(true);
+ m_socket->deleteLater();
+ }
+
+ if (m_statusView) delete m_statusView;
+
+ closeRawLog();
+ closeChannelListPanel();
+
+ m_channelList.setAutoDelete(true);
+ m_channelList.clear();
+
+ m_queryList.setAutoDelete(true);
+ m_queryList.clear();
+
+ // Delete all the NickInfos and ChannelNick structures.
+ m_allNicks.clear();
+
+ ChannelMembershipMap::ConstIterator it;
+
+ for ( it = m_joinedChannels.begin(); it != m_joinedChannels.end(); ++it )
+ delete it.data();
+ m_joinedChannels.clear();
+
+ for ( it = m_unjoinedChannels.begin(); it != m_unjoinedChannels.end(); ++it )
+ delete it.data();
+ m_unjoinedChannels.clear();
+
+ m_queryNicks.clear();
+
+ //Delete the queues
+ for (QValueVector<IRCQueue *>::iterator it=m_queues.begin(); it != m_queues.end(); ++it)
+ delete *it;
+
+ emit destroyed(m_connectionId);
+
+ kdDebug() << "~Server done" << endl;
+}
+
+//... so called to match the ChatWindow derivatives.
+bool Server::closeYourself(bool)
+{
+ QTimer::singleShot(0, m_statusView, SLOT(serverSaysClose()));
+ return true;
+}
+
+void Server::doPreShellCommand()
+{
+
+ QString command = getIdentity()->getShellCommand();
+ getStatusView()->appendServerMessage(i18n("Info"),"Running preconfigured command...");
+
+ connect(&m_preShellCommand,SIGNAL(processExited(KProcess*)), this, SLOT(preShellCommandExited(KProcess*)));
+
+ QStringList commandList = QStringList::split(" ",command);
+
+ for (QStringList::ConstIterator it = commandList.begin(); it != commandList.end(); ++it)
+ m_preShellCommand << *it;
+
+ if (!m_preShellCommand.start()) preShellCommandExited(NULL);
+}
+
+void Server::_fetchRates()
+{
+ for (int i=0;i<=_max_queue();i++)
+ {
+ QValueList<int> r=Preferences::queueRate(i);
+ staticrates[i]=IRCQueue::EmptyingRate(r[0], r[1]*1000,IRCQueue::EmptyingRate::RateType(r[2]));
+ }
+}
+
+void Server::_stashRates()
+{
+ for (int i=0;i<=_max_queue();i++)
+ {
+ QValueList<int> r;
+ r.append(staticrates[i].m_rate);
+ r.append(staticrates[i].m_interval/1000);
+ r.append(int(staticrates[i].m_type));
+ Preferences::setQueueRate(i, r);
+ }
+}
+
+void Server::_resetRates()
+{
+ for (int i=0;i<=_max_queue();i++)
+ {
+ Preferences::self()->queueRateItem(i)->setDefault();
+ QValueList<int> r=Preferences::queueRate(i);
+ staticrates[i]=IRCQueue::EmptyingRate(r[0], r[1]*1000,IRCQueue::EmptyingRate::RateType(r[2]));
+ }
+}
+
+void Server::initTimers()
+{
+ m_notifyTimer.setName("notify_timer");
+ m_incomingTimer.setName("incoming_timer");
+}
+
+void Server::connectSignals()
+{
+ // Timers
+ connect(&m_incomingTimer, SIGNAL(timeout()), this, SLOT(processIncomingData()));
+ connect(&m_notifyTimer, SIGNAL(timeout()), this, SLOT(notifyTimeout()));
+ connect(&m_pingResponseTimer, SIGNAL(timeout()), this, SLOT(updateLongPongLag()));
+
+ // OutputFilter
+ connect(getOutputFilter(), SIGNAL(requestDccSend()), this,SLOT(requestDccSend()));
+ connect(getOutputFilter(), SIGNAL(requestDccSend(const QString&)), this, SLOT(requestDccSend(const QString&)));
+ connect(getOutputFilter(), SIGNAL(multiServerCommand(const QString&, const QString&)),
+ this, SLOT(sendMultiServerCommand(const QString&, const QString&)));
+ connect(getOutputFilter(), SIGNAL(reconnectServer()), this, SLOT(reconnect()));
+ connect(getOutputFilter(), SIGNAL(disconnectServer()), this, SLOT(disconnect()));
+ connect(getOutputFilter(), SIGNAL(openDccSend(const QString &, KURL)), this, SLOT(addDccSend(const QString &, KURL)));
+ connect(getOutputFilter(), SIGNAL(openDccChat(const QString &)), this, SLOT(openDccChat(const QString &)));
+ connect(getOutputFilter(), SIGNAL(sendToAllChannels(const QString&)), this, SLOT(sendToAllChannels(const QString&)));
+ connect(getOutputFilter(), SIGNAL(banUsers(const QStringList&,const QString&,const QString&)),
+ this, SLOT(requestBan(const QStringList&,const QString&,const QString&)));
+ connect(getOutputFilter(), SIGNAL(unbanUsers(const QString&,const QString&)),
+ this, SLOT(requestUnban(const QString&,const QString&)));
+ connect(getOutputFilter(), SIGNAL(openRawLog(bool)), this, SLOT(addRawLog(bool)));
+ connect(getOutputFilter(), SIGNAL(closeRawLog()), this, SLOT(closeRawLog()));
+ connect(getOutputFilter(), SIGNAL(encodingChanged()), this, SLOT(updateEncoding()));
+
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ connect(getOutputFilter(), SIGNAL(connectTo(Konversation::ConnectionFlag, const QString&,
+ const QString&, const QString&, const QString&, const QString&, bool)),
+ konvApp->getConnectionManager(), SLOT(connectTo(Konversation::ConnectionFlag,
+ const QString&, const QString&, const QString&, const QString&, const QString&, bool)));
+
+ connect(konvApp->getDccTransferManager(), SIGNAL(newTransferQueued(DccTransfer*)),
+ this, SLOT(slotNewDccTransferItemQueued(DccTransfer*)));
+
+ connect(konvApp, SIGNAL(appearanceChanged()), this, SLOT(startNotifyTimer()));
+
+ // ViewContainer
+ connect(this, SIGNAL(showView(ChatWindow*)), getViewContainer(), SLOT(showView(ChatWindow*)));
+ connect(this, SIGNAL(addDccPanel()), getViewContainer(), SLOT(addDccPanel()));
+ connect(this, SIGNAL(addDccChat(const QString&,const QString&,const QStringList&,bool)),
+ getViewContainer(), SLOT(addDccChat(const QString&,const QString&,const QStringList&,bool)) );
+ connect(this, SIGNAL(serverLag(Server*, int)), getViewContainer(), SIGNAL(updateStatusBarLagLabel(Server*, int)));
+ connect(this, SIGNAL(tooLongLag(Server*, int)), getViewContainer(), SIGNAL(setStatusBarLagLabelTooLongLag(Server*, int)));
+ connect(this, SIGNAL(resetLag()), getViewContainer(), SIGNAL(resetStatusBarLagLabel()));
+ connect(getOutputFilter(), SIGNAL(showView(ChatWindow*)), getViewContainer(), SLOT(showView(ChatWindow*)));
+ connect(getOutputFilter(), SIGNAL(openKonsolePanel()), getViewContainer(), SLOT(addKonsolePanel()));
+ connect(getOutputFilter(), SIGNAL(openChannelList(const QString&, bool)), getViewContainer(), SLOT(openChannelList(const QString&, bool)));
+ connect(getOutputFilter(), SIGNAL(closeDccPanel()), getViewContainer(), SLOT(closeDccPanel()));
+ connect(getOutputFilter(), SIGNAL(addDccPanel()), getViewContainer(), SLOT(addDccPanel()));
+ connect(&m_inputFilter, SIGNAL(addDccChat(const QString&,const QString&,const QStringList&,bool)),
+ getViewContainer(), SLOT(addDccChat(const QString&,const QString&,const QStringList&,bool)) );
+
+ // Inputfilter
+ connect(&m_inputFilter, SIGNAL(welcome(const QString&)), this, SLOT(connectionEstablished(const QString&)));
+ connect(&m_inputFilter, SIGNAL(notifyResponse(const QString&)), this, SLOT(notifyResponse(const QString&)));
+ connect(&m_inputFilter, SIGNAL(startReverseDccSendTransfer(const QString&,const QStringList&)),
+ this, SLOT(startReverseDccSendTransfer(const QString&,const QStringList&)));
+ connect(&m_inputFilter, SIGNAL(addDccGet(const QString&, const QStringList&)),
+ this, SLOT(addDccGet(const QString&, const QStringList&)));
+ connect(&m_inputFilter, SIGNAL(resumeDccGetTransfer(const QString&, const QStringList&)),
+ this, SLOT(resumeDccGetTransfer(const QString&, const QStringList&)));
+ connect(&m_inputFilter, SIGNAL(resumeDccSendTransfer(const QString&, const QStringList&)),
+ this, SLOT(resumeDccSendTransfer(const QString&, const QStringList&)));
+ connect(&m_inputFilter, SIGNAL(userhost(const QString&,const QString&,bool,bool)),
+ this, SLOT(userhost(const QString&,const QString&,bool,bool)) );
+ connect(&m_inputFilter, SIGNAL(topicAuthor(const QString&,const QString&,QDateTime)),
+ this, SLOT(setTopicAuthor(const QString&,const QString&,QDateTime)) );
+ connect(&m_inputFilter, SIGNAL(endOfWho(const QString&)),
+ this, SLOT(endOfWho(const QString&)) );
+ connect(&m_inputFilter, SIGNAL(invitation(const QString&,const QString&)),
+ this,SLOT(invitation(const QString&,const QString&)) );
+ connect(&m_inputFilter, SIGNAL(addToChannelList(const QString&, int, const QString& )),
+ this, SLOT(addToChannelList(const QString&, int, const QString& )));
+
+ // Status View
+ connect(this, SIGNAL(serverOnline(bool)), getStatusView(), SLOT(serverOnline(bool)));
+
+ // Scripts
+ connect(getOutputFilter(), SIGNAL(launchScript(const QString&, const QString&)),
+ m_scriptLauncher, SLOT(launchScript(const QString&, const QString&)));
+ connect(m_scriptLauncher, SIGNAL(scriptNotFound(const QString&)),
+ this, SLOT(scriptNotFound(const QString&)));
+ connect(m_scriptLauncher, SIGNAL(scriptExecutionError(const QString&)),
+ this, SLOT(scriptExecutionError(const QString&)));
+
+ // Stats
+ connect(this, SIGNAL(sentStat(int, int)), SLOT(collectStats(int, int)));
+}
+
+int Server::getPort()
+{
+ return getConnectionSettings().server().port();
+}
+
+int Server::getLag() const
+{
+ return m_currentLag;
+}
+
+bool Server::getAutoJoin() const
+{
+ return m_autoJoin;
+}
+
+void Server::setAutoJoin(bool on)
+{
+ m_autoJoin = on;
+}
+
+void Server::preShellCommandExited(KProcess* proc)
+{
+
+ if (proc && proc->normalExit())
+ getStatusView()->appendServerMessage(i18n("Info"),"Process executed successfully!");
+ else
+ getStatusView()->appendServerMessage(i18n("Warning"),"There was a problem while executing the command!");
+
+ connectToIRCServer();
+ connectSignals();
+}
+
+void Server::connectToIRCServer()
+{
+ if (!isConnected())
+ {
+ updateConnectionState(Konversation::SSConnecting);
+
+ m_autoIdentifyLock = false;
+ m_ownIpByUserhost = QString();
+
+ resetQueues();
+
+ // This is needed to support server groups with mixed SSL and nonSSL servers
+ delete m_socket;
+ m_socket = 0;
+ resetNickSelection();
+
+ // connect() will do a async lookup too
+ if(!getConnectionSettings().server().SSLEnabled())
+ {
+ m_socket = new KNetwork::KBufferedSocket(QString(), QString(), 0L, "serverSocket");
+ connect(m_socket, SIGNAL(connected(const KResolverEntry&)), SLOT (ircServerConnectionSuccess()));
+ }
+ else
+ {
+ m_socket = new SSLSocket(getViewContainer()->getWindow(), 0L, "serverSSLSocket");
+ connect(m_socket, SIGNAL(sslInitDone()), SLOT(ircServerConnectionSuccess()));
+ connect(m_socket, SIGNAL(sslFailure(const QString&)), SIGNAL(sslInitFailure()));
+ connect(m_socket, SIGNAL(sslFailure(const QString&)), SLOT(sslError(const QString&)));
+ }
+
+ m_socket->enableWrite(false);
+
+ connect(m_socket, SIGNAL(hostFound()), SLOT(lookupFinished()));
+ connect(m_socket, SIGNAL(gotError(int)), SLOT(broken(int)) );
+ connect(m_socket, SIGNAL(readyRead()), SLOT(incoming()));
+ connect(m_socket, SIGNAL(closed()), SLOT(closed()));
+
+ m_socket->connect(getConnectionSettings().server().host(), QString::number(getConnectionSettings().server().port()));
+
+ // set up the connection details
+ setPrefixes(m_serverNickPrefixModes, m_serverNickPrefixes);
+ getStatusView()->appendServerMessage(i18n("Info"),i18n("Looking for server %1:%2...")
+ .arg(getConnectionSettings().server().host())
+ .arg(getConnectionSettings().server().port()));
+ // reset InputFilter (auto request info, /WHO request info)
+ m_inputFilter.reset();
+ }
+ else
+ kdDebug() << "connectToIRCServer() called while already connected: This should never happen." << endl;
+}
+
+void Server::showSSLDialog()
+{
+ SSLSocket* sslsocket = dynamic_cast<SSLSocket*>(m_socket);
+
+ if (sslsocket) sslsocket->showInfoDialog();
+}
+
+// set available channel types according to 005 RPL_ISUPPORT
+void Server::setChannelTypes(const QString &pre)
+{
+ m_channelPrefixes = pre;
+}
+
+QString Server::getChannelTypes() const
+{
+ return m_channelPrefixes;
+}
+
+// set user mode prefixes according to non-standard 005-Reply (see inputfilter.cpp)
+void Server::setPrefixes(const QString &modes, const QString& prefixes)
+{
+ // NOTE: serverModes is QString::null, if server did not supply the
+ // modes which relates to the network's nick-prefixes
+ m_serverNickPrefixModes = modes;
+ m_serverNickPrefixes = prefixes;
+}
+
+// return a nickname without possible mode character at the beginning
+void Server::mangleNicknameWithModes(QString& nickname,bool& isAdmin,bool& isOwner,
+bool& isOp,bool& isHalfop,bool& hasVoice)
+{
+ isAdmin = false;
+ isOwner = false;
+ isOp = false;
+ isHalfop = false;
+ hasVoice = false;
+
+ int modeIndex;
+
+ if (nickname.isEmpty()) return;
+
+ while ((modeIndex = m_serverNickPrefixes.find(nickname[0])) != -1)
+ {
+ if(nickname.isEmpty())
+ return;
+ nickname = nickname.mid(1);
+ // cut off the prefix
+ bool recognisedMode = false;
+ // determine, whether status is like op or like voice
+ while((modeIndex)<int(m_serverNickPrefixes.length()) && !recognisedMode)
+ {
+ switch(m_serverNickPrefixes[modeIndex].latin1())
+ {
+ case '*': // admin (EUIRC)
+ {
+ isAdmin = true;
+ recognisedMode = true;
+ break;
+ }
+ case '&': // admin (unrealircd)
+ {
+ isAdmin = true;
+ recognisedMode = true;
+ break;
+ }
+ case '!': // channel owner (RFC2811)
+ {
+ isOwner = true;
+ recognisedMode = true;
+ break;
+ }
+ case '~': // channel owner (unrealircd)
+ {
+ isOwner = true;
+ recognisedMode = true;
+ break;
+ }
+ case '@': // channel operator (RFC1459)
+ {
+ isOp = true;
+ recognisedMode = true;
+ break;
+ }
+ case '%': // halfop
+ {
+ isHalfop = true;
+ recognisedMode = true;
+ break;
+ }
+ case '+': // voiced (RFC1459)
+ {
+ hasVoice = true;
+ recognisedMode = true;
+ break;
+ }
+ default:
+ {
+ ++modeIndex;
+ break;
+ }
+ } //switch to recognise the mode.
+ } // loop through the modes to find one recognised
+ } // loop through the name
+}
+
+void Server::lookupFinished()
+{
+ // error during lookup
+ if(m_socket->status())
+ {
+ // inform user about the error
+ getStatusView()->appendServerMessage(i18n("Error"),i18n("Server %1 not found: %2")
+ .arg(getConnectionSettings().server().host())
+ .arg(m_socket->errorString(m_socket->error())));
+
+ m_socket->resetStatus();
+
+ // broken connection
+ broken(m_socket->error());
+ }
+ else
+ getStatusView()->appendServerMessage(i18n("Info"),i18n("Server found, connecting..."));
+}
+
+void Server::ircServerConnectionSuccess()
+{
+ getConnectionSettings().setReconnectCount(0);
+
+ Konversation::ServerSettings serverSettings = getConnectionSettings().server();
+
+ connect(this, SIGNAL(nicknameChanged(const QString&)), getStatusView(), SLOT(setNickname(const QString&)));
+ getStatusView()->appendServerMessage(i18n("Info"),i18n("Connected; logging in..."));
+
+ QString connectString = "USER " +
+ getIdentity()->getIdent() +
+ " 8 * :" + // 8 = +i; 4 = +w
+ getIdentity()->getRealName();
+
+ QStringList ql;
+ if (!serverSettings.password().isEmpty())
+ ql << "PASS " + serverSettings.password();
+
+ ql << "NICK "+getNickname();
+ ql << connectString;
+ queueList(ql, HighPriority);
+
+ emit nicknameChanged(getNickname());
+
+ m_socket->enableRead(true);
+}
+
+void Server::broken(int state)
+{
+ kdDebug() << "Connection broken (Socket fd " << m_socket->socketDevice()->socket() << ") " << state << "!" << endl;
+
+ m_socket->enableRead(false);
+ m_socket->enableWrite(false); //FIXME if we rely on this signal, it should be turned back on somewhere...
+ m_socket->blockSignals(true);
+
+ resetQueues();
+
+ m_notifyTimer.stop();
+ m_pingResponseTimer.stop();
+ m_inputFilter.setLagMeasuring(false);
+ m_currentLag = -1;
+
+ // HACK Only show one nick change dialog at connection time
+ if (getStatusView())
+ {
+ KDialogBase* nickChangeDialog = dynamic_cast<KDialogBase*>(
+ getStatusView()->child("NickChangeDialog", "KInputDialog"));
+
+ if (nickChangeDialog) nickChangeDialog->cancel();
+ }
+
+ emit resetLag();
+ emit nicksNowOnline(this,QStringList(),true);
+
+ updateAutoJoin();
+
+ if (getConnectionState() != Konversation::SSDeliberatelyDisconnected)
+ {
+ static_cast<KonversationApplication*>(kapp)->notificationHandler()->connectionFailure(getStatusView(), getServerName());
+
+ QString error = i18n("Connection to Server %1 lost: %2.")
+ .arg(getConnectionSettings().server().host())
+ .arg(KNetwork::KSocketBase::errorString((KNetwork::KSocketBase::SocketError)state));
+
+ getStatusView()->appendServerMessage(i18n("Error"), error);
+
+ updateConnectionState(Konversation::SSInvoluntarilyDisconnected);
+ }
+}
+
+void Server::sslError(const QString& reason)
+{
+ QString error = i18n("Could not connect to %1:%2 using SSL encryption.Maybe the server does not support SSL, or perhaps you have the wrong port? %3")
+ .arg(getConnectionSettings().server().host())
+ .arg(getConnectionSettings().server().port())
+ .arg(reason);
+ getStatusView()->appendServerMessage(i18n("SSL Connection Error"),error);
+
+ updateConnectionState(Konversation::SSDeliberatelyDisconnected);
+}
+
+// Will be called from InputFilter as soon as the Welcome message was received
+void Server::connectionEstablished(const QString& ownHost)
+{
+ // Some servers don't include the userhost in RPL_WELCOME, so we
+ // need to use RPL_USERHOST to get ahold of our IP later on
+ if (!ownHost.isEmpty())
+ KNetwork::KResolver::resolveAsync(this,SLOT(gotOwnResolvedHostByWelcome(KResolverResults)),ownHost,"0");
+
+ updateConnectionState(Konversation::SSConnected);
+
+ // Make a helper object to build ISON (notify) list and map offline nicks to addressbook.
+ // TODO: Give the object a kick to get it started?
+ m_serverISON = new ServerISON(this);
+ // get first notify very early
+ startNotifyTimer(1000);
+ // Register with services
+ registerWithServices();
+ // get own ip by userhost
+ requestUserhost(getNickname());
+
+ // Start the PINGPONG match
+ QTimer::singleShot(1000 /*1 sec*/, this, SLOT(sendPing()));
+
+ // Recreate away state if we were set away prior to a reconnect.
+ if (m_away)
+ {
+ // Correct server's beliefs about its away state.
+ m_away = false;
+ requestAway(m_awayReason);
+ }
+}
+
+void Server::registerWithServices()
+{
+ if (getIdentity() && !getIdentity()->getBot().isEmpty()
+ && !getIdentity()->getPassword().isEmpty()
+ && !m_autoIdentifyLock)
+ {
+ queue("PRIVMSG "+getIdentity()->getBot()+" :identify "+getIdentity()->getPassword(), HighPriority);
+
+ m_autoIdentifyLock = true;
+ }
+}
+
+//FIXME operator[] inserts an empty T& so each destination might just as well have its own key storage
+QCString Server::getKeyForRecipient(const QString& recipient) const
+{
+ return m_keyMap[recipient];
+}
+
+void Server::setKeyForRecipient(const QString& recipient, const QCString& key)
+{
+ m_keyMap[recipient] = key;
+}
+
+void Server::gotOwnResolvedHostByWelcome(KResolverResults res)
+{
+ if (res.error() == KResolver::NoError && !res.isEmpty())
+ m_ownIpByWelcome = res.first().address().nodeName();
+ else
+ kdDebug() << "Server::gotOwnResolvedHostByWelcome(): Got error: " << ( int )res.error() << endl;
+}
+
+void Server::quitServer()
+{
+ // Make clear this is deliberate even if the QUIT never actually goes through the queue
+ // (i.e. this is not redundant with _send_internal()'s updateConnectionState() call for
+ // a QUIT).
+ updateConnectionState(Konversation::SSDeliberatelyDisconnected);
+
+ QString command(Preferences::commandChar()+"QUIT");
+ Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),command, QString());
+ queue(result.toServer, HighPriority);
+
+ m_socket->enableRead(false);
+
+ flushQueues();
+
+ m_socket->close();
+
+ getStatusView()->appendServerMessage(i18n("Info"), i18n("Disconnected from %1.").arg(getConnectionSettings().server().host()));
+}
+
+void Server::notifyAction(const QString& nick)
+{
+ // parse wildcards (toParse,nickname,channelName,nickList,parameter)
+ QString out = parseWildcards(Preferences::notifyDoubleClickAction(),
+ getNickname(),
+ QString(),
+ QString(),
+ nick,
+ QString());
+
+ // Send all strings, one after another
+ QStringList outList = QStringList::split('\n',out);
+ for (unsigned int index=0; index<outList.count(); ++index)
+ {
+ Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),outList[index],QString());
+ queue(result.toServer);
+ } // endfor
+}
+
+void Server::notifyResponse(const QString& nicksOnline)
+{
+ bool nicksOnlineChanged = false;
+ QStringList actualList = QStringList::split(' ',nicksOnline);
+ QString lcActual = ' ' + nicksOnline + ' ';
+ QString lcPrevISON = ' ' + (m_prevISONList.join(" ")) + ' ';
+
+ QStringList::iterator it;
+
+ //Are any nicks gone offline
+ for (it = m_prevISONList.begin(); it != m_prevISONList.end(); ++it)
+ {
+ if (lcActual.find(' ' + (*it) + ' ', 0, false) == -1)
+ {
+ setNickOffline(*it);
+ nicksOnlineChanged = true;
+ }
+ }
+
+ //Are any nicks gone online
+ for (it = actualList.begin(); it != actualList.end(); ++it)
+ {
+ if (lcPrevISON.find(' ' + (*it) + ' ', 0, false) == -1) {
+ setWatchedNickOnline(*it);
+ nicksOnlineChanged = true;
+ }
+ }
+
+ // Note: The list emitted in this signal *does* include nicks in joined channels.
+ emit nicksNowOnline(this, actualList, nicksOnlineChanged);
+
+ m_prevISONList = actualList;
+
+ // Next round
+ startNotifyTimer();
+}
+
+void Server::startNotifyTimer(int msec)
+{
+ // make sure the timer gets started properly in case we have reconnected
+ m_notifyTimer.stop();
+
+ if (msec == 0) msec = Preferences::notifyDelay()*1000;
+
+ // start the timer in one shot mode
+ if (Preferences::useNotify())
+ m_notifyTimer.start(msec, true);
+}
+
+void Server::notifyTimeout()
+{
+ // Notify delay time is over, send ISON request if desired
+ if (Preferences::useNotify())
+ {
+ // But only if there actually are nicks in the notify list
+ QString list = getISONListString();
+
+ if (!list.isEmpty()) queue("ISON "+list, LowPriority);
+
+ }
+}
+
+void Server::autoCommandsAndChannels()
+{
+ if (getServerGroup() && !getServerGroup()->connectCommands().isEmpty())
+ {
+ QString connectCommands = getServerGroup()->connectCommands();
+
+ if (!getNickname().isEmpty())
+ connectCommands.replace("%nick", getNickname());
+
+ QStringList connectCommandsList = QStringList::split(";", connectCommands);
+ QStringList::iterator iter;
+
+ for (iter = connectCommandsList.begin(); iter != connectCommandsList.end(); ++iter)
+ {
+ QString output(*iter);
+ output = output.simplifyWhiteSpace();
+ getOutputFilter()->replaceAliases(output);
+ Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),output,QString());
+ queue(result.toServer);
+ }
+ }
+
+ if (getAutoJoin())
+ {
+ for ( QStringList::Iterator it = m_autoJoinCommands.begin(); it != m_autoJoinCommands.end(); ++it )
+ queue((*it));
+ }
+}
+
+/** Create a set of indices into the nickname list of the current identity based on the current nickname.
+ *
+ * The index list is only used if the current nickname is not available. If the nickname is in the identity,
+ * we do not want to retry it. If the nickname is not in the identity, it is considered to be at position -1.
+ */
+void Server::resetNickSelection()
+{
+ m_nickIndices.clear();
+ //for equivalence testing in case the identity gets changed underneath us
+ m_referenceNicklist = getIdentity()->getNicknameList();
+ //where in this identities nicklist will we have started?
+ int start = m_referenceNicklist.findIndex(getNickname());
+ int len = m_referenceNicklist.count();
+
+ //we first use this list of indices *after* we've already tried the current nick, which we don't want
+ //to retry if we wrapped, so exclude its index here
+ //if it wasn't in the list, we get -1 back, so then we *want* to include 0
+ for (int i=start+1; i<len; i++)
+ m_nickIndices.append(i);
+ //now, from the beginning of the list, to the item before start
+ for (int i=0; i<start; i++)
+ m_nickIndices.append(i);
+ //cause it to try to get an invalid nick number
+ m_nickIndices.append(len);
+}
+
+QString Server::getNextNickname()
+{
+ //if the identity changed underneath us (likely impossible), start over
+ if (m_referenceNicklist != getIdentity()->getNicknameList())
+ resetNickSelection();
+
+ QString newNick = getIdentity()->getNickname(m_nickIndices.front());
+ m_nickIndices.pop_front();
+
+ if (newNick.isNull())
+ {
+ QString inputText = i18n("No nicknames from the \"%1\" identity were accepted by the connection \"%2\".\nPlease enter a new one or press Cancel to disconnect:").arg(getIdentity()->getName()).arg(getDisplayName());
+ newNick = KInputDialog::getText(i18n("Nickname error"), inputText,
+ QString(), 0, getStatusView(), "NickChangeDialog");
+ }
+
+ return newNick;
+}
+
+void Server::processIncomingData()
+{
+ m_incomingTimer.stop();
+
+ if (!m_inputBuffer.isEmpty() && !m_processingIncoming)
+ {
+ m_processingIncoming = true;
+ QString front(m_inputBuffer.front());
+ m_inputBuffer.pop_front();
+ if (m_rawLog)
+ {
+ QString toRaw = front;
+ m_rawLog->appendRaw("&gt;&gt; " + toRaw.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace(QRegExp("\\s"), "&nbsp;"));
+ }
+ m_inputFilter.parseLine(front);
+ m_processingIncoming = false;
+
+ if (!m_inputBuffer.isEmpty()) m_incomingTimer.start(0);
+ }
+}
+
+void Server::incoming()
+{
+ if (getConnectionSettings().server().SSLEnabled())
+ emit sslConnected(this);
+
+ // We read all available bytes here because readyRead() signal will be emitted when there is new data
+ // else we will stall when displaying MOTD etc.
+ int max_bytes = m_socket->bytesAvailable();
+
+ QByteArray buffer(max_bytes+1);
+ int len = 0;
+
+ // Read at max "max_bytes" bytes into "buffer"
+ len = m_socket->readBlock(buffer.data(),max_bytes);
+
+ if (len <= 0 && getConnectionSettings().server().SSLEnabled())
+ return;
+
+ if (len <= 0) // Zero means buffer is empty which shouldn't happen because readyRead signal is emitted
+ {
+ getStatusView()->appendServerMessage(i18n("Error"),
+ i18n("There was an error reading the data from the server: %1").
+ arg(m_socket->errorString()));
+
+ broken(m_socket->error());
+ return;
+ }
+
+ buffer[len] = 0;
+
+ QCString qcsBuffer = m_inputBufferIncomplete + QCString(buffer);
+
+ // split buffer to lines
+ QValueList<QCString> qcsBufferLines;
+ int lastLFposition = -1;
+ for( int nextLFposition ; ( nextLFposition = qcsBuffer.find('\n', lastLFposition+1) ) != -1 ; lastLFposition = nextLFposition )
+ qcsBufferLines << qcsBuffer.mid(lastLFposition+1, nextLFposition-lastLFposition-1);
+
+ // remember the incomplete line (split by packets)
+ m_inputBufferIncomplete = qcsBuffer.right(qcsBuffer.length()-lastLFposition-1);
+
+ while(!qcsBufferLines.isEmpty())
+ {
+ // Pre parsing is needed in case encryption/decryption is needed
+ // BEGIN set channel encoding if specified
+ QString senderNick;
+ bool isServerMessage = false;
+ QString channelKey;
+ QTextCodec* codec = getIdentity()->getCodec();
+ QCString front = qcsBufferLines.front();
+
+ QStringList lineSplit = QStringList::split(" ",codec->toUnicode(front));
+
+ if( lineSplit.count() >= 1 )
+ {
+ if( lineSplit[0][0] == ':' ) // does this message have a prefix?
+ {
+ if( !lineSplit[0].contains('!') ) // is this a server(global) message?
+ isServerMessage = true;
+ else
+ senderNick = lineSplit[0].mid(1, lineSplit[0].find('!')-1);
+
+ lineSplit.pop_front(); // remove prefix
+ }
+ }
+
+ // BEGIN pre-parse to know where the message belongs to
+ QString command = lineSplit[0].lower();
+ if( isServerMessage )
+ {
+ if( lineSplit.count() >= 3 )
+ {
+ if( command == "332" ) // RPL_TOPIC
+ channelKey = lineSplit[2];
+ if( command == "372" ) // RPL_MOTD
+ channelKey = ":server";
+ }
+ }
+ else // NOT a global message
+ {
+ if( lineSplit.count() >= 2 )
+ {
+ // query
+ if( ( command == "privmsg" ||
+ command == "notice" ) &&
+ lineSplit[1] == getNickname() )
+ {
+ channelKey = senderNick;
+ }
+ // channel message
+ else if( command == "privmsg" ||
+ command == "notice" ||
+ command == "join" ||
+ command == "kick" ||
+ command == "part" ||
+ command == "topic" )
+ {
+ channelKey = lineSplit[1];
+ }
+ }
+ }
+ // END pre-parse to know where the message belongs to
+ // Decrypt if necessary
+ if(command == "privmsg")
+ Konversation::decrypt(channelKey,front,this);
+ else if(command == "332" || command == "topic")
+ {
+ Konversation::decryptTopic(channelKey,front,this);
+ }
+
+ bool isUtf8 = Konversation::isUtf8(front);
+
+ if( isUtf8 )
+ m_inputBuffer << QString::fromUtf8(front);
+ else
+ {
+ // check setting
+ QString channelEncoding;
+ if( !channelKey.isEmpty() )
+ {
+ channelEncoding = Preferences::channelEncoding(getDisplayName(), channelKey);
+ }
+ // END set channel encoding if specified
+
+ if( !channelEncoding.isEmpty() )
+ codec = Konversation::IRCCharsets::self()->codecForName(channelEncoding);
+
+ // if channel encoding is utf-8 and the string is definitely not utf-8
+ // then try latin-1
+ if ( !isUtf8 && codec->mibEnum() == 106 )
+ codec = QTextCodec::codecForMib( 4 /* iso-8859-1 */ );
+
+ m_inputBuffer << codec->toUnicode(front);
+ }
+ qcsBufferLines.pop_front();
+ m_bytesReceived+=m_inputBuffer.back().length();
+ }
+
+ if( !m_incomingTimer.isActive() && !m_processingIncoming )
+ m_incomingTimer.start(0);
+}
+
+/** Calculate how long this message premable will be.
+
+ This is necessary because the irc server will clip messages so that the
+ client receives a maximum of 512 bytes at once.
+*/
+int Server::getPreLength(const QString& command, const QString& dest)
+{
+ NickInfo* info = getNickInfo(getNickname());
+ int hostMaskLength = 0;
+
+ if(info)
+ hostMaskLength = info->getHostmask().length();
+
+ //:Sho_!i=ehs1@konversation/developer/hein PRIVMSG #konversation :and then back to it
+
+ //<colon>$nickname<!>$hostmask<space>$command<space>$destination<space><colon>$message<cr><lf>
+ int x= 512 - 8 - (m_nickname.length() + hostMaskLength + command.length() + dest.length());
+
+ return x;
+}
+
+//Commands greater than 1 have localizeable text: 0 1 2 3 4 5 6
+static QStringList outcmds=QStringList::split(QChar(' '),"WHO QUIT PRIVMSG NOTICE KICK PART TOPIC");
+
+int Server::_send_internal(QString outputLine)
+{
+ QStringList outputLineSplit=QStringList::split(" ", outputLine);
+ //Lets cache the uppercase command so we don't miss or reiterate too much
+ int outboundCommand=outcmds.findIndex(outputLineSplit[0].upper());
+
+ if (outputLine.at(outputLine.length()-1) == '\n')
+ {
+ kdDebug() << "found \\n on " << outboundCommand << endl;
+ outputLine.setLength(outputLine.length()-1);
+ }
+
+ // remember the first arg of /WHO to identify responses
+ if (outboundCommand == 0) //"WHO"
+ {
+ if (outputLineSplit.count() >= 2)
+ m_inputFilter.addWhoRequest(outputLineSplit[1]);
+ else // no argument (servers recognize it as "*")
+ m_inputFilter.addWhoRequest("*");
+ }
+ else if (outboundCommand == 1) //"QUIT"
+ updateConnectionState(Konversation::SSDeliberatelyDisconnected);
+
+ // set channel encoding if specified
+ QString channelCodecName;
+
+ //[ PRIVMSG | NOTICE | KICK | PART | TOPIC ] target :message
+ if (outputLineSplit.count() > 2 && outboundCommand > 1)
+ channelCodecName=Preferences::channelEncoding(getDisplayName(), outputLineSplit[1]);
+
+ QTextCodec* codec;
+ if (channelCodecName.isEmpty())
+ codec = getIdentity()->getCodec();
+ else
+ codec = Konversation::IRCCharsets::self()->codecForName(channelCodecName);
+
+ // Some codecs don't work with a negative value. This is a bug in Qt 3.
+ // ex.: JIS7, eucJP, SJIS
+ //int outlen=-1;
+ int outlen=outputLine.length();
+
+ //leaving this done twice for now, i'm uncertain of the implications of not encoding other commands
+ QCString encoded=codec->fromUnicode(outputLine, outlen);
+
+ QString blowfishKey=getKeyForRecipient(outputLineSplit[1]);
+ if (!blowfishKey.isEmpty() && outboundCommand >1)
+ {
+ int colon = outputLine.find(':');
+ if (colon > -1)
+ {
+ colon++;
+
+ QString pay(outputLine.mid(colon));
+ int len=pay.length();
+ //only encode the actual user text, IRCD *should* desire only ASCII 31 < x < 127 for protocol elements
+ QCString payload=codec->fromUnicode(pay, len);
+ //apparently channel name isn't a protocol element...
+ len=outputLineSplit[1].length();
+ QCString dest=codec->fromUnicode(outputLineSplit[1], len);
+
+ if (outboundCommand == 2 || outboundCommand == 6) // outboundCommand == 3
+ {
+ bool doit = true;
+ if (outboundCommand == 2)
+ {
+ //if its a privmsg and a ctcp but not an action, don't encrypt
+ //not interpreting `payload` in case encoding bollixed it
+ if (outputLineSplit[2].startsWith(":\x01") && outputLineSplit[2] != ":\x01""ACTION")
+ doit = false;
+ }
+ if (doit)
+ {
+ Konversation::encrypt(blowfishKey, payload);
+ encoded = outputLineSplit[0].ascii();
+ //two lines because the compiler insists on using the wrong operator+
+ encoded += ' ' + dest + " :" + payload;
+ }
+ }
+ }
+ }
+ encoded += '\n';
+ Q_LONG sout = m_socket->writeBlock(encoded, encoded.length());
+
+ if (m_rawLog)
+ m_rawLog->appendRaw("&lt;&lt; " + outputLine.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;"));
+
+ return sout;
+}
+
+void Server::toServer(QString&s, IRCQueue* q)
+{
+
+ int sizesent = _send_internal(s);
+ emit sentStat(s.length(), sizesent, q); //tell the queues what we sent
+ //tell everyone else
+ emit sentStat(s.length(), sizesent);
+}
+
+void Server::collectStats(int bytes, int encodedBytes)
+{
+ m_bytesSent += bytes;
+ m_encodedBytesSent += encodedBytes;
+ m_linesSent++;
+}
+
+bool Server::validQueue(QueuePriority priority)
+{
+ if (priority >=0 && priority <= _max_queue())
+ return true;
+ return false;
+}
+
+bool Server::queue(const QString& line, QueuePriority priority)
+{
+ if (!line.isEmpty() && validQueue(priority))
+ {
+ IRCQueue& out=*m_queues[priority];
+ out.enqueue(line);
+ return true;
+ }
+ return false;
+}
+
+bool Server::queueList(const QStringList& buffer, QueuePriority priority)
+{
+ if (buffer.isEmpty() || !validQueue(priority))
+ return false;
+
+ IRCQueue& out=*(m_queues[priority]);
+
+ for(unsigned int i=0;i<buffer.count();i++)
+ {
+ QString line=*buffer.at(i);
+ if (!line.isEmpty())
+ out.enqueue(line);
+ }
+ return true;
+}
+
+void Server::resetQueues()
+{
+ for (int i=0;i<=_max_queue();i++)
+ m_queues[i]->reset();
+}
+
+//this could flood you off, but you're leaving anyway...
+void Server::flushQueues()
+{
+ int cue;
+ do
+ {
+ cue=-1;
+ int wait=0;
+ for (int i=1;i<=_max_queue();i++) //slow queue can rot
+ {
+ IRCQueue *queue=m_queues[i];
+ //higher queue indices have higher priorty, higher queue priority wins tie
+ if (!queue->isEmpty() && queue->currentWait()>=wait)
+ {
+ cue=i;
+ wait=queue->currentWait();
+ }
+ }
+ if (cue>-1)
+ m_queues[cue]->sendNow();
+ } while (cue>-1);
+}
+
+void Server::closed()
+{
+ broken(m_socket->error());
+}
+
+void Server::dcopRaw(const QString& command)
+{
+ if(command.startsWith(Preferences::commandChar()))
+ {
+ queue(command.section(Preferences::commandChar(), 1));
+ }
+ else
+ queue(command);
+}
+
+void Server::dcopSay(const QString& target,const QString& command)
+{
+ if(isAChannel(target))
+ {
+ Channel* channel=getChannelByName(target);
+ if(channel) channel->sendChannelText(command);
+ }
+ else
+ {
+ class Query* query=getQueryByName(target);
+ if(query==0)
+ {
+ NickInfoPtr nickinfo = obtainNickInfo(target);
+ query=addQuery(nickinfo, true);
+ }
+ if(query)
+ {
+ if(!command.isEmpty())
+ query->sendQueryText(command);
+ else
+ {
+ query->adjustFocus();
+ getViewContainer()->getWindow()->show();
+ KWin::demandAttention(getViewContainer()->getWindow()->winId());
+ KWin::activateWindow(getViewContainer()->getWindow()->winId());
+ }
+ }
+ }
+}
+
+void Server::dcopInfo(const QString& string)
+{
+ appendMessageToFrontmost(i18n("DCOP"),string);
+}
+
+void Server::ctcpReply(const QString &receiver,const QString &text)
+{
+ queue("NOTICE "+receiver+" :"+'\x01'+text+'\x01');
+}
+
+// Given a nickname, returns NickInfo object. 0 if not found.
+NickInfoPtr Server::getNickInfo(const QString& nickname)
+{
+ QString lcNickname(nickname.lower());
+ if (m_allNicks.contains(lcNickname))
+ {
+ NickInfoPtr nickinfo = m_allNicks[lcNickname];
+ Q_ASSERT(nickinfo);
+ return nickinfo;
+ }
+ else
+ return 0;
+}
+
+// Given a nickname, returns an existing NickInfo object, or creates a new NickInfo object.
+// Returns pointer to the found or created NickInfo object.
+NickInfoPtr Server::obtainNickInfo(const QString& nickname)
+{
+ NickInfoPtr nickInfo = getNickInfo(nickname);
+ if (!nickInfo)
+ {
+ nickInfo = new NickInfo(nickname, this);
+ m_allNicks.insert(QString(nickname.lower()), nickInfo);
+ }
+ return nickInfo;
+}
+
+const NickInfoMap* Server::getAllNicks() { return &m_allNicks; }
+
+// Returns the list of members for a channel in the joinedChannels list.
+// 0 if channel is not in the joinedChannels list.
+// Using code must not alter the list.
+const ChannelNickMap *Server::getJoinedChannelMembers(const QString& channelName) const
+{
+ QString lcChannelName = channelName.lower();
+ if (m_joinedChannels.contains(lcChannelName))
+ return m_joinedChannels[lcChannelName];
+ else
+ return 0;
+}
+
+// Returns the list of members for a channel in the unjoinedChannels list.
+// 0 if channel is not in the unjoinedChannels list.
+// Using code must not alter the list.
+const ChannelNickMap *Server::getUnjoinedChannelMembers(const QString& channelName) const
+{
+ QString lcChannelName = channelName.lower();
+ if (m_unjoinedChannels.contains(lcChannelName))
+ return m_unjoinedChannels[lcChannelName];
+ else
+ return 0;
+}
+
+// Searches the Joined and Unjoined lists for the given channel and returns the member list.
+// 0 if channel is not in either list.
+// Using code must not alter the list.
+const ChannelNickMap *Server::getChannelMembers(const QString& channelName) const
+{
+ const ChannelNickMap *members = getJoinedChannelMembers(channelName);
+ if (members)
+ return members;
+ else
+ return getUnjoinedChannelMembers(channelName);
+}
+
+// Returns pointer to the ChannelNick (mode and pointer to NickInfo) for a given channel and nickname.
+// 0 if not found.
+ChannelNickPtr Server::getChannelNick(const QString& channelName, const QString& nickname)
+{
+ QString lcNickname = nickname.lower();
+ const ChannelNickMap *channelNickMap = getChannelMembers(channelName);
+ if (channelNickMap)
+ {
+ if (channelNickMap->contains(lcNickname))
+ return (*channelNickMap)[lcNickname];
+ else
+ return 0;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// Updates a nickname in a channel. If not on the joined or unjoined lists, and nick
+// is in the watch list, adds the channel and nick to the unjoinedChannels list.
+// If mode != 99, sets the mode for the nick in the channel.
+// Returns the NickInfo object if nick is on any lists, otherwise 0.
+ChannelNickPtr Server::setChannelNick(const QString& channelName, const QString& nickname, unsigned int mode)
+{
+ QString lcNickname = nickname.lower();
+ // If already on a list, update mode.
+ ChannelNickPtr channelNick = getChannelNick(channelName, lcNickname);
+ if (!channelNick)
+ {
+ // Get watch list from preferences.
+ QString watchlist=getWatchListString();
+ // Create a lower case nick list from the watch list.
+ QStringList watchLowerList=QStringList::split(' ',watchlist.lower());
+ // If on the watch list, add channel and nick to unjoinedChannels list.
+ if (watchLowerList.find(lcNickname) != watchLowerList.end())
+ {
+ channelNick = addNickToUnjoinedChannelsList(channelName, nickname);
+ channelNick->setMode(mode);
+ }
+ else return 0;
+ }
+
+ if (mode != 99) channelNick->setMode(mode);
+ return channelNick;
+}
+
+// Returns a list of all the joined channels that a nick is in.
+QStringList Server::getNickJoinedChannels(const QString& nickname)
+{
+ QString lcNickname = nickname.lower();
+ QStringList channellist;
+ ChannelMembershipMap::ConstIterator channel;
+ for( channel = m_joinedChannels.begin(); channel != m_joinedChannels.end(); ++channel )
+ {
+ if (channel.data()->contains(lcNickname)) channellist.append(channel.key());
+ }
+ return channellist;
+}
+
+// Returns a list of all the channels (joined or unjoined) that a nick is in.
+QStringList Server::getNickChannels(const QString& nickname)
+{
+ QString lcNickname = nickname.lower();
+ QStringList channellist;
+ ChannelMembershipMap::ConstIterator channel;
+ for( channel = m_joinedChannels.begin(); channel != m_joinedChannels.end(); ++channel )
+ {
+ if (channel.data()->contains(lcNickname)) channellist.append(channel.key());
+ }
+ for( channel = m_unjoinedChannels.begin(); channel != m_unjoinedChannels.end(); ++channel )
+ {
+ if (channel.data()->contains(lcNickname)) channellist.append(channel.key());
+ }
+ return channellist;
+}
+
+bool Server::isNickOnline(const QString &nickname)
+{
+ NickInfoPtr nickInfo = getNickInfo(nickname);
+ return (nickInfo != 0);
+}
+
+QString Server::getOwnIpByNetworkInterface()
+{
+ return m_socket->localAddress().nodeName();
+}
+
+QString Server::getOwnIpByServerMessage()
+{
+ if(!m_ownIpByWelcome.isEmpty())
+ return m_ownIpByWelcome;
+ else if(!m_ownIpByUserhost.isEmpty())
+ return m_ownIpByUserhost;
+ else
+ return QString();
+}
+
+class Query* Server::addQuery(const NickInfoPtr & nickInfo, bool weinitiated)
+{
+ QString nickname = nickInfo->getNickname();
+ // Only create new query object if there isn't already one with the same name
+ class Query* query=getQueryByName(nickname);
+
+ if (!query)
+ {
+ QString lcNickname = nickname.lower();
+ query = getViewContainer()->addQuery(this, nickInfo, weinitiated);
+
+ connect(query, SIGNAL(sendFile(const QString&)),this, SLOT(requestDccSend(const QString&)));
+ connect(this, SIGNAL(serverOnline(bool)), query, SLOT(serverOnline(bool)));
+
+ // Append query to internal list
+ m_queryList.append(query);
+
+ m_queryNicks.insert(lcNickname, nickInfo);
+
+ if (!weinitiated)
+ static_cast<KonversationApplication*>(kapp)->notificationHandler()->query(query, nickname);
+ }
+
+ // try to get hostmask if there's none yet
+ if (query->getNickInfo()->getHostmask().isEmpty()) requestUserhost(nickname);
+
+ Q_ASSERT(query);
+
+ return query;
+}
+
+void Server::closeQuery(const QString &name)
+{
+ class Query* query = getQueryByName(name);
+ removeQuery(query);
+
+ // Update NickInfo. If no longer on any lists, delete it altogether, but
+ // only if not on the watch list. ISON replies will determine whether the NickInfo
+ // is deleted altogether in that case.
+ QString lcNickname = name.lower();
+ m_queryNicks.remove(lcNickname);
+ if (!isWatchedNick(name)) deleteNickIfUnlisted(name);
+}
+
+void Server::closeChannel(const QString& name)
+{
+ kdDebug() << "Server::closeChannel(" << name << ")" << endl;
+ Channel* channelToClose = getChannelByName(name);
+
+ if(channelToClose)
+ {
+ Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),
+ Preferences::commandChar() + "PART", name);
+ queue(result.toServer);
+ }
+}
+
+void Server::requestChannelList()
+{
+ m_inputFilter.setAutomaticRequest("LIST", QString(), true);
+ queue(QString("LIST"));
+}
+
+void Server::requestWhois(const QString& nickname)
+{
+ m_inputFilter.setAutomaticRequest("WHOIS", nickname, true);
+ queue("WHOIS "+nickname, LowPriority);
+}
+
+void Server::requestWho(const QString& channel)
+{
+ m_inputFilter.setAutomaticRequest("WHO", channel, true);
+ queue("WHO "+channel, LowPriority);
+}
+
+void Server::requestUserhost(const QString& nicks)
+{
+ QStringList nicksList = QStringList::split(" ", nicks);
+ for(QStringList::ConstIterator it=nicksList.begin() ; it!=nicksList.end() ; ++it)
+ m_inputFilter.setAutomaticRequest("USERHOST", *it, true);
+ queue("USERHOST "+nicks, LowPriority);
+}
+
+void Server::requestTopic(const QString& channel)
+{
+ m_inputFilter.setAutomaticRequest("TOPIC", channel, true);
+ queue("TOPIC "+channel, LowPriority);
+}
+
+void Server::resolveUserhost(const QString& nickname)
+{
+ m_inputFilter.setAutomaticRequest("WHOIS", nickname, true);
+ m_inputFilter.setAutomaticRequest("DNS", nickname, true);
+ queue("WHOIS "+nickname, LowPriority); //FIXME when is this really used?
+}
+
+void Server::requestBan(const QStringList& users,const QString& channel,const QString& a_option)
+{
+ QString hostmask;
+ QString option=a_option.lower();
+
+ Channel* targetChannel=getChannelByName(channel);
+
+ for(unsigned int index=0;index<users.count();index++)
+ {
+ // first, set the ban mask to the specified nick
+ QString mask=users[index];
+ // did we specify an option?
+ if(!option.isEmpty())
+ {
+ // try to find specified nick on the channel
+ Nick* targetNick=targetChannel->getNickByName(mask);
+ // if we found the nick try to find their hostmask
+ if(targetNick)
+ {
+ QString hostmask=targetNick->getChannelNick()->getHostmask();
+ // if we found the hostmask, add it to the ban mask
+ if(!hostmask.isEmpty())
+ {
+ mask=targetNick->getChannelNick()->getNickname()+'!'+hostmask;
+
+ // adapt ban mask to the option given
+ if(option=="host")
+ mask="*!*@*."+hostmask.section('.',1);
+ else if(option=="domain")
+ mask="*!*@"+hostmask.section('@',1);
+ else if(option=="userhost")
+ mask="*!"+hostmask.section('@',0,0)+"@*."+hostmask.section('.',1);
+ else if(option=="userdomain")
+ mask="*!"+hostmask.section('@',0,0)+'@'+hostmask.section('@',1);
+ }
+ }
+ }
+
+ Konversation::OutputFilterResult result = getOutputFilter()->execBan(mask,channel);
+ queue(result.toServer);
+ }
+}
+
+void Server::requestUnban(const QString& mask,const QString& channel)
+{
+ Konversation::OutputFilterResult result = getOutputFilter()->execUnban(mask,channel);
+ queue(result.toServer);
+}
+
+void Server::requestDccSend()
+{
+ requestDccSend(QString());
+}
+
+void Server::sendURIs(const QStrList& uris, const QString& nick)
+{
+ for (QStrListIterator it(uris) ; *it; ++it)
+ addDccSend(nick,KURL(*it));
+}
+
+void Server::requestDccSend(const QString &a_recipient)
+{
+ QString recipient(a_recipient);
+ // if we don't have a recipient yet, let the user select one
+ if(recipient.isEmpty())
+ {
+ QStringList nickList;
+ Channel* lookChannel=m_channelList.first();
+
+ // fill nickList with all nicks we know about
+ while (lookChannel)
+ {
+ QPtrList<Nick> nicks=lookChannel->getNickList();
+ Nick* lookNick=nicks.first();
+ while(lookNick)
+ {
+ if(!nickList.contains(lookNick->getChannelNick()->getNickname())) nickList.append(lookNick->getChannelNick()->getNickname());
+ lookNick=nicks.next();
+ }
+ lookChannel=m_channelList.next();
+ }
+
+ // add Queries as well, but don't insert duplicates
+ class Query* lookQuery=m_queryList.first();
+ while(lookQuery)
+ {
+ if(!nickList.contains(lookQuery->getName())) nickList.append(lookQuery->getName());
+ lookQuery=m_queryList.next();
+ }
+
+ recipient=DccRecipientDialog::getNickname(getViewContainer()->getWindow(),nickList);
+ }
+ // do we have a recipient *now*?
+ if(!recipient.isEmpty())
+ {
+ KURL::List fileURLs=KFileDialog::getOpenURLs(
+ ":lastDccDir",
+ QString(),
+ getViewContainer()->getWindow(),
+ i18n("Select File(s) to Send to %1").arg(recipient)
+ );
+ KURL::List::iterator it;
+ for ( it = fileURLs.begin() ; it != fileURLs.end() ; ++it )
+ {
+ addDccSend( recipient, *it );
+ }
+ }
+}
+
+void Server::slotNewDccTransferItemQueued(DccTransfer* transfer)
+{
+ if (transfer->getConnectionId() == connectionId() )
+ {
+ kdDebug() << "Server::slotNewDccTranfserItemQueued(): connecting slots for " << transfer->getFileName() << " [" << transfer->getType() << "]" << endl;
+ if ( transfer->getType() == DccTransfer::Receive )
+ {
+ connect( transfer, SIGNAL( done( DccTransfer* ) ), this, SLOT( dccGetDone( DccTransfer* ) ) );
+ connect( transfer, SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, SLOT( dccStatusChanged( DccTransfer*, int, int ) ) );
+ }
+ else
+ {
+ connect( transfer, SIGNAL( done( DccTransfer* ) ), this, SLOT( dccSendDone( DccTransfer* ) ) );
+ connect( transfer, SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, SLOT( dccStatusChanged( DccTransfer*, int, int ) ) );
+ }
+ }
+}
+
+void Server::addDccSend(const QString &recipient,KURL fileURL, const QString &altFileName, uint fileSize)
+{
+ if (!fileURL.isValid()) return;
+
+ emit addDccPanel();
+
+ // We already checked that the file exists in output filter / requestDccSend() resp.
+ DccTransferSend* newDcc = KonversationApplication::instance()->getDccTransferManager()->newUpload();
+
+ newDcc->setConnectionId( connectionId() );
+
+ newDcc->setPartnerNick( recipient );
+ newDcc->setFileURL( fileURL );
+ if ( !altFileName.isEmpty() )
+ newDcc->setFileName( altFileName );
+ if ( fileSize != 0 )
+ newDcc->setFileSize( fileSize );
+
+ if ( newDcc->queue() )
+ newDcc->start();
+}
+
+void Server::addDccGet(const QString &sourceNick, const QStringList &dccArguments)
+{
+ emit addDccPanel();
+
+ DccTransferRecv* newDcc = KonversationApplication::instance()->getDccTransferManager()->newDownload();
+
+ newDcc->setConnectionId( connectionId() );
+
+ newDcc->setPartnerNick( sourceNick );
+ newDcc->setPartnerIp( DccCommon::numericalIpToTextIp( dccArguments[1] ) );
+ newDcc->setPartnerPort( dccArguments[2] );
+ if ( dccArguments[2] == "0" && dccArguments.count() == 5) // Reverse DCC
+ newDcc->setReverse( true, dccArguments[4] );
+
+ newDcc->setFileName( dccArguments[0] );
+ newDcc->setFileSize( dccArguments[3].isEmpty() ? 0 : dccArguments[3].toULong() );
+
+ if ( newDcc->queue() )
+ {
+ QString showfile = newDcc->getFileName();
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ appendMessageToFrontmost( i18n( "DCC" ),
+ i18n( "%1 offers to send you \"%2\" (%3)..." )
+ .arg( newDcc->getPartnerNick(),
+ showfile,
+ ( newDcc->getFileSize() == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( newDcc->getFileSize() ) ) );
+
+ if(Preferences::dccAutoGet())
+ newDcc->start();
+ }
+}
+
+void Server::openDccChat(const QString& nickname)
+{
+ emit addDccChat(getNickname(),nickname,QStringList(),true);
+}
+
+void Server::requestDccChat(const QString& partnerNick, const QString& numericalOwnIp, const QString& ownPort)
+{
+ queue(QString("PRIVMSG %1 :\001DCC CHAT chat %2 %3\001").arg(partnerNick).arg(numericalOwnIp).arg(ownPort));
+}
+
+void Server::dccSendRequest(const QString &partner, const QString &fileName, const QString &address, const QString &port, unsigned long size)
+{
+ Konversation::OutputFilterResult result = getOutputFilter()->sendRequest(partner,fileName,address,port,size);
+ queue(result.toServer);
+
+ QString showfile = fileName;
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ appendMessageToFrontmost( i18n( "DCC" ),
+ i18n( "Asking %1 to accept upload of \"%2\" (%3)..." )
+ .arg( partner,
+ showfile,
+ ( size == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( size ) ) );
+}
+
+void Server::dccPassiveSendRequest(const QString& recipient,const QString& fileName,const QString& address,unsigned long size,const QString& token)
+{
+ Konversation::OutputFilterResult result = getOutputFilter()->passiveSendRequest(recipient,fileName,address,size,token);
+ queue(result.toServer);
+}
+
+void Server::dccResumeGetRequest(const QString &sender, const QString &fileName, const QString &port, KIO::filesize_t startAt)
+{
+ Konversation::OutputFilterResult result;
+
+ if (fileName.contains(" ") > 0)
+ result = getOutputFilter()->resumeRequest(sender,"\""+fileName+"\"",port,startAt);
+ else
+ result = getOutputFilter()->resumeRequest(sender,fileName,port,startAt);
+
+ queue(result.toServer);
+}
+
+void Server::dccReverseSendAck(const QString& partnerNick,const QString& fileName,const QString& ownAddress,const QString& ownPort,unsigned long size,const QString& reverseToken)
+{
+ Konversation::OutputFilterResult result = getOutputFilter()->acceptPassiveSendRequest(partnerNick,fileName,ownAddress,ownPort,size,reverseToken);
+ queue(result.toServer);
+}
+
+void Server::startReverseDccSendTransfer(const QString& sourceNick,const QStringList& dccArguments)
+{
+ DccTransferManager* dtm = KonversationApplication::instance()->getDccTransferManager();
+
+ if ( dtm->startReverseSending( connectionId(), sourceNick,
+ dccArguments[0], // filename
+ DccCommon::numericalIpToTextIp( dccArguments[1] ), // partner IP
+ dccArguments[2], // partner port
+ dccArguments[3].toInt(), // filesize
+ dccArguments[4] // Reverse DCC token
+ ) == 0 )
+ {
+ QString showfile = dccArguments[0];
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ // DTM could not find a matched item
+ appendMessageToFrontmost( i18n( "Error" ),
+ i18n( "%1 = file name, %2 = nickname",
+ "Received invalid passive DCC send acceptance message for \"%1\" from %2." )
+ .arg( showfile,
+ sourceNick ) );
+
+ }
+
+}
+
+void Server::resumeDccGetTransfer(const QString &sourceNick, const QStringList &dccArguments)
+{
+ DccTransferManager* dtm = KonversationApplication::instance()->getDccTransferManager();
+
+ QString fileName( dccArguments[0] );
+ QString ownPort( dccArguments[1] );
+ unsigned long position = dccArguments[2].toULong();
+
+ DccTransferRecv* dccTransfer = dtm->resumeDownload( connectionId(), sourceNick, fileName, ownPort, position );
+
+ QString showfile = fileName;
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ if ( dccTransfer )
+ {
+ appendMessageToFrontmost( i18n( "DCC" ),
+ i18n( "%1 = file name, %2 = nickname of sender, %3 = percentage of file size, %4 = file size",
+ "Resuming download of \"%1\" from %2 starting at %3% of %4..." )
+ .arg( showfile,
+ sourceNick,
+ QString::number( dccTransfer->getProgress() ),
+ ( dccTransfer->getFileSize() == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( dccTransfer->getFileSize() ) ) );
+ }
+ else
+ {
+ appendMessageToFrontmost( i18n( "Error" ),
+ i18n( "%1 = file name, %2 = nickname",
+ "Received invalid resume acceptance message for \"%1\" from %2." )
+ .arg( showfile,
+ sourceNick ) );
+ }
+}
+
+void Server::resumeDccSendTransfer(const QString &sourceNick, const QStringList &dccArguments)
+{
+ DccTransferManager* dtm = KonversationApplication::instance()->getDccTransferManager();
+
+ QString fileName( dccArguments[0] );
+ QString ownPort( dccArguments[1] );
+ unsigned long position = dccArguments[2].toULong();
+
+ DccTransferSend* dccTransfer = dtm->resumeUpload( connectionId(), sourceNick, fileName, ownPort, position );
+
+ QString showfile = fileName;
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ if ( dccTransfer )
+ {
+ appendMessageToFrontmost( i18n( "DCC" ),
+ i18n( "%1 = file name, %2 = nickname of recipient, %3 = percentage of file size, %4 = file size",
+ "Resuming upload of \"%1\" to %2 starting at %3% of %4...")
+ .arg( showfile,
+ sourceNick,
+ QString::number(dccTransfer->getProgress()),
+ ( dccTransfer->getFileSize() == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( dccTransfer->getFileSize() ) ) );
+
+ // FIXME: this operation should be done by DccTransferManager
+ Konversation::OutputFilterResult result = getOutputFilter()->acceptResumeRequest( sourceNick, fileName, ownPort, position );
+ queue( result.toServer );
+
+ }
+ else
+ {
+ appendMessageToFrontmost( i18n( "Error" ),
+ i18n( "%1 = file name, %2 = nickname",
+ "Received invalid resume request for \"%1\" from %2." )
+ .arg( showfile,
+ sourceNick ) );
+ }
+}
+
+void Server::dccGetDone(DccTransfer* item)
+{
+ if (!item)
+ return;
+
+ QString showfile = item->getFileName();
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ if(item->getStatus()==DccTransfer::Done)
+ appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of sender",
+ "Download of \"%1\" from %2 finished.").arg(showfile, item->getPartnerNick()));
+ else if(item->getStatus()==DccTransfer::Failed)
+ appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of sender",
+ "Download of \"%1\" from %2 failed. Reason: %3.").arg(showfile,
+ item->getPartnerNick(), item->getStatusDetail()));
+}
+
+void Server::dccSendDone(DccTransfer* item)
+{
+ if (!item)
+ return;
+
+ QString showfile = item->getFileName();
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ if(item->getStatus()==DccTransfer::Done)
+ appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of recipient",
+ "Upload of \"%1\" to %2 finished.").arg(showfile, item->getPartnerNick()));
+ else if(item->getStatus()==DccTransfer::Failed)
+ appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of recipient",
+ "Upload of \"%1\" to %2 failed. Reason: %3.").arg(showfile, item->getPartnerNick(),
+ item->getStatusDetail()));
+}
+
+void Server::dccStatusChanged(DccTransfer *item, int newStatus, int oldStatus)
+{
+ if(!item)
+ return;
+
+ QString showfile = item->getFileName();
+
+ if(showfile.startsWith("\"") && showfile.endsWith("\""))
+ showfile = showfile.mid(1, showfile.length() - 2);
+
+ if ( item->getType() == DccTransfer::Send )
+ {
+ // when resuming, a message about the receiver's acceptance has been shown already, so suppress this message
+ if ( newStatus == DccTransfer::Transferring && oldStatus == DccTransfer::WaitingRemote && !item->isResumed() )
+ appendMessageToFrontmost( i18n( "DCC" ), i18n( "%1 = file name, %2 nickname of recipient",
+ "Sending \"%1\" to %2...").arg( showfile, item->getPartnerNick() ) );
+ }
+ else // type == Receive
+ {
+ if ( newStatus == DccTransfer::Transferring && !item->isResumed() )
+ {
+ appendMessageToFrontmost( i18n( "DCC" ),
+ i18n( "%1 = file name, %2 = file size, %3 = nickname of sender", "Downloading \"%1\" (%2) from %3...")
+ .arg( showfile,
+ ( item->getFileSize() == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( item->getFileSize() ),
+ item->getPartnerNick() ) );
+ }
+ }
+}
+
+void Server::removeQuery(class Query* query)
+{
+ // Traverse through list to find the query
+ class Query* lookQuery = m_queryList.first();
+
+ while (lookQuery)
+ {
+ // Did we find our query?
+ if (lookQuery == query)
+ {
+ // Remove it from the query list
+ m_queryList.remove(lookQuery);
+ // break out of the loop
+ lookQuery = 0;
+ }
+ // else select next query
+ else lookQuery = m_queryList.next();
+ }
+
+ query->deleteLater();
+}
+
+void Server::sendJoinCommand(const QString& name, const QString& password)
+{
+ Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),
+ Preferences::commandChar() + "JOIN " + name + ' ' + password, QString());
+ queue(result.toServer);
+}
+
+void Server::joinChannel(const QString& name, const QString& hostmask)
+{
+ // (re-)join channel, open a new panel if needed
+ Channel* channel = getChannelByName(name);
+
+ if (!channel)
+ {
+ channel=getViewContainer()->addChannel(this,name);
+ Q_ASSERT(channel);
+ channel->setIdentity(getIdentity());
+ channel->setNickname(getNickname());
+ channel->indicateAway(m_away);
+
+ if (getServerGroup())
+ {
+ Konversation::ChannelSettings channelSettings = getServerGroup()->channelByNameFromHistory(name);
+ channel->setNotificationsEnabled(channelSettings.enableNotifications());
+ getServerGroup()->appendChannelHistory(channelSettings);
+ }
+
+ m_channelList.append(channel);
+
+ connect(channel,SIGNAL (sendFile()),this,SLOT (requestDccSend()) );
+ connect(this, SIGNAL(nicknameChanged(const QString&)), channel, SLOT(setNickname(const QString&)));
+ }
+ // Move channel from unjoined (if present) to joined list and add our own nickname to the joined list.
+ ChannelNickPtr channelNick = addNickToJoinedChannelsList(name, getNickname());
+
+ if ((channelNick->getHostmask() != hostmask ) && !hostmask.isEmpty())
+ {
+ NickInfoPtr nickInfo = channelNick->getNickInfo();
+ nickInfo->setHostmask(hostmask);
+ }
+
+ channel->joinNickname(channelNick);
+}
+
+void Server::removeChannel(Channel* channel)
+{
+ // Update NickInfo.
+ removeJoinedChannel(channel->getName());
+
+ if (getServerGroup())
+ {
+ Konversation::ChannelSettings channelSettings = getServerGroup()->channelByNameFromHistory(channel->getName());
+ channelSettings.setNotificationsEnabled(channel->notificationsEnabled());
+ getServerGroup()->appendChannelHistory(channelSettings);
+ }
+
+ m_channelList.removeRef(channel);
+}
+
+void Server::updateChannelMode(const QString &updater, const QString &channelName, char mode, bool plus, const QString &parameter)
+{
+
+ Channel* channel=getChannelByName(channelName);
+
+ if(channel) //Let the channel be verbose to the screen about the change, and update channelNick
+ channel->updateMode(updater, mode, plus, parameter);
+ // TODO: What is mode character for owner?
+ // Answer from JOHNFLUX - I think that admin is the same as owner. Channel.h has owner as "a"
+ // "q" is the likely answer.. UnrealIRCd and euIRCd use it.
+ // TODO these need to become dynamic
+ QString userModes="vhoqa"; // voice halfop op owner admin
+ int modePos = userModes.find(mode);
+ if (modePos > 0)
+ {
+ ChannelNickPtr updateeNick = getChannelNick(channelName, parameter);
+ if(!updateeNick)
+ {
+/*
+ if(parameter.isEmpty())
+ {
+ kdDebug() << "in updateChannelMode, a nick with no-name has had their mode '" << mode << "' changed to (" <<plus << ") in channel '" << channelName << "' by " << updater << ". How this happened, I have no idea. Please report this message to irc #konversation if you want to be helpful." << endl << "Ignoring the error and continuing." << endl;
+ //this will get their attention.
+ kdDebug() << kdBacktrace() << endl;
+ }
+ else
+ {
+ kdDebug() << "in updateChannelMode, could not find updatee nick " << parameter << " for channel " << channelName << endl;
+ kdDebug() << "This could indicate an obscure race condition that is safely being handled (like the mode of someone changed and they quit almost simulatanously, or it could indicate an internal error." << endl;
+ }
+*/
+ //TODO Do we need to add this nick?
+ return;
+ }
+
+ updateeNick->setMode(mode, plus);
+
+ // Note that channel will be moved to joined list if necessary.
+ addNickToJoinedChannelsList(channelName, parameter);
+ }
+
+ // Update channel ban list.
+ if (mode == 'b')
+ {
+ if (plus)
+ {
+ QDateTime when;
+ addBan(channelName, QString("%1 %2 %3").arg(parameter).arg(updater).arg(QDateTime::currentDateTime().toTime_t()));
+ } else {
+ removeBan(channelName, parameter);
+ }
+ }
+}
+
+void Server::updateChannelModeWidgets(const QString &channelName, char mode, const QString &parameter)
+{
+ Channel* channel=getChannelByName(channelName);
+ if(channel) channel->updateModeWidgets(mode,true,parameter);
+}
+
+void Server::updateChannelQuickButtons()
+{
+ Channel* channel=m_channelList.first();
+
+ while (channel)
+ {
+ channel->updateQuickButtons(Preferences::quickButtonList());
+ channel = m_channelList.next();
+ }
+}
+
+Channel* Server::getChannelByName(const QString& name)
+{
+ // Convert wanted channel name to lowercase
+ QString wanted=name;
+ wanted=wanted.lower();
+
+ // Traverse through list to find the channel named "name"
+ Channel* lookChannel =m_channelList.first();
+ while (lookChannel)
+ {
+ if (lookChannel->getName().lower()==wanted) return lookChannel;
+ lookChannel = m_channelList.next();
+ }
+ // No channel by that name found? Return 0. Happens on first channel join
+ return 0;
+}
+
+class Query* Server::getQueryByName(const QString& name)
+{
+ // Convert wanted query name to lowercase
+ QString wanted=name;
+ wanted=wanted.lower();
+
+ // Traverse through list to find the query with "name"
+ class Query* lookQuery=m_queryList.first();
+ while(lookQuery)
+ {
+ if(lookQuery->getName().lower()==wanted) return lookQuery;
+ lookQuery=m_queryList.next();
+ }
+ // No query by that name found? Must be a new query request. Return 0
+ return 0;
+}
+
+void Server::resetNickList(const QString& channelName)
+{
+ Channel* outChannel=getChannelByName(channelName);
+ if(outChannel) outChannel->resetNickList();
+}
+
+void Server::addPendingNickList(const QString& channelName,const QStringList& nickList)
+{
+ Channel* outChannel=getChannelByName(channelName);
+ if(outChannel) outChannel->addPendingNickList(nickList);
+}
+
+// Adds a nickname to the joinedChannels list.
+// Creates new NickInfo if necessary.
+// If needed, moves the channel from the unjoined list to the joined list.
+// Returns the NickInfo for the nickname.
+ChannelNickPtr Server::addNickToJoinedChannelsList(const QString& channelName, const QString& nickname)
+{
+ bool doChannelJoinedSignal = false;
+ bool doWatchedNickChangedSignal = false;
+ bool doChannelMembersChangedSignal = false;
+ QString lcNickname = nickname.lower();
+ // Create NickInfo if not already created.
+ NickInfoPtr nickInfo = getNickInfo(nickname);
+ if (!nickInfo)
+ {
+ nickInfo = new NickInfo(nickname, this);
+ m_allNicks.insert(lcNickname, nickInfo);
+ doWatchedNickChangedSignal = isWatchedNick(nickname);
+ }
+ // if nickinfo already exists update nickname, in case we created the nickinfo based
+ // on e.g. an incorrectly capitalized ISON request
+ else
+ nickInfo->setNickname(nickname);
+
+ // Move the channel from unjoined list (if present) to joined list.
+ QString lcChannelName = channelName.lower();
+ ChannelNickMap *channel;
+ if (m_unjoinedChannels.contains(lcChannelName))
+ {
+ channel = m_unjoinedChannels[lcChannelName];
+ m_unjoinedChannels.remove(lcChannelName);
+ m_joinedChannels.insert(lcChannelName, channel);
+ doChannelJoinedSignal = true;
+ }
+ else
+ {
+ // Create a new list in the joined channels if not already present.
+ if (!m_joinedChannels.contains(lcChannelName))
+ {
+ channel = new ChannelNickMap;
+ m_joinedChannels.insert(lcChannelName, channel);
+ doChannelJoinedSignal = true;
+ }
+ else
+ channel = m_joinedChannels[lcChannelName];
+ }
+ // Add NickInfo to channel list if not already in the list.
+ ChannelNickPtr channelNick;
+ if (!channel->contains(lcNickname))
+ {
+ channelNick = new ChannelNick(nickInfo, false, false, false, false, false);
+ Q_ASSERT(channelNick);
+ channel->insert(lcNickname, channelNick);
+ doChannelMembersChangedSignal = true;
+ }
+ channelNick = (*channel)[lcNickname];
+ Q_ASSERT(channelNick); //Since we just added it if it didn't exist, it should be guaranteed to exist now
+ if (doWatchedNickChangedSignal) emit watchedNickChanged(this, nickname, true);
+ if (doChannelJoinedSignal) emit channelJoinedOrUnjoined(this, channelName, true);
+ if (doChannelMembersChangedSignal) emit channelMembersChanged(this, channelName, true, false, nickname);
+ return channelNick;
+}
+
+/** This function should _only_ be called from the ChannelNick class.
+ * This function should also be the only one to emit this signal.
+ * In this class, when channelNick is changed, it emits its own signal, and
+ * calls this function itself.
+ */
+void Server::emitChannelNickChanged(const ChannelNickPtr channelNick)
+{
+ emit channelNickChanged(this, channelNick);
+}
+
+/** This function should _only_ be called from the NickInfo class.
+ * This function should also be the only one to emit this signal.
+ * In this class, when nickInfo is changed, it emits its own signal, and
+ * calls this function itself.
+ */
+void Server::emitNickInfoChanged(const NickInfoPtr nickInfo)
+{
+ emit nickInfoChanged(this, nickInfo);
+}
+
+// Adds a nickname to the unjoinedChannels list.
+// Creates new NickInfo if necessary.
+// If needed, moves the channel from the joined list to the unjoined list.
+// If mode != 99 sets the mode for this nick in this channel.
+// Returns the NickInfo for the nickname.
+ChannelNickPtr Server::addNickToUnjoinedChannelsList(const QString& channelName, const QString& nickname)
+{
+ bool doChannelUnjoinedSignal = false;
+ bool doWatchedNickChangedSignal = false;
+ bool doChannelMembersChangedSignal = false;
+ QString lcNickname = nickname.lower();
+ // Create NickInfo if not already created.
+ NickInfoPtr nickInfo = getNickInfo(nickname);
+ if (!nickInfo)
+ {
+ nickInfo = new NickInfo(nickname, this);
+ m_allNicks.insert(lcNickname, nickInfo);
+ doWatchedNickChangedSignal = isWatchedNick(nickname);
+ }
+ // Move the channel from joined list (if present) to unjoined list.
+ QString lcChannelName = channelName.lower();
+ ChannelNickMap *channel;
+ if (m_joinedChannels.contains(lcChannelName))
+ {
+ channel = m_joinedChannels[lcChannelName];
+ m_joinedChannels.remove(lcChannelName);
+ m_unjoinedChannels.insert(lcChannelName, channel);
+ doChannelUnjoinedSignal = true;
+ }
+ else
+ {
+ // Create a new list in the unjoined channels if not already present.
+ if (!m_unjoinedChannels.contains(lcChannelName))
+ {
+ channel = new ChannelNickMap;
+ m_unjoinedChannels.insert(lcChannelName, channel);
+ doChannelUnjoinedSignal = true;
+ }
+ else
+ channel = m_unjoinedChannels[lcChannelName];
+ }
+ // Add NickInfo to unjoinedChannels list if not already in the list.
+ ChannelNickPtr channelNick;
+ if (!channel->contains(lcNickname))
+ {
+ channelNick = new ChannelNick(nickInfo, false, false, false, false, false);
+ channel->insert(lcNickname, channelNick);
+ doChannelMembersChangedSignal = true;
+ }
+ channelNick = (*channel)[lcNickname];
+ // Set the mode for the nick in this channel.
+ if (doWatchedNickChangedSignal) emit watchedNickChanged(this, nickname, true);
+ if (doChannelUnjoinedSignal) emit channelJoinedOrUnjoined(this, channelName, false);
+ if (doChannelMembersChangedSignal) emit channelMembersChanged(this, channelName, false, false, nickname);
+ return channelNick;
+}
+
+/**
+ * If not already online, changes a nick to the online state by creating
+ * a NickInfo for it and emits various signals and messages for it.
+ * This method should only be called for nicks on the watch list.
+ * @param nickname The nickname that is online.
+ * @return Pointer to NickInfo for nick.
+ */
+NickInfoPtr Server::setWatchedNickOnline(const QString& nickname)
+{
+ NickInfoPtr nickInfo = getNickInfo(nickname);
+ if (!nickInfo)
+ {
+ QString lcNickname = nickname.lower();
+ nickInfo = new NickInfo(nickname, this);
+ m_allNicks.insert(lcNickname, nickInfo);
+ }
+
+ emit watchedNickChanged(this, nickname, true);
+ KABC::Addressee addressee = nickInfo->getAddressee();
+ if (!addressee.isEmpty()) Konversation::Addressbook::self()->emitContactPresenceChanged(addressee.uid());
+
+ appendMessageToFrontmost(i18n("Notify"),"<a href=\"#"+nickname+"\">"+
+ i18n("%1 is online (%2).").arg(nickname).arg(getServerName())+"</a>", getStatusView());
+
+ static_cast<KonversationApplication*>(kapp)->notificationHandler()->nickOnline(getStatusView(), nickname);
+
+ nickInfo->setPrintedOnline(true);
+ return nickInfo;
+}
+
+void Server::setWatchedNickOffline(const QString& nickname, const NickInfoPtr nickInfo)
+{
+ if (nickInfo) {
+ KABC::Addressee addressee = nickInfo->getAddressee();
+ if (!addressee.isEmpty()) Konversation::Addressbook::self()->emitContactPresenceChanged(addressee.uid(), 1);
+ }
+
+ emit watchedNickChanged(this, nickname, false);
+
+ appendMessageToFrontmost(i18n("Notify"), i18n("%1 went offline (%2).").arg(nickname).arg(getServerName()), getStatusView());
+
+ static_cast<KonversationApplication*>(kapp)->notificationHandler()->nickOffline(getStatusView(), nickname);
+
+}
+
+bool Server::setNickOffline(const QString& nickname)
+{
+ QString lcNickname = nickname.lower();
+ NickInfoPtr nickInfo = getNickInfo(lcNickname);
+ bool wasOnline = nickInfo->getPrintedOnline();
+
+ if (nickInfo && wasOnline)
+ {
+ // Delete from query list, if present.
+ if (m_queryNicks.contains(lcNickname)) m_queryNicks.remove(lcNickname);
+ // Delete the nickname from all channels (joined or unjoined).
+ QStringList nickChannels = getNickChannels(lcNickname);
+ QStringList::iterator itEnd = nickChannels.end();
+
+ for(QStringList::iterator it = nickChannels.begin(); it != itEnd; ++it)
+ {
+ QString channel = (*it);
+ removeChannelNick(channel, lcNickname);
+ }
+
+ // Delete NickInfo.
+ if (m_allNicks.contains(lcNickname)) m_allNicks.remove(lcNickname);
+ // If the nick was in the watch list, emit various signals and messages.
+ if (isWatchedNick(nickname)) setWatchedNickOffline(nickname, nickInfo);
+
+ nickInfo->setPrintedOnline(false);
+ }
+
+ return (nickInfo != 0);
+}
+
+/**
+ * If nickname is no longer on any channel list, or the query list, delete it altogether.
+ * Call this routine only if the nick is not on the notify list or is on the notify
+ * list but is known to be offline.
+ * @param nickname The nickname to be deleted. Case insensitive.
+ * @return True if the nickname is deleted.
+ */
+bool Server::deleteNickIfUnlisted(const QString &nickname)
+{
+ QString lcNickname = nickname.lower();
+ // Don't delete our own nickinfo.
+ if (lcNickname == loweredNickname()) return false;
+
+ if (!m_queryNicks.contains(lcNickname))
+ {
+ QStringList nickChannels = getNickChannels(nickname);
+ if (nickChannels.isEmpty())
+ {
+ m_allNicks.remove(lcNickname);
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Remove nickname from a channel (on joined or unjoined lists).
+ * @param channelName The channel name. Case insensitive.
+ * @param nickname The nickname. Case insensitive.
+ */
+void Server::removeChannelNick(const QString& channelName, const QString& nickname)
+{
+ bool doSignal = false;
+ bool joined = false;
+ QString lcChannelName = channelName.lower();
+ QString lcNickname = nickname.lower();
+ ChannelNickMap *channel;
+ if (m_joinedChannels.contains(lcChannelName))
+ {
+ channel = m_joinedChannels[lcChannelName];
+ if (channel->contains(lcNickname))
+ {
+ channel->remove(lcNickname);
+ doSignal = true;
+ joined = true;
+ // Note: Channel should not be empty because user's own nick should still be
+ // in it, so do not need to delete empty channel here.
+ }
+ }
+ else
+ {
+ if (m_unjoinedChannels.contains(lcChannelName))
+ {
+ channel = m_unjoinedChannels[lcChannelName];
+ if (channel->contains(lcNickname))
+ {
+ channel->remove(lcNickname);
+ doSignal = true;
+ joined = false;
+ // If channel is now empty, delete it.
+ // Caution: Any iterators across unjoinedChannels will be come invalid here.
+ if (channel->isEmpty()) m_unjoinedChannels.remove(lcChannelName);
+ }
+ }
+ }
+ if (doSignal) emit channelMembersChanged(this, channelName, joined, true, nickname);
+}
+
+QStringList Server::getWatchList()
+{
+ // no nickinfo ISON for the time being
+ return Preferences::notifyListByGroupName(getDisplayName());
+ if (m_serverISON)
+ return m_serverISON->getWatchList();
+ else
+ return QStringList();
+}
+
+QString Server::getWatchListString() { return getWatchList().join(" "); }
+
+QStringList Server::getISONList()
+{
+ // no nickinfo ISON for the time being
+ return Preferences::notifyListByGroupName(getDisplayName());
+ if (m_serverISON)
+ return m_serverISON->getISONList();
+ else
+ return QStringList();
+}
+
+QString Server::getISONListString() { return getISONList().join(" "); }
+
+/**
+ * Return true if the given nickname is on the watch list.
+ */
+bool Server::isWatchedNick(const QString& nickname)
+{
+ // Get watch list from preferences.
+ QString watchlist= ' ' + getWatchListString() + ' ';
+ // Search case-insensitivly
+ return (watchlist.find(' ' + nickname + ' ', 0, 0) != -1);
+}
+
+/**
+ * Remove channel from the joined list, placing it in the unjoined list.
+ * All the unwatched nicks are removed from the channel. If the channel becomes
+ * empty, it is deleted.
+ * @param channelName Name of the channel. Case sensitive.
+ */
+void Server::removeJoinedChannel(const QString& channelName)
+{
+ bool doSignal = false;
+ QStringList watchListLower = getWatchList();
+ QString lcChannelName = channelName.lower();
+ // Move the channel nick list from the joined to unjoined lists.
+ if (m_joinedChannels.contains(lcChannelName))
+ {
+ doSignal = true;
+ ChannelNickMap* channel = m_joinedChannels[lcChannelName];
+ m_joinedChannels.remove(lcChannelName);
+ m_unjoinedChannels.insert(lcChannelName, channel);
+ // Remove nicks not on the watch list.
+ bool allDeleted = true;
+ Q_ASSERT(channel);
+ if(!channel) return; //already removed.. hmm
+ ChannelNickMap::Iterator member;
+ for ( member = channel->begin(); member != channel->end() ;)
+ {
+ QString lcNickname = member.key();
+ if (watchListLower.find(lcNickname) == watchListLower.end())
+ {
+ // Remove the unwatched nickname from the unjoined channel.
+ channel->remove(member);
+ // If the nick is no longer listed in any channels or query list, delete it altogether.
+ deleteNickIfUnlisted(lcNickname);
+ member = channel->begin();
+ }
+ else
+ {
+ allDeleted = false;
+ ++member;
+ }
+ }
+ // If all were deleted, remove the channel from the unjoined list.
+ if (allDeleted)
+ {
+ channel = m_unjoinedChannels[lcChannelName];
+ m_unjoinedChannels.remove(lcChannelName);
+ delete channel; // recover memory!
+ }
+ }
+ if (doSignal) emit channelJoinedOrUnjoined(this, channelName, false);
+}
+
+// Renames a nickname in all NickInfo lists.
+// Returns pointer to the NickInfo object or 0 if nick not found.
+void Server::renameNickInfo(NickInfoPtr nickInfo, const QString& newname)
+{
+ if (nickInfo)
+ {
+ // Get existing lowercase nickname and rename nickname in the NickInfo object.
+ QString lcNickname = nickInfo->loweredNickname();
+ nickInfo->setNickname(newname);
+ nickInfo->setIdentified(false);
+ QString lcNewname = newname.lower();
+ // Rename the key in m_allNicks list.
+ m_allNicks.remove(lcNickname);
+ m_allNicks.insert(lcNewname, nickInfo);
+ // Rename key in the joined and unjoined lists.
+ QStringList nickChannels = getNickChannels(lcNickname);
+ QStringList::iterator itEnd = nickChannels.end();
+
+ for(QStringList::iterator it = nickChannels.begin(); it != itEnd; ++it)
+ {
+ const ChannelNickMap *channel = getChannelMembers(*it);
+ Q_ASSERT(channel);
+ ChannelNickPtr member = (*channel)[lcNickname];
+ Q_ASSERT(member);
+ const_cast<ChannelNickMap *>(channel)->remove(lcNickname);
+ const_cast<ChannelNickMap *>(channel)->insert(lcNewname, member);
+ }
+
+ // Rename key in Query list.
+ if (m_queryNicks.contains(lcNickname))
+ {
+ m_queryNicks.remove(lcNickname);
+ m_queryNicks.insert(lcNewname, nickInfo);
+ }
+ }
+ else
+ {
+ kdDebug() << "server::renameNickInfo() was called for newname='" << newname << "' but nickInfo is null" << endl;
+ }
+}
+
+Channel* Server::nickJoinsChannel(const QString &channelName, const QString &nickname, const QString &hostmask)
+{
+ Channel* outChannel=getChannelByName(channelName);
+ if(outChannel)
+ {
+ // Update NickInfo.
+ ChannelNickPtr channelNick = addNickToJoinedChannelsList(channelName, nickname);
+ NickInfoPtr nickInfo = channelNick->getNickInfo();
+ if ((nickInfo->getHostmask() != hostmask) && !hostmask.isEmpty())
+ {
+ nickInfo->setHostmask(hostmask);
+ }
+ outChannel->joinNickname(channelNick);
+ }
+
+ return outChannel;
+}
+
+void Server::addHostmaskToNick(const QString& sourceNick, const QString& sourceHostmask)
+{
+ // Update NickInfo.
+ NickInfoPtr nickInfo=getNickInfo(sourceNick);
+ if (nickInfo)
+ {
+ if ((nickInfo->getHostmask() != sourceHostmask) && !sourceHostmask.isEmpty())
+ {
+ nickInfo->setHostmask(sourceHostmask);
+ }
+ }
+}
+
+Channel* Server::removeNickFromChannel(const QString &channelName, const QString &nickname, const QString &reason, bool quit)
+{
+ Channel* outChannel=getChannelByName(channelName);
+ if(outChannel)
+ {
+ ChannelNickPtr channelNick = getChannelNick(channelName, nickname);
+ if(channelNick) outChannel->removeNick(channelNick,reason,quit);
+ }
+
+ // Remove the nick from the channel.
+ removeChannelNick(channelName, nickname);
+ // If not listed in any channel, and not on query list, delete the NickInfo,
+ // but only if not on the notify list. ISON replies will take care of deleting
+ // the NickInfo, if on the notify list.
+ if (!isWatchedNick(nickname))
+ {
+ QString nicky = nickname;
+ deleteNickIfUnlisted(nicky);
+ }
+
+ return outChannel;
+}
+
+void Server::nickWasKickedFromChannel(const QString &channelName, const QString &nickname, const QString &kicker, const QString &reason)
+{
+ Channel* outChannel=getChannelByName(channelName);
+ if(outChannel)
+ {
+ ChannelNickPtr channelNick = getChannelNick(channelName, nickname);
+
+ if(channelNick)
+ {
+ outChannel->kickNick(channelNick, kicker, reason);
+ // Tell Nickinfo
+ removeChannelNick(channelName,nickname);
+ }
+ }
+}
+
+void Server::removeNickFromServer(const QString &nickname,const QString &reason)
+{
+ Channel* channel = m_channelList.first();
+ while (channel)
+ {
+ // Check if nick is in this channel or not.
+ if(channel->getNickByName(nickname))
+ removeNickFromChannel(channel->getName(),nickname,reason,true);
+
+ channel = m_channelList.next();
+ }
+
+ Query* query=getQueryByName(nickname);
+ if (query) query->quitNick(reason);
+
+ // Delete the nick from all channels and then delete the nickinfo,
+ // emitting signal if on the watch list.
+ setNickOffline(nickname);
+}
+
+void Server::renameNick(const QString &nickname, const QString &newNick)
+{
+ if(nickname.isEmpty() || newNick.isEmpty())
+ {
+ kdDebug() << "server::renameNick called with empty strings! Trying to rename '" << nickname << "' to '" << newNick << "'" << endl;
+ return;
+ }
+
+ // If this was our own nickchange, tell our server object about it
+ if (nickname == getNickname())
+ {
+ setNickname(newNick);
+
+ // We may get a request from nickserv, so remove the auto-identify lock.
+ m_autoIdentifyLock = false;
+ }
+
+ //Actually do the rename.
+ NickInfoPtr nickInfo = getNickInfo(nickname);
+
+ if(!nickInfo)
+ {
+ kdDebug() << "server::renameNick called for nickname '" << nickname << "' to '" << newNick << "' but getNickInfo('" << nickname << "') returned no results." << endl;
+ }
+ else
+ {
+ renameNickInfo(nickInfo, newNick);
+ //The rest of the code below allows the channels to echo to the user to tell them that the nick has changed.
+
+ // Rename the nick in every channel they are in
+ Channel* channel=m_channelList.first();
+ while (channel)
+ {
+ // All we do is notify that the nick has been renamed.. we haven't actually renamed it yet
+ // Note that NickPanel has already updated, so pass new nick to getNickByName.
+ if (channel->getNickByName(newNick)) channel->nickRenamed(nickname, *nickInfo);
+ channel = m_channelList.next();
+ }
+ //Watched nicknames stuff
+ if (isWatchedNick(nickname)) setWatchedNickOffline(nickname, 0);
+ }
+ // If we had a query with this nick, change that name, too
+
+}
+
+void Server::userhost(const QString& nick,const QString& hostmask,bool away,bool /* ircOp */)
+{
+ addHostmaskToNick(nick, hostmask);
+ // remember my IP for DCC things
+ // myself
+ if (m_ownIpByUserhost.isEmpty() && nick == getNickname())
+ {
+ QString myhost = hostmask.section('@', 1);
+ // Use async lookup else you will be blocking GUI badly
+ KNetwork::KResolver::resolveAsync(this,SLOT(gotOwnResolvedHostByUserhost(KResolverResults)),myhost,"0");
+ }
+ NickInfoPtr nickInfo = getNickInfo(nick);
+ if (nickInfo)
+ {
+ if (nickInfo->isAway() != away)
+ {
+ nickInfo->setAway(away);
+ }
+ }
+}
+
+void Server::gotOwnResolvedHostByUserhost(KResolverResults res)
+{
+ if ( res.error() == KResolver::NoError && !res.isEmpty() )
+ m_ownIpByUserhost = res.first().address().nodeName();
+ else
+ kdDebug() << "Server::gotOwnResolvedHostByUserhost(): Got error: " << ( int )res.error() << endl;
+}
+
+void Server::appendServerMessageToChannel(const QString& channel,const QString& type,const QString& message)
+{
+ Channel* outChannel = getChannelByName(channel);
+ if (outChannel) outChannel->appendServerMessage(type,message);
+}
+
+void Server::appendCommandMessageToChannel(const QString& channel,const QString& command,const QString& message, bool highlight)
+{
+ Channel* outChannel = getChannelByName(channel);
+ if (outChannel)
+ {
+ outChannel->appendCommandMessage(command,message,true,true,!highlight);
+ }
+ else
+ {
+ appendStatusMessage(command, QString("%1 %2").arg(channel).arg(message));
+ }
+}
+
+void Server::appendStatusMessage(const QString& type,const QString& message)
+{
+ getStatusView()->appendServerMessage(type,message);
+}
+
+void Server::appendMessageToFrontmost(const QString& type,const QString& message, bool parseURL)
+{
+ getViewContainer()->appendToFrontmost(type, message, getStatusView(), parseURL);
+}
+
+void Server::setNickname(const QString &newNickname)
+{
+ m_nickname = newNickname;
+ m_loweredNickname = newNickname.lower();
+ emit nicknameChanged(newNickname);
+}
+
+void Server::setChannelTopic(const QString &channel, const QString &newTopic)
+{
+ Channel* outChannel = getChannelByName(channel);
+ if(outChannel)
+ {
+ // encoding stuff is done in send()
+ outChannel->setTopic(newTopic);
+ }
+}
+
+ // Overloaded
+void Server::setChannelTopic(const QString& nickname, const QString &channel, const QString &newTopic)
+{
+ Channel* outChannel = getChannelByName(channel);
+ if(outChannel)
+ {
+ // encoding stuff is done in send()
+ outChannel->setTopic(nickname,newTopic);
+ }
+}
+
+void Server::setTopicAuthor(const QString& channel, const QString& author, QDateTime time)
+{
+ Channel* outChannel = getChannelByName(channel);
+ if(outChannel)
+ outChannel->setTopicAuthor(author, time);
+}
+
+void Server::endOfWho(const QString& target)
+{
+ Channel* channel = getChannelByName(target);
+ if(channel)
+ channel->scheduleAutoWho();
+}
+
+bool Server::isNickname(const QString &compare) const
+{
+ return (m_nickname == compare);
+}
+
+QString Server::getNickname() const
+{
+ return m_nickname;
+}
+
+QString Server::loweredNickname() const
+{
+ return m_loweredNickname;
+}
+
+QString Server::parseWildcards(const QString& toParse,
+const QString& sender,
+const QString& channelName,
+const QString& channelKey,
+const QString& nick,
+const QString& parameter)
+{
+ return parseWildcards(toParse,sender,channelName,channelKey,QStringList::split(' ',nick),parameter);
+}
+
+QString Server::parseWildcards(const QString& toParse,
+const QString& sender,
+const QString& channelName,
+const QString& channelKey,
+const QStringList& nickList,
+const QString& /*parameter*/)
+{
+ // TODO: parameter handling, since parameters are not functional yet
+
+ // store the parsed version
+ QString out;
+
+ // default separator
+ QString separator(" ");
+
+ int index = 0, found = 0;
+ QChar toExpand;
+
+ while ((found = toParse.find('%',index)) != -1)
+ {
+ // append part before the %
+ out.append(toParse.mid(index,found-index));
+ index = found + 1; // skip the part before, including %
+ if (index >= (int)toParse.length())
+ break; // % was the last char (not valid)
+ toExpand = toParse.at(index++);
+ if (toExpand == 's')
+ {
+ found = toParse.find('%',index);
+ if (found == -1) // no other % (not valid)
+ break;
+ separator = toParse.mid(index,found-index);
+ index = found + 1; // skip separator, including %
+ }
+ else if (toExpand == 'u')
+ {
+ out.append(nickList.join(separator));
+ }
+ else if (toExpand == 'c')
+ {
+ if(!channelName.isEmpty())
+ out.append(channelName);
+ }
+ else if (toExpand == 'o')
+ {
+ out.append(sender);
+ }
+ else if (toExpand == 'k')
+ {
+ if(!channelKey.isEmpty())
+ out.append(channelKey);
+ }
+ else if (toExpand == 'K')
+ {
+ if(getConnectionSettings().server().password().isEmpty())
+ out.append(getConnectionSettings().server().password());
+ }
+ else if (toExpand == 'n')
+ {
+ out.append("\n");
+ }
+ else if (toExpand == 'p')
+ {
+ out.append("%");
+ }
+ }
+
+ // append last part
+ out.append(toParse.mid(index,toParse.length()-index));
+ return out;
+}
+
+void Server::sendToAllChannels(const QString &text)
+{
+ // Send a message to all channels we are in
+ Channel* channel = m_channelList.first();
+ while (channel)
+ {
+ channel->sendChannelText(text);
+ channel = m_channelList.next();
+ }
+}
+
+void Server::invitation(const QString& nick,const QString& channel)
+{
+ if(KMessageBox::questionYesNo(getViewContainer()->getWindow(),
+ i18n("You were invited by %1 to join channel %2. "
+ "Do you accept the invitation?").arg(nick).arg(channel),
+ i18n("Invitation"),
+ i18n("Join"),
+ i18n("Ignore"),
+ "Invitation")==KMessageBox::Yes)
+ {
+ sendJoinCommand(channel);
+ }
+}
+
+void Server::scriptNotFound(const QString& name)
+{
+ appendMessageToFrontmost(i18n("DCOP"),i18n("Error: Could not find script \"%1\".").arg(name));
+}
+
+void Server::scriptExecutionError(const QString& name)
+{
+ appendMessageToFrontmost(i18n("DCOP"),i18n("Error: Could not execute script \"%1\". Check file permissions.").arg(name));
+}
+
+bool Server::isAChannel(const QString &channel) const
+{
+ return (getChannelTypes().contains(channel.at(0)) > 0);
+}
+
+void Server::addRawLog(bool show)
+{
+ if (!m_rawLog) m_rawLog = getViewContainer()->addRawLog(this);
+
+ connect(this, SIGNAL(serverOnline(bool)), m_rawLog, SLOT(serverOnline(bool)));
+
+ // bring raw log to front since the main window does not do this for us
+ if (show) emit showView(m_rawLog);
+}
+
+void Server::closeRawLog()
+{
+ if (m_rawLog) delete m_rawLog;
+}
+
+ChannelListPanel* Server::addChannelListPanel()
+{
+ if(!m_channelListPanel)
+ {
+ m_channelListPanel = getViewContainer()->addChannelListPanel(this);
+
+ connect(m_channelListPanel, SIGNAL(refreshChannelList()), this, SLOT(requestChannelList()));
+ connect(m_channelListPanel, SIGNAL(joinChannel(const QString&)), this, SLOT(sendJoinCommand(const QString&)));
+ connect(this, SIGNAL(serverOnline(bool)), m_channelListPanel, SLOT(serverOnline(bool)));
+ }
+
+ return m_channelListPanel;
+}
+
+void Server::addToChannelList(const QString& channel, int users, const QString& topic)
+{
+ addChannelListPanel();
+ m_channelListPanel->addToChannelList(channel, users, topic);
+}
+
+ChannelListPanel* Server::getChannelListPanel() const
+{
+ return m_channelListPanel;
+}
+
+void Server::closeChannelListPanel()
+{
+ if (m_channelListPanel) delete m_channelListPanel;
+}
+
+void Server::updateAutoJoin(Konversation::ChannelSettings channel)
+{
+ if (!channel.name().isEmpty())
+ {
+ setAutoJoin(true);
+
+ setAutoJoinCommands(QStringList("JOIN " + channel.name() + " " + channel.password()));
+
+ return;
+ }
+
+ Konversation::ChannelList tmpList;
+
+ if (m_channelList.isEmpty() && getServerGroup())
+ tmpList = getServerGroup()->channelList();
+ else
+ {
+ QPtrListIterator<Channel> it(m_channelList);
+ Channel* channel;
+
+ while ((channel = it.current()) != 0)
+ {
+ ++it;
+ tmpList << channel->channelSettings();
+ }
+ }
+
+ if (!tmpList.isEmpty())
+ {
+ setAutoJoin(true);
+
+ QStringList channels;
+ QStringList passwords;
+ QStringList joinCommands;
+ uint length = 0;
+
+ Konversation::ChannelList::iterator it;
+
+ for (it = tmpList.begin(); it != tmpList.end(); ++it)
+ {
+ QString channel = (*it).name();;
+ QString password = ((*it).password().isEmpty() ? "." : (*it).password());
+
+ int tempLen = channel.length();
+ length += getIdentity()->getCodec()->fromUnicode(channel, tempLen).length();
+ tempLen = password.length();
+ length += getIdentity()->getCodec()->fromUnicode(password, tempLen).length();
+
+ if (length + 6 < 512) // 6: "JOIN " plus separating space between chans and pws.
+ {
+ channels << channel;
+ passwords << password;
+ }
+ else
+ {
+ if (passwords.last() == ".") passwords.pop_back();
+
+ joinCommands << "JOIN " + channels.join(",") + " " + passwords.join(",");
+
+ channels.clear();
+ passwords.clear();
+
+ channels << channel;
+ passwords << password;
+
+ length = 0;
+
+ tempLen = channel.length();
+ length += getIdentity()->getCodec()->fromUnicode(channel, tempLen).length();
+ tempLen = password.length();
+ length += getIdentity()->getCodec()->fromUnicode(password, tempLen).length();
+ }
+ }
+
+ if (passwords.last() == ".") passwords.pop_back();
+
+ joinCommands << "JOIN " + channels.join(",") + " " + passwords.join(",");
+
+ setAutoJoinCommands(joinCommands);
+ }
+ else
+ setAutoJoin(false);
+}
+
+ViewContainer* Server::getViewContainer() const
+{
+ KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
+ return konvApp->getMainWindow()->getViewContainer();
+}
+
+bool Server::getUseSSL() const
+{
+ SSLSocket* sslsocket = dynamic_cast<SSLSocket*>(m_socket);
+
+ return (sslsocket != 0);
+}
+
+QString Server::getSSLInfo() const
+{
+ SSLSocket* sslsocket = dynamic_cast<SSLSocket*>(m_socket);
+
+ if(sslsocket)
+ return sslsocket->details();
+
+ return QString();
+}
+
+void Server::sendMultiServerCommand(const QString& command, const QString& parameter)
+{
+ emit multiServerCommand(command, parameter);
+}
+
+void Server::executeMultiServerCommand(const QString& command, const QString& parameter)
+{
+ if (command == "msg")
+ sendToAllChannelsAndQueries(parameter);
+ else
+ sendToAllChannelsAndQueries(Preferences::commandChar() + command + ' ' + parameter);
+}
+
+void Server::sendToAllChannelsAndQueries(const QString& text)
+{
+ // Send a message to all channels we are in
+ Channel* channel = m_channelList.first();
+
+ while (channel)
+ {
+ channel->sendChannelText(text);
+ channel = m_channelList.next();
+ }
+
+ // Send a message to all queries we are in
+ class Query* query = m_queryList.first();
+
+ while (query)
+ {
+ query->sendQueryText(text);
+ query = m_queryList.next();
+ }
+}
+
+bool Server::isSocketConnected() const
+{
+ if (!m_socket) return false;
+
+ return (m_socket->state() == KNetwork::KClientSocketBase::Connected);
+}
+
+void Server::updateConnectionState(Konversation::ConnectionState state)
+{
+ if (state != m_connectionState)
+ {
+ m_connectionState = state;
+
+ if (m_connectionState == Konversation::SSConnected)
+ emit serverOnline(true);
+ else if (m_connectionState != Konversation::SSConnecting)
+ emit serverOnline(false);
+
+ emit connectionStateChanged(this, state);
+ }
+}
+
+void Server::reconnect()
+{
+ if (isConnecting() || isSocketConnected()) quitServer();
+
+ // Use asynchronous invocation so that the broken() that the above
+ // quitServer might cause is delivered before connectToIRCServer
+ // sets SSConnecting and broken() announces a deliberate disconnect
+ // due to the failure allegedly occuring during SSConnecting.
+ QTimer::singleShot(0, this, SLOT(connectToIRCServer()));
+}
+
+void Server::disconnect()
+{
+ if (isSocketConnected()) quitServer();
+}
+
+void Server::requestAway(const QString& reason)
+{
+ QString awayReason = reason;
+ IdentityPtr identity = getIdentity();
+
+ if (awayReason.isEmpty() || !identity)
+ awayReason = i18n("Gone away for now");
+
+ setAwayReason(awayReason);
+
+ queue("AWAY :" + awayReason);
+}
+
+void Server::requestUnaway()
+{
+ queue("AWAY");
+}
+
+void Server::setAway(bool away)
+{
+ IdentityPtr identity = getIdentity();
+
+ if (away)
+ {
+ if (!m_away) startAwayTimer();
+
+ m_away = true;
+
+ emit awayState(true);
+
+ if (identity && !identity->getAwayNick().isEmpty() && identity->getAwayNick() != getNickname())
+ {
+ m_nonAwayNick = getNickname();
+ queue("NICK " + getIdentity()->getAwayNick());
+ }
+
+ appendMessageToFrontmost(i18n("Away"), i18n("You are now marked as being away."));
+
+ if (identity && identity->getShowAwayMessage())
+ {
+ QString message = identity->getAwayMessage();
+ sendToAllChannels(message.replace(QRegExp("%s", false), m_awayReason));
+ }
+
+ if (identity && identity->getInsertRememberLineOnAway())
+ emit awayInsertRememberLine(this);
+ }
+ else
+ {
+ m_awayReason = QString();
+
+ emit awayState(false);
+
+ if (!identity->getAwayNick().isEmpty() && !m_nonAwayNick.isEmpty())
+ {
+ queue("NICK " + m_nonAwayNick);
+ m_nonAwayNick = "";
+ }
+
+ if (m_away)
+ {
+ appendMessageToFrontmost(i18n("Away"), i18n("You are no longer marked as being away."));
+
+ if (identity && identity->getShowAwayMessage())
+ {
+ QString message = identity->getReturnMessage();
+ sendToAllChannels(message.replace(QRegExp("%t", false), awayTime()));
+ }
+ }
+ else
+ appendMessageToFrontmost(i18n("Away"), i18n("You are not marked as being away."));
+
+ m_away = false;
+ }
+}
+
+QString Server::awayTime() const
+{
+ QString retVal;
+
+ if (m_away)
+ {
+ int diff = QDateTime::currentDateTime().toTime_t() - m_awayTime;
+ int num = diff / 3600;
+
+ if (num < 10)
+ retVal = '0' + QString::number(num) + ':';
+ else
+ retVal = QString::number(num) + ':';
+
+ num = (diff % 3600) / 60;
+
+ if (num < 10) retVal += '0';
+
+ retVal += QString::number(num) + ':';
+
+ num = (diff % 3600) % 60;
+
+ if (num < 10) retVal += '0';
+
+ retVal += QString::number(num);
+ }
+ else
+ retVal = "00:00:00";
+
+ return retVal;
+}
+
+void Server::startAwayTimer()
+{
+ m_awayTime = QDateTime::currentDateTime().toTime_t();
+}
+
+KABC::Addressee Server::getOfflineNickAddressee(QString& nickname)
+{
+ if (m_serverISON)
+ return m_serverISON->getOfflineNickAddressee(nickname);
+ else
+ return KABC::Addressee();
+}
+
+void Server::enableIdentifyMsg(bool enabled)
+{
+ m_identifyMsg = enabled;
+}
+
+bool Server::identifyMsgEnabled()
+{
+ return m_identifyMsg;
+}
+
+void Server::addBan(const QString &channel, const QString &ban)
+{
+ Channel* outChannel = getChannelByName(channel);
+ if(outChannel)
+ {
+ outChannel->addBan(ban);
+ }
+}
+
+void Server::removeBan(const QString &channel, const QString &ban)
+{
+ Channel* outChannel = getChannelByName(channel);
+ if(outChannel)
+ {
+ outChannel->removeBan(ban);
+ }
+}
+
+void Server::sendPing()
+{
+ //WHO ourselves once a minute in case the irc server has changed our
+ //hostmask, such as what happens when a Freenode cloak is activated.
+ //It might be more intelligent to only do this when there is text
+ //in the inputbox. Kinda changes this into a "do minutely"
+ //queue :-)
+ QStringList ql;
+ ql << "PING LAG" + QTime::currentTime().toString("hhmmss");
+ getInputFilter()->setAutomaticRequest("WHO", getNickname(), true);
+ ql << "WHO " + getNickname();
+ queueList(ql, HighPriority);
+
+ m_lagTime.start();
+ m_inputFilter.setLagMeasuring(true);
+ m_pingResponseTimer.start(1000 /*1 sec*/);
+}
+
+void Server::pongReceived()
+{
+ m_currentLag = m_lagTime.elapsed();
+ m_inputFilter.setLagMeasuring(false);
+ m_pingResponseTimer.stop();
+
+ emit serverLag(this, m_currentLag);
+
+ // Send another PING in 60 seconds
+ QTimer::singleShot(60000 /*60 sec*/, this, SLOT(sendPing()));
+}
+
+void Server::updateLongPongLag()
+{
+ if (isSocketConnected())
+ {
+ m_currentLag = m_lagTime.elapsed();
+ emit tooLongLag(this, m_currentLag);
+ // kdDebug() << "Current lag: " << currentLag << endl;
+
+ if (m_currentLag > (Preferences::maximumLagTime() * 1000))
+ m_socket->close();
+ }
+}
+
+void Server::updateEncoding()
+{
+ if(getViewContainer() && getViewContainer()->getFrontView())
+ getViewContainer()->updateViewEncoding(getViewContainer()->getFrontView());
+}
+
+#include "server.moc"
+
+// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
+// vim: set et sw=4 ts=4 cino=l1,cs,U1:
diff --git a/konversation/src/server.h b/konversation/src/server.h
new file mode 100644
index 0000000..e2781fb
--- /dev/null
+++ b/konversation/src/server.h
@@ -0,0 +1,717 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2005-2006 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eli J. MacKenzie <argonel at gmail.com>
+ Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef SERVER_H
+#define SERVER_H
+
+#include "common.h"
+#include "channelnick.h"
+#include "inputfilter.h"
+#include "outputfilter.h"
+#include "nickinfo.h"
+#include "sslsocket.h"
+#include "serversettings.h"
+#include "servergroupsettings.h"
+#include "connectionsettings.h"
+
+#include <qtimer.h>
+#include <qvaluevector.h>
+#include <qguardedptr.h>
+
+#include <ksharedptr.h>
+#include <kprocess.h>
+#include <ksocketbase.h>
+#include <kbufferedsocket.h>
+#include <kstreamsocket.h>
+
+
+class Channel;
+class DccTransfer;
+class DccTransferPanelItem;
+class Query;
+class StatusPanel;
+class Identity;
+class RawLog;
+class ChannelListPanel;
+class ScriptLauncher;
+class ServerISON;
+class QStrList;
+class ChatWindow;
+class ViewContainer;
+
+using namespace KNetwork;
+
+class IRCQueue;
+
+class Server : public QObject
+{
+ Q_OBJECT
+ friend class IRCQueue;
+ friend class QueueTuner;
+void resetNickSelection();
+ public:
+ enum QueuePriority
+ {
+ LowPriority, ///<slow queue, for automatic info gathering
+ StandardPriority, ///<regular queue, for chat and user initiated commands
+ HighPriority, ///<for pongs and quits
+
+ Howmanyqueuesdoweneedanywayquestionmark,
+
+ HiPriority=HighPriority,
+ LoPriority=LowPriority,
+ NormalPriorty=StandardPriority,
+ RegularPriority=StandardPriority
+ };
+
+ Server(QObject* parent, ConnectionSettings& settings);
+ ~Server();
+
+ int connectionId() { return m_connectionId; }
+
+ ConnectionSettings& getConnectionSettings() { return m_connectionSettings; }
+ void setConnectionSettings(ConnectionSettings& settings) { m_connectionSettings = settings; }
+
+ QString getDisplayName() { return m_connectionSettings.name(); }
+ QString getServerName() { return m_connectionSettings.server().host(); }
+
+ Konversation::ServerGroupSettingsPtr getServerGroup() { return m_connectionSettings.serverGroup(); }
+ IdentityPtr getIdentity() { return m_connectionSettings.identity(); }
+
+ Konversation::ConnectionState getConnectionState() { return m_connectionState; }
+
+ bool isConnected() { return (m_connectionState == Konversation::SSConnected); }
+ bool isConnecting() { return (m_connectionState == Konversation::SSConnecting); }
+
+ bool getUseSSL() const;
+ QString getSSLInfo() const;
+ int getPort();
+ int getLag() const;
+
+ void updateAutoJoin(Konversation::ChannelSettings channel = Konversation::ChannelSettings());
+
+ void resetNickList(const QString& channelName);
+ void addPendingNickList(const QString& channelName,const QStringList& nickList);
+ void addHostmaskToNick(const QString &sourceNick, const QString &sourceHostmask);
+ Channel* nickJoinsChannel(const QString &channelName, const QString &nickname, const QString &hostmask);
+ void renameNick(const QString &nickname,const QString &newNick);
+ Channel* removeNickFromChannel(const QString &channelName, const QString &nickname, const QString &reason, bool quit=false);
+ void nickWasKickedFromChannel(const QString &channelName, const QString &nickname, const QString &kicker, const QString &reason);
+ void removeNickFromServer(const QString &nickname, const QString &reason);
+
+ void setChannelTypes(const QString &types);
+ QString getChannelTypes() const;
+
+ // extended user modes support
+ void setPrefixes(const QString &modes, const QString& prefixes);
+ void mangleNicknameWithModes(QString &nickname,bool& isAdmin,bool& isOwner,bool &isOp,
+ bool& isHalfop,bool &hasVoice);
+
+ bool isAChannel(const QString &channel) const;
+ bool isNickname(const QString& compare) const;
+
+ QString getNickname() const;
+ QString loweredNickname() const;
+
+ QString getNextNickname();
+
+ InputFilter* getInputFilter() { return &m_inputFilter; }
+ Konversation::OutputFilter* getOutputFilter() { return m_outputFilter; };
+
+ void joinChannel(const QString& name, const QString& hostmask);
+ void removeChannel(Channel* channel);
+ void appendServerMessageToChannel(const QString& channel, const QString& type, const QString& message);
+ void appendCommandMessageToChannel(const QString& channel, const QString& command, const QString& message, bool highlight = true);
+ void appendStatusMessage(const QString& type,const QString& message);
+ void appendMessageToFrontmost(const QString& type,const QString& message, bool parseURL = true);
+
+ int getPreLength(const QString& command, const QString& dest);
+
+ void dcopRaw(const QString& command);
+ void dcopSay(const QString& target,const QString& command);
+ void dcopInfo(const QString& string);
+ void ctcpReply(const QString& receiver, const QString& text);
+
+ void setChannelTopic(const QString& channel, const QString& topic);
+ // Overloaded
+ void setChannelTopic(const QString& nickname, const QString& channel, const QString& topic);
+ void updateChannelMode(const QString& nick, const QString& channel, char mode, bool plus, const QString& parameter);
+ void updateChannelModeWidgets(const QString& channel, char mode, const QString& parameter);
+
+ Channel* getChannelByName(const QString& name);
+ Query* getQueryByName(const QString& name);
+ QString parseWildcards(const QString& toParse, const QString& nickname, const QString& channelName, const QString &channelKey, const QStringList &nickList, const QString& parameter);
+ QString parseWildcards(const QString& toParse, const QString& nickname, const QString& channelName, const QString &channelKey, const QString& nick, const QString& parameter);
+
+ void autoCommandsAndChannels();
+
+ void sendURIs(const QStrList& uris, const QString& nick);
+
+ void notifyAction(const QString& nick);
+ ChannelListPanel* getChannelListPanel() const;
+
+ StatusPanel* getStatusView() const { return m_statusView; }
+ virtual bool closeYourself(bool askForConfirmation=true);
+
+ QString getOwnIpByNetworkInterface();
+ QString getOwnIpByServerMessage();
+
+ void requestAway(const QString& reason = "");
+ void requestUnaway();
+
+ bool isAway() { return m_away; }
+ void setAway(bool away);
+ QString awayTime() const;
+
+ void setAwayReason(const QString& reason) { m_awayReason = reason; }
+
+ /**
+ * Returns true if the given nickname is known to be online.
+ * @param nickname The nickname. Case insensitive.
+ * @return True if the nickname is known to be online by the server.
+ * Note that a nick that is not in any of the joined channels and is not on the
+ * notify list, and has not initiated a query with you, may well be online,
+ * but server doesn't know if it is or not, in which case False is returned.
+ */
+ bool isNickOnline(const QString &nickname);
+ /** Given a nickname, returns NickInfo object.
+ * @param nickname The desired nickname. Case insensitive.
+ * @return Pointer to the nickinfo for this nickname if one exists.
+ * 0 if not known to be online.
+ *
+ * A NickInfo pointer will only be returned if the nickname is known to the Konvi
+ * Server object. A nick will be known if:
+ * - It is in one of the server's channels user has joined.
+ * - It is on the notify list and is known to be online.
+ * - The nick initiated a query with the user.
+ * A NickInfo is destroyed when it is offline.
+ */
+ NickInfoPtr getNickInfo(const QString& nickname);
+ /** Given a nickname, returns an existing NickInfo object, or creates a new NickInfo object.
+ * Guaranteed to return a nickinfo.
+ * @param nickname The desired nickname. Case sensitive.
+ * @return Pointer to the found or created NickInfo object.
+ */
+ NickInfoPtr obtainNickInfo(const QString& nickname);
+ /** Returns a list of all the NickInfos that are online and known to the server.
+ * Caller should not modify the list.
+ * A nick will be known if:
+ * - It is in one of the server's channels user has joined.
+ * - It is on the notify list and is known to be online.
+ * - The nick initiated a query with the user.
+ *
+ * @return A QMap of KSharedPtrs to NickInfos indexed by lowercase nickname.
+ */
+ const NickInfoMap* getAllNicks();
+ /** Returns the list of members for a channel in the joinedChannels list.
+ * A joinedChannel is one that you are in, as opposed to a channel that you aren't in,
+ * but one of your watched nicks is in.
+ * Code that calls this must not modify the list.
+ * @param channelName Name of desired channel. Case insensitive.
+ * @return A map of all the nicks in the channel.
+ * 0 if channel is not in the joinedChannels list.
+ */
+ const ChannelNickMap *getJoinedChannelMembers(const QString& channelName) const;
+ /** Returns the list of members for a channel in the unjoinedChannels list.
+ * An unjoinedChannel is a channel you aren't in. As such, this is only going to return
+ * nicks that you know are in that channel because a /whois has been done against them.
+ * This could be done automatically if they are on the watch list.
+ * Code that calls this must not modify the list.
+ * @param channelName Name of desired channel. Case insensitive.
+ * @return A map of only the nicks that we know that are in the channel.
+ * 0 if channel is not in the unjoinedChannels list.
+ */
+ const ChannelNickMap *getUnjoinedChannelMembers(const QString& channelName) const;
+ /** Searches the Joined and Unjoined lists for the given channel and returns the member list.
+ * Code that calls this must not modify the list.
+ * @param channelName Name of desired channel. Case insensitive.
+ * @return A map of nicks in that channel. 0 if channel is not in either list.
+ *
+ * @see getJoinedChannelMembers(const QString& channelName)
+ * @see getUnjoinedChannelMembers(const QString& channelName)
+ */
+ const ChannelNickMap *getChannelMembers(const QString& channelName) const;
+ /** Returns a list of all the joined channels that a nick is in.
+ * @param nickname The desired nickname. Case insensitive.
+ * @return A list of joined channels the nick is in. Empty if none.
+ */
+ QStringList getNickJoinedChannels(const QString& nickname);
+ /** Returns a list of all the channels (joined or unjoined) that a nick is in.
+ * @param nickname The desired nickname. Case insensitive.
+ * @return A list of channels the nick is in. Empty if none.
+ *
+ * A nick will not appear in the Unjoined channels list unless a WHOIS
+ * has been performed on it.
+ */
+ QStringList getNickChannels(const QString& nickname);
+ /** Returns pointer to the ChannelNick (mode and pointer to NickInfo) for a
+ * given channel and nickname.
+ * @param channelName The desired channel name. Case insensitive.
+ * @param nickname The desired nickname. Case insensitive.
+ * @return Pointer to ChannelNick structure containing a pointer
+ * to the NickInfo and the mode of the nick in the channel.
+ * 0 if not found.
+ */
+ ChannelNickPtr getChannelNick(const QString& channelName, const QString& nickname);
+ /** Updates a nickname in a channel. If not on the joined or unjoined lists, and nick
+ * is in the watch list, adds the channel and nick to the unjoinedChannels list.
+ * If mode != 99, sets the mode for the nick in the channel.
+ * Returns the NickInfo object if nick is on any lists, otherwise 0.
+ * @param channelName The channel name. Case sensitive.
+ * @param nickname The nickname. Case sensitive.
+ * @param mode Bit mask containing the modes the nick has in the channel,
+ * or 99 if not known. See channelnick.cpp for bit definitions.
+ */
+ ChannelNickPtr setChannelNick(const QString& channelName, const QString& nickname, unsigned int mode = 99);
+ /**
+ * Given the nickname of nick that is offline (or at least not known to be online),
+ * returns the addressbook entry (if any) for the nick.
+ * @param nickname Desired nickname. Case insensitive.
+ * @return Addressbook entry of the nick or empty if not found.
+ */
+ KABC::Addressee getOfflineNickAddressee(QString& nickname);
+
+ /**
+ * Returns a QPtrList of all channels
+ */
+ const QPtrList<Channel>& getChannelList() const { return m_channelList; }
+
+ void emitChannelNickChanged(const ChannelNickPtr channelNick);
+ void emitNickInfoChanged(const NickInfoPtr nickInfo);
+
+ /**
+ * Returns a list of all the nicks on the user watch list plus nicks in the addressbook.
+ */
+ QStringList getWatchList();
+ QString getWatchListString();
+ /**
+ * Return true if the given nickname is on the watch list.
+ */
+ bool isWatchedNick(const QString& nickname);
+ /**
+ * Returns a list of all the nicks on the watch list that are not in joined
+ * channels. ISON command is sent for these nicks.
+ */
+ QStringList getISONList();
+ QString getISONListString();
+
+ ViewContainer* getViewContainer() const;
+
+ /** Adds a nickname to the joinedChannels list.
+ * Creates new NickInfo if necessary.
+ * If needed, moves the channel from the unjoined list to the joined list.
+ * If needed, moves the nickname from the Offline to Online lists.
+ * If mode != 99 sets the mode for this nick in this channel.
+ * @param channelName The channel name. Case sensitive.
+ * @param nickname The nickname. Case sensitive.
+ * @return The NickInfo for the nickname.
+ */
+ ChannelNickPtr addNickToJoinedChannelsList(const QString& channelName, const QString& nickname);
+
+ void setAllowedChannelModes(const QString& modes) { m_allowedChannelModes = modes; }
+ QString allowedChannelModes() const { return m_allowedChannelModes; }
+
+ void registerWithServices();
+
+ // Blowfish stuff
+ QCString getKeyForRecipient(const QString& recipient) const;
+ void setKeyForRecipient(const QString& recipient, const QCString& key);
+
+ bool identifyMsg() const { return m_identifyMsg; }
+
+ ChannelListPanel* addChannelListPanel();
+
+ // invoked by DccTransferSend
+ void dccSendRequest(const QString& recipient,const QString& fileName,const QString& address,const QString& port,unsigned long size);
+ void dccPassiveSendRequest(const QString& recipient,const QString& fileName,const QString& address,unsigned long size,const QString& token);
+ // invoked by DccTransferRecv
+ void dccResumeGetRequest(const QString& sender,const QString& fileName,const QString& port,KIO::filesize_t startAt);
+ void dccReverseSendAck(const QString& partnerNick,const QString& fileName,const QString& ownAddress,const QString& ownPort,unsigned long size,const QString& reverseToken);
+
+ // IRCQueueManager
+ bool validQueue(QueuePriority priority); ///< is this queue index valid?
+ void resetQueues(); ///< Tell all of the queues to reset
+ static int _max_queue() { return Howmanyqueuesdoweneedanywayquestionmark-1; }
+
+ /** Forces the queued data to be sent in sequence of age, without pause.
+
+ This could flood you off but since you're quitting, we probably don't care. This is done
+ here instead of in the queues themselves so we can interleave the queues without having to
+ zip the queues together. If you want to quit the server normally without sending, reset the queues
+ first.
+ */
+ void flushQueues();
+
+ //These are really only here to limit where ircqueue.h is included
+ static void _fetchRates(); ///< on server construction
+ static void _stashRates(); ///< on application exit
+ static void _resetRates(); ///< when QueueTuner says to
+
+
+ signals:
+ void destroyed(int connectionId);
+ void nicknameChanged(const QString&);
+ void serverLag(Server* server,int msec); /// will be connected to KonversationMainWindow::updateLag()
+ void tooLongLag(Server* server, int msec);/// will be connected to KonversationMainWindow::updateLag()
+ void resetLag(); ///< will be emitted when new 303 came in
+ void nicksNowOnline(Server* server,const QStringList& list,bool changed);
+ void awayState(bool away); /// will be connected to any user input panel;
+ void multiServerCommand(const QString& command, const QString& parameter);
+
+ /**
+ * Emitted when the server gains/loses connection.
+ * Will be connected to all server dependant tabs.
+ */
+ void serverOnline(bool state);
+
+ /**
+ * Emitted every time something gets sent.
+ *
+ * @param bytes The count of bytes sent to the server, before re-encoding.
+ * @param encodedBytes The count of bytes sent to the server after re-encoding.
+ */
+ void sentStat(int bytes, int encodedBytes, IRCQueue *whichQueue);
+ //FIXME can anyone who can connect to a Server signal not know about an IRCQueue?
+ void sentStat(int bytes, int encodedBytes);
+
+ //Note that these signals haven't been implemented yet.
+ /// Fires when the information in a NickInfo object changes.
+ void nickInfoChanged(Server* server, const NickInfoPtr nickInfo);
+
+ /// Fires when the mode of a nick in a channel changes.
+ void channelNickChanged(Server* server, const ChannelNickPtr channelNick);
+
+ /// Fires when a nick leaves or joins a channel. Based on joined flag, receiver could
+ /// call getJoinedChannelMembers or getUnjoinedChannelMembers, or just
+ /// getChannelMembers to get a list of all the nicks now in the channel.
+ /// parted indicates whether the nick joined or left the channel.
+ void channelMembersChanged(Server* server, const QString& channelName, bool joined, bool parted, const QString& nickname);
+
+ /// Fires when a channel is moved to/from the Joinied/Unjoined lists.
+ /// joined indicates which list it is now on. Note that if joined is False, it is
+ /// possible the channel does not exist in any list anymore.
+ void channelJoinedOrUnjoined(Server* server, const QString& channelName, bool joined);
+
+ /// Fires when a nick on the watch list goes online or offline.
+ void watchedNickChanged(Server* server, const QString& nickname, bool online);
+ ///Fires when the user switches his state to away and has enabled "Insert Remember Line on away" in his identity.
+ void awayInsertRememberLine(Server* server);
+ void sslInitFailure();
+ void sslConnected(Server* server);
+
+ void connectionStateChanged(Server* server, Konversation::ConnectionState state);
+
+ void showView(ChatWindow* view);
+ void addDccPanel();
+ void addDccChat(const QString& myNick,const QString& nick,const QStringList& arguments,bool listen);
+
+ public slots:
+ void connectToIRCServer();
+
+ /** Adds line to queue if non-empty. */
+ bool queue(const QString& line, QueuePriority priority=StandardPriority);
+ //TODO this should be an overload, not a separate name. ambiguous cases need QString() around the cstring
+ bool queueList(const QStringList& buffer, QueuePriority priority=StandardPriority);
+
+ void setNickname(const QString &newNickname);
+ /** This is called when we want to open a new query, or focus an existing one.
+ * @param nickInfo The nickinfo we want to open the query to. Must exist.
+ * @param weinitiated This is whether we initiated this - did we do /query, or somebody else sending us a message.
+ * @return A pointer to a new or already-existing query. Guaranteed to be non-null
+ */
+ Query *addQuery(const NickInfoPtr & nickInfo, bool weinitiated);
+ void closeQuery(const QString &name);
+ void closeChannel(const QString &name);
+ void quitServer();
+ void openDccChat(const QString& nickname);
+ void requestDccChat(const QString& partnerNick, const QString& numericalOwnIp, const QString& ownPort);
+ void requestBan(const QStringList& users,const QString& channel,const QString& option);
+ void requestUnban(const QString& mask,const QString& channel);
+
+ void addDccSend(const QString &recipient,KURL fileURL, const QString &altFileName = QString(), uint fileSize = 0);
+ void removeQuery(Query *query);
+ void startNotifyTimer(int msec=0);
+ void sendJoinCommand(const QString& channelName, const QString& password = QString());
+ void requestChannelList();
+ void requestWhois(const QString& nickname);
+ void requestWho(const QString& channel);
+ void requestUserhost(const QString& nicks);
+ void requestTopic(const QString& channel);
+ void resolveUserhost(const QString& nickname);
+ void addRawLog(bool show);
+ void closeRawLog();
+ void addToChannelList(const QString& channel, int users, const QString& topic);
+ void closeChannelListPanel();
+ void updateChannelQuickButtons();
+ void sendMultiServerCommand(const QString& command, const QString& parameter);
+ void executeMultiServerCommand(const QString& command, const QString& parameter);
+ void reconnect();
+ void disconnect(); //FIXME is this overriding a qobject method? do we care?
+ void showSSLDialog();
+ void sendToAllChannels(const QString& text);
+ void notifyTimeout();
+
+ void enableIdentifyMsg(bool enabled);
+ bool identifyMsgEnabled();
+ void addBan(const QString &channel, const QString &ban);
+ void removeBan(const QString &channel, const QString &ban);
+
+ /// Called when we received a PONG from the server
+ void pongReceived();
+
+ protected slots:
+ void lookupFinished();
+ void preShellCommandExited(KProcess*);
+ void ircServerConnectionSuccess();
+ void startAwayTimer();
+ void incoming();
+ void processIncomingData();
+ /// Sends the QString to the socket. No longer has any internal concept of queueing
+ void toServer(QString&, IRCQueue *);
+ /// Because KBufferedSocket has no closed(int) signal we use this slot to call broken(0)
+ void closed();
+ void broken(int state);
+ /** This is connected to the SSLSocket failed.
+ * @param reason The reason why this failed. This is already translated, ready to show the user.
+ */
+ void sslError(const QString& reason);
+ void connectionEstablished(const QString& ownHost);
+ void notifyResponse(const QString& nicksOnline);
+ void slotNewDccTransferItemQueued(DccTransfer* transfer);
+ void startReverseDccSendTransfer(const QString& sourceNick,const QStringList& dccArguments);
+ void addDccGet(const QString& sourceNick,const QStringList& dccArguments);
+ void requestDccSend(); // -> to outputFilter, dccPanel
+ // -> to outputFilter
+ void requestDccSend(const QString& recipient);
+ // -> to inputFilter
+ void resumeDccGetTransfer(const QString& sourceNick,const QStringList& dccArguments);
+ // -> to inputFilter
+ void resumeDccSendTransfer(const QString& sourceNick,const QStringList& dccArguments);
+ void dccGetDone(DccTransfer* item);
+ void dccSendDone(DccTransfer* item);
+ void dccStatusChanged(DccTransfer* item, int newStatus, int oldStatus);
+ void scriptNotFound(const QString& name);
+ void scriptExecutionError(const QString& name);
+ void userhost(const QString& nick,const QString& hostmask,bool away,bool ircOp);
+ void setTopicAuthor(const QString& channel,const QString& author, QDateTime t);
+ void endOfWho(const QString& target);
+ void invitation(const QString& nick,const QString& channel);
+ void sendToAllChannelsAndQueries(const QString& text);
+ void gotOwnResolvedHostByWelcome(KResolverResults res);
+ void gotOwnResolvedHostByUserhost(KResolverResults res);
+
+ /// Send a PING to the server so we can meassure the lag
+ void sendPing();
+ /// Updates GUI when the lag gets high
+ void updateLongPongLag();
+
+ /// Update the encoding shown in the mainwindow's actions
+ void updateEncoding();
+
+ private slots:
+ void collectStats(int bytes, int encodedBytes);
+
+ /** Called in the server constructor if the preferences are set to run a command on a new server instance.
+ * This sets up the kprocess, runs it, and connects the signals to call preShellCommandExited when done. */
+ void doPreShellCommand();
+
+ protected:
+ // constants
+ static const int BUFFER_LEN=513;
+
+ /// Initialize the timers
+ void initTimers();
+
+ /// Connect to the signals used in this class.
+ void connectSignals();
+
+ int _send_internal(QString outputline); ///< Guts of old send, isn't a slot.
+
+ /** Adds a nickname to the unjoinedChannels list.
+ * Creates new NickInfo if necessary.
+ * If needed, moves the channel from the joined list to the unjoined list.
+ * If needed, moves the nickname from the Offline to the Online list.
+ * If mode != 99 sets the mode for this nick in this channel.
+ * @param channelName The channel name. Case sensitive.
+ * @param nickname The nickname. Case sensitive.
+ * @return The NickInfo for the nickname.
+ */
+ ChannelNickPtr addNickToUnjoinedChannelsList(const QString& channelName, const QString& nickname);
+
+ /**
+ * If not already online, changes a nick to the online state by creating
+ * a NickInfo for it and emits various signals and messages for it.
+ * This method should only be called for nicks on the watch list.
+ * @param nickname The nickname that is online.
+ * @return Pointer to NickInfo for nick.
+ */
+ NickInfoPtr setWatchedNickOnline(const QString& nickname);
+
+ /**
+ * Display offline notification for a certain nickname. The function doesn't change NickInfo objects.
+ * If NickInfoPtr is given, then also the integration with KAddressBook is engaged (i.e. the
+ * nick is marked as away)
+ * @param nickname The nickname that is offline
+ * @param nickInfo Pointer to NickInfo for nick
+ */
+ void setWatchedNickOffline(const QString& nickname, const NickInfoPtr nickInfo);
+
+ /**
+ * If nickname is no longer on any channel list, or the query list, delete it altogether.
+ * Call this routine only if the nick is not on the notify list or is on the notify
+ * list but is known to be offline.
+ * @param nickname The nickname to be deleted. Case insensitive.
+ * @return True if the nickname is deleted.
+ */
+ bool deleteNickIfUnlisted(const QString &nickname);
+
+ /**
+ * If not already offline, changes a nick to the offline state.
+ * Removes it from all channels on the joined and unjoined lists.
+ * If the nick is in the watch list, and went offline, emits a signal,
+ * posts a Notify message, and posts a KNotify.
+ * If the nick is in the addressbook, and went offline, informs addressbook of change.
+ * If the nick goes offline, the NickInfo is deleted.
+ *
+ * @param nickname The nickname. Case sensitive.
+ * @return True if the nick was online.
+ */
+ bool setNickOffline(const QString& nickname);
+
+ /** Remove nickname from a channel (on joined or unjoined lists).
+ * @param channelName The channel name. Case insensitive.
+ * @param nickname The nickname. Case insensitive.
+ */
+ void removeChannelNick(const QString& channelName, const QString& nickname);
+
+ /** Remove channel from the joined list.
+ * Nicknames in the channel are added to the unjoined list if they are in the watch list.
+ * @param channelName The channel name. Case insensitive.
+ */
+ void removeJoinedChannel(const QString& channelName);
+
+ /** Renames a nickname in all NickInfo lists.
+ * @param nickInfo Pointer to existing NickInfo object.
+ * @param newname New nickname for the nick. Case sensitive.
+ */
+ void renameNickInfo(NickInfoPtr nickInfo, const QString& newname);
+
+ bool getAutoJoin() const;
+ void setAutoJoin(bool on);
+
+ QStringList getAutoJoinCommands() const { return m_autoJoinCommands; }
+ void setAutoJoinCommands(const QStringList& commands) { m_autoJoinCommands = commands; }
+
+ unsigned int m_completeQueryPosition;
+ QValueList<int> m_nickIndices;
+ QStringList m_referenceNicklist;
+
+ // TODO roll these into a QMap.
+ QString m_serverNickPrefixes; // Prefixes used by the server to indicate a mode
+ QString m_serverNickPrefixModes; // if supplied: modes related to those prefixes
+ QString m_channelPrefixes; // prefixes that indicate channel names. defaults to RFC1459 "#&"
+
+ bool m_autoJoin;
+
+ QStringList m_autoJoinCommands;
+
+ KNetwork::KStreamSocket* m_socket;
+
+ QTimer m_reconnectTimer;
+ QTimer m_incomingTimer;
+ QTimer m_notifyTimer;
+ QStringList m_notifyCache; // List of users found with ISON
+ int m_checkTime; // Time elapsed while waiting for server 303 response
+ int m_currentLag;
+
+ QCString m_inputBufferIncomplete;
+ QStringList m_inputBuffer;
+
+ QValueVector<IRCQueue *> m_queues;
+ int m_bytesSent, m_encodedBytesSent, m_linesSent, m_bytesReceived;
+
+ QString m_nickname;
+ QString m_loweredNickname;
+ QString m_ownIpByUserhost; // RPL_USERHOST
+ QString m_ownIpByWelcome; // RPL_WELCOME
+
+ QPtrList<Channel> m_channelList;
+ QPtrList<Query> m_queryList;
+
+ InputFilter m_inputFilter;
+ Konversation::OutputFilter* m_outputFilter;
+
+ QGuardedPtr<StatusPanel> m_statusView;
+ QGuardedPtr<RawLog> m_rawLog;
+ QGuardedPtr<ChannelListPanel> m_channelListPanel;
+
+ bool m_away;
+ QString m_awayReason;
+ QString m_nonAwayNick;
+ int m_awayTime;
+
+ Konversation::ConnectionState m_connectionState;
+ void updateConnectionState(Konversation::ConnectionState state);
+ bool isSocketConnected() const;
+
+ ScriptLauncher* m_scriptLauncher;
+
+ KProcess m_preShellCommand;
+
+ private:
+ /// Helper object to construct ISON (notify) list and map offline nicks to
+ /// addressbook.
+ ServerISON* m_serverISON;
+ /// All nicks known to this server. Note this is NOT a list of all nicks on the server.
+ /// Any nick appearing in this list is online, but may not necessarily appear in
+ /// any of the joined or unjoined channel lists because a WHOIS has not yet been
+ /// performed on the nick.
+ NickInfoMap m_allNicks;
+ /// List of membership lists for joined channels. A "joined" channel is a channel
+ /// that user has joined, i.e., a tab appears for the channel in the main window.
+ ChannelMembershipMap m_joinedChannels;
+ /// List of membership lists for unjoined channels. These come from WHOIS responses.
+ /// Note that this is NOT a list of all channels on the server, just those we are
+ /// interested in because of nicks in the Nick Watch List.
+ ChannelMembershipMap m_unjoinedChannels;
+ /// List of nicks in Queries.
+ NickInfoMap m_queryNicks;
+
+ QString m_allowedChannelModes;
+
+ // Blowfish key map
+ QMap<QString,QCString> m_keyMap;
+
+ bool m_identifyMsg;
+ bool m_autoIdentifyLock;
+
+ /// Used to lock incomingTimer while processing message.
+ bool m_processingIncoming;
+
+ /// Meassures the lag between PING and PONG
+ QTime m_lagTime;
+ /// Updates the gui when the lag gets too high
+ QTimer m_pingResponseTimer;
+
+ /// Previous ISON reply of the server, needed for comparison with the next reply
+ QStringList m_prevISONList;
+
+ ConnectionSettings m_connectionSettings;
+
+ static int m_availableConnectionId;
+ int m_connectionId;
+};
+
+#endif
diff --git a/konversation/src/serverdialog.cpp b/konversation/src/serverdialog.cpp
new file mode 100644
index 0000000..1c185dd
--- /dev/null
+++ b/konversation/src/serverdialog.cpp
@@ -0,0 +1,103 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#include "serverdialog.h"
+#include "serversettings.h"
+
+#include <qlayout.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qcheckbox.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+
+namespace Konversation
+{
+
+ ServerDialog::ServerDialog(const QString& title, QWidget *parent, const char *name)
+ : KDialogBase(Plain, title, Ok|Cancel, Ok, parent, name)
+ {
+ QFrame* mainWidget = plainPage();
+ QGridLayout* mainLayout = new QGridLayout(mainWidget, 1, 4, 0, spacingHint());
+
+ QLabel* serverLbl = new QLabel(i18n("&Server:"), mainWidget);
+ m_serverEdit = new QLineEdit(mainWidget);
+ QWhatsThis::add(m_serverEdit, i18n("The name or IP number of the server. irchelp.org maintains a list of servers."));
+ serverLbl->setBuddy(m_serverEdit);
+
+ QLabel* portLbl = new QLabel(i18n("&Port:"), mainWidget);
+
+ m_portSBox = new QSpinBox(1, 65535, 1, mainWidget);
+ m_portSBox->setValue(6667);
+ QWhatsThis::add(m_portSBox, i18n("Enter the port number required to connect to the server. For most servers, this should be <b>6667</b>."));
+ portLbl->setBuddy(m_portSBox);
+
+ QLabel* passwordLbl = new QLabel(i18n("Pass&word:"), mainWidget);
+ m_passwordEdit = new QLineEdit(mainWidget);
+ m_passwordEdit->setEchoMode(QLineEdit::Password);
+ passwordLbl->setBuddy(m_passwordEdit);
+
+ m_sslChBox = new QCheckBox(i18n("S&ecure connection (SSL)"), mainWidget);
+ QWhatsThis::add(m_sslChBox, i18n("Check if you want to use Secure Socket Layer (SSL) protocol to communicate with the server. This protects the privacy of your communications between your computer and the IRC server. The server must support SSL protocol for this to work. In most cases, if the server does not support SSL, the connection will fail."));
+
+ mainLayout->addWidget(serverLbl, 0, 0);
+ mainLayout->addWidget(m_serverEdit, 0, 1);
+ mainLayout->addWidget(portLbl, 0, 2);
+ mainLayout->addWidget(m_portSBox, 0, 3);
+ mainLayout->addWidget(passwordLbl, 1, 0);
+ mainLayout->addMultiCellWidget(m_passwordEdit, 1, 1, 1, 3);
+ mainLayout->addMultiCellWidget(m_sslChBox, 2, 2, 0, 3);
+
+ m_serverEdit->setFocus();
+ }
+
+ ServerDialog::~ServerDialog()
+ {
+ }
+
+ void ServerDialog::setServerSettings(const ServerSettings& server)
+ {
+ m_serverEdit->setText(server.host());
+ m_portSBox->setValue(server.port());
+ m_passwordEdit->setText(server.password());
+ m_sslChBox->setChecked(server.SSLEnabled());
+ }
+
+ ServerSettings ServerDialog::serverSettings()
+ {
+ ServerSettings server;
+ server.setHost(m_serverEdit->text());
+ server.setPort(m_portSBox->value());
+ server.setPassword(m_passwordEdit->text());
+ server.setSSLEnabled(m_sslChBox->isChecked());
+
+ return server;
+ }
+
+ void ServerDialog::slotOk()
+ {
+ if (m_serverEdit->text().isEmpty())
+ {
+ KMessageBox::error(this, i18n("The server address is required."));
+ }
+ else
+ {
+ accept();
+ }
+ }
+}
+
+#include "serverdialog.moc"
diff --git a/konversation/src/serverdialog.h b/konversation/src/serverdialog.h
new file mode 100644
index 0000000..48ece84
--- /dev/null
+++ b/konversation/src/serverdialog.h
@@ -0,0 +1,48 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+#ifndef KONVERSATIONSERVERDIALOG_H
+#define KONVERSATIONSERVERDIALOG_H
+
+#include <kdialogbase.h>
+
+class QLineEdit;
+class QSpinBox;
+class QCheckBox;
+
+namespace Konversation
+{
+
+ class ServerSettings;
+
+ class ServerDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+ explicit ServerDialog(const QString& title, QWidget *parent = 0, const char *name = 0);
+ ~ServerDialog();
+
+ void setServerSettings(const ServerSettings& server);
+ ServerSettings serverSettings();
+
+ protected slots:
+ void slotOk();
+
+ private:
+ QLineEdit* m_serverEdit;
+ QSpinBox* m_portSBox;
+ QLineEdit* m_passwordEdit;
+ QCheckBox* m_sslChBox;
+ };
+
+}
+#endif
diff --git a/konversation/src/servergroupdialog.cpp b/konversation/src/servergroupdialog.cpp
new file mode 100644
index 0000000..0bfbfb2
--- /dev/null
+++ b/konversation/src/servergroupdialog.cpp
@@ -0,0 +1,418 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#include "servergroupdialog.h"
+#include "identity.h"
+#include "konversationapplication.h"
+#include "viewcontainer.h"
+#include "preferences.h"
+#include "serversettings.h"
+#include "serverdialog.h"
+#include "channeldialog.h"
+#include "identitydialog.h"
+#include "servergroupdialogui.h"
+
+#include <qframe.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qgroupbox.h>
+#include <qlistbox.h>
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+#include <qcheckbox.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kcombobox.h>
+#include <klistbox.h>
+#include <kpushbutton.h>
+
+
+namespace Konversation
+{
+
+ ServerGroupDialog::ServerGroupDialog(const QString& title, QWidget *parent, const char *name)
+ : KDialogBase(Swallow, title, Ok|Cancel, Ok, parent, name)
+ {
+ m_id = -1;
+ m_identitiesNeedsUpdate = false;
+ m_editedServer = false;
+
+ m_mainWidget = new ServerGroupDialogUI(this);
+ setMainWidget(m_mainWidget);
+
+ QWhatsThis::add(m_mainWidget->m_nameEdit, i18n("Enter the name of the Network here. You may create as many entries in the Server List screen with the same Network as you like."));
+ m_mainWidget->m_networkLabel->setBuddy(m_mainWidget->m_nameEdit);
+
+ QWhatsThis::add(m_mainWidget->m_identityCBox,i18n("Choose an existing Identity or click the Edit button to add a new Identity or edit an existing one. The Identity will identify you and determine your nickname when you connect to the network."));
+ m_mainWidget->m_identityLabel->setBuddy(m_mainWidget->m_identityCBox);
+ connect(m_mainWidget->m_editIdentityButton, SIGNAL(clicked()), this, SLOT(editIdentity()));
+
+ IdentityList identities = Preferences::identityList();
+
+ for (IdentityList::ConstIterator it = identities.begin(); it != identities.end(); ++it)
+ m_mainWidget->m_identityCBox->insertItem((*it)->getName());
+
+ QWhatsThis::add(m_mainWidget->m_commandEdit, i18n("Optional. This command will be sent to the server after connecting. Example: <b>/msg NickServ IDENTIFY <i>konvirocks</i></b>. This example is for the freenode network, which requires users to register their nickname with a password and login when connecting. <i>konvirocks<i> is the password for the nickname given in Identity. You may enter more than one command by separating them with semicolons."));
+ m_mainWidget->m_commandsLabel->setBuddy(m_mainWidget->m_commandEdit);
+
+ QWhatsThis::add(m_mainWidget->m_autoConnectCBox, i18n("Check here if you want Konversation to automatically connect to this network whenever you open Konversation."));
+
+ QWhatsThis::add(m_mainWidget->m_serverLBox, i18n("This is a list of IRC Servers in the network. When connecting to the network, Konversation will attempt to connect to the top server first. If this fails, it will attempt the second server. If this fails, it will attempt the third, and so on. At least one server must be specified. Click a server to highlight it."));
+ m_mainWidget->m_removeServerButton->setIconSet(SmallIconSet("editdelete"));
+ m_mainWidget->m_removeServerButton->setTextLabel(i18n("Delete"));
+ m_mainWidget->m_upServerBtn->setIconSet(SmallIconSet("up"));
+ m_mainWidget->m_downServerBtn->setIconSet(SmallIconSet("down"));
+
+ connect(m_mainWidget->m_addServerButton, SIGNAL(clicked()), this, SLOT(addServer()));
+ connect(m_mainWidget->m_changeServerButton, SIGNAL(clicked()), this, SLOT(editServer()));
+ connect(m_mainWidget->m_removeServerButton, SIGNAL(clicked()), this, SLOT(deleteServer()));
+ connect(m_mainWidget->m_serverLBox, SIGNAL(selectionChanged()), this, SLOT(updateServerArrows()));
+ connect(m_mainWidget->m_upServerBtn, SIGNAL(clicked()), this, SLOT(moveServerUp()));
+ connect(m_mainWidget->m_downServerBtn, SIGNAL(clicked()), this, SLOT(moveServerDown()));
+
+ QWhatsThis::add(m_mainWidget->m_channelLBox, i18n("Optional. This is a list of the channels that will be automatically joined once Konversation has connected to a server. You may leave this blank if you wish to not automatically join any channels."));
+ m_mainWidget->m_removeChannelButton->setIconSet(SmallIconSet("editdelete"));
+ m_mainWidget->m_removeChannelButton->setTextLabel(i18n("Delete"));
+ m_mainWidget->m_upChannelBtn->setIconSet(SmallIconSet("up"));
+ m_mainWidget->m_downChannelBtn->setIconSet(SmallIconSet("down"));
+
+ connect(m_mainWidget->m_addChannelButton, SIGNAL(clicked()), this, SLOT(addChannel()));
+ connect(m_mainWidget->m_changeChannelButton, SIGNAL(clicked()), this, SLOT(editChannel()));
+ connect(m_mainWidget->m_removeChannelButton, SIGNAL(clicked()), this, SLOT(deleteChannel()));
+ connect(m_mainWidget->m_channelLBox, SIGNAL(selectionChanged()), this, SLOT(updateChannelArrows()));
+ connect(m_mainWidget->m_upChannelBtn, SIGNAL(clicked()), this, SLOT(moveChannelUp()));
+ connect(m_mainWidget->m_downChannelBtn, SIGNAL(clicked()), this, SLOT(moveChannelDown()));
+
+ setButtonOK(KGuiItem(i18n("&OK"), "button_ok", i18n("Change network information")));
+ setButtonCancel(KGuiItem(i18n("&Cancel"), "button_cancel", i18n("Discards all changes made")));
+
+ m_mainWidget->m_nameEdit->setFocus();
+ }
+
+ ServerGroupDialog::~ServerGroupDialog()
+ {
+ }
+
+ void ServerGroupDialog::setServerGroupSettings(ServerGroupSettingsPtr settings)
+ {
+ m_id = settings->id();
+ m_sortIndex = settings->sortIndex();
+ m_expanded = settings->expanded();
+ m_enableNotifications = settings->enableNotifications();
+ m_mainWidget->m_nameEdit->setText(settings->name());
+ m_mainWidget->m_identityCBox->setCurrentText(settings->identity()->getName());
+ m_mainWidget->m_commandEdit->setText(settings->connectCommands());
+ m_mainWidget->m_autoConnectCBox->setChecked(settings->autoConnectEnabled());
+ m_serverList = settings->serverList();
+ m_channelHistory = settings->channelHistory();
+ ServerList::iterator it;
+ m_mainWidget->m_serverLBox->clear();
+
+ for(it = m_serverList.begin(); it != m_serverList.end(); ++it)
+ {
+ m_mainWidget->m_serverLBox->insertItem((*it).host());
+ }
+
+ m_channelList = settings->channelList();
+ ChannelList::iterator it2;
+
+ for(it2 = m_channelList.begin(); it2 != m_channelList.end(); ++it2)
+ {
+ m_mainWidget->m_channelLBox->insertItem((*it2).name());
+ }
+ }
+
+ ServerGroupSettingsPtr ServerGroupDialog::serverGroupSettings()
+ {
+ ServerGroupSettingsPtr settings = new ServerGroupSettings(m_id);
+ settings->setSortIndex(m_sortIndex);
+ settings->setName(m_mainWidget->m_nameEdit->text());
+ IdentityList identities = Preferences::identityList();
+ settings->setIdentityId(identities[m_mainWidget->m_identityCBox->currentItem()]->id());
+ settings->setConnectCommands(m_mainWidget->m_commandEdit->text());
+ settings->setAutoConnectEnabled(m_mainWidget->m_autoConnectCBox->isChecked());
+ settings->setServerList(m_serverList);
+ settings->setChannelList(m_channelList);
+ settings->setChannelHistory(m_channelHistory);
+ settings->setNotificationsEnabled(m_enableNotifications);
+ settings->setExpanded(m_expanded);
+
+ return settings;
+ }
+
+ ServerSettings ServerGroupDialog::editedServer()
+ {
+ if (m_editedServer && m_editedServerIndex < m_serverList.count())
+ {
+ return m_serverList[m_editedServerIndex];
+ }
+
+ return ServerSettings("");
+ }
+
+ int ServerGroupDialog::execAndEditServer(ServerSettings server)
+ {
+ show();
+ editServer(server);
+ return exec();
+ }
+
+ void ServerGroupDialog::addServer()
+ {
+ ServerDialog dlg(i18n("Add Server"), this);
+
+ if(dlg.exec() == KDialog::Accepted)
+ {
+ ServerSettings server = dlg.serverSettings();
+ m_mainWidget->m_serverLBox->insertItem(server.host());
+ m_serverList.append(server);
+ updateServerArrows();
+ }
+ }
+
+ void ServerGroupDialog::editServer()
+ {
+ uint current = m_mainWidget->m_serverLBox->currentItem();
+
+ if(current < m_serverList.count())
+ {
+ ServerDialog dlg(i18n("Edit Server"), this);
+ dlg.setServerSettings(m_serverList[current]);
+
+ if(dlg.exec() == KDialog::Accepted)
+ {
+ ServerSettings server = dlg.serverSettings();
+ m_mainWidget->m_serverLBox->changeItem(server.host(), current);
+ m_serverList[current] = server;
+ }
+ }
+ }
+
+ void ServerGroupDialog::editServer(ServerSettings server)
+ {
+ // Track the server the Server List dialog told us to edit
+ // and find out which server to select in the listbox
+ m_editedServer = true;
+ m_editedServerIndex = m_serverList.findIndex(server);
+ m_mainWidget->m_serverLBox->setCurrentItem(m_editedServerIndex);
+
+ editServer();
+ }
+
+ void ServerGroupDialog::deleteServer()
+ {
+ uint current = m_mainWidget->m_serverLBox->currentItem();
+
+ if (current < m_serverList.count())
+ {
+ m_serverList.remove(m_serverList.at(current));
+ m_mainWidget->m_serverLBox->removeItem(current);
+
+ // Track the server the Server List dialog told us to edit
+ if (m_editedServer && m_editedServerIndex==current)
+ m_editedServer = false;
+ }
+
+ updateServerArrows();
+ }
+
+ void ServerGroupDialog::updateServerArrows()
+ {
+ m_mainWidget->m_upServerBtn->setEnabled( m_mainWidget->m_serverLBox->count()>1 && m_mainWidget->m_serverLBox->currentItem()>0 );
+
+ m_mainWidget->m_downServerBtn->setEnabled( m_mainWidget->m_serverLBox->count()>1 &&
+ m_mainWidget->m_serverLBox->currentItem()<m_mainWidget->m_serverLBox->numRows()-1 );
+ bool enabled = m_mainWidget->m_serverLBox->currentItem() >= 0;
+ m_mainWidget->m_removeServerButton->setEnabled(enabled);
+ m_mainWidget->m_changeServerButton->setEnabled(enabled);
+ }
+
+ void ServerGroupDialog::moveServerUp()
+ {
+ uint current = m_mainWidget->m_serverLBox->currentItem();
+
+ if (current > 0)
+ {
+ ServerSettings server = m_serverList[current];
+ m_mainWidget->m_serverLBox->removeItem(current);
+ m_mainWidget->m_serverLBox->insertItem(server.host(), current - 1);
+ m_mainWidget->m_serverLBox->setCurrentItem(current - 1);
+ ServerList::iterator it = m_serverList.remove(m_serverList.at(current));
+ --it;
+ m_serverList.insert(it, server);
+
+ // Track the server the Server List dialog told us to edit
+ if (m_editedServer && m_editedServerIndex==current)
+ m_editedServerIndex = current - 1;
+ }
+
+ updateServerArrows();
+ }
+
+ void ServerGroupDialog::moveServerDown()
+ {
+ uint current = m_mainWidget->m_serverLBox->currentItem();
+
+ if (current < (m_serverList.count() - 1))
+ {
+ ServerSettings server = m_serverList[current];
+ m_mainWidget->m_serverLBox->removeItem(current);
+ m_mainWidget->m_serverLBox->insertItem(server.host(), current + 1);
+ m_mainWidget->m_serverLBox->setCurrentItem(current + 1);
+ ServerList::iterator it = m_serverList.remove(m_serverList.at(current));
+ ++it;
+ m_serverList.insert(it, server);
+
+ // Track the server the Server List dialog told us to edit
+ if (m_editedServer && m_editedServerIndex==current)
+ m_editedServerIndex = current + 1;
+ }
+
+ updateServerArrows();
+ }
+
+ void ServerGroupDialog::addChannel()
+ {
+ ChannelDialog dlg(i18n("Add Channel"), this);
+
+ if(dlg.exec() == KDialog::Accepted)
+ {
+ ChannelSettings channel = dlg.channelSettings();
+ m_mainWidget->m_channelLBox->insertItem(channel.name());
+ m_channelList.append(channel);
+ updateChannelArrows();
+ }
+ }
+
+ void ServerGroupDialog::editChannel()
+ {
+ uint current = m_mainWidget->m_channelLBox->currentItem();
+
+ if(current < m_channelList.count())
+ {
+ ChannelDialog dlg(i18n("Edit Channel"), this);
+ dlg.setChannelSettings(m_channelList[current]);
+
+ if(dlg.exec() == KDialog::Accepted)
+ {
+ ChannelSettings channel = dlg.channelSettings();
+ m_mainWidget->m_channelLBox->changeItem(channel.name(), current);
+ m_channelList[current] = channel;
+ }
+ }
+ }
+
+ void ServerGroupDialog::deleteChannel()
+ {
+ uint current = m_mainWidget->m_channelLBox->currentItem();
+
+ if(current < m_channelList.count())
+ {
+ m_channelList.remove(m_channelList.at(current));
+ m_mainWidget->m_channelLBox->removeItem(current);
+ updateChannelArrows();
+ }
+ }
+
+ void ServerGroupDialog::updateChannelArrows()
+ {
+ m_mainWidget->m_upChannelBtn->setEnabled( m_mainWidget->m_channelLBox->count()>1 && m_mainWidget->m_channelLBox->currentItem()>0 );
+
+ m_mainWidget->m_downChannelBtn->setEnabled( m_mainWidget->m_channelLBox->count()>1 &&
+ m_mainWidget->m_channelLBox->currentItem()<m_mainWidget->m_channelLBox->numRows()-1 );
+ bool selected = m_mainWidget->m_channelLBox->currentItem() >= 0;
+ m_mainWidget->m_removeChannelButton->setEnabled(selected);
+ m_mainWidget->m_changeChannelButton->setEnabled(selected);
+ }
+
+ void ServerGroupDialog::moveChannelUp()
+ {
+ uint current = m_mainWidget->m_channelLBox->currentItem();
+
+ if(current > 0)
+ {
+ ChannelSettings channel = m_channelList[current];
+ m_mainWidget->m_channelLBox->removeItem(current);
+ m_mainWidget->m_channelLBox->insertItem(channel.name(), current - 1);
+ m_mainWidget->m_channelLBox->setCurrentItem(current - 1);
+ ChannelList::iterator it = m_channelList.remove(m_channelList.at(current));
+ --it;
+ m_channelList.insert(it, channel);
+ }
+
+ updateChannelArrows();
+ }
+
+ void ServerGroupDialog::moveChannelDown()
+ {
+ uint current = m_mainWidget->m_channelLBox->currentItem();
+
+ if(current < (m_channelList.count() - 1))
+ {
+ ChannelSettings channel = m_channelList[current];
+ m_mainWidget->m_channelLBox->removeItem(current);
+ m_mainWidget->m_channelLBox->insertItem(channel.name(), current + 1);
+ m_mainWidget->m_channelLBox->setCurrentItem(current + 1);
+ ChannelList::iterator it = m_channelList.remove(m_channelList.at(current));
+ ++it;
+ m_channelList.insert(it, channel);
+ }
+
+ updateChannelArrows();
+ }
+
+ void ServerGroupDialog::editIdentity()
+ {
+ IdentityDialog dlg(this);
+ dlg.setCurrentIdentity(m_mainWidget->m_identityCBox->currentItem());
+
+ if(dlg.exec() == KDialog::Accepted)
+ {
+ IdentityList identities = Preferences::identityList();
+ m_mainWidget->m_identityCBox->clear();
+
+ for(IdentityList::ConstIterator it = identities.begin(); it != identities.end(); ++it)
+ {
+ m_mainWidget->m_identityCBox->insertItem((*it)->getName());
+ }
+
+ m_mainWidget->m_identityCBox->setCurrentText(dlg.currentIdentity()->getName());
+ m_identitiesNeedsUpdate = true; // and what's this for?
+ ViewContainer* vc = KonversationApplication::instance()->getMainWindow()->getViewContainer();
+ vc->updateViewEncoding(vc->getFrontView());
+ }
+ }
+
+ void ServerGroupDialog::slotOk()
+ {
+ if (m_mainWidget->m_nameEdit->text().isEmpty())
+ {
+ KMessageBox::error(this, i18n("The network name is required."));
+ }
+ else if (m_serverList.count() == 0)
+ {
+ KMessageBox::error(this, i18n("You need to add at least one server to the network."));
+ }
+ else
+ {
+ accept();
+ }
+ }
+
+}
+
+#include "servergroupdialog.moc"
diff --git a/konversation/src/servergroupdialog.h b/konversation/src/servergroupdialog.h
new file mode 100644
index 0000000..a8222d0
--- /dev/null
+++ b/konversation/src/servergroupdialog.h
@@ -0,0 +1,85 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ copyright: (C) 2004 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#ifndef KONVERSATIONSERVERGROUPDIALOG_H
+#define KONVERSATIONSERVERGROUPDIALOG_H
+
+#include "servergroupsettings.h"
+
+#include <kdialogbase.h>
+
+
+class QLineEdit;
+class QComboBox;
+class QListBox;
+class QCheckBox;
+class QToolButton;
+class ServerGroupDialogUI;
+
+namespace Konversation
+{
+
+ class ServerGroupDialog : public KDialogBase
+ {
+ Q_OBJECT
+ public:
+ explicit ServerGroupDialog(const QString& title, QWidget* parent = 0, const char* name = 0);
+ ~ServerGroupDialog();
+
+ void setServerGroupSettings(ServerGroupSettingsPtr settings);
+ ServerGroupSettingsPtr serverGroupSettings();
+
+ ServerSettings editedServer();
+
+ int execAndEditServer(ServerSettings server);
+
+ bool identitiesNeedsUpdate() const { return m_identitiesNeedsUpdate; }
+
+ protected slots:
+ virtual void slotOk();
+
+ void addServer();
+ void editServer();
+ void editServer(ServerSettings server);
+ void deleteServer();
+ void updateServerArrows();
+ void moveServerUp();
+ void moveServerDown();
+
+ void addChannel();
+ void editChannel();
+ void deleteChannel();
+ void updateChannelArrows();
+ void moveChannelUp();
+ void moveChannelDown();
+
+ void editIdentity();
+
+ private:
+ ServerGroupDialogUI* m_mainWidget;
+ bool m_expanded;
+ bool m_enableNotifications;
+ bool m_configBacked;
+ int m_id;
+ int m_sortIndex;
+
+ bool m_identitiesNeedsUpdate;
+
+ bool m_editedServer;
+ uint m_editedServerIndex;
+ ServerList m_serverList;
+ ChannelList m_channelList;
+ ChannelList m_channelHistory;
+ };
+
+}
+#endif
diff --git a/konversation/src/servergroupdialogui.ui b/konversation/src/servergroupdialogui.ui
new file mode 100644
index 0000000..182147f
--- /dev/null
+++ b/konversation/src/servergroupdialogui.ui
@@ -0,0 +1,318 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ServerGroupDialogUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ServerGroupDialogUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>491</width>
+ <height>493</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_networkLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Network name:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_nameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>m_identityLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Identity:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>m_commandsLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Commands:</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>m_editIdentityButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Edit...</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_commandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_autoConnectCBox</cstring>
+ </property>
+ <property name="text">
+ <string>Co&amp;nnect on application start up</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_serverGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Servers</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListBox" row="0" column="0" rowspan="1" colspan="6">
+ <property name="name">
+ <cstring>m_serverLBox</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>m_addServerButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>m_changeServerButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>E&amp;dit...</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="2">
+ <property name="name">
+ <cstring>m_removeServerButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="5">
+ <property name="name">
+ <cstring>m_downServerBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="4">
+ <property name="name">
+ <cstring>m_upServerBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="1" column="3">
+ <property name="name">
+ <cstring>serverSpacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="5" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_channelGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Auto Join Channels</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListBox" row="0" column="0" rowspan="1" colspan="6">
+ <property name="name">
+ <cstring>m_channelLBox</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>m_addChannelButton</cstring>
+ </property>
+ <property name="text">
+ <string>Add...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>m_changeChannelButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Ed&amp;it...</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="5">
+ <property name="name">
+ <cstring>m_downChannelBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="2">
+ <property name="name">
+ <cstring>m_removeChannelButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="1" column="3">
+ <property name="name">
+ <cstring>channelSpacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>255</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QToolButton" row="1" column="4">
+ <property name="name">
+ <cstring>m_upChannelBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>m_identityCBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistbox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistbox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/servergroupsettings.cpp b/konversation/src/servergroupsettings.cpp
new file mode 100644
index 0000000..e10d553
--- /dev/null
+++ b/konversation/src/servergroupsettings.cpp
@@ -0,0 +1,229 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004, 2007 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "servergroupsettings.h"
+#include "konversationapplication.h"
+
+
+namespace Konversation
+{
+
+ int ServerGroupSettings::s_availableId = 0;
+
+ ServerGroupSettings::ServerGroupSettings()
+ : KShared()
+ {
+ m_id = s_availableId;
+ s_availableId++;
+ m_sortIndex = m_id;
+ m_autoConnect = false;
+ m_identityId = 0;
+ m_enableNotifications = true;
+ }
+
+ ServerGroupSettings::ServerGroupSettings(int id)
+ : KShared()
+ {
+ if(id < 0)
+ {
+ m_id = s_availableId;
+ s_availableId++;
+ }
+ else
+ {
+ m_id = id;
+ }
+
+ m_sortIndex = m_id;
+ m_autoConnect = false;
+ m_identityId = 0;
+ m_enableNotifications = true;
+ }
+
+ ServerGroupSettings::ServerGroupSettings(const ServerGroupSettings& settings)
+ : KShared()
+ {
+ setName(settings.name());
+ setServerList(settings.serverList());
+ setIdentityId(settings.identityId());
+ setChannelList(settings.channelList());
+ setConnectCommands(settings.connectCommands());
+ setAutoConnectEnabled(settings.autoConnectEnabled());
+ setNotificationsEnabled(settings.enableNotifications());
+ m_id = settings.id();
+ m_sortIndex = settings.sortIndex();
+ }
+
+ ServerGroupSettings::ServerGroupSettings(const QString& name)
+ : KShared()
+ {
+ setName(name);
+ m_id = s_availableId;
+ s_availableId++;
+ m_sortIndex = m_id;
+ m_autoConnect = false;
+ m_identityId = 0;
+ m_enableNotifications = true;
+ }
+
+ ServerGroupSettings::~ServerGroupSettings()
+ {
+ }
+
+ void ServerGroupSettings::setServerList(const ServerList& list)
+ {
+ m_serverList.clear();
+ m_serverList = list;
+ }
+
+ void ServerGroupSettings::removeServer(const ServerSettings settings)
+ {
+ Konversation::ServerList::iterator it;
+
+ for (it = m_serverList.begin(); it != m_serverList.end(); ++it)
+ {
+ if ((*it)==settings)
+ {
+ m_serverList.remove((*it));
+
+ return;
+ }
+ }
+ }
+
+ ServerSettings ServerGroupSettings::serverByIndex(unsigned int index) const
+ {
+ ServerList servers = serverList();
+
+ if(index < servers.count())
+ {
+ return servers[index];
+ }
+
+ return ServerSettings();
+ }
+
+ void ServerGroupSettings::setChannelList(const ChannelList& list)
+ {
+ m_channelList.clear();
+ m_channelList = list;
+ }
+
+ ChannelSettings ServerGroupSettings::channelByIndex(unsigned int index) const
+ {
+ if(index < m_channelList.count())
+ {
+ return m_channelList[index];
+ }
+
+ return ChannelSettings();
+ }
+
+ void ServerGroupSettings::addChannel(const ChannelSettings& channel, const ChannelSettings& before)
+ {
+ if (before.name().isEmpty())
+ m_channelList.append(channel);
+ else
+ m_channelList.insert(m_channelList.find(before), channel);
+ }
+
+ void ServerGroupSettings::removeChannel(const ChannelSettings& channel)
+ {
+ m_channelList.remove(channel);
+ }
+
+ IdentityPtr ServerGroupSettings::identity() const
+ {
+ return Preferences::identityById(m_identityId);
+ }
+
+ void ServerGroupSettings::appendChannelHistory(const ChannelSettings& channel)
+ {
+ ChannelList::iterator endIt = m_channelHistory.end();
+
+ for(ChannelList::iterator it = m_channelHistory.begin(); it != endIt; ++it)
+ {
+ if(channel.name() == (*it).name())
+ {
+ (*it).setPassword(channel.password());
+ (*it).setNotificationsEnabled(channel.enableNotifications());
+ return;
+ }
+ }
+
+ m_channelHistory.append(channel);
+ }
+
+ ChannelSettings ServerGroupSettings::channelByNameFromHistory(const QString& channelName)
+ {
+ ChannelList::iterator endIt = m_channelHistory.end();
+
+ for(ChannelList::iterator it = m_channelHistory.begin(); it != endIt; ++it)
+ {
+ if(channelName == (*it).name())
+ {
+ return (*it);
+ }
+ }
+
+ return ChannelSettings(channelName);
+ }
+
+ //
+ // ChannelSettings
+ //
+
+ ChannelSettings::ChannelSettings()
+ {
+ setNotificationsEnabled(true);
+ }
+
+ ChannelSettings::ChannelSettings(const ChannelSettings& settings)
+ {
+ setName(settings.name());
+ setPassword(settings.password());
+ setNotificationsEnabled(settings.enableNotifications());
+ }
+
+ ChannelSettings::ChannelSettings(const QString& name)
+ {
+ setName(name);
+ setNotificationsEnabled(true);
+ }
+
+ ChannelSettings::ChannelSettings(const QString& name, const QString& password)
+ {
+ setName(name);
+ setPassword(password);
+ setNotificationsEnabled(true);
+ }
+
+ ChannelSettings::ChannelSettings(const QString& name, const QString& password, bool enableNotifications)
+ {
+ setName(name);
+ setPassword(password);
+ setNotificationsEnabled(enableNotifications);
+ }
+
+ bool ChannelSettings::operator== (const ChannelSettings& channel) const
+ {
+ if (m_name.lower() == channel.name().lower())
+ return true;
+ else
+ return false;
+ }
+
+ ChannelSettings::~ChannelSettings()
+ {
+ }
+
+}
diff --git a/konversation/src/servergroupsettings.h b/konversation/src/servergroupsettings.h
new file mode 100644
index 0000000..903725a
--- /dev/null
+++ b/konversation/src/servergroupsettings.h
@@ -0,0 +1,131 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004, 2007 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONVERSATIONSERVERGROUPSETTINGS_H
+#define KONVERSATIONSERVERGROUPSETTINGS_H
+
+#include "serversettings.h"
+#include "identity.h"
+
+#include <qvaluelist.h>
+#include <qstringlist.h>
+
+#include <ksharedptr.h>
+
+
+namespace Konversation
+{
+
+ class ChannelSettings
+ {
+ public:
+ ChannelSettings();
+ ChannelSettings(const ChannelSettings& settings);
+ explicit ChannelSettings(const QString& name);
+ ChannelSettings(const QString& name, const QString& password);
+ ChannelSettings(const QString& name, const QString& password, bool enableNotifications);
+ ~ChannelSettings();
+
+ void setName(const QString& name) { m_name = name; }
+ QString name() const { return m_name; }
+
+ void setPassword(const QString& password) { m_password = password; }
+ QString password() const { return m_password; }
+
+ void setNotificationsEnabled(bool enable) { m_enableNotifications = enable; }
+ bool enableNotifications() const { return m_enableNotifications; }
+
+ bool operator==(const ChannelSettings& channel) const;
+
+ private:
+ QString m_name;
+ QString m_password;
+
+ bool m_enableNotifications;
+ };
+
+ class ServerGroupSettings;
+ typedef KSharedPtr<ServerGroupSettings> ServerGroupSettingsPtr;
+ typedef QValueList<ServerGroupSettingsPtr> ServerGroupList;
+ typedef QValueList<ServerSettings> ServerList;
+ typedef QValueList<ChannelSettings> ChannelList;
+
+ class ServerGroupSettings : public KShared
+ {
+ public:
+ ServerGroupSettings();
+ ServerGroupSettings(int id);
+ ServerGroupSettings(const ServerGroupSettings& settings);
+ ServerGroupSettings(const QString& name);
+ ~ServerGroupSettings();
+
+ void setName(const QString& name) { m_name = name; }
+ QString name() const { return m_name; }
+
+ void setServerList(const ServerList& list);
+ void addServer(const ServerSettings& settings) { m_serverList.append(settings); }
+ void removeServer(const ServerSettings settings);
+ ServerList serverList() const { return m_serverList; }
+ ServerSettings serverByIndex(unsigned int index) const;
+
+
+ void setIdentityId(int identityId) { m_identityId = identityId; }
+ int identityId() const { return m_identityId; }
+ IdentityPtr identity() const;
+
+ void setChannelList(const ChannelList& list);
+ void addChannel(const ChannelSettings& channel) { m_channelList.append(channel); }
+ void addChannel(const ChannelSettings& channel, const ChannelSettings& before);
+ void removeChannel(const ChannelSettings& channel);
+ ChannelList channelList() const { return m_channelList; }
+ ChannelSettings channelByIndex(unsigned int index) const;
+
+ void setConnectCommands(const QString& commands) { m_connectCommands = commands; }
+ QString connectCommands() const { return m_connectCommands; }
+
+ void setAutoConnectEnabled(bool enabled) { m_autoConnect = enabled; }
+ bool autoConnectEnabled() const { return m_autoConnect; }
+
+ int id() const { return m_id; }
+
+ void setSortIndex(int sortIndex) { m_sortIndex = sortIndex; }
+ int sortIndex() const { return m_sortIndex; }
+
+ void setChannelHistory(const ChannelList& list) { m_channelHistory = list; }
+ void appendChannelHistory(const ChannelSettings& channel);
+ ChannelList channelHistory() const { return m_channelHistory; }
+ ChannelSettings channelByNameFromHistory(const QString& channelName);
+
+ void setNotificationsEnabled(bool enable) { m_enableNotifications = enable; }
+ bool enableNotifications() const { return m_enableNotifications; }
+
+ void setExpanded(bool enable) { m_expanded = enable; }
+ bool expanded() const { return m_expanded; }
+
+ private:
+ static int s_availableId;
+ int m_sortIndex;
+ QString m_name;
+ ServerList m_serverList;
+ int m_identityId;
+ ChannelList m_channelList;
+ ChannelList m_channelHistory;
+ QString m_connectCommands;
+ bool m_autoConnect;
+ QString m_group;
+ int m_id;
+ bool m_enableNotifications;
+ bool m_expanded;
+ };
+
+}
+#endif
diff --git a/konversation/src/serverison.cpp b/konversation/src/serverison.cpp
new file mode 100644
index 0000000..b2d66a2
--- /dev/null
+++ b/konversation/src/serverison.cpp
@@ -0,0 +1,242 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ serverison.h - Class to give a list of all the nicks known to the
+ addressbook and watchednick list that are on this
+ server. There is one instance of this class for
+ each Server object.
+ begin: Fri Sep 03 2004
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+#include "serverison.h"
+#include "server.h"
+#include "addressbook.h"
+#include "konversationapplication.h"
+#include "nickinfo.h"
+#include "viewcontainer.h"
+
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kabc/addressbook.h>
+#include <kabc/stdaddressbook.h>
+
+
+ServerISON::ServerISON(Server* server) : m_server(server)
+{
+ m_ISONList_invalid = true;
+ //We need to know when the addressbook changes because if the info for an offline nick changes,
+ //we won't get a nickInfoChanged signal.
+ connect( Konversation::Addressbook::self()->getAddressBook(), SIGNAL( addressBookChanged( AddressBook * ) ),
+ this, SLOT( addressbookChanged() ) );
+ connect( Konversation::Addressbook::self(), SIGNAL(addresseesChanged()),
+ this, SLOT(addressbookChanged()));
+ connect( m_server, SIGNAL(nickInfoChanged(Server*, const NickInfoPtr)),
+ this, SLOT(nickInfoChanged(Server*, const NickInfoPtr)));
+ connect( m_server,
+ SIGNAL(channelMembersChanged(Server*, const QString&, bool, bool, const QString& )),
+ this,
+ SLOT(slotChannelMembersChanged(Server*, const QString&, bool, bool, const QString& )));
+ connect( m_server,
+ SIGNAL(channelJoinedOrUnjoined(Server*, const QString&, bool )),
+ this,
+ SLOT(slotChannelJoinedOrUnjoined(Server*, const QString&, bool )));
+ connect(KonversationApplication::instance(), SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)),
+ this, SLOT(slotServerGroupsChanged()));
+}
+
+QStringList ServerISON::getWatchList()
+{
+ if(m_ISONList_invalid)
+ recalculateAddressees();
+ return m_watchList;
+}
+
+QStringList ServerISON::getISONList()
+{
+ if(m_ISONList_invalid)
+ recalculateAddressees();
+ return m_ISONList;
+}
+
+QStringList ServerISON::getAddressees()
+{
+ if(m_ISONList_invalid)
+ recalculateAddressees();
+ return m_addresseesISON;
+}
+
+KABC::Addressee ServerISON::getOfflineNickAddressee(QString& nickname)
+{
+ QString lcNickname = nickname.lower();
+ if(m_ISONList_invalid)
+ recalculateAddressees();
+ if (m_offlineNickToAddresseeMap.contains(lcNickname))
+ return m_offlineNickToAddresseeMap[lcNickname];
+ else
+ return KABC::Addressee();
+}
+
+void ServerISON::recalculateAddressees()
+{
+ // If not watching nicks, no need to build notify list.
+ if (Preferences::useNotify())
+ {
+ // Get all nicks known to be online.
+ const NickInfoMap* allNicks = m_server->getAllNicks();
+ // Build a map of online nicknames with associated addressbook entry,
+ // indexed by KABC::Addressee uid.
+ // Note that there can be more than one nick associated with an addressee.
+ QMap<QString,QStringList> addresseeToOnlineNickMap;
+ NickInfoMap::ConstIterator nickInfoItEnd = allNicks->constEnd();
+ for(NickInfoMap::ConstIterator nickInfoIt=allNicks->constBegin();
+ nickInfoIt != nickInfoItEnd; ++nickInfoIt)
+ {
+ NickInfoPtr nickInfo = nickInfoIt.data();
+ KABC::Addressee addressee = nickInfo->getAddressee();
+ if (!addressee.isEmpty())
+ {
+ QString uid = addressee.uid();
+ QStringList nicknames = addresseeToOnlineNickMap[uid];
+ nicknames.append(nickInfo->getNickname());
+ addresseeToOnlineNickMap[uid] = nicknames;
+ }
+ }
+
+ // Lowercase server name and server group.
+ QString lserverName = m_server->getServerName().lower();
+ QString lserverGroup = m_server->getDisplayName().lower();
+
+ // Build notify list from nicks in addressbook, eliminating dups (case insensitive).
+ QMap<QString,QString> ISONMap;
+ m_offlineNickToAddresseeMap.clear();
+ for( KABC::AddressBook::ConstIterator it =
+ Konversation::Addressbook::self()->getAddressBook()->begin();
+ it != Konversation::Addressbook::self()->getAddressBook()->end(); ++it )
+ {
+ if(Konversation::Addressbook::self()->hasAnyNicks(*it))
+ {
+ QString uid = (*it).uid();
+ // First check if we already know that this addressee is online.
+ // If so, add all the nicks of the addressee that are online, but do not
+ // add the offline nicks. There is no point in monitoring such nicks.
+ if (addresseeToOnlineNickMap.contains(uid))
+ {
+ QStringList nicknames = addresseeToOnlineNickMap[uid];
+ QStringList::iterator itEnd = nicknames.end();
+
+ for(QStringList::iterator it = nicknames.begin(); it != itEnd; ++it)
+ {
+ ISONMap.insert((*it).lower(), (*it), true);
+ }
+ }
+ else
+ {
+ // If addressee is not known to be online, add all of the nicknames
+ // of the addressee associated with this server or server group (if any)
+ // to the notify list.
+ // Simultaneously, build a map of all offline nicks and corresponding
+ // KABC::Addressee, indexed by lowercase nickname.
+ QStringList nicks = QStringList::split( QChar( 0xE000 ),
+ (*it).custom("messaging/irc", "All") );
+ QStringList::ConstIterator nicksItEnd = nicks.constEnd();
+ for( QStringList::ConstIterator nicksIt = nicks.constBegin();
+ nicksIt != nicksItEnd; ++nicksIt )
+ {
+ QString lserverOrGroup = (*nicksIt).section(QChar(0xE120),1).lower();
+ if(lserverOrGroup == lserverName || lserverOrGroup == lserverGroup ||
+ lserverOrGroup.isEmpty())
+ {
+ QString nickname = (*nicksIt).section(QChar(0xE120),0,0);
+ QString lcNickname = nickname.lower();
+ ISONMap.insert(lcNickname, nickname, true);
+ m_offlineNickToAddresseeMap.insert(lcNickname, *it, true);
+ }
+ }
+ }
+ }
+ }
+ // The part of the ISON list due to the addressbook.
+ m_addresseesISON = ISONMap.values();
+ // Merge with watch list from prefs, eliminating dups (case insensitive).
+ // TODO: Don't add nick on user watch list if nick is known to be online
+ // under a different nickname?
+ QStringList prefsWatchList =
+ Preferences::notifyListByGroupName(m_server->getDisplayName());
+ QStringList::iterator itEnd = prefsWatchList.end();
+
+ for(QStringList::iterator it = prefsWatchList.begin(); it != itEnd; ++it)
+ {
+ ISONMap.insert((*it).lower(), (*it), true);
+ }
+
+ // Build final watch list.
+ m_watchList = ISONMap.values();
+ // Eliminate nicks that are online in a joined channel, since there is no point
+ // in doing an ISON on such nicks.
+ m_ISONList.clear();
+ itEnd = m_watchList.end();
+
+ for(QStringList::iterator it = m_watchList.begin(); it != itEnd; ++it)
+ {
+ if (m_server->getNickJoinedChannels(*it).isEmpty())
+ {
+ m_ISONList.append(*it);
+ }
+ }
+ }
+ else
+ {
+ m_addresseesISON.clear();
+ m_ISONList.clear();
+ }
+
+ m_ISONList_invalid = false;
+}
+
+// When user changes preferences and has nick watching turned on, rebuild notify list.
+void ServerISON::slotServerGroupsChanged()
+{
+ kdDebug() << "ServerISON::slotServerGroupsChanged" << endl;
+ m_ISONList_invalid = true;
+}
+
+void ServerISON::nickInfoChanged(Server* /*server*/, const NickInfoPtr /*nickInfo*/) {
+//We need to call recalculateAddressees before returning m_ISONList
+
+//Maybe we could do something like:
+//if(m_ISONList.contains(nickInfo->getNickName())) return;
+m_ISONList_invalid = true;
+}
+
+void ServerISON::addressbookChanged()
+{
+ //We need to call recalculateAddressees before returning m_ISONList
+ m_ISONList_invalid = true;
+}
+
+void ServerISON::slotChannelMembersChanged(Server* /*server*/, const QString& /*channelName*/,
+bool joined, bool parted, const QString& nickname)
+{
+ // Whenever a nick on the watch list leaves the last joined channel, must recalculate lists.
+ // The nick will be added to the ISON list.
+ if (joined && parted && m_watchList.contains(nickname))
+ if (m_server->getNickJoinedChannels(nickname).isEmpty()) m_ISONList_invalid = true;
+}
+
+void ServerISON::slotChannelJoinedOrUnjoined(Server* /*server*/,
+const QString& /*channelName*/, bool /*joined*/)
+{
+ // If user left or joined a channel, need to recalculate lists, since watched nicks
+ // may need to be moved from/to ISON list.
+ m_ISONList_invalid = true;
+}
+
+#include "serverison.moc"
diff --git a/konversation/src/serverison.h b/konversation/src/serverison.h
new file mode 100644
index 0000000..4500564
--- /dev/null
+++ b/konversation/src/serverison.h
@@ -0,0 +1,113 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ serverison.h - Class to give a list of all the nicks known to the
+ addressbook and watchednick list that are on this
+ server. There is one instance of this class for
+ each Server object.
+ begin: Fri Sep 03 2004
+ copyright: (C) 2004 by John Tapsell
+ email: john@geola.co.uk
+*/
+
+/**
+ * @author John Tapsell <john@geola.co.uk>
+ * @author Gary Cramblitt <garycramblitt@comcast.net>
+ */
+
+#ifndef SERVERISON_H
+#define SERVERISON_H
+
+#include "nickinfo.h"
+
+
+class Server;
+
+typedef QMap<QString,KABC::Addressee> OfflineNickToAddresseeMap;
+
+class ServerISON : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit ServerISON(Server* server);
+ /**
+ * Returns a list of nicks that we want to know whether they are online
+ * of offline.
+ *
+ * Calls getAddressees() and merges with the Watch List from preferences.
+ * The resulting nicks don't have the servername/servergroup attached.
+ *
+ * @returns A list of nicks that we want to know if they are on or not.
+ *
+ * @see getAddressees()
+ */
+ QStringList getWatchList();
+ /**
+ * The same list as getWatchList, but with nicks in joined channels eliminated.
+ * There is no point in performing an ISON on such nicks because we already
+ * know they are online. This function is called, and the result sent to the
+ * server as an /ISON command.
+ */
+ QStringList getISONList();
+
+ /**
+ * Returns _some_ of the nicks that the addressees have.
+ * It loops through all the addressees that have nickinfos.
+ *
+ * - If that addressee has some nicks, and at least one of them is in a
+ * channel we are in, then we know they are online, so don't add.
+ * - Otherwise, if that addressee has some nicks, and we think they are
+ * online, add the nick that they are currently online with. This does
+ * mean that if they change their nick, they will appear offline for the
+ * duration between ISON's.
+ * - Otherwise, add all the nicks we know the addressee has.
+ */
+ QStringList getAddressees();
+
+ /**
+ * Given the nickname of nick that is offline (or at least not known to be online),
+ * returns the addressbook entry (if any) for the nick.
+ * @param nickname Desired nickname. Case insensitive.
+ * @return Addressbook entry of the nick or empty if not found.
+ */
+ KABC::Addressee getOfflineNickAddressee(QString& nickname);
+
+ private slots:
+ void addressbookChanged();
+ void nickInfoChanged(Server* server, const NickInfoPtr nickInfo);
+ void slotServerGroupsChanged();
+ void slotChannelMembersChanged(Server* server, const QString& channelName, bool joined, bool parted, const QString& nickname);
+ void slotChannelJoinedOrUnjoined(Server* server, const QString& channelName, bool joined);
+
+ private:
+ /** Map of all offline nicks in the addressbook associated with this server
+ * or server group and their addressbook entry, indexed by lowercase nickname.
+ */
+ OfflineNickToAddresseeMap m_offlineNickToAddresseeMap;
+
+ /// A pointer to the server we are a member of.
+ Server* m_server;
+ /// List of nicks to watch that come from addressbook.
+ QStringList m_addresseesISON;
+ /// List from above merged with Watch List from preferences.
+ QStringList m_watchList;
+ /// List from above but with nicks that are in joined channels eliminated.
+ /// There is no point in doing an ISON on such nicks because we know they are
+ /// online in one of the channels the user is in.
+ QStringList m_ISONList;
+ /// If this is true, then we need to call recalculateAddressee before returning m_ISONList
+ bool m_ISONList_invalid;
+ /**
+ * Rebuilds list of nicks to watch whenever an addressbook change occurs
+ * or preferences change (whenever m_ISONLIst_invalid is true).
+ */
+ void recalculateAddressees();
+
+};
+#endif
diff --git a/konversation/src/serverlistdialog.cpp b/konversation/src/serverlistdialog.cpp
new file mode 100644
index 0000000..92a4a80
--- /dev/null
+++ b/konversation/src/serverlistdialog.cpp
@@ -0,0 +1,564 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004, 2007 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "serverlistdialog.h"
+#include "preferences.h"
+#include "konversationapplication.h"
+#include "servergroupdialog.h"
+#include "connectionsettings.h"
+
+#include <qpushbutton.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qstringlist.h>
+#include <qwhatsthis.h>
+#include <qheader.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kguiitem.h>
+#include <kmessagebox.h>
+
+
+namespace Konversation
+{
+ //
+ // ServerListItem
+ //
+
+ ServerListItem::ServerListItem(KListView* parent, int serverGroupId, int sortIndex,
+ const QString& serverGroup, const QString& identity, const QString& channels)
+ : KListViewItem(parent, serverGroup, identity, channels)
+ {
+ m_serverGroupId = serverGroupId;
+ m_sortIndex = sortIndex;
+ m_name = serverGroup;
+ m_isServer = false;
+ }
+
+ ServerListItem::ServerListItem(QListViewItem* parent, int serverGroupId, int sortIndex,
+ const QString& name, const ServerSettings& server)
+ : KListViewItem(parent, name)
+ {
+ m_serverGroupId = serverGroupId;
+ m_sortIndex = sortIndex;
+ m_name = name;
+ m_server = server;
+ m_isServer = true;
+ }
+
+ int ServerListItem::selectedChildrenCount()
+ {
+ int count = 0;
+
+ QListViewItem* item = firstChild();
+
+ while (item)
+ {
+ if (item->isSelected())
+ ++count;
+
+ item = item->nextSibling();
+ }
+
+ return count;
+ }
+
+ int ServerListItem::compare(QListViewItem *i, int col, bool ascending) const
+ {
+ ServerListItem* item = static_cast<ServerListItem*>(i);
+
+ if (col==0)
+ {
+ if (!item->server().host().isEmpty())
+ {
+ if (sortIndex() == item->sortIndex())
+ return 0;
+ else if (sortIndex() < item->sortIndex())
+ return ascending ? -1 : 1;
+ else
+ return ascending ? 1 : -1;
+ }
+
+ if (sortIndex() == item->sortIndex())
+ return 0;
+ else if (sortIndex() < item->sortIndex())
+ return -1;
+ else
+ return 1;
+ }
+
+ return key( col, ascending ).localeAwareCompare( i->key( col, ascending ) );
+ }
+
+ ServerListDialog::ServerListDialog(QWidget *parent, const char *name)
+ : KDialogBase(Plain, i18n("Server List"), Ok|Close, Ok, parent, name, false)
+ {
+ setButtonOK(KGuiItem(i18n("C&onnect"), "connect_creating", i18n("Connect to the server"), i18n("Click here to connect to the selected IRC network and channel.")));
+
+ QFrame* mainWidget = plainPage();
+
+ m_serverList = new ServerListView(mainWidget);
+ QWhatsThis::add(m_serverList, i18n("This shows the listof configured IRC networks. An IRC network is a collection of cooperating servers. You need only connect to one of the servers in the network to be connected to the entire IRC network. Once connected, Konversation will automatically join the channels shown. When Konversation is started for the first time, the Freenode network and the <i>#kde</i> channel are already entered for you."));
+ m_serverList->setAllColumnsShowFocus(true);
+ m_serverList->setRootIsDecorated(true);
+ m_serverList->setResizeMode(QListView::AllColumns);
+ m_serverList->addColumn(i18n("Network"));
+ m_serverList->addColumn(i18n("Identity"));
+ m_serverList->addColumn(i18n("Channels"));
+ m_serverList->setSelectionModeExt(KListView::Extended);
+ m_serverList->setShowSortIndicator(true);
+ m_serverList->setSortColumn(0);
+ m_serverList->setDragEnabled(true);
+ m_serverList->setAcceptDrops(true);
+ m_serverList->setDropVisualizer(true);
+ m_serverList->header()->setMovingEnabled(false);
+
+ m_addButton = new QPushButton(i18n("&New..."), mainWidget);
+ QWhatsThis::add(m_addButton, i18n("Click here to define a new Network, including the server to connect to, and the Channels to automatically join once connected."));
+ m_editButton = new QPushButton(i18n("&Edit..."), mainWidget);
+ m_delButton = new QPushButton(i18n("&Delete"), mainWidget);
+
+ QCheckBox* showAtStartup = new QCheckBox(i18n("Show at application startup"), mainWidget);
+ showAtStartup->setChecked(Preferences::showServerList());
+ connect(showAtStartup, SIGNAL(toggled(bool)), this, SLOT(setShowAtStartup(bool)));
+
+ QGridLayout* layout = new QGridLayout(mainWidget, 5, 2, 0, spacingHint());
+
+ layout->addMultiCellWidget(m_serverList, 0, 3, 0, 0);
+ layout->addWidget(m_addButton, 0, 1);
+ layout->addWidget(m_editButton, 1, 1);
+ layout->addWidget(m_delButton, 2, 1);
+ layout->addMultiCellWidget(showAtStartup, 4, 4, 0, 1);
+ layout->setRowStretch(3, 10);
+
+ m_serverList->setFocus();
+
+ m_selectedItem = false;
+ m_selectedServer = ServerSettings("");
+
+ // Load server list
+ updateServerList();
+
+ connect(m_serverList, SIGNAL(aboutToMove()), this, SLOT(slotAboutToMove()));
+ connect(m_serverList, SIGNAL(moved()), this, SLOT(slotMoved()));
+ connect(m_serverList, SIGNAL(doubleClicked(QListViewItem *, const QPoint&, int)), this, SLOT(slotOk()));
+ connect(m_serverList, SIGNAL(selectionChanged()), this, SLOT(updateButtons()));
+ connect(m_serverList, SIGNAL(expanded(QListViewItem*)), this, SLOT(slotSetGroupExpanded(QListViewItem*)));
+ connect(m_serverList, SIGNAL(collapsed(QListViewItem*)), this, SLOT(slotSetGroupCollapsed(QListViewItem*)));
+ connect(m_addButton, SIGNAL(clicked()), this, SLOT(slotAdd()));
+ connect(m_editButton, SIGNAL(clicked()), this, SLOT(slotEdit()));
+ connect(m_delButton, SIGNAL(clicked()), this, SLOT(slotDelete()));
+
+ updateButtons();
+
+ KConfig* config = kapp->config();
+ config->setGroup("ServerListDialog");
+ QSize newSize = size();
+ newSize = config->readSizeEntry("Size", &newSize);
+ resize(newSize);
+
+ m_serverList->setSelected(m_serverList->firstChild(), true);
+ }
+
+ ServerListDialog::~ServerListDialog()
+ {
+ KConfig* config = kapp->config();
+ config->setGroup("ServerListDialog");
+ config->writeEntry("Size", size());
+ }
+
+ void ServerListDialog::slotClose()
+ {
+ slotApply();
+ accept();
+ }
+
+ void ServerListDialog::slotOk()
+ {
+ QPtrList<QListViewItem> selected = m_serverList->selectedItems();
+ ServerListItem * item = static_cast<ServerListItem*>(selected.first());
+
+ while (item)
+ {
+ if (item->isServer())
+ {
+ ConnectionSettings settings;
+
+ settings.setServerGroup(Preferences::serverGroupById(item->serverGroupId()));
+
+ settings.setServer(item->server());
+
+ emit connectTo(Konversation::PromptToReuseConnection, settings);
+ }
+ else
+ emit connectTo(Konversation::PromptToReuseConnection, item->serverGroupId());
+
+ item = static_cast<ServerListItem*>(selected.next());
+ }
+ }
+
+ void ServerListDialog::slotAdd()
+ {
+ ServerGroupDialog dlg(i18n("New Network"), this);
+
+ if(dlg.exec() == KDialog::Accepted)
+ {
+ addServerGroup(dlg.serverGroupSettings());
+
+ emit serverGroupsChanged(dlg.serverGroupSettings());
+ }
+ }
+
+ void ServerListDialog::slotEdit()
+ {
+ ServerListItem* item = static_cast<ServerListItem*>(m_serverList->selectedItems().first());
+
+ if (item)
+ {
+ Konversation::ServerGroupSettingsPtr serverGroup = Preferences::serverGroupById(item->serverGroupId());
+
+ if (serverGroup)
+ {
+ ServerGroupDialog dlg(i18n("Edit Network"), this);
+
+ dlg.setServerGroupSettings(serverGroup);
+
+ if (item->isServer())
+ {
+ if(dlg.execAndEditServer(item->server()) == KDialog::Accepted)
+ {
+ delete item;
+
+ m_selectedItem = true;
+ m_selectedServerGroupId = serverGroup->id();
+ m_selectedServer = dlg.editedServer();
+
+ *serverGroup = *(dlg.serverGroupSettings());
+
+ emit serverGroupsChanged(serverGroup);
+ }
+ }
+ else
+ {
+ if(dlg.exec() == KDialog::Accepted)
+ {
+ delete item;
+
+ m_selectedItem = true;
+ m_selectedServerGroupId = serverGroup->id();
+ m_selectedServer = ServerSettings("");
+
+ *serverGroup = *(dlg.serverGroupSettings());
+
+ emit serverGroupsChanged(serverGroup);
+ }
+ }
+ }
+ }
+ }
+
+ void ServerListDialog::slotDelete()
+ {
+ QPtrList<QListViewItem> selectedItems = m_serverList->selectedServerListItems();
+
+ if (selectedItems.isEmpty())
+ return;
+
+ ServerListItem* item = static_cast<ServerListItem*>(selectedItems.first());
+ ServerListItem* parent = 0;
+
+ // Make sure we're not deleting a network's only servers
+ while (item)
+ {
+ if (item->isServer())
+ {
+ parent = static_cast<ServerListItem*>(item->parent());
+
+ if (parent && parent->childCount() == 1)
+ {
+ KMessageBox::error(this, i18n("You cannot delete %1.\n\nThe network %2 needs to have at least one server.").arg(item->name()).arg(parent->name()));
+ return;
+ }
+ else if (parent && parent->childCount() == parent->selectedChildrenCount())
+ {
+ KMessageBox::error(this, i18n("You cannot delete the selected servers.\n\nThe network %1 needs to have at least one server.").arg(parent->name()));
+ return;
+ }
+ }
+
+ item = static_cast<ServerListItem*>(selectedItems.next());
+ }
+
+ // Reset item
+ item = static_cast<ServerListItem*>(selectedItems.first());
+
+ // Ask the user if he really wants to delete what he selected
+ QString question;
+
+ if (selectedItems.count()>1)
+ question = i18n("Do you really want to delete the selected entries?");
+ else
+ question = i18n("Do you really want to delete %1?").arg(item->name());
+
+ if (KMessageBox::warningContinueCancel(this,question) == KMessageBox::Cancel)
+ {
+ return;
+ }
+
+ QListViewItem* itemBelow = 0;
+ QListViewItem* itemAbove = 0;
+
+ // Have fun deleting
+ while (item)
+ {
+ itemBelow = item->nextSibling();
+ itemAbove = item->itemAbove();
+
+ if (item->isServer())
+ {
+ Konversation::ServerGroupSettingsPtr serverGroup = Preferences::serverGroupById(item->serverGroupId());
+ serverGroup->removeServer(item->server());
+ delete item;
+ }
+ else
+ {
+ Preferences::removeServerGroup(item->serverGroupId());
+ delete item;
+ }
+
+ item = static_cast<ServerListItem*>(selectedItems.next());
+ }
+
+ if (itemBelow)
+ {
+ m_serverList->setSelected(itemBelow,true);
+ m_serverList->setCurrentItem(itemBelow);
+ }
+ else if (itemAbove)
+ {
+ m_serverList->setSelected(itemAbove,true);
+ m_serverList->setCurrentItem(itemAbove);
+ }
+ else
+ {
+ if (m_serverList->firstChild())
+ {
+ m_serverList->setSelected(m_serverList->firstChild(),true);
+ m_serverList->setCurrentItem(m_serverList->firstChild());
+ }
+ }
+
+ emit serverGroupsChanged();
+ }
+
+ void ServerListDialog::slotSetGroupExpanded(QListViewItem* item)
+ {
+ ServerListItem* listItem = static_cast<ServerListItem*>(item);
+ Konversation::ServerGroupSettingsPtr serverGroup = Preferences::serverGroupById(listItem->serverGroupId());
+ serverGroup->setExpanded(true);
+ }
+
+ void ServerListDialog::slotSetGroupCollapsed(QListViewItem* item)
+ {
+ ServerListItem* listItem = static_cast<ServerListItem*>(item);
+ Konversation::ServerGroupSettingsPtr serverGroup = Preferences::serverGroupById(listItem->serverGroupId());
+ serverGroup->setExpanded(false);
+ }
+
+ void ServerListDialog::slotAboutToMove()
+ {
+ m_lastSortColumn = m_serverList->sortColumn();
+ m_lastSortOrder = m_serverList->sortOrder();
+ m_serverList->setSortColumn(-1);
+ }
+
+ void ServerListDialog::slotMoved()
+ {
+ Konversation::ServerGroupList newServerGroupList;
+
+ ServerListItem* item = static_cast<ServerListItem*>(m_serverList->firstChild());
+ int newSortIndex = 0;
+
+ while (item)
+ {
+ Konversation::ServerGroupSettingsPtr serverGroup = Preferences::serverGroupById(item->serverGroupId());
+ serverGroup->setSortIndex(newSortIndex);
+
+ newServerGroupList.append(serverGroup);
+
+ item->setSortIndex(newSortIndex);
+
+ ++newSortIndex;
+ item = static_cast<ServerListItem*>(item->nextSibling());
+ }
+
+ Preferences::setServerGroupList(newServerGroupList);
+
+ m_serverList->setSortColumn(m_lastSortColumn);
+ m_serverList->setSortOrder(m_lastSortOrder);
+
+ emit serverGroupsChanged();
+ }
+
+ void ServerListDialog::updateButtons()
+ {
+ int count = m_serverList->selectedItems().count();
+ bool enable = (count > 0);
+
+ enableButtonOK(enable);
+ m_delButton->setEnabled(enable);
+
+ enable = (count == 1);
+ m_editButton->setEnabled(enable);
+ }
+
+ void ServerListDialog::addServerGroup(ServerGroupSettingsPtr serverGroup)
+ {
+ if (m_serverList->lastChild())
+ {
+ ServerListItem* lastChild = static_cast<ServerListItem*>(m_serverList->lastChild());
+ serverGroup->setSortIndex(lastChild->sortIndex() + 1);
+ }
+
+ Preferences::addServerGroup(serverGroup);
+ QListViewItem* item = insertServerGroup(serverGroup);
+ m_serverList->clearSelection();
+ m_serverList->setSelected(item,true);
+ m_serverList->setCurrentItem(item);
+ m_serverList->ensureItemVisible(item);
+ }
+
+ void ServerListDialog::updateServerList()
+ {
+ if (!m_selectedItem && m_serverList->currentItem())
+ {
+ ServerListItem* item = static_cast<ServerListItem*>(m_serverList->currentItem());
+
+ m_selectedItem = true;
+ m_selectedServerGroupId = item->serverGroupId();
+
+ if (item->isServer())
+ m_selectedServer = item->server();
+ else
+ m_selectedServer = ServerSettings("");
+ }
+
+ m_serverList->setUpdatesEnabled(false);
+ m_serverList->clear();
+
+ Konversation::ServerGroupList serverGroups = Preferences::serverGroupList();
+ Konversation::ServerGroupList::iterator it;
+
+ QListViewItem* networkItem = 0;
+
+ for(it = serverGroups.begin(); it != serverGroups.end(); ++it)
+ {
+ networkItem = insertServerGroup((*it));
+
+ // The method was called by slotEdit() ... initialize a pointer to the new
+ // location of the edited server group
+ if (m_selectedItem && m_selectedServer.host().isEmpty() && (*it)->id()==m_selectedServerGroupId)
+ {
+ m_selectedItemPtr = networkItem;
+ }
+ }
+
+ // Highlight the last edited item
+ if (m_selectedItem)
+ {
+ m_serverList->setSelected(m_selectedItemPtr,true);
+ m_serverList->setCurrentItem(m_selectedItemPtr);
+ m_selectedItem = false;
+ }
+
+ m_serverList->setUpdatesEnabled(true);
+ m_serverList->repaint();
+ }
+
+ QListViewItem* ServerListDialog::insertServerGroup(ServerGroupSettingsPtr serverGroup)
+ {
+ // Produce a list of this server group's channels
+ QString channels;
+
+ Konversation::ChannelList channelList = serverGroup->channelList();
+ Konversation::ChannelList::iterator channelIt;
+ Konversation::ChannelList::iterator begin = channelList.begin();
+
+ for(channelIt = begin; channelIt != channelList.end(); ++channelIt)
+ {
+ if (channelIt != begin)
+ channels += ", ";
+
+ channels += (*channelIt).name();
+ }
+
+ QListViewItem* networkItem = 0;
+
+ // Insert the server group into the list
+ networkItem = new ServerListItem(m_serverList,
+ serverGroup->id(),
+ serverGroup->sortIndex(),
+ serverGroup->name(),
+ serverGroup->identity()->getName(),
+ channels);
+
+ // Recreate expanded/collapsed state
+ if (serverGroup->expanded())
+ networkItem->setOpen(true);
+
+ // Produce a list of this server group's servers and iterate over it
+ Konversation::ServerList serverList = serverGroup->serverList();
+ Konversation::ServerList::iterator serverIt;
+
+ QListViewItem* serverItem = 0;
+ int i = 0;
+
+ for (serverIt = serverList.begin(); serverIt != serverList.end(); ++serverIt)
+ {
+ // Produce a string representation of the server object
+ QString name = (*serverIt).host();
+
+ if ((*serverIt).port() != 6667)
+ name += ':' + QString::number((*serverIt).port());
+
+ if ((*serverIt).SSLEnabled())
+ name += + " (SSL)";
+
+ // Insert the server into the list, as child of the server group list item
+ serverItem = new ServerListItem(networkItem,
+ serverGroup->id(),
+ i,
+ name,
+ (*serverIt));
+
+ // The listview shouldn't allow this to be dragged
+ serverItem->setDragEnabled(false);
+
+ // Initialize a pointer to the new location of the last edited server
+ if (m_selectedItem && m_selectedServer==(*serverIt))
+ m_selectedItemPtr = serverItem;
+
+ ++i;
+ }
+
+ return networkItem;
+ }
+
+ void ServerListDialog::setShowAtStartup(bool show)
+ {
+ Preferences::setShowServerList(show);
+ }
+}
+
+#include "serverlistdialog.moc"
diff --git a/konversation/src/serverlistdialog.h b/konversation/src/serverlistdialog.h
new file mode 100644
index 0000000..909f235
--- /dev/null
+++ b/konversation/src/serverlistdialog.h
@@ -0,0 +1,110 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONVERSATIONSERVERLISTDIALOG_H
+#define KONVERSATIONSERVERLISTDIALOG_H
+
+#include "serverlistview.h"
+#include "common.h"
+#include "servergroupsettings.h"
+
+#include <kdialogbase.h>
+
+class ConnectionSettings;
+class Preferences;
+class QPushButton;
+class QStringList;
+
+namespace Konversation
+{
+ class ServerListItem : public KListViewItem
+ {
+ public:
+ ServerListItem(KListView* parent, int serverGroupId, int sortIndex,
+ const QString& serverGroup, const QString& identity, const QString& channels);
+ ServerListItem(QListViewItem* parent, int serverGroupId, int sortIndex,
+ const QString& name, const ServerSettings& server);
+
+ int serverGroupId() const { return m_serverGroupId; }
+
+ void setSortIndex(int id) { m_sortIndex = id; }
+ int sortIndex() const { return m_sortIndex; }
+
+ ServerSettings server() const { return m_server; }
+ QString name() const { return m_name; }
+ bool isServer() const { return m_isServer; }
+
+ int selectedChildrenCount();
+
+ int compare(QListViewItem *i, int col, bool ascending) const;
+
+ private:
+ int m_serverGroupId;
+ int m_sortIndex;
+ QString m_name;
+ ServerSettings m_server;
+ bool m_isServer;
+ };
+
+ class ServerListDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+ explicit ServerListDialog(QWidget *parent = 0, const char *name = 0);
+ ~ServerListDialog();
+
+ public slots:
+ void updateServerList();
+
+ signals:
+ void connectTo(Konversation::ConnectionFlag flag, int serverGroupId);
+ void connectTo(Konversation::ConnectionFlag flag, ConnectionSettings& connectionSettings);
+ void serverGroupsChanged(const Konversation::ServerGroupSettings* serverGroup = 0);
+
+ protected slots:
+ virtual void slotOk();
+ void slotClose();
+ void slotAdd();
+ void slotEdit();
+ void slotDelete();
+
+ void slotSetGroupExpanded(QListViewItem* item);
+ void slotSetGroupCollapsed(QListViewItem* item);
+
+ void slotAboutToMove();
+ void slotMoved();
+
+ void updateButtons();
+
+ void setShowAtStartup(bool show);
+
+ protected:
+ QListViewItem* insertServerGroup(ServerGroupSettingsPtr serverGroup);
+ void addServerGroup(ServerGroupSettingsPtr serverGroup);
+
+ private:
+ ServerListView* m_serverList;
+ QPushButton* m_addButton;
+ QPushButton* m_editButton;
+ QPushButton* m_delButton;
+
+ bool m_selectedItem;
+ int m_selectedServerGroupId;
+ ServerSettings m_selectedServer;
+ QListViewItem* m_selectedItemPtr;
+
+ int m_lastSortColumn;
+ SortOrder m_lastSortOrder;
+ };
+}
+#endif
diff --git a/konversation/src/serverlistview.cpp b/konversation/src/serverlistview.cpp
new file mode 100644
index 0000000..dbae9a9
--- /dev/null
+++ b/konversation/src/serverlistview.cpp
@@ -0,0 +1,114 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ ServerListView is derived from KListView and implements custom
+ drag'n'drop behavior needed in ServerListDialog.
+
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "serverlistview.h"
+
+#include <qdragobject.h>
+#include <kdebug.h>
+
+
+ServerListView::ServerListView(QWidget *parent)
+ : KListView(parent)
+{
+}
+
+ServerListView::~ServerListView()
+{
+}
+
+QPtrList<QListViewItem> ServerListView::selectedServerListItems()
+{
+
+ QPtrList<QListViewItem> selectedItems = KListView::selectedItems();
+ QPtrList<QListViewItem> selectedServerListItems;
+
+ QListViewItem* item = selectedItems.first();
+
+ while (item)
+ {
+ if (item->parent())
+ {
+ if (!item->parent()->isSelected())
+ selectedServerListItems.append(item);
+ }
+ else
+ {
+ selectedServerListItems.append(item);
+ }
+
+ item = selectedItems.next();
+ }
+
+ return selectedServerListItems;
+}
+
+void ServerListView::findDrop(const QPoint &pos, QListViewItem *&parent, QListViewItem *&after)
+{
+ QPoint p (contentsToViewport(pos));
+
+ // Get the position to put it in
+ QListViewItem *atpos = itemAt(p);
+
+ QListViewItem *above;
+ if (!atpos) // put it at the end
+ above = lastItem();
+ else
+ {
+ // Get the closest item before us ('atpos' or the one above, if any)
+ if (p.y() - itemRect(atpos).topLeft().y() < (atpos->height()/2))
+ above = atpos->itemAbove();
+ else
+ above = atpos;
+ }
+
+ if (above)
+ {
+ if (above->firstChild())
+ {
+ after = above;
+ parent = after->parent();
+ return;
+ }
+ else
+ {
+ after = above->parent();
+ parent = after ? after->parent() : 0L;
+ return;
+ }
+ }
+ // set as sibling
+ after = above;
+ parent = after ? after->parent() : 0L;
+}
+
+QDragObject* ServerListView::dragObject()
+{
+ if (!currentItem())
+ return 0;
+
+ QPtrList<QListViewItem> selected = selectedItems();
+ QListViewItem* item = selected.first();
+
+ while (item)
+ {
+ if (!item->dragEnabled())
+ return 0;
+
+ item = selected.next();
+ }
+
+ return new QStoredDrag("application/x-qlistviewitem", viewport());
+}
+
+#include "serverlistview.moc"
diff --git a/konversation/src/serverlistview.h b/konversation/src/serverlistview.h
new file mode 100644
index 0000000..08153a8
--- /dev/null
+++ b/konversation/src/serverlistview.h
@@ -0,0 +1,38 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ ServerListView is derived from KListView and implements custom
+ drag'n'drop behavior needed in ServerListDialog.
+
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef SERVERLISTVIEW_H
+#define SERVERLISTVIEW_H
+
+#include <klistview.h>
+
+
+class QDragObject;
+
+class ServerListView : public KListView
+{
+ Q_OBJECT
+
+ public:
+ explicit ServerListView(QWidget *parent);
+ ~ServerListView();
+
+ QPtrList<QListViewItem> selectedServerListItems();
+
+ protected:
+ void findDrop(const QPoint &pos, QListViewItem *&parent, QListViewItem *&after);
+ QDragObject* dragObject();
+};
+
+#endif
diff --git a/konversation/src/serversettings.cpp b/konversation/src/serversettings.cpp
new file mode 100644
index 0000000..de5214e
--- /dev/null
+++ b/konversation/src/serversettings.cpp
@@ -0,0 +1,66 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#include "serversettings.h"
+
+
+namespace Konversation
+{
+
+ ServerSettings::ServerSettings()
+ {
+ setPort(6667);
+ setSSLEnabled(false);
+ }
+
+ ServerSettings::ServerSettings(const ServerSettings& settings)
+ {
+ setHost(settings.host());
+ setPort(settings.port());
+ setPassword(settings.password());
+ setSSLEnabled(settings.SSLEnabled());
+ }
+
+ ServerSettings::ServerSettings(const QString& host)
+ {
+ setHost(host);
+ setPort(6667);
+ setSSLEnabled(false);
+ }
+
+ ServerSettings::~ServerSettings()
+ {
+ }
+
+ bool ServerSettings::operator==(const ServerSettings& settings) const
+ {
+ if (m_host.lower() == settings.host().lower()
+ && m_port == settings.port()
+ && m_password == settings.password()
+ && m_SSLEnabled == settings.SSLEnabled())
+ {
+ return true;
+ }
+ else
+ return false;
+ }
+
+ void ServerSettings::setHost(const QString& host)
+ {
+ m_host = host.stripWhiteSpace();
+ }
+
+ void ServerSettings::setPassword(const QString& password)
+ {
+ m_password = password.stripWhiteSpace();
+ }
+}
diff --git a/konversation/src/serversettings.h b/konversation/src/serversettings.h
new file mode 100644
index 0000000..d1eec69
--- /dev/null
+++ b/konversation/src/serversettings.h
@@ -0,0 +1,52 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONVERSATIONSERVERSETTINGS_H
+#define KONVERSATIONSERVERSETTINGS_H
+
+#include <qstring.h>
+
+namespace Konversation
+{
+
+ class ServerSettings
+ {
+ public:
+ ServerSettings();
+ ServerSettings(const ServerSettings& settings);
+ explicit ServerSettings(const QString& host);
+ ~ServerSettings();
+
+ void setHost(const QString& host);
+ QString host() const { return m_host; }
+
+ void setPort(int port) { m_port = port; }
+ int port() const { return m_port;}
+
+ void setPassword(const QString& password);
+ QString password() const { return m_password; }
+
+ void setSSLEnabled(bool enabled) { m_SSLEnabled = enabled; }
+ bool SSLEnabled() const { return m_SSLEnabled; }
+
+ bool operator== (const ServerSettings& settings) const;
+
+ private:
+ QString m_host;
+ int m_port;
+ QString m_password;
+ bool m_SSLEnabled;
+
+ };
+
+}
+#endif
diff --git a/konversation/src/ssllabel.cpp b/konversation/src/ssllabel.cpp
new file mode 100644
index 0000000..593acbe
--- /dev/null
+++ b/konversation/src/ssllabel.cpp
@@ -0,0 +1,29 @@
+/*
+ Copyright (c) 2004 by İsmail Dönmez <ismail.donmez@boun.edu.tr>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+ */
+
+#include "ssllabel.h"
+
+
+SSLLabel::SSLLabel(QWidget* parent,const char* name)
+: QLabel(parent,name)
+{
+}
+
+void SSLLabel::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_UNUSED(e);
+ emit clicked();
+}
+
+#include "ssllabel.moc"
diff --git a/konversation/src/ssllabel.h b/konversation/src/ssllabel.h
new file mode 100644
index 0000000..d96223e
--- /dev/null
+++ b/konversation/src/ssllabel.h
@@ -0,0 +1,33 @@
+#ifndef SSLLABEL_H
+#define SSLLABEL_H
+
+/*
+ Copyright (c) 2004 by İsmail Dönmez <ismail.donmez@boun.edu.tr>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+ */
+
+#include <qlabel.h>
+
+class SSLLabel : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ SSLLabel(QWidget* parent, const char* name);
+
+ protected:
+ void mouseReleaseEvent(QMouseEvent *e);
+
+ signals:
+ void clicked();
+};
+#endif
diff --git a/konversation/src/sslsocket.cpp b/konversation/src/sslsocket.cpp
new file mode 100644
index 0000000..189a504
--- /dev/null
+++ b/konversation/src/sslsocket.cpp
@@ -0,0 +1,358 @@
+/*
+ Copyright (c) 2004,2005 by İsmail Dönmez <ismail.donmez@boun.edu.tr>
+
+ based on the code by :
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+*************************************************************************
+*/
+
+#include "sslsocket.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kssl.h>
+#include <ksslinfodlg.h>
+#include <ksslpeerinfo.h>
+#include <ksslcertchain.h>
+#include <ksslcertificatecache.h>
+#include <kmessagebox.h>
+#include <ksocketdevice.h>
+
+
+struct SSLSocketPrivate
+{
+ int m_sslCertState;
+ QString remoteHost;
+ QString url;
+ QString m_sslCertErrors;
+
+ KSSL* kssl;
+ KSSLCertificateCache* cc;
+};
+
+SSLSocket::SSLSocket(QWidget* serverParent, QObject* parent, const char* name)
+: KStreamSocket(0L,0L,parent,name), m_serverParent(serverParent)
+{
+ d = new SSLSocketPrivate;
+ d->kssl = 0L;
+ d->cc = new KSSLCertificateCache;
+ d->cc->reload();
+
+}
+
+SSLSocket::~SSLSocket()
+{
+ // Close stream socket
+ close();
+
+ // close ssl socket
+ if( d->kssl ) d->kssl->close();
+
+ delete d->kssl;
+ delete d->cc;
+ delete d;
+}
+
+Q_LONG SSLSocket::writeBlock(const char *data, Q_ULONG len)
+{
+ if (d->kssl && KSSL::doesSSLWork() && state() == KNetwork::KClientSocketBase::Connected)
+ return d->kssl->write( data,len );
+ else
+ return 0;
+}
+
+Q_LONG SSLSocket::readBlock(char *data, Q_ULONG maxlen)
+{
+ int err = d->kssl->read( data, maxlen );
+ return err;
+}
+
+void SSLSocket::stateChanging(KClientSocketBase::SocketState newState)
+{
+ if(newState == KClientSocketBase::Connected)
+ {
+ KClientSocketBase::stateChanging(KClientSocketBase::Connected);
+ connected();
+ }
+ else
+ KClientSocketBase::stateChanging(newState);
+}
+
+const QString SSLSocket::details()
+{
+ int strength = d->kssl->connectionInfo().getCipherUsedBits();
+
+ QString details = i18n("Connection is secured with %1 bit SSL.").arg(strength);
+
+ return details;
+}
+
+void SSLSocket::connected()
+{
+
+ if( KSSL::doesSSLWork() )
+ {
+ if( !d->kssl )
+ {
+ d->kssl = new KSSL();
+ if( d->kssl->connect( socketDevice()->socket() ) )
+ {
+ if( verifyCertificate() != 1 )
+ {
+ close();
+ }
+ else
+ emit sslInitDone();
+ }
+ }
+ else
+ {
+ d->kssl->reInitialize();
+ }
+ }
+ else
+ {
+ kdError() << "SSL not functional!" << endl;
+ emit sslFailure(i18n("The functionality to connect to servers using encrypted SSL communications is not available to Konversation because OpenSSL support was not enabled at compile time. You will need to get new version of KDE that has SSL support."));
+ close();
+ }
+}
+
+void SSLSocket::showInfoDialog()
+{
+ if( state() == KNetwork::KClientSocketBase::Connected )
+ {
+ showSSLInfoDialog();
+ }
+}
+
+void SSLSocket::showSSLInfoDialog()
+{
+
+ KSSLInfoDlg* sslInfoDlg = new KSSLInfoDlg(true, m_serverParent, "sslInfoDlg", true);
+ sslInfoDlg->setCertState( d->m_sslCertErrors );
+ sslInfoDlg->setup( *(d->kssl),
+ (const QString&) d->remoteHost,
+ (const QString&) d->url
+ );
+ sslInfoDlg->exec();
+}
+
+int SSLSocket::verifyCertificate()
+{
+ int rc = 0;
+ int result;
+ bool permacache = false;
+ bool ipMatchesCN = false;
+ bool doAddHost = false;
+ QString hostname;
+ KSSLCertificate::KSSLValidation validation;
+
+ d->remoteHost = peerAddress().nodeName();
+ d->url = "irc://"+d->remoteHost+':'+peerAddress().serviceName();
+
+ KSSLCertificate& peerCertificate = d->kssl->peerInfo().getPeerCertificate();
+
+ validation = peerCertificate.validate();
+ if(validation == KSSLCertificate::Unknown )
+ {
+ emit sslFailure(i18n("The SSL certificate returned from the server was not recognized. Maybe this server does not support SSL on the given port? If this server supports normal, non-SSL communications as well, then SSL will be on a different port."));
+ return 0;
+ }
+
+ KSSLX509Map certinfo(peerCertificate.getSubject());
+ hostname = certinfo.getValue("CN");
+
+ KSSLCertificate::KSSLValidationList validationList
+ = peerCertificate.validateVerbose(KSSLCertificate::SSLServer);
+
+ ipMatchesCN = d->kssl->peerInfo().certMatchesAddress();
+
+ validation = KSSLCertificate::Ok;
+
+ if (!validationList.isEmpty())
+ validation = validationList.first();
+
+ for(KSSLCertificate::KSSLValidationList::ConstIterator it = validationList.begin();
+ it != validationList.end(); ++it)
+ {
+ d->m_sslCertErrors += QString::number(*it)+':';
+ }
+
+ if (peerCertificate.chain().isValid() && peerCertificate.chain().depth() > 1)
+ {
+ QString theChain;
+ QPtrList<KSSLCertificate> chain = peerCertificate.chain().getChain();
+ for (KSSLCertificate *c = chain.first(); c; c = chain.next())
+ {
+ theChain += c->toString();
+ theChain += '\n';
+ }
+ }
+
+ d->m_sslCertState = validation;
+
+ if (validation == KSSLCertificate::Ok)
+ rc = 1;
+
+ // - Read from cache and see if there is a policy for this
+ KSSLCertificateCache::KSSLCertificatePolicy cp = d->cc->getPolicyByCertificate(peerCertificate);
+
+ // - validation code
+ if (validation != KSSLCertificate::Ok)
+ {
+ if( cp == KSSLCertificateCache::Unknown || cp == KSSLCertificateCache::Ambiguous)
+ {
+ cp = KSSLCertificateCache::Prompt;
+ }
+ else
+ {
+ // A policy was already set so let's honor that.
+ permacache = d->cc->isPermanent(peerCertificate);
+ }
+
+ if (!ipMatchesCN && cp == KSSLCertificateCache::Accept )
+ {
+ do
+ {
+ QString msg = i18n("The IP address of the host %1 "
+ "does not match the one the "
+ "certificate was issued to.");
+ result = KMessageBox::warningYesNoCancel( m_serverParent,
+ msg.arg(hostname),
+ i18n("Server Authentication"),
+ KGuiItem(i18n("Details")),
+ KGuiItem(i18n("Continue")),
+ "SslIpCNMismatch");
+ if(result == KMessageBox::Yes)
+ showInfoDialog();
+ else if(result == KMessageBox::Cancel)
+ {
+ return 0;
+ }
+ }
+ while ( result == KMessageBox::Yes );
+
+ }
+
+ // Precondition: cp is one of Reject, Accept or Prompt
+ switch (cp)
+ {
+ case KSSLCertificateCache::Accept:
+ rc = 1;
+ break;
+
+ case KSSLCertificateCache::Reject:
+ rc = -1;
+ break;
+
+ case KSSLCertificateCache::Prompt:
+ {
+ do
+ {
+ if (validation == KSSLCertificate::InvalidHost)
+ {
+ QString msg = i18n("The IP address of the host %1 "
+ "does not match the one the "
+ "certificate was issued to.");
+ result = KMessageBox::warningYesNoCancel( m_serverParent,
+ msg.arg(hostname),
+ i18n("Server Authentication"),
+ KGuiItem(i18n("Details")),
+ KGuiItem(i18n("Continue")),
+ QString::null,
+ KMessageBox::Dangerous);
+ }
+ else
+ {
+ QString msg = i18n("The server (%1) certificate failed the "
+ "authenticity test.");
+ result = KMessageBox::warningYesNoCancel( m_serverParent,
+ msg.arg(hostname),
+ i18n("Server Authentication"),
+ KGuiItem(i18n("Details")),
+ KGuiItem(i18n("Continue")),
+ QString::null,
+ KMessageBox::Dangerous);
+ }
+
+ if (result == KMessageBox::Yes)
+ {
+ showInfoDialog();
+ }
+ else if(result == KMessageBox::Cancel)
+ {
+ return 0;
+ }
+ }
+ while (result == KMessageBox::Yes);
+
+ if (result == KMessageBox::No)
+ {
+ rc = 1;
+ cp = KSSLCertificateCache::Accept;
+ doAddHost = true;
+ result = KMessageBox::warningYesNo( m_serverParent,
+ i18n("Would you like to accept this "
+ "certificate forever without "
+ "being prompted?"),
+ i18n("Server Authentication"),
+ KGuiItem(i18n("&Forever")),
+ KGuiItem(i18n("&Current Sessions Only"))
+ );
+
+ if (result == KMessageBox::Yes)
+ permacache = true;
+ else
+ permacache = false;
+ }
+ else
+ {
+ rc = -1;
+ cp = KSSLCertificateCache::Prompt;
+ }
+
+ break;
+ }
+ default:
+ kdDebug() << "SSL error in cert code." << endl;
+ break;
+ }
+ }
+
+ // - cache the results
+ d->cc->addCertificate(peerCertificate, cp, permacache);
+ if (doAddHost)
+ d->cc->addHost(peerCertificate, d->remoteHost);
+
+ if (rc == -1)
+ return rc;
+ /*
+ kdDebug() << "SSL connection information follows:" << endl
+ << "+-----------------------------------------------" << endl
+ << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
+ << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
+ << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
+ << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
+ << " of " << d->kssl->connectionInfo().getCipherBits()
+ << " bits used." << endl
+ << "| PEER:" << endl
+ << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
+ << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
+ << "| Validation: " << (int) validation << endl
+ << "+-----------------------------------------------"
+ << endl;
+ */
+ return rc;
+}
+
+#include "sslsocket.moc"
diff --git a/konversation/src/sslsocket.h b/konversation/src/sslsocket.h
new file mode 100644
index 0000000..aa15de0
--- /dev/null
+++ b/konversation/src/sslsocket.h
@@ -0,0 +1,60 @@
+#ifndef SSL_SOCKET_H
+#define SSL_SOCKET_H
+/*
+ KDE SSL Socket
+
+ Copyright (c) 2004,2005 by İsmail Dönmez <ismail.donmez@boun.edu.tr>
+
+ based on the code by:
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+*************************************************************************
+*/
+
+#include <kstreamsocket.h>
+using namespace KNetwork;
+
+struct SSLSocketPrivate;
+
+class SSLSocket : public KStreamSocket
+{
+ Q_OBJECT
+
+ public:
+ explicit SSLSocket(QWidget* serverParent, QObject* parent = 0L, const char* name = 0L);
+ ~SSLSocket();
+
+ void showInfoDialog();
+ const QString details();
+
+ Q_LONG writeBlock (const char *data, Q_ULONG len);
+ Q_LONG readBlock (char *data, Q_ULONG maxlen);
+
+ protected:
+ void stateChanging (KClientSocketBase::SocketState newState);
+
+ signals:
+ /** Emitted when there is a problem with SSL
+ * @param reason An error string that has already been through i18n
+ */
+ void sslFailure(const QString& reason);
+ void sslInitDone();
+
+ private:
+ void connected();
+ int verifyCertificate();
+ void showSSLInfoDialog();
+
+ QWidget* m_serverParent;
+
+ SSLSocketPrivate* d;
+};
+#endif
diff --git a/konversation/src/statuspanel.cpp b/konversation/src/statuspanel.cpp
new file mode 100644
index 0000000..f593559
--- /dev/null
+++ b/konversation/src/statuspanel.cpp
@@ -0,0 +1,398 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2003 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "statuspanel.h"
+#include "channel.h"
+#include "konversationapplication.h"
+#include "ircinput.h"
+#include "ircview.h"
+#include "ircviewbox.h"
+#include "server.h"
+
+#include <qpushbutton.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qhbox.h>
+#include <qtextcodec.h>
+#include <qlineedit.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+
+StatusPanel::StatusPanel(QWidget* parent) : ChatWindow(parent)
+{
+ setType(ChatWindow::Status);
+
+ setChannelEncodingSupported(true);
+
+ awayChanged=false;
+ awayState=false;
+
+ // set up text view, will automatically take care of logging
+ IRCViewBox* ircBox = new IRCViewBox(this, 0); // Server will be set later in setServer()
+ setTextView(ircBox->ircView());
+
+ QHBox* commandLineBox=new QHBox(this);
+ commandLineBox->setSpacing(spacing());
+ commandLineBox->setMargin(0);
+
+ nicknameCombobox=new QComboBox(commandLineBox);
+ nicknameCombobox->setEditable(true);
+ nicknameCombobox->insertStringList(Preferences::nicknameList());
+ oldNick=nicknameCombobox->currentText();
+
+ awayLabel=new QLabel(i18n("(away)"),commandLineBox);
+ awayLabel->hide();
+ statusInput=new IRCInput(commandLineBox);
+
+ getTextView()->installEventFilter(statusInput);
+ statusInput->installEventFilter(this);
+
+ setLog(Preferences::log());
+
+ connect(getTextView(),SIGNAL (gotFocus()),statusInput,SLOT (setFocus()) );
+
+ connect(getTextView(),SIGNAL (sendFile()),this,SLOT (sendFileMenu()) );
+ connect(getTextView(),SIGNAL (autoText(const QString&)),this,SLOT (sendStatusText(const QString&)) );
+
+ connect(statusInput,SIGNAL (submit()),this,SLOT(statusTextEntered()) );
+ connect(statusInput,SIGNAL (textPasted(const QString&)),this,SLOT(textPasted(const QString&)) );
+ connect(getTextView(), SIGNAL(textPasted(bool)), statusInput, SLOT(paste(bool)));
+ connect(getTextView(), SIGNAL(popupCommand(int)), this, SLOT(popupCommand(int)));
+
+ connect(nicknameCombobox,SIGNAL (activated(int)),this,SLOT(nicknameComboboxChanged()));
+ Q_ASSERT(nicknameCombobox->lineEdit()); //it should be editedable. if we design it so it isn't, remove these lines.
+ if(nicknameCombobox->lineEdit())
+ connect(nicknameCombobox->lineEdit(), SIGNAL (lostFocus()),this,SLOT(nicknameComboboxChanged()));
+
+ updateAppearance();
+}
+
+StatusPanel::~StatusPanel()
+{
+}
+
+void StatusPanel::serverSaysClose()
+{
+ closeYourself(false);
+}
+
+void StatusPanel::setNickname(const QString& newNickname)
+{
+ nicknameCombobox->setCurrentText(newNickname);
+}
+
+void StatusPanel::childAdjustFocus()
+{
+ statusInput->setFocus();
+}
+
+void StatusPanel::sendStatusText(const QString& sendLine)
+{
+ // create a work copy
+ QString outputAll(sendLine);
+ // replace aliases and wildcards
+ if(m_server->getOutputFilter()->replaceAliases(outputAll))
+ {
+ outputAll = m_server->parseWildcards(outputAll, m_server->getNickname(), QString(), QString(), QString(), QString());
+ }
+
+ // Send all strings, one after another
+ QStringList outList=QStringList::split('\n',outputAll);
+ for(unsigned int index=0;index<outList.count();index++)
+ {
+ QString output(outList[index]);
+
+ // encoding stuff is done in Server()
+ Konversation::OutputFilterResult result = m_server->getOutputFilter()->parse(m_server->getNickname(), output, QString());
+
+ if(!result.output.isEmpty())
+ {
+ appendServerMessage(result.typeString, result.output);
+ }
+ m_server->queue(result.toServer);
+ } // for
+}
+
+void StatusPanel::statusTextEntered()
+{
+ QString line=statusInput->text();
+ statusInput->setText("");
+
+ if(line.lower()==Preferences::commandChar()+"clear") textView->clear();
+ else
+ {
+ if(line.length()) sendStatusText(line);
+ }
+}
+
+void StatusPanel::textPasted(const QString& text)
+{
+ if(m_server)
+ {
+ QStringList multiline=QStringList::split('\n',text);
+ for(unsigned int index=0;index<multiline.count();index++)
+ {
+ QString line=multiline[index];
+ QString cChar(Preferences::commandChar());
+ // make sure that lines starting with command char get escaped
+ if(line.startsWith(cChar)) line=cChar+line;
+ sendStatusText(line);
+ }
+ }
+}
+
+void StatusPanel::updateAppearance()
+{
+ QColor fg;
+ QColor bg;
+ if(Preferences::inputFieldsBackgroundColor())
+ {
+ fg=Preferences::color(Preferences::ChannelMessage);
+ bg=Preferences::color(Preferences::TextViewBackground);
+ }
+ else
+ {
+ fg=colorGroup().foreground();
+ bg=colorGroup().base();
+ }
+
+ statusInput->unsetPalette();
+ statusInput->setPaletteForegroundColor(fg);
+ statusInput->setPaletteBackgroundColor(bg);
+
+ getTextView()->unsetPalette();
+
+ if(Preferences::showBackgroundImage())
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ Preferences::backgroundImage());
+ }
+ else
+ {
+ getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
+ QString());
+ }
+
+ if (Preferences::customTextFont())
+ {
+ getTextView()->setFont(Preferences::textFont());
+ statusInput->setFont(Preferences::textFont());
+ nicknameCombobox->setFont(Preferences::textFont());
+ }
+ else
+ {
+ getTextView()->setFont(KGlobalSettings::generalFont());
+ statusInput->setFont(KGlobalSettings::generalFont());
+ nicknameCombobox->setFont(KGlobalSettings::generalFont());
+ }
+
+ showNicknameBox(Preferences::showNicknameBox());
+
+ ChatWindow::updateAppearance();
+}
+
+void StatusPanel::setName(const QString& newName)
+{
+ ChatWindow::setName(newName);
+ setLogfileName(newName.lower());
+}
+
+void StatusPanel::updateName()
+{
+ QString newName = getServer()->getDisplayName();
+ setName(newName);
+ setLogfileName(newName.lower());
+}
+
+void StatusPanel::sendFileMenu()
+{
+ emit sendFile();
+}
+
+void StatusPanel::indicateAway(bool show)
+{
+ // QT does not redraw the label properly when they are not on screen
+ // while getting hidden, so we remember the "soon to be" state here.
+ if(isHidden())
+ {
+ awayChanged=true;
+ awayState=show;
+ }
+ else
+ {
+ if(show)
+ awayLabel->show();
+ else
+ awayLabel->hide();
+ }
+}
+
+// fix Qt's broken behavior on hidden QListView pages
+void StatusPanel::showEvent(QShowEvent*)
+{
+ if(awayChanged)
+ {
+ awayChanged=false;
+ indicateAway(awayState);
+ }
+}
+
+QString StatusPanel::getTextInLine() { return statusInput->text(); }
+
+bool StatusPanel::canBeFrontView() { return true; }
+bool StatusPanel::searchView() { return true; }
+
+void StatusPanel::setNotificationsEnabled(bool enable)
+{
+ if (m_server->getServerGroup()) m_server->getServerGroup()->setNotificationsEnabled(enable);
+
+ m_notificationsEnabled = enable;
+}
+
+bool StatusPanel::closeYourself(bool confirm)
+{
+ int result;
+
+ //FIXME: Show "Do you really want to close ..." warnings in
+ // disconnected state instead of closing directly. Can't do
+ // that due to string freeze at the moment.
+ if (confirm && !m_server->isConnected())
+ {
+ result = KMessageBox::warningContinueCancel(this, i18n("Do you really want to close '%1'?\n\n All associated tabs will be closed as well.").arg(getName()),
+ i18n("Close Tab"), i18n("Close"), "QuitServerTab");
+ }
+ else
+ {
+ result = KMessageBox::warningContinueCancel(
+ this,
+ i18n("Do you want to disconnect from '%1'?\n\n All associated tabs will be closed as well.").arg(m_server->getServerName()),
+ i18n("Disconnect From Server"),
+ i18n("Disconnect"),
+ "QuitServerTab");
+ }
+
+ if (result==KMessageBox::Continue)
+ {
+ if (m_server->getServerGroup()) m_server->getServerGroup()->setNotificationsEnabled(notificationsEnabled());
+ m_server->quitServer();
+ // This will delete the status view as well.
+ m_server->deleteLater();
+ m_server = 0;
+ return true;
+ }
+ return false;
+}
+
+void StatusPanel::nicknameComboboxChanged()
+{
+ QString newNick=nicknameCombobox->currentText();
+ oldNick=m_server->getNickname();
+ if(oldNick!=newNick)
+ {
+ nicknameCombobox->setCurrentText(oldNick);
+ m_server->queue("NICK "+newNick);
+ }
+ // return focus to input line
+ statusInput->setFocus();
+}
+
+void StatusPanel::changeNickname(const QString& newNickname)
+{
+ m_server->queue("NICK "+newNickname);
+}
+
+void StatusPanel::emitUpdateInfo()
+{
+ emit updateInfo(getServer()->getDisplayName());
+}
+
+void StatusPanel::appendInputText(const QString& text, bool fromCursor)
+{
+ if(!fromCursor)
+ {
+ statusInput->append(text);
+ }
+ else
+ {
+ int para = 0, index = 0;
+ statusInput->getCursorPosition(&para, &index);
+ statusInput->insertAt(text, para, index);
+ statusInput->setCursorPosition(para, index + text.length());
+ }
+}
+
+ // virtual
+void StatusPanel::setChannelEncoding(const QString& encoding)
+{
+ Preferences::setChannelEncoding(m_server->getDisplayName(), ":server", encoding);
+}
+
+QString StatusPanel::getChannelEncoding() // virtual
+{
+ return Preferences::channelEncoding(m_server->getDisplayName(), ":server");
+}
+
+ // virtual
+QString StatusPanel::getChannelEncodingDefaultDesc()
+{
+ return i18n("Identity Default ( %1 )").arg(getServer()->getIdentity()->getCodecName());
+}
+
+//Used to disable functions when not connected
+void StatusPanel::serverOnline(bool online)
+{
+ //statusInput->setEnabled(online);
+ getTextView()->setNickAndChannelContextMenusEnabled(online);
+ nicknameCombobox->setEnabled(online);
+}
+
+void StatusPanel::showNicknameBox(bool show)
+{
+ if(show)
+ {
+ nicknameCombobox->show();
+ }
+ else
+ {
+ nicknameCombobox->hide();
+ }
+}
+
+void StatusPanel::setIdentity(const IdentityPtr identity)
+{
+ if (identity)
+ {
+ nicknameCombobox->clear();
+ nicknameCombobox->insertStringList(identity->getNicknameList());
+ }
+}
+
+void StatusPanel::popupCommand(int command)
+{
+ switch(command)
+ {
+ case Konversation::Join:
+ m_server->queue("JOIN " + getTextView()->currentChannel());
+ break;
+ case Konversation::Topic:
+ m_server->requestTopic(getTextView()->currentChannel());
+ break;
+ case Konversation::Names:
+ m_server->queue("NAMES " + getTextView()->currentChannel(), Server::LowPriority);
+ break;
+ }
+}
+
+#include "statuspanel.moc"
diff --git a/konversation/src/statuspanel.h b/konversation/src/statuspanel.h
new file mode 100644
index 0000000..3704361
--- /dev/null
+++ b/konversation/src/statuspanel.h
@@ -0,0 +1,96 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2003 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef STATUSPANEL_H
+#define STATUSPANEL_H
+
+#include "chatwindow.h"
+
+#include <qstring.h>
+
+
+class QPushButton;
+class QCheckBox;
+class QLabel;
+class QComboBox;
+
+class IRCInput;
+class NickChangeDialog;
+
+class StatusPanel : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit StatusPanel(QWidget* parent);
+ ~StatusPanel();
+
+ virtual void setName(const QString& newName);
+
+ virtual QString getTextInLine();
+ virtual bool closeYourself(bool askForConfirmation=true);
+ virtual bool canBeFrontView();
+ virtual bool searchView();
+
+ virtual void setChannelEncoding(const QString& encoding);
+ virtual QString getChannelEncoding();
+ virtual QString getChannelEncodingDefaultDesc();
+ virtual void emitUpdateInfo();
+
+ void setIdentity(const IdentityPtr identity);
+
+ virtual bool isInsertSupported() { return true; }
+
+ virtual void setNotificationsEnabled(bool enable);
+
+ signals:
+ void sendFile();
+
+ public slots:
+ void setNickname(const QString& newNickname);
+ virtual void indicateAway(bool show);
+ void showNicknameBox(bool show);
+ void updateAppearance();
+ virtual void appendInputText(const QString&, bool fromCursor);
+ void updateName();
+ void serverSaysClose();
+
+ protected slots:
+ void sendFileMenu();
+ void statusTextEntered();
+ void sendStatusText(const QString& line);
+ // connected to IRCInput::textPasted() - used for large/multiline pastes
+ void textPasted(const QString& text);
+ void changeNickname(const QString& newNickname);
+ void nicknameComboboxChanged();
+ //Used to disable functions when not connected
+ virtual void serverOnline(bool online);
+
+ void popupCommand(int command);
+
+ protected:
+
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+
+ bool awayChanged;
+ bool awayState;
+
+ void showEvent(QShowEvent* event);
+
+ QComboBox* nicknameCombobox;
+ QLabel* awayLabel;
+ IRCInput* statusInput;
+ QCheckBox* logCheckBox;
+ QString oldNick;
+};
+#endif
diff --git a/konversation/src/tabnotifications_preferences.ui b/konversation/src/tabnotifications_preferences.ui
new file mode 100644
index 0000000..337b010
--- /dev/null
+++ b/konversation/src/tabnotifications_preferences.ui
@@ -0,0 +1,407 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>TabNotifications_Config</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TabNotifications_Config</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>278</width>
+ <height>418</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Look</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsText</cstring>
+ </property>
+ <property name="text">
+ <string>Use colored text</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsLeds</cstring>
+ </property>
+ <property name="text">
+ <string>Use colored LEDs</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Notifications</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsSystem</cstring>
+ </property>
+ <property name="text">
+ <string>Application event</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Application events occur in Konsole tabs, the DCC Status tab and other application tabs not used directly for chatting.</string>
+ </property>
+ </widget>
+ <spacer row="5" column="3">
+ <property name="name">
+ <cstring>spacer45_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="5" column="4">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsSystemColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsMsgs</cstring>
+ </property>
+ <property name="text">
+ <string>Message</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>spacer42</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="0" column="4">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsMsgsColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="defaultColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ </widget>
+ <spacer row="1" column="3">
+ <property name="name">
+ <cstring>spacer42_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>60</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsPrivate</cstring>
+ </property>
+ <property name="text">
+ <string>Private message</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="4">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsPrivateColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="defaultColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsNick</cstring>
+ </property>
+ <property name="text">
+ <string>Current nick used</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="2" column="3">
+ <property name="name">
+ <cstring>spacer44</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>spacer45</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="2" column="4">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsNickColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="4">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsHighlightsColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsEvents</cstring>
+ </property>
+ <property name="text">
+ <string>Channel event</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Channel events are mode changes or users joining/leaving a channel.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsHighlights</cstring>
+ </property>
+ <property name="text">
+ <string>Highlight</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="4" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer43</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="4" column="4">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsEventsColor</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox10</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_TabNotificationsOverride</cstring>
+ </property>
+ <property name="text">
+ <string>Give precedence to chat window highlight colors</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you select this, the colors you chose in the Highlights preferences page will override the colors for "Current nick used" and "Highlight".</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer47</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>60</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_TabNotificationsMsgs</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TabNotificationsMsgsColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_TabNotificationsEvents</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TabNotificationsEventsColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_TabNotificationsNick</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TabNotificationsNickColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_TabNotificationsHighlights</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TabNotificationsHighlightsColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_TabNotificationsSystem</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TabNotificationsSystemColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_TabNotificationsText</tabstop>
+ <tabstop>kcfg_TabNotificationsLeds</tabstop>
+ <tabstop>kcfg_TabNotificationsMsgs</tabstop>
+ <tabstop>kcfg_TabNotificationsMsgsColor</tabstop>
+ <tabstop>kcfg_TabNotificationsPrivate</tabstop>
+ <tabstop>kcfg_TabNotificationsPrivateColor</tabstop>
+ <tabstop>kcfg_TabNotificationsNick</tabstop>
+ <tabstop>kcfg_TabNotificationsNickColor</tabstop>
+ <tabstop>kcfg_TabNotificationsHighlights</tabstop>
+ <tabstop>kcfg_TabNotificationsHighlightsColor</tabstop>
+ <tabstop>kcfg_TabNotificationsEvents</tabstop>
+ <tabstop>kcfg_TabNotificationsEventsColor</tabstop>
+ <tabstop>kcfg_TabNotificationsSystem</tabstop>
+ <tabstop>kcfg_TabNotificationsSystemColor</tabstop>
+ <tabstop>kcfg_TabNotificationsOverride</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/tabs_preferences.cpp b/konversation/src/tabs_preferences.cpp
new file mode 100644
index 0000000..80815b3
--- /dev/null
+++ b/konversation/src/tabs_preferences.cpp
@@ -0,0 +1,59 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "tabs_preferences.h"
+#include "tabs_preferencesui.h"
+
+#include <qcombobox.h>
+#include <qcheckbox.h>
+
+
+Tabs_Config::Tabs_Config(QWidget *parent, const char *name)
+ : Tabs_PreferencesUI(parent, name)
+{
+ connect(kcfg_TabPlacement, SIGNAL(activated(int)), this, SLOT(toggleCheckBoxes(int)));
+}
+
+Tabs_Config::~Tabs_Config()
+{
+}
+
+void Tabs_Config::show()
+{
+ QWidget::show();
+
+ if (kcfg_TabPlacement->currentItem() == 0 || kcfg_TabPlacement->currentItem() == 1)
+ {
+ kcfg_ShowTabBarCloseButton->setEnabled(true);
+ kcfg_UseMaxSizedTabs->setEnabled(true);
+ }
+ else
+ {
+ kcfg_ShowTabBarCloseButton->setEnabled(false);
+ kcfg_UseMaxSizedTabs->setEnabled(false);
+ }
+}
+
+void Tabs_Config::toggleCheckBoxes(int activated)
+{
+ if (activated == 0 || activated == 1)
+ {
+ kcfg_ShowTabBarCloseButton->setEnabled(true);
+ kcfg_UseMaxSizedTabs->setEnabled(true);
+ }
+ else
+ {
+ kcfg_ShowTabBarCloseButton->setEnabled(false);
+ kcfg_UseMaxSizedTabs->setEnabled(false);
+ }
+}
+
+#include "tabs_preferences.moc"
diff --git a/konversation/src/tabs_preferences.h b/konversation/src/tabs_preferences.h
new file mode 100644
index 0000000..de78817
--- /dev/null
+++ b/konversation/src/tabs_preferences.h
@@ -0,0 +1,33 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef TABS_PREFERENCES_H
+#define TABS_PREFERENCES_H
+
+#include "tabs_preferencesui.h"
+
+
+class Tabs_Config : public Tabs_PreferencesUI
+{
+ Q_OBJECT
+
+ public:
+ explicit Tabs_Config(QWidget *parent = 0, const char *name = 0);
+ ~Tabs_Config();
+
+ public slots:
+ virtual void show();
+
+ protected slots:
+ void toggleCheckBoxes(int activated);
+};
+
+#endif
diff --git a/konversation/src/tabs_preferencesui.ui b/konversation/src/tabs_preferencesui.ui
new file mode 100644
index 0000000..a0063f7
--- /dev/null
+++ b/konversation/src/tabs_preferencesui.ui
@@ -0,0 +1,192 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Tabs_PreferencesUI</class>
+<comment>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.</comment>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Tabs_PreferencesUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>337</width>
+ <height>495</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>TabBar_Config</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>lookGBox</cstring>
+ </property>
+ <property name="title">
+ <string>Look</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>placementLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Placement:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Top</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Bottom</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_TabPlacement</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>111</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_CloseButtons</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;close button on tabs</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_ShowTabBarCloseButton</cstring>
+ </property>
+ <property name="text">
+ <string>Show a close button on the &amp;right side of the tab bar</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_UseMaxSizedTabs</cstring>
+ </property>
+ <property name="text">
+ <string>Limit the &amp;size of the tab labels to fit all on screen</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>focusGBox</cstring>
+ </property>
+ <property name="title">
+ <string>Focus</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_BringToFront</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Focus new tabs</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_FocusNewQueries</cstring>
+ </property>
+ <property name="text">
+ <string>Focus new &amp;queries</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>mouseGBox</cstring>
+ </property>
+ <property name="title">
+ <string>Mouse</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_MiddleClickClose</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Middle-click on a tab to close it</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>100</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/konversation/src/theme_preferences.cpp b/konversation/src/theme_preferences.cpp
new file mode 100644
index 0000000..35f93d2
--- /dev/null
+++ b/konversation/src/theme_preferences.cpp
@@ -0,0 +1,307 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2007 Eike Hein <hein@kde.org>
+*/
+
+#include "theme_preferences.h"
+#include "preferences_base.h"
+#include "images.h"
+#include "common.h"
+#include "konversationapplication.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qvbox.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+
+#include <klistbox.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <kfiledialog.h>
+#include <ktar.h>
+#include <kdesktopfile.h>
+#include <kconfigdialog.h>
+
+#include <unistd.h> // unlink()
+
+
+using namespace Konversation;
+
+Theme_Config::Theme_Config(QWidget* parent, const char* name)
+ : Theme_ConfigUI( parent, name)
+{
+ m_defaultThemeIndex = -1;
+
+ // load the current settings
+ loadSettings();
+
+ connect(iconThemeIndex,SIGNAL(highlighted(int)),this,SLOT(updatePreview(int)));
+ connect(iconThemeIndex,SIGNAL(selectionChanged()),this,SLOT(updateButtons()));
+ connect(iconThemeIndex,SIGNAL(selectionChanged()),this,SIGNAL(modified()));
+ connect(installButton,SIGNAL(clicked()),this,SLOT(installTheme()));
+ connect(removeButton,SIGNAL(clicked()),this,SLOT(removeTheme()));
+}
+
+Theme_Config::~Theme_Config()
+{
+}
+
+void Theme_Config::loadSettings()
+{
+ QString themeName, themeComment, themeDir;
+ QString currentTheme = Preferences::iconTheme();
+ int currentThemeIndex = 0;
+
+ // get list of theme dirs
+ m_dirs = KGlobal::dirs()->findAllResources("data","konversation/themes/*/index.desktop");
+
+ // if we have any themes
+ if(m_dirs.count() > 0)
+ {
+ m_dirs.sort();
+
+ // clear listview
+ iconThemeIndex->clear();
+ // initialize index counter
+ int i = 0;
+ // iterate through all found theme directories
+ for(QStringList::ConstIterator it = m_dirs.begin(); it != m_dirs.end(); ++it)
+ {
+ KDesktopFile themeRC(*it);
+ // get the name and comment from the theme
+ themeName = themeRC.readName();
+ themeComment = themeRC.readComment();
+
+ // extract folder name
+ themeDir=(*it).section('/',-2,-2);
+ // is this our currently used theme?
+ if (themeDir==currentTheme)
+ {
+ // remember for hasChanged()
+ m_oldTheme=themeDir;
+ // remember for updatePreview()
+ currentThemeIndex = i;
+ }
+
+ if (themeDir=="default")
+ m_defaultThemeIndex= i;
+
+ // if there was a comment to the theme, add it to the listview entry string
+ if(!themeComment.isEmpty())
+ themeName = themeName+" ("+themeComment+')';
+
+ // insert entry into the listview
+ iconThemeIndex->insertItem(themeName);
+
+ // increment index counter
+ ++i;
+ }
+ // highlight currently active theme and update preview box
+ iconThemeIndex->setSelected(currentThemeIndex, true);
+ updatePreview(currentThemeIndex);
+ }
+
+ // if there was no currently used theme found, use the default theme
+ // If anyone knows how to get the default value from this, please change this!
+ if(m_oldTheme.isEmpty())
+ m_oldTheme = "default";
+
+ // update enabled/disabled state of buttons
+ updateButtons();
+}
+
+bool Theme_Config::hasChanged()
+{
+ // return true if the theme selected is different from the saved theme
+ return ( m_oldTheme != m_currentTheme );
+}
+
+void Theme_Config::saveSettings()
+{
+ // if there are any themes in the listview ...
+ if(iconThemeIndex->count())
+ {
+ // and if anything has changed ...
+ if(hasChanged())
+ {
+ // save icon theme name
+ KConfig* config = kapp->config();
+ config->setGroup("Themes");
+ config->writeEntry("IconTheme",m_currentTheme);
+ // set in-memory theme to the saved theme
+ Preferences::setIconTheme(m_currentTheme);
+ // update theme on runtime
+ KonversationApplication::instance()->images()->initializeNickIcons();
+
+ // remember current theme for hasChanged()
+ m_oldTheme = m_currentTheme;
+ }
+ }
+}
+
+void Theme_Config::restorePageToDefaults()
+{
+ if (m_defaultThemeIndex != -1)
+ iconThemeIndex->setSelected(m_defaultThemeIndex, true);
+}
+
+void Theme_Config::installTheme()
+{
+ KURL themeURL = KFileDialog::getOpenURL(QString(),
+ i18n("*.tar.gz *.tar.bz2 *.tar *.zip|Konversation Themes"),
+ NULL,
+ i18n("Select Theme Package")
+ );
+
+ if(themeURL.isEmpty())
+ return;
+
+ QString themesDir(locateLocal("data", "konversation/themes/"));
+ QString tmpThemeFile;
+
+ if(!KIO::NetAccess::download(themeURL, tmpThemeFile, NULL))
+ {
+ KMessageBox::error(0L,
+ KIO::NetAccess::lastErrorString(),
+ i18n("Failed to Download Theme"),
+ KMessageBox::Notify
+ );
+ return;
+ }
+
+ QDir themeInstallDir(tmpThemeFile);
+
+ if(themeInstallDir.exists()) // We got a directory not a file
+ {
+ if(themeInstallDir.exists("index.desktop"))
+ KIO::NetAccess::dircopy(KURL(tmpThemeFile),KURL(themesDir),0L);
+ else
+ {
+ KMessageBox::error(0L,
+ i18n("Theme archive is invalid."),
+ i18n("Cannot Install Theme"),
+ KMessageBox::Notify
+ );
+ }
+ }
+ else // we got a file
+ {
+
+ KTar themeArchive(tmpThemeFile);
+ themeArchive.open(IO_ReadOnly);
+ kapp->processEvents();
+
+ const KArchiveDirectory* themeDir = themeArchive.directory();;
+ QStringList allEntries = themeDir->entries();
+
+ for(QStringList::ConstIterator it=allEntries.begin(); it != allEntries.end(); ++it)
+ {
+ if(themeDir->entry(*it+"/index.desktop") == NULL)
+ {
+ KMessageBox::error(0L,
+ i18n("Theme archive is invalid."),
+ i18n("Cannot Install Theme"),
+ KMessageBox::Notify
+ );
+ break;
+ }
+ else
+ themeDir->copyTo(themesDir);
+
+ }
+ themeArchive.close();
+ }
+
+ loadSettings();
+ KIO::NetAccess::removeTempFile(tmpThemeFile);
+
+}
+
+void Theme_Config::removeTheme()
+{
+ QString dir;
+ QString themeName = iconThemeIndex->currentText();
+
+ dir = m_dirs[iconThemeIndex->currentItem()];
+
+ int remove = KMessageBox::warningContinueCancel(0L,
+ i18n("Do you want to remove %1 ?").arg(themeName),
+ i18n("Remove Theme"),
+ KStdGuiItem::del(),
+ "warningRemoveTheme"
+ );
+
+ if(remove == KMessageBox::Continue)
+ {
+ unlink(QFile::encodeName(dir));
+ KIO::DeleteJob* job = KIO::del(KURL(dir.remove("index.desktop")));
+ connect(job, SIGNAL(result(KIO::Job*)), this, SLOT(postRemoveTheme(KIO::Job*)));
+ }
+}
+
+void Theme_Config::postRemoveTheme(KIO::Job* /* delete_job */)
+{
+ loadSettings();
+}
+
+void Theme_Config::updatePreview(int id)
+{
+ QString dir;
+ dir = m_dirs[id];
+ dir.remove("/index.desktop");
+ QPixmap normal(dir+"/irc_normal.png");
+
+ previewLabel1->setPixmap(normal);
+ previewLabel2->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_away.png")));
+ previewLabel3->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_voice.png")));
+ previewLabel4->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_halfop.png")));
+ previewLabel5->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_op.png")));
+ previewLabel6->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_admin.png")));
+ previewLabel7->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_owner.png")));
+}
+
+void Theme_Config::updateButtons()
+{
+ // don't allow clicking "remove" if there is only one or even no theme installed
+ if(iconThemeIndex->count() < 2)
+ {
+ removeButton->setEnabled(false);
+ return;
+ }
+
+ // get directory of current theme
+ QString dir = m_dirs[iconThemeIndex->currentItem()];
+ QFile themeRC(dir);
+ // get name for directory
+ m_currentTheme = dir.section('/',-2,-2);
+
+ // allow delete action only for themes that have been installed by the user
+ if(!themeRC.open(IO_ReadOnly | IO_WriteOnly))
+ removeButton->setEnabled(false);
+ else
+ removeButton->setEnabled(true);
+
+ themeRC.close();
+}
+
+#include "theme_preferences.moc"
diff --git a/konversation/src/theme_preferences.h b/konversation/src/theme_preferences.h
new file mode 100644
index 0000000..2027910
--- /dev/null
+++ b/konversation/src/theme_preferences.h
@@ -0,0 +1,56 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2007 Eike Hein <hein@kde.org>
+*/
+
+#ifndef PREFSPAGETHEMES_H
+#define PREFSPAGETHEMES_H
+
+#include "theme_preferencesui.h"
+#include "konvisettingspage.h"
+
+#include <kio/job.h>
+
+
+class QStringList;
+
+class Theme_Config : public Theme_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit Theme_Config(QWidget* parent, const char* name=NULL);
+ ~Theme_Config();
+
+ virtual void restorePageToDefaults();
+ virtual void saveSettings();
+ virtual void loadSettings();
+
+ virtual bool hasChanged();
+
+ signals:
+ void modified();
+
+ protected slots:
+ void updatePreview(int id);
+ void updateButtons();
+ void installTheme();
+ void removeTheme();
+ void postRemoveTheme(KIO::Job* delete_job);
+
+ private:
+ QStringList m_dirs;
+ QString m_oldTheme;
+ QString m_currentTheme;
+ int m_defaultThemeIndex;
+};
+#endif
diff --git a/konversation/src/theme_preferencesui.ui b/konversation/src/theme_preferencesui.ui
new file mode 100644
index 0000000..ae2624d
--- /dev/null
+++ b/konversation/src/theme_preferencesui.ui
@@ -0,0 +1,222 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Theme_ConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Theme_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>541</width>
+ <height>556</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Theme_Config</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>iconThemeIndex</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>installButton</cstring>
+ </property>
+ <property name="text">
+ <string>I&amp;nstall Theme...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Remove Theme</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer172</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>131</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QFrame" row="1" column="1">
+ <property name="name">
+ <cstring>frame3_2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>previewLabel1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon for normal users</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>previewLabel2</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon for away users</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>previewLabel3</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon for users with voice</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>previewLabel4</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon for users with half-operator privileges</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="4">
+ <property name="name">
+ <cstring>previewLabel5</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon for users with operator privileges</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="5">
+ <property name="name">
+ <cstring>previewLabel6</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon for users with admin privileges</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="6">
+ <property name="name">
+ <cstring>previewLabel7</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon for users with owner privileges</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Preview:</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistbox.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/topiccombobox.cpp b/konversation/src/topiccombobox.cpp
new file mode 100644
index 0000000..2b71e9a
--- /dev/null
+++ b/konversation/src/topiccombobox.cpp
@@ -0,0 +1,50 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Don Nov 21 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "topiccombobox.h"
+
+
+TopicComboBox::TopicComboBox(QWidget* parent) :
+KComboBox(parent,"topic_combo_box")
+{
+ connect(this,SIGNAL(activated(int)),this,SLOT(topicActivated(int)));
+ connect(this,SIGNAL(returnPressed(const QString&)),this,SLOT(topicActivated(const QString&)));
+}
+
+TopicComboBox::~TopicComboBox()
+{
+}
+
+void TopicComboBox::topicActivated(const QString& newTopic)
+{
+ emit topicChanged(newTopic);
+}
+
+void TopicComboBox::topicActivated(int index)
+{
+ emit topicChanged(text(index).section(' ',1));
+}
+
+void TopicComboBox::insertStringList(const QStringList& list)
+{
+ KComboBox::insertStringList(list);
+
+ setEditText(list[0].section(' ',1));
+}
+
+void TopicComboBox::wheelEvent(QWheelEvent *ev)
+{
+ ev->ignore();
+}
+
+#include "topiccombobox.moc"
diff --git a/konversation/src/topiccombobox.h b/konversation/src/topiccombobox.h
new file mode 100644
index 0000000..5ccc193
--- /dev/null
+++ b/konversation/src/topiccombobox.h
@@ -0,0 +1,39 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ begin: Don Nov 21 2002
+ copyright: (C) 2002 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef TOPICCOMBOBOX_H
+#define TOPICCOMBOBOX_H
+
+#include <kcombobox.h>
+
+
+class TopicComboBox : public KComboBox
+{
+ Q_OBJECT
+
+ public:
+ explicit TopicComboBox(QWidget* parent);
+ ~TopicComboBox();
+ void insertStringList(const QStringList& list);
+
+ protected:
+ void wheelEvent(QWheelEvent *ev);
+
+ signals:
+ void topicChanged(const QString& newTopic);
+
+ protected slots:
+ void topicActivated(const QString& newTopic);
+ void topicActivated(int index);
+};
+#endif
diff --git a/konversation/src/topiclabel.cpp b/konversation/src/topiclabel.cpp
new file mode 100644
index 0000000..b9fdd87
--- /dev/null
+++ b/konversation/src/topiclabel.cpp
@@ -0,0 +1,385 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "topiclabel.h"
+#include "konversationapplication.h"
+#include "connectionmanager.h"
+#include "server.h"
+#include "common.h"
+#include "channel.h"
+
+#include <qsimplerichtext.h>
+#include <qtooltip.h>
+#include <qclipboard.h>
+
+#include <krun.h>
+#include <kprocess.h>
+#include <kshell.h>
+#include <kurldrag.h>
+#include <kstringhandler.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <kiconloader.h>
+#include <kbookmarkmanager.h>
+#include <kdeversion.h>
+
+
+namespace Konversation
+{
+
+ TopicLabel::TopicLabel(QWidget *parent, const char *name)
+ : KActiveLabel(parent, name)
+ {
+ setWrapPolicy(QTextEdit::AtWordOrDocumentBoundary);
+ setFocusPolicy(QWidget::ClickFocus);
+
+ m_isOnChannel = false;
+ m_copyUrlMenu = false;
+ mousePressed=false;
+
+ m_popup = new QPopupMenu(this,"topiclabel_context_menu");
+ m_popup->insertItem(SmallIconSet("editcopy"),i18n("&Copy"),Copy);
+ m_popup->insertItem(i18n("Select All"),SelectAll);
+
+ setupChannelPopupMenu();
+
+ connect(this, SIGNAL(highlighted(const QString&)), this, SLOT(highlightedSlot(const QString&)));
+ }
+
+ TopicLabel::~TopicLabel()
+ {
+ }
+
+ QSize TopicLabel::minimumSizeHint() const
+ {
+ int minHeight = fontMetrics().lineSpacing() + fontMetrics().descent();
+ return QSize(0, minHeight);
+ }
+
+ QSize TopicLabel::sizeHint() const
+ {
+ int minHeight = fontMetrics().lineSpacing() + fontMetrics().descent();
+ return QSize(0, minHeight);
+ }
+
+ void TopicLabel::setServer(Server* server)
+ {
+ m_server = server;
+ }
+
+ void TopicLabel::contentsMousePressEvent(QMouseEvent *e)
+ {
+ if (e->button()==QMouseEvent::LeftButton)
+ {
+ pressPosition=e->pos();
+ urlToDrag = anchorAt(pressPosition);
+ // HACK Replace % with \x03 in the url to keep Qt from doing stupid things
+ urlToDrag = urlToDrag.replace ('\x03', "%");
+ // Hack to counter the fact that we're given an decoded url
+ urlToDrag = KURL::fromPathOrURL(urlToDrag).url();
+ if (!urlToDrag.isNull())
+ {
+ mousePressed=true;
+ return;
+ }
+ }
+ KActiveLabel::contentsMousePressEvent(e);
+ }
+
+ void TopicLabel::contentsMouseReleaseEvent(QMouseEvent *e)
+ {
+ if (e->button()==QMouseEvent::LeftButton)
+ {
+ if (mousePressed) openLink(urlToDrag);
+ mousePressed=false;
+ }
+ KActiveLabel::contentsMouseReleaseEvent(e);
+ }
+
+ void TopicLabel::contentsMouseMoveEvent(QMouseEvent *e)
+ {
+ if (mousePressed && (pressPosition-e->pos()).manhattanLength() > QApplication::startDragDistance())
+ {
+ mousePressed=false;
+ removeSelection();
+ KURL ux = KURL::fromPathOrURL(urlToDrag);
+ //FIXME consistent IRC URL serialization
+ if (urlToDrag.startsWith("##")) ux=QString("irc://%1:%2/%3").arg(m_server->getServerName()).
+ arg(m_server->getPort()).arg(urlToDrag.mid(2));
+ KURLDrag* u=new KURLDrag(ux,viewport());
+ u->drag();
+ }
+ KActiveLabel::contentsMouseMoveEvent(e);
+ }
+
+ void TopicLabel::leaveEvent(QEvent*)
+ {
+ emit clearStatusBarTempText();
+ m_lastStatusText = QString();
+ }
+
+ void TopicLabel::openLink(const QString& link)
+ {
+ if (!link.isEmpty())
+ {
+ if (link.startsWith("irc://"))
+ {
+ KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
+ konvApp->getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, link);
+ }
+ else if (link.startsWith("#") && m_server && m_server->isConnected())
+ {
+ QString channel(link);
+ channel.replace("##","#");
+ m_server->sendJoinCommand(channel);
+ }
+ // Always use KDE default mailer.
+ else if (!Preferences::useCustomBrowser() || link.lower().startsWith("mailto:"))
+ {
+ new KRun(KURL::fromPathOrURL(link));
+ }
+ else
+ {
+ QString cmd = Preferences::webBrowserCmd();
+ cmd.replace("%u",KURL::fromPathOrURL(link).url());
+ KProcess *proc = new KProcess;
+ QStringList cmdAndArgs = KShell::splitArgs(cmd);
+ *proc << cmdAndArgs;
+ // This code will also work, but starts an extra shell process.
+ // kdDebug() << "IRCView::linkClickSlot(): cmd = " << cmd << endl;
+ // *proc << cmd;
+ // proc->setUseShell(true);
+ proc->start(KProcess::DontCare);
+ delete proc;
+ }
+ }
+ }
+
+ void TopicLabel::contentsContextMenuEvent(QContextMenuEvent* ev)
+ {
+ bool block = contextMenu(ev);
+
+ if(!block)
+ {
+ KActiveLabel::contentsContextMenuEvent(ev);
+ }
+ }
+
+ bool TopicLabel::contextMenu(QContextMenuEvent* ce)
+ {
+ if (m_isOnChannel)
+ {
+ m_channelPopup->exec(ce->globalPos());
+ m_isOnChannel = false;
+ }
+ else
+ {
+ m_popup->setItemEnabled(Copy,(hasSelectedText()));
+
+ int r = m_popup->exec(ce->globalPos());
+
+ switch(r)
+ {
+ case -1:
+ // dummy. -1 means, no entry selected. we don't want -1to go in default, so
+ // we catch it here
+ break;
+ case Copy:
+ copy();
+ break;
+ case CopyUrl:
+ {
+ QClipboard *cb = KApplication::kApplication()->clipboard();
+ cb->setText(m_urlToCopy,QClipboard::Selection);
+ cb->setText(m_urlToCopy,QClipboard::Clipboard);
+ break;
+ }
+ case SelectAll:
+ selectAll();
+ break;
+ case Bookmark:
+ {
+ KBookmarkManager* bm = KBookmarkManager::userBookmarksManager();
+ KBookmarkGroup bg = bm->addBookmarkDialog(m_urlToCopy, QString());
+ bm->save();
+ bm->emitChanged(bg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return true;
+ }
+
+ void TopicLabel::setupChannelPopupMenu()
+ {
+ m_channelPopup = new KPopupMenu(this,"channel_context_menu");
+ m_channelPopupId = m_channelPopup->insertTitle(m_currentChannel);
+ m_channelPopup->insertItem(i18n("&Join"),Konversation::Join);
+ m_channelPopup->insertItem(i18n("Get &user list"),Konversation::Names);
+ m_channelPopup->insertItem(i18n("Get &topic"),Konversation::Topic);
+
+ connect(m_channelPopup, SIGNAL(activated(int)), this, SIGNAL(popupCommand(int)));
+ }
+
+ void TopicLabel::setText(const QString& text)
+ {
+ m_fullText = text;
+ updateSqueezedText();
+ }
+
+ void TopicLabel::updateSqueezedText()
+ {
+ QToolTip::remove(this);
+
+ if (m_fullText.isEmpty())
+ {
+ KActiveLabel::setText(QString::null);
+
+ return;
+ }
+
+ QFontMetrics fm(currentFont());
+ QString text = m_fullText;
+ // text.replace("&", "&amp;"). Not needed as we do it in tagURLs
+ text.replace("<", "\x0blt;"). // tagUrls will replace \x0b with &
+ replace(">", "\x0bgt;");
+ text = tagURLs(text, "", false);
+
+ if(height() < (fm.lineSpacing() * 2))
+ {
+ text = rPixelSqueeze(text, visibleWidth() - 10);
+ setWordWrap(NoWrap);
+ QToolTip::add(this, "<qt>" + QStyleSheet::escape(m_fullText) + "</qt>");
+ }
+ else
+ {
+ setWordWrap(WidgetWidth);
+
+ if(height() < contentsHeight())
+ {
+ QToolTip::add(this, "<qt>" + QStyleSheet::escape(m_fullText) + "</qt>");
+ }
+ }
+
+ KActiveLabel::setText("<qt>" + text + "</qt>");
+ }
+
+ void TopicLabel::resizeEvent(QResizeEvent* ev)
+ {
+ KActiveLabel::resizeEvent(ev);
+ updateSqueezedText();
+ }
+
+ QString TopicLabel::rPixelSqueeze(const QString& text, uint maxPixels)
+ {
+ QFontMetrics fm(currentFont());
+ uint tw = textWidth(text, fm);
+
+ if(tw > maxPixels)
+ {
+ QString tmp = text;
+ const uint em = fm.maxWidth();
+ maxPixels -= fm.width("...");
+ int len, delta;
+
+ while((tw > maxPixels) && !tmp.isEmpty())
+ {
+ len = tmp.length();
+ delta = (tw - maxPixels) / em;
+ delta = kClamp(delta, 1, len);
+
+ tmp.remove(len - delta, delta);
+ tw = textWidth(tmp, fm);
+ }
+
+ return tmp.append("...");
+ }
+
+ return text;
+ }
+
+ uint TopicLabel::textWidth(const QString& text, const QFontMetrics& fm)
+ {
+ QSimpleRichText richText("<qt>" + text + "</qt>", currentFont());
+ richText.setWidth(fm.width(text));
+
+ return richText.widthUsed();
+ }
+
+ void TopicLabel::highlightedSlot(const QString& _link)
+ {
+ QString link = KURL::fromPathOrURL(_link).url();
+ //we just saw this a second ago. no need to reemit.
+ if (link == m_lastStatusText && !link.isEmpty())
+ return;
+
+ // remember current URL to overcome link clicking problems in QTextBrowser
+ m_highlightedURL = link;
+
+ if (link.isEmpty())
+ {
+ if (!m_lastStatusText.isEmpty())
+ {
+ emit clearStatusBarTempText();
+ m_lastStatusText = QString();
+ }
+ } else {
+ m_lastStatusText = link;
+ }
+
+ if (!link.startsWith("#"))
+ {
+ m_isOnChannel = false;
+
+ if (!link.isEmpty()) {
+ //link therefore != m_lastStatusText so emit with this new text
+ emit setStatusBarTempText(link);
+ }
+ if (link.isEmpty() && m_copyUrlMenu)
+ {
+ m_popup->removeItem(CopyUrl);
+ m_popup->removeItem(Bookmark);
+ m_copyUrlMenu = false;
+ }
+ else if (!link.isEmpty() && !m_copyUrlMenu)
+ {
+ m_popup->insertItem(SmallIcon("editcopy"), i18n("Copy URL to Clipboard"), CopyUrl, 0);
+ m_popup->insertItem(SmallIcon("bookmark"), i18n("Add to Bookmarks"), Bookmark, 1);
+ m_copyUrlMenu = true;
+ m_urlToCopy = link;
+ }
+ }
+ else if (link.startsWith("##"))
+ {
+ m_currentChannel = link.mid(1);
+
+ emit currentChannelChanged(m_currentChannel);
+
+ QString prettyId = m_currentChannel;
+
+ if (prettyId.length()>15)
+ {
+ prettyId.truncate(15);
+ prettyId.append("...");
+ }
+
+ m_channelPopup->changeTitle(m_channelPopupId,prettyId);
+ m_isOnChannel = true;
+ emit setStatusBarTempText(i18n("Join the channel %1").arg(m_currentChannel));
+ }
+ }
+}
+
+#include "topiclabel.moc"
diff --git a/konversation/src/topiclabel.h b/konversation/src/topiclabel.h
new file mode 100644
index 0000000..06c472f
--- /dev/null
+++ b/konversation/src/topiclabel.h
@@ -0,0 +1,90 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2004 Peter Simonsson <psn@linux.se>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef KONVERSATIONTOPICLABEL_H
+#define KONVERSATIONTOPICLABEL_H
+
+#include <kactivelabel.h>
+#include <klocale.h>
+
+
+class QFontMetrics;
+class Server;
+class KPopupMenu;
+
+namespace Konversation
+{
+
+ class TopicLabel : public KActiveLabel
+ {
+ Q_OBJECT
+
+ public:
+ explicit TopicLabel(QWidget *parent = 0, const char *name = 0);
+ ~TopicLabel();
+
+ QSize minimumSizeHint() const;
+ QSize sizeHint() const;
+ void setServer(Server* server);
+
+ enum PopupIDs { Copy,CopyUrl,SelectAll,Bookmark };
+
+ public slots:
+ virtual void openLink(const QString& link);
+ void setText(const QString& text);
+
+ signals:
+ void setStatusBarTempText(const QString&);
+ void clearStatusBarTempText();
+ void popupCommand(int);
+ void currentChannelChanged(const QString&);
+
+ protected:
+ void updateSqueezedText();
+ QString rPixelSqueeze(const QString& text, uint maxPixels);
+ uint textWidth(const QString& text, const QFontMetrics& fm);
+ virtual void contentsMousePressEvent(QMouseEvent *e);
+ virtual void contentsMouseReleaseEvent(QMouseEvent *e);
+ virtual void contentsMouseMoveEvent(QMouseEvent *e);
+ virtual void leaveEvent (QEvent*);
+ virtual void contentsContextMenuEvent(QContextMenuEvent* ev);
+ void resizeEvent(QResizeEvent*);
+ bool contextMenu(QContextMenuEvent* ce);
+
+ void setupChannelPopupMenu();
+
+ protected slots:
+ void highlightedSlot(const QString&);
+
+ private:
+ Server* m_server;
+
+ QPopupMenu* m_popup;
+ KPopupMenu* m_channelPopup;
+
+ QString m_fullText;
+ bool mousePressed;
+ QString urlToDrag;
+ QPoint pressPosition;
+ QString m_lastStatusText;
+ QString m_highlightedURL;
+ QString m_currentChannel;
+ bool m_isOnChannel;
+ int m_nickPopupId;
+ int m_channelPopupId;
+ bool m_copyUrlMenu;
+ QString m_urlToCopy;
+
+ };
+
+}
+#endif
diff --git a/konversation/src/trayicon.cpp b/konversation/src/trayicon.cpp
new file mode 100644
index 0000000..e4b3007
--- /dev/null
+++ b/konversation/src/trayicon.cpp
@@ -0,0 +1,99 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ This class handles the system tray icon
+ begin: Sun Nov 9 2003
+ copyright: (C) 2003 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#include "trayicon.h"
+#include "konversationapplication.h"
+#include "channel.h"
+#include "server.h"
+#include "chatwindow.h"
+#include "config/preferences.h"
+
+#include <qtimer.h>
+#include <qtooltip.h>
+
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+
+namespace Konversation
+{
+
+ TrayIcon::TrayIcon(QWidget* parent) : KSystemTray(parent)
+ {
+ m_notificationEnabled = false;
+ m_blinkTimer = new QTimer(this);
+ connect(m_blinkTimer, SIGNAL(timeout()), SLOT(blinkTimeout()));
+
+ updateAppearance();
+
+ QToolTip::add(this,i18n("Konversation - IRC Client"));
+ }
+
+ TrayIcon::~TrayIcon()
+ {
+ }
+
+ void TrayIcon::startNotification()
+ {
+ if(!m_notificationEnabled)
+ {
+ return;
+ }
+
+ if(Preferences::trayNotifyBlink())
+ {
+ if(!m_blinkTimer->isActive())
+ {
+ setPixmap(m_messagePix);
+ m_blinkOn = true;
+ m_blinkTimer->start(500);
+ }
+ }
+ else
+ {
+ setPixmap(m_messagePix);
+ m_blinkTimer->stop();
+ }
+ }
+
+ void TrayIcon::endNotification()
+ {
+ m_blinkTimer->stop();
+ setPixmap(m_nomessagePix);
+ }
+
+ void TrayIcon::blinkTimeout()
+ {
+ m_blinkOn = !m_blinkOn;
+
+ if(m_blinkOn)
+ {
+ setPixmap(m_messagePix);
+ }
+ else
+ {
+ setPixmap(m_nomessagePix);
+ }
+ }
+
+ void TrayIcon::updateAppearance()
+ {
+ m_nomessagePix = loadIcon("konversation");
+ m_messagePix = loadIcon("konv_message");
+ setPixmap(m_nomessagePix);
+ }
+}
+
+#include "trayicon.moc"
diff --git a/konversation/src/trayicon.h b/konversation/src/trayicon.h
new file mode 100644
index 0000000..a4d61b8
--- /dev/null
+++ b/konversation/src/trayicon.h
@@ -0,0 +1,60 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ This class handles the system tray icon
+ begin: Sun Nov 9 2003
+ copyright: (C) 2003 by Peter Simonsson
+ email: psn@linux.se
+*/
+
+#ifndef TRAYICON_H
+#define TRAYICON_H
+
+#include <qptrlist.h>
+#include <qpixmap.h>
+
+#include <ksystemtray.h>
+
+
+class QTimer;
+class Server;
+
+namespace Konversation
+{
+
+ class TrayIcon : public KSystemTray
+ {
+ Q_OBJECT
+
+ public:
+ explicit TrayIcon(QWidget* parent = 0);
+ ~TrayIcon();
+
+ bool notificationEnabled() { return m_notificationEnabled; }
+
+ public slots:
+ void startNotification();
+ void endNotification();
+ void setNotificationEnabled(bool notify) { m_notificationEnabled = notify; }
+ void updateAppearance();
+
+ protected slots:
+ void blinkTimeout();
+
+ private:
+ QTimer* m_blinkTimer;
+ bool m_blinkOn;
+
+ bool m_notificationEnabled;
+
+ QPixmap m_nomessagePix;
+ QPixmap m_messagePix;
+ };
+
+}
+#endif
diff --git a/konversation/src/unicode.cpp b/konversation/src/unicode.cpp
new file mode 100644
index 0000000..650775b
--- /dev/null
+++ b/konversation/src/unicode.cpp
@@ -0,0 +1,145 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ The Original Code is mozilla.org code.
+ See http://lxr.mozilla.org/mozilla/source/modules/rdf/src/utils.c#540
+
+ Copyright (C) 1998 Netscape Communications Corporation
+ Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
+*/
+
+#define kLeft1BitMask 0x80
+#define kLeft2BitsMask 0xC0
+#define kLeft3BitsMask 0xE0
+#define kLeft4BitsMask 0xF0
+#define kLeft5BitsMask 0xF8
+#define kLeft6BitsMask 0xFC
+#define kLeft7BitsMask 0xFE
+
+#define k2BytesLeadByte kLeft2BitsMask
+#define k3BytesLeadByte kLeft3BitsMask
+#define k4BytesLeadByte kLeft4BitsMask
+#define k5BytesLeadByte kLeft5BitsMask
+#define k6BytesLeadByte kLeft6BitsMask
+#define kTrialByte kLeft1BitMask
+
+#define UTF8_1Byte(c) ( 0 == ((c) & kLeft1BitMask))
+#define UTF8_2Bytes(c) ( k2BytesLeadByte == ((c) & kLeft3BitsMask))
+#define UTF8_3Bytes(c) ( k3BytesLeadByte == ((c) & kLeft4BitsMask))
+#define UTF8_4Bytes(c) ( k4BytesLeadByte == ((c) & kLeft5BitsMask))
+#define UTF8_5Bytes(c) ( k5BytesLeadByte == ((c) & kLeft6BitsMask))
+#define UTF8_6Bytes(c) ( k6BytesLeadByte == ((c) & kLeft7BitsMask))
+#define UTF8_ValidTrialByte(c) ( kTrialByte == ((c) & kLeft2BitsMask))
+
+
+bool isUtf8(const QCString& text)
+{
+ int i;
+ int j;
+ int clen = 0;
+ int len = text.length();
+
+ JapaneseCode* jc = new JapaneseCode();
+
+ JapaneseCode::Type result = jc->guess_jp(text, len);
+
+ switch(result)
+ {
+ case JapaneseCode::SJIS:
+ case JapaneseCode::JIS:
+ delete jc;
+ return false;
+ default:
+ delete jc;
+ break;
+ }
+
+ for(i=0; i < len; i += clen)
+ {
+ if(UTF8_1Byte(text[i]))
+ {
+ clen = 1;
+ }
+ else if(UTF8_2Bytes(text[i]))
+ {
+ clen = 2;
+
+ /* No enough trail bytes */
+ if( (i + clen) > len)
+ return false;
+
+ /* 0000 0000 - 0000 007F : should encode in less bytes */
+ if(0 == (text[i] & 0x1E ))
+ return false;
+ }
+ else if(UTF8_3Bytes(text[i]))
+ {
+ clen = 3;
+
+ /* No enough trail bytes */
+ if( (i + clen) > len)
+ return false;
+
+ /* a single Surrogate should not show in 3 bytes UTF8, instead, the pair should be intepreted
+ as one single UCS4 char and encoded UTF8 in 4 bytes */
+ if((QChar(0xED) == text[i] ) && (0xA0 == (text[i+1] & 0xA0 ) ))
+ return false;
+
+ /* 0000 0000 - 0000 07FF : should encode in less bytes */
+ if((0 == (text[i] & 0x0F )) && (0 == (text[i+1] & 0x20 ) ))
+ return false;
+ }
+ else if(UTF8_4Bytes(text[i]))
+ {
+ clen = 4;
+
+ /* No enough trail bytes */
+ if( (i + clen) > len)
+ return false;
+
+ /* 0000 0000 - 0000 FFFF : should encode in less bytes */
+ if((0 == (text[i] & 0x07 )) && (0 == (text[i+1] & 0x30 )) )
+ return false;
+ }
+ else if(UTF8_5Bytes(text[i]))
+ {
+ clen = 5;
+
+ /* No enough trail bytes */
+ if( (i + clen) > len)
+ return false;
+
+ /* 0000 0000 - 001F FFFF : should encode in less bytes */
+ if((0 == (text[i] & 0x03 )) && (0 == (text[i+1] & 0x38 )) )
+ return false;
+ }
+ else if(UTF8_6Bytes(text[i]))
+ {
+ clen = 6;
+
+ /* No enough trail bytes */
+ if( (i + clen) > len)
+ return false;
+
+ /* 0000 0000 - 03FF FFFF : should encode in less bytes */
+ if((0 == (text[i] & 0x01 )) && (0 == (text[i+1] & 0x3E )) )
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+
+ for(j = 1; j<clen ;++j)
+ {
+ if(! UTF8_ValidTrialByte(text[i+j])) /* Trail bytes invalid */
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/konversation/src/urlcatcher.cpp b/konversation/src/urlcatcher.cpp
new file mode 100644
index 0000000..142d5f2
--- /dev/null
+++ b/konversation/src/urlcatcher.cpp
@@ -0,0 +1,233 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ shows all URLs found by the client
+ begin: Die Mai 27 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#include "urlcatcher.h"
+#include "channel.h"
+#include "server.h"
+#include "konversationapplication.h"
+#include "viewcontainer.h"
+
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qclipboard.h>
+#include <qwhatsthis.h>
+#include <qlayout.h>
+
+#include <kapplication.h>
+#include <kactionclasses.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <klistview.h>
+#include <krun.h>
+#include <kfiledialog.h>
+#include <kprocess.h>
+#include <kdeversion.h>
+#include <kshell.h>
+#include <klistviewsearchline.h>
+
+
+UrlCatcher::UrlCatcher(QWidget* parent) : ChatWindow(parent)
+{
+ layout()->setAutoAdd(false);
+ setName(i18n("URL Catcher"));
+ setType(ChatWindow::UrlCatcher);
+
+ urlListView=new KListView(this,"url_list_view");
+ urlListView->addColumn(i18n("Nick"));
+ urlListView->addColumn(i18n("URL"));
+ urlListView->setFullWidth(true);
+ urlListView->setAllColumnsShowFocus(true);
+ QString urlListViewWT = i18n(
+ "List of Uniform Resource Locators mentioned in any of the Konversation windows "
+ "during this session.");
+ QWhatsThis::add(urlListView, urlListViewWT);
+
+ searchWidget = new KListViewSearchLineWidget(urlListView, this, "search_line");
+ searchWidget->setEnabled(false);
+
+ QHBox* buttonBox=new QHBox(this);
+ buttonBox->setSpacing(spacing());
+
+ openUrlButton=new QPushButton(i18n("&Open URL"),buttonBox,"open_url_button");
+ QString openUrlButtonWT = i18n(
+ "<p>Select a <b>URL</b> above, then click this button to launch the "
+ "application associated with the mimetype of the URL.</p>"
+ "<p>In the <b>Settings</b>, under <b>Behavior</b> | <b>General</b>, "
+ "you can specify a custom web browser for web URLs.</p>");
+ QWhatsThis::add(openUrlButton, openUrlButtonWT);
+ copyUrlButton=new QPushButton(i18n("&Copy URL"),buttonBox,"copy_url_button");
+ QString copyUrlButtonWT = i18n(
+ "Select a <b>URL</b> above, then click this button to copy the URL to the clipboard.");
+ QWhatsThis::add(copyUrlButton, copyUrlButtonWT);
+ deleteUrlButton=new QPushButton(i18n("&Delete URL"),buttonBox,"delete_url_button");
+ QString deleteUrlButtonWT = i18n(
+ "Select a <b>URL</b> above, then click this button to delete the URL from the list.");
+ QWhatsThis::add(deleteUrlButton, deleteUrlButtonWT);
+ saveListButton=new QPushButton(i18n("Sa&ve List..."),buttonBox,"save_list_button");
+ QString saveListButtonWT = i18n(
+ "Click to save the entire list to a file.");
+ QWhatsThis::add(saveListButton, saveListButtonWT);
+ clearListButton=new QPushButton(i18n("C&lear List"),buttonBox,"clear_list_button");
+ QString clearListButtonWT = i18n(
+ "Click to erase the entire list.");
+ QWhatsThis::add(clearListButton, clearListButtonWT);
+
+ connect(urlListView,SIGNAL (executed(QListViewItem*)),this,SLOT (openUrl(QListViewItem*)) );
+ connect(urlListView,SIGNAL (selectionChanged()),this,SLOT (urlSelected()) );
+
+ connect(openUrlButton,SIGNAL (clicked()),this,SLOT (openUrlClicked()) );
+ connect(copyUrlButton,SIGNAL (clicked()),this,SLOT (copyUrlClicked()) );
+ connect(deleteUrlButton,SIGNAL (clicked()),this,SLOT (deleteUrlClicked()) );
+ connect(saveListButton,SIGNAL (clicked()),this,SLOT (saveListClicked()) );
+ connect(clearListButton,SIGNAL (clicked()),this,SLOT (clearListClicked()) );
+
+ saveListButton->setEnabled(false);
+ clearListButton->setEnabled(false);
+
+ layout()->add(searchWidget);
+ layout()->add(urlListView);
+ layout()->add(buttonBox);
+
+ urlSelected();
+}
+
+
+UrlCatcher::~UrlCatcher()
+{
+}
+
+void UrlCatcher::urlSelected()
+{
+ QListViewItem* item=urlListView->selectedItem();
+ if(item)
+ {
+ openUrlButton->setEnabled(true);
+ copyUrlButton->setEnabled(true);
+ deleteUrlButton->setEnabled(true);
+ }
+ else
+ {
+ openUrlButton->setEnabled(false);
+ copyUrlButton->setEnabled(false);
+ deleteUrlButton->setEnabled(false);
+ }
+}
+
+void UrlCatcher::addUrl(const QString& who,const QString& url)
+{
+ new KListViewItem(urlListView,who,url);
+ clearListButton->setEnabled(true);
+ saveListButton->setEnabled(true);
+ searchWidget->setEnabled(true);
+}
+
+void UrlCatcher::openUrl(QListViewItem* item)
+{
+ QString url = item->text(1);
+ if (!Preferences::useCustomBrowser() || url.lower().startsWith("mailto:") )
+ {
+ new KRun(KURL(url));
+ }
+ else
+ {
+ QString cmd = Preferences::webBrowserCmd();
+ cmd.replace("%u", url);
+ KProcess *proc = new KProcess;
+ QStringList cmdAndArgs = KShell::splitArgs(cmd);
+ *proc << cmdAndArgs;
+ // This code will also work, but starts an extra shell process.
+ // kdDebug() << "UrlCatcher::openUrl(): cmd = " << cmd << endl;
+ // *proc << cmd;
+ // proc->setUseShell(true);
+ proc->start(KProcess::DontCare);
+ delete proc;
+ }
+}
+
+void UrlCatcher::openUrlClicked()
+{
+ QListViewItem* item=urlListView->selectedItem();
+ if(item) openUrl(item);
+}
+
+void UrlCatcher::copyUrlClicked()
+{
+ QListViewItem* item=urlListView->selectedItem();
+ if(item)
+ {
+ QClipboard *cb=KApplication::kApplication()->clipboard();
+ cb->setText(item->text(1),QClipboard::Selection);
+ cb->setText(item->text(1),QClipboard::Clipboard);
+ }
+}
+
+void UrlCatcher::deleteUrlClicked()
+{
+ QListViewItem* item=urlListView->selectedItem();
+ if(item)
+ {
+ emit deleteUrl(item->text(0),item->text(1));
+ delete item;
+ // select next item
+ item=urlListView->currentItem();
+ if(item) urlListView->setSelected(item,true);
+ else
+ {
+ saveListButton->setEnabled(false);
+ clearListButton->setEnabled(false);
+ searchWidget->setEnabled(false);
+ }
+ }
+}
+
+void UrlCatcher::clearListClicked()
+{
+ urlListView->clear();
+ saveListButton->setEnabled(false);
+ clearListButton->setEnabled(false);
+ urlSelected();
+ emit clearUrlList();
+}
+
+void UrlCatcher::saveListClicked()
+{
+ // Ask user for file name
+ QString fileName=KFileDialog::getSaveFileName(
+ QString(),
+ QString(),
+ this,
+ i18n("Save URL List"));
+
+ if(!fileName.isEmpty())
+ {
+ // now save the list to disk
+ QFile listFile(fileName);
+ listFile.open(IO_WriteOnly);
+ // wrap the file into a stream
+ QTextStream stream(&listFile);
+ QListViewItem* item=urlListView->itemAtIndex(0);
+ while(item)
+ {
+ stream << item->text(0) << ": " << item->text(1) << endl;
+ item=item->itemBelow();
+ } // while
+ }
+}
+
+void UrlCatcher::childAdjustFocus()
+{
+}
+
+#include "urlcatcher.moc"
diff --git a/konversation/src/urlcatcher.h b/konversation/src/urlcatcher.h
new file mode 100644
index 0000000..b713a4d
--- /dev/null
+++ b/konversation/src/urlcatcher.h
@@ -0,0 +1,66 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ shows all URLs found by the client
+ begin: Die Mai 27 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef URLCATCHER_H
+#define URLCATCHER_H
+
+#include "chatwindow.h"
+
+
+class KListView;
+class QListViewItem;
+class QPushButton;
+class ViewContainer;
+class KListViewSearchLineWidget;
+
+class UrlCatcher : public ChatWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit UrlCatcher(QWidget* parent);
+ ~UrlCatcher();
+
+ virtual bool canBeFrontView() { return true; }
+
+ signals:
+ void deleteUrl(const QString& who,const QString& url);
+ void clearUrlList();
+
+ public slots:
+ void addUrl(const QString& who,const QString& url);
+
+ protected slots:
+ void urlSelected();
+ void openUrl(QListViewItem* item);
+
+ void openUrlClicked();
+ void copyUrlClicked();
+ void deleteUrlClicked();
+ void saveListClicked();
+ void clearListClicked();
+
+ protected:
+ KListView* urlListView;
+ KListViewSearchLineWidget* searchWidget;
+
+ /** Called from ChatWindow adjustFocus */
+ virtual void childAdjustFocus();
+ QPushButton* openUrlButton;
+ QPushButton* copyUrlButton;
+ QPushButton* deleteUrlButton;
+ QPushButton* saveListButton;
+ QPushButton* clearListButton;
+};
+#endif
diff --git a/konversation/src/valuelistviewitem.cpp b/konversation/src/valuelistviewitem.cpp
new file mode 100644
index 0000000..262e43d
--- /dev/null
+++ b/konversation/src/valuelistviewitem.cpp
@@ -0,0 +1,40 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ List view item that carries an arbitrary value
+ begin: Fre Apr 25 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+
+*/
+
+#include "valuelistviewitem.h"
+
+
+ValueListViewItem::ValueListViewItem(int newValue, KListView* parent, const QString& label)
+: KListViewItem(parent,label)
+{
+ m_value=newValue;
+ enforceSortOrder();
+}
+
+ValueListViewItem::ValueListViewItem(int newValue, KListView* parent, QListViewItem* after, const QString& label)
+: KListViewItem(parent,after,label)
+{
+ m_value=newValue;
+ enforceSortOrder();
+}
+
+ValueListViewItem::~ValueListViewItem()
+{
+}
+
+int ValueListViewItem::getValue() const
+{
+ return m_value;
+}
diff --git a/konversation/src/valuelistviewitem.h b/konversation/src/valuelistviewitem.h
new file mode 100644
index 0000000..b56021d
--- /dev/null
+++ b/konversation/src/valuelistviewitem.h
@@ -0,0 +1,33 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ List view item that carries an arbitrary value
+ begin: Fre Apr 25 2003
+ copyright: (C) 2003 by Dario Abatianni
+ email: eisfuchs@tigress.com
+*/
+
+#ifndef VALUELISTVIEWITEM_H
+#define VALUELISTVIEWITEM_H
+
+#include <klistview.h>
+
+
+class ValueListViewItem : public KListViewItem
+{
+ public:
+ ValueListViewItem(int newValue, KListView* parent, const QString& label);
+ ValueListViewItem(int newValue, KListView* parent, QListViewItem* after, const QString& label);
+ ~ValueListViewItem();
+
+ int getValue() const;
+
+ protected:
+ int m_value;
+};
+#endif
diff --git a/konversation/src/version.h b/konversation/src/version.h
new file mode 100644
index 0000000..cb57f70
--- /dev/null
+++ b/konversation/src/version.h
@@ -0,0 +1,3 @@
+#ifndef KONVI_VERSION
+#define KONVI_VERSION "1.1"
+#endif
diff --git a/konversation/src/viewcontainer.cpp b/konversation/src/viewcontainer.cpp
new file mode 100644
index 0000000..9fbcb09
--- /dev/null
+++ b/konversation/src/viewcontainer.cpp
@@ -0,0 +1,2454 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#include "viewcontainer.h"
+#include "queuetuner.h"
+#include "viewtree.h"
+#include "konversationapplication.h"
+#include "notificationhandler.h"
+#include "images.h"
+#include "irccharsets.h"
+#include "ircview.h"
+#include "logfilereader.h"
+#include "konsolepanel.h"
+#include "urlcatcher.h"
+#include "dcctransferpanel.h"
+#include "dcctransfermanager.h"
+#include "dccchat.h"
+#include "statuspanel.h"
+#include "channel.h"
+#include "query.h"
+#include "rawlog.h"
+#include "channellistpanel.h"
+#include "nicksonline.h"
+#include "insertchardialog.h"
+#include "irccolorchooser.h"
+#include "joinchanneldialog.h"
+#include "servergroupsettings.h"
+
+#include <qsplitter.h>
+#include <qpopupmenu.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <ktabwidget.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kglobalsettings.h>
+
+
+ViewContainer::ViewContainer(KonversationMainWindow* window):
+ m_vbox(0), m_queueTuner(0)
+{
+ m_window = window;
+
+ images = KonversationApplication::instance()->images();
+
+ m_tabWidget = 0;
+ m_viewTree = 0;
+
+ m_urlCatcherPanel = 0;
+ m_nicksOnlinePanel = 0;
+
+ m_insertCharDialog = 0;
+
+ m_queryViewCount = 0;
+
+ m_viewTreeSplitter = new QSplitter(m_window, "view_tree_splitter");
+ m_viewTreeSplitter->setOpaqueResize(KGlobalSettings::opaqueResize());
+ m_saveSplitterSizesLock = true;
+
+ // The tree needs to be initialized before the tab widget so that it
+ // may assume a leading role in view selection management.
+ if (Preferences::tabPlacement()==Preferences::Left) setupViewTree();
+
+ setupTabWidget();
+
+ initializeSplitterSizes();
+
+ m_dccPanel = new DccTransferPanel(m_tabWidget);
+ m_dccPanel->hide();
+ m_dccPanelOpen = false;
+ connect(m_dccPanel, SIGNAL(updateTabNotification(ChatWindow*,const Konversation::TabNotifyType&)), this, SLOT(setViewNotification(ChatWindow*,const Konversation::TabNotifyType&)));
+}
+
+ViewContainer::~ViewContainer()
+{
+}
+
+void ViewContainer::showQueueTuner(bool p)
+{
+ if (p)
+ m_queueTuner->open();
+ else
+ m_queueTuner->close();
+}
+
+///Use this instead of setting m_frontServer directly so we can emit the frontServerChanging signal easily.
+void ViewContainer::setFrontServer(Server* newserver)
+{
+ if (m_frontServer == QGuardedPtr<Server>(newserver))
+ return;
+ emit frontServerChanging(newserver);
+ m_frontServer = newserver;
+}
+
+void ViewContainer::prepareShutdown()
+{
+ if (!m_tabWidget) return;
+
+ deleteDccPanel();
+ closeNicksOnlinePanel();
+
+ for (int i = 0; i < m_tabWidget->count(); ++i)
+ m_tabWidget->page(i)->blockSignals(true);
+
+ m_tabWidget->blockSignals(true);
+
+ m_tabWidget = 0;
+}
+
+void ViewContainer::initializeSplitterSizes()
+{
+ if (m_viewTree && !m_viewTree->isHidden())
+ {
+ QValueList<int> sizes = Preferences::treeSplitterSizes();
+
+ if (sizes.isEmpty())
+ sizes << 145 << (m_window->width()-145);
+ m_viewTreeSplitter->setSizes(sizes);
+
+ m_saveSplitterSizesLock = false;
+ }
+}
+
+void ViewContainer::saveSplitterSizes()
+{
+ if (!m_saveSplitterSizesLock)
+ {
+ Preferences::setTreeSplitterSizes(m_viewTreeSplitter->sizes());
+ m_saveSplitterSizesLock = false;
+ }
+}
+
+void ViewContainer::setupTabWidget()
+{
+ m_popupViewIndex = -1;
+
+ m_vbox = new QVBox(m_viewTreeSplitter, "main_window_right_side");
+ m_tabWidget = new KTabWidget(m_vbox, "main_window_tab_widget");
+ m_queueTuner = new QueueTuner(m_vbox, this);
+ m_queueTuner->hide();
+
+ m_tabWidget->setTabReorderingEnabled(true);
+ m_tabWidget->setTabCloseActivatePrevious(true);
+
+ m_vbox->hide(); //m_tabWidget->hide();
+
+ KPushButton* closeBtn = new KPushButton(m_tabWidget);
+ closeBtn->setPixmap(KGlobal::iconLoader()->loadIcon("tab_remove", KIcon::Small));
+ closeBtn->resize(22, 22);
+ closeBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ m_tabWidget->setCornerWidget(closeBtn);
+ connect(closeBtn, SIGNAL(clicked()), this, SLOT(closeCurrentView()));
+
+ connect(m_tabWidget, SIGNAL(currentChanged(QWidget*)), this, SLOT (switchView(QWidget*)));
+ connect(m_tabWidget, SIGNAL(closeRequest(QWidget*)), this, SLOT(closeView(QWidget*)));
+ connect(m_tabWidget, SIGNAL(contextMenu(QWidget*, const QPoint&)), this, SLOT(showViewContextMenu(QWidget*, const QPoint&)));
+ connect(m_tabWidget, SIGNAL(mouseMiddleClick(QWidget*)), this, SLOT(closeViewMiddleClick(QWidget*)));
+
+ updateTabWidgetAppearance();
+}
+
+void ViewContainer::setupViewTree()
+{
+ m_viewTree = new ViewTree(m_viewTreeSplitter);
+ m_viewTreeSplitter->setResizeMode(m_viewTree, QSplitter::KeepSize);
+ m_viewTree->hide();
+
+ connect(KonversationApplication::instance(), SIGNAL(appearanceChanged()), m_viewTree, SLOT(updateAppearance()));
+ connect(this, SIGNAL(viewChanged(ChatWindow*)), m_viewTree, SLOT(selectView(ChatWindow*)));
+ connect(this, SIGNAL(removeView(ChatWindow*)), m_viewTree, SLOT(removeView(ChatWindow*)));
+ connect(this, SIGNAL(contextMenuClosed()), m_viewTree, SLOT(unHighlight()));
+ connect(m_viewTree, SIGNAL(setViewTreeShown(bool)), this, SLOT(setViewTreeShown(bool)));
+ connect(m_viewTree, SIGNAL(showView(ChatWindow*)), this, SLOT(showView(ChatWindow*)));
+ connect(m_viewTree, SIGNAL(closeView(ChatWindow*)), this, SLOT(closeView(ChatWindow*)));
+ connect(m_viewTree, SIGNAL(showViewContextMenu(QWidget*, const QPoint&)), this, SLOT(showViewContextMenu(QWidget*, const QPoint&)));
+ connect(m_viewTree, SIGNAL(sizeChanged()), this, SLOT(saveSplitterSizes()));
+ connect(m_viewTree, SIGNAL(syncTabBarToTree()), this, SLOT(syncTabBarToTree()));
+
+ KAction* action;
+
+ action = actionCollection()->action("move_tab_left");
+
+ if (action)
+ {
+ action->setText(i18n("Move Tab Up"));
+ action->setIcon("1uparrow");
+ }
+
+ action = actionCollection()->action("move_tab_right");
+
+ if (action)
+ {
+ action->setText(i18n("Move Tab Down"));
+ action->setIcon("1downarrow");
+ }
+
+ // If the tab widget already exists we may need to sync the ViewTree
+ // with an already-populated tab bar.
+ if (m_tabWidget)
+ {
+ // Explicitly move to the left since we've been added after the
+ // tab widget.
+ m_viewTreeSplitter->moveToFirst(m_viewTree);
+
+ if (m_tabWidget->count() > 0)
+ {
+ // Add StatusPanels first, or curious tab bar sortings may break
+ // the tree hierarchy.
+ for (int i = 0; i < m_tabWidget->count(); ++i)
+ {
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if (view->getType() == ChatWindow::Status)
+ {
+ if (view == m_frontView)
+ m_viewTree->addView(view->getName(), view, m_tabWidget->tabIconSet(view), true);
+ else
+ m_viewTree->addView(view->getName(), view, m_tabWidget->tabIconSet(view));
+ }
+ }
+
+ for (int i = 0; i < m_tabWidget->count(); ++i)
+ {
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if (!view->getType() == ChatWindow::Status)
+ {
+ if (view == m_frontView)
+ m_viewTree->addView(view->getName(), view, m_tabWidget->tabIconSet(view), true);
+ else
+ m_viewTree->addView(view->getName(), view, m_tabWidget->tabIconSet(view));
+ }
+ }
+
+ syncTabBarToTree();
+ }
+ }
+ else
+ {
+ // Since the ViewTree was created before the tab widget, it
+ // is free to select the first view added to the tree. Other-
+ // wise the currently focused view would have been selected
+ // by the tabbar/viewtree sync loop above. This ensures a
+ // properly selected list item in the tree on app startup.
+ m_viewTree->selectFirstView(true);
+ }
+}
+
+void ViewContainer::setViewTreeShown(bool show)
+{
+ if (m_viewTree)
+ {
+ if (!show)
+ {
+ m_saveSplitterSizesLock = true;
+ m_viewTree->hide();
+ }
+ else
+ {
+ m_viewTree->show();
+ initializeSplitterSizes();
+ m_saveSplitterSizesLock = false;
+ }
+ }
+}
+
+void ViewContainer::removeViewTree()
+{
+ disconnect(KonversationApplication::instance(), SIGNAL(appearanceChanged()), m_viewTree, SLOT(updateAppearance()));
+ disconnect(this, SIGNAL(viewChanged(ChatWindow*)), m_viewTree, SLOT(selectView(ChatWindow*)));
+ disconnect(this, SIGNAL(removeView(ChatWindow*)), m_viewTree, SLOT(removeView(ChatWindow*)));
+ disconnect(this, SIGNAL(contextMenuClosed()), m_viewTree, SLOT(unHighlight()));
+ disconnect(m_viewTree, SIGNAL(setViewTreeShown(bool)), this, SLOT(setViewTreeShown(bool)));
+ disconnect(m_viewTree, SIGNAL(showView(ChatWindow*)), this, SLOT(showView(ChatWindow*)));
+ disconnect(m_viewTree, SIGNAL(closeView(ChatWindow*)), this, SLOT(closeView(ChatWindow*)));
+ disconnect(m_viewTree, SIGNAL(showViewContextMenu(QWidget*, const QPoint&)), this, SLOT(showViewContextMenu(QWidget*, const QPoint&)));
+ disconnect(m_viewTree, SIGNAL(sizeChanged()), this, SLOT(saveSplitterSizes()));
+ disconnect(m_viewTree, SIGNAL(syncTabBarToTree()), this, SLOT(syncTabBarToTree()));
+
+ KAction* action;
+
+ action = actionCollection()->action("move_tab_left");
+
+ if (action)
+ {
+ action->setText(i18n("Move Tab Left"));
+ action->setIcon("1leftarrow");
+ }
+
+ action = actionCollection()->action("move_tab_right");
+
+ if (action)
+ {
+ action->setText(i18n("Move Tab Right"));
+ action->setIcon("1rightarrow");
+ }
+
+ delete m_viewTree;
+ m_viewTree = 0;
+}
+
+void ViewContainer::syncTabBarToTree()
+{
+ QPtrList<ChatWindow> viewList = m_viewTree->getSortedViewList();
+
+ if (m_tabWidget && !viewList.isEmpty())
+ {
+ QPtrListIterator<ChatWindow> it(viewList);
+ ChatWindow* view;
+ int index = 0;
+ int oldIndex = 0;
+
+ while ((view = it.current()) != 0)
+ {
+ ++it;
+
+ oldIndex = m_tabWidget->indexOf(view);
+
+ if (!(oldIndex == index))
+ m_tabWidget->moveTab(oldIndex, index);
+
+ ++index;
+ }
+ }
+
+ updateViewActions(m_tabWidget->currentPageIndex());
+}
+
+void ViewContainer::updateAppearance()
+{
+ if (Preferences::tabPlacement()==Preferences::Left && m_viewTree == 0)
+ {
+ m_saveSplitterSizesLock = true;
+ setupViewTree();
+ }
+
+ if (!(Preferences::tabPlacement()==Preferences::Left) && m_viewTree)
+ {
+ m_saveSplitterSizesLock = true;
+ removeViewTree();
+ }
+
+ updateViews();
+ updateTabWidgetAppearance();
+
+ KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("hide_nicknamelist"));
+ action->setChecked(!Preferences::showNickList());
+
+ if(m_insertCharDialog)
+ {
+ QFont font;
+
+ if (Preferences::customTextFont())
+ font = Preferences::textFont();
+ else
+ font = KGlobalSettings::generalFont();
+
+ m_insertCharDialog->setFont(font);
+ }
+}
+
+void ViewContainer::updateTabWidgetAppearance()
+{
+ if (!m_tabWidget) return;
+
+ m_tabWidget->setTabBarHidden((Preferences::tabPlacement()==Preferences::Left));
+
+ if (Preferences::customTabFont())
+ m_tabWidget->setFont(Preferences::tabFont());
+ else
+ m_tabWidget->setFont(KGlobalSettings::generalFont());
+
+ m_tabWidget->setTabPosition((Preferences::tabPlacement()==Preferences::Top) ?
+ QTabWidget::Top : QTabWidget::Bottom);
+
+ if (Preferences::showTabBarCloseButton() && !(Preferences::tabPlacement()==Preferences::Left))
+ m_tabWidget->cornerWidget()->show();
+ else
+ m_tabWidget->cornerWidget()->hide();
+
+ m_tabWidget->setHoverCloseButton(Preferences::closeButtons());
+
+ #if KDE_IS_VERSION(3,4,0)
+ m_tabWidget->setAutomaticResizeTabs(Preferences::useMaxSizedTabs());
+ #endif
+}
+
+void ViewContainer::updateViewActions(int index)
+{
+ if (!m_tabWidget) return;
+
+ KAction* action;
+
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(index));
+
+ if (m_tabWidget->count() > 0 && view)
+ {
+ ChatWindow::WindowType viewType = view->getType();
+ Server* server = view->getServer();
+ bool insertSupported = view->isInsertSupported();
+
+ if (m_viewTree)
+ {
+ action = actionCollection()->action("move_tab_left");
+ if (action) action->setEnabled(m_viewTree->canMoveViewUp(view));
+
+ action = actionCollection()->action("move_tab_right");
+ if (action) action->setEnabled(m_viewTree->canMoveViewDown(view));
+ }
+ else if (m_tabWidget)
+ {
+ action = actionCollection()->action("move_tab_left");
+ if (action) action->setEnabled(index > 0);
+
+ action = actionCollection()->action("move_tab_right");
+ if (action) action->setEnabled(index < (m_tabWidget->count() - 1));
+ }
+
+ if (server && (viewType == ChatWindow::Status || server == m_frontServer))
+ {
+ action = actionCollection()->action("reconnect_server");
+ if (action) action->setEnabled(true);
+
+
+ action = actionCollection()->action("disconnect_server");
+ if (action) action->setEnabled(server->isConnected());
+
+
+ action = actionCollection()->action("join_channel");
+ if (action) action->setEnabled(server->isConnected());
+ }
+ else
+ {
+ action = actionCollection()->action("reconnect_server");
+ if (action) action->setEnabled(false);
+
+
+ action = actionCollection()->action("disconnect_server");
+ if (action) action->setEnabled(false);
+
+
+ action = actionCollection()->action("join_channel");
+ if (action) action->setEnabled(false);
+ }
+
+ KToggleAction* notifyAction = static_cast<KToggleAction*>(actionCollection()->action("tab_notifications"));
+ if (notifyAction)
+ {
+ notifyAction->setEnabled(viewType == ChatWindow::Channel || viewType == ChatWindow::Query ||
+ viewType == ChatWindow::Status || viewType == ChatWindow::Konsole ||
+ viewType == ChatWindow::DccTransferPanel || viewType == ChatWindow::RawLog);
+ notifyAction->setChecked(view->notificationsEnabled());
+ }
+
+ KToggleAction* autoJoinAction = static_cast<KToggleAction*>(actionCollection()->action("tab_autojoin"));
+ Channel* channel = static_cast<Channel*>(view);
+ if (autoJoinAction && viewType == ChatWindow::Channel && channel->getServer()->getServerGroup())
+ {
+ autoJoinAction->setEnabled(true);
+ autoJoinAction->setChecked(channel->autoJoin());
+ }
+ else if (!(viewType != ChatWindow::Channel && index != m_tabWidget->currentPageIndex()))
+ {
+ autoJoinAction->setEnabled(false);
+ autoJoinAction->setChecked(false);
+ }
+
+ action = actionCollection()->action("rejoin_channel");
+ if (action) action->setEnabled(viewType == ChatWindow::Channel && channel->rejoinable());
+
+ action = actionCollection()->action("close_queries");
+ if (action) action->setEnabled(m_queryViewCount > 0);
+
+ action = actionCollection()->action("clear_tabs");
+ if (action) action->setEnabled(true);
+
+ action = actionCollection()->action("toggle_away");
+ if (action) action->setEnabled(true);
+
+ action = actionCollection()->action("next_tab");
+ if (action) action->setEnabled(true);
+
+ action = actionCollection()->action("previous_tab");
+ if (action) action->setEnabled(true);
+
+ action = actionCollection()->action("next_active_tab");
+ if (action) action->setEnabled(true);
+
+ action = actionCollection()->action("close_tab");
+ if (action) action->setEnabled(true);
+
+ if (index == m_tabWidget->currentPageIndex())
+ {
+ // The following only need to be updated when this run is related
+ // to the active tab, e.g. when it was just changed.
+
+ action = actionCollection()->action("insert_marker_line");
+ if (action) action->setEnabled(insertSupported);
+
+ action = actionCollection()->action("insert_character");
+ if (action) action->setEnabled(insertSupported);
+
+ action = actionCollection()->action("irc_colors");
+ if (action) action->setEnabled(insertSupported);
+
+ action = actionCollection()->action("clear_lines");
+ if (action) action->setEnabled(insertSupported && view->getTextView()->hasLines());
+
+ action = actionCollection()->action("clear_window");
+ if (action) action->setEnabled(insertSupported);
+
+ action = actionCollection()->action("edit_find");
+ if (action)
+ {
+ action->setText(i18n("Find Text..."));
+ action->setEnabled(view->searchView());
+ action->setToolTip(i18n("Search for text in the current tab"));
+ }
+
+ action = actionCollection()->action("edit_find_next");
+ if (action) action->setEnabled(view->searchView());
+
+ action = actionCollection()->action("edit_find_last");
+ if (action) action->setEnabled(view->searchView());
+
+ KToggleAction* channelListAction = static_cast<KToggleAction*>(actionCollection()->action("open_channel_list"));
+ if (channelListAction)
+ {
+ if (m_frontServer)
+ {
+ QString name = m_frontServer->getDisplayName();
+ name = name.replace('&', "&&");
+ channelListAction->setEnabled(true);
+ channelListAction->setChecked(m_frontServer->getChannelListPanel());
+ channelListAction->setText(i18n("Channel &List for %1").arg(name));
+ }
+ else
+ {
+ channelListAction->setEnabled(false);
+ channelListAction->setChecked(false);
+ channelListAction->setText(i18n("Channel &List"));
+ }
+ }
+
+ action = actionCollection()->action("open_logfile");
+ if (action)
+ {
+ action->setEnabled(!view->logFileName().isEmpty());
+ if (view->logFileName().isEmpty())
+ action->setText(i18n("&Open Logfile"));
+ else
+ {
+ QString name = view->getName();
+ name = name.replace('&', "&&");
+ action->setText(i18n("&Open Logfile for %1").arg(name));
+ }
+ }
+
+ action = actionCollection()->action("hide_nicknamelist");
+ if (action) action->setEnabled(view->getType() == ChatWindow::Channel);
+
+ action = actionCollection()->action("channel_settings");
+ if (action && view->getType() == ChatWindow::Channel)
+ {
+ action->setEnabled(true);
+ action->setText(i18n("&Channel Settings for %1...").arg(view->getName()));
+ }
+ else if (action)
+ {
+ action->setEnabled(false);
+ action->setText(i18n("&Channel Settings..."));
+ }
+ }
+ }
+ else
+ {
+ action = actionCollection()->action("move_tab_left");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("move_tab_right");
+ if(action) action->setEnabled(false);
+
+ action = actionCollection()->action("next_tab");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("previous_tab");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("close_tab");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("next_active_tab");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("tab_notifications");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("tab_autojoin");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("rejoin_channel");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("insert_marker_line");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("insert_character");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("irc_colors");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("clear_lines");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("clear_window");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("clear_tabs");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("edit_find");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("edit_find_next");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("edit_find_last");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("open_channel_list");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("open_logfile");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("toggle_away");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("join_channel");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("disconnect_server");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("reconnect_server");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("hide_nicknamelist");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("channel_settings");
+ if (action) action->setEnabled(false);
+
+ action = actionCollection()->action("close_queries");
+ if (action) action->setEnabled(false);
+ }
+}
+
+void ViewContainer::updateFrontView()
+{
+ if (!m_tabWidget) return;
+
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->currentPage());
+
+ if (!view) return;
+
+ // Make sure that only views with info output get to be the m_frontView
+ if (m_frontView)
+ {
+ disconnect(m_frontView, SIGNAL(updateInfo(const QString &)), this, SIGNAL(setStatusBarInfoLabel(const QString &)));
+ }
+
+ if (view->canBeFrontView())
+ {
+ m_frontView = view;
+
+ connect(view, SIGNAL(updateInfo(const QString &)), this, SIGNAL(setStatusBarInfoLabel(const QString &)));
+ view->emitUpdateInfo();
+ }
+ else
+ {
+ QString viewName = Konversation::removeIrcMarkup(view->getName());
+
+ if(viewName != "ChatWindowObject")
+ emit setStatusBarInfoLabel(viewName);
+ else
+ emit clearStatusBarInfoLabel();
+ }
+
+ switch (view->getType())
+ {
+ case ChatWindow::Channel:
+ case ChatWindow::Query:
+ case ChatWindow::Status:
+ case ChatWindow::ChannelList:
+ case ChatWindow::RawLog:
+ emit setStatusBarLagLabelShown(true);
+ break;
+
+ default:
+ emit setStatusBarLagLabelShown(false);
+ break;
+ }
+
+ // Make sure that only text views get to be the m_searchView
+ if (view->searchView()) m_searchView = view;
+
+ updateViewActions(m_tabWidget->currentPageIndex());
+}
+
+void ViewContainer::updateViews(const Konversation::ServerGroupSettings* serverGroup)
+{
+ if (!m_tabWidget) return;
+
+ for (int i = 0; i < m_tabWidget->count(); ++i)
+ {
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if (serverGroup)
+ {
+ if (view->getType() == ChatWindow::Status && view->getServer()->getServerGroup() == serverGroup)
+ {
+ QString label = view->getServer()->getDisplayName();
+
+ if (!label.isEmpty() && m_tabWidget->tabLabel(view) != label)
+ {
+ if (m_tabWidget) m_tabWidget->setTabLabel(view, label);
+ if (m_viewTree) m_viewTree->setViewName(view, label);
+
+ if (view == m_frontView)
+ {
+ emit setStatusBarInfoLabel(label);
+ emit setWindowCaption(label);
+ }
+
+ static_cast<StatusPanel*>(view)->updateName();
+ }
+ }
+
+ if (i == m_tabWidget->currentPageIndex())
+ updateViewActions(m_tabWidget->currentPageIndex());
+ }
+
+ if (m_viewTree)
+ {
+ if (!Preferences::tabNotificationsLeds() && !Preferences::closeButtons())
+ m_viewTree->setViewIcon(view, QIconSet());
+
+ if (Preferences::closeButtons() && !Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getCloseIcon());
+
+
+ if (!Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, m_window->colorGroup().foreground());
+ }
+ else if (m_tabWidget)
+ {
+ if (!Preferences::tabNotificationsLeds() && !Preferences::closeButtons())
+ m_tabWidget->setTabIconSet(view, QIconSet());
+
+ if (Preferences::closeButtons() && !Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getCloseIcon());
+
+ if (!Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, m_window->colorGroup().foreground());
+ }
+
+ if (Preferences::tabNotificationsLeds() || Preferences::tabNotificationsText())
+ {
+ if (view->currentTabNotification()==Konversation::tnfNone)
+ unsetViewNotification(view);
+ else if (view->currentTabNotification()==Konversation::tnfNormal && !Preferences::tabNotificationsMsgs())
+ unsetViewNotification(view);
+ else if (view->currentTabNotification()==Konversation::tnfPrivate && !Preferences::tabNotificationsPrivate())
+ unsetViewNotification(view);
+ else if (view->currentTabNotification()==Konversation::tnfSystem && !Preferences::tabNotificationsSystem())
+ unsetViewNotification(view);
+ else if (view->currentTabNotification()==Konversation::tnfControl && !Preferences::tabNotificationsEvents())
+ unsetViewNotification(view);
+ else if (view->currentTabNotification()==Konversation::tnfNick && !Preferences::tabNotificationsNick())
+ unsetViewNotification(view);
+ else if (view->currentTabNotification()==Konversation::tnfHighlight && !Preferences::tabNotificationsHighlights())
+ unsetViewNotification(view);
+ else if (view==m_tabWidget->currentPage())
+ unsetViewNotification(view);
+ else
+ setViewNotification(view, view->currentTabNotification());
+ }
+ }
+}
+
+void ViewContainer::updateViewIcons()
+{
+ if (!m_tabWidget) return;
+
+ for (int i = 0; i < m_tabWidget->count(); ++i)
+ {
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if (Preferences::closeButtons() && !Preferences::tabNotificationsLeds())
+ {
+ if (m_viewTree)
+ m_viewTree->setViewIcon(view, images->getCloseIcon());
+ else if (m_tabWidget)
+ m_tabWidget->setTabIconSet(view, images->getCloseIcon());
+ }
+ }
+}
+
+void ViewContainer::setViewNotification(ChatWindow* view, const Konversation::TabNotifyType& type)
+{
+ if (!view || view == m_tabWidget->currentPage())
+ return;
+
+ if (type < Konversation::tnfControl && (m_activeViewOrderList.find(view) == m_activeViewOrderList.end()))
+ m_activeViewOrderList.append(view);
+
+ if (!Preferences::tabNotificationsLeds() && !Preferences::tabNotificationsText())
+ return;
+
+ if (m_viewTree)
+ {
+ switch (type)
+ {
+ case Konversation::tnfNormal:
+ if (Preferences::tabNotificationsMsgs())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getMsgsLed(true));
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, Preferences::tabNotificationsMsgsColor());
+ }
+ break;
+
+ case Konversation::tnfPrivate:
+ if (Preferences::tabNotificationsPrivate())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getPrivateLed(true));
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, Preferences::tabNotificationsPrivateColor());
+ }
+ break;
+
+ case Konversation::tnfSystem:
+ if (Preferences::tabNotificationsSystem())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getSystemLed(true));
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, Preferences::tabNotificationsSystemColor());
+ }
+ break;
+
+ case Konversation::tnfControl:
+ if (Preferences::tabNotificationsEvents())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getEventsLed());
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, Preferences::tabNotificationsEventsColor());
+ }
+ break;
+
+ case Konversation::tnfNick:
+ if (Preferences::tabNotificationsNick())
+ {
+ if (Preferences::tabNotificationsOverride() && Preferences::highlightNick())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getLed(Preferences::highlightNickColor(),true));
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, Preferences::highlightNickColor());
+ }
+ else
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getNickLed());
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, Preferences::tabNotificationsNickColor());
+ }
+ }
+ else
+ {
+ setViewNotification(view,Konversation::tnfNormal);
+ }
+ break;
+
+ case Konversation::tnfHighlight:
+ if (Preferences::tabNotificationsHighlights())
+ {
+ if (Preferences::tabNotificationsOverride() && view->highlightColor().isValid())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getLed(view->highlightColor(),true));
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, view->highlightColor());
+ }
+ else
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_viewTree->setViewIcon(view, images->getHighlightsLed());
+ if (Preferences::tabNotificationsText())
+ m_viewTree->setViewColor(view, Preferences::tabNotificationsHighlightsColor());
+ }
+ }
+ else
+ {
+ setViewNotification(view,Konversation::tnfNormal);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (m_tabWidget)
+ {
+ switch (type)
+ {
+ case Konversation::tnfNormal:
+ if (Preferences::tabNotificationsMsgs())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getMsgsLed(true));
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, Preferences::tabNotificationsMsgsColor());
+ }
+ break;
+
+ case Konversation::tnfPrivate:
+ if (Preferences::tabNotificationsPrivate())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getPrivateLed(true));
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, Preferences::tabNotificationsPrivateColor());
+ }
+ break;
+
+ case Konversation::tnfSystem:
+ if (Preferences::tabNotificationsSystem())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getSystemLed(true));
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, Preferences::tabNotificationsSystemColor());
+ }
+ break;
+
+ case Konversation::tnfControl:
+ if (Preferences::tabNotificationsEvents())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getEventsLed());
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, Preferences::tabNotificationsEventsColor());
+ }
+ break;
+
+ case Konversation::tnfNick:
+ if (Preferences::tabNotificationsNick())
+ {
+ if (Preferences::tabNotificationsOverride() && Preferences::highlightNick())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getLed(Preferences::highlightNickColor(),true));
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, Preferences::highlightNickColor());
+ }
+ else
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getNickLed());
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, Preferences::tabNotificationsNickColor());
+ }
+ }
+ else
+ {
+ setViewNotification(view,Konversation::tnfNormal);
+ }
+ break;
+
+ case Konversation::tnfHighlight:
+ if (Preferences::tabNotificationsHighlights())
+ {
+ if (Preferences::tabNotificationsOverride() && view->highlightColor().isValid())
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getLed(view->highlightColor(),true));
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, view->highlightColor());
+ }
+ else
+ {
+ if (Preferences::tabNotificationsLeds())
+ m_tabWidget->setTabIconSet(view, images->getHighlightsLed());
+ if (Preferences::tabNotificationsText())
+ m_tabWidget->setTabColor(view, Preferences::tabNotificationsHighlightsColor());
+ }
+ }
+ else
+ {
+ setViewNotification(view,Konversation::tnfNormal);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void ViewContainer::unsetViewNotification(ChatWindow* view)
+{
+ if (m_viewTree)
+ {
+ if (Preferences::tabNotificationsLeds())
+ {
+ switch (view->getType())
+ {
+ case ChatWindow::Channel:
+ case ChatWindow::DccChat:
+ m_viewTree->setViewIcon(view, images->getMsgsLed(false));
+ break;
+
+ case ChatWindow::Query:
+ m_viewTree->setViewIcon(view, images->getPrivateLed(false));
+ break;
+
+ case ChatWindow::Status:
+ m_viewTree->setViewIcon(view, images->getServerLed(false));
+ break;
+
+ default:
+ m_viewTree->setViewIcon(view, images->getSystemLed(false));
+ break;
+ }
+ }
+
+ QColor textColor = (Preferences::inputFieldsBackgroundColor()
+ ? Preferences::color(Preferences::ChannelMessage) : m_window->colorGroup().foreground());
+
+ if (view->getType() == ChatWindow::Channel)
+ {
+ Channel *channel = static_cast<Channel*>(view);
+
+ if (!channel->joined())
+ textColor = KonversationApplication::instance()->palette(m_viewTree).disabled().text();
+ }
+ else if (view->getType() == ChatWindow::Query)
+ {
+ if (!view->getServer()->isConnected())
+ textColor = KonversationApplication::instance()->palette(m_tabWidget).disabled().text();
+ }
+
+ m_viewTree->setViewColor(view, textColor);
+ }
+ else if (m_tabWidget)
+ {
+ if (Preferences::tabNotificationsLeds())
+ {
+ switch (view->getType())
+ {
+ case ChatWindow::Channel:
+ case ChatWindow::DccChat:
+ m_tabWidget->setTabIconSet(view, images->getMsgsLed(false));
+ break;
+
+ case ChatWindow::Query:
+ m_tabWidget->setTabIconSet(view, images->getPrivateLed(false));
+ break;
+
+ case ChatWindow::Status:
+ m_tabWidget->setTabIconSet(view, images->getServerLed(false));
+ break;
+
+ default:
+ m_tabWidget->setTabIconSet(view, images->getSystemLed(false));
+ break;
+ }
+ }
+
+ QColor textColor = m_window->colorGroup().foreground();
+
+ if (view->getType() == ChatWindow::Channel)
+ {
+ Channel *channel = static_cast<Channel*>(view);
+
+ if (!channel->joined())
+ textColor = KonversationApplication::instance()->palette(m_tabWidget).disabled().text();
+ }
+ else if (view->getType() == ChatWindow::Query)
+ {
+ if (!view->getServer()->isConnected())
+ textColor = KonversationApplication::instance()->palette(m_tabWidget).disabled().text();
+ }
+
+ m_tabWidget->setTabColor(view, textColor);
+ }
+
+ QValueList<ChatWindow*>::iterator it = m_activeViewOrderList.find(view);
+
+ if (it != m_activeViewOrderList.end())
+ m_activeViewOrderList.remove(it);
+}
+
+void ViewContainer::toggleViewNotifications()
+{
+ ChatWindow* view = 0;
+
+ if (m_popupViewIndex == -1)
+ view = static_cast<ChatWindow*>(m_tabWidget->currentPage());
+ else
+ view = static_cast<ChatWindow*>(m_tabWidget->page(m_popupViewIndex));
+
+ if (view)
+ {
+ if (!view->notificationsEnabled())
+ {
+ view->setNotificationsEnabled(true);
+ updateViews();
+ KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("tab_notifications"));
+ if (action) action->setChecked(view->notificationsEnabled());
+ }
+ else
+ {
+ view->setNotificationsEnabled(false);
+ unsetViewNotification(view);
+ KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("tab_notifications"));
+ if (action) action->setChecked(view->notificationsEnabled());
+ }
+ }
+
+ m_popupViewIndex = -1;
+}
+
+void ViewContainer::toggleAutoJoin()
+{
+ Channel* channel = 0;
+
+ if (m_popupViewIndex == -1)
+ channel = static_cast<Channel*>(m_tabWidget->currentPage());
+ else
+ channel = static_cast<Channel*>(m_tabWidget->page(m_popupViewIndex));
+
+ if (channel && channel->getType() == ChatWindow::Channel)
+ {
+ bool autoJoin = channel->autoJoin();
+
+ channel->setAutoJoin(!autoJoin);
+
+ emit autoJoinToggled(channel->getServer()->getServerGroup());
+ }
+}
+
+void ViewContainer::addView(ChatWindow* view, const QString& label, bool weinitiated)
+{
+ ChatWindow *tmp_ChatWindow;
+ int placement = -1;
+ ChatWindow::WindowType wtype;
+ QIconSet iconSet;
+
+ connect(KonversationApplication::instance(), SIGNAL(appearanceChanged()), view, SLOT(updateAppearance()));
+ connect(view, SIGNAL(setStatusBarTempText(const QString&)), this, SIGNAL(setStatusBarTempText(const QString&)));
+ connect(view, SIGNAL(clearStatusBarTempText()), this, SIGNAL(clearStatusBarTempText()));
+ connect(view, SIGNAL(closing(ChatWindow*)), this, SIGNAL(removeView(ChatWindow*)));
+ connect(view, SIGNAL(closing(ChatWindow*)), this, SLOT(cleanupAfterClose(ChatWindow*)));
+
+ // Please be careful about changing any of the grouping behavior in here,
+ // because it needs to match up with the sorting behavior of the tree list,
+ // otherwise they may become out of sync, wreaking havoc with the move
+ // actions. Yes, this would do well with a more reliable approach in the
+ // future. Then again, while this is ugly, it's also very fast.
+ switch (view->getType())
+ {
+ case ChatWindow::Channel:
+ if (Preferences::tabNotificationsLeds())
+ iconSet = images->getMsgsLed(false);
+ else if (Preferences::closeButtons())
+ iconSet = images->getCloseIcon();
+
+ for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->page(sindex));
+
+ if (tmp_ChatWindow->getType() == ChatWindow::Status && tmp_ChatWindow->getServer() == view->getServer())
+ {
+ for (int index = sindex + 1; index < m_tabWidget->count(); index++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->page(index));
+ wtype = tmp_ChatWindow->getType();
+
+ if (wtype != ChatWindow::Channel && wtype != ChatWindow::RawLog)
+ {
+ placement = index;
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ break;
+
+ case ChatWindow::RawLog:
+ if (Preferences::tabNotificationsLeds())
+ iconSet = images->getSystemLed(false);
+ else if (Preferences::closeButtons())
+ iconSet = images->getCloseIcon();
+
+ for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->page(sindex));
+
+ if (tmp_ChatWindow->getType() == ChatWindow::Status && tmp_ChatWindow->getServer() == view->getServer())
+ {
+ placement = sindex + 1;
+ break;
+ }
+ }
+
+ break;
+
+ case ChatWindow::Query:
+ if (Preferences::tabNotificationsLeds())
+ iconSet = images->getPrivateLed(false);
+ else if (Preferences::closeButtons())
+ iconSet = images->getCloseIcon();
+
+ for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->page(sindex));
+
+ if (tmp_ChatWindow->getType() == ChatWindow::Status && tmp_ChatWindow->getServer() == view->getServer())
+ {
+ for (int index = sindex + 1; index < m_tabWidget->count(); index++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->page(index));
+ wtype = tmp_ChatWindow->getType();
+
+ if (wtype != ChatWindow::Channel && wtype != ChatWindow::RawLog && wtype != ChatWindow::Query)
+ {
+ placement = index;
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ break;
+
+ case ChatWindow::DccChat:
+ if (Preferences::tabNotificationsLeds())
+ iconSet = images->getMsgsLed(false);
+ else if (Preferences::closeButtons())
+ iconSet = images->getCloseIcon();
+
+ for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow*>(m_tabWidget->page(sindex));
+ wtype = tmp_ChatWindow->getType();
+
+ if (wtype != ChatWindow::Status && wtype != ChatWindow::Channel
+ && wtype != ChatWindow::RawLog && wtype != ChatWindow::Query
+ && wtype != ChatWindow::DccChat && wtype != ChatWindow::ChannelList)
+ {
+ placement = sindex;
+ break;
+ }
+ }
+ break;
+
+ case ChatWindow::Status:
+ if (Preferences::tabNotificationsLeds())
+ iconSet = images->getServerLed(false);
+ else if (Preferences::closeButtons())
+ iconSet = images->getCloseIcon();
+
+ if (m_viewTree)
+ {
+ for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->page(sindex));
+
+ if (tmp_ChatWindow->getType() != ChatWindow::Channel
+ && tmp_ChatWindow->getType() != ChatWindow::Status
+ && tmp_ChatWindow->getType() != ChatWindow::RawLog
+ && tmp_ChatWindow->getType() != ChatWindow::Query
+ && tmp_ChatWindow->getType() != ChatWindow::DccChat)
+ {
+ placement = sindex;
+ break;
+ }
+ }
+ }
+ break;
+
+ case ChatWindow::ChannelList:
+ if (Preferences::tabNotificationsLeds())
+ iconSet = images->getSystemLed(false);
+ else if (Preferences::closeButtons())
+ iconSet = images->getCloseIcon();
+
+ for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
+ {
+ tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->page(sindex));
+
+ if (tmp_ChatWindow->getServer() == view->getServer())
+ placement = sindex + 1;
+ }
+
+ break;
+
+ default:
+ if (Preferences::tabNotificationsLeds())
+ iconSet = images->getSystemLed(false);
+ else if (Preferences::closeButtons())
+ iconSet = images->getCloseIcon();
+ break;
+ }
+
+ m_tabWidget->insertTab(view, iconSet, label, placement);
+ m_vbox->show();//m_tabWidget->show();
+
+ if (m_viewTree)
+ {
+ if (placement != -1 && m_tabWidget->page(placement-1))
+ {
+ ChatWindow* after = static_cast<ChatWindow*>(m_tabWidget->page(placement-1));
+ m_viewTree->addView(label, view, iconSet, false, after);
+ }
+ else
+ m_viewTree->addView(label, view, iconSet);
+ }
+
+ // Check, if user was typing in old input line
+ bool doBringToFront=false;
+
+ if (Preferences::focusNewQueries() && view->getType()==ChatWindow::Query && !weinitiated)
+ doBringToFront = true;
+
+ if (Preferences::bringToFront() && view->getType()!=ChatWindow::RawLog)
+ doBringToFront = true;
+
+ // make sure that bring to front only works when the user wasn't typing something
+ if (m_frontView && view->getType() != ChatWindow::UrlCatcher && view->getType() != ChatWindow::Konsole)
+ {
+ if (!m_frontView->getTextInLine().isEmpty())
+ doBringToFront = false;
+ }
+
+ if (doBringToFront) showView(view);
+
+ updateViewActions(m_tabWidget->currentPageIndex());
+}
+
+void ViewContainer::switchView(QWidget* newView)
+{
+ ChatWindow* view = static_cast<ChatWindow*>(newView);
+
+ emit viewChanged(view);
+
+ if (m_frontView)
+ {
+ m_frontView->resetTabNotification();
+
+ disconnect(m_frontView, SIGNAL(updateInfo(const QString &)), this, SIGNAL(setStatusBarInfoLabel(const QString &)));
+
+ if (Preferences::automaticRememberLine() && m_frontView->isInsertSupported())
+ m_frontView->getTextView()->insertRememberLine();
+ }
+
+ m_frontView = 0;
+ m_searchView = 0;
+
+ setFrontServer(view->getServer());
+
+ // display this server's lag time
+ if (m_frontServer)
+ {
+ updateStatusBarSSLLabel(m_frontServer);
+ updateStatusBarLagLabel(m_frontServer, m_frontServer->getLag());
+ }
+
+ emit clearStatusBarTempText();
+
+ updateFrontView();
+
+ unsetViewNotification(view);
+
+ view->resetTabNotification();
+
+ if (!m_viewTree || !m_viewTree->hasFocus()) view->adjustFocus();
+
+ if (view->isInsertSupported()) view->getTextView()->cancelRememberLine();
+
+ updateViewEncoding(view);
+
+ QString tabName = Konversation::removeIrcMarkup(view->getName());
+
+ if (tabName != "ChatWindowObject")
+ emit setWindowCaption(tabName);
+ else
+ emit setWindowCaption(QString());
+}
+
+void ViewContainer::showView(ChatWindow* view)
+{
+ // Don't bring Tab to front if TabWidget is hidden. Otherwise QT gets confused
+ // and shows the Tab as active but will display the wrong pane
+ if (m_tabWidget && m_tabWidget->isVisible())
+ m_tabWidget->showPage(view);
+}
+
+void ViewContainer::goToView(int page)
+{
+ if (page == m_tabWidget->currentPageIndex())
+ return;
+
+ if (page > m_tabWidget->count())
+ return;
+
+ if (page >= m_tabWidget->count())
+ page = 0;
+ else if (page < 0)
+ page = m_tabWidget->count() - 1;
+
+ if (page >= 0)
+ m_tabWidget->setCurrentPage(page);
+
+
+ m_popupViewIndex = -1;
+}
+
+void ViewContainer::showNextView()
+{
+ goToView(m_tabWidget->currentPageIndex()+1);
+}
+
+void ViewContainer::showPreviousView()
+{
+ goToView(m_tabWidget->currentPageIndex()-1);
+}
+
+void ViewContainer::moveViewLeft()
+{
+ int index;
+
+ if (m_popupViewIndex == -1)
+ index = m_tabWidget->currentPageIndex();
+ else
+ index = m_popupViewIndex;
+
+ if (index)
+ {
+ if (m_viewTree)
+ {
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(index));
+ m_viewTree->moveViewUp(view);
+ syncTabBarToTree();
+ }
+ else if (m_tabWidget)
+ {
+ m_tabWidget->moveTab(index, index - 1);
+ updateViewActions(index - 1);
+ }
+ }
+
+ m_popupViewIndex = -1;
+}
+
+void ViewContainer::moveViewRight()
+{
+ int index;
+
+ if (m_popupViewIndex == -1)
+ index = m_tabWidget->currentPageIndex();
+ else
+ index = m_popupViewIndex;
+
+ if (index < (m_tabWidget->count() - 1))
+ {
+ if (m_viewTree)
+ {
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(index));
+ m_viewTree->moveViewDown(view);
+ syncTabBarToTree();
+ }
+ else if (m_tabWidget)
+ {
+ m_tabWidget->moveTab(index, index + 1);
+ updateViewActions(index + 1);
+ }
+ }
+
+ m_popupViewIndex = -1;
+}
+
+void ViewContainer::closeView(QWidget* view)
+{
+ ChatWindow* viewToClose = static_cast<ChatWindow*>(view);
+
+ closeView(viewToClose);
+}
+
+void ViewContainer::closeView(ChatWindow* view)
+{
+ if (view)
+ {
+ ChatWindow::WindowType viewType = view->getType();
+
+ bool closeConfirmed = true;
+
+ switch (viewType)
+ {
+ case ChatWindow::DccTransferPanel:
+ closeDccPanel();
+ break;
+ case ChatWindow::UrlCatcher:
+ closeUrlCatcher();
+ break;
+ case ChatWindow::NicksOnline:
+ closeNicksOnlinePanel();
+ break;
+ default:
+ closeConfirmed = view->closeYourself();
+ break;
+ }
+ }
+}
+
+void ViewContainer::cleanupAfterClose(ChatWindow* view)
+{
+ if (view == m_frontView) m_frontView = 0;
+
+ if (m_tabWidget)
+ {
+ m_tabWidget->removePage(view);
+ emit removeView(view);
+
+ if (m_tabWidget->count() <= 0)
+ {
+ m_saveSplitterSizesLock = true;
+ m_vbox->hide();
+ emit resetStatusBar();
+ emit setWindowCaption(QString::null);
+ }
+ }
+
+ // Remove the view from the active view list if it's still on it
+ QValueList<ChatWindow*>::iterator it = m_activeViewOrderList.find(view);
+
+ if (it != m_activeViewOrderList.end())
+ m_activeViewOrderList.remove(it);
+
+ if (view->getType() == ChatWindow::Query)
+ --m_queryViewCount;
+
+ if (m_queryViewCount == 0 && actionCollection())
+ {
+ KAction* action = actionCollection()->action("close_queries");
+ if (action) action->setEnabled(false);
+ }
+}
+
+void ViewContainer::closeViewMiddleClick(QWidget* view)
+{
+ if (Preferences::middleClickClose())
+ closeView(view);
+}
+
+void ViewContainer::closeCurrentView()
+{
+ if (m_popupViewIndex == -1)
+ closeView(m_tabWidget->currentPage());
+ else
+ closeView(m_tabWidget->page(m_popupViewIndex));
+
+ m_popupViewIndex = -1;
+}
+
+void ViewContainer::changeViewCharset(int index)
+{
+ ChatWindow* chatWin;
+
+ if (m_popupViewIndex == -1)
+ chatWin = static_cast<ChatWindow*>(m_tabWidget->currentPage());
+ else
+ chatWin = static_cast<ChatWindow*>(m_tabWidget->page(m_popupViewIndex));
+
+ if (chatWin)
+ {
+ if (index == 0)
+ chatWin->setChannelEncoding(QString());
+ else
+ chatWin->setChannelEncoding(Konversation::IRCCharsets::self()->availableEncodingShortNames()[index - 1]);
+ }
+
+ m_popupViewIndex = -1;
+}
+
+void ViewContainer::updateViewEncoding(ChatWindow* view)
+{
+ if (view)
+ {
+ ChatWindow::WindowType viewType = view->getType();
+ KSelectAction* codecAction = static_cast<KSelectAction*>(actionCollection()->action("tab_encoding"));
+
+ if (codecAction)
+ {
+ if(viewType == ChatWindow::Channel || viewType == ChatWindow::Query || viewType == ChatWindow::Status)
+ {
+ codecAction->setEnabled(view->isChannelEncodingSupported());
+ QString encoding = view->getChannelEncoding();
+
+ if(m_frontServer)
+ {
+ codecAction->changeItem(0, i18n("Default encoding", "Default ( %1 )").arg(m_frontServer->getIdentity()->getCodecName()));
+ }
+
+ if(encoding.isEmpty())
+ {
+ codecAction->setCurrentItem(0);
+ }
+ else
+ {
+ codecAction->setCurrentItem(Konversation::IRCCharsets::self()->shortNameToIndex(encoding) + 1);
+ }
+ }
+ else
+ {
+ codecAction->setEnabled(false);
+ }
+ }
+ }
+}
+
+void ViewContainer::showViewContextMenu(QWidget* tab, const QPoint& pos)
+{
+ m_popupViewIndex = m_tabWidget->indexOf(tab);
+
+ updateViewActions(m_popupViewIndex);
+ QPopupMenu* menu = static_cast<QPopupMenu*>(m_window->factory()->container("tabContextMenu", m_window));
+
+ if (!menu) return;
+
+ ChatWindow* view = static_cast<ChatWindow*>(tab);
+ KToggleAction* autoJoinAction = static_cast<KToggleAction*>(actionCollection()->action("tab_autojoin"));
+ KAction* rejoinAction = actionCollection()->action("rejoin_channel");
+
+ if (view)
+ {
+ ChatWindow::WindowType viewType = view->getType();
+
+ updateViewEncoding(view);
+
+ if (viewType == ChatWindow::Channel)
+ {
+ autoJoinAction->plug(menu, 1);
+
+ Channel *channel = static_cast<Channel*>(view);
+ if (channel->rejoinable() && rejoinAction)
+ {
+ rejoinAction->plug(menu, 0);
+ rejoinAction->setEnabled(true);
+ }
+ }
+
+ if (viewType == ChatWindow::Status)
+ {
+ QPtrList<KAction> serverActions;
+ KAction* action = actionCollection()->action("disconnect_server");
+ if (action) serverActions.append(action);
+ action = actionCollection()->action("reconnect_server");
+ if (action) serverActions.append(action);
+ action = actionCollection()->action("join_channel");
+ if (action) serverActions.append(action);
+ action = new KActionSeparator();
+ if (action) serverActions.append(action);
+ m_window->plugActionList("server_actions", serverActions);
+ m_contextServer = view->getServer();
+ }
+ else
+ m_contextServer = 0;
+ }
+
+ if (menu->exec(pos) == -1)
+ {
+ m_popupViewIndex = -1;
+ view = static_cast<ChatWindow*>(m_tabWidget->currentPage());
+
+ if (view) updateViewEncoding(view);
+ }
+
+ autoJoinAction->unplug(menu);
+ rejoinAction->unplug(menu);
+
+ m_window->unplugActionList("server_actions");
+
+ emit contextMenuClosed();
+
+ updateViewActions(m_tabWidget->currentPageIndex());
+}
+
+QString ViewContainer::currentViewTitle()
+{
+ if (m_frontServer)
+ {
+ if (m_frontView && m_frontView->getType() == ChatWindow::Channel)
+ return m_frontView->getName();
+ else
+ return m_frontServer->getDisplayName();
+ }
+ else
+ {
+ return QString();
+ }
+}
+
+QString ViewContainer::currentViewURL(bool passNetwork)
+{
+ QString url;
+ QString channel;
+ QString port;
+ QString server;
+
+ if (m_frontServer && m_frontView)
+ {
+ updateFrontView();
+
+ if (m_frontView->getType() == ChatWindow::Channel)
+ channel = m_frontView->getName();
+
+ if (passNetwork)
+ server = m_frontServer->getDisplayName();
+ else
+ {
+ server = m_frontServer->getServerName();
+ port = ':'+QString::number(m_frontServer->getPort());
+ }
+
+ if (server.contains(':')) // IPv6
+ server = '['+server+']';
+
+ url = "irc://"+server+port+'/'+channel;
+ }
+
+ return url;
+}
+
+int ViewContainer::getViewIndex(QWidget* widget)
+{
+ return m_tabWidget->indexOf(widget);
+}
+
+void ViewContainer::clearView()
+{
+ if (m_frontView) m_frontView->getTextView()->clear();
+}
+
+void ViewContainer::clearAllViews()
+{
+ int total=m_tabWidget->count()-1;
+ ChatWindow* nextPage;
+
+ for(int i=0;i<=total;i++)
+ {
+ nextPage=static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if(nextPage && nextPage->getTextView())
+ nextPage->getTextView()->clear();
+ }
+}
+
+void ViewContainer::findText()
+{
+ if (!m_searchView)
+ {
+ KMessageBox::sorry(m_window,
+ i18n("You can only search in text fields."),
+ i18n("Find Text Information"));
+ }
+ else
+ {
+ m_searchView->getTextView()->search();
+ }
+}
+
+void ViewContainer::findNextText()
+{
+ if (m_searchView) m_searchView->getTextView()->searchAgain();
+}
+
+void ViewContainer::findPrevText()
+{
+ if (m_searchView) m_searchView->getTextView()->searchNext(true);
+}
+
+void ViewContainer::appendToFrontmost(const QString& type,const QString& message,ChatWindow* serverView, bool parseURL)
+{
+ if (!m_tabWidget) return;
+
+ if (!serverView) // e.g. DCOP info call
+ {
+ if (m_frontView) // m_frontView == NULL if canBeFrontView() == false for active ChatWindow
+ serverView = m_frontView->getServer()->getStatusView();
+ else if (m_frontServer) // m_fronView == NULL && m_frontServer != NULL if ChannelListPanel is active.
+ serverView = m_frontServer->getStatusView();
+ }
+
+ // This might happen if canBeFrontView() is false for active ChatWindow
+ // and the view does not belong to any server (e.g. DCC Status View).
+ // Discard message in this case.
+ if (!serverView) return;
+
+ updateFrontView();
+
+ if (!m_frontView || // Check if the m_frontView can actually display text or ...
+ // if it does not belong to this server or...
+ serverView->getServer()!=m_frontView->getServer() ||
+ // if the user decided to force it.
+ Preferences::redirectServerAndAppMsgToStatusPane())
+ {
+ // if not, take server specified fallback view instead
+ serverView->appendServerMessage(type, message, parseURL);
+ // FIXME: this signal should be sent from the status panel instead, so it
+ // can be using the correct highlight color, would be more consistent
+ // anyway!
+ // FIXME newText(serverView,QString::null,true);
+ }
+ else
+ m_frontView->appendServerMessage(type, message, parseURL);
+}
+
+void ViewContainer::insertCharacter()
+{
+ QFont font;
+
+ if (Preferences::customTextFont())
+ font = Preferences::textFont();
+ else
+ font = KGlobalSettings::generalFont();
+
+ if (!m_insertCharDialog)
+ {
+ m_insertCharDialog = new Konversation::InsertCharDialog(font.family(), m_window);
+ connect(m_insertCharDialog, SIGNAL(insertChar(const QChar&)), this, SLOT(insertChar(const QChar&)));
+ }
+
+ m_insertCharDialog->setFont(font);
+ m_insertCharDialog->show();
+}
+
+void ViewContainer::insertChar(const QChar& chr)
+{
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->currentPage());
+
+ if (view) view->appendInputText(chr, true/*fromCursor*/);
+}
+
+void ViewContainer::insertIRCColor()
+{
+ IRCColorChooser dlg(m_window);
+
+ if (dlg.exec() == KDialog::Accepted) m_frontView->appendInputText(dlg.color(), true/*fromCursor*/);
+}
+
+void ViewContainer::clearViewLines()
+{
+ if (m_frontView && m_frontView->isInsertSupported())
+ {
+ m_frontView->getTextView()->clearLines();
+
+ KAction* action = actionCollection()->action("clear_lines");
+ if (action) action->setEnabled(false);
+ }
+}
+
+void ViewContainer::insertRememberLine()
+{
+ if (Preferences::automaticRememberLine())
+ {
+ if (m_frontView && m_frontView->isInsertSupported())
+ m_frontView->getTextView()->insertRememberLine();
+ }
+}
+
+void ViewContainer::insertRememberLines(Server* server)
+{
+ for (int i = 0; i < m_tabWidget->count(); ++i)
+ {
+ ChatWindow* view = static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if (view->getServer() == server && view->isInsertSupported())
+ view->getTextView()->insertRememberLine();
+ }
+}
+
+void ViewContainer::cancelRememberLine()
+{
+ if (m_frontView && m_frontView->isInsertSupported())
+ {
+ m_frontView->getTextView()->cancelRememberLine();
+
+ KAction* action = actionCollection()->action("clear_lines");
+ if (action) action->setEnabled(m_frontView->getTextView()->hasLines());
+ }
+}
+
+void ViewContainer::insertMarkerLine()
+{
+ if (Preferences::markerLineInAllViews())
+ {
+ int total = m_tabWidget->count()-1;
+ ChatWindow* view;
+
+ for (int i = 0; i <= total; ++i)
+ {
+ view = static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if (view->isInsertSupported()) view->getTextView()->insertMarkerLine();
+ }
+ }
+ else
+ {
+ if (m_frontView && m_frontView->isInsertSupported())
+ m_frontView->getTextView()->insertMarkerLine();
+ }
+
+ if (m_frontView && m_frontView->isInsertSupported())
+ {
+ KAction* action = actionCollection()->action("clear_lines");
+ if (action) action->setEnabled(m_frontView->getTextView()->hasLines());
+ }
+}
+
+void ViewContainer::openLogFile()
+{
+ if (m_frontView)
+ {
+ ChatWindow* view=static_cast<ChatWindow*>(m_frontView);
+ ChatWindow::WindowType viewType=view->getType();
+ if (viewType==ChatWindow::Channel || viewType==ChatWindow::Query ||
+ viewType==ChatWindow::Status || viewType==ChatWindow::DccChat)
+ {
+ openLogFile(view->getName(), view->logFileName());
+ }
+ }
+}
+
+void ViewContainer::openLogFile(const QString& caption, const QString& file)
+{
+ if (!file.isEmpty())
+ {
+ LogfileReader* logReader = new LogfileReader(m_tabWidget, file);
+ addView(logReader, i18n("Logfile of %1").arg(caption));
+ logReader->setServer(0);
+ }
+}
+
+void ViewContainer::addKonsolePanel()
+{
+ KonsolePanel* panel=new KonsolePanel(m_tabWidget);
+ panel->setName(i18n("Konsole"));
+ addView(panel, i18n("Konsole"));
+ connect(panel, SIGNAL(updateTabNotification(ChatWindow*,const Konversation::TabNotifyType&)), this, SLOT(setViewNotification(ChatWindow*,const Konversation::TabNotifyType&)));
+ connect(panel, SIGNAL(closeView(ChatWindow*)), this, SLOT(closeView(ChatWindow*)));
+}
+
+void ViewContainer::addUrlCatcher()
+{
+ // if the panel wasn't open yet
+ if (m_urlCatcherPanel==0)
+ {
+ m_urlCatcherPanel=new UrlCatcher(m_tabWidget);
+ addView(m_urlCatcherPanel, i18n("URL Catcher"));
+ KonversationApplication *konvApp=static_cast<KonversationApplication *>(KApplication::kApplication());
+ connect(konvApp,SIGNAL(catchUrl(const QString&,const QString&)),
+ m_urlCatcherPanel, SLOT(addUrl(const QString&,const QString&)) );
+ connect(m_urlCatcherPanel, SIGNAL(deleteUrl(const QString&,const QString&)),
+ konvApp, SLOT(deleteUrl(const QString&,const QString&)) );
+ connect(m_urlCatcherPanel, SIGNAL(clearUrlList()),
+ konvApp, SLOT(clearUrlList()));
+
+ QStringList urlList=konvApp->getUrlList();
+ for(unsigned int index=0;index<urlList.count();index++)
+ {
+ QString urlItem=urlList[index];
+ m_urlCatcherPanel->addUrl(urlItem.section(' ',0,0),urlItem.section(' ',1,1));
+ } // for
+ (dynamic_cast<KToggleAction*>(actionCollection()->action("open_url_catcher")))->setChecked(true);
+ }
+ else
+ closeUrlCatcher();
+}
+
+void ViewContainer::closeUrlCatcher()
+{
+ // if there actually is a dcc panel
+ if (m_urlCatcherPanel)
+ {
+ delete m_urlCatcherPanel;
+ m_urlCatcherPanel = 0;
+ (dynamic_cast<KToggleAction*>(actionCollection()->action("open_url_catcher")))->setChecked(false);
+ }
+}
+
+void ViewContainer::toggleDccPanel()
+{
+ if (m_dccPanel==0 || !m_dccPanelOpen)
+ addDccPanel();
+ else
+ closeDccPanel();
+}
+
+void ViewContainer::addDccPanel()
+{
+ if (!m_dccPanelOpen)
+ {
+ addView(m_dccPanel, i18n("DCC Status"));
+ m_dccPanelOpen=true;
+ (dynamic_cast<KToggleAction*>(actionCollection()->action("open_dccstatus_window")))->setChecked(true);
+ }
+}
+
+void ViewContainer::closeDccPanel()
+{
+ // if there actually is a dcc panel
+ if (m_dccPanel)
+ {
+ // hide it from view, does not delete it
+ emit removeView(m_dccPanel);
+ if (m_tabWidget) m_tabWidget->removePage(m_dccPanel);
+ m_dccPanelOpen=false;
+ (dynamic_cast<KToggleAction*>(actionCollection()->action("open_dccstatus_window")))->setChecked(false);
+ }
+}
+
+void ViewContainer::deleteDccPanel()
+{
+ if (m_dccPanel)
+ {
+ closeDccPanel();
+ delete m_dccPanel;
+ m_dccPanel=0;
+ }
+}
+
+DccTransferPanel* ViewContainer::getDccPanel()
+{
+ return m_dccPanel;
+}
+
+void ViewContainer::addDccChat(const QString& myNick,const QString& nick,const QStringList& arguments,bool listen)
+{
+ if (!listen) // Someone else initiated dcc chat
+ {
+ KonversationApplication* konv_app=static_cast<KonversationApplication*>(KApplication::kApplication());
+ konv_app->notificationHandler()->dccChat(m_frontView, nick);
+ }
+
+ if (m_frontServer)
+ {
+ DccChat* dccChatPanel=listen
+ ? new DccChat(m_tabWidget, listen, m_frontServer, myNick, nick )
+ : new DccChat(m_tabWidget, listen, m_frontServer, myNick, nick, arguments[1], arguments[2].toInt() );
+
+ connect(dccChatPanel, SIGNAL(updateTabNotification(ChatWindow*,const Konversation::TabNotifyType&)), this, SLOT(setViewNotification(ChatWindow*,const Konversation::TabNotifyType&)));
+
+ // This needs to be here as addView will change m_frontServer if focus new tabs is enabled.
+ addView(dccChatPanel, dccChatPanel->getName());
+ }
+}
+
+StatusPanel* ViewContainer::addStatusView(Server* server)
+{
+ StatusPanel* statusView = new StatusPanel(m_tabWidget);
+
+ statusView->setServer(server);
+ statusView->setIdentity(server->getIdentity());
+
+ if (server->getServerGroup()) statusView->setNotificationsEnabled(server->getServerGroup()->enableNotifications());
+
+ // Get group name for tab if available
+ QString label = server->getDisplayName();
+ statusView->setName(label);
+
+ QObject::connect(server, SIGNAL(sslInitFailure()), this, SIGNAL(removeStatusBarSSLLabel()));
+ QObject::connect(server, SIGNAL(sslConnected(Server*)), this, SIGNAL(updateStatusBarSSLLabel(Server*)));
+
+ // ... then put it into the tab widget, otherwise we'd have a race with server member
+ addView(statusView, label);
+
+ connect(statusView, SIGNAL(updateTabNotification(ChatWindow*,const Konversation::TabNotifyType&)),
+ this, SLOT(setViewNotification(ChatWindow*,const Konversation::TabNotifyType&)));
+ connect(statusView, SIGNAL(sendFile()), server, SLOT(requestDccSend()));
+ connect(server, SIGNAL(awayState(bool)), statusView, SLOT(indicateAway(bool)) );
+
+ // make sure that m_frontServer gets set on adding the first status panel, too,
+ // since there won't be a switchView happening
+ if (!m_frontServer) setFrontServer(server);
+
+ return statusView;
+}
+
+RawLog* ViewContainer::addRawLog(Server* server)
+{
+ RawLog* rawLog = new RawLog(m_tabWidget);
+ rawLog->setServer(server);
+ rawLog->setLog(false);
+
+ if (server->getServerGroup()) rawLog->setNotificationsEnabled(server->getServerGroup()->enableNotifications());
+
+ addView(rawLog, i18n("Raw Log"));
+
+ connect(rawLog, SIGNAL(updateTabNotification(ChatWindow*,const Konversation::TabNotifyType&)),
+ this, SLOT(setViewNotification(ChatWindow*,const Konversation::TabNotifyType&)));
+
+ return rawLog;
+}
+
+void ViewContainer::reconnectFrontServer()
+{
+ Server* server = 0;
+
+ if (m_contextServer)
+ server = m_contextServer;
+ else
+ server = m_frontServer;
+
+ if (server) server->reconnect();
+}
+
+void ViewContainer::disconnectFrontServer()
+{
+ Server* server = 0;
+
+ if (m_contextServer)
+ server = m_contextServer;
+ else
+ server = m_frontServer;
+
+ if (server && server->isConnected())
+ server->disconnect();
+}
+
+void ViewContainer::showJoinChannelDialog()
+{
+ Server* server = 0;
+
+ if (m_contextServer)
+ server = m_contextServer;
+ else
+ server = m_frontServer;
+
+ if (!server)
+ return;
+
+ Konversation::JoinChannelDialog dlg(server, m_window);
+
+ if (dlg.exec() == QDialog::Accepted)
+ server->sendJoinCommand(dlg.channel(), dlg.password());
+}
+
+void ViewContainer::connectionStateChanged(Server* server, Konversation::ConnectionState state)
+{
+ Server* updateServer = 0;
+
+ if (m_contextServer)
+ updateServer = m_contextServer;
+ else
+ updateServer = m_frontServer;
+
+ if (updateServer && updateServer == server)
+ {
+ KAction* action = actionCollection()->action("disconnect_server");
+ if (action)
+ action->setEnabled(state == Konversation::SSConnected);
+
+ action = actionCollection()->action("join_channel");
+ if (action)
+ action->setEnabled(state == Konversation::SSConnected);
+
+ if (m_frontView && m_frontView->getServer() == server
+ && m_frontView->getType() == ChatWindow::Channel)
+ {
+ ChatWindow* view = m_frontView;
+ Channel* channel = static_cast<Channel*>(view);
+
+ action = actionCollection()->action("rejoin_channel");
+ if (action) action->setEnabled(state == Konversation::SSConnected && channel->rejoinable());
+ }
+ }
+}
+
+void ViewContainer::channelJoined(Channel* channel)
+{
+ ChatWindow* view = m_frontView;
+
+ if (view == channel)
+ {
+ KAction* action = actionCollection()->action("rejoin_channel");
+ if (action) action->setEnabled(false);
+ }
+}
+
+Channel* ViewContainer::addChannel(Server* server, const QString& name)
+{
+ Channel* channel=new Channel(m_tabWidget, name);
+ channel->setServer(server);
+ channel->setName(name); //still have to do this for now
+ addView(channel, name);
+
+ connect(this, SIGNAL(updateChannelAppearance()), channel, SLOT(updateAppearance()));
+ connect(channel, SIGNAL(updateTabNotification(ChatWindow*,const Konversation::TabNotifyType&)), this, SLOT(setViewNotification(ChatWindow*,const Konversation::TabNotifyType&)));
+ connect(server, SIGNAL(awayState(bool)), channel, SLOT(indicateAway(bool)) );
+ connect(channel, SIGNAL(joined(Channel*)), this, SLOT(channelJoined(Channel*)));
+
+ return channel;
+}
+
+void ViewContainer::rejoinChannel()
+{
+ Channel* channel = 0;
+
+ if (m_popupViewIndex == -1)
+ channel = static_cast<Channel*>(m_tabWidget->currentPage());
+ else
+ channel = static_cast<Channel*>(m_tabWidget->page(m_popupViewIndex));
+
+ if (channel && channel->getType() == ChatWindow::Channel)
+ channel->rejoin();
+}
+
+void ViewContainer::openChannelSettings()
+{
+ if (m_frontView->getType() == ChatWindow::Channel)
+ {
+ Channel* channel = static_cast<Channel*>(m_tabWidget->currentPage());
+ channel->showOptionsDialog();
+ }
+}
+
+void ViewContainer::toggleChannelNicklists()
+{
+ KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("hide_nicknamelist"));
+
+ if (action)
+ {
+ Preferences::setShowNickList(!action->isChecked());
+ Preferences::writeConfig();
+
+ emit updateChannelAppearance();
+ }
+}
+
+Query* ViewContainer::addQuery(Server* server, const NickInfoPtr& nickInfo, bool weinitiated)
+{
+ QString name = nickInfo->getNickname();
+ Query* query=new Query(m_tabWidget, name);
+ query->setServer(server);
+ query->setNickInfo(nickInfo); //still have to do this
+ addView(query, name, weinitiated);
+
+ // About to increase the number of queries, so enable the close action
+ if (m_queryViewCount == 0)
+ actionCollection()->action("close_queries")->setEnabled(true);
+
+ ++m_queryViewCount;
+
+ connect(query, SIGNAL(updateTabNotification(ChatWindow*,const Konversation::TabNotifyType&)), this, SLOT(setViewNotification(ChatWindow*,const Konversation::TabNotifyType&)));
+ connect(query, SIGNAL(updateQueryChrome(ChatWindow*, const QString &)), this, SLOT(updateQueryChrome(ChatWindow*, const QString &)));
+ connect(server, SIGNAL(awayState(bool)), query, SLOT(indicateAway(bool)));
+
+ return query;
+}
+
+void ViewContainer::updateQueryChrome(ChatWindow* view, const QString& name)
+{
+ //FIXME: updateQueryChrome is a last minute fix for 0.19 because
+ // the updateInfo mess is indecipherable. Replace with a sane and
+ // encompassing system.
+
+ QString newName = Konversation::removeIrcMarkup(name);
+
+ if (!newName.isEmpty() && m_tabWidget->tabLabel(view) != newName)
+ {
+ if (m_viewTree) m_viewTree->setViewName(view, newName);
+ if (m_tabWidget) m_tabWidget->setTabLabel(view, newName);
+ }
+
+ if (!newName.isEmpty() && view==m_frontView)
+ emit setWindowCaption(newName);
+}
+
+void ViewContainer::closeQueries()
+{
+ int total=m_tabWidget->count()-1;
+ int operations = 0;
+ ChatWindow* nextPage;
+
+ for (int i=0; i <=total; i++)
+ {
+ if (operations > total)
+ break;
+
+ nextPage = static_cast<ChatWindow*>(m_tabWidget->page(i));
+
+ if (nextPage && nextPage->getType()==ChatWindow::Query)
+ {
+ closeView(nextPage);
+ if (m_tabWidget->indexOf(nextPage) == -1) --i;
+ }
+ ++operations;
+ }
+
+ m_queryViewCount = 0;
+
+ actionCollection()->action("close_queries")->setEnabled(false);
+}
+
+ChannelListPanel* ViewContainer::addChannelListPanel(Server* server)
+{
+ ChannelListPanel* channelListPanel=new ChannelListPanel(m_tabWidget);
+ channelListPanel->setServer(server);
+ addView(channelListPanel, i18n("Channel List"));
+
+ KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("open_channel_list"));
+ if ((server == m_frontServer) && action) action->setChecked(true);
+
+ return channelListPanel;
+}
+
+void ViewContainer::openChannelList(const QString& filter, bool getList)
+{
+ if (m_frontServer)
+ {
+ ChannelListPanel* panel = m_frontServer->getChannelListPanel();
+
+ if (panel)
+ {
+ closeView(panel);
+ KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("open_channel_list"));
+ if (action) action->setChecked(false);
+ }
+ else
+ {
+ int ret = KMessageBox::Continue;
+
+ if (filter.isEmpty())
+ {
+ ret = KMessageBox::warningContinueCancel(m_window,i18n("Using this function may result in a lot "
+ "of network traffic. If your connection is not fast "
+ "enough, it is possible that your client will be "
+ "disconnected by the server."), i18n("Channel List Warning"),
+ KStdGuiItem::cont(), "ChannelListWarning");
+ }
+
+ if (ret != KMessageBox::Continue) return;
+
+ panel = m_frontServer->addChannelListPanel();
+
+ panel->setFilter(filter);
+
+ if(getList) panel->applyFilterClicked();
+ }
+ }
+ else
+ {
+ KMessageBox::information(m_window,
+ i18n(
+ "The channel list can only be opened from a "
+ "query, channel or status window to find out, "
+ "which server this list belongs to."
+ ),
+ i18n("Channel List"),
+ "ChannelListNoServerSelected");
+ }
+}
+
+void ViewContainer::openNicksOnlinePanel()
+{
+ if (!m_nicksOnlinePanel)
+ {
+ m_nicksOnlinePanel=new NicksOnline(m_window);
+ addView(m_nicksOnlinePanel, i18n("Watched Nicks Online"));
+ connect(m_nicksOnlinePanel, SIGNAL(editClicked()), m_window, SLOT(openNotify()));
+ connect(m_nicksOnlinePanel, SIGNAL(doubleClicked(const QString&,const QString&)), m_window, SLOT(notifyAction(const QString&,const QString&)));
+ connect(m_nicksOnlinePanel, SIGNAL(showView(ChatWindow*)), this, SLOT(showView(ChatWindow*)));
+ connect(m_window, SIGNAL(nicksNowOnline(Server*)), m_nicksOnlinePanel, SLOT(updateServerOnlineList(Server*)));
+ (dynamic_cast<KToggleAction*>(actionCollection()->action("open_nicksonline_window")))->setChecked(true);
+ }
+ else
+ {
+ closeNicksOnlinePanel();
+ (dynamic_cast<KToggleAction*>(actionCollection()->action("open_nicksonline_window")))->setChecked(false);
+ }
+
+}
+
+void ViewContainer::closeNicksOnlinePanel()
+{
+ if(m_nicksOnlinePanel)
+ {
+ delete m_nicksOnlinePanel;
+ m_nicksOnlinePanel = 0;
+ }
+ (dynamic_cast<KToggleAction*>(actionCollection()->action("open_nicksonline_window")))->setChecked(false);
+}
+
+void ViewContainer::showNextActiveView()
+{
+ if (!m_activeViewOrderList.isEmpty())
+ {
+ ChatWindow* prev = m_activeViewOrderList.first();
+ ChatWindow* view = prev;
+
+ QValueList<ChatWindow*>::ConstIterator it;
+
+ for (it = m_activeViewOrderList.begin(); it != m_activeViewOrderList.end(); ++it)
+ {
+ if ((*it)->currentTabNotification() < prev->currentTabNotification())
+ view = (*it);
+ }
+
+ m_tabWidget->setCurrentPage(m_tabWidget->indexOf(view));
+ }
+}
+
+/*!
+ \fn ViewContainer::frontServerChanging(Server *newServer)
+
+ This signal is emitted immediately before the front server is changed.
+
+ If the server is being removed this will fire with a null pointer.
+*/
+
+#include "viewcontainer.moc"
diff --git a/konversation/src/viewcontainer.h b/konversation/src/viewcontainer.h
new file mode 100644
index 0000000..bacedf4
--- /dev/null
+++ b/konversation/src/viewcontainer.h
@@ -0,0 +1,229 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+#ifndef VIEWCONTAINER_H
+#define VIEWCONTAINER_H
+
+#include "konversationmainwindow.h"
+#include "common.h"
+#include "server.h"
+
+#include <qobject.h>
+#include <qguardedptr.h>
+
+
+class QSplitter;
+
+class KTabWidget;
+class KActionCollection;
+
+class KonversationMainWindow;
+class ViewTree;
+class ChatWindow;
+class Server;
+class Images;
+class UrlCatcher;
+class DccTransferPanel;
+class NicksOnline;
+class QueueTuner;
+
+namespace Konversation
+{
+ class InsertCharDialog;
+ class ServerGroupSettings;
+}
+
+class ViewContainer : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit ViewContainer(KonversationMainWindow* window);
+ ~ViewContainer();
+
+ QSplitter* getWidget() { return m_viewTreeSplitter; }
+ KonversationMainWindow* getWindow() { return m_window; }
+ KActionCollection* actionCollection() { return m_window->actionCollection(); }
+
+ QGuardedPtr<ChatWindow> getFrontView() { return m_frontView; }
+ Server* getFrontServer() { return m_frontServer; }
+
+ void prepareShutdown();
+
+ QString currentViewTitle();
+ QString currentViewURL(bool passNetwork);
+
+ void appendToFrontmost(const QString& type,const QString& message,ChatWindow* serverView,
+ bool parseURL = true);
+
+ void showQueueTuner(bool);
+
+ int getViewIndex(QWidget* widget);
+
+ public slots:
+ void updateAppearance();
+ void saveSplitterSizes();
+ void setViewTreeShown(bool show);
+ void syncTabBarToTree();
+
+ void updateViews(const Konversation::ServerGroupSettings* serverGroup = 0);
+ void updateViewIcons();
+ void setViewNotification(ChatWindow* widget, const Konversation::TabNotifyType& type);
+ void unsetViewNotification(ChatWindow* view);
+ void toggleViewNotifications();
+ void toggleAutoJoin();
+
+ void switchView(QWidget* newView);
+ void showView(ChatWindow* view);
+
+ void goToView(int page);
+ void showNextView();
+ void showPreviousView();
+ void moveViewLeft();
+ void moveViewRight();
+
+ void closeView(QWidget* view);
+ void closeView(ChatWindow* view);
+ void closeViewMiddleClick(QWidget* view);
+ void closeCurrentView();
+ void cleanupAfterClose(ChatWindow* view);
+
+ void changeViewCharset(int index);
+ void updateViewEncoding(ChatWindow* view);
+
+ void showViewContextMenu(QWidget* tab, const QPoint& pos);
+
+ void clearView();
+ void clearAllViews();
+
+ void findText();
+ void findNextText();
+ void findPrevText();
+
+ void insertCharacter();
+ void insertChar(const QChar& chr);
+ void insertIRCColor();
+
+ void clearViewLines();
+ void insertRememberLine();
+ void cancelRememberLine();
+ void insertMarkerLine();
+ void insertRememberLines(Server* server);
+
+ void openLogFile();
+ void openLogFile(const QString& caption, const QString& file);
+
+ void addKonsolePanel();
+
+ void addUrlCatcher();
+ void closeUrlCatcher();
+
+ void toggleDccPanel();
+ void addDccPanel();
+ void closeDccPanel();
+ void deleteDccPanel();
+ DccTransferPanel* getDccPanel();
+
+ void addDccChat(const QString& myNick,const QString& nick,const QStringList& arguments,bool listen);
+
+ StatusPanel* addStatusView(Server* server);
+ RawLog* addRawLog(Server* server);
+ void disconnectFrontServer();
+ void reconnectFrontServer();
+ void showJoinChannelDialog();
+ void connectionStateChanged(Server* server, Konversation::ConnectionState state);
+ void channelJoined(Channel* channel);
+
+ Channel* addChannel(Server* server, const QString& name);
+ void rejoinChannel();
+ void openChannelSettings();
+ void toggleChannelNicklists();
+
+ Query* addQuery(Server* server,const NickInfoPtr & name, bool weinitiated=true);
+ void updateQueryChrome(ChatWindow* view, const QString& name);
+ void closeQueries();
+
+ ChannelListPanel* addChannelListPanel(Server* server);
+ void openChannelList(const QString& filter = QString(), bool getList = false);
+
+ void openNicksOnlinePanel();
+ void closeNicksOnlinePanel();
+
+ void showNextActiveView();
+
+ signals:
+ void viewChanged(ChatWindow* view);
+ void removeView(ChatWindow* view);
+ void setWindowCaption(const QString& caption);
+ void updateChannelAppearance();
+ void contextMenuClosed();
+ void resetStatusBar();
+ void setStatusBarTempText(const QString& text);
+ void clearStatusBarTempText();
+ void setStatusBarInfoLabel(const QString& text);
+ void clearStatusBarInfoLabel();
+ void setStatusBarLagLabelShown(bool shown);
+ void updateStatusBarLagLabel(Server* server, int msec);
+ void resetStatusBarLagLabel();
+ void setStatusBarLagLabelTooLongLag(Server* server, int msec);
+ void updateStatusBarSSLLabel(Server* server);
+ void removeStatusBarSSLLabel();
+ void autoJoinToggled(const Konversation::ServerGroupSettings*);
+
+ void frontServerChanging(Server *);
+
+ private:
+ void setupTabWidget();
+ void setupViewTree();
+ void removeViewTree();
+ void updateTabWidgetAppearance();
+
+ void addView(ChatWindow* view, const QString& label, bool weinitiated=true);
+
+ void updateViewActions(int index);
+ void updateSwitchViewAction();
+ void updateFrontView();
+
+ void setFrontServer(Server *);
+
+ void initializeSplitterSizes();
+ bool m_saveSplitterSizesLock;
+
+ KonversationMainWindow* m_window;
+
+ QSplitter* m_viewTreeSplitter;
+ KTabWidget* m_tabWidget;
+ ViewTree* m_viewTree;
+ QVBox *m_vbox;
+ QueueTuner *m_queueTuner;
+
+ Images* images;
+
+ QGuardedPtr<Server> m_frontServer;
+ QGuardedPtr<Server> m_contextServer;
+ QGuardedPtr<ChatWindow> m_frontView;
+ QGuardedPtr<ChatWindow> m_searchView;
+
+ UrlCatcher* m_urlCatcherPanel;
+ NicksOnline* m_nicksOnlinePanel;
+
+ DccTransferPanel* m_dccPanel;
+ bool m_dccPanelOpen;
+
+ Konversation::InsertCharDialog* m_insertCharDialog;
+
+ int m_popupViewIndex;
+ int m_queryViewCount;
+
+ QValueList<ChatWindow*> m_activeViewOrderList;
+};
+
+#endif
diff --git a/konversation/src/viewtree.cpp b/konversation/src/viewtree.cpp
new file mode 100644
index 0000000..7d40ce8
--- /dev/null
+++ b/konversation/src/viewtree.cpp
@@ -0,0 +1,973 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "viewtree.h"
+#include "viewtreeitem.h"
+#include "preferences.h"
+#include "chatwindow.h"
+#include "server.h"
+#include "channel.h"
+#include "ircview.h"
+#include "konsolepanel.h"
+
+#include <qheader.h>
+#include <qdragobject.h>
+#include <qlistview.h>
+#include <qptrlist.h>
+#include <qpoint.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobalsettings.h>
+#include <kapplication.h>
+
+
+class ViewTree::ToolTip : public QToolTip
+{
+ public:
+ ToolTip(QWidget *parent, KListView *viewTree);
+ virtual ~ToolTip() {}
+
+ protected:
+ virtual void maybeTip(const QPoint &pos);
+
+ private:
+ KListView* viewTree;
+};
+
+ViewTree::ToolTip::ToolTip(QWidget *parent, KListView *viewTree)
+ : QToolTip(parent), viewTree(viewTree)
+{
+}
+
+void ViewTree::ToolTip::maybeTip (const QPoint &pos)
+{
+ if (!parentWidget() || !viewTree) return;
+
+ ViewTreeItem* view = static_cast<ViewTreeItem*>(viewTree->itemAt(pos));
+
+ if (view && view->isTruncated()) tip(viewTree->itemRect(view), view->getName());
+}
+
+
+ViewTree::ViewTree(QWidget *parent)
+ : KListView(parent)
+{
+ header()->hide();
+ setHScrollBarMode(QScrollView::AlwaysOff);
+
+ addColumn(i18n("Tabs"));
+ setSortColumn(0);
+ setSortOrder(Qt::Ascending);
+
+ setResizeMode(QListView::AllColumns);
+ setSelectionModeExt(KListView::Single);
+ setRootIsDecorated(false);
+
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setDropVisualizer(true);
+
+ setShowToolTips(false);
+ m_toolTip = new ViewTree::ToolTip(viewport(), this);
+
+ // Controls whether or not to select the first view added
+ // to the tree. Don't do so by default; only when told to
+ // by the ViewContainer.
+ m_selectFirstView = false;
+
+ m_separator = 0;
+ m_specialViewCount = 0;
+
+ m_closeButtonItem = 0;
+ m_enableCloseButtonTimer = new QTimer(this);
+
+ m_middleClickItem = 0;
+
+ connect(m_enableCloseButtonTimer, SIGNAL(timeout()), SLOT(enableCloseButton()));
+ connect(this, SIGNAL(selectionChanged(QListViewItem*)), SLOT(announceSelection(QListViewItem*)));
+ connect(this, SIGNAL(aboutToMove()), SLOT(slotAboutToMoveView()));
+ connect(this, SIGNAL(moved()), SLOT(slotMovedView()));
+}
+
+ViewTree::~ViewTree()
+{
+ delete m_toolTip;
+ m_toolTip = 0;
+ emit setViewTreeShown(false);
+}
+
+void ViewTree::updateAppearance()
+{
+ if (Preferences::customTabFont())
+ setFont(Preferences::tabFont());
+ else
+ setFont(KGlobalSettings::generalFont());
+
+ QColor fg, bg;
+
+ if (Preferences::inputFieldsBackgroundColor())
+ {
+ fg = Preferences::color(Preferences::ChannelMessage);
+ bg = Preferences::color(Preferences::TextViewBackground);
+ }
+ else
+ {
+ bg = KGlobalSettings::baseColor();
+ fg = KGlobalSettings::textColor();
+ }
+
+ setPalette(KApplication::palette());
+ setPaletteForegroundColor(fg);
+ setPaletteBackgroundColor(bg);
+}
+
+void ViewTree::addView(const QString& name, ChatWindow* view, const QIconSet &iconset, bool select, ChatWindow* afterView)
+{
+ ViewTreeItem* item = 0;
+ ViewTreeItem* parent = 0;
+
+ if (view->getType() != ChatWindow::DccChat)
+ parent = getParentItemForView(view);
+
+ if (parent)
+ {
+ if (afterView)
+ {
+ ViewTreeItem* afterItem = getItemForView(afterView);
+ slotAboutToMoveView();
+ item = new ViewTreeItem(parent, afterItem, name, view);
+ slotMovedView();
+ }
+ else
+ item = new ViewTreeItem(parent, name, view);
+ }
+ else
+ item = new ViewTreeItem(this, name, view);
+
+ if (item)
+ {
+ if (item->sortLast()) ++m_specialViewCount;
+
+ item->setIcon(iconset.pixmap());
+
+ if (select || m_selectFirstView)
+ {
+ setSelected(item, true);
+
+ // The work is done - the first view is selected.
+ m_selectFirstView = false;
+ }
+
+ toggleSeparator();
+
+ // The tree may have been hidden previously as the last
+ // view was closed.
+ if (isHidden()) emit setViewTreeShown(true);
+ }
+}
+
+void ViewTree::toggleSeparator()
+{
+ if (m_separator == 0 && m_specialViewCount > 0 && !(childCount() == m_specialViewCount))
+ m_separator = new ViewTreeItem(this);
+
+ if (m_separator && m_specialViewCount == 0)
+ {
+ delete m_separator;
+ m_separator = 0;
+ }
+
+ if (m_separator && childCount() == (m_specialViewCount + 1))
+ {
+ delete m_separator;
+ m_separator = 0;
+ }
+
+ sort();
+}
+
+void ViewTree::removeView(ChatWindow* view)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (item)
+ {
+ // During the short delay between a close button-induced view deletion
+ // and the actual removal from the list, mouse events may cause our
+ // m_enableCloseButtonTimer to be activated again, and removeView() to
+ // finish just before its timeout(), causing enableCloseButton() to hit
+ // a dangling pointer. Hence, if the item to be deleted is identical to
+ // m_closeButtonItem, stop the timer and set the pointer to 0.
+ if (item == m_closeButtonItem)
+ {
+ m_enableCloseButtonTimer->stop();
+ m_closeButtonItem = 0;
+ }
+
+ if (item->sortLast()) --m_specialViewCount;
+
+ if (item->childCount() > 0)
+ {
+ while (item->firstChild() != 0)
+ {
+ ViewTreeItem* firstChild = static_cast<ViewTreeItem*>(item->firstChild());
+
+ delete firstChild;
+ }
+
+ delete item;
+ }
+ else
+ delete item;
+
+
+ toggleSeparator();
+
+ // Hide empty tree.
+ if (childCount() == 0)
+ {
+ emit setViewTreeShown(false);
+ m_selectFirstView = true;
+ }
+ }
+}
+
+void ViewTree::selectView(ChatWindow* view)
+{
+ // Repaint everything.
+ triggerUpdate();
+
+ ViewTreeItem* item = getItemForView(view);
+
+ if (item && !item->isSelected())
+ setSelected(item, true);
+}
+
+void ViewTree::selectFirstView(bool select)
+{
+ m_selectFirstView = select;
+}
+
+void ViewTree::setViewName(ChatWindow* view, const QString& name)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (item) item->setName(name);
+}
+
+
+void ViewTree::setViewColor(ChatWindow* view, QColor color)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (item) item->setColor(color);
+}
+
+void ViewTree::setViewIcon(ChatWindow* view, const QIconSet &iconset)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (item) item->setIcon(iconset.pixmap());
+}
+
+void ViewTree::announceSelection(QListViewItem* item)
+{
+ unHighlight();
+
+ ViewTreeItem* newItem = static_cast<ViewTreeItem*>(item);
+
+ emit showView(newItem->getView());
+}
+
+bool ViewTree::canMoveViewUp(ChatWindow* view)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (item)
+ return canMoveItemUp(item);
+
+ return false;
+}
+
+bool ViewTree::canMoveViewDown(ChatWindow* view)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (item)
+ return canMoveItemDown(item);
+
+ return false;
+}
+
+bool ViewTree::canMoveItemUp(ViewTreeItem* item)
+{
+ if (item->isSeparator())
+ return false;
+
+ if (!item->itemAbove())
+ return false;
+
+ ViewTreeItem* itemAbove = static_cast<ViewTreeItem*>(item->itemAbove());
+
+ if (item->sortLast() && !itemAbove->sortLast())
+ return false;
+
+ if (item->sortLast() && itemAbove->isSeparator())
+ return false;
+
+ if (item->depth() > 0 && itemAbove->depth() != item->depth())
+ return false;
+
+ return true;
+}
+
+bool ViewTree::canMoveItemDown(ViewTreeItem* item)
+{
+ if (item->isSeparator())
+ return false;
+
+ if (!item->itemBelow())
+ return false;
+
+ ViewTreeItem* itemBelow = static_cast<ViewTreeItem*>(item->itemBelow());
+
+ if (!item->sortLast() && itemBelow->sortLast())
+ return false;
+
+ if (item->depth() > 0 && itemBelow->depth() != item->depth())
+ return false;
+
+ if (item->depth() == 0 && !item->sortLast() && itemBelow->depth() > 0)
+ {
+ int companionsBelow = 0;
+
+ while ((itemBelow = static_cast<ViewTreeItem*>(itemBelow->itemBelow())) != 0)
+ {
+ if (!itemBelow->sortLast() && itemBelow->depth() == item->depth())
+ ++companionsBelow;
+ }
+
+ if (!companionsBelow)
+ return false;
+ }
+
+ return true;
+}
+
+void ViewTree::moveViewUp(ChatWindow* view)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (canMoveItemUp(item))
+ {
+ ViewTreeItem* itemAbove = static_cast<ViewTreeItem*>(item->itemAbove());
+
+ if (item->depth() == itemAbove->depth())
+ {
+ int newSortIndex = itemAbove->getSortIndex();
+ int oldSortIndex = item->getSortIndex();
+
+ item->setSortIndex(newSortIndex);
+ itemAbove->setSortIndex(oldSortIndex);
+
+ sort();
+ }
+ else if (item->depth() < itemAbove->depth())
+ {
+ ViewTreeItem* parent = static_cast<ViewTreeItem*>(itemAbove->parent());
+
+ if (parent)
+ {
+ int newSortIndex = parent->getSortIndex();
+ int oldSortIndex = item->getSortIndex();
+
+ item->setSortIndex(newSortIndex);
+ parent->setSortIndex(oldSortIndex);
+
+ sort();
+ }
+ }
+ }
+}
+
+void ViewTree::moveViewDown(ChatWindow* view)
+{
+ ViewTreeItem* item = getItemForView(view);
+
+ if (canMoveItemDown(item))
+ {
+ ViewTreeItem* itemBelow = static_cast<ViewTreeItem*>(item->itemBelow());
+
+ if (item->depth() == itemBelow->depth())
+ {
+ int newSortIndex = itemBelow->getSortIndex();
+ int oldSortIndex = item->getSortIndex();
+
+ item->setSortIndex(newSortIndex);
+ itemBelow->setSortIndex(oldSortIndex);
+
+ sort();
+ }
+ else if (item->depth() < itemBelow->depth())
+ {
+ while ((itemBelow = static_cast<ViewTreeItem*>(itemBelow->itemBelow())) != 0)
+ {
+ if (!itemBelow->sortLast() && itemBelow->depth() == item->depth())
+ break;
+ }
+
+ int newSortIndex = itemBelow->getSortIndex();
+ int oldSortIndex = item->getSortIndex();
+
+ item->setSortIndex(newSortIndex);
+ itemBelow->setSortIndex(oldSortIndex);
+
+ sort();
+ }
+ }
+}
+
+void ViewTree::slotAboutToMoveView()
+{
+ setSortColumn(-1);
+}
+
+void ViewTree::slotMovedView()
+{
+ int newSortIndex = 0;
+
+ ViewTreeItem* tempItem = static_cast<ViewTreeItem*>(this->firstChild());
+
+ while (tempItem)
+ {
+ tempItem->setSortIndex(newSortIndex);
+ ++newSortIndex;
+ tempItem = static_cast<ViewTreeItem*>(tempItem->itemBelow());
+ }
+
+ setSortColumn(0);
+
+ triggerUpdate();
+
+ emit syncTabBarToTree();
+}
+
+void ViewTree::unHighlight()
+{
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(firstChild());
+
+ while (item)
+ {
+ item->setHighlighted(false);
+ item = static_cast<ViewTreeItem*>(item->itemBelow());
+ }
+}
+
+void ViewTree::hideCloseButtons(ViewTreeItem* exception)
+{
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(firstChild());
+
+ if (exception)
+ {
+ while (item)
+ {
+ if (item != exception) item->setCloseButtonShown(false);
+ item = static_cast<ViewTreeItem*>(item->itemBelow());
+ }
+ }
+ else
+ {
+ while (item)
+ {
+ item->setCloseButtonShown(false);
+ item = static_cast<ViewTreeItem*>(item->itemBelow());
+ }
+ }
+}
+
+void ViewTree::enableCloseButton()
+{
+ if (m_closeButtonItem) m_closeButtonItem->setCloseButtonEnabled();
+}
+
+bool ViewTree::isAboveIcon(QPoint point, ViewTreeItem* item)
+{
+ QPoint inItem = point - itemRect(item).topLeft();
+
+ int MARGIN = 2;
+ int LED_ICON_SIZE = 14;
+
+ int horizOffset = MARGIN + depthToPixels(item->depth());
+ int vertOffset = (item->height() - LED_ICON_SIZE) / 2;
+
+ if ((inItem.x() > horizOffset && inItem.x() < (LED_ICON_SIZE + horizOffset))
+ && (inItem.y() > vertOffset && inItem.y() < (LED_ICON_SIZE + vertOffset)))
+ {
+ return true;
+ }
+ else
+ return false;
+}
+
+void ViewTree::contentsMousePressEvent(QMouseEvent* e)
+{
+ QPoint vp = contentsToViewport(e->pos());
+
+ // Don't allow selecting the separator via the mouse.
+ if (itemAt(vp) == m_separator)
+ return;
+
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(itemAt(vp));
+
+ // Prevent selection being undone by a stray click into
+ // the empty area of the widget by only passing on the
+ // mouse event if it's on a list item.
+ if (item)
+ {
+ // Don't change the selected item when the user only
+ // wants to get the context menu for a non-selected
+ // item.
+ if (e->button() == RightButton && !item->isSelected())
+ return;
+
+ if (Preferences::closeButtons() && e->button() == LeftButton && isAboveIcon(vp, item))
+ {
+ m_pressedAboveCloseButton = true;
+ if (!item->getCloseButtonEnabled()) KListView::contentsMousePressEvent(e);
+ }
+ else
+ {
+ m_pressedAboveCloseButton = false;
+ KListView::contentsMousePressEvent(e);
+ }
+ m_middleClickItem = (Preferences::middleClickClose() && e->button() == MidButton) ? item : 0;
+ }
+}
+
+void ViewTree::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ QPoint vp = contentsToViewport(e->pos());
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(itemAt(vp));
+
+ if (!item && e->button() == RightButton)
+ return;
+
+ if (item)
+ {
+ if (Preferences::closeButtons() && e->button() == LeftButton
+ && isAboveIcon(vp, item) && m_pressedAboveCloseButton
+ && item->getCloseButtonEnabled())
+ {
+ emit closeView(item->getView());
+ }
+
+ if (Preferences::middleClickClose() && e->button() == MidButton
+ && item == m_middleClickItem)
+ {
+ emit closeView(item->getView());
+
+ m_middleClickItem = 0;
+ }
+ }
+ else
+ KListView::contentsMouseReleaseEvent(e);
+}
+
+void ViewTree::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ QPoint vp = contentsToViewport(e->pos());
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(itemAt(vp));
+
+ if (item && item->isSeparator())
+ return;
+
+ // Cancel middle-click close.
+ if (item != m_middleClickItem) m_middleClickItem = 0;
+
+ // Allow dragging only with the middle mouse button, just
+ // like for the tab bar.
+ if ((e->state() & MidButton) == MidButton)
+ KListView::contentsMouseMoveEvent(e);
+ else if ((e->state() & LeftButton) == LeftButton)
+ {
+ if (item && (item != selectedItem()) && !item->isSeparator())
+ setSelected(item, true);
+ }
+
+ if (Preferences::closeButtons())
+ {
+ if (!(e->state() & LeftButton) && !(e->state() & MidButton) && !(e->state() & RightButton))
+ {
+ if (item)
+ {
+ hideCloseButtons(item);
+
+ if (isAboveIcon(vp, item))
+ {
+ item->setCloseButtonShown(true);
+ m_closeButtonItem = item;
+ if (!m_enableCloseButtonTimer->isActive())
+ m_enableCloseButtonTimer->start(QApplication::doubleClickInterval(), true);
+ }
+ else
+ {
+ m_closeButtonItem = 0;
+ item->setCloseButtonShown(false);
+ m_enableCloseButtonTimer->stop();
+ }
+ }
+ else
+ {
+ hideCloseButtons();
+ }
+ }
+ }
+}
+
+void ViewTree::contentsContextMenuEvent(QContextMenuEvent* e)
+{
+ QPoint vp = contentsToViewport(e->pos());
+ ViewTreeItem* atpos = static_cast<ViewTreeItem*>(itemAt(vp));
+
+ if (atpos && !atpos->isSeparator())
+ {
+ if (!atpos->isSelected()) atpos->setHighlighted(true);
+ emit showViewContextMenu(atpos->getView(),e->globalPos());
+ }
+
+ KListView::contentsContextMenuEvent(e);
+}
+
+void ViewTree::contentsWheelEvent(QWheelEvent* e)
+{
+ if (e->delta() > 0)
+ selectUpper(true);
+ else
+ selectLower(true);
+
+ if (selectedItem())
+ {
+ ChatWindow* view = static_cast<ViewTreeItem*>(selectedItem())->getView();
+ if (view) view->adjustFocus();
+ }
+}
+
+void ViewTree::keyPressEvent(QKeyEvent* e)
+{
+ if (e->key() == Key_Up)
+ selectUpper();
+ else if (e->key() == Key_Down)
+ selectLower();
+ else
+ {
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(selectedItem());
+ if (item && item->getView() && item->getView()->isInsertSupported())
+ {
+ KApplication::sendEvent(item->getView()->getTextView(), e);
+ item->getView()->adjustFocus();
+ }
+ else if (item && item->getView() && item->getView()->getType() == ChatWindow::Konsole)
+ {
+ KonsolePanel* panel = static_cast<KonsolePanel*>(item->getView());
+ KApplication::sendEvent(panel->getWidget(), e);
+ item->getView()->adjustFocus();
+ }
+ }
+}
+
+void ViewTree::selectUpper(bool wrap)
+{
+ if (!selectedItem()) return;
+
+ ViewTreeItem* itemAbove = static_cast<ViewTreeItem*>(selectedItem()->itemAbove());
+
+ if (itemAbove)
+ {
+ if (itemAbove->isSeparator())
+ itemAbove = static_cast<ViewTreeItem*>(m_separator->itemAbove());
+
+ setSelected(itemAbove, true);
+ }
+ else
+ {
+ if (wrap) setSelected(lastItem(), true);
+ }
+
+ ensureItemVisible(selectedItem());
+}
+
+void ViewTree::selectLower(bool wrap)
+{
+ if (!selectedItem()) return;
+
+ ViewTreeItem* itemBelow = static_cast<ViewTreeItem*>(selectedItem()->itemBelow());
+
+ if (itemBelow)
+ {
+ if (itemBelow->isSeparator())
+ itemBelow = static_cast<ViewTreeItem*>(m_separator->itemBelow());
+
+ setSelected(itemBelow, true);
+ }
+ else
+ {
+ if (wrap) setSelected(firstChild(), true);
+ }
+
+ ensureItemVisible(selectedItem());
+}
+
+void ViewTree::resizeEvent(QResizeEvent* e)
+{
+ KListView::resizeEvent(e);
+
+ emit sizeChanged();
+}
+
+void ViewTree::findDrop(const QPoint &pos, QListViewItem *&parent, QListViewItem *&after)
+{
+ QPoint p (contentsToViewport(pos));
+
+ QListViewItem *atpos = itemAt(p);
+
+ QListViewItem *above;
+
+ if (!atpos)
+ above = lastItem();
+ else
+ {
+ // Get the closest item before us ('atpos' or the one above, if any).
+ if (p.y() - itemRect(atpos).topLeft().y() < (atpos->height()/2))
+ above = atpos->itemAbove();
+ else
+ above = atpos;
+ }
+
+ ViewTreeItem* itemAbove = static_cast<ViewTreeItem*>(above);
+ ViewTreeItem* dragItem = static_cast<ViewTreeItem*>(selectedItem());
+
+ if (above)
+ {
+ if (dragItem->sortLast())
+ {
+ if (itemAbove->sortLast())
+ {
+ after = itemAbove;
+ parent = after->parent();
+ return;
+
+ }
+ else
+ {
+ after = m_separator;
+ parent = after->parent();
+ return;
+ }
+ }
+ else if (dragItem->depth() == 0)
+ {
+ if (itemAbove->sortLast())
+ {
+ after = m_separator->itemAbove();
+ after = (!after || after->depth() == 0) ? after : after->parent();
+ parent = 0L;
+ return;
+ }
+ else if (above->depth() == dragItem->depth())
+ {
+ after = above;
+ parent = 0L;
+ return;
+ }
+ else
+ {
+ after = above->parent();
+ parent = 0L;
+ return;
+ }
+ }
+ else
+ {
+ if (!itemAbove->getView() || itemAbove->sortLast())
+ {
+ after = getLastChild(dragItem->parent());
+ parent = after ? after->parent() : 0L;
+ return;
+ }
+ else if (itemAbove->getView()->getServer() != dragItem->getView()->getServer())
+ {
+ if (itemIndex(itemAbove) > itemIndex(dragItem))
+ {
+ after = getLastChild(dragItem->parent());
+ parent = after ? after->parent() : 0L;
+ return;
+ }
+ else
+ {
+ after = 0L;
+ parent = dragItem->parent();
+ return;
+ }
+ }
+ else
+ {
+ if (above == dragItem->parent())
+ after = 0L;
+ else
+ after = above;
+
+ parent = dragItem->parent();
+ return;
+ }
+ }
+ }
+ else
+ {
+ if (dragItem->sortLast())
+ {
+ after = m_separator;
+ parent = after->parent();
+ return;
+ }
+ else if (dragItem->depth() == 0)
+ {
+ after = 0L;
+ parent = 0L;
+ return;
+ }
+ else
+ {
+ after = 0L;
+ parent = dragItem->parent();
+ return;
+ }
+ }
+
+ after = 0L;
+ parent = 0L;
+}
+
+QDragObject* ViewTree::dragObject()
+{
+ if (!currentItem())
+ return 0;
+
+ QListViewItem* item = selectedItem();
+
+ if (!item->dragEnabled())
+ return 0;
+
+ return new QStoredDrag("application/x-qlistviewitem", viewport());
+}
+
+QPtrList<ChatWindow> ViewTree::getSortedViewList()
+{
+ QPtrList<ChatWindow> viewList;
+
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(firstChild());
+
+ while (item)
+ {
+ if (!item->isSeparator()) viewList.append(item->getView());
+
+ item = static_cast<ViewTreeItem*>(item->itemBelow());
+ }
+
+ return viewList;
+}
+
+ViewTreeItem* ViewTree::getItemForView(ChatWindow* view)
+{
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(firstChild());
+
+ while (item)
+ {
+ if (item->getView() && item->getView()==view)
+ {
+ return item;
+ break;
+ }
+
+ item = static_cast<ViewTreeItem*>(item->itemBelow());
+ }
+
+ return 0;
+}
+
+ViewTreeItem* ViewTree::getParentItemForView(ChatWindow* view)
+{
+ Server* server = view->getServer();
+
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(firstChild());
+
+ while (item)
+ {
+ if (item->getViewType() == ChatWindow::Status
+ && item->getView()
+ && item->getView()->getServer() == server)
+ {
+ return item;
+ break;
+ }
+
+ item = static_cast<ViewTreeItem*>(item->itemBelow());
+ }
+
+ return 0;
+}
+
+ViewTreeItem* ViewTree::getLastChild(QListViewItem* parent)
+{
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(parent);
+ Server* server = item->getView()->getServer();
+ ViewTreeItem* lastChild = 0;
+
+ while (item->getView() && item->getView()->getServer() == server)
+ {
+ lastChild = item;
+ item = static_cast<ViewTreeItem*>(item->itemBelow());
+ }
+
+ return lastChild;
+}
+
+void ViewTree::paintEmptyArea(QPainter* p, const QRect& rect)
+{
+ KListView::paintEmptyArea(p, rect);
+
+ ViewTreeItem* last = static_cast<ViewTreeItem*>(lastItem());
+
+ if (last && last->isSelected())
+ {
+ int y = last->itemPos() + last->height();
+ int x = visibleWidth();
+
+ if (!rect.contains(x-1, y+2))
+ return;
+
+ QColor bgColor = paletteBackgroundColor();
+ QColor selColor = KGlobalSettings::highlightColor();
+ QColor midColor = last->mixColor(bgColor, selColor);
+
+ p->setPen(selColor);
+ p->drawPoint(x - 1, y);
+ p->drawPoint(x - 2, y);
+ p->drawPoint(x - 1, y + 1);
+ p->setPen(midColor);
+ p->drawPoint(x - 3, y);
+ p->drawPoint(x - 1, y + 2);
+ }
+}
+
+#include "viewtree.moc"
diff --git a/konversation/src/viewtree.h b/konversation/src/viewtree.h
new file mode 100644
index 0000000..948326c
--- /dev/null
+++ b/konversation/src/viewtree.h
@@ -0,0 +1,112 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef VIEWTREE_H
+#define VIEWTREE_H
+
+#include <klistview.h>
+
+
+class ViewTreeItem;
+class ChatWindow;
+
+class ViewTree : public KListView
+{
+ Q_OBJECT
+
+ public:
+ explicit ViewTree(QWidget *parent);
+ ~ViewTree();
+
+ void selectFirstView(bool select);
+ void addView(const QString& name, ChatWindow* view, const QIconSet &iconset, bool select = false, ChatWindow* afterView = 0);
+ void setViewName(ChatWindow* view, const QString& name);
+ void setViewColor(ChatWindow* view, QColor color);
+ void setViewIcon(ChatWindow* view, const QIconSet &iconset);
+
+ void moveViewUp(ChatWindow* view);
+ void moveViewDown(ChatWindow* view);
+ bool canMoveViewUp(ChatWindow* view);
+ bool canMoveViewDown(ChatWindow* view);
+
+ QPtrList<ChatWindow> getSortedViewList();
+
+ public slots:
+ void updateAppearance();
+ void removeView(ChatWindow* view);
+ void selectView(ChatWindow* view);
+ void unHighlight();
+
+ signals:
+ void setViewTreeShown(bool show);
+ void showView(ChatWindow* view);
+ void closeView(ChatWindow* view);
+ void showViewContextMenu(QWidget* widget, const QPoint& point);
+ void sizeChanged();
+ void syncTabBarToTree();
+
+ protected:
+ void contentsMousePressEvent(QMouseEvent* e);
+ void contentsMouseReleaseEvent(QMouseEvent* e);
+ void contentsMouseMoveEvent(QMouseEvent* e);
+ void contentsWheelEvent(QWheelEvent* e);
+ void contentsContextMenuEvent(QContextMenuEvent* e);
+ void keyPressEvent(QKeyEvent* e);
+
+ void resizeEvent(QResizeEvent* e);
+
+ void findDrop(const QPoint &pos, QListViewItem *&parent, QListViewItem *&after);
+ QDragObject* dragObject();
+
+ void paintEmptyArea(QPainter* p, const QRect& rect);
+
+ private slots:
+ void announceSelection(QListViewItem* item);
+ void slotAboutToMoveView();
+ void slotMovedView();
+ void enableCloseButton();
+
+ private:
+ class ToolTip;
+ ViewTree::ToolTip* m_toolTip;
+
+ void toggleSeparator();
+ void selectUpper(bool wrap = false);
+ void selectLower(bool wrap = false);
+
+ ViewTreeItem* getItemForView(ChatWindow* view);
+ ViewTreeItem* getParentItemForView(ChatWindow* view);
+ ViewTreeItem* getLastChild(QListViewItem* parent);
+
+ bool canMoveItemUp(ViewTreeItem* item);
+ bool canMoveItemDown(ViewTreeItem* item);
+
+ bool isAboveIcon(QPoint point, ViewTreeItem* item);
+ void hideCloseButtons(ViewTreeItem* exception = 0);
+
+ ViewTreeItem* m_separator;
+ int m_specialViewCount;
+
+ // Controls whether or not to select the first view added
+ // to the tree.
+ bool m_selectFirstView;
+
+ // Used in mouse handling to determine whether both the press
+ // and the release event occurred over a close button.
+ bool m_pressedAboveCloseButton;
+ ViewTreeItem* m_closeButtonItem;
+ QTimer* m_enableCloseButtonTimer;
+
+ // Used for middle-click close.
+ ViewTreeItem* m_middleClickItem;
+};
+
+#endif
diff --git a/konversation/src/viewtreeitem.cpp b/konversation/src/viewtreeitem.cpp
new file mode 100644
index 0000000..c53699c
--- /dev/null
+++ b/konversation/src/viewtreeitem.cpp
@@ -0,0 +1,490 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#include "viewtreeitem.h"
+#include "konversationapplication.h"
+#include "chatwindow.h"
+#include "preferences.h"
+#include "images.h"
+
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <kstringhandler.h>
+
+
+int ViewTreeItem::s_availableSortIndex = 0;
+
+ViewTreeItem::ViewTreeItem(QListView* parent, const QString& name, ChatWindow* view)
+ : QListViewItem(parent, name)
+{
+ m_sortIndex = s_availableSortIndex;
+ s_availableSortIndex++;
+
+ setView(view);
+ setViewType(view->getType());
+
+ m_color = KGlobalSettings::textColor();
+ m_customColorSet = false;
+
+ setOpen(true);
+ setDragEnabled(true);
+
+ m_isSeparator = false;
+ m_isHighlighted = false;
+ m_isTruncated = false;
+
+ images = KonversationApplication::instance()->images();
+ m_closeButtonShown = false;
+ m_closeButtonEnabled = false;
+}
+
+ViewTreeItem::ViewTreeItem(QListViewItem* parent, const QString& name, ChatWindow* view, int sortIndex)
+ : QListViewItem(parent, name)
+{
+ if (sortIndex != -1)
+ setSortIndex(sortIndex);
+ else
+ {
+ m_sortIndex = s_availableSortIndex;
+ s_availableSortIndex++;
+ }
+
+ setView(view);
+ setViewType(view->getType());
+
+ m_color = KGlobalSettings::textColor();
+ m_customColorSet = false;
+
+ setOpen(true);
+ setDragEnabled(true);
+
+ m_isSeparator = false;
+ m_isHighlighted = false;
+ m_isTruncated = false;
+ m_customColorSet = false;
+
+ images = KonversationApplication::instance()->images();
+ m_closeButtonShown = false;
+ m_closeButtonEnabled = false;
+}
+
+ViewTreeItem::ViewTreeItem(QListViewItem* parent, QListViewItem* afterItem, const QString& name, ChatWindow* view)
+ : QListViewItem(parent, afterItem, name)
+{
+ m_sortIndex = s_availableSortIndex;
+ s_availableSortIndex++;
+
+ setView(view);
+ setViewType(view->getType());
+
+ m_color = KGlobalSettings::textColor();
+ m_customColorSet = false;
+
+ setOpen(true);
+ setDragEnabled(true);
+
+ m_isSeparator = false;
+ m_isHighlighted = false;
+ m_isTruncated = false;
+ m_customColorSet = false;
+
+ images = KonversationApplication::instance()->images();
+ m_closeButtonShown = false;
+ m_closeButtonEnabled = false;
+}
+
+ViewTreeItem::ViewTreeItem(QListView* parent) : QListViewItem(parent)
+{
+ setView(0);
+ setOpen(true);
+ setDragEnabled(false);
+
+ m_isSeparator = true;
+ m_isHighlighted = false;
+ m_isTruncated = false;
+}
+
+ViewTreeItem::~ViewTreeItem()
+{
+}
+
+void ViewTreeItem::setSortIndex(int newSortIndex)
+{
+ m_sortIndex = newSortIndex;
+}
+
+int ViewTreeItem::getSortIndex() const
+{
+ return m_sortIndex;
+}
+
+void ViewTreeItem::setName(const QString& name)
+{
+ setText(0, name);
+}
+
+QString ViewTreeItem::getName() const
+{
+ return text(0);
+}
+
+bool ViewTreeItem::isTruncated() const
+{
+ return m_isTruncated;
+}
+
+void ViewTreeItem::setView(ChatWindow* view)
+{
+ m_view = view;
+}
+
+ChatWindow* ViewTreeItem::getView() const
+{
+ return m_view;
+}
+
+void ViewTreeItem::setViewType(ChatWindow::WindowType viewType)
+{
+ m_viewType = viewType;
+}
+
+ChatWindow::WindowType ViewTreeItem::getViewType() const
+{
+ return m_viewType;
+}
+
+void ViewTreeItem::setColor(QColor color)
+{
+ if (color != m_color)
+ {
+ m_color = color;
+ m_customColorSet = true;
+ repaint();
+ }
+}
+
+QColor ViewTreeItem::getColor() const
+{
+ if (!m_customColorSet)
+ {
+ if (Preferences::inputFieldsBackgroundColor())
+ return Preferences::color(Preferences::ChannelMessage);
+ else
+ return KGlobalSettings::textColor();
+ }
+ else
+ return m_color;
+}
+
+void ViewTreeItem::setIcon(const QPixmap& pm)
+{
+ m_oldPixmap = pm;
+ if (!m_closeButtonShown) setPixmap(0, pm);
+}
+
+void ViewTreeItem::setHighlighted(bool highlight)
+{
+ if (m_isHighlighted != highlight)
+ {
+ m_isHighlighted = highlight;
+ repaint();
+ }
+}
+
+void ViewTreeItem::setCloseButtonShown(bool show)
+{
+ if (!show && !m_closeButtonShown)
+ return;
+
+ if (show && m_closeButtonShown)
+ return;
+
+ if (show && !m_closeButtonShown)
+ {
+ setPixmap(0, images->getDisabledCloseIcon());
+ m_closeButtonShown = true;
+ m_closeButtonEnabled = false;
+ }
+
+ if (!show && m_closeButtonShown)
+ {
+ setPixmap(0, m_oldPixmap);
+ m_closeButtonShown = false;
+ m_closeButtonEnabled = false;
+ }
+}
+
+void ViewTreeItem::setCloseButtonEnabled()
+{
+ if (m_closeButtonShown)
+ {
+ m_closeButtonEnabled = true;
+ setPixmap(0, images->getCloseIcon());
+ }
+}
+
+bool ViewTreeItem::getCloseButtonEnabled()
+{
+ return m_closeButtonEnabled;
+}
+
+bool ViewTreeItem::sortLast() const
+{
+ if (!m_isSeparator)
+ {
+ if (getViewType() == ChatWindow::Status
+ || getViewType() == ChatWindow::Channel
+ || getViewType() == ChatWindow::Query
+ || getViewType() == ChatWindow::RawLog
+ || getViewType() == ChatWindow::DccChat
+ || getViewType() == ChatWindow::ChannelList)
+ {
+ return false;
+ }
+ else
+ return true;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+bool ViewTreeItem::isSeparator() const
+{
+ return m_isSeparator;
+}
+
+int ViewTreeItem::compare(QListViewItem *i, int /* col */, bool /* ascending */) const
+{
+ ViewTreeItem* item = static_cast<ViewTreeItem*>(i);
+
+ if (sortLast() == item->sortLast())
+ {
+ if (isSeparator() && !item->isSeparator())
+ return -1;
+ if (!isSeparator() && item->isSeparator())
+ return 1;
+ else
+ {
+ if (getSortIndex() == item->getSortIndex())
+ return 0;
+ else if (getSortIndex() < item->getSortIndex())
+ return -1;
+ else
+ return 1;
+ }
+ }
+ else if (sortLast())
+ return 1;
+ else
+ return -1;
+}
+
+void ViewTreeItem::setup()
+{
+ widthChanged();
+
+ if (!m_isSeparator)
+ {
+ int LED_ICON_SIZE = 14;
+ int MARGIN = 2;
+
+ QRect textRect = listView()->fontMetrics().boundingRect(0, 0, /*width=*/1, 500000, Qt::AlignAuto | Qt::AlignTop | Qt::ShowPrefix, text(/*column=*/0));
+ int height = MARGIN + kMax(LED_ICON_SIZE, textRect.height()) + MARGIN;
+ setHeight(height);
+ }
+ else
+ setHeight(11);
+}
+
+QColor ViewTreeItem::mixColor(const QColor &color1, const QColor &color2)
+{
+ QColor mixedColor;
+ mixedColor.setRgb( (color1.red() + color2.red()) / 2,
+ (color1.green() + color2.green()) / 2,
+ (color1.blue() + color2.blue()) / 2 );
+ return mixedColor;
+}
+
+void ViewTreeItem::paintFocus(QPainter* /* p */, const QColorGroup& /* cg */, const QRect& /* r */)
+{
+ // Do nothing.
+ return;
+}
+
+void ViewTreeItem::paintCell(QPainter* p, const QColorGroup& /* cg */, int /* column */, int width, int /* align */)
+{
+ // Workaround a Qt bug:
+ // When the splitter is moved to hide the tree view and then the application is restarted,
+ // Qt try to draw items with a negative size.
+ if (width <= 0) return;
+
+ int LED_ICON_SIZE = 14;
+ int MARGIN = 2;
+
+ // Bufferize the drawing of items.
+ QPixmap buffer(width, height());
+ QPainter painter(&buffer);
+
+ QColor textColor = isSelected() ? KGlobalSettings::highlightedTextColor() : getColor();
+ QColor background = isSelected() ? KGlobalSettings::highlightColor() : listView()->paletteBackgroundColor();
+ if (m_isHighlighted) background = Preferences::inputFieldsBackgroundColor()
+ ? Preferences::color(Preferences::AlternateBackground) : KGlobalSettings::alternateBackgroundColor();
+
+ // Fill in background.
+ painter.fillRect(0, 0, width, height(), background);
+
+ QColor bgColor = listView()->paletteBackgroundColor();
+ QColor selColor = m_isHighlighted ? background : KGlobalSettings::highlightColor();
+ QColor midColor = mixColor(bgColor, selColor);
+
+ int iconWidth = pixmap(0) ? LED_ICON_SIZE : 0;
+ int textWidth = width - MARGIN - iconWidth - MARGIN - MARGIN;
+
+ if (!m_isSeparator)
+ {
+ // Draw the rounded rectangle.
+ QRect textRect = listView()->fontMetrics().boundingRect(0, 0, /*width=*/1, 500000, Qt::AlignAuto | Qt::AlignTop | Qt::ShowPrefix, text(/*column=*/0));
+ int xRound = MARGIN;
+ int yRound = MARGIN;
+ int hRound = height() - 2 * MARGIN;
+ int wRound = kMin(LED_ICON_SIZE + MARGIN + textRect.width() + hRound/2, width - MARGIN - MARGIN);
+
+ if (wRound > 0)
+ {
+ QPixmap buffer(wRound * 2, hRound * 2);
+ buffer.fill(background);
+ QPainter pBuffer(&buffer);
+ QColor colorRound = background;
+ pBuffer.setPen(colorRound);
+ pBuffer.setBrush(colorRound);
+
+ // If the rectangle is higher than wide, don't overlap ellipses.
+ if (wRound > hRound)
+ {
+ pBuffer.drawEllipse(0, 0, hRound * 2, hRound * 2);
+ pBuffer.drawEllipse(wRound * 2 - hRound * 2, 0, hRound * 2, hRound * 2);
+ pBuffer.fillRect(hRound*2/2, 0, wRound * 2 - hRound * 2, hRound * 2, colorRound);
+ }
+ else
+ pBuffer.drawEllipse(0, 0, wRound * 2, hRound * 2);
+
+ pBuffer.end();
+ QImage imageToScale = buffer.convertToImage();
+ QPixmap pmScaled;
+ pmScaled.convertFromImage(imageToScale.smoothScale(wRound, hRound));
+ painter.drawPixmap(xRound, yRound, pmScaled);
+ textWidth -= hRound/2;
+ }
+
+ if (isSelected() || m_isHighlighted)
+ {
+ painter.setPen(bgColor);
+ painter.drawPoint(0, 0);
+ painter.drawPoint(1, 0);
+ painter.drawPoint(0, 1);
+ painter.drawPoint(0, height() - 1);
+ painter.drawPoint(1, height() - 1);
+ painter.drawPoint(0, height() - 2);
+ painter.setPen(midColor);
+ painter.drawPoint(2, 0);
+ painter.drawPoint(0, 2);
+ painter.drawPoint(2, height() - 1);
+ painter.drawPoint(0, height() - 3);
+ }
+ }
+
+ if (m_isHighlighted)
+ {
+ selColor = KGlobalSettings::highlightColor();
+ midColor = mixColor(bgColor, selColor);
+ }
+
+ if (itemBelow() && itemBelow()->isSelected())
+ {
+ painter.setPen(selColor);
+ painter.drawPoint(width - 1, height() - 1);
+ painter.drawPoint(width - 2, height() - 1);
+ painter.drawPoint(width - 1, height() - 2);
+ painter.setPen(midColor);
+ painter.drawPoint(width - 3, height() - 1);
+ painter.drawPoint(width - 1, height() - 3);
+ }
+
+ if (itemAbove() && itemAbove()->isSelected())
+ {
+ painter.setPen(selColor);
+ painter.drawPoint(width - 1, 0);
+ painter.drawPoint(width - 2, 0);
+ painter.drawPoint(width - 1, 1);
+ painter.setPen(midColor);
+ painter.drawPoint(width - 3, 0);
+ painter.drawPoint(width - 1, 2);
+ }
+
+ if (!m_isSeparator)
+ {
+ // Draw icon.
+ if (pixmap(0))
+ {
+ int yPixmap = (height() - pixmap(0)->height()) / 2;
+
+ int xPixmap = MARGIN;
+
+ if (pixmap(0)->width() < LED_ICON_SIZE)
+ xPixmap = MARGIN + ((LED_ICON_SIZE - pixmap(0)->width()) / 2);
+
+ painter.drawPixmap(xPixmap, yPixmap, *pixmap(0));
+ }
+
+ // Enough space left to draw icon and text?
+ if (textWidth > 0)
+ {
+ int xText = MARGIN;
+
+ // Shift by icon width.
+ if (pixmap(0))
+ xText = MARGIN + LED_ICON_SIZE + MARGIN;
+
+ QString text = getName();
+
+ if (p->fontMetrics().width(text) > textWidth)
+ {
+ m_isTruncated = true;
+ text = KStringHandler::rPixelSqueeze(text, p->fontMetrics(), textWidth);
+ }
+ else
+ m_isTruncated = false;
+
+ painter.setPen(textColor);
+ painter.setFont(listView()->font());
+ painter.drawText(xText, 0, textWidth, height(), Qt::AlignAuto | Qt::AlignVCenter, text);
+ }
+ }
+ else
+ {
+ QColor lineColor = Preferences::inputFieldsBackgroundColor()
+ ? Preferences::color(Preferences::AlternateBackground) : KGlobalSettings::alternateBackgroundColor();
+ painter.setPen(lineColor);
+ painter.drawLine(0, 5, width, 5);
+ }
+
+ painter.end();
+
+ p->drawPixmap(0, 0, buffer);
+}
diff --git a/konversation/src/viewtreeitem.h b/konversation/src/viewtreeitem.h
new file mode 100644
index 0000000..2d5312b
--- /dev/null
+++ b/konversation/src/viewtreeitem.h
@@ -0,0 +1,92 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Eike Hein <hein@kde.org>
+*/
+
+#ifndef VIEWTREEITEM_H
+#define VIEWTREEITEM_H
+
+#include "chatwindow.h"
+
+#include <qobject.h>
+#include <qlistview.h>
+#include <qtooltip.h>
+#include <qguardedptr.h>
+#include <qpixmap.h>
+
+
+class ChatWindow;
+class Images;
+
+class ViewTreeItem : public QListViewItem
+{
+
+ public:
+ ViewTreeItem(QListView* parent, const QString& name, ChatWindow* view);
+ ViewTreeItem(QListViewItem* parent, const QString& name, ChatWindow* view, int sortIndex = -1);
+ ViewTreeItem(QListViewItem* parent, QListViewItem* afterItem, const QString& name, ChatWindow* view);
+ // Minimal constructor for separator items.
+ explicit ViewTreeItem(QListView* parent);
+ ~ViewTreeItem();
+
+ void setSortIndex(int newSortIndex);
+ int getSortIndex() const;
+
+ void setName(const QString& name);
+ QString getName() const;
+ bool isTruncated() const;
+
+ void setView(ChatWindow* view);
+ ChatWindow* getView() const;
+
+ ChatWindow::WindowType getViewType() const;
+ void setViewType(ChatWindow::WindowType);
+
+ void setColor(QColor color);
+ QColor getColor() const;
+
+ void setIcon(const QPixmap& pm);
+
+ void setHighlighted(bool highlight);
+ void setCloseButtonShown(bool show);
+ void setCloseButtonEnabled();
+ bool getCloseButtonEnabled();
+
+ bool sortLast() const;
+ bool isSeparator() const;
+ int compare(QListViewItem* i, int col, bool ascending) const;
+
+ void setup();
+ void paintFocus(QPainter* p, const QColorGroup& cg, const QRect& r);
+ void paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align);
+
+ QColor mixColor(const QColor &color1, const QColor &color2);
+
+ private:
+ uint m_sortIndex;
+ static int s_availableSortIndex;
+ QGuardedPtr<ChatWindow> m_view;
+ ChatWindow::WindowType m_viewType;
+ QColor m_color;
+
+ bool m_isSeparator;
+ bool m_isHighlighted;
+ bool m_isTruncated;
+ bool m_customColorSet;
+
+ Images* images;
+
+ QPixmap m_closeButton;
+ QPixmap m_disabledCloseButton;
+ QPixmap m_oldPixmap;
+ bool m_closeButtonShown;
+ bool m_closeButtonEnabled;
+};
+
+#endif
diff --git a/konversation/src/warnings_preferences.cpp b/konversation/src/warnings_preferences.cpp
new file mode 100644
index 0000000..c775ba7
--- /dev/null
+++ b/konversation/src/warnings_preferences.cpp
@@ -0,0 +1,185 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+ Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
+*/
+
+
+#include "warnings_preferences.h"
+#include "konviconfigdialog.h"
+
+#include <qlistview.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <klistview.h>
+
+
+Warnings_Config::Warnings_Config( QWidget* parent, const char* name, WFlags fl )
+ : Warnings_ConfigUI( parent, name, fl )
+{
+ dialogListView->setSorting(1);
+ loadSettings();
+ connect(dialogListView, SIGNAL(clicked(QListViewItem *)), this, SIGNAL(modified()));
+}
+
+Warnings_Config::~Warnings_Config()
+{
+}
+
+void Warnings_Config::restorePageToDefaults()
+{
+
+ QCheckListItem* item=static_cast<QCheckListItem*>(dialogListView->itemAtIndex(0));
+ bool changed=false;
+ while(item)
+ {
+ if(!item->isOn()) {
+ item->setOn(true);
+ changed=true;
+ }
+ item=static_cast<QCheckListItem*>(item->itemBelow());
+ }
+ if(changed) {
+ emit modified();
+ }
+}
+
+void Warnings_Config::saveSettings()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("Notification Messages");
+
+ // prepare list
+ QString warningsChecked;
+
+ QCheckListItem* item=static_cast<QCheckListItem*>(dialogListView->itemAtIndex(0));
+ int i = 0;
+ while(item)
+ {
+ // save state of this item in hasChanged() list
+ warningsChecked+=item->isOn();
+
+ if (item->text(2) == "LargePaste" || item->text(2) == "Invitation")
+ {
+ if (item->isOn())
+ {
+ config->writeEntry(item->text(2), 1);
+ }
+ else
+ {
+ QString state = config->readEntry(item->text(2));
+
+ if (!state.isEmpty() && (state == "yes" || state == "no"))
+ config->writeEntry(item->text(2), state);
+ else
+ config->writeEntry(item->text(2), "yes");
+ }
+ }
+ else
+ {
+ config->writeEntry(item->text(2),item->isOn() ? "1" : "0");
+ }
+
+ item=static_cast<QCheckListItem*>(item->itemBelow());
+ ++i;
+ }
+
+ // remember checkbox state for hasChanged()
+ m_oldWarningsChecked=warningsChecked;
+}
+
+void Warnings_Config::loadSettings()
+{
+ QStringList dialogDefinitions;
+ QString flagNames = "Invitation,SaveLogfileNote,ClearLogfileQuestion,CloseQueryAfterIgnore,ReconnectWithDifferentServer,ReuseExistingConnection,QuitServerTab,QuitChannelTab,QuitQueryTab,ChannelListNoServerSelected,HideMenuBarWarning,ChannelListWarning,LargePaste,systemtrayquitKonversation,IgnoreNick,UnignoreNick,QuitWithActiveDccTransfers";
+ dialogDefinitions.append(i18n("Automatically join channel on invite"));
+ dialogDefinitions.append(i18n("Notice that saving logfiles will save whole file"));
+ dialogDefinitions.append(i18n("Ask before deleting logfile contents"));
+ dialogDefinitions.append(i18n("Ask about closing queries after ignoring the nickname"));
+ dialogDefinitions.append(i18n("Ask before switching a connection to a network to a different server"));
+ dialogDefinitions.append(i18n("Ask before creating another connection to the same network or server"));
+ dialogDefinitions.append(i18n("Close server tab"));
+ dialogDefinitions.append(i18n("Close channel tab"));
+ dialogDefinitions.append(i18n("Close query tab"));
+ dialogDefinitions.append(i18n("The channel list can only be opened from server-aware tabs"));
+ dialogDefinitions.append(i18n("Warning on hiding the main window menu"));
+ dialogDefinitions.append(i18n("Warning on high traffic with channel list"));
+ dialogDefinitions.append(i18n("Warning on pasting large portions of text"));
+ dialogDefinitions.append(i18n("Warning on quitting Konversation"));
+ dialogDefinitions.append(i18n("Ignore"));
+ dialogDefinitions.append(i18n("Unignore"));
+ dialogDefinitions.append(i18n("Warn before quitting with active DCC file transfers"));
+ QCheckListItem *item;
+ dialogListView->clear();
+
+ KConfig* config = kapp->config();
+ config->setGroup("Notification Messages");
+ QString flagName;
+ for(unsigned int i=0; i<dialogDefinitions.count() ;i++)
+ {
+ item=new QCheckListItem(dialogListView,dialogDefinitions[i],QCheckListItem::CheckBox);
+ item->setText(1,dialogDefinitions[i]);
+ flagName = flagNames.section(",",i,i);
+ item->setText(2,flagName);
+
+ if (flagName == "LargePaste" || flagName == "Invitation")
+ {
+ QString state = config->readEntry(flagName);
+
+ if (state == "yes" || state == "no")
+ item->setOn(false);
+ else
+ item->setOn(true);
+ }
+ else
+ {
+ item->setOn(config->readBoolEntry(flagName,true));
+ }
+ }
+ // remember checkbox state for hasChanged()
+ m_oldWarningsChecked=currentWarningsChecked();
+}
+
+// get a list of checked/unchecked items for hasChanged()
+QString Warnings_Config::currentWarningsChecked()
+{
+ // prepare list
+ QString newList;
+
+ // get first checklist item
+ QListViewItem* item=dialogListView->firstChild();
+ while(item)
+ {
+ // save state of this item in hasChanged() list
+ newList+=(static_cast<QCheckListItem*>(item)->isOn()) ? "1" : "0";
+ item=item->itemBelow();
+ }
+ // return list
+ return newList;
+}
+
+bool Warnings_Config::hasChanged()
+{
+ return(m_oldWarningsChecked!=currentWarningsChecked());
+}
+
+/*
+ * Sets the strings of the subwidgets using the current
+ * language.
+ */
+void Warnings_Config::languageChange()
+{
+ loadSettings();
+}
+
+#include "warnings_preferences.moc"
diff --git a/konversation/src/warnings_preferences.h b/konversation/src/warnings_preferences.h
new file mode 100644
index 0000000..4d783cd
--- /dev/null
+++ b/konversation/src/warnings_preferences.h
@@ -0,0 +1,48 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+ Copyright (C) 2006 John Tapsell <johnflux@gmail.com>
+*/
+
+#ifndef WARNINGS_CONFIG_H
+#define WARNINGS_CONFIG_H
+
+#include "warnings_preferencesui.h"
+#include "konvisettingspage.h"
+
+class KListView;
+class QListViewItem;
+
+class Warnings_Config : public Warnings_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit Warnings_Config( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ ~Warnings_Config();
+
+ virtual void restorePageToDefaults();
+ virtual void saveSettings();
+ virtual void loadSettings();
+
+ virtual bool hasChanged();
+
+ public slots:
+ virtual void languageChange();
+
+ protected:
+ QString currentWarningsChecked(); // for hasChanged()
+
+ QString m_oldWarningsChecked; // for hasChanged()
+
+ signals:
+ void modified();
+};
+
+#endif // WARNINGS_CONFIG_H
diff --git a/konversation/src/warnings_preferencesui.ui b/konversation/src/warnings_preferencesui.ui
new file mode 100644
index 0000000..2f189cc
--- /dev/null
+++ b/konversation/src/warnings_preferencesui.ui
@@ -0,0 +1,50 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Warnings_ConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Warnings_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>570</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Warning Dialogs to Show</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>dialogListView</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+</widget>
+<tabstops>
+ <tabstop>dialogListView</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/konversation/src/watchednicknames_preferences.cpp b/konversation/src/watchednicknames_preferences.cpp
new file mode 100644
index 0000000..74f94ec
--- /dev/null
+++ b/konversation/src/watchednicknames_preferences.cpp
@@ -0,0 +1,420 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#include "watchednicknames_preferences.h"
+#include "watchednicknames_preferencesui.h"
+#include "config/preferences.h"
+#include "konversationapplication.h"
+#include "konversationmainwindow.h"
+#include "valuelistviewitem.h"
+
+#include <qlabel.h>
+#include <qcombobox.h>
+#include <qcheckbox.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kpushbutton.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+
+WatchedNicknames_Config::WatchedNicknames_Config(QWidget *parent, const char *name)
+ : WatchedNicknames_ConfigUI(parent, name)
+{
+ notifyListView->setRenameable(0,false);
+ notifyListView->setSorting(-1);
+ // reset flag to defined state (used to block signals when just selecting a new item)
+ newItemSelected=false;
+
+ loadSettings();
+
+ connect(kcfg_UseNotify,SIGNAL (toggled(bool)),this,SLOT (checkIfEmptyListview(bool)) );
+ connect(newButton,SIGNAL (clicked()),this,SLOT (newNotify()) );
+ connect(removeButton,SIGNAL (clicked()),this,SLOT (removeNotify()) );
+ connect(notifyListView,SIGNAL (selectionChanged(QListViewItem*)),this,SLOT (entrySelected(QListViewItem*)) );
+ connect(notifyListView,SIGNAL (clicked(QListViewItem*)),this,SLOT (entrySelected(QListViewItem*)) );
+
+ connect(networkDropdown,SIGNAL (activated(const QString&)),this,SLOT (networkChanged(const QString&)) );
+ connect(nicknameInput,SIGNAL (textChanged(const QString&)),this,SLOT (nicknameChanged(const QString&)) );
+
+ connect(KonversationApplication::instance(), SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)),
+ this, SLOT(updateNetworkNames()));
+}
+
+WatchedNicknames_Config::~WatchedNicknames_Config()
+{
+}
+
+void WatchedNicknames_Config::restorePageToDefaults()
+{
+}
+
+// fill in the notify listview with groups and nicknames
+void WatchedNicknames_Config::loadSettings()
+{
+ // cleanup, so we won't add duplicate items
+ notifyListView->clear();
+ networkDropdown->clear();
+ // make sure all widgets are disabled
+ notifyListView->clearSelection();
+ enableEditWidgets(false);
+
+ // get list of server networks
+ Konversation::ServerGroupList serverGroupList = Preferences::serverGroupList();
+
+ // iterate through all networks in the server group list
+ for(unsigned int gIndex=0;gIndex<serverGroupList.count();gIndex++)
+ {
+ // add server group branch to the notify listview so we can add notify items
+ addNetworkBranch(serverGroupList[gIndex]);
+ }
+ // remember current list for hasChanged()
+ m_oldNotifyList=currentNotifyList();
+}
+
+// adds a new network branch to the listview
+void WatchedNicknames_Config::addNetworkBranch(Konversation::ServerGroupSettingsPtr serverGroupList)
+{
+ // get the current notify list and an iterator
+ QMap<int, QStringList> notifyList = Preferences::notifyList();
+
+ ValueListViewItem* groupItem=new ValueListViewItem(serverGroupList->id(),notifyListView,notifyListView->lastChild(),serverGroupList->name());
+ // get the group iterator to find all servers in the group
+ QMapConstIterator<int, QStringList> groupIt=notifyList.find(serverGroupList->id());
+
+ // get list of nicks for the current group
+ QStringList nicks=groupIt.data();
+ // add group to dropdown list
+ networkDropdown->insertItem(serverGroupList->name(),0);
+ // add nicknames to group branch (reverse order again)
+ for(unsigned int index=nicks.count();index;index--)
+ {
+ new KListViewItem(groupItem,nicks[index-1]);
+ } // for
+ // unfold group branch
+ notifyListView->setOpen(groupItem,true);
+}
+
+// save list of notifies permanently, taken from the listview
+void WatchedNicknames_Config::saveSettings()
+{
+ // create new in-memory notify structure
+ QMap<int,QStringList> notifyList;
+
+ // get first notify group
+ KListView* listView=notifyListView;
+ QListViewItem* group=listView->firstChild();
+
+ // loop as long as there are more groups in the listview
+ while(group)
+ {
+ int groupId=static_cast<ValueListViewItem*>(group)->getValue();
+
+ // later contains all nicks separated by blanks
+ QString nicks;
+ // get first nick in the group
+ QListViewItem* nick=group->firstChild();
+ // loop as long as there are still nicks in this group
+ while(nick)
+ {
+ // add nick to string container and add a blank
+ nicks+=nick->text(0)+' ';
+ // get next nick in the group
+ nick=nick->nextSibling();
+ } // while
+
+ // write nick list to in-memory notify qstringlist
+ notifyList.insert(groupId,QStringList::split(' ',nicks.stripWhiteSpace()));
+ // get next group
+ group=group->nextSibling();
+ } // while
+
+ // update in-memory notify list
+ Preferences::setNotifyList(notifyList);
+ static_cast<KonversationApplication*>(kapp)->saveOptions(false);
+
+ // remember current list for hasChanged()
+ m_oldNotifyList=currentNotifyList();
+}
+
+// returns the currently edited notify list
+QStringList WatchedNicknames_Config::currentNotifyList()
+{
+ // prepare list
+ QStringList newList;
+
+ // get first item
+ KListView* listView=notifyListView;
+ QListViewItem* item=listView->firstChild();
+
+ // loop as long as there are more groups in the listview
+ while(item)
+ {
+ newList.append(item->text(0));
+ item=item->itemBelow();
+ } // while
+
+ // return list
+ return newList;
+}
+
+bool WatchedNicknames_Config::hasChanged()
+{
+ // return true if something has changed
+ return(m_oldNotifyList!=currentNotifyList());
+}
+
+// slots
+
+void WatchedNicknames_Config::updateNetworkNames()
+{
+ // get first notify group
+ KListView* listView=notifyListView;
+ QListViewItem* group=listView->firstChild();
+
+ // make sure all widgets are disabled
+ listView->clearSelection();
+ enableEditWidgets(false);
+
+ // kill dropdown list, the networks might have been renamed
+ networkDropdown->clear();
+
+ // loop as long as there are more groups in the listview
+ while(group)
+ {
+ // get the group id from the listview item
+ int groupId=static_cast<ValueListViewItem*>(group)->getValue();
+ // get the name of the group by having a look at the serverGroupSettings
+ Konversation::ServerGroupSettingsPtr serverGroup=Preferences::serverGroupById(groupId);
+
+ // check if the server group still exists
+ if(serverGroup)
+ {
+ // get the new name of the server group
+ QString serverGroupName=serverGroup->name();
+
+ // update the name of the group in the listview
+ group->setText(0,serverGroupName);
+
+ // re-add group to dropdown list
+ networkDropdown->insertItem(serverGroupName,-1);
+ // get next group
+ group=group->nextSibling();
+ }
+ else
+ {
+ // get the next group from the listview
+ QListViewItem* tmp=group->nextSibling();
+ // remove the group
+ delete group;
+ // set the current group
+ group=tmp;
+ }
+ } // while
+
+ // get list of server networks
+ Konversation::ServerGroupList serverGroupList = Preferences::serverGroupList();
+
+ // iterate through all networks in the server group list in reverse order
+ // to find if any new networks have been added
+ for(unsigned int gIndex=serverGroupList.count();gIndex;gIndex--)
+ {
+ // try to find the network id in the listview
+ if(!getItemById(listView,serverGroupList[gIndex-1]->id()))
+ // add new server group branch to the notify listview
+ addNetworkBranch(serverGroupList[gIndex-1]);
+ }
+ // remember current list for hasChanged()
+ m_oldNotifyList=currentNotifyList();
+
+}
+
+// check if an item with the given id exists in the listview
+QListViewItem* WatchedNicknames_Config::getItemById(QListView* listView,int id)
+{
+ // get the first item in the listview
+ QListViewItem* lookItem=listView->firstChild();
+ // look for an item with the given id
+ while(lookItem)
+ {
+ // return item if it matches
+ if(static_cast<ValueListViewItem*>(lookItem)->getValue()==id) return lookItem;
+ // otherwise jump to the next group
+ lookItem=lookItem->nextSibling();
+ } // while
+
+ // not found, return 0
+ return 0;
+}
+
+// helper function to disable "New" button on empty listview
+void WatchedNicknames_Config::checkIfEmptyListview(bool state)
+{
+ // only enable "New" button if there is at least one group in the list
+ if(!notifyListView->childCount()) state=false;
+ newButton->setEnabled(state);
+}
+
+// add new notify entry
+void WatchedNicknames_Config::newNotify()
+{
+ // get listview object and possible first selected item
+ KListView* listView=notifyListView;
+ QListViewItem* item=listView->selectedItem();
+
+ // if there was an item selected, try to find the group it belongs to,
+ // so the newly created item will go into the same group, otherwise
+ // just create the new entry inside of the first group
+ if(item)
+ {
+ if(item->parent()) item=item->parent();
+ }
+ else
+ item=listView->firstChild();
+
+ // finally insert new item
+ item=new QListViewItem(item,i18n("New"));
+ // make this item the current and selected item
+ item->setSelected(true);
+ listView->setCurrentItem(item);
+ // update network and nickname inputs
+ entrySelected(item);
+ // unfold group branch in case it was empty before
+ listView->ensureItemVisible(item);
+ nicknameInput->setFocus();
+ nicknameInput->selectAll();
+ // tell the config system that something has changed
+ emit modified();
+}
+
+// remove a notify entry from the listview
+void WatchedNicknames_Config::removeNotify()
+{
+ // get listview pointer and the selected item
+ KListView* listView=notifyListView;
+ QListViewItem* item=listView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // check which item to highlight after we deleted the chosen one
+ QListViewItem* itemAfter=item->itemBelow();
+ if(!itemAfter) itemAfter=item->itemAbove();
+ delete(item);
+
+ // if there was an item, highlight it
+ if(itemAfter)
+ {
+ itemAfter->setSelected(true);
+ listView->setCurrentItem(itemAfter);
+ // update input widgets
+ entrySelected(itemAfter);
+ }
+ // tell the config system that something has changed
+ emit modified();
+ }
+}
+
+// what to do when the user selects an entry
+void WatchedNicknames_Config::entrySelected(QListViewItem* notifyEntry)
+{
+ // play it safe, assume disabling all widgets first
+ bool enabled=false;
+
+ // if there actually was an entry selected ...
+ if(notifyEntry)
+ {
+ // is this entry a nickname?
+ QListViewItem* group=notifyEntry->parent();
+ if(group)
+ {
+ // all edit widgets may be enabled
+ enabled=true;
+ // tell all now emitted signals that we just clicked on a new item, so they should
+ // not emit the modified() signal.
+ newItemSelected=true;
+ // copy network name and nickname to edit widgets
+ nicknameInput->setText(notifyEntry->text(0));
+ networkDropdown->setCurrentText(group->text(0));
+ // all signals will now emit the modified() signal again
+ newItemSelected=false;
+ }
+ }
+ enableEditWidgets(enabled);
+}
+
+// enable/disable edit widgets
+void WatchedNicknames_Config::enableEditWidgets(bool enabled)
+{
+ removeButton->setEnabled(enabled);
+ networkLabel->setEnabled(enabled);
+ networkDropdown->setEnabled(enabled);
+ nicknameLabel->setEnabled(enabled);
+ nicknameInput->setEnabled(enabled);
+}
+
+// user changed the network this nickname is on
+void WatchedNicknames_Config::networkChanged(const QString& newNetwork)
+{
+ // get listview pointer and selected entry
+ KListView* listView=notifyListView;
+ QListViewItem* item=listView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // get group the nickname is presently associated to
+ QListViewItem* group=item->parent();
+ // did the user actually change anything?
+ if(group && group->text(0)!=newNetwork)
+ {
+ // find the branch the new network is in
+ QListViewItem* lookGroup=listView->firstChild();
+ while(lookGroup && (lookGroup->text(0)!=newNetwork)) lookGroup=lookGroup->nextSibling();
+ // if it was found (should never fail)
+ if(lookGroup)
+ {
+ // deselect nickname, unlink it and relink it to the new network
+ item->setSelected(false);
+ group->takeItem(item);
+ lookGroup->insertItem(item);
+ // make the moved nickname current and selected item
+ item->setSelected(true);
+ listView->setCurrentItem(item);
+ // unfold group branch in case it was empty before
+ listView->setOpen(lookGroup,true);
+ // tell the config system that something has changed
+ if(!newItemSelected) emit modified();
+ }
+ }
+ }
+}
+
+// the user edited the nickname
+void WatchedNicknames_Config::nicknameChanged(const QString& newNickname)
+{
+ // get listview pointer and selected item
+ KListView* listView=notifyListView;
+ QListViewItem* item=listView->selectedItem();
+
+ // sanity check
+ if(item)
+ {
+ // rename item
+ item->setText(0,newNickname);
+ // tell the config system that something has changed
+ if(!newItemSelected) emit modified();
+ }
+}
+
+#include "watchednicknames_preferences.moc"
diff --git a/konversation/src/watchednicknames_preferences.h b/konversation/src/watchednicknames_preferences.h
new file mode 100644
index 0000000..71b8be7
--- /dev/null
+++ b/konversation/src/watchednicknames_preferences.h
@@ -0,0 +1,59 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+/*
+ Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
+*/
+
+#ifndef WATCHEDNICKNAMES_CONFIG_H
+#define WATCHEDNICKNAMES_CONFIG_H
+
+#include "konvisettingspage.h"
+#include "servergroupsettings.h"
+#include "watchednicknames_preferencesui.h"
+
+
+class QListView;
+class QListViewItem;
+
+class WatchedNicknames_Config : public WatchedNicknames_ConfigUI, public KonviSettingsPage
+{
+ Q_OBJECT
+
+ public:
+ explicit WatchedNicknames_Config(QWidget *parent = 0, const char *name = 0);
+ ~WatchedNicknames_Config();
+
+ virtual void saveSettings();
+ virtual void loadSettings();
+ virtual void restorePageToDefaults();
+
+ virtual bool hasChanged();
+
+ signals:
+ void modified();
+
+ protected slots:
+ void checkIfEmptyListview(bool state);
+ void newNotify();
+ void removeNotify();
+ void entrySelected(QListViewItem* notifyEntry);
+ void networkChanged(const QString& newNetwork);
+ void nicknameChanged(const QString& newNickname);
+ void updateNetworkNames();
+
+ protected:
+ void enableEditWidgets(bool enabled);
+ QStringList currentNotifyList(); // for hasChanged()
+ void addNetworkBranch(Konversation::ServerGroupSettingsPtr group);
+ QListViewItem* getItemById(QListView* listView,int id);
+
+ bool newItemSelected;
+ QStringList m_oldNotifyList;
+};
+
+#endif
diff --git a/konversation/src/watchednicknames_preferencesui.ui b/konversation/src/watchednicknames_preferencesui.ui
new file mode 100644
index 0000000..3195e64
--- /dev/null
+++ b/konversation/src/watchednicknames_preferencesui.ui
@@ -0,0 +1,415 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>WatchedNicknames_ConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WatchedNicknames_ConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>623</width>
+ <height>418</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>notifyActionLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Command e&amp;xecuted when nickname is double clicked:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_NotifyDoubleClickAction</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;
+&lt;p&gt;When you double click a nickname in the &lt;b&gt;Nicks Online&lt;/b&gt; window, this
+command is placed in the &lt;b&gt;Input Line&lt;/b&gt; on the server window.&lt;/p&gt;
+&lt;p&gt;The following symbols can be used in the command:&lt;/p&gt;&lt;ul&gt;
+&lt;li&gt;%u: The nickname double clicked.&lt;/li&gt;
+&lt;li&gt;%K: Server password.&lt;/li&gt;
+&lt;li&gt;%n: Send command directly to the server instead of your input line.&lt;/li&gt;"
+&lt;/ul&gt;
+&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_NotifyDoubleClickAction</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;
+&lt;p&gt;When you double click a nickname in the &lt;b&gt;Nicks Online&lt;/b&gt; window, this
+ command is placed in the &lt;b&gt;Input Line&lt;/b&gt; on the server window.&lt;/p&gt;
+&lt;p&gt;The following symbols can be used in the command:&lt;/p&gt;&lt;ul&gt;
+&lt;li&gt;%u: The nickname double clicked.&lt;/li&gt;
+&lt;li&gt;%K: Server password.&lt;/li&gt;
+&lt;li&gt;%n: Send command directly to the server instead of your input line.&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>notifyDelayLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Update interval:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_NotifyDelay</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Konversation will check the status of the nicknames listed below at this interval.</string>
+ </property>
+ </widget>
+ <spacer row="3" column="2">
+ <property name="name">
+ <cstring>spacer36</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>150</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>kcfg_NotifyDelay</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> seconds</string>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="minValue">
+ <number>5</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Konversation will check the status of the nicknames listed below at this interval.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_OpenWatchedNicksAtStartup</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Show watched nicks online tab on &amp;application startup</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;When checked, the &lt;b&gt;Nicks Online&lt;/b&gt; window will be automatically opened when starting Konversation.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_UseNotify</cstring>
+ </property>
+ <property name="text">
+ <string>Enable nic&amp;kname watcher</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;
+&lt;p&gt;
+When the nickname watcher is turned on, you will be notified when the nicknames appearing in the &lt;b&gt;Watched Networks/Nicknames&lt;/b&gt; list come online or go offline.&lt;/p&gt;
+&lt;p&gt;You can also open the &lt;b&gt;Nicks Online&lt;/b&gt; window to see the status of all the watched nicknames.&lt;/p&gt;
+&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>listGroupBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Watched Nicknames</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>networkLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Network:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The irc server network (such as 'freenode') that the selected user to watch is on.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>networkDropdown</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The irc server network (such as 'freenode') that the selected user to watch is on.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>nicknameLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Nickname:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The nick (username) of the selected person to watch</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>nicknameInput</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The nick (username) of the selected person to watch</string>
+ </property>
+ </widget>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="4">
+ <column>
+ <property name="text">
+ <string>Watched Networks/Nicknames</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>notifyListView</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;
+&lt;p&gt;
+When the nickname watcher is turned on, you will be notified when the nicknames appearing in the &lt;b&gt;Watched Networks/Nicknames&lt;/b&gt; list come online or go offline.&lt;/p&gt;
+&lt;p&gt;You can also open the &lt;b&gt;Nicks Online&lt;/b&gt; window to see the status of all the watched nicknames.&lt;/p&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="4" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>newButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;New...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to add a nickname to the list.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to remove the selected nickname from the list.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer34</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>160</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_UseNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_OpenWatchedNicksAtStartup</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_NotifyDelay</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>notifyListView</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>notifyDelayLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>notifyActionLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_NotifyDoubleClickAction</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_UseNotify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>listGroupBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_UseNotify</tabstop>
+ <tabstop>kcfg_OpenWatchedNicksAtStartup</tabstop>
+ <tabstop>kcfg_NotifyDoubleClickAction</tabstop>
+ <tabstop>kcfg_NotifyDelay</tabstop>
+ <tabstop>notifyListView</tabstop>
+ <tabstop>networkDropdown</tabstop>
+ <tabstop>nicknameInput</tabstop>
+ <tabstop>newButton</tabstop>
+ <tabstop>removeButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>