unixcmd 0.1.0
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.
- 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: []
|