class Openall_time_applet::Gui::Win_main
attr_reader :args, :gui
def initialize(args)
@args = args
@gui = Gtk::Builder.new.add("../glade/win_main.glade")
@gui.translate
@gui.connect_signals{|h| method(h)}
#Generate list-store containing tasks for the task-column.
task_ls = Gtk::ListStore.new(String, String)
iter = task_ls.append
iter[0] = _("None")
iter[1] = 0.to_s
tasks = [_("Choose:")]
@args[:oata].ob.list(:Task, {"orderby" => "title"}) do |task|
iter = task_ls.append
iter[0] = task[:title]
iter[1] = task.id.to_s
tasks << task
end
#Add the tasks to the combo-box.
@gui["cbTask"].init(tasks)
init_data = @gui["tvTimelogs"].init([
_("ID"),
{
:title => _("Description"),
:type => :string,
:markup => true
},
{
:title => _("Timestamp"),
:type => :string,
:markup => true
},
{
:title => _("Time"),
:type => :string,
:markup => true
},
{
:title => _("Transport"),
:type => :string,
:markup => true
},
{
:title => _("Length"),
:type => :string,
:markup => true
},
{
:title => _("Descr."),
:type => :string,
:markup => true
},
{
:title => _("Costs"),
:type => :string,
:markup => true
},
{
:title => _("Fixed"),
:type => :toggle
},
{
:title => _("Int. work"),
:type => :toggle
},
{
:title => _("Sync?"),
:type => :toggle
},
{
:title => _("Task"),
:type => :combo,
:model => task_ls,
:has_entry => false,
:markup => true
}
])
Knj::Gtk2::Tv.editable_text_renderers_to_model(
:ob => @args[:oata].ob,
:tv => @gui["tvTimelogs"],
:model_class => :Timelog,
:renderers => init_data[:renderers],
:change_before => proc{|d|
if (d[:col_no] == 11 or d[:col_no] == 3 or d[:col_no] == 2) and @args[:oata].timelog_active and @args[:oata].timelog_active.id == d[:model].id
raise _("You cannot edit the time for the active timelog.")
end
@dont_reload = true
},
:change_after => proc{
@dont_reload = false
},
:cols => {
1 => :descr,
2 => {:col => :timestamp, :type => :datetime},
3 => {:col => :time, :type => :time_as_sec},
4 => {:col => :time_transport, :type => :time_as_sec},
5 => {:col => :transportlength, :type => :int},
6 => {:col => :transportdescription},
7 => {:col => :transportcosts, :type => :human_number, :decimals => 2},
8 => {:col => :travelfixed},
9 => {:col => :workinternal},
10 => {:col => :sync_need},
11 => {
:col => :task_id,
:value_callback => lambda{ |data|
task = @args[:oata].ob.get_by(:Task, {"title" => data[:value]})
if !task
return 0
else
return task.id
end
},
:value_set_callback => proc{ |data| data[:model].task_name }
}
}
)
#The ID column should not be visible (it is only used to identify which timelog the row represents).
@gui["tvTimelogs"].columns[0].visible = false
#Connect certain column renderers to the editingStarted-method, so editing can be canceled, if the user tries to edit forbidden data on the active timelog.
init_data[:renderers][1].signal_connect_after("editing-started", :descr, &self.method(:on_cell_editingStarted))
init_data[:renderers][2].signal_connect_after("editing-started", :timestamp, &self.method(:on_cell_editingStarted))
init_data[:renderers][3].signal_connect_after("editing-started", :time, &self.method(:on_cell_editingStarted))
init_data[:renderers][11].signal_connect_after("editing-started", :task, &self.method(:on_cell_editingStarted))
#Fills the timelogs-treeview with data.
self.reload_timelogs
#Reload the treeview if something happened to a timelog.
@reload_id = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["add", "update", "delete"], &self.method(:reload_timelogs))
#Update switch-button.
self.update_switch_button
#Update switch-button when active timelog is changed.
@event_timelog_active_changed = @args[:oata].events.connect(:timelog_active_changed) do
self.update_switch_button
self.check_rows
self.timelog_info_trigger
end
#This timeout controls the updating of the timelog-info-frame and the time-counter for the active timelog in the treeview.
@timeout_id = Gtk.timeout_add(1000) do
self.check_rows
self.timelog_info_trigger
true
end
#Show the window.
@gui["window"].show_all
self.timelog_info_trigger
width = @gui["window"].size[0]
@gui["window"].resize(width, 1)
end
#This method is called, when editting starts in a description-, time- or task-cell. If it is the active timelog, then editting is canceled.
def on_cell_editingStarted(renderer, editable, path, col_title)
iter = @gui["tvTimelogs"].model.get_iter(path)
timelog_id = @gui["tvTimelogs"].model.get_value(iter, 0).to_i
if tlog = @args[:oata].timelog_active and tlog.id.to_i == timelog_id
renderer.stop_editing(true)
Knj::Gtk2.msgbox(_("You cannot edit this on the active timelog."))
end
end
#This method is used to do stuff without having the treeview reloading. It executes the given block and then makes the treeview reloadable again.
def dont_reload
@dont_reload = true
begin
yield
ensure
@dont_reload = false
end
end
def reload_timelogs
return nil if @dont_reload or @gui["tvTimelogs"].destroyed?
@gui["tvTimelogs"].model.clear
@args[:oata].ob.list(:Timelog, {"orderby" => "id"}) do |timelog|
@gui["tvTimelogs"].append([
timelog.id,
Knj::Web.html(timelog[:descr]),
timelog.timestamp_str,
timelog.time_as_human,
timelog.time_transport_as_human,
Knj::Locales.number_out(timelog[:transportlength], 0),
Knj::Web.html(timelog[:transportdescription]),
Knj::Locales.number_out(timelog[:transportcosts], 2),
Knj::Strings.yn_str(timelog[:travelfixed], true, false),
Knj::Strings.yn_str(timelog[:workinternal], true, false),
Knj::Strings.yn_str(timelog[:sync_need], true, false),
timelog.task_name
])
end
#Reset cache of which rows are set to bold.
@bold_rows = {}
end
def on_imiQuit_activate
#Check if a timelog needs to be synced. If so the user needs to confirm he really wants to quit.
timelog_found = nil
do_destroy = true
@args[:oata].ob.list(:Timelog) do |timelog|
if timelog[:time].to_f > 0 or timelog[:time_transport].to_f > 0 or timelog[:sync_need].to_i == 1
timelog_found = timelog
break
end
end
if timelog_found
if Knj::Gtk2.msgbox(sprintf(_("The timelog '%s' has not been synced. Are you sure you want to quit?"), timelog_found[:descr]), "yesno") != "yes"
do_destroy = false
end
end
@args[:oata].destroy if do_destroy
end
def on_imiPreferences_activate
@args[:oata].show_preferences
end
def on_imiWeekview_activate
@args[:oata].show_worktime_overview
end
def on_window_destroy
#Unconnect reload-event. Else it will crash on call to destroyed object. Also frees up various ressources.
@args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id)
@args[:oata].events.disconnect(:timelog_active_changed, @event_timelog_active_changed) if @event_timelog_active_changed
@event_timelog_active_changed = nil
Gtk.timeout_remove(@timeout_id)
end
def on_expOverview_activate(expander)
if expander.expanded?
@gui["window"].resize(@gui["window"].size[0], 480)
self.timelog_info_trigger
else
Gtk.timeout_add(200) do
self.timelog_info_trigger
@gui["window"].resize(@gui["window"].size[0], 1)
false
end
end
end
#This method handles the "Timelog info"-frame. Hides, shows and updates the info in it.
def timelog_info_trigger
if !@gui["expOverview"].expanded? and tlog = @args[:oata].timelog_active
@gui["labTimelogInfoDescr"].markup = "#{Knj::Web.html(tlog[:descr])}"
task = tlog.task
if !task
task_text = "[#{_("no task sat on the timelog")}]"
else
task_text = task.name
end
@gui["labTimelogInfoTask"].markup = "#{Knj::Web.html(task_text)}"
time_tracked = Knj::Strings.secs_to_human_time_str(@args[:oata].timelog_active_time_tracked + tlog[:time].to_i)
@gui["labTimelogInfoTime"].markup = "#{Knj::Web.html(time_tracked)}"
@gui["frameTimelogInfo"].show_all
else
visible = @gui["frameTimelogInfo"].visible?
@gui["frameTimelogInfo"].hide
#Resize to minimum height, so a big space isnt left.
@gui["window"].resize(@gui["window"].size[0], 1) if visible and !@gui["expOverview"].expanded?
end
end
def on_btnSwitch_clicked
if @args[:oata].timelog_active
@args[:oata].timelog_stop_tracking
@gui["txtDescr"].grab_focus
else
task = @gui["cbTask"].sel
if !task.is_a?(Knj::Datarow)
task_id = 0
else
task_id = task.id
end
@timelog = @args[:oata].ob.add(:Timelog, {
:task_id => task_id,
:descr => @gui["txtDescr"].text
})
@args[:oata].timelog_active = @timelog
@gui["txtDescr"].text = ""
@gui["cbTask"].sel = _("Choose:")
end
self.update_switch_button
end
#This method updates the switch button to start or stop, based on the if a timelog is tracked or not.
def update_switch_button
but = @gui["btnSwitch"]
tlog_act = @args[:oata].timelog_active
if tlog_act
but.image = Gtk::Image.new(Gtk::Stock::MEDIA_STOP, Gtk::IconSize::BUTTON)
but.label = _("Stop")
else
but.image = Gtk::Image.new(Gtk::Stock::MEDIA_RECORD, Gtk::IconSize::BUTTON)
but.label = _("Start")
end
end
#This method runs through all rows in the treeview and checks if a row should be marked with bold. It also increases the time in the time-column for the tracked timelog.
def check_rows
act_timelog = @args[:oata].timelog_active
if act_timelog
act_timelog_id = act_timelog.id
else
act_timelog_id = nil
end
rows_bold = [1, 2, 3, 4, 5, 6, 7, 11]
@gui["tvTimelogs"].model.each do |model, path, iter|
timelog_id = model.get_value(iter, 0).to_i
bold = false
iter_id = iter.to_s.to_i
#Update time tracked.
if timelog_id == act_timelog_id
secs = act_timelog[:time].to_i + @args[:oata].timelog_active_time_tracked
iter[3] = "#{Knj::Strings.secs_to_human_time_str(secs)}"
bold = true
end
#Set all columns to bold if not already set.
if bold and !@bold_rows.key?(iter_id)
rows_bold.each do |row_no|
iter[row_no] = "#{model.get_value(iter, row_no)}"
end
@bold_rows[iter_id] = true
end
end
end
def on_btnSync_clicked
@args[:oata].show_prepare_sync
end
def on_miSyncStatic_activate
@args[:oata].sync_static("transient_for" => @gui["window"])
end
def on_btnMinus_clicked
sel = @gui["tvTimelogs"].sel
tlog = @args[:oata].ob.get(:Timelog, sel[0]) if sel
if !sel or !tlog
Knj::Gtk2.msgbox(_("Please choose a timelog to delete."), "warning")
return nil
end
return nil if Knj::Gtk2.msgbox(_("Do you want to remove this timelog?"), "yesno") != "yes"
@args[:oata].ob.delete(tlog)
end
def on_btnPlus_clicked
#Add new timelog to database.
timelog = @args[:oata].ob.add(:Timelog)
#Focus new timelog in treeview and open the in-line-editting for the description.
added_id = timelog.id.to_i
@gui["tvTimelogs"].model.each do |model, path, iter|
timelog_id = model.get_value(iter, 0).to_i
if timelog_id == added_id
col = @gui["tvTimelogs"].columns[1]
@gui["tvTimelogs"].set_cursor(path, col, true)
break
end
end
end
#Redirects 'enter'-events to 'switch'-click-event.
def on_txtDescr_activate(*args)
self.on_btnSwitch_clicked
end
end