unixcmd 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/bin/unixcmd +96 -0
- data/lib/unixcmd/aux.rb +157 -0
- data/lib/unixcmd/cmd.rb +217 -0
- data/lib/unixcmd/config.rb +10 -0
- data/lib/unixcmd/dirview.rb +187 -0
- data/lib/unixcmd/panel.rb +159 -0
- data/lib/unixcmd/version.rb +2 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
OTdhMmZkYzFhNGI4YWY4MTFiZDA3MjM3NWY3ZmU3ZDEzZTk0OTI2OQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTcwNDMzOWZmMDMxNGMyNDlkNWM5ZGVhYTE2MDVlOTBmZjY0MjRjNA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YjNmOTcyYmEzYjUwMWU2ZTRiNWYyZDMzNmJlMTUyYzEyYTkxNTQ3MDA3ZTBk
|
10
|
+
MTNiZGY4ZWMyNGYyZTFlN2NlMTQxNTIyMmI4NGVlMjQzYjNiMDFlY2RlYzJl
|
11
|
+
YTJjZTIwZWYwNmNhMjhkYzIzNzQ0MGI1MGZlN2NlMjk1ZDU4YzY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Mjg4YzE0YjFlZjlkYWE0Nzk0ZWMwOGNiMWVkMjhhZGEwMWIzN2JiMTYxOWUz
|
14
|
+
MDBjM2Y0YTZmOTgyMjQ0MDBmNmU5NmJkMmFmZDY5ZWQxOTI2OGZmZTk2MTBh
|
15
|
+
MzExMDMxZGQ1MjcwNGIwYTI2YTg0Y2U4NmUwMzIyYjNkNzU2ZDA=
|
data/bin/unixcmd
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/ruby19
|
2
|
+
|
3
|
+
require 'gtk2'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
require 'unixcmd/panel'
|
7
|
+
require 'unixcmd/version'
|
8
|
+
require 'unixcmd/config'
|
9
|
+
|
10
|
+
|
11
|
+
class CmdWnd < Gtk::Window
|
12
|
+
@vbox
|
13
|
+
@btnpanel
|
14
|
+
@panes
|
15
|
+
@left
|
16
|
+
@right
|
17
|
+
|
18
|
+
def left() @left end
|
19
|
+
def right() @right end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
|
24
|
+
set_title APP_NAME + ' ' + APP_VERSION
|
25
|
+
|
26
|
+
set_border_width 5
|
27
|
+
|
28
|
+
@vbox = Gtk::VBox.new
|
29
|
+
@panes = Gtk::HPaned.new
|
30
|
+
@left = CmdPanelWidget.new
|
31
|
+
@right = CmdPanelWidget.new
|
32
|
+
|
33
|
+
@panes.pack1 @left, true, true
|
34
|
+
@panes.pack2 @right, true, true
|
35
|
+
|
36
|
+
@vbox.pack_start @panes, true
|
37
|
+
|
38
|
+
add @vbox
|
39
|
+
|
40
|
+
signal_connect('destroy') { destroy }
|
41
|
+
|
42
|
+
self.focus_chain = [@left, @right]
|
43
|
+
|
44
|
+
set_default_size 1024, 768
|
45
|
+
show_all
|
46
|
+
end
|
47
|
+
|
48
|
+
def curpanel
|
49
|
+
return @left if self.focus.parent.parent.parent == @left
|
50
|
+
@right
|
51
|
+
end
|
52
|
+
|
53
|
+
def otherpanel
|
54
|
+
return @left if self.focus.parent.parent.parent == @right
|
55
|
+
@right
|
56
|
+
end
|
57
|
+
|
58
|
+
def selected_file
|
59
|
+
curpanel().path + curpanel().selection[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
def destroy
|
63
|
+
Gtk.main_quit
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
GLib::Thread.init
|
69
|
+
Gdk::Threads.init
|
70
|
+
Gdk::Threads.enter
|
71
|
+
|
72
|
+
Gtk::AccelMap.add_entry '<unixcmd>/panel/reload', Gdk::Keyval::GDK_F2, nil
|
73
|
+
|
74
|
+
Gtk::AccelMap.add_entry '<unixcmd>/file/view', Gdk::Keyval::GDK_F3, nil
|
75
|
+
Gtk::AccelMap.add_entry '<unixcmd>/file/edit', Gdk::Keyval::GDK_F4, nil
|
76
|
+
Gtk::AccelMap.add_entry '<unixcmd>/file/copy', Gdk::Keyval::GDK_F5, nil
|
77
|
+
Gtk::AccelMap.add_entry '<unixcmd>/file/move', Gdk::Keyval::GDK_F6, nil
|
78
|
+
Gtk::AccelMap.add_entry '<unixcmd>/file/mkdir', Gdk::Keyval::GDK_F7, nil
|
79
|
+
Gtk::AccelMap.add_entry '<unixcmd>/file/remove', Gdk::Keyval::GDK_F8, Gdk::Window::SHIFT_MASK
|
80
|
+
|
81
|
+
Gtk::AccelMap.add_entry '<unixcmd>/test/print_selection', Gdk::Keyval::GDK_F11, nil
|
82
|
+
|
83
|
+
Gtk::AccelMap.add_entry '<unixcmd>/file/info', Gdk::Keyval::GDK_Return, Gdk::Window::MOD1_MASK
|
84
|
+
|
85
|
+
Gtk::AccelMap.add_entry '<unixcmd>/app/quit', Gdk::Keyval::GDK_F4, Gdk::Window::MOD1_MASK
|
86
|
+
|
87
|
+
|
88
|
+
require 'unixcmd/cmd'
|
89
|
+
|
90
|
+
|
91
|
+
$wnd = CmdWnd.new
|
92
|
+
$wnd.add_accel_group $accels
|
93
|
+
|
94
|
+
Gtk.main
|
95
|
+
|
96
|
+
Gdk::Threads.leave
|
data/lib/unixcmd/aux.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# TODO: find more elegance solution
|
2
|
+
class Fixnum
|
3
|
+
def to_rwx
|
4
|
+
res = '----------'
|
5
|
+
|
6
|
+
if self&(1<<0) > 0 then res[res.size-1] = 'x' end
|
7
|
+
if self&(1<<1) > 0 then res[res.size-2] = 'w' end
|
8
|
+
if self&(1<<2) > 0 then res[res.size-3] = 'r' end
|
9
|
+
|
10
|
+
if self&(1<<3) > 0 then res[res.size-4] = 'x' end
|
11
|
+
if self&(1<<4) > 0 then res[res.size-5] = 'w' end
|
12
|
+
if self&(1<<5) > 0 then res[res.size-6] = 'r' end
|
13
|
+
|
14
|
+
if self&(1<<6) > 0 then res[res.size-7] = 'x' end
|
15
|
+
if self&(1<<7) > 0 then res[res.size-8] = 'w' end
|
16
|
+
if self&(1<<8) > 0 then res[res.size-9] = 'r' end
|
17
|
+
|
18
|
+
if (self>>9) == 040 then res[res.size-10] = 'd' end
|
19
|
+
|
20
|
+
res
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# TODO: Bug in Pathname
|
25
|
+
class Pathname
|
26
|
+
alias_method :old_plus, :+
|
27
|
+
alias_method :old_exist, :exist?
|
28
|
+
alias_method :old_directory, :directory?
|
29
|
+
alias_method :old_entries, :entries
|
30
|
+
alias_method :old_readable_real, :readable_real?
|
31
|
+
alias_method :old_writable_real, :writable_real?
|
32
|
+
alias_method :old_mkdir, :mkdir
|
33
|
+
|
34
|
+
def + (pathname)
|
35
|
+
return old_plus pathname if pathname.class != Pathname || to_s != '~' || pathname.to_s != '..'
|
36
|
+
|
37
|
+
expand_path.old_plus pathname
|
38
|
+
end
|
39
|
+
|
40
|
+
def exist?() expand_path.old_exist end
|
41
|
+
def directory?() expand_path.old_directory end
|
42
|
+
def entries() expand_path.old_entries end
|
43
|
+
def readable_real?() expand_path.old_readable_real end
|
44
|
+
def writable_real?() expand_path.old_writable_real end
|
45
|
+
def mkdir() expand_path.old_mkdir end
|
46
|
+
end
|
47
|
+
|
48
|
+
# TODO: find another way
|
49
|
+
UNITS = %W(k M G T).freeze
|
50
|
+
|
51
|
+
class Integer
|
52
|
+
def to_szstr
|
53
|
+
number = self
|
54
|
+
if number < 1024
|
55
|
+
exponent = 0
|
56
|
+
else
|
57
|
+
max_exp = UNITS.size - 1
|
58
|
+
|
59
|
+
exponent = ( Math.log(number) / Math.log(1024) ).to_i # convert to base
|
60
|
+
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
|
61
|
+
|
62
|
+
number /= 1024 ** exponent
|
63
|
+
end
|
64
|
+
|
65
|
+
"#{number}" + (exponent > 0 ? " #{UNITS[exponent-1]}" : '')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# TODO: bug in Time::strftime
|
70
|
+
class Time
|
71
|
+
alias_method :old_strftime, :strftime
|
72
|
+
|
73
|
+
def strftime(format)
|
74
|
+
if ENV['LANG'] == 'ru_RU.UTF-8' then
|
75
|
+
format = format.sub '%x', '%d.%m.%Y'
|
76
|
+
end
|
77
|
+
|
78
|
+
return old_strftime(format)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class MIME::Types
|
83
|
+
def self.get_icon_names(filepath)
|
84
|
+
types = MIME::Types.of filepath
|
85
|
+
return nil if types == nil || types.count == 0
|
86
|
+
script_path = Pathname.new(__FILE__).expand_path.dirname.to_s
|
87
|
+
res = `#{script_path}/gnome-get-filetype-icon #{types[0]}`
|
88
|
+
return (res.strip.split ' ').drop(2) if $? == 0
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.get_icon_path(filepath)
|
93
|
+
icons_path = Pathname.new '/usr/share/icons/gnome/16x16'
|
94
|
+
|
95
|
+
return icons_path + Pathname.new('places/folder.png') if filepath.directory?
|
96
|
+
|
97
|
+
icon_names = MIME::Types.get_icon_names(filepath.to_s)
|
98
|
+
|
99
|
+
return icons_path + Pathname.new('mimetypes/text-x-generic.png') if icon_names == nil
|
100
|
+
|
101
|
+
icon_names.each do |icon|
|
102
|
+
icon_path = icons_path + Pathname.new('mimetypes') + Pathname.new(icon.to_s + '.png')
|
103
|
+
|
104
|
+
return icon_path.to_s if icon_path.exist?
|
105
|
+
end
|
106
|
+
|
107
|
+
icons_path + Pathname.new('mimetypes/text-x-generic.png')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Gtk::Widget
|
112
|
+
def connect_bypath(path)
|
113
|
+
signal_connect('clicked') { $actMap[path].call }
|
114
|
+
self
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# continue from exception
|
119
|
+
require 'continuation'
|
120
|
+
|
121
|
+
class Exception
|
122
|
+
class NoContinuation < StandardError
|
123
|
+
end
|
124
|
+
|
125
|
+
attr_accessor :continuation
|
126
|
+
|
127
|
+
def continue
|
128
|
+
raise NoContinuation unless continuation.respond_to?(:call)
|
129
|
+
continuation.call
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
module NeverGonnaLetYouDown
|
134
|
+
def raise(exception = RuntimeError, string = nil, array = caller)
|
135
|
+
# With a single String argument, raises a
|
136
|
+
# RuntimeError with the string as a message.
|
137
|
+
if exception.is_a?(String)
|
138
|
+
string = exception
|
139
|
+
exception = RuntimeError
|
140
|
+
end
|
141
|
+
|
142
|
+
callcc do |cc|
|
143
|
+
obj = exception.exception(string)
|
144
|
+
obj.set_backtrace(array)
|
145
|
+
obj.continuation = cc
|
146
|
+
super obj
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def fail(exception = RuntimeError, string = nil, array = caller)
|
151
|
+
raise(exception, string, array)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class Object
|
156
|
+
include NeverGonnaLetYouDown
|
157
|
+
end
|
data/lib/unixcmd/cmd.rb
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
$accels = Gtk::AccelGroup.new
|
2
|
+
|
3
|
+
$actMap = Hash.new
|
4
|
+
|
5
|
+
require 'thread'
|
6
|
+
|
7
|
+
#require_relative './copier.rb'
|
8
|
+
#require_relative './copyctrl.rb'
|
9
|
+
#require_relative './fileinfodlg.rb'
|
10
|
+
|
11
|
+
$actMap['<unixcmd>/panel/reload'] = Proc.new { $wnd.curpanel.reload }
|
12
|
+
|
13
|
+
$actMap['<unixcmd>/file/copy'] = Proc.new { cmd_file_copy $wnd.curpanel.selection, $wnd.curpanel.path, $wnd.otherpanel.path }
|
14
|
+
$actMap['<unixcmd>/file/remove'] = Proc.new { cmd_file_remove $wnd.curpanel.selection }
|
15
|
+
|
16
|
+
$actMap['<unixcmd>/file/view'] = Proc.new { cmd_file_view $wnd.selected_file }
|
17
|
+
$actMap['<unixcmd>/file/edit'] = Proc.new { cmd_file_edit $wnd.selected_file }
|
18
|
+
|
19
|
+
$actMap['<unixcmd>/file/info'] = Proc.new { cmd_file_info $wnd.selected_file }
|
20
|
+
|
21
|
+
$actMap['<unixcmd>/app/quit'] = Proc.new { $wnd.destroy }
|
22
|
+
|
23
|
+
$actMap['<unixcmd>/test/print_selection'] = Proc.new { p $wnd.curpanel.selection }
|
24
|
+
|
25
|
+
$actMap.each_key do |key|
|
26
|
+
$accels.connect(key, &$actMap[key])
|
27
|
+
end
|
28
|
+
|
29
|
+
#module MyCopyModel
|
30
|
+
# include CopyModel
|
31
|
+
#
|
32
|
+
# def cp(src, dst)
|
33
|
+
# return if src == dst
|
34
|
+
#
|
35
|
+
# p [:cp, [src, dst]]
|
36
|
+
#
|
37
|
+
# srcfile = File.new src.expand_path, 'rb'
|
38
|
+
# dstfile = File.new dst.expand_path, 'wb'
|
39
|
+
# copied_sum = 0
|
40
|
+
# size = srcfile.stat.size
|
41
|
+
# block_size = $cfg['copy']['block_size']
|
42
|
+
# time_start = Time::now.to_f
|
43
|
+
# elapsed = 0.0
|
44
|
+
#
|
45
|
+
# begin
|
46
|
+
# copied = IO::copy_stream srcfile, dstfile, block_size
|
47
|
+
# copied_sum += copied
|
48
|
+
#
|
49
|
+
# view.file_progress copied_sum.to_f/size.to_f
|
50
|
+
#
|
51
|
+
# elapsed = Time::now.to_f - time_start
|
52
|
+
# view.file_speed (copied_sum/elapsed).to_i if elapsed > 0
|
53
|
+
# end while copied > 0
|
54
|
+
#
|
55
|
+
# nil
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# def mkdir(path)
|
59
|
+
# p [:mkdir, [path]]
|
60
|
+
# path.mkdir
|
61
|
+
# end
|
62
|
+
#end
|
63
|
+
|
64
|
+
#module MyCopyView
|
65
|
+
# include CopyView
|
66
|
+
#
|
67
|
+
# class << View
|
68
|
+
# @queue
|
69
|
+
#
|
70
|
+
# def queue=(queue) @queue = queue end
|
71
|
+
#
|
72
|
+
# def start() end
|
73
|
+
# def end() @queue.push [:end] end
|
74
|
+
#
|
75
|
+
# def file_start(src, dst) @queue.push [:file_start, [src, dst]] end
|
76
|
+
#
|
77
|
+
# def file_end() end
|
78
|
+
#
|
79
|
+
# def file_progress(percent) @queue.push [:file_progress, [percent]] end
|
80
|
+
# def progress(percent) @queue.push [:progress, [percent]] end
|
81
|
+
# def dir_create(path) @queue.push [:dir_create, [path]] end
|
82
|
+
#
|
83
|
+
# def file_speed(speed) @queue.push [:file_speed, [speed]] end
|
84
|
+
# end
|
85
|
+
#end
|
86
|
+
|
87
|
+
def cmd_file_copy(files, srcdir, dstdir)
|
88
|
+
# (dlg = CopyOptionsDlg.new files, srcdir, dstdir).run do |res|
|
89
|
+
# case res
|
90
|
+
# when Gtk::Dialog::RESPONSE_CANCEL, Gtk::Dialog::RESPONSE_DELETE_EVENT
|
91
|
+
# dlg.destroy
|
92
|
+
# return
|
93
|
+
# when Gtk::Dialog::RESPONSE_OK then dlg.destroy
|
94
|
+
# else
|
95
|
+
# raise ArgumentError, res.to_s
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# queue = Queue.new
|
100
|
+
#
|
101
|
+
# copier = Copier.new dlg.files, dlg.src, dlg.dst, false, false
|
102
|
+
# copier.view = MyCopyView
|
103
|
+
# copier.model = MyCopyModel
|
104
|
+
# copier.view.queue = queue
|
105
|
+
#
|
106
|
+
# dlg = CopyDlg.new
|
107
|
+
#
|
108
|
+
# cpthread = Thread.new do
|
109
|
+
# begin
|
110
|
+
# copier.run
|
111
|
+
# rescue => err
|
112
|
+
# print 'error: '
|
113
|
+
# p err.hash[:type]
|
114
|
+
#
|
115
|
+
# if err.hash[:type] == :not_exist
|
116
|
+
# p err.hash[:path]
|
117
|
+
# #err.hash[:stop] = true
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# if err.hash[:type] == :srcdstdifftype
|
121
|
+
# p err.hash[:src]
|
122
|
+
# p err.hash[:dst]
|
123
|
+
# #err.hash[:stop] = true
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# if err.hash[:type] == :access_denied
|
127
|
+
# p err.hash[:path]
|
128
|
+
# #err.hash[:stop] = true
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# if err.hash[:type] == :exist
|
132
|
+
# p err.hash[:path]
|
133
|
+
# err.hash[:overwrite] = true if err.hash[:path].basename.to_s == '6'
|
134
|
+
# #err.hash[:stop] = true
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# err.continue
|
138
|
+
# end
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# cpthread.abort_on_exception = true
|
142
|
+
#
|
143
|
+
# idle = Gtk.idle_add do
|
144
|
+
# if !queue.empty?
|
145
|
+
# call = queue.pop
|
146
|
+
# args = call[1]
|
147
|
+
#
|
148
|
+
# dlg.dispatch_model_call call[0], args
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# true
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# dlg.run do |res|
|
155
|
+
# case res
|
156
|
+
# when Gtk::Dialog::RESPONSE_CANCEL then copy.stop
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# Gtk.idle_remove idle
|
161
|
+
# cpthread.join
|
162
|
+
# cpthread.exit
|
163
|
+
end
|
164
|
+
|
165
|
+
def cmd_file_remove(files)
|
166
|
+
files.each do |file|
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def cmd_file_view(file)
|
171
|
+
fullpath = file.expand_path
|
172
|
+
|
173
|
+
return if fullpath.directory?
|
174
|
+
|
175
|
+
if file.expand_path.directory?
|
176
|
+
filetype = 'directory'
|
177
|
+
else
|
178
|
+
filetype = MIME::Types.of fullpath.to_s
|
179
|
+
filetype = 'binary (undefined)' if filetype == nil || filetype.count == 0
|
180
|
+
filetype = filetype[0].to_s if filetype.class == Array
|
181
|
+
end
|
182
|
+
|
183
|
+
viewer = 'xdg-open'
|
184
|
+
|
185
|
+
viewer = $cfg['viewers'][filetype] if $cfg['viewers'].has_key? filetype
|
186
|
+
|
187
|
+
Open3.popen3 viewer, fullpath.to_s
|
188
|
+
end
|
189
|
+
|
190
|
+
def cmd_file_edit(file)
|
191
|
+
fullpath = file.expand_path
|
192
|
+
|
193
|
+
return if fullpath.directory?
|
194
|
+
|
195
|
+
if file.expand_path.directory?
|
196
|
+
filetype = 'directory'
|
197
|
+
else
|
198
|
+
filetype = MIME::Types.of fullpath.to_s
|
199
|
+
filetype = 'binary (undefined)' if filetype == nil || filetype.count == 0
|
200
|
+
filetype = filetype[0].to_s if filetype.class == Array
|
201
|
+
end
|
202
|
+
|
203
|
+
editor = 'xdg-open'
|
204
|
+
|
205
|
+
editor = $cfg['editors'][filetype] if $cfg['editors'].has_key? filetype
|
206
|
+
|
207
|
+
Open3.popen3 editor, fullpath.to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
def cmd_file_info(file)
|
211
|
+
dlg = FileInfoDlg.new file
|
212
|
+
dlg.run
|
213
|
+
dlg.destroy
|
214
|
+
end
|
215
|
+
|
216
|
+
# vim: sw=2 sts=2 ts=8:
|
217
|
+
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'launchy'
|
2
|
+
require 'mime/types'
|
3
|
+
require 'unixcmd/aux'
|
4
|
+
|
5
|
+
|
6
|
+
class CmdDirWidget < Gtk::Frame
|
7
|
+
@path
|
8
|
+
@view
|
9
|
+
|
10
|
+
def view() @view end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super
|
14
|
+
|
15
|
+
model = Gtk::ListStore.new String, String, String, String, String, String, Gdk::Pixbuf, Integer
|
16
|
+
@view = Gtk::TreeView.new model
|
17
|
+
|
18
|
+
Gtk::VScrollbar.new @view.vadjustment
|
19
|
+
|
20
|
+
@view.signal_connect('row-activated') { |view, path, column| open(path) }
|
21
|
+
|
22
|
+
scrollwnd = Gtk::ScrolledWindow.new
|
23
|
+
scrollwnd.set_policy Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC
|
24
|
+
|
25
|
+
scrollwnd.add @view
|
26
|
+
|
27
|
+
add_child Gtk::Builder.new, scrollwnd
|
28
|
+
|
29
|
+
checker = Gtk::CellRendererToggle.new
|
30
|
+
checker.signal_connect('toggled') { |s, path| iter = @view.model.get_iter(path); iter[7] = ~iter[7]; }
|
31
|
+
|
32
|
+
@view.signal_connect('key-press-event') { |s, event| key_pressed(event) }
|
33
|
+
|
34
|
+
cols = [ Gtk::TreeViewColumn.new('', checker, :active => 7 ),
|
35
|
+
Gtk::TreeViewColumn.new('Name'),
|
36
|
+
Gtk::TreeViewColumn.new('Ext', Gtk::CellRendererText.new, :text => 2),
|
37
|
+
Gtk::TreeViewColumn.new('Size', Gtk::CellRendererText.new, :text => 3),
|
38
|
+
Gtk::TreeViewColumn.new('Date', Gtk::CellRendererText.new, :text => 4),
|
39
|
+
Gtk::TreeViewColumn.new('Attr', Gtk::CellRendererText.new, :text => 5) ]
|
40
|
+
|
41
|
+
renderer = Gtk::CellRendererPixbuf.new
|
42
|
+
cols[1].pack_start renderer, false
|
43
|
+
cols[1].add_attribute renderer, 'pixbuf', 6
|
44
|
+
|
45
|
+
renderer = Gtk::CellRendererText.new
|
46
|
+
cols[1].pack_start renderer, false
|
47
|
+
cols[1].add_attribute renderer, 'text', 1
|
48
|
+
|
49
|
+
(1..cols.count).each do |i|
|
50
|
+
cols[i-1].sort_column_id = i
|
51
|
+
end
|
52
|
+
|
53
|
+
cols.drop(2).each { |col| col.expand = false }
|
54
|
+
cols[0].expand = false
|
55
|
+
cols[1].expand = true
|
56
|
+
|
57
|
+
cols.each { |col| @view.append_column col }
|
58
|
+
|
59
|
+
@view.selection.mode = Gtk::SELECTION_MULTIPLE
|
60
|
+
|
61
|
+
model.set_sort_column_id 2
|
62
|
+
|
63
|
+
self.focus_chain = [ @view ]
|
64
|
+
|
65
|
+
grab_focus
|
66
|
+
|
67
|
+
chdir Pathname.new '~'
|
68
|
+
end
|
69
|
+
|
70
|
+
def path() @path end
|
71
|
+
def expanded_path() @path.expand_path end
|
72
|
+
|
73
|
+
def back() chdir(@path + Pathname.new('..')) end
|
74
|
+
|
75
|
+
def reload
|
76
|
+
model = @view.model
|
77
|
+
|
78
|
+
model.clear
|
79
|
+
|
80
|
+
@view.columns[0].set_visible(false)
|
81
|
+
|
82
|
+
files = expanded_path.entries.sort
|
83
|
+
|
84
|
+
first_row = nil
|
85
|
+
|
86
|
+
files.each do |file|
|
87
|
+
full_path = expanded_path + file
|
88
|
+
file_icon_path = MIME::Types.get_icon_path full_path
|
89
|
+
|
90
|
+
next if file.to_s == '.'
|
91
|
+
next if file.to_s == '..' && expanded_path.root?
|
92
|
+
next if file.to_s[0] == '.' && file.to_s != '..'
|
93
|
+
|
94
|
+
row = model.append
|
95
|
+
|
96
|
+
first_row = row if first_row.nil?
|
97
|
+
|
98
|
+
row[0] = file.to_s
|
99
|
+
|
100
|
+
if full_path.directory? then
|
101
|
+
row[1] = file.to_s + '/'
|
102
|
+
row[3] = '<DIR>'
|
103
|
+
else
|
104
|
+
row[1] = file.basename('.*').to_s
|
105
|
+
row[3] = full_path.stat.size.to_i.to_szstr
|
106
|
+
end
|
107
|
+
|
108
|
+
row[2] = file.extname
|
109
|
+
row[4] = Time.at(full_path.stat.mtime).strftime '%x %R'
|
110
|
+
row[5] = full_path.stat.mode.to_rwx
|
111
|
+
row[6] = Gdk::Pixbuf.new file_icon_path.to_s
|
112
|
+
row[7] = 0
|
113
|
+
end
|
114
|
+
|
115
|
+
@view.selection.select_iter first_row
|
116
|
+
@view.scroll_to_cell first_row.path, @view.columns[1], false, 0.0, 0.0
|
117
|
+
end
|
118
|
+
|
119
|
+
def open(path)
|
120
|
+
iter = @view.model.get_iter(path)
|
121
|
+
|
122
|
+
file = iter.get_value 0
|
123
|
+
|
124
|
+
if (expanded_path + Pathname.new(file)).directory? then
|
125
|
+
chdir @path + Pathname.new(file)
|
126
|
+
else
|
127
|
+
Launchy.open (expanded_path + Pathname.new(file)).to_s
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def chdir(path)
|
132
|
+
old_path = @path == nil ? Pathname.new('') : @path
|
133
|
+
@path = path
|
134
|
+
|
135
|
+
set_label @path.to_s
|
136
|
+
|
137
|
+
reload
|
138
|
+
|
139
|
+
if path == old_path.parent then
|
140
|
+
dirname = old_path.basename
|
141
|
+
|
142
|
+
@view.model.each do |model, path, iter|
|
143
|
+
if iter.get_value(0) == dirname.to_s then
|
144
|
+
@view.selection.select_path path
|
145
|
+
@view.set_cursor path, nil, false
|
146
|
+
break
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
if @view.selection.count_selected_rows == 0 then
|
153
|
+
@view.selection.select_iter @view.model.iter_first
|
154
|
+
@view.set_cursor @view.model.iter_first.path, nil, false
|
155
|
+
end
|
156
|
+
|
157
|
+
grab_focus
|
158
|
+
|
159
|
+
signal_emit 'dir-changed'
|
160
|
+
end
|
161
|
+
|
162
|
+
def key_pressed(event)
|
163
|
+
if event.keyval == Gdk::Keyval::GDK_Insert
|
164
|
+
@view.columns[0].visible = true
|
165
|
+
|
166
|
+
last = nil
|
167
|
+
|
168
|
+
@view.selection.selected_each do |model, path, iter|
|
169
|
+
@view.selection.unselect_iter iter
|
170
|
+
iter[7] = ~iter[7] if iter[0] != '..'
|
171
|
+
last = iter
|
172
|
+
end
|
173
|
+
|
174
|
+
@view.selection.select_iter last if last && last.next!
|
175
|
+
|
176
|
+
return true
|
177
|
+
elsif event.keyval == Gdk::Keyval::GDK_space
|
178
|
+
return true
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
type_register
|
183
|
+
|
184
|
+
signal_new('dir_changed', GLib::Signal::RUN_FIRST, nil, nil)
|
185
|
+
|
186
|
+
def signal_do_dir_changed() nil end
|
187
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'sys/filesystem'
|
2
|
+
require 'unixcmd/dirview'
|
3
|
+
|
4
|
+
class CmdPanelCaptionWidget < Gtk::HBox
|
5
|
+
@mounts
|
6
|
+
@mounts_handler
|
7
|
+
@text
|
8
|
+
|
9
|
+
type_register
|
10
|
+
|
11
|
+
signal_new('go_root', GLib::Signal::RUN_FIRST, nil, nil)
|
12
|
+
signal_new('go_home', GLib::Signal::RUN_FIRST, nil, nil)
|
13
|
+
signal_new('go_back', GLib::Signal::RUN_FIRST, nil, nil)
|
14
|
+
signal_new('go_mountpoint', GLib::Signal::RUN_FIRST, nil, nil, String)
|
15
|
+
|
16
|
+
def signal_do_go_root() nil end
|
17
|
+
def signal_do_go_home() nil end
|
18
|
+
def signal_do_go_back() nil end
|
19
|
+
def signal_do_go_mountpoint(point) nil end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
|
24
|
+
@text = Gtk::Label.new 'Caption'
|
25
|
+
@mounts = Gtk::ComboBox.new
|
26
|
+
|
27
|
+
btn_root = Gtk::Button.new '/'
|
28
|
+
btn_home = Gtk::Button.new '~'
|
29
|
+
btn_back = Gtk::Button.new '..'
|
30
|
+
|
31
|
+
btn_root.relief = Gtk::RELIEF_NONE
|
32
|
+
btn_home.relief = Gtk::RELIEF_NONE
|
33
|
+
btn_back.relief = Gtk::RELIEF_NONE
|
34
|
+
|
35
|
+
btn_root.signal_connect('clicked') { signal_emit 'go-root' }
|
36
|
+
btn_home.signal_connect('clicked') { signal_emit 'go-home' }
|
37
|
+
btn_back.signal_connect('clicked') { signal_emit 'go-back' }
|
38
|
+
|
39
|
+
@mounts_handler = @mounts.signal_connect('changed') { signal_emit 'go-mountpoint', @mounts.active_text }
|
40
|
+
|
41
|
+
@mounts.focus_on_click = false
|
42
|
+
|
43
|
+
self.focus_chain = []
|
44
|
+
|
45
|
+
reload_mounts
|
46
|
+
|
47
|
+
pack_start @mounts, false
|
48
|
+
pack_start @text, true
|
49
|
+
pack_start Gtk::VSeparator.new, false
|
50
|
+
pack_start btn_root, false
|
51
|
+
pack_start Gtk::VSeparator.new, false
|
52
|
+
pack_start btn_home, false
|
53
|
+
pack_start Gtk::VSeparator.new, false
|
54
|
+
pack_start btn_back, false
|
55
|
+
end
|
56
|
+
|
57
|
+
def reload_mounts
|
58
|
+
cur_mounts = Array.new
|
59
|
+
new_mounts = Array.new
|
60
|
+
|
61
|
+
Sys::Filesystem.mounts do |mount|
|
62
|
+
# TODO: make correct mounts filter
|
63
|
+
next if mount.mount_type != 'vfat' && mount.mount_type != 'ntfs' && mount.mount_type != 'ext3' && mount.mount_type != 'ext2' && mount.mount_type != 'ext4'
|
64
|
+
new_mounts << Pathname.new(mount.mount_point).to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
@mounts.model.each do |model, path, iter|
|
68
|
+
cur_mounts << model.get_value(iter, 0)
|
69
|
+
end
|
70
|
+
|
71
|
+
if new_mounts != cur_mounts
|
72
|
+
@mounts.signal_handler_block @mounts_handler
|
73
|
+
@mounts.model.clear
|
74
|
+
@mounts.active = -1
|
75
|
+
new_mounts.each { |point| @mounts.append_text point }
|
76
|
+
@mounts.signal_handler_unblock @mounts_handler
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def update_mount_point(point)
|
81
|
+
new_active = nil
|
82
|
+
|
83
|
+
@mounts.model.each do |model, path, iter|
|
84
|
+
new_active = model.get_iter(path) if point == model.get_iter(path).get_value(0)
|
85
|
+
end
|
86
|
+
|
87
|
+
@mounts.signal_handler_block @mounts_handler
|
88
|
+
@mounts.active_iter = new_active if @mounts.active_iter != new_active
|
89
|
+
@mounts.signal_handler_unblock @mounts_handler
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_text(text)
|
93
|
+
@text.text = text
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class CmdPanelWidget < Gtk::VBox
|
98
|
+
@dir
|
99
|
+
@caption
|
100
|
+
|
101
|
+
def initialize
|
102
|
+
super
|
103
|
+
|
104
|
+
@caption = CmdPanelCaptionWidget.new
|
105
|
+
|
106
|
+
@dir = CmdDirWidget.new
|
107
|
+
|
108
|
+
pack_start @caption, false
|
109
|
+
pack_end @dir
|
110
|
+
|
111
|
+
@dir.signal_connect('dir-changed') { update_caption }
|
112
|
+
@caption.signal_connect('go-root') { @dir.chdir Pathname.new '/' }
|
113
|
+
@caption.signal_connect('go-home') { @dir.chdir Pathname.new '~' }
|
114
|
+
@caption.signal_connect('go-back') { @dir.back }
|
115
|
+
@caption.signal_connect('go-mountpoint') { |widget, point| @dir.chdir Pathname.new point if point != nil }
|
116
|
+
|
117
|
+
self.focus_chain = [ @dir ]
|
118
|
+
|
119
|
+
@dir.chdir Pathname.new '~'
|
120
|
+
end
|
121
|
+
|
122
|
+
def update_caption
|
123
|
+
path = @dir.path.expand_path.to_s
|
124
|
+
mount_point = Sys::Filesystem.mount_point path
|
125
|
+
|
126
|
+
stat = Sys::Filesystem.stat mount_point
|
127
|
+
|
128
|
+
capacity = stat.blocks*stat.block_size
|
129
|
+
free = stat.blocks_free*stat.block_size
|
130
|
+
|
131
|
+
@caption.set_text(sprintf("%s of %s free", free.to_i.to_szstr, capacity.to_i.to_szstr))
|
132
|
+
@caption.reload_mounts
|
133
|
+
@caption.update_mount_point mount_point
|
134
|
+
end
|
135
|
+
|
136
|
+
def path
|
137
|
+
@dir.path
|
138
|
+
end
|
139
|
+
|
140
|
+
def selection
|
141
|
+
res = Array.new
|
142
|
+
|
143
|
+
@dir.view().model.each { |model, path, iter|
|
144
|
+
res << Pathname.new(iter[0]) if(iter[7] != 0)
|
145
|
+
}
|
146
|
+
|
147
|
+
if res.empty? then
|
148
|
+
@dir.view.selection.selected_each do |model, path, iter|
|
149
|
+
res << Pathname.new(model.get_value(iter, 0))
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
res.count > 0 ? res : nil
|
154
|
+
end
|
155
|
+
|
156
|
+
def reload
|
157
|
+
@dir.reload
|
158
|
+
end
|
159
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unixcmd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Levenkov Artem
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Orthodox file manager
|
14
|
+
email: artem@levenkov.org
|
15
|
+
executables:
|
16
|
+
- unixcmd
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/unixcmd/panel.rb
|
21
|
+
- lib/unixcmd/dirview.rb
|
22
|
+
- lib/unixcmd/config.rb
|
23
|
+
- lib/unixcmd/cmd.rb
|
24
|
+
- lib/unixcmd/version.rb
|
25
|
+
- lib/unixcmd/aux.rb
|
26
|
+
- bin/unixcmd
|
27
|
+
homepage: http://github.com/levenkov/unixcmd
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 2.0.14
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: UNIX Commander
|
51
|
+
test_files: []
|