XDCC-Fetch 1.386
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +23 -0
- data/XDCC-Fetch.rbw +54 -0
- data/doc/ark.png +0 -0
- data/doc/connect_established.png +0 -0
- data/doc/index.html +300 -0
- data/doc/mega_ark.png +0 -0
- data/doc/package.png +0 -0
- data/doc/package_unknown.png +0 -0
- data/doc/shot1.png +0 -0
- data/doc/shot1_mini.png +0 -0
- data/doc/shot2.png +0 -0
- data/doc/shot2_mini.png +0 -0
- data/doc/xdccfetch.css +155 -0
- data/icons/ark.png +0 -0
- data/icons/ark_big.png +0 -0
- data/icons/camera_test.png +0 -0
- data/icons/cancel.png +0 -0
- data/icons/connect_creating.png +0 -0
- data/icons/connect_established.png +0 -0
- data/icons/connect_failed.png +0 -0
- data/icons/connect_no.png +0 -0
- data/icons/edit_add.png +0 -0
- data/icons/edit_remove.png +0 -0
- data/icons/exit.png +0 -0
- data/icons/fileclose.png +0 -0
- data/icons/folder_inbox.png +0 -0
- data/icons/idea.png +0 -0
- data/icons/mega_ark.png +0 -0
- data/icons/messagebox_critical.png +0 -0
- data/icons/messagebox_info.png +0 -0
- data/icons/messagebox_warning.png +0 -0
- data/icons/messagebox_warning_small.png +0 -0
- data/icons/package.png +0 -0
- data/icons/package_favourite.png +0 -0
- data/icons/package_unknown.png +0 -0
- data/src/Console/Console_Parser.rb +71 -0
- data/src/Console/XDCC_Pack_Match_Template.rb +29 -0
- data/src/Console/xdcc-fetch.rb +123 -0
- data/src/GUI/About_Dialog.rb +50 -0
- data/src/GUI/Application_Builder.rb +280 -0
- data/src/GUI/Context_Menu.rb +81 -0
- data/src/GUI/Custom_Tabs.rb +60 -0
- data/src/GUI/Dialog_Box.rb +116 -0
- data/src/GUI/Download_Finished_Box.rb +41 -0
- data/src/GUI/Empty_Text_Field_Handler.rb +86 -0
- data/src/GUI/Gui_Logic.rb +629 -0
- data/src/GUI/Icon_Loader.rb +58 -0
- data/src/GUI/Main_Window.rb +227 -0
- data/src/GUI/Packet_Item.rb +171 -0
- data/src/GUI/Packet_List.rb +145 -0
- data/src/GUI/Speed_Widget.rb +101 -0
- data/src/GUI/Talk_Back.rb +118 -0
- data/src/GUI/Toggle_Button.rb +56 -0
- data/src/Network/CTCP_Handler.rb +61 -0
- data/src/Network/DCC_File.rb +323 -0
- data/src/Network/DCC_Parser.rb +71 -0
- data/src/Network/IPAddr_Ext.rb +76 -0
- data/src/Network/IRC_Message.rb +161 -0
- data/src/Network/IRC_Server.rb +273 -0
- data/src/Network/IRC_Server_Respond_Map.rb +223 -0
- data/src/Network/IRC_User.rb +58 -0
- data/src/Network/TCP_Connection.rb +168 -0
- data/src/Network/XDCC_Announcement.rb +120 -0
- data/src/Network/XDCC_Announcement_Storage.rb +167 -0
- data/src/Network/XDCC_Download_Handler.rb +412 -0
- data/src/Network/XDCC_Pack.rb +58 -0
- data/src/Network/XDCC_Parser.rb +253 -0
- data/src/Translations/README +61 -0
- data/src/Translations/check_translations +83 -0
- data/src/Translations/de.rb +140 -0
- data/src/Translations/en.rb +145 -0
- data/src/Utilities/Configuration.rb +91 -0
- data/src/Utilities/Events.rb +87 -0
- data/src/Utilities/Globals.rb +138 -0
- data/src/Utilities/PrettyException.rb +1091 -0
- data/src/Utilities/Recursive_Open_Struct.rb +159 -0
- data/src/Utilities/Timer.rb +71 -0
- metadata +135 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# Copyright (c) 2004, 2005 Martin Ankerl
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice, this list
|
8
|
+
# of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
# of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
# provided with the distribution.
|
12
|
+
# * Neither the name of Martin Ankerl nor the names of its contributors may be used to
|
13
|
+
# endorse or promote products derived from this software without specific prior written
|
14
|
+
# permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
17
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
18
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
19
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
22
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
23
|
+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
# Context_Menu is a more convenient way to use context menus. It allows the creation of states that enable/disable items.
|
26
|
+
class Context_Menu
|
27
|
+
attr_accessor :data
|
28
|
+
|
29
|
+
# Creates a new Context_Menu for given parent. If you provide a block,
|
30
|
+
# this code is called whenever the menu is shown.
|
31
|
+
def initialize(parent, title="", icon=nil, &proc) # :yields: self, sender, sel, data
|
32
|
+
@proc = proc
|
33
|
+
@data = nil
|
34
|
+
@menu_commands = Array.new
|
35
|
+
@states = Hash.new
|
36
|
+
@parent = parent
|
37
|
+
@pane = FXMenuPane.new(parent)
|
38
|
+
@title = FXMenuTitle.new(parent, title, icon, @pane)
|
39
|
+
|
40
|
+
#@parent.connect(SEL_RIGHTBUTTONRELEASE, method(:on_right_click))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Add an FXMenuSeparator separator.
|
44
|
+
def add_separator
|
45
|
+
FXMenuSeparator.new(@pane)
|
46
|
+
@pane.recalc
|
47
|
+
end
|
48
|
+
|
49
|
+
# Add a new entry to the context menu. The supplied code is called
|
50
|
+
# when this entry is activated.
|
51
|
+
def add(text, icon=nil, &proc) # :yields: self, sender, sel, data
|
52
|
+
cmd = FXMenuCommand.new(@pane, text, icon)
|
53
|
+
@menu_commands.push cmd
|
54
|
+
cmd.connect(SEL_COMMAND) { |sender, sel, data| proc.call(self, sender, sel, data) }
|
55
|
+
@pane.recalc
|
56
|
+
cmd
|
57
|
+
end
|
58
|
+
|
59
|
+
# Create a new state which can later be accessed using the key state_name.
|
60
|
+
# For each menu entry, you need to specify if it should be enabled or disabled (true/false).
|
61
|
+
def new_state(state_name, *args)
|
62
|
+
raise "need #{@menu_commands.size} stati, but got #{args.size}" if @menu_commands.size != args.size
|
63
|
+
@states[state_name] = args
|
64
|
+
end
|
65
|
+
|
66
|
+
# Loads a state which has previously been set using #new_state.
|
67
|
+
def load_state(state_name)
|
68
|
+
state = @states[state_name]
|
69
|
+
state.each_index do |i|
|
70
|
+
@menu_commands[i].enabled = state[i]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Shows the context menu.
|
75
|
+
def execute(sender, sel, data)
|
76
|
+
@proc.call(self, sender, sel, data) if @proc
|
77
|
+
@pane.create
|
78
|
+
@pane.popup(nil, data.root_x, data.root_y)
|
79
|
+
@parent.app.runModalWhileShown(@pane)
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Copyright (c) 2004, 2005 Martin Ankerl
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice, this list
|
8
|
+
# of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
# of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
# provided with the distribution.
|
12
|
+
# * Neither the name of Martin Ankerl nor the names of its contributors may be used to
|
13
|
+
# endorse or promote products derived from this software without specific prior written
|
14
|
+
# permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
17
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
18
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
19
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
22
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
23
|
+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
# The Fox tabs do not look very nice. This simple handler uses buttons to show/hide frames to
|
26
|
+
# emulate a similar, better looking widget.
|
27
|
+
class Custom_Tabs
|
28
|
+
|
29
|
+
# Creates a new hander. btn_to_frame needs to be a hash where each key is
|
30
|
+
# a button and each value a frame associated to the button.
|
31
|
+
def initialize(btn_to_frame)
|
32
|
+
@btn_to_frame = btn_to_frame
|
33
|
+
create_tabs
|
34
|
+
end
|
35
|
+
|
36
|
+
# Show the frame associated with the button show_btn.
|
37
|
+
def show(show_btn)
|
38
|
+
# disable everything
|
39
|
+
@btn_to_frame.each_value do |frame|
|
40
|
+
frame.hide
|
41
|
+
end
|
42
|
+
@btn_to_frame.each_key do |btn|
|
43
|
+
btn.state = STATE_DOWN
|
44
|
+
end
|
45
|
+
# enable current tab
|
46
|
+
@btn_to_frame[show_btn].show
|
47
|
+
@btn_to_frame[show_btn].parent.recalc
|
48
|
+
show_btn.state = STATE_UP
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# connect buttons
|
54
|
+
def create_tabs
|
55
|
+
@btn_to_frame.each do |btn, frame|
|
56
|
+
btn.connect(SEL_COMMAND) { show(btn) }
|
57
|
+
btn.connect(SEL_LEFTBUTTONPRESS) { show(btn) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Copyright (c) 2004,2005 Martin Ankerl
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice, this list
|
8
|
+
# of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
# of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
# provided with the distribution.
|
12
|
+
# * Neither the name of Martin Ankerl nor the names of its contributors may be used to
|
13
|
+
# endorse or promote products derived from this software without specific prior written
|
14
|
+
# permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
17
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
18
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
19
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
22
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
23
|
+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
|
26
|
+
require 'src/GUI/Empty_Text_Field_Handler'
|
27
|
+
|
28
|
+
# A convinient dialog box for standard user input like username, password, etc.
|
29
|
+
class Dialog_Box < FXDialogBox
|
30
|
+
# Entry stores the data of one user-input entry of the dialog box
|
31
|
+
Entry = Struct.new("Entry", :handler, :opts, :default_value, :is_required, :empty_text)
|
32
|
+
|
33
|
+
# Creates a new Dialog_Box for the parent with the given title.
|
34
|
+
def initialize(parent, title)
|
35
|
+
super(parent, title, DECOR_TITLE|DECOR_BORDER, 0,0,0,0, 0,0,0,0)
|
36
|
+
@parent = parent
|
37
|
+
@entries = Array.new
|
38
|
+
build_controls
|
39
|
+
build_connections
|
40
|
+
end
|
41
|
+
|
42
|
+
# Add a new entry to the dialog box. for the opts you can specify any FXTextField options like
|
43
|
+
# TEXTFIELD_PASSWD,TEXTFIELD_INTEGER etc.
|
44
|
+
def add(title, opts=0, default_value=nil, is_required=true, empty_text="")
|
45
|
+
# use data to fill dialog box
|
46
|
+
FXLabel.new(@matrixFrame, title, nil, LAYOUT_CENTER_Y|JUSTIFY_RIGHT|LAYOUT_FILL_X)
|
47
|
+
text_field = FXTextField.new(@matrixFrame, 1, nil, 0, opts|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X|LAYOUT_FIX_WIDTH, 0,0,$cfg.dialogbox.textwidth,0)
|
48
|
+
handler = Empty_Text_Field_Handler.new(text_field, empty_text)
|
49
|
+
@entries.push Entry.new(handler, opts, default_value, is_required, empty_text)
|
50
|
+
text_field.connect(SEL_COMMAND) do |sender, sel, data|
|
51
|
+
lastFocus = @matrixFrame.focus
|
52
|
+
@ok_btn.setFocus if lastFocus
|
53
|
+
on_ok_button if lastFocus
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Displays the dialog box. If OK is pressed, the given block is called with all the user
|
58
|
+
# input as parameters.
|
59
|
+
def execute(&proc)
|
60
|
+
# clear controls
|
61
|
+
@entries.each do |entry|
|
62
|
+
entry.handler.text = entry.default_value
|
63
|
+
end
|
64
|
+
# focus first item
|
65
|
+
@entries.first.handler.setFocus
|
66
|
+
# show dialog
|
67
|
+
@proc = proc
|
68
|
+
self.show(PLACEMENT_SCREEN)
|
69
|
+
app.runModalWhileShown(self)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def build_controls
|
75
|
+
FXVerticalFrame.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0,0,0) do |base|
|
76
|
+
FXMatrix.new(base, 2, MATRIX_BY_COLUMNS|LAYOUT_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y) do |matrix|
|
77
|
+
@matrixFrame = matrix
|
78
|
+
end
|
79
|
+
FXHorizontalSeparator.new(base, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|SEPARATOR_GROOVE)
|
80
|
+
FXHorizontalFrame.new(base, LAYOUT_FILL_X, 0,0,0,0) do |button_frame|
|
81
|
+
FXHorizontalFrame.new(button_frame, LAYOUT_FILL_X, 0,0,0,0)
|
82
|
+
@ok_btn = FXButton.new(button_frame, "&Ok", nil, nil, 0, FRAME_RAISED)
|
83
|
+
@cancel_btn = FXButton.new(button_frame, "&Cancel", nil,self, ID_HIDE, FRAME_RAISED)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_connections
|
89
|
+
@ok_btn.connect(SEL_COMMAND) { on_ok_button }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Ensure all required data is available, convert data, and call given proc.
|
93
|
+
def on_ok_button
|
94
|
+
# check that each text_field contains data
|
95
|
+
empty_field = @entries.detect { |entry| entry.is_required && entry.handler.empty? }
|
96
|
+
if empty_field
|
97
|
+
empty_field.handler.setFocus
|
98
|
+
else
|
99
|
+
# collect data
|
100
|
+
data = Array.new
|
101
|
+
@entries.each do |entry|
|
102
|
+
if (entry.handler.text == "")
|
103
|
+
data.push nil
|
104
|
+
elsif (entry.opts & TEXTFIELD_INTEGER != 0)
|
105
|
+
data.push entry.handler.text.to_i
|
106
|
+
elsif (entry.opts & TEXTFIELD_REAL != 0)
|
107
|
+
data.push entry.handler.text.to_f
|
108
|
+
else
|
109
|
+
data.push entry.handler.text
|
110
|
+
end
|
111
|
+
end
|
112
|
+
self.hide
|
113
|
+
@proc.call(data)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright (c) 2004, Christoph Heindl and Martin Ankerl
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice, this list
|
8
|
+
# of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
# of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
# provided with the distribution.
|
12
|
+
# * Neither the name of Christoph Heindl and Martin Ankerl nor the names of its contributors
|
13
|
+
# may be used to endorse or promote products derived from this software without specific
|
14
|
+
# prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
17
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
18
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
19
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
22
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
23
|
+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
class Download_Finished_Box
|
26
|
+
def initialize(parent, pack_name)
|
27
|
+
@download_finished_box = FXDialogBox.new(parent, "", DECOR_BORDER, 0,0,0,0, 0,0,0,0,0,0) do |dlg|
|
28
|
+
FXLabel.new(dlg, $cfg.text.download_finished_box + ":\n" + pack_name, $cfg.icons.active, JUSTIFY_LEFT|ICON_BEFORE_TEXT)
|
29
|
+
end
|
30
|
+
@download_finished_box.create
|
31
|
+
timed_show(5)
|
32
|
+
end
|
33
|
+
|
34
|
+
def timed_show(timeout)
|
35
|
+
@download_finished_box.show
|
36
|
+
Thread.new do
|
37
|
+
sleep(timeout)
|
38
|
+
@download_finished_box.hide
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# Copyright (c) 2004, 2005 Martin Ankerl
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice, this list
|
8
|
+
# of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
# of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
# provided with the distribution.
|
12
|
+
# * Neither the name of Martin Ankerl nor the names of its contributors may be used to
|
13
|
+
# endorse or promote products derived from this software without specific prior written
|
14
|
+
# permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
17
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
18
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
19
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
22
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
23
|
+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
# Shows a grey text in an FXTextField if the user did not enter any input. This is a nice way to
|
26
|
+
# give the user more information about what to enter into a text field, without the need of additional
|
27
|
+
# space in the GUI.
|
28
|
+
class Empty_Text_Field_Handler
|
29
|
+
|
30
|
+
# Create a new handler for the specified textfield, with the given text. From now on you have to use the
|
31
|
+
# created object to get and set text, not the textfield or this handler would come out of sync
|
32
|
+
def initialize(textField, myText)
|
33
|
+
@textField = textField
|
34
|
+
@myText = myText
|
35
|
+
@isEmpty = true
|
36
|
+
onTextFieldFocusOut
|
37
|
+
# create connections
|
38
|
+
@textField.connect(SEL_FOCUSIN, method(:onTextFieldFocusIn))
|
39
|
+
@textField.connect(SEL_FOCUSOUT, method(:onTextFieldFocusOut))
|
40
|
+
end
|
41
|
+
|
42
|
+
# Check if textfield is empty (no user input).
|
43
|
+
def empty?
|
44
|
+
@isEmpty
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set new text for the textfield
|
48
|
+
def text=(newText)
|
49
|
+
onTextFieldFocusIn
|
50
|
+
@textField.text = newText.to_s
|
51
|
+
onTextFieldFocusOut
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get the textfield's text, if the user has entered something.
|
55
|
+
def text
|
56
|
+
if empty? && !@inside
|
57
|
+
""
|
58
|
+
else
|
59
|
+
@textField.text
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Set focus to the textfield.
|
64
|
+
def setFocus
|
65
|
+
@textField.setFocus
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def onTextFieldFocusIn(*args)
|
71
|
+
@inside = true
|
72
|
+
return if !@isEmpty
|
73
|
+
@textField.textColor = FXColor::Black
|
74
|
+
@textField.text = ""
|
75
|
+
end
|
76
|
+
|
77
|
+
def onTextFieldFocusOut(*args)
|
78
|
+
@inside = false
|
79
|
+
@textField.killSelection
|
80
|
+
@isEmpty = @textField.text == ""
|
81
|
+
return if !@isEmpty
|
82
|
+
@textField.textColor = FXColor::DarkGrey
|
83
|
+
@textField.text = @myText
|
84
|
+
@isEmpty = true
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,629 @@
|
|
1
|
+
# Copyright (c) 2004, 2005 Martin Ankerl
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice, this list
|
8
|
+
# of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
# of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
# provided with the distribution.
|
12
|
+
# * Neither the name of Martin Ankerl nor the names of its contributors may be used to
|
13
|
+
# endorse or promote products derived from this software without specific prior written
|
14
|
+
# permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
17
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
18
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
19
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
22
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
23
|
+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
require 'thread'
|
26
|
+
|
27
|
+
# The module Business_Logic is included into the Main_Window. It contains all the logic required for the GUI except
|
28
|
+
# creation stuff.
|
29
|
+
class Gui_Logic
|
30
|
+
CONNECTED = 1
|
31
|
+
CONNECTING = 2
|
32
|
+
DISCONNECTED = 3
|
33
|
+
CONNECTION_FAILED = 4
|
34
|
+
|
35
|
+
def initialize(gui, data)
|
36
|
+
@gui = gui
|
37
|
+
@data = data
|
38
|
+
end
|
39
|
+
|
40
|
+
# Adds a new IRC_server and connects to it.
|
41
|
+
def on_add_server_click(menu, sender, sel, data)
|
42
|
+
@gui.add_server_box.execute do |ip, port, nick, pwd|
|
43
|
+
add_server(ip, port, nick, pwd, true)
|
44
|
+
# add to config
|
45
|
+
$cfg.config.servers[ip] = {
|
46
|
+
:port => port,
|
47
|
+
:nick => nick,
|
48
|
+
:pwd => pwd,
|
49
|
+
:channels => { },
|
50
|
+
:connected => true
|
51
|
+
}
|
52
|
+
@data.config.save
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrieves the clicked item of the connection widget, which is a FXTreeList. For FXIconLists like Packet_List
|
57
|
+
# use #current_item.
|
58
|
+
def current_connections_item
|
59
|
+
@gui.connections.cursorItem
|
60
|
+
end
|
61
|
+
|
62
|
+
# Retrieves the clicked item of the given Packet_List, or any other FXIconList.
|
63
|
+
def current_item(list)
|
64
|
+
pos = list.cursorItem
|
65
|
+
return list.getItem(pos) if (pos != -1)
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convert a string containing a packetsize into the real size which is used for
|
70
|
+
# sorting.Possible strings are e.g. "100M", "43G", "9.9k", etc.
|
71
|
+
def size_to_nr(x)
|
72
|
+
return 0 if x.nil? || x==""
|
73
|
+
x = x.upcase.strip
|
74
|
+
fact = 1.0
|
75
|
+
char = x[-1]
|
76
|
+
# check if there is MB, KB etc.
|
77
|
+
char = x[-2] if char == ?B
|
78
|
+
case char
|
79
|
+
when ?K then fact = 1024
|
80
|
+
when ?M then fact = 1024**2
|
81
|
+
when ?G then fact = 1024**3
|
82
|
+
end
|
83
|
+
x.to_f * fact
|
84
|
+
end
|
85
|
+
|
86
|
+
# Converts a given md5 status text into a representation used for sorting.
|
87
|
+
def sortable_md5_status(x)
|
88
|
+
case x
|
89
|
+
when $cfg.text.md5.ok then 1
|
90
|
+
when $cfg.text.md5.disabled then 2
|
91
|
+
when $cfg.text.md5.unavailable then 3
|
92
|
+
when $cfg.text.md5.failed then 4
|
93
|
+
else 5
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Converts a given download status text into a representation used for sorting.
|
98
|
+
def sortable_status(x)
|
99
|
+
# 2e7: failed
|
100
|
+
# 1e7: download finished
|
101
|
+
# 0..100: download percentage
|
102
|
+
# 0.5: download started
|
103
|
+
# -1..-1e7: remote queue position
|
104
|
+
# -1e7..-2e7: local queue position
|
105
|
+
# - 4e7: waiting
|
106
|
+
prev = x
|
107
|
+
if x.include?($cfg.text.download_failed)
|
108
|
+
x = 2e7
|
109
|
+
elsif x == $cfg.text.download_finished
|
110
|
+
x = 1e7
|
111
|
+
elsif x =~ /%/
|
112
|
+
x = x.to_f
|
113
|
+
elsif x == $cfg.text.download_started
|
114
|
+
x = 0.5
|
115
|
+
elsif x.include?($cfg.text.queue_remote)
|
116
|
+
/.*\s(\d+)/.match(x)
|
117
|
+
x = - $1.to_i
|
118
|
+
elsif x.include?($cfg.text.queue_local)
|
119
|
+
/.*\s(\d+)/.match(x)
|
120
|
+
x = - $1.to_i - 1e7
|
121
|
+
elsif x == $cfg.text.download_waiting
|
122
|
+
x = -4e7
|
123
|
+
else
|
124
|
+
x = 3e7
|
125
|
+
end
|
126
|
+
x
|
127
|
+
end
|
128
|
+
|
129
|
+
# Update the text for the number of displayed packs.
|
130
|
+
def update_search_status_text
|
131
|
+
@gui.search_status_label.text = " #{@gui.packet_list.numItems} #{$cfg.text.of} #{@data.allpack_to_item.size} #{$cfg.text.packs}"
|
132
|
+
end
|
133
|
+
|
134
|
+
# Find out if item is matched by current search criteria.
|
135
|
+
def match?(item, match_data)
|
136
|
+
# check icon
|
137
|
+
return false unless @data.packet_icon_to_toggle_btn[item.icon].toggled?
|
138
|
+
|
139
|
+
# check text
|
140
|
+
return true if match_data.empty?
|
141
|
+
item_str = item.sortable(1)
|
142
|
+
match_data.each do |match_str|
|
143
|
+
if match_str[0] == ?-
|
144
|
+
# the next items are not allowed to be contained in pack name
|
145
|
+
return false if (match_str[1..-1]!="") && (item_str.include?(match_str[1..-1]))
|
146
|
+
else
|
147
|
+
return false if !item_str.include?(match_str)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
153
|
+
# Executed whenever a search criteria changes to update the packet list.
|
154
|
+
def on_search
|
155
|
+
# restart current search
|
156
|
+
# TODO what happens when new announcements come every 0.2 second or so?
|
157
|
+
# the list will never be updated... :-/
|
158
|
+
@end_time = Time.now + 0.5
|
159
|
+
@restart_search = true
|
160
|
+
@gui.search_label.enabled = false
|
161
|
+
return if @search_thread && @search_thread.status
|
162
|
+
|
163
|
+
@search_thread = Thread.new(@search_thread) do
|
164
|
+
begin
|
165
|
+
@gui.search_label.enabled = false
|
166
|
+
# wait untill deadline
|
167
|
+
while (t = (@end_time - Time.now)) > 0
|
168
|
+
sleep(t)
|
169
|
+
end
|
170
|
+
|
171
|
+
@data.gui_mutex.synchronize do
|
172
|
+
# the thread has to use the gui mutex inside
|
173
|
+
@restart_search = false
|
174
|
+
|
175
|
+
@match_data = @gui.search_field.text.downcase.split
|
176
|
+
|
177
|
+
# remove all items
|
178
|
+
@gui.packet_list.dirty_clear
|
179
|
+
|
180
|
+
# add all items that match the search criteria
|
181
|
+
sort_deadline = Time.now
|
182
|
+
status_text_deadline = Time.now
|
183
|
+
@data.allpack_to_item.each_value do |item|
|
184
|
+
#item.parent = @gui.packet_list if match?(item, match_data)
|
185
|
+
if match?(item, @match_data)
|
186
|
+
item.show
|
187
|
+
|
188
|
+
# take only about 10% of the cpu to sort items.
|
189
|
+
# with few items this results to very fast sorts,
|
190
|
+
# with many items this leads to fast updates.
|
191
|
+
if (sort_deadline < Time.now)
|
192
|
+
before = Time.now
|
193
|
+
@gui.packet_list.sortItems
|
194
|
+
after = Time.now
|
195
|
+
sort_deadline = after + (after-before) *10
|
196
|
+
end
|
197
|
+
|
198
|
+
# update every 0.1 second
|
199
|
+
if (status_text_deadline < Time.now)
|
200
|
+
update_search_status_text
|
201
|
+
status_text_deadline = Time.now + 0.1
|
202
|
+
end
|
203
|
+
end
|
204
|
+
break if @restart_search
|
205
|
+
end
|
206
|
+
update_search_status_text
|
207
|
+
@gui.packet_list.sortItems
|
208
|
+
@gui.search_label.enabled = true
|
209
|
+
|
210
|
+
FXApp::instance.flush
|
211
|
+
end # synchronize
|
212
|
+
end while @restart_search# || @match_data != @gui.search_field.text.downcase.split
|
213
|
+
end #thread.new
|
214
|
+
end
|
215
|
+
|
216
|
+
def add_server(ip, port, nick, pwd, do_connect)
|
217
|
+
name = "#{ip}"
|
218
|
+
server_item = @gui.connections.addItemLast(nil, name, $cfg.icons.disconnected, $cfg.icons.disconnected)
|
219
|
+
@data.server_item_to_status[server_item] = CONNECTING
|
220
|
+
# create server
|
221
|
+
server = IRC_Server.new
|
222
|
+
@data.ann_parser.attach_to_server(server)
|
223
|
+
@data.irc_server_to_item[server] = server_item
|
224
|
+
server_item.data = server
|
225
|
+
server_item.expanded = true
|
226
|
+
@gui.connections.sortItems
|
227
|
+
if do_connect
|
228
|
+
server_item.openIcon = $cfg.icons.connecting
|
229
|
+
server_item.closedIcon = $cfg.icons.connecting
|
230
|
+
server.connect(ip, port, nick, $cfg.alternative_user_name, pwd)
|
231
|
+
end
|
232
|
+
connect_to_server_events(server)
|
233
|
+
# server -> channelarray
|
234
|
+
@data.server_to_channels[server_item] = Array.new
|
235
|
+
server_item
|
236
|
+
end
|
237
|
+
|
238
|
+
# Connect to all servers stored in $cfg.config.servers
|
239
|
+
def connect_to_saved_servers
|
240
|
+
$cfg.config.servers.each do |ip, data|
|
241
|
+
server_item = add_server(ip, data[:port], data[:nick], data[:pwd], data[:connected])
|
242
|
+
@data.server_item_to_status[server_item] = data[:connected] ? CONNECTING : DISCONNECTED
|
243
|
+
# add channels
|
244
|
+
connect_to_saved_channels(server_item)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def connect_to_saved_channels(server_item)
|
249
|
+
server_data = $cfg.config.servers[server_item.text]
|
250
|
+
server_data[:channels].each do |channel_name, data|
|
251
|
+
add_channel(server_item, channel_name.dup, data[:pwd], server_data[:connected])
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# Disconnects from a server.
|
256
|
+
def on_remove_server_click(menu, sender, sel, data)
|
257
|
+
server_item = menu.data
|
258
|
+
# remove from config
|
259
|
+
$cfg.config.servers.delete(server_item.text)
|
260
|
+
@data.config.save
|
261
|
+
# remove
|
262
|
+
@data.ann_parser.detach_from_server(server_item.data)
|
263
|
+
server_item.data.disconnect
|
264
|
+
@data.irc_server_to_item.delete(server_item.data)
|
265
|
+
@gui.connections.removeItem(server_item)
|
266
|
+
end
|
267
|
+
|
268
|
+
def on_server_disconnect_click(menu, sender, sel, data)
|
269
|
+
server_item = menu.data
|
270
|
+
# set to disconnected, before actually disconnecting.
|
271
|
+
# This is used to distinguish between user disconnects, and forced disconnects.
|
272
|
+
@data.server_item_to_status[server_item] = DISCONNECTED
|
273
|
+
server_item.data.disconnect
|
274
|
+
# update config
|
275
|
+
$cfg.config.servers[server_item.text][:connected] = false
|
276
|
+
@data.config.save
|
277
|
+
end
|
278
|
+
|
279
|
+
def on_server_connect_click(menu, sender, sel, data)
|
280
|
+
server_item = menu.data
|
281
|
+
# update config
|
282
|
+
$cfg.config.servers[server_item.text][:connected] = true
|
283
|
+
@data.config.save
|
284
|
+
# connect irc server
|
285
|
+
irc_server = server_item.data
|
286
|
+
irc_server.disconnect
|
287
|
+
server_data = $cfg.config.servers[server_item.text]
|
288
|
+
irc_server.connect(server_item.text, server_data[:port], server_data[:nick], $cfg.alternative_user_name, server_data[:pwd])
|
289
|
+
# update icons
|
290
|
+
server_item.openIcon = $cfg.icons.connecting
|
291
|
+
server_item.closedIcon = $cfg.icons.connecting
|
292
|
+
# recreate all channels
|
293
|
+
@data.server_to_channels[server_item].each do |channel_item|
|
294
|
+
@gui.connections.removeItem(channel_item)
|
295
|
+
end
|
296
|
+
@data.server_to_channels[server_item] = Array.new
|
297
|
+
connect_to_saved_channels(server_item)
|
298
|
+
|
299
|
+
@data.server_item_to_status[server_item] = CONNECTING
|
300
|
+
@gui.connections.recalc
|
301
|
+
end
|
302
|
+
|
303
|
+
# Joins a channel at the clicked IRC_server.
|
304
|
+
def on_add_channel_click(menu, sender, sel, data)
|
305
|
+
@gui.join_channel_box.execute do |channel_name, password|
|
306
|
+
server_item = menu.data
|
307
|
+
do_connect = (@data.server_item_to_status[server_item] != DISCONNECTED &&
|
308
|
+
@data.server_item_to_status[server_item] != CONNECTION_FAILED)
|
309
|
+
real_channel_name = add_channel(server_item, channel_name, password, do_connect)
|
310
|
+
# add to config
|
311
|
+
$cfg.config.servers[server_item.text][:channels][real_channel_name] = { :pwd => password }
|
312
|
+
@data.config.save
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def add_channel(server_item, channel_name, password, do_connect)
|
317
|
+
server_item.expanded = true
|
318
|
+
# automatically prepend #
|
319
|
+
channel_name = "#" + channel_name unless channel_name[0] == ?#
|
320
|
+
channel_name.downcase!
|
321
|
+
# check if channel already available
|
322
|
+
return if @data.server_to_channels[server_item].detect { |channel_item| channel_item.text == channel_name }
|
323
|
+
# create item
|
324
|
+
channel_item = @gui.connections.addItemLast(server_item, channel_name, $cfg.icons.disconnected, $cfg.icons.disconnected)
|
325
|
+
if do_connect
|
326
|
+
channel_item.openIcon = $cfg.icons.connecting
|
327
|
+
channel_item.closedIcon = $cfg.icons.connecting
|
328
|
+
server_item.data.join(channel_name, password)
|
329
|
+
end
|
330
|
+
@data.server_to_channels[server_item].push channel_item
|
331
|
+
@gui.connections.sortChildItems(server_item)
|
332
|
+
channel_name
|
333
|
+
end
|
334
|
+
|
335
|
+
# Part channel.
|
336
|
+
def on_remove_channel_click(menu, sender, sel, data)
|
337
|
+
channel_item = menu.data
|
338
|
+
# remove from config
|
339
|
+
$cfg.config.servers[channel_item.parent.text][:channels].delete(channel_item.text)
|
340
|
+
@data.config.save
|
341
|
+
# remove
|
342
|
+
channel_item.parent.data.part(channel_item.text)
|
343
|
+
@data.server_to_channels[channel_item.parent].delete channel_item
|
344
|
+
@gui.connections.removeItem(channel_item)
|
345
|
+
end
|
346
|
+
|
347
|
+
# Show a nifty �ber-cool about dialog.
|
348
|
+
def on_about_click
|
349
|
+
About_Dialog.new(@gui.main_window).execute
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
# Let user select a target directory and initiate download if user clicks OK.
|
354
|
+
def on_download_click(menu, sender, sel, data)
|
355
|
+
fox_item = menu.data
|
356
|
+
return if !fox_item
|
357
|
+
item = fox_item.packet_item
|
358
|
+
packet = item.data
|
359
|
+
return if !packet
|
360
|
+
dir_dlg = FXDirDialog.new(@gui.main_window, $cfg.text.target_directory)
|
361
|
+
dir_dlg.directory = $cfg.config.download_directory
|
362
|
+
return if (dir_dlg.execute == 0)
|
363
|
+
# save new directory settings, if modified
|
364
|
+
new_dir = dir_dlg.directory
|
365
|
+
if (new_dir != $cfg.config.download_directory)
|
366
|
+
$cfg.config.download_directory = new_dir
|
367
|
+
@data.config.save
|
368
|
+
end
|
369
|
+
|
370
|
+
# get download handler
|
371
|
+
handler = nil
|
372
|
+
if (handler = @data.download_handlers[packet.irc_user_bot]) == nil
|
373
|
+
handler = @data.download_handlers[packet.irc_user_bot] = XDCC_Download_Handler.new(packet.irc_user_bot)
|
374
|
+
handler.connect_to_events(XDCC_Download_Handler::ON_ALL_EVENTS, self)
|
375
|
+
end
|
376
|
+
|
377
|
+
# add download item
|
378
|
+
item = @gui.active_list.create_item($cfg.icons.packet, item[0], item[1], item[2], $cfg.text.download_waiting, "")
|
379
|
+
item.fox_item.miniIcon = $cfg.icons.packet
|
380
|
+
item.data = packet
|
381
|
+
|
382
|
+
# get pack
|
383
|
+
@data.downloadpack_to_item[packet] = item
|
384
|
+
handler.add_pack(packet, $cfg.config.download_directory)
|
385
|
+
end
|
386
|
+
|
387
|
+
# Cancels a download.
|
388
|
+
def on_cancel_download_click(menu, sender, sel, data)
|
389
|
+
fox_item = menu.data
|
390
|
+
return if !fox_item
|
391
|
+
item = fox_item.packet_item
|
392
|
+
packet = item.data
|
393
|
+
return if !packet
|
394
|
+
|
395
|
+
handler = @data.download_handlers[packet.irc_user_bot]
|
396
|
+
item[3] = $cfg.text.download_cancelling
|
397
|
+
handler.cancel_pack(packet)
|
398
|
+
end
|
399
|
+
|
400
|
+
# Remove all items from the error list
|
401
|
+
def on_clear_error_list
|
402
|
+
@gui.error_list.clear
|
403
|
+
end
|
404
|
+
|
405
|
+
# Remove all items from the completed list
|
406
|
+
def on_clear_completed_list
|
407
|
+
@gui.completed_list.clear
|
408
|
+
end
|
409
|
+
|
410
|
+
def on_show_warning(menu, sender, sel, data)
|
411
|
+
server_item = menu.data
|
412
|
+
box = FXMessageBox.new(
|
413
|
+
FXApp::instance,
|
414
|
+
$cfg.text.message.connection_error_title,
|
415
|
+
@data.server_item_to_warning[server_item],
|
416
|
+
$cfg.icons.warning_big, MBOX_OK)
|
417
|
+
box.create
|
418
|
+
box.show(PLACEMENT_OWNER)
|
419
|
+
end
|
420
|
+
|
421
|
+
|
422
|
+
# Connect to all required events of an IRC_Server.
|
423
|
+
def connect_to_server_events(server)
|
424
|
+
server.connect_to_events(IRC_Server::ON_SERVER_REGISTERED, self)
|
425
|
+
server.connect_to_events(IRC_Server::ON_CHANNEL_JOIN_SUCCESS, self)
|
426
|
+
server.connect_to_events(IRC_Server::ON_SERVER_DISCONNECTED, self)
|
427
|
+
server.connect_to_events(IRC_Server::ON_USER_KICK, self)
|
428
|
+
end
|
429
|
+
|
430
|
+
# Handles all callbacks from IRC_Server, XDCC_Announcement_Storage and XDCC_Download_Handler.
|
431
|
+
def on_event(caller, event_type, args)
|
432
|
+
@data.gui_mutex.synchronize do
|
433
|
+
case event_type
|
434
|
+
when IRC_Server::ON_SERVER_REGISTERED
|
435
|
+
server_item = @data.irc_server_to_item[caller]
|
436
|
+
return if !server_item
|
437
|
+
@data.server_item_to_status[server_item] = CONNECTED
|
438
|
+
server_item.closedIcon = $cfg.icons.connected
|
439
|
+
server_item.openIcon = $cfg.icons.connected
|
440
|
+
@gui.connections.recalc
|
441
|
+
|
442
|
+
when IRC_Server::ON_SERVER_DISCONNECTED
|
443
|
+
error_msg = args[0] || ""
|
444
|
+
server_item = @data.irc_server_to_item[caller]
|
445
|
+
return if !server_item
|
446
|
+
# disconnected by the user, or forced disconnect?
|
447
|
+
icon = $cfg.icons.disconnected
|
448
|
+
unless @data.server_item_to_status[server_item] == DISCONNECTED
|
449
|
+
@data.server_item_to_status[server_item] = CONNECTION_FAILED
|
450
|
+
@data.server_item_to_warning[server_item] = $cfg.text.message.connection_error + ":\n" + error_msg
|
451
|
+
icon = $cfg.icons.connection_failed
|
452
|
+
else
|
453
|
+
@data.server_item_to_status[server_item] = DISCONNECTED
|
454
|
+
end
|
455
|
+
server_item.closedIcon = icon
|
456
|
+
server_item.openIcon = icon
|
457
|
+
# set all channel items to disconnected
|
458
|
+
server_item.each do |channel_item|
|
459
|
+
channel_item.closedIcon = icon
|
460
|
+
channel_item.openIcon = icon
|
461
|
+
end
|
462
|
+
@gui.connections.recalc
|
463
|
+
|
464
|
+
when IRC_Server::ON_CHANNEL_JOIN_SUCCESS
|
465
|
+
channel_name, channel_mode = args
|
466
|
+
channel_name.downcase!
|
467
|
+
server_item = @data.irc_server_to_item[caller]
|
468
|
+
channel = @data.server_to_channels[server_item].detect { |channel| channel.text==channel_name }
|
469
|
+
|
470
|
+
# Follow automatic channel forwards
|
471
|
+
if !channel
|
472
|
+
# create icon
|
473
|
+
channel_name = "#" + channel_name unless channel_name[0] == ?#
|
474
|
+
channel_name.downcase!
|
475
|
+
server_item.expanded = true
|
476
|
+
# check if channel already available
|
477
|
+
return if @data.server_to_channels[server_item].detect { |channel_item| channel_item.text == channel_name }
|
478
|
+
# create item
|
479
|
+
channel = @gui.connections.addItemLast(server_item, channel_name, $cfg.icons.connecting, $cfg.icons.connecting)
|
480
|
+
@data.server_to_channels[server_item].push channel
|
481
|
+
@gui.connections.sortChildItems(server_item)
|
482
|
+
end
|
483
|
+
channel.closedIcon = $cfg.icons.connected
|
484
|
+
channel.openIcon = $cfg.icons.connected
|
485
|
+
@gui.connections.recalc
|
486
|
+
|
487
|
+
when XDCC_Announcement_Storage::ON_ANNOUNCEMENT_NEW
|
488
|
+
ann = args[0]
|
489
|
+
bot = ann.irc_user_bot.name
|
490
|
+
|
491
|
+
# set slot representing icon
|
492
|
+
if ann.ann_type == XDCC_Announcement::ANN_FULL
|
493
|
+
icon = $cfg.icons.packet
|
494
|
+
icon = $cfg.icons.free_slots if ann.openslot_cnt > 0
|
495
|
+
else
|
496
|
+
icon = $cfg.icons.slots_unknown
|
497
|
+
end
|
498
|
+
|
499
|
+
is_added = false
|
500
|
+
match_data = @gui.search_field.text.downcase.split
|
501
|
+
ann.packs.each do |pack|
|
502
|
+
# create an unconnected item
|
503
|
+
item = Packet_Item.new(nil, icon, bot, pack.name, pack.size)
|
504
|
+
item.data = pack
|
505
|
+
@data.allpack_to_item[pack] = item
|
506
|
+
|
507
|
+
# item needs to be added to a list, before calling match()
|
508
|
+
item.parent = @gui.packet_list
|
509
|
+
if match?(item, match_data)
|
510
|
+
item.show
|
511
|
+
is_added = true
|
512
|
+
end
|
513
|
+
end
|
514
|
+
if is_added
|
515
|
+
update_search_status_text
|
516
|
+
@gui.packet_list.sortItems
|
517
|
+
end
|
518
|
+
#on_search
|
519
|
+
|
520
|
+
when XDCC_Announcement_Storage::ON_ANNOUNCEMENT_LOST
|
521
|
+
ann = args[0]
|
522
|
+
ann.packs.each do |pack|
|
523
|
+
item = @data.allpack_to_item.delete(pack)
|
524
|
+
item.parent = nil
|
525
|
+
end
|
526
|
+
update_search_status_text
|
527
|
+
|
528
|
+
when XDCC_Download_Handler::ON_DOWNLOAD_STARTED
|
529
|
+
item = @data.downloadpack_to_item[args[0]]
|
530
|
+
item.fox_item.miniIcon = $cfg.icons.free_slots
|
531
|
+
item[3] = $cfg.text.download_started
|
532
|
+
@gui.active_list.sortItems
|
533
|
+
|
534
|
+
when XDCC_Download_Handler::ON_DOWNLOAD_FINISHED
|
535
|
+
pack = args[0]
|
536
|
+
item = @data.downloadpack_to_item[pack]
|
537
|
+
@gui.speed_widget.delete(item)
|
538
|
+
item.parent = @gui.completed_list
|
539
|
+
item.show
|
540
|
+
item.fox_item.miniIcon = $cfg.icons.completed
|
541
|
+
item[3] = $cfg.text.download_finished
|
542
|
+
@gui.completed_list.sortItems
|
543
|
+
# show "download finished" box
|
544
|
+
Download_Finished_Box.new(@gui.main_window, item[1])
|
545
|
+
|
546
|
+
when XDCC_Download_Handler::ON_MD5_STATUS
|
547
|
+
pack, status = args
|
548
|
+
item = @data.downloadpack_to_item[pack]
|
549
|
+
item[3] = case status
|
550
|
+
when XDCC_Download_Handler::MD5_DISABLED
|
551
|
+
$cfg.text.md5.disabled
|
552
|
+
when XDCC_Download_Handler::MD5_UNAVAILABLE
|
553
|
+
$cfg.text.md5.unavailable
|
554
|
+
when XDCC_Download_Handler::MD5_OK
|
555
|
+
$cfg.text.md5.ok
|
556
|
+
when XDCC_Download_Handler::MD5_FAILED
|
557
|
+
$cfg.text.md5.failed
|
558
|
+
end
|
559
|
+
@gui.completed_list.sortItems
|
560
|
+
|
561
|
+
when XDCC_Download_Handler::ON_DOWNLOAD_FAILED
|
562
|
+
pack, error_msg = args
|
563
|
+
item = @data.downloadpack_to_item[pack]
|
564
|
+
@gui.speed_widget.delete(item)
|
565
|
+
item.parent = @gui.error_list
|
566
|
+
item.show
|
567
|
+
item.fox_item.miniIcon = $cfg.icons.package_failed
|
568
|
+
item[3] = error_msg
|
569
|
+
|
570
|
+
when XDCC_Download_Handler::ON_DOWNLOAD_PROGRESS
|
571
|
+
pack, percentage, received_bytes, speed = args
|
572
|
+
item = @data.downloadpack_to_item[pack]
|
573
|
+
@gui.speed_widget[item] = speed
|
574
|
+
item[3] = sprintf("%.1f%%", percentage)
|
575
|
+
item[4] = sprintf("%.1fkb", speed)
|
576
|
+
@gui.active_list.sortItems
|
577
|
+
|
578
|
+
when XDCC_Download_Handler::ON_QUEUED_LOCALLY
|
579
|
+
pack, queue_pos, queue_length = args
|
580
|
+
if queue_length
|
581
|
+
queue_length = queue_length.to_s
|
582
|
+
else
|
583
|
+
queue_length = "?"
|
584
|
+
end
|
585
|
+
item = @data.downloadpack_to_item[pack]
|
586
|
+
item[3] = sprintf($cfg.text.queue_local + " %d/%s", queue_pos, queue_length)
|
587
|
+
@gui.active_list.sortItems
|
588
|
+
|
589
|
+
when XDCC_Download_Handler::ON_QUEUED_REMOTELY
|
590
|
+
pack, queue_pos, queue_length = args
|
591
|
+
if queue_length
|
592
|
+
queue_length = queue_length.to_s
|
593
|
+
else
|
594
|
+
queue_length = "?"
|
595
|
+
end
|
596
|
+
item = @data.downloadpack_to_item[pack]
|
597
|
+
return if !item
|
598
|
+
item[3] = sprintf($cfg.text.queue_remote + " %d/%s", queue_pos, queue_length)
|
599
|
+
@gui.active_list.sortItems
|
600
|
+
|
601
|
+
when XDCC_Download_Handler::ON_PACK_REQUESTED
|
602
|
+
pack = args[0]
|
603
|
+
item = @data.downloadpack_to_item[pack]
|
604
|
+
str = item[3]
|
605
|
+
|
606
|
+
if str.include?($cfg.text.download_requesting)
|
607
|
+
# animate with ... to visualize something is happening :-)
|
608
|
+
if str[-3..-1] == "..."
|
609
|
+
item[3] = $cfg.text.download_requesting
|
610
|
+
else
|
611
|
+
item[3] += "."
|
612
|
+
end
|
613
|
+
else
|
614
|
+
item[3] = $cfg.text.download_requesting
|
615
|
+
end
|
616
|
+
@gui.active_list.sortItems
|
617
|
+
|
618
|
+
when IRC_Server::ON_USER_KICK
|
619
|
+
irc_user, channel_name, kick_msg = args
|
620
|
+
if irc_user == caller.me
|
621
|
+
server_item = @data.irc_server_to_item[caller]
|
622
|
+
channel_item = @data.server_to_channels[server_item].detect { |channel_item| channel_item.text == channel_name }
|
623
|
+
channel_item.closedIcon = $cfg.icons.connection_failed
|
624
|
+
channel_item.openIcon = $cfg.icons.connection_failed
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
end
|