LINUX.ORG.RU
ФорумTalks

[плача тред] Задолбался с этим вашим windows way-ем.


0

3

Вопросы, на которые я не знаю ответов:

1. Как из скрипта получить список объектов «Открыть с помощью», ассоциированных с заданным MIME?

2. Какое есть приложение для отображения диалога «Свойства файлов» а ля аналогичные диалоги из наутилус/тунар/pcmanfm? (Нет такого приложения)

3. Какая есть утилита, аналогичная диалогу копирования/перемещения файлов из двухпанельных ФМ. С умными вопросами типа «файл уже существует, заменить/пропустить/переименовать?» (Нет такой утилиты.)

4. Какая есть утилита — универсальный вьюер файлов. С переключением режимов текст/hex-дамп/html/картинко и т.п. С поддержкой кодировок. С поддержкой файлов произвольного размера. С поддержкой плагинов. (Нет такой утилиты.)

5. Какая есть утилита графический фронт-энд для find? (Нет такой утилиты. catfish убог чуть более, чем совсем. Уже подумываю о том, чтобы запилить собственную на Ruby и назвать dogfish.)

Ну и так далее. Смотрю на типичный софт и вижу: не линукс, а сплошная «винда-виндой, только бесплатная». Горы неюзабельного, недопиленного говна. Четыре DE. Десяток файловых менеджеров. Два десятка проигрывателей. Четыре десятка текстовых редакторов. Комбайны, архитектурно не способные к интеграции и взаимодействию. Задрало.

Выдохнул.

Ответ на: комментарий от geekless

Может быть, мне прикрутить CMake для упрощения установки? Или механизм для сохранения\чтения конфигов программы? А может быть, больше опций и критериев для поиска?

netcat ★★
()
Ответ на: комментарий от netcat

Ну если ты у меня спрашиваешь задач, я могу их миллион нагенерировать:

  • Добавить вкладочное переключение поисковых агентов.
  • Реализовать агент для locate.
  • Реализовать агент для tracker.
  • Реализовать сохранение-востановление поисковой истории в полях ввода в $XDG_CACHE/dogfish/${идентификатор-агента}/${идентификатор-поля}
  • Реализовать в контекстном меню пункты Копировать/Вырезать/Поместить в корзину
  • Реализовать подгрузку иконок.
  • Реализовать альтернативный режим представления результатов: не таблицей, а просто текстом.
  • Реализовать сортировку.

Ну и так далее. Так что просто делай, что посчитаешь нужным.

Установочные скрипты только не надо, какой нафиг CMake для руби-скриптов...

geekless ★★
() автор топика
Ответ на: комментарий от aptyp

Да хоть на баше. Узкие места переписать всегда успеется, главное подход.

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Конкретно сейчас буду реализовывать сохранение/восстановление истории поиска. В субботу-воскресение отправлю pull request.

netcat ★★
()
Ответ на: комментарий от geekless

Скорее всего в воскресение. Ruby выучить надо все-таки.

netcat ★★
()
Ответ на: комментарий от geekless

Я решил создать отдельный класс HistoryDispatcher. Все получается, только проблема - как получить id поля ввода?

netcat ★★
()
Ответ на: комментарий от netcat

только проблема - как получить id поля ввода?

Я полагаю, это так можно сделать:

1. Приложение инициализирует диспетчера истории.

2. Приложение вызывает метод агента, строящий панель для ввода данных.

3. Агент создаёт там виджеты. При этом для каждого виджета он вызывает метод диспетчера, сообщая ему, что за данным виджетом надо следить. Как-то так: @dogfish.history_dispatcher.register_widget(виджет, идентификатор агента, идентификатор виджета)

4. Диспетчер для каждого виджета создаёт соответствующую gtk-шную обвязку для автодополнения по истории. Грузит историю из файла, который находит исходя их переданных идентификаторов.

5. Когда приложение дергает у агента метод «ищи», агент говорит диспетчеру «запомни историю для моего идентификатора». Диспетчер читает значения из всех полей ввода, зарегистрированных с данным «идентификатор агента», и добавляет их в историю. Сохраняет историю в соответствующий файл.

Еще такой вопрос: что лучше использовать для полей ввода: текстовое поле с автодополнением или выпадающий список со свободным вводом?

geekless ★★
() автор топика
Ответ на: комментарий от geekless

выпадающий список, конечно. удобнее для пользователя.

netcat ★★
()
Ответ на: комментарий от geekless

Я же с С пришел. К такой роскоши не привык :-)

netcat ★★
()
Ответ на: комментарий от geekless

Ну там же все так здорово. Легко проги писать, в стандартной библиотеке функций много, что там еще...

netcat ★★
()
Ответ на: комментарий от geekless

Черт, в Dogfish.initialize я написал @history_dispatcher = HistoryDispatcher.new , а в SearchAgentFind.build_gui я написал @dogfish.history_dispatcher.register_widget(blahblah). Ruby выводит синтаксическую ошибку, типа нету в Dogfish метода history_dispatcher. А я просто пользуюсь им как ссылкой на другой объект. В чем проблема?

netcat ★★
()
Ответ на: комментарий от geekless

Я думаю, ты не будешь против, если я заодно поменяю поля ввода на выпадающие списки? Заодно и прикручу механизм заполнения оных историей поиска.

netcat ★★
()
Ответ на: комментарий от geekless

И да, как в ComboBoxEntry установить значение по умолчанию (а ля @entry_find_path.text = «/dev/ass»)? А то мне сейчас костыль городить придется.

netcat ★★
()
Ответ на: комментарий от stevejobs

Для универсальности нужно договариваться между собой. А никто того не хочет, каждый своё делать хочет. Допустим, универсальный просмотровщик свойств файлов — что стоило бы его выдернуть из дельфина?

Казалось бы, можно было бы выделить общие модели, а представления пусть каждый тулкит свои делает. Но нет же, гномосеки начнут вопить, чтобы все было на С, а кдеешники скажут «да вы упопролись»

annulen ★★★★★
()
Ответ на: комментарий от annulen

Казалось бы, можно было бы выделить общие модели, а представления пусть каждый тулкит свои делает. Но нет же, гномосеки начнут вопить, чтобы все было на С, а кдеешники скажут «да вы упопролись»

Привязки к ЯП вообще быть не должно.

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Привязки к ЯП вообще быть не должно.

Ну а как тогда? Для GUI юникс-вей не эффективен, нужен какой-то API

annulen ★★★★★
()
Ответ на: комментарий от geekless

Можно кончено все через dbus дергать, но будет тормозить

annulen ★★★★★
()
Ответ на: комментарий от geekless

Ух ты, работает. Я даже и не знал про этот трюк.

netcat ★★
()
Ответ на: комментарий от annulen

Ну давай подумаем, что нам нужно чтобы сделать, например, «просмотрщик свойств файлов».

1.

1.1.

Во-первых, способ вызвать этот просмотрщик. Это подразумевает, что помимо общей свалки *.desktop-файлов, нужен способ-делать-что-то-с-файлами, и о семантике этого «делать-что-то» другим программам будет известно. Т.е. программа, когда ей надо, просто говорит «хочу для файла F запустить действие A», где возможный набор вариантов для A четко документирован. Примеры: «хочу сделать delete F» (пользователь сам настоит, будет ли оно в корзину, или обычный rm, или с предварительным забиванием файла мусором), «хочу сделать show-properties F» (пользователь сам решит, какой фронт-энд будет показывать properties).

Итак, это можно сделать простым вызовом команды. Нужно только всем договориться об имени этой команды.

Допустим, договорились, что оно будет зваться run-file-action и имеет формат вызова run-file-action actionname files.

Теперь любой ФМ и вообще любая программа может вызвать свойства файла, запустив run-file-action show-properties filename.

1.2.

Откуда run-file-action будет знать, как выполнять действия? На это пишем вторую спеку. Скажем, оно будет просто запускать их из файла $XDG_CONFIG_HOME/file-actions/${actionname} Отлично.

1.3

А как заполнять значения в $XDG_CONFIG_HOME/file-actions/ , чтоб не вручную, а автоматически? Пишем расширение к *.desktop-спеке, которое позволяет указывать семантику команд в *.desktop-файлах. Например, запись FileAction=ShowProperties в *.desktop-файле пусть означает, что команда, описанная в этом файле, — это просмотрщик свойств.

Теперь мы легко можем написать настроечный гуй а ля «в вашей системе установлены следующие обработчики для следующих действий, выберите, какие желаете использовать».

2.1.

С запуском разобрались. Теперь о том, что будет делать сам диалог свойств. Разумеется, любители минимализма вольны разработать и использовать простейший диалог, который показывает размер, дату модификации, позволяет править права доступа, и все, больше ничего не умеет. А мы подумаем о том, что делать тем, кто хочет больших удобств.

2.2.

Итак, просмотрщик получает 1 или несколько файлов. Определяет их mime. В этом месте мы останавливаемся и пишем еще одну спеку поверх *.desktop-файлов: о том, как приложения могут в этих файлах заявить о себе «а я могу принимать участие в показе свойств для файла такого-то типа».

Итак, наш универмальный просмотрщик теперь может отыскать агентов, которые могут-что-то-рассказать-о-файле. Для каждого агента он создаёт отдельную вкладку и...

2.3.

...и в этом месте мы снова останавливаемся и думаем: а как же агент сообщит нам то, что знает о файле? И пишем еще один протокол. Или даже два. Ведь в разных случаях удобны разные способы. Способ самый очевидный: «я создам для тебя вкладку, а ты внедряйся в неё по протоколу xembed и рисуй там что хочешь». Второй способ, удобный, если агент написан на скриптовом языке: «я тебя запущу, а ты в stdout выведи данные на xml-базированном языке, я их распарсю и сам построю все виджеты на вкладке».

Я всё это придумал за 10 минут. Почему люди, которые десятилетиями занимаются разработкой DE, не могут это придумать?

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Финишная прямая

Я вот-вот закончу работу, но напоролся на один баг, причину которого выяснить не могу. Вот патч с изменениями:

diff --git a/src/dogfish.rb b/src/dogfish.rb
index b8c8334..a3314d2 100755
--- a/src/dogfish.rb
+++ b/src/dogfish.rb
@@ -8,11 +8,49 @@ def _(v)
 	v
 end
 
+class HistoryDispatcher
+
+	def initialize()
+		@widgets = Hash.new
+	end
+
+	def get_history_list(agent_id, widget_id)
+		history_list = Array.new
+		a = ENV['XDG_CONFIG_HOME'] + "/dogfish/" + agent_id + "/" + widget_id + "/history"
+		if(File::exist?(a) == false) then
+			return nil
+		end
+
+		file = File.new(a, "r")
+		while(line = file.gets())
+			history_list.push(line)		
+		end
+		file.close()
+		return history_list
+	end
+
+	def register_widget(widget, agent_id, widget_id)
+		@widgets[agent_id => [widget_id => widget]]
+		history_list =  get_history_list(agent_id, widget_id)
+		if (not (history_list == nil)) then
+			@widgets[agent_id][widget_id].append(history_list)
+		end
+	end 
+
+	def add_item_to_history(agent_id, widget_id)
+		a = ENV['XDG_CONFIG_HOME'] + "/dogfish/" + agent_id + "/" + widget_id + "/history"
+		file = File.new(a, "a")
+		file.puts @widgets[agent_id][widget_id].child().text
+		file.close()	
+ 	end
+
+end
 
 class SearchAgentFind
 
-	def initialize(dogfish)
+	def initialize(dogfish, history_dispatcher)
 		@dogfish = dogfish
+		@history_dispatcher = history_dispatcher
 	end
 
 	def build_gui(box)
@@ -24,46 +62,55 @@ class SearchAgentFind
 		l = Gtk::Label.new(_('_File name'), true)
 		l.set_alignment(0, 0.5)
 		t.attach l, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::FILL, 2
-		l.mnemonic_widget = @entry_find_text = Gtk::Entry.new()
+		l.mnemonic_widget = @entry_find_text = Gtk::ComboBoxEntry.new()
 		t.attach @entry_find_text, 1, 4, 0, 1
+		@history_dispatcher.register_widget(l, "find", "file_name")
 
 		l = Gtk::Label.new(_('_Path'), true)
 		l.set_alignment(0, 0.5)
 		t.attach l, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::FILL, 2
-		l.mnemonic_widget = @entry_find_path = Gtk::Entry.new()
+		l.mnemonic_widget = @entry_find_path = Gtk::ComboBoxEntry.new()
 		t.attach @entry_find_path, 1, 4, 1, 2
-		@entry_find_path.text = '/'
+		@entry_find_path.child().text = "/"
+		@history_dispatcher.register_widget(l, "find", "path")
 
 		l = Gtk::Label.new(_('_Size'), true)
 		l.set_alignment(0, 0.5)
 		t.attach l, 0, 1, 2, 3, Gtk::SHRINK|Gtk::FILL, Gtk::FILL, 2
-		l.mnemonic_widget = @entry_find_size = Gtk::Entry.new()
+		l.mnemonic_widget = @entry_find_size = Gtk::ComboBoxEntry.new()
 		t.attach @entry_find_size, 1, 2, 2, 3
-		@entry_find_size.text = '*'
+		@entry_find_size.child().text = "*"
+		@history_dispatcher.register_widget(l, "find", "size")
 
 		l = Gtk::Label.new(_('_Type'), true)
 		l.set_alignment(0, 0.5)
 		t.attach l, 2, 3, 2, 3, Gtk::SHRINK|Gtk::FILL, Gtk::FILL, 2
-		l.mnemonic_widget = @entry_find_type = Gtk::Entry.new()
+		l.mnemonic_widget = @entry_find_type = Gtk::ComboBoxEntry.new()
 		t.attach @entry_find_type, 3, 4, 2, 3
-		@entry_find_type.text = '*'
+		@entry_find_type.child().text = "*"
+		@history_dispatcher.register_widget(l, "find", "type")
 
 		l = Gtk::Label.new(_('Max _Depth'), true)
 		l.set_alignment(0, 0.5)
 		t.attach l, 0, 1, 3, 4, Gtk::SHRINK|Gtk::FILL, Gtk::FILL, 2
-		l.mnemonic_widget = @entry_find_maxdepth = Gtk::Entry.new()
+		l.mnemonic_widget = @entry_find_maxdepth = Gtk::ComboBoxEntry.new()
 		t.attach @entry_find_maxdepth, 1, 2, 3, 4
-		@entry_find_maxdepth.text = '*'
-
+		@entry_find_maxdepth.child().text = "*"
+		@history_dispatcher.register_widget(l, "find", "max_depth")
 	end
 
 	def do_search
-		text = @entry_find_text.text
-		path = @entry_find_path.text
-		size = @entry_find_size.text
-		type = @entry_find_type.text
-		maxdepth = @entry_find_maxdepth.text
-
+		text = @entry_find_text.child().text	
+		path = @entry_find_path.child().text
+		size = @entry_find_size.child().text
+		type = @entry_find_type.child().text
+		maxdepth = @entry_find_maxdepth.child().text
+		@history_dispatcher.add_item_to_history("find", "file_name")
+		@history_dispatcher.add_item_to_history("find", "path")
+		@history_dispatcher.add_item_to_history("find", "size")
+		@history_dispatcher.add_item_to_history("find", "type")
+		@history_dispatcher.add_item_to_history("find", "max_depth")
+                   
 		f1r, f1 = IO.pipe
 		f2r, f2 = IO.pipe
 
@@ -189,6 +236,7 @@ p command
 		Process.kill("KILL", pid)
 
 	end
+
 end
 
 class Dogfish < Gtk::Window
@@ -201,8 +249,8 @@ class Dogfish < Gtk::Window
 
 	def initialize
 		super
-
-		@agent = SearchAgentFind.new(self)
+		@history_dispatcher = HistoryDispatcher.new
+		@agent = SearchAgentFind.new(self, @history_dispatcher)
 
 		@found_files = []
 
@@ -405,5 +453,3 @@ class Dogfish < Gtk::Window
 end
 
 Dogfish.new
-
-

Введи имя файла и нажми на кнопку «Поиск». Баг сразу вылезет.

netcat ★★
()
Ответ на: Финишная прямая от netcat

Во-первых, надо добавить проверок в секции ensure:

		f1r.close if !f1r.nil?
		f2r.close if !f2r.nil?
		Process.kill("KILL", pid) if !pid.nil?

После этого программа перестанет падать с исключением при обработке предыдущего исключения, и мы увидим настоящую причину:

$ ./src/dogfish.rb 
./src/dogfish.rb:42:in `initialize': Нет такого файла или каталога - /media/work/home/vadim/.config/dogfish/find/file_name/history
	 from ./src/dogfish.rb:42:in `new'
	 from ./src/dogfish.rb:42:in `add_item_to_history'
	 from ./src/dogfish.rb:108:in `do_search'
	 from ./src/dogfish.rb:425:in `find'
	 from ./src/dogfish.rb:448:in `on_button_find_clicked'
	 from ./src/dogfish.rb:307:in `block in initialize'
	 from ./src/dogfish.rb:339:in `call'
	 from ./src/dogfish.rb:339:in `main'
	 from ./src/dogfish.rb:339:in `initialize'
	 from ./src/dogfish.rb:455:in `new'
	 from ./src/dogfish.rb:455:in `<main>'

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Хм, в учебнике Фитцджеральда написано, что если файла нет, то он создастся автоматически (в случае открытия файла в режиме append). А как тогда создать файл?

netcat ★★
()
Ответ на: комментарий от geekless

Еще одна запара. Создаю новые пары в хэше так

	@widgets[agent_id] = {widget_id => widget}

Но получить доступ к виджету так

@widgets[agent_id][widget_id].child().text
не получается. Ruby ругается:
src/dogfish.rb:55:in `add_item_to_history': undefined method `[]' for nil:NilClass
	 from src/dogfish.rb:120:in `do_search'
	 from src/dogfish.rb:437:in `find'
	 from src/dogfish.rb:460:in `on_button_find_clicked'
	 from src/dogfish.rb:319:in `block in initialize'
	 from src/dogfish.rb:351:in `call'
	 from src/dogfish.rb:351:in `main'
	 from src/dogfish.rb:351:in `initialize'
	 from src/dogfish.rb:467:in `new'
	 from src/dogfish.rb:467:in `<main>'

netcat ★★
()
Ответ на: комментарий от geekless

Все, сам справился, послал pull request. Если что не так, ты его сразу не закрывай, скажи мне, я исправлю.

netcat ★★
()
Ответ на: комментарий от geekless

Да пофиг, лишь бы не EULA. Andrey Inishev. Сейчас буду пилить поддержку вкладок.

netcat ★★
()
Ответ на: комментарий от geekless
 @change_agent.signal_connect("changed"){
                        if (@change_agent.active_text() == "find") then
                                @agent_gui_box = Gtk::VBox.new()
                                @agent = SearchAgentFind.new(self, @history_dispatcher)
                                @agent.build_gui(@agent_gui_box)
                                @agent_gui_box.show
                        elsif (@change_agent.active_text() == "locate") then
                                @agent_gui_box = Gtk::VBox.new()
                                @agent = SearchAgentLocate.new(self, @history_dispatcher)
                                @agent.build_gui(@agent_gui_box)
                                @agent_gui_box.show
                        end
                }

Так, как ты сказал, не получилось. Пытаюсь так, но не получается(содержимое бокса не обновляется).

netcat ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.