#!/usr/bin/env ruby

require 'gtk2'
require 'find'
require 'fileutils'

# this is Marvin the file/project manager for scite, using ruby/gtk2.
# enable 'check.if.already.open=1' in scite's Global Config file 
# other tabbed text editors must support multiple opens in a 
# current session otherwise there will be a new window for each file.
# check http://forum.ruby-portal.de/viewtopic.php?p=31271&sid=7ad2ffde86d46016419127684f662410
# for the original treeview implementation of treestore loading. 
# TODO: review treestore/treeview handling code. It may be best served if there 
# is a modified/inherited TreeView class handling all the mess
# FIXED: mkdir and background simbol (&) at each editor call.
#

class TextInputDialog < Gtk::Dialog
	def initialize (title, path)
		super()
		set_modal(true)
		set_default_size(200,100)
		set_title(title)

		entryText = Gtk::Entry.new
		vbox.add(entryText)
		
		btOk = Gtk::Button.new('Ok')
		btCancel = Gtk::Button.new('Cancel')
		action_area.add(btOk)
		action_area.add(btCancel)

		btOk.signal_connect('clicked') do
			@filename=entryText.text
			response(Gtk::Dialog::RESPONSE_ACCEPT)
		end
		
		btCancel.signal_connect('clicked') do
			response(Gtk::Dialog::RESPONSE_CANCEL)
		end

		show_all
	end
	def filename
		@filename
	end
	
end

class Marvin < Gtk::Window
	def buildTreeModel(model, cwd)
		this = []
		that = [nil]
		Find.find(cwd) do |path|
			path = path.sub(/#{cwd}/, '');
			counter = 0
			path.scan(/([^\/]+)\/?/).flatten.each do |element|
			counter += 1
				if this[counter] != element then
					this[counter] = element
					that[counter] = model.append(that[counter - 1]).set_value(0, this[counter])
				end	
			end
		end
	end

	def updateTreeModel (model) 
		model.clear
		buildTreeModel(model, @tcwd)
	end

	def showAboutDialog
		props = {
			:name => 'marvin.rb',
			:copyright => %{\
				(C) 2007 gm
			}, 
			:comments => %{\
			A simple file manager for scite written on rails/gtk2},
			:website => 'https://zenmachine.wordpress.com/',
		}
		Gtk::AboutDialog.show(nil, props)
	end

	def createMenuBar
		topmen = Gtk::MenuItem.new( "Main" )
		menu = Gtk::Menu.new
		
		item = Gtk::MenuItem.new( "About" )
		item.signal_connect("activate") { showAboutDialog }
		menu.append( item )
		
		item = Gtk::SeparatorMenuItem.new
		menu.append(item)
		
		item = Gtk::MenuItem.new( "Quit" )
		item.signal_connect("activate") { Gtk.main_quit }
		menu.append( item )
		
		topmen.set_submenu( menu )
		
		menubar = Gtk::MenuBar.new
		menubar.append( topmen )
		return menubar
	end


	def itemContextMenu(event, path, model)
		menu = Gtk::Menu.new
		item = Gtk::MenuItem.new("New File")
		item.signal_connect("activate") { 
			txtdlg = TextInputDialog.new("Create new file", path)
			if txtdlg.run == Gtk::Dialog::RESPONSE_ACCEPT
				newFile=path+"/"+txtdlg.filename
			end
			txtdlg.destroy
			if (File.exists?(newFile)) 
				errdlg=Gtk::MessageDialog.new(nil,
					Gtk::MessageDialog::DESTROY_WITH_PARENT,
					Gtk::MessageDialog::INFO, Gtk::MessageDialog::BUTTONS_CLOSE,
					"File already exists")
				errdlg.run
				errdlg.destroy
			else
				puts "Creating: "+newFile
				FileUtils.touch(newFile)
				system @textEditor+newFile+"&"
				# rebuild tree model
				updateTreeModel(model)
			end
		}
		menu.append(item)
		
		item = Gtk::MenuItem.new("New Folder")
		item.signal_connect("activate") {
			if (not File.directory?(path))
				path=File.dirname(path)
			end
			txtdlg = TextInputDialog.new("Create new folder", path)
			if txtdlg.run == Gtk::Dialog::RESPONSE_ACCEPT
				newFolder=path+"/"+txtdlg.filename
			end
			txtdlg.destroy
			if (File.exists?(newFolder)) 
					errdlg=Gtk::MessageDialog.new(nil,
						Gtk::MessageDialog::DESTROY_WITH_PARENT,
						Gtk::MessageDialog::INFO, Gtk::MessageDialog::BUTTONS_CLOSE,
						"Folder already exists")
					errdlg.run
					errdlg.destroy
				else
					puts "Creating: "+newFolder
					Dir.mkdir(newFolder)
					# rebuild tree model
					updateTreeModel(model)
				end
			}
		menu.append(item)
		
		item = Gtk::MenuItem.new("Delete")
		item.signal_connect("activate") { 
			if (not File.exists?(path)) 
				# msgdialog ok/cancel
				errdlg=Gtk::MessageDialog.new(nil,
					Gtk::MessageDialog::DESTROY_WITH_PARENT,
					Gtk::MessageDialog::INFO, Gtk::MessageDialog::BUTTONS_CLOSE,
					"Folder not found")
				errdlg.run
				errdlg.destroy
			else
				puts "Deleting: "+path
				FileUtils.rm_r(path, :secure=>true)
				# rebuild tree model
				updateTreeModel(model)
			end
		}
		menu.append(item)
		
		menu.show_all()
		menu.popup(nil, nil, event.button, event.time)
	end


	def createTree(cwd, model)
		expandedRows = Array.new
		tree = Gtk::TreeView.new model
		tree.signal_connect("row-activated") { |view, path, column|
		    if iter = view.model.get_iter(path)
			path = [iter[0]]
			while iter.parent != nil
			    iter = iter.parent
			    path << iter[0]
			end
			path = cwd + "/" + path.reverse.join("/")
			if (not FileTest.directory? path)
				system @textEditor+" "+path+"&"
			end
		    end
		}

		tree.signal_connect("button_press_event") do |widget, event|
			if event.kind_of? Gdk::EventButton and event.button==3
				widget.selection.selected_each { |tModel, path, iter|
					if iter = tModel.get_iter(path)
						path = [iter[0]]
						while iter.parent != nil
							iter = iter.parent
							path << iter[0]
						end
						# save expand status
						expandedRows.clear
						tree.map_expanded_rows do |tree,treepath|
							expandedRows << treepath
						end
						path = cwd + "/" + path.reverse.join("/")
						itemContextMenu(event, path, model)
					end
				}
			end
		end
		
		model.signal_connect("row-changed") { 
			#restore any stored expand status
			expandedRows.each do |tp|
				tree.expand_row(tp, false)
			end
			
		}
		render = Gtk::CellRendererText.new()
		c1 = Gtk::TreeViewColumn.new(cwd, render, {:text => 0})
		tree.append_column(c1)

		buildTreeModel(model, cwd)
		return tree
	end

	def selectWorkingDir
		dialog = Gtk::FileChooserDialog.new("Select Project Directory",
					nil,
					Gtk::FileChooser::ACTION_SELECT_FOLDER,
					nil,
					[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
					[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
		if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
			peta=dialog.current_folder #+"/"
		end
		dialog.destroy
		return peta
	end


	def initialize
		super()
		# change your text editor here (but remember to check if it can open new files in the same window/new tab)
		@textEditor="scite"	
		@tcwd=selectWorkingDir()
		self.set_size_request(240, 600)
		self.signal_connect("delete_event") { Gtk.main_quit }
		self.set_title("Marvin")

		vbox2 = Gtk::VBox.new(false, 0)

		menubar = createMenuBar()
		vbox2.pack_start(menubar, false, false, 0)

		scroller = Gtk::ScrolledWindow.new
		scroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
		vbox2.pack_start(scroller, true, true, 0)

		model = Gtk::TreeStore.new(String)
		tree = createTree(@tcwd, model)
		scroller.add(tree)
		
		self.add(vbox2)
		self.show_all()
	end
end

Gtk.init
Marvin.new
Gtk.main
 
%d bloggers like this: