Piggy 0.4.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +91 -0
- data/INSTALL.txt +77 -0
- data/LICENCE.txt +339 -0
- data/README.txt +27 -0
- data/UNINSTALL.txt +24 -0
- data/bin/directory_diff +6 -0
- data/bin/ftp_browser +6 -0
- data/bin/piggy +6 -0
- data/lib/directory_diff.rb +35 -0
- data/lib/ftp_browser.rb +33 -0
- data/lib/icons/auto-select-for-del.ico +0 -0
- data/lib/icons/auto-select-right.ico +0 -0
- data/lib/icons/auto-upload-right.ico +0 -0
- data/lib/icons/back.ico +0 -0
- data/lib/icons/checked.ico +0 -0
- data/lib/icons/copy.png +0 -0
- data/lib/icons/cut.png +0 -0
- data/lib/icons/dir.ico +0 -0
- data/lib/icons/dirup.ico +0 -0
- data/lib/icons/file.ico +0 -0
- data/lib/icons/fileopen.png +0 -0
- data/lib/icons/filesave.ico +0 -0
- data/lib/icons/gallery.ico +0 -0
- data/lib/icons/generate.ico +0 -0
- data/lib/icons/minus.png +0 -0
- data/lib/icons/move-right-dir.ico +0 -0
- data/lib/icons/move-right-file.ico +0 -0
- data/lib/icons/newer-file.ico +0 -0
- data/lib/icons/next-image.ico +0 -0
- data/lib/icons/paste.png +0 -0
- data/lib/icons/plus.png +0 -0
- data/lib/icons/preview.png +0 -0
- data/lib/icons/previous-image.ico +0 -0
- data/lib/icons/reload.ico +0 -0
- data/lib/icons/remove-selected.ico +0 -0
- data/lib/icons/rotate.ico +0 -0
- data/lib/icons/slides.ico +0 -0
- data/lib/icons/stop-slides.ico +0 -0
- data/lib/icons/unchecked-dir.ico +0 -0
- data/lib/icons/unchecked.ico +0 -0
- data/lib/icons/upload-selected.ico +0 -0
- data/lib/icons/upload.ico +0 -0
- data/lib/piggy-core/alive_check.rb +44 -0
- data/lib/piggy-core/debug.rb +21 -0
- data/lib/piggy-core/encoding.rb +11 -0
- data/lib/piggy-core/environment.rb +66 -0
- data/lib/piggy-core/exifr_adapter.rb +85 -0
- data/lib/piggy-core/file_info.rb +218 -0
- data/lib/piggy-core/ftp_adapter.rb +106 -0
- data/lib/piggy-core/htmlgen.rb +242 -0
- data/lib/piggy-core/nconvert_thumbsgen.rb +73 -0
- data/lib/piggy-core/options.rb +116 -0
- data/lib/piggy-core/options_persistence.rb +54 -0
- data/lib/piggy-core/progress.rb +48 -0
- data/lib/piggy-core/rmagick_thumbnail_page_generator.rb +30 -0
- data/lib/piggy-core/thumbnail_generator.rb +79 -0
- data/lib/piggy-core/thumbnail_page_generator.rb +542 -0
- data/lib/piggy-core/upload_info.rb +41 -0
- data/lib/piggy-core/version.rb +18 -0
- data/lib/piggy-core/winshell.rb +240 -0
- data/lib/piggy-gui/directory_diff_widget.rb +398 -0
- data/lib/piggy-gui/filtered_file_list.rb +243 -0
- data/lib/piggy-gui/fox_thumbsgen.rb +18 -0
- data/lib/piggy-gui/ftp_browser_widget.rb +395 -0
- data/lib/piggy-gui/html_generation_dialog.rb +157 -0
- data/lib/piggy-gui/image_processor.rb +140 -0
- data/lib/piggy-gui/multiimagecanvas.rb +163 -0
- data/lib/piggy-gui/options_dialog.rb +85 -0
- data/lib/piggy-gui/piggy_image_browser.rb +776 -0
- data/lib/piggy-gui/pipe_log.rb +67 -0
- data/lib/piggy-gui/progress_with_dialog.rb +51 -0
- data/lib/piggy-gui/require-fox.rb +87 -0
- data/lib/piggy.rb +35 -0
- data/lib/templates/fuss.htm +10 -0
- data/lib/templates/kopf.htm +13 -0
- data/lib/templates/navigation.htm +11 -0
- data/lib/templates/slideshow.htm +129 -0
- data/lib/templates/slideshow.js +691 -0
- data/lib/templates/styles/basic/style.css +27 -0
- data/lib/templates/styles/black/style.css +64 -0
- data/lib/templates/styles/roundedbox/roundedbox_lo.gif +0 -0
- data/lib/templates/styles/roundedbox/roundedbox_lu.gif +0 -0
- data/lib/templates/styles/roundedbox/roundedbox_ro.gif +0 -0
- data/lib/templates/styles/roundedbox/roundedbox_ru.gif +0 -0
- data/lib/templates/styles/roundedbox/style.css +70 -0
- data/lib/templates/styles/shadow/lo.gif +0 -0
- data/lib/templates/styles/shadow/lu.gif +0 -0
- data/lib/templates/styles/shadow/ro.gif +0 -0
- data/lib/templates/styles/shadow/ru.gif +0 -0
- data/lib/templates/styles/shadow/style.css +63 -0
- data/test/file_info_test.rb +117 -0
- data/test.rb +8 -0
- data/web/IMAGE_PROCESSING.txt +53 -0
- data/web/INSTALL.txt +74 -0
- data/web/extern.gif +0 -0
- data/web/ftp-browser.png +0 -0
- data/web/index-de.html +60 -0
- data/web/index.html +57 -0
- data/web/piggy.png +0 -0
- data/web/style.css +14 -0
- metadata +177 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
## $Id: winshell.rb 176 2007-11-21 21:35:51Z Sascha $
|
3
|
+
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
# Windows oriented but platform independent file system abstraction.
|
7
|
+
class ShellInterface
|
8
|
+
def initialize
|
9
|
+
@specialFolderIds = {
|
10
|
+
"ALTSTARTUP" => 0x1d,
|
11
|
+
"APPDATA" => 0x1a,
|
12
|
+
"BITBUCKET" => 0xa,
|
13
|
+
"COMMONALTSTARTUP" => 0x1e,
|
14
|
+
"COMMONAPPDATA" => 0x23,
|
15
|
+
"COMMONDESKTOPDIR" => 0x19,
|
16
|
+
"COMMONFAVORITES" => 0x1f,
|
17
|
+
"COMMONPROGRAMS" => 0x17,
|
18
|
+
"COMMONSTARTMENU" => 0x16,
|
19
|
+
"COMMONSTARTUP" => 0x18,
|
20
|
+
"CONTROLS" => 0x3,
|
21
|
+
"COOKIES" => 0x21,
|
22
|
+
"DESKTOP" => 0x0,
|
23
|
+
"DESKTOPDIRECTORY" => 0x10,
|
24
|
+
"DRIVES" => 0x11,
|
25
|
+
"FAVORITES" => 0x6,
|
26
|
+
"FONTS" => 0x14,
|
27
|
+
"HISTORY" => 0x22,
|
28
|
+
"INTERNETCACHE" => 0x20,
|
29
|
+
"LOCALAPPDATA" => 0x1c,
|
30
|
+
"MYPICTURES" => 0x27,
|
31
|
+
"NETHOOD" => 0x13,
|
32
|
+
"NETWORK" => 0x12,
|
33
|
+
"PERSONAL" => 0x5,
|
34
|
+
"PRINTERS" => 0x4,
|
35
|
+
"PRINTHOOD" => 0x1b,
|
36
|
+
"PROFILE" => 0x28,
|
37
|
+
"PROGRAMFILES" => 0x26,
|
38
|
+
"PROGRAMS" => 0x2,
|
39
|
+
"RECENT" => 0x8,
|
40
|
+
"SENDTO" => 0x9,
|
41
|
+
"STARTMENU" => 0xb,
|
42
|
+
"STARTUP" => 0x7,
|
43
|
+
"SYSTEM" => 0x25,
|
44
|
+
"TEMPLATES" => 0x15,
|
45
|
+
"WINDOWS" => 0x24
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# try to provide a platform independent default
|
50
|
+
def pathForSpecialFolder(name)
|
51
|
+
home = ENV['HOME']
|
52
|
+
home = ENV['HOMEPATH'] unless home
|
53
|
+
home = '/' unless home
|
54
|
+
return home
|
55
|
+
end
|
56
|
+
|
57
|
+
def tmpPath
|
58
|
+
tmp = ENV['TEMP']
|
59
|
+
tmp = ENV['TMP'] unless tmp
|
60
|
+
tmp = '/' unless tmp
|
61
|
+
return tmp
|
62
|
+
end
|
63
|
+
|
64
|
+
# platform dependent path (no better default possible)
|
65
|
+
def osPath(pathString)
|
66
|
+
return winPath(pathString)
|
67
|
+
end
|
68
|
+
|
69
|
+
# for use in commandline execution
|
70
|
+
def escape(pathString)
|
71
|
+
return pathString
|
72
|
+
end
|
73
|
+
|
74
|
+
# old name, use osPath instead
|
75
|
+
def winPath(pathString)
|
76
|
+
return pathString
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get a path string usable by common ruby classes such as Dir and File.
|
80
|
+
# Default: convert '\' to '/' on all systems, even on Unix
|
81
|
+
def rubyPath(pathString)
|
82
|
+
return pathString.gsub(/\\/, separator)
|
83
|
+
end
|
84
|
+
|
85
|
+
def pictureDirectory
|
86
|
+
return pathForSpecialFolder("MYPICTURES")
|
87
|
+
end
|
88
|
+
|
89
|
+
def desktopDirectory
|
90
|
+
return pathForSpecialFolder("DESKTOPDIRECTORY")
|
91
|
+
end
|
92
|
+
|
93
|
+
def specialFolderListing
|
94
|
+
@specialFolderIds.keys.sort.each do |aFolder|
|
95
|
+
printf "#{aFolder}: "
|
96
|
+
puts "#{pathForSpecialFolder aFolder}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Get the path separator used by common ruby classes such as Dir and File.
|
101
|
+
def separator
|
102
|
+
return File::SEPARATOR
|
103
|
+
end
|
104
|
+
|
105
|
+
def removePath(filenameWithPath)
|
106
|
+
return rubyPath(filenameWithPath).split(separator)[-1]
|
107
|
+
end
|
108
|
+
|
109
|
+
# Is filename a Windows shortcut? - Default: no file is a shortcut
|
110
|
+
def link?(filename)
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
|
114
|
+
# Has filename the Extension ext (e. g. ".jpg")
|
115
|
+
def hasExtension?(filename, ext)
|
116
|
+
raise ArgumentError, "Extension missing" if ext.empty?
|
117
|
+
pExt = (ext[0] == '.'[0] ? ext : '.' + ext).downcase
|
118
|
+
dcFilename = filename.downcase
|
119
|
+
return File.basename(dcFilename, pExt) != File.basename(dcFilename)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Default access to file system abstraction.
|
125
|
+
# A platform dependent singleton will be instantiated.
|
126
|
+
class WinShell < ShellInterface
|
127
|
+
include Singleton
|
128
|
+
end
|
129
|
+
|
130
|
+
if RUBY_PLATFORM =~ /win32/
|
131
|
+
require 'win32ole'
|
132
|
+
|
133
|
+
class WinShell < ShellInterface
|
134
|
+
|
135
|
+
def initialize
|
136
|
+
@shell = WIN32OLE.new('Shell.application')
|
137
|
+
super
|
138
|
+
end
|
139
|
+
|
140
|
+
def pathForSpecialFolder(name)
|
141
|
+
return desktopDirectory if name=="DESKTOP"
|
142
|
+
oleFolder = specialFolder(name)
|
143
|
+
return "" if !oleFolder
|
144
|
+
return pathForFolder(specialFolder(name))
|
145
|
+
end
|
146
|
+
|
147
|
+
# Is filename a Windows shortcut?
|
148
|
+
def link?(filename)
|
149
|
+
return hasExtension?(filename, '.lnk')
|
150
|
+
end
|
151
|
+
|
152
|
+
# Get a path string usable by Windows systems.
|
153
|
+
def winPath(pathString)
|
154
|
+
return pathString.gsub(separator, "\\")
|
155
|
+
end
|
156
|
+
|
157
|
+
# Get a path string usable by common ruby classes such as Dir and File.
|
158
|
+
def rubyPath(pathString)
|
159
|
+
return pathString.gsub(/\\/, separator)
|
160
|
+
end
|
161
|
+
|
162
|
+
# for use in commandline execution
|
163
|
+
def escape(pathString)
|
164
|
+
return "\"#{pathString}\""
|
165
|
+
end
|
166
|
+
|
167
|
+
#Get the path stored in the Windows shortcut filename. PRE: link?(filename)
|
168
|
+
def getLinkTarget(filename, pathString = Dir.getwd)
|
169
|
+
raise(ArgumentError, "Kein Link") if !link?(filename)
|
170
|
+
filenameOnly = removePath(filename) # avoid errors ignore performance
|
171
|
+
ext = '.' + filenameOnly.split('.')[-1]
|
172
|
+
linkdef = File.basename(filenameOnly, ext)
|
173
|
+
raise ArgumentError, "Linkname leer" if linkdef.empty?
|
174
|
+
item = getFolderItems(pathString).find { |fi|
|
175
|
+
linkdef == (fi.Name.to_s) }
|
176
|
+
return item ? rubyPath(item.GetLink.Path.to_s) : ''
|
177
|
+
end
|
178
|
+
|
179
|
+
def pathForFolder(folder)
|
180
|
+
current = folder.ParentFolder
|
181
|
+
path = folder.Title
|
182
|
+
while current do
|
183
|
+
title = current.Title
|
184
|
+
if title == "Desktop" # winXP, nicht win2000
|
185
|
+
current = specialFolder("DESKTOPDIRECTORY").ParentFolder
|
186
|
+
title = current.Title
|
187
|
+
end
|
188
|
+
if title =~ /(.):/
|
189
|
+
title.gsub!(/.*\((\w:)\)/, '\1')
|
190
|
+
return title + separator + path
|
191
|
+
end
|
192
|
+
path = title + separator + path
|
193
|
+
current = current.ParentFolder
|
194
|
+
end
|
195
|
+
return path
|
196
|
+
end
|
197
|
+
|
198
|
+
# private; public for better testing only
|
199
|
+
|
200
|
+
# My shell is the ole object "Shell" as described in the Windows SDK.
|
201
|
+
def shell
|
202
|
+
return @shell
|
203
|
+
end
|
204
|
+
|
205
|
+
def specialFolder(name)
|
206
|
+
id = @specialFolderIds[name]
|
207
|
+
return shell.NameSpace(id)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Get Directory entries as ole-objects, not as strings!
|
211
|
+
def getFolderItems(directoryPathString)
|
212
|
+
folder = shell.NameSpace(winPath(directoryPathString))
|
213
|
+
raise "Folderermittlung fehlgeschlagen" if !folder
|
214
|
+
items = folder.items
|
215
|
+
return (0..items.Count-1).collect { |i| items.item(i) }
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
# Example
|
220
|
+
|
221
|
+
if $0 == __FILE__
|
222
|
+
def example
|
223
|
+
aShell = WinShell.instance
|
224
|
+
h = "Example 1: WinShell.new.specialFolderListing"
|
225
|
+
hLine = (1..h.size).collect { |c| '*'}
|
226
|
+
puts "#{hLine}\n#{h}\n#{hLine}"
|
227
|
+
aShell.specialFolderListing
|
228
|
+
h = "Example 2. Links on Desktop"
|
229
|
+
hLine = (1..h.size).collect { |c| '*'}
|
230
|
+
puts "#{hLine}\n#{h}\n#{hLine}"
|
231
|
+
desktopDir = aShell.desktopDirectory
|
232
|
+
links = Dir[desktopDir + '/*'].select { |p| aShell.link?(File.basename(p)) }
|
233
|
+
links.each do |l|
|
234
|
+
lName = File.basename l
|
235
|
+
puts "#{lName} => \"#{aShell.getLinkTarget lName, desktopDir}\""
|
236
|
+
end
|
237
|
+
end
|
238
|
+
example
|
239
|
+
end
|
240
|
+
|
@@ -0,0 +1,398 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# $Id: directory_diff_widget.rb 190 2008-02-02 18:52:57Z Sascha $
|
3
|
+
|
4
|
+
require 'piggy-core/environment'
|
5
|
+
require 'piggy-core/options'
|
6
|
+
require 'piggy-core/file_info'
|
7
|
+
require 'piggy-gui/require-fox'
|
8
|
+
require 'piggy-gui/image_processor'
|
9
|
+
|
10
|
+
# DirectoryDiffWidget shows differences between two directories.
|
11
|
+
class DirectoryDiffMainWindow < Fox::FXMainWindow
|
12
|
+
include Fox
|
13
|
+
def initialize(app, options = PiggyOptions.new)
|
14
|
+
super(app, titleString, nil, nil, DECOR_ALL,
|
15
|
+
0, 0, 200, 700, 0, 0)
|
16
|
+
@frame = FXVerticalFrame.new(self)
|
17
|
+
@frame.setLayoutHints(LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
18
|
+
setMainWidget(options)
|
19
|
+
@browser.quitButton.connect(SEL_COMMAND, method(:onTerminate))
|
20
|
+
end
|
21
|
+
|
22
|
+
def titleString
|
23
|
+
"Piggy Directory Diff"
|
24
|
+
end
|
25
|
+
|
26
|
+
def setMainWidget(options)
|
27
|
+
@browser = DirectoryDiffWidget.new(@frame, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def create
|
31
|
+
resize(800, 600)
|
32
|
+
place(PIGGY_WINDOW_PLACEMENT)
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def onTerminate(sender, sel, ptr)
|
37
|
+
@browser.prepareClosure
|
38
|
+
getApp.exit(0)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class DirectoryDiffWidget < Fox::FXScrollWindow
|
43
|
+
include Fox
|
44
|
+
Placement = PIGGY_WINDOW_PLACEMENT
|
45
|
+
MsgStyle = MBOX_OK|DECOR_TITLE|DECOR_CLOSE|PIGGY_WINDOW_PLACEMENT
|
46
|
+
|
47
|
+
attr_reader(:options, :quitButton)
|
48
|
+
|
49
|
+
def initialize(window, options = PiggyOptions.new)
|
50
|
+
super(window)
|
51
|
+
@options = options
|
52
|
+
@iconLoader = ImageProcessor.new(getApp)
|
53
|
+
loadIcons
|
54
|
+
initializeFrames
|
55
|
+
initializeControls
|
56
|
+
initializeLayout
|
57
|
+
setDefaults
|
58
|
+
connectEvents
|
59
|
+
@rightContents = DirectoryContents.new(rightPath)
|
60
|
+
@leftContents = DirectoryContents.new(leftPath)
|
61
|
+
@excludeFilters = ['~$', '#$', '^\.']
|
62
|
+
end
|
63
|
+
|
64
|
+
def loadIcons
|
65
|
+
@dirIcon = basicLoadIcon('dir.ico')
|
66
|
+
@fileIcon = basicLoadIcon('file.ico')
|
67
|
+
@dirupIcon = basicLoadIcon('dirup.ico')
|
68
|
+
@fileAbsentIcon = basicLoadIcon('unchecked.ico')
|
69
|
+
@dirAbsentIcon = basicLoadIcon('unchecked-dir.ico')
|
70
|
+
@newerFileIcon = basicLoadIcon('newer-file.ico')
|
71
|
+
end
|
72
|
+
|
73
|
+
def basicLoadIcon(filename)
|
74
|
+
fileWithPath = File.join(PIGGY_PATH, 'icons', filename)
|
75
|
+
@iconLoader.loadIcon(fileWithPath)
|
76
|
+
end
|
77
|
+
|
78
|
+
def spaceInto(frame)
|
79
|
+
space = FXHorizontalFrame.new(frame)
|
80
|
+
space.setLayoutHints(LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
81
|
+
end
|
82
|
+
|
83
|
+
def hSepInto(frame)
|
84
|
+
sep = FXHorizontalSeparator.new(frame, SEPARATOR_GROOVE)
|
85
|
+
sep.setLayoutHints(LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
86
|
+
end
|
87
|
+
|
88
|
+
def initializeFrames
|
89
|
+
@frame = FXVerticalFrame.new(self)
|
90
|
+
@fourSplitter = FX4Splitter.new(@frame, FOURSPLITTER_TRACKING)
|
91
|
+
@splitOL = FXVerticalFrame.new(@fourSplitter)
|
92
|
+
@splitOR = FXVerticalFrame.new(@fourSplitter)
|
93
|
+
@splitUL = FXVerticalFrame.new(@fourSplitter)
|
94
|
+
@splitUR = FXVerticalFrame.new(@fourSplitter)
|
95
|
+
@leftListPropFrame = FXMatrix.new(@splitOL, 2, MATRIX_BY_COLUMNS)
|
96
|
+
@rightListPropFrame = FXMatrix.new(@splitOR, 2, MATRIX_BY_COLUMNS)
|
97
|
+
@leftButtonsFrame = FXHorizontalFrame.new(@splitUL)
|
98
|
+
@rightButtonsFrame = FXHorizontalFrame.new(@splitUR)
|
99
|
+
@leftListFrame = FXHorizontalFrame.new(@splitUL)
|
100
|
+
@rightListFrame = FXHorizontalFrame.new(@splitUR)
|
101
|
+
@separator = FXHorizontalSeparator.new(@frame, SEPARATOR_GROOVE)
|
102
|
+
@buttonsFrame = FXHorizontalFrame.new(@frame)
|
103
|
+
end
|
104
|
+
|
105
|
+
def initializeControls
|
106
|
+
@labels = Array.new
|
107
|
+
initializeControlsOL
|
108
|
+
initializeControlsOR
|
109
|
+
initializeControlsUL
|
110
|
+
initializeControlsUR
|
111
|
+
initializeButtons
|
112
|
+
end
|
113
|
+
|
114
|
+
def initializeControlsOL
|
115
|
+
@labels.push(FXLabel.new(@leftListPropFrame,
|
116
|
+
"Dir:"))
|
117
|
+
@leftPathField = FXTextField.new(@leftListPropFrame, 32)
|
118
|
+
end
|
119
|
+
|
120
|
+
def initializeControlsOR
|
121
|
+
@labels.push(FXLabel.new(@rightListPropFrame, "Dir:"))
|
122
|
+
@rightPathField = FXTextField.new(@rightListPropFrame, 32)
|
123
|
+
end
|
124
|
+
|
125
|
+
def initializeControlsUL
|
126
|
+
@dirLeftUpButton = FXButton.new(@leftButtonsFrame,
|
127
|
+
"\tGo up to higher directory",
|
128
|
+
@dirupIcon)
|
129
|
+
@leftFileList = FXIconList.new(@leftListFrame)
|
130
|
+
@leftFileList.setListStyle(ICONLIST_DETAILED)
|
131
|
+
@leftFileList.appendHeader("Name", nil, 200)
|
132
|
+
@leftFileList.appendHeader("Date", nil, 200)
|
133
|
+
end
|
134
|
+
|
135
|
+
def initializeControlsUR
|
136
|
+
@dirRightUpButton = FXButton.new(@rightButtonsFrame,
|
137
|
+
"\tGo up to higher directory",
|
138
|
+
@dirupIcon)
|
139
|
+
@rightFileList = FXIconList.new(@rightListFrame)
|
140
|
+
@rightFileList.setListStyle(ICONLIST_DETAILED)
|
141
|
+
@rightFileList.appendHeader("Name", nil, 200)
|
142
|
+
@rightFileList.appendHeader("Date", nil, 200)
|
143
|
+
end
|
144
|
+
|
145
|
+
def initializeButtons
|
146
|
+
@quitButton = FXButton.new(@buttonsFrame, "&Quit")
|
147
|
+
end
|
148
|
+
|
149
|
+
def initializeLayout
|
150
|
+
@labels.each { |label| label.setLayoutHints LAYOUT_CENTER_Y }
|
151
|
+
[
|
152
|
+
self, @frame,
|
153
|
+
@fourSplitter,
|
154
|
+
# @leftListPropFrame, @rightListPropFrame,
|
155
|
+
# @splitUL, @splitUR,
|
156
|
+
# @leftButtonsFrame, @rightButtonsFrame,
|
157
|
+
@leftListFrame, @rightListFrame,
|
158
|
+
# @serverField, @userField, @passwordField, @leftPathField,
|
159
|
+
# @rightPathField,
|
160
|
+
@leftFileList, @rightFileList
|
161
|
+
].each { |w| w.setLayoutHints(LAYOUT_FILL_X|LAYOUT_FILL_Y) }
|
162
|
+
[
|
163
|
+
@splitOL, @splitOR, @splitUL, @splitUR,
|
164
|
+
@leftButtonsFrame, @rightButtonsFrame,
|
165
|
+
@leftListFrame, @rightListFrame
|
166
|
+
].each { |w| w.setFrameStyle(FRAME_NONE) }
|
167
|
+
@buttonsFrame.setLayoutHints(LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X)
|
168
|
+
@buttonsFrame.setPackingHints(PACK_UNIFORM_WIDTH)
|
169
|
+
@quitButton.setLayoutHints(LAYOUT_RIGHT)
|
170
|
+
end
|
171
|
+
|
172
|
+
def connectEvents
|
173
|
+
@rightPathField.connect(SEL_COMMAND) {
|
174
|
+
setRightPath(@rightPathField.getText)
|
175
|
+
updateRightList
|
176
|
+
updateStatusIcons
|
177
|
+
}
|
178
|
+
@leftPathField.connect(SEL_COMMAND) {
|
179
|
+
setLeftPath(@leftPathField.getText)
|
180
|
+
updateLeftList
|
181
|
+
updateStatusIcons
|
182
|
+
}
|
183
|
+
@dirLeftUpButton.connect(SEL_COMMAND, method(:onDirUpLeftList))
|
184
|
+
@dirRightUpButton.connect(SEL_COMMAND, method(:onDirUpRightList))
|
185
|
+
|
186
|
+
@leftFileList.connect(SEL_DOUBLECLICKED,
|
187
|
+
method(:onDoubleclickLeftFileList))
|
188
|
+
@rightFileList.connect(SEL_DOUBLECLICKED,
|
189
|
+
method(:onDoubleclickRightFileList))
|
190
|
+
@leftFileList.connect(SEL_RIGHTBUTTONRELEASE,
|
191
|
+
method(:onMenuLeftList))
|
192
|
+
@rightFileList.connect(SEL_RIGHTBUTTONRELEASE,
|
193
|
+
method(:onMenuRightList))
|
194
|
+
end
|
195
|
+
|
196
|
+
def setDefaults
|
197
|
+
@rightPathField.setText(@options.localDestinationPath)
|
198
|
+
@leftPathField.setText(@options.localDestinationPath)
|
199
|
+
end
|
200
|
+
|
201
|
+
# accessing
|
202
|
+
|
203
|
+
def rightPath
|
204
|
+
@options.localDestinationPath
|
205
|
+
end
|
206
|
+
|
207
|
+
def setRightPath(p)
|
208
|
+
@options.localDestinationPath = FilePath.internPath(p)
|
209
|
+
@rightPathField.setText(p) # fixme winpath
|
210
|
+
end
|
211
|
+
|
212
|
+
def leftPath
|
213
|
+
FilePath.internPath(@leftPathField.getText)
|
214
|
+
end
|
215
|
+
|
216
|
+
def setLeftPath(p)
|
217
|
+
@leftPathField.setText(p) # fixme winpath
|
218
|
+
end
|
219
|
+
|
220
|
+
def selectionInList(list)
|
221
|
+
selectedInd = (0..list.getNumItems-1).select { |i|
|
222
|
+
list.isItemSelected(i)
|
223
|
+
}
|
224
|
+
return selectedInd.collect { |i| itemFilename(list, i) }
|
225
|
+
end
|
226
|
+
|
227
|
+
def itemFilename(list, index)
|
228
|
+
txt = fox2Ruby(list.getItemText(index))
|
229
|
+
return txt.split(Regexp.compile("\t"))[0]
|
230
|
+
end
|
231
|
+
|
232
|
+
# update
|
233
|
+
|
234
|
+
def updateLeftList
|
235
|
+
@leftContents = updateLocalList(@leftFileList, leftPath)
|
236
|
+
end
|
237
|
+
|
238
|
+
def updateRightList
|
239
|
+
@rightContents = updateLocalList(@rightFileList, rightPath)
|
240
|
+
end
|
241
|
+
|
242
|
+
def updateLocalList(listWidget, newPath)
|
243
|
+
listWidget.clearItems
|
244
|
+
contents = DirectoryContents.new(newPath)
|
245
|
+
return contents unless newPath && File.directory?(newPath)
|
246
|
+
getApp.beginWaitCursor
|
247
|
+
begin
|
248
|
+
updateList(Dir.entries(newPath), listWidget, contents) do
|
249
|
+
|file, path|
|
250
|
+
info = FileInfo.new(file, path)
|
251
|
+
info.mtime = File.stat(info.nameWithPath).mtime
|
252
|
+
info.directory! if File.directory?(info.nameWithPath)
|
253
|
+
info
|
254
|
+
end
|
255
|
+
rescue StandardError => ex
|
256
|
+
error(ex.message)
|
257
|
+
end
|
258
|
+
getApp.endWaitCursor
|
259
|
+
return contents
|
260
|
+
end
|
261
|
+
|
262
|
+
def includeFile?(filename)
|
263
|
+
@excludeFilters.select { |pattern|
|
264
|
+
Regexp.compile(pattern) =~ filename
|
265
|
+
}.empty?
|
266
|
+
end
|
267
|
+
|
268
|
+
def updateList(dirEntries, listWidget, contents, &getInfoblock)
|
269
|
+
listWidget.clearItems
|
270
|
+
items = Hash.new
|
271
|
+
dirEntries.each do
|
272
|
+
|file|
|
273
|
+
if includeFile?(file)
|
274
|
+
info = getInfoblock.call(file, contents.path)
|
275
|
+
text = ruby2Fox("#{info.name}\t#{info.mtimeString}")
|
276
|
+
if info.directory?
|
277
|
+
items[file] = FXIconItem.new(text, nil, @dirIcon)
|
278
|
+
else
|
279
|
+
items[file] = FXIconItem.new(text, nil, @fileIcon)
|
280
|
+
end
|
281
|
+
contents.add(info)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
contents.directories.each { |n| listWidget.appendItem(items[n]) }
|
285
|
+
contents.files.each { |n| listWidget.appendItem(items[n]) }
|
286
|
+
end
|
287
|
+
|
288
|
+
def updateStatusIconsFor(listWidget, myContents, otherContents)
|
289
|
+
(0..listWidget.getNumItems - 1).each do
|
290
|
+
|index|
|
291
|
+
file = itemFilename(listWidget, index)
|
292
|
+
myInfo = myContents[file]
|
293
|
+
otherInfo = otherContents[file]
|
294
|
+
icon = if myInfo.directory?
|
295
|
+
otherInfo ? @dirIcon : @dirAbsentIcon
|
296
|
+
else
|
297
|
+
if otherInfo
|
298
|
+
otherInfo.mtime < myInfo.mtime ? @newerFileIcon : @fileIcon
|
299
|
+
else
|
300
|
+
@fileAbsentIcon
|
301
|
+
end
|
302
|
+
end
|
303
|
+
listWidget.setItemMiniIcon(index, icon)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def updateStatusIconsLeft
|
308
|
+
updateStatusIconsFor(@leftFileList, @leftContents, @rightContents)
|
309
|
+
end
|
310
|
+
|
311
|
+
def updateStatusIconsRight
|
312
|
+
updateStatusIconsFor(@rightFileList, @rightContents, @leftContents)
|
313
|
+
end
|
314
|
+
|
315
|
+
def updateStatusIcons
|
316
|
+
updateStatusIconsLeft
|
317
|
+
updateStatusIconsRight
|
318
|
+
end
|
319
|
+
|
320
|
+
# messages
|
321
|
+
|
322
|
+
def error(msg, title = 'Error')
|
323
|
+
FXMessageBox::warning(self, MsgStyle, title, msg)
|
324
|
+
return false
|
325
|
+
end
|
326
|
+
|
327
|
+
# actions
|
328
|
+
|
329
|
+
# events
|
330
|
+
|
331
|
+
def onDirUpLeftList(sender, sel, ptr)
|
332
|
+
return false unless leftPath
|
333
|
+
suggestion = FilePath.oneDirUp(leftPath)
|
334
|
+
return false if leftPath == suggestion
|
335
|
+
setLeftPath(suggestion)
|
336
|
+
updateLeftList
|
337
|
+
updateStatusIcons
|
338
|
+
return true
|
339
|
+
end
|
340
|
+
|
341
|
+
def onDirUpRightList(sender, sel, ptr)
|
342
|
+
return false unless rightPath
|
343
|
+
suggestion = FilePath.oneDirUp(rightPath)
|
344
|
+
return false if rightPath == suggestion
|
345
|
+
setRightPath(suggestion)
|
346
|
+
updateRightList
|
347
|
+
updateStatusIcons
|
348
|
+
return true
|
349
|
+
end
|
350
|
+
|
351
|
+
def onDoubleclickLeftFileList(sender, sel, index)
|
352
|
+
fileOrFolder = @leftFileList.getItemText(index)
|
353
|
+
file = fileOrFolder.split(Regexp.compile("\t"))[0]
|
354
|
+
info = @leftContents[file]
|
355
|
+
return false unless info && info.directory?
|
356
|
+
fileWithPath = FilePath.join(leftPath, file)
|
357
|
+
setLeftPath(fileWithPath)
|
358
|
+
updateLeftList
|
359
|
+
updateStatusIcons
|
360
|
+
return true
|
361
|
+
end
|
362
|
+
|
363
|
+
def onDoubleclickRightFileList(sender, sel, index)
|
364
|
+
fileOrFolder = @rightFileList.getItemText(index)
|
365
|
+
file = fileOrFolder.split(Regexp.compile("\t"))[0]
|
366
|
+
info = @rightContents[file]
|
367
|
+
return false unless info && info.directory?
|
368
|
+
fileWithPath = FilePath.join(rightPath, file)
|
369
|
+
setRightPath(fileWithPath)
|
370
|
+
updateRightList
|
371
|
+
updateStatusIcons
|
372
|
+
return true
|
373
|
+
end
|
374
|
+
|
375
|
+
def onMenuLeftList(sender, sel, event)
|
376
|
+
end
|
377
|
+
|
378
|
+
def onMenuRightList(sender, sel, event)
|
379
|
+
end
|
380
|
+
|
381
|
+
# open/close
|
382
|
+
|
383
|
+
def inCreate
|
384
|
+
updateLeftList
|
385
|
+
updateRightList
|
386
|
+
updateStatusIcons
|
387
|
+
@fourSplitter.setVSplit(600)
|
388
|
+
end
|
389
|
+
|
390
|
+
def create
|
391
|
+
inCreate
|
392
|
+
super
|
393
|
+
end
|
394
|
+
|
395
|
+
def prepareClosure
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|