XDCC-Fetch 1.386
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|