openall_time_applet 0.0.38 → 0.0.40

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.
data/gui/win_main.rb CHANGED
@@ -1,29 +1,64 @@
1
1
  class Openall_time_applet::Gui::Win_main
2
+ ROWS_BOLD = [:timestamp, :time, :descr, :ttime, :tkm, :ttype, :tdescr, :cost, :task]
3
+
2
4
  attr_reader :args, :gui
3
5
 
4
6
  def initialize(args)
5
7
  @args = args
8
+ @oata = @args[:oata]
9
+ @ob = @oata.ob
10
+ @log = @oata.log
6
11
 
7
12
  @gui = Gtk::Builder.new.add("#{File.dirname(__FILE__)}/../glade/win_main.glade")
8
13
  @gui.translate
9
14
  @gui.connect_signals{|h| method(h)}
10
- @gui["window"].icon = "#{File.dirname(__FILE__)}/../gfx/icon_time_black.png"
15
+
16
+ #Shortcut-variables to very used widgets.
17
+ @window = @gui["window"]
18
+ @expander = @gui["expOverview"]
19
+ @tv = @gui["tvTimelogs"]
20
+ @tvpt = @gui["tvTimelogsPrepareTransfer"]
21
+
22
+
23
+ #Set icon for window.
24
+ @window.icon = "#{File.dirname(__FILE__)}/../gfx/icon_time_black.png"
11
25
 
12
26
 
27
+ #Settings for various widgets.
28
+ Gtk2_expander_settings.new(:expander => @expander, :name => "main_expander", :db => @oata.db)
29
+ Gtk2_window_settings.new(:window => @window, :name => "main_window", :db => @oata.db)
30
+
31
+ #Trigger max. height stuff.
32
+ self.on_expOverview_activate(@expander)
33
+
13
34
  #Generate list-store containing tasks for the task-column.
14
- task_ls = Gtk::ListStore.new(String, String)
15
- iter = task_ls.append
35
+ @task_ls = Gtk::ListStore.new(String, String)
36
+
37
+ iter = @task_ls.append
16
38
  iter[0] = _("None")
17
- iter[1] = 0.to_s
39
+ iter[1] = "0"
18
40
 
19
41
  tasks = [_("Choose:")]
20
- @args[:oata].ob.list(:Task, "orderby" => "title") do |task|
21
- iter = task_ls.append
22
- iter[0] = task[:title]
42
+ @ob.static(:Task, :tasks_to_show) do |task|
43
+ iter = @task_ls.append
44
+ iter[0] = task.name
23
45
  iter[1] = task.id.to_s
24
46
  tasks << task
25
47
  end
26
48
 
49
+ self.task_ls_resort
50
+ @gui["cbTask"].init(tasks)
51
+
52
+
53
+ #Generate list-store containing time-types.
54
+ @time_types = Openall_time_applet::Models::Timelog.time_types
55
+ time_types_ls = Gtk::ListStore.new(String, String)
56
+ @time_types.each do |key, val|
57
+ iter = time_types_ls.append
58
+ iter[0] = val
59
+ iter[1] = key
60
+ end
61
+
27
62
 
28
63
  #Set up completion for description entry.
29
64
  @descr_ec = Gtk::EntryCompletion.new
@@ -35,11 +70,10 @@ class Openall_time_applet::Gui::Win_main
35
70
 
36
71
 
37
72
 
38
- #Add the tasks to the combo-box.
39
- @gui["cbTask"].init(tasks)
40
-
41
- init_data = @gui["tvTimelogs"].init(
73
+ init_data = @tv.init(
42
74
  :type => :treestore,
75
+ :reorderable => false,
76
+ :sortable => false,
43
77
  :cols => [
44
78
  _("ID"),
45
79
  {
@@ -79,6 +113,14 @@ class Openall_time_applet::Gui::Win_main
79
113
  :expand => true,
80
114
  :fixed_width => 160
81
115
  },
116
+ {
117
+ :title => _("T-type"),
118
+ :type => :combo,
119
+ :model => time_types_ls,
120
+ :has_entry => false,
121
+ :fixed_width => 120,
122
+ :markup => true
123
+ },
82
124
  {
83
125
  :title => _("Cost"),
84
126
  :type => :string,
@@ -90,14 +132,14 @@ class Openall_time_applet::Gui::Win_main
90
132
  :expand => false
91
133
  },
92
134
  {
93
- :title => _("Work"),
135
+ :title => _("Internal")[0, 3] + ".",
94
136
  :type => :toggle,
95
137
  :expand => false
96
138
  },
97
139
  {
98
140
  :title => _("Task"),
99
141
  :type => :combo,
100
- :model => task_ls,
142
+ :model => @task_ls,
101
143
  :has_entry => false,
102
144
  :markup => true,
103
145
  :expand => true,
@@ -112,8 +154,7 @@ class Openall_time_applet::Gui::Win_main
112
154
  )
113
155
 
114
156
  @tv_settings = Gtk2_treeview_settings.new(
115
- :id => "win_main_tvTimelogs",
116
- :tv => @gui["tvTimelogs"],
157
+ :tv => @tv,
117
158
  :col_ids => {
118
159
  0 => :id,
119
160
  1 => :timestamp,
@@ -122,21 +163,22 @@ class Openall_time_applet::Gui::Win_main
122
163
  4 => :ttime,
123
164
  5 => :tkm,
124
165
  6 => :tdescr,
125
- 7 => :cost,
126
- 8 => :fixed,
127
- 9 => :int,
128
- 10 => :task,
129
- 11 => :track
166
+ 7 => :ttype,
167
+ 8 => :cost,
168
+ 9 => :fixed,
169
+ 10 => :int,
170
+ 11 => :task,
171
+ 12 => :track
130
172
  }
131
173
  )
132
174
 
133
175
  Knj::Gtk2::Tv.editable_text_renderers_to_model(
134
- :ob => @args[:oata].ob,
135
- :tv => @gui["tvTimelogs"],
176
+ :ob => @oata.ob,
177
+ :tv => @tv,
136
178
  :model_class => :Timelog,
137
179
  :renderers => init_data[:renderers],
138
180
  :change_before => proc{|d|
139
- if d[:col_no] == 2 and @args[:oata].timelog_active and @args[:oata].timelog_active.id == d[:model].id
181
+ if d[:col_no] == 2 and @oata.timelog_active and @oata.timelog_active.id == d[:model].id
140
182
  raise _("You cannot edit the time for the active timelog.")
141
183
  end
142
184
 
@@ -152,53 +194,64 @@ class Openall_time_applet::Gui::Win_main
152
194
  @tv_editting = nil
153
195
  },
154
196
  :cols => {
155
- 1 => {
197
+ @tv_settings.col_orig_no_for_id(:timestamp) => {
156
198
  :col => :timestamp,
157
199
  :value_callback => self.method(:tv_editable_timestamp_callback),
158
200
  :value_set_callback => self.method(:tv_editable_timestamp_set_callback)
159
201
  },
160
- 2 => {
202
+ @tv_settings.col_orig_no_for_id(:time) => {
161
203
  :col => :time,
162
204
  :value_callback => proc{ |data| Knj::Strings.human_time_str_to_secs(data[:value]) },
163
205
  :value_set_callback => proc{ |data| Knj::Strings.secs_to_human_time_str(data[:value], :secs => false) }
164
206
  },
165
- 3 => :descr,
166
- 4 => {
207
+ @tv_settings.col_orig_no_for_id(:descr) => :descr,
208
+ @tv_settings.col_orig_no_for_id(:ttime) => {
167
209
  :col => :time_transport,
168
210
  :value_callback => proc{ |data| Knj::Strings.human_time_str_to_secs(data[:value]) },
169
211
  :value_set_callback => proc{ |data| Knj::Strings.secs_to_human_time_str(data[:value], :secs => false) }
170
212
  },
171
- 5 => {:col => :transportlength, :type => :int},
172
- 6 => {:col => :transportdescription},
173
- 7 => {:col => :transportcosts, :type => :human_number, :decimals => 2},
174
- 8 => {:col => :travelfixed},
175
- 9 => {:col => :workinternal},
176
- 10 => {
213
+ @tv_settings.col_orig_no_for_id(:tkm) => {:col => :transportlength, :type => :int},
214
+ @tv_settings.col_orig_no_for_id(:tdescr) => {:col => :transportdescription},
215
+ @tv_settings.col_orig_no_for_id(:ttype) => {
216
+ :col => :timetype,
217
+ :value_callback => lambda{|data|
218
+ @time_types.key(data[:value])
219
+ },
220
+ :value_set_callback => proc{|data|
221
+ Knj::Web.html(@time_types.fetch(data[:value]))
222
+ }
223
+ },
224
+ @tv_settings.col_orig_no_for_id(:cost) => {:col => :transportcosts, :type => :human_number, :decimals => 2},
225
+ @tv_settings.col_orig_no_for_id(:fixed) => {:col => :travelfixed},
226
+ @tv_settings.col_orig_no_for_id(:int) => {:col => :workinternal},
227
+ @tv_settings.col_orig_no_for_id(:task) => {
177
228
  :col => :task_id,
178
229
  :value_callback => lambda{|data|
179
- task = @args[:oata].ob.get_by(:Task, "title" => data[:value])
180
-
181
- if !task
182
- return 0
183
- else
184
- return task.id
230
+ #Return ID of the found task or 0 if none was found.
231
+ @ob.list(:Task) do |task|
232
+ return task.id if task.name == data[:value]
185
233
  end
234
+
235
+ return 0
186
236
  },
187
- :value_set_callback => proc{|data| data[:model].task_name }
237
+ :value_set_callback => proc{|data|
238
+ #Return the task-name for the current rows timelog.
239
+ data[:model].task_name
240
+ }
188
241
  },
189
- 11 => {
242
+ @tv_settings.col_orig_no_for_id(:track) => {
190
243
  :value_callback => lambda{|data|
191
244
  if !data[:model]
192
245
  Knj::Gtk2.msgbox(_("You cannot track a date. Please track a timelog instead."))
193
246
  return false
194
247
  else
195
248
  if data[:value]
196
- @args[:oata].timelog_active = data[:model]
197
- elsif data[:model] == @args[:oata].timelog_active
198
- @args[:oata].timelog_stop_tracking
249
+ @oata.timelog_active = data[:model]
250
+ elsif data[:model] == @oata.timelog_active
251
+ @oata.timelog_stop_tracking
199
252
  end
200
253
 
201
- return data[:model] == @args[:oata].timelog_active
254
+ return data[:model] == @oata.timelog_active
202
255
  end
203
256
  }
204
257
  }
@@ -207,11 +260,20 @@ class Openall_time_applet::Gui::Win_main
207
260
 
208
261
 
209
262
  #The ID column should not be visible (it is only used to identify which timelog the row represents).
210
- @gui["tvTimelogs"].columns[0].visible = false
263
+ @tv.columns[0].visible = false
211
264
 
212
265
 
213
266
  #Move the columns around to the right order (the way Jacob wanted them).
214
- @gui["tvTimelogs"].move_column_after(@gui["tvTimelogs"].columns[10], @gui["tvTimelogs"].columns[3])
267
+ @tv.move_column_after(@tv.columns[10], @tv.columns[3])
268
+ @tv.move_column_after(@tv.columns[11], @tv.columns[3])
269
+ @tv.move_column_after(@tv.columns[11], @tv.columns[5])
270
+ @tv.move_column_after(@tv.columns[9], @tv.columns[6])
271
+ @tv.move_column_after(@tv.columns[11], @tv.columns[8])
272
+
273
+
274
+ #When a new row is selected, is should be evaluated if the minus-button should be active or not.
275
+ @tv.selection.signal_connect("changed", &self.method(:validate_minus_active))
276
+ self.validate_minus_active
215
277
 
216
278
 
217
279
  #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.
@@ -223,8 +285,10 @@ class Openall_time_applet::Gui::Win_main
223
285
 
224
286
 
225
287
  #Reload the treeview if something happened to a timelog.
226
- @reload_id = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["add", "update", "delete"], &self.method(:reload_timelogs))
227
- @reload_id_update = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["update"], &self.method(:on_timelog_update))
288
+ @reload_id = @ob.connect("object" => :Timelog, "signals" => ["add", "update"], &self.method(:reload_timelogs))
289
+ @reload_id_delete = @ob.connect("object" => :Timelog, "signals" => ["delete"], &self.method(:on_timelog_delete))
290
+ @reload_id_update = @ob.connect("object" => :Timelog, "signals" => ["update"], &self.method(:on_timelog_update))
291
+ @reload_tasks_id = @ob.connect("object" => :Task, "signals" => ["add"], &self.method(:on_task_added))
228
292
 
229
293
 
230
294
  #Update switch-button.
@@ -232,40 +296,43 @@ class Openall_time_applet::Gui::Win_main
232
296
 
233
297
 
234
298
  #Update switch-button when active timelog is changed.
235
- @event_timelog_active_changed = @args[:oata].events.connect(:timelog_active_changed, &self.method(:on_timelog_active_changed))
299
+ @event_timelog_active_changed = @oata.events.connect(:timelog_active_changed, &self.method(:on_timelog_active_changed))
236
300
 
237
301
 
238
302
  #This timeout controls the updating of the timelog-info-frame and the time-counter for the active timelog in the treeview.
239
303
  @timeout_id = Gtk.timeout_add(1000, &self.method(:timeout_update_sec))
240
-
304
+ self.timeout_update_sec if @oata.timelog_active
241
305
 
242
306
 
243
307
  #Initializes sync-box.
244
- @gui["btnSyncPrepareTransfer"].label = _("Transfer")
308
+ lab_transfer = _("Transfer")
309
+ lab_transfer = "#{lab_transfer[0, 1]}_#{lab_transfer[1, 99]}" #For 'r'-shortcut.
310
+ @gui["btnSyncPrepareTransfer"].label = lab_transfer
311
+
245
312
 
246
313
  #Generate list-store containing tasks for the task-column.
247
- task_ls = Gtk::ListStore.new(String, String)
248
- iter = task_ls.append
314
+ @task_ls = Gtk::ListStore.new(String, String)
315
+ iter = @task_ls.append
249
316
  iter[0] = _("None")
250
317
  iter[1] = 0.to_s
251
318
 
252
319
  tasks = [_("Choose:")]
253
- @args[:oata].ob.list(:Task, "orderby" => "title") do |task|
254
- iter = task_ls.append
255
- iter[0] = task[:title]
320
+ @ob.static(:Task, :tasks_to_show) do |task|
321
+ iter = @task_ls.append
322
+ iter[0] = task.name
256
323
  iter[1] = task.id.to_s
257
324
  tasks << task
258
325
  end
259
326
 
260
327
  #Initialize timelog treeview.
261
- init_data = Knj::Gtk2::Tv.init(@gui["tvTimelogsPrepareTransfer"], [
328
+ init_data = Knj::Gtk2::Tv.init(@tvpt, [
262
329
  _("ID"),
263
330
  {
264
331
  :title => _("Description"),
265
332
  :type => :string,
266
333
  :expand => true
267
334
  },
268
- _("Timestamp"),
335
+ _("Stamp"),
269
336
  _("Time"),
270
337
  _("T-time"),
271
338
  _("T-km"),
@@ -280,7 +347,7 @@ class Openall_time_applet::Gui::Win_main
280
347
  :type => :toggle
281
348
  },
282
349
  {
283
- :title => _("Internal"),
350
+ :title => _("Internal")[0, 3] + ".",
284
351
  :type => :toggle
285
352
  },
286
353
  {
@@ -290,7 +357,7 @@ class Openall_time_applet::Gui::Win_main
290
357
  {
291
358
  :title => _("Task"),
292
359
  :type => :combo,
293
- :model => task_ls,
360
+ :model => @task_ls,
294
361
  :has_entry => false,
295
362
  :expand => true
296
363
  },
@@ -299,7 +366,7 @@ class Openall_time_applet::Gui::Win_main
299
366
 
300
367
  @tv_settings_pt = Gtk2_treeview_settings.new(
301
368
  :id => "win_main_tvTimelogsPrepareTransfer",
302
- :tv => @gui["tvTimelogsPrepareTransfer"],
369
+ :tv => @tvpt,
303
370
  :col_ids => {
304
371
  0 => :id,
305
372
  1 => :descr,
@@ -317,13 +384,16 @@ class Openall_time_applet::Gui::Win_main
317
384
  }
318
385
  )
319
386
 
320
- @gui["tvTimelogsPrepareTransfer"].move_column_after(@gui["tvTimelogsPrepareTransfer"].columns[1], @gui["tvTimelogsPrepareTransfer"].columns[3])
321
- @gui["tvTimelogsPrepareTransfer"].move_column_after(@gui["tvTimelogsPrepareTransfer"].columns[11], @gui["tvTimelogsPrepareTransfer"].columns[4])
387
+ @tvpt.move_column_after(@tvpt.columns[1], @tvpt.columns[3])
388
+ @tvpt.move_column_after(@tvpt.columns[11], @tvpt.columns[3])
389
+ @tvpt.move_column_after(@tvpt.columns[9], @tvpt.columns[4])
390
+ @tvpt.move_column_after(@tvpt.columns[10], @tvpt.columns[5])
391
+ @tvpt.move_column_after(@tvpt.columns[9], @tvpt.columns[6])
322
392
 
323
393
  #Make columns editable.
324
394
  Knj::Gtk2::Tv.editable_text_renderers_to_model(
325
- :ob => @args[:oata].ob,
326
- :tv => @gui["tvTimelogsPrepareTransfer"],
395
+ :ob => @oata.ob,
396
+ :tv => @tvpt,
327
397
  :model_class => :Timelog,
328
398
  :renderers => init_data[:renderers],
329
399
  :change_before => proc{ @dont_reload_sync = true },
@@ -337,17 +407,79 @@ class Openall_time_applet::Gui::Win_main
337
407
  }
338
408
  }
339
409
  )
340
- @gui["tvTimelogsPrepareTransfer"].columns[0].visible = false
410
+ @tvpt.columns[0].visible = false
341
411
  @gui["vboxPrepareTransfer"].hide
342
- @reload_preparetransfer_id = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["add", "update", "delete"], &self.method(:reload_timelogs))
412
+ @reload_preparetransfer_id = @ob.connect("object" => :Timelog, "signals" => ["add", "update"], &self.method(:reload_timelogs_preparetransfer))
343
413
 
344
414
 
345
415
 
346
416
  #Show the window.
347
- @gui["window"].show
417
+ @window.show
348
418
  self.timelog_info_trigger
349
- width = @gui["window"].size[0]
350
- @gui["window"].resize(width, 1)
419
+ width = @window.size[0]
420
+ @window.resize(width, 1)
421
+ end
422
+
423
+ def window_resize_height_disable
424
+ hints = Gdk::Geometry.new
425
+
426
+ hints.min_width = 1
427
+ hints.max_width = 9999
428
+
429
+ hints.min_height = 1
430
+ hints.max_height = 1
431
+
432
+ @window.set_geometry_hints(@expander, hints, Gdk::Window::HINT_MAX_SIZE)
433
+ end
434
+
435
+ def window_resize_height_enable
436
+ hints = Gdk::Geometry.new
437
+
438
+ hints.min_width = 1
439
+ hints.max_width = 9999
440
+
441
+ hints.min_height = 1
442
+ hints.max_height = 9999
443
+
444
+ @window.set_geometry_hints(@expander, hints, Gdk::Window::HINT_MAX_SIZE)
445
+ end
446
+
447
+ def task_ls_resort
448
+ @task_ls.set_sort_column_id(0)
449
+ @task_ls.set_sort_func(0, &lambda{|iter1, iter2|
450
+ task_id_1 = iter1[1].to_i
451
+ task_id_2 = iter2[1].to_i
452
+
453
+ task_name_1 = iter1[0].to_s.downcase
454
+ task_name_2 = iter2[0].to_s.downcase
455
+
456
+ if task_id_1 == 0
457
+ return -1
458
+ elsif task_id_2 == 0
459
+ return 1
460
+ else
461
+ return task_name_1 <=> task_name_2
462
+ end
463
+ })
464
+ end
465
+
466
+ #Called after a new task has been added.
467
+ def on_task_added(task)
468
+ puts "New task added: #{task.to_hash}"
469
+
470
+ #Add the new task to the treeview rows.
471
+ iter = @task_ls.append
472
+ iter[0] = task.name
473
+ iter[1] = task.id.to_s
474
+
475
+ self.task_ls_resort
476
+
477
+ renderer = @tv_settings.cellrenderer_for_id(:task)
478
+ renderer.model = @task_ls
479
+
480
+ #Add the new task to the combobox in the top.
481
+ @gui["cbTask"].append_model(:model => task)
482
+ @gui["cbTask"].resort
351
483
  end
352
484
 
353
485
  #This is called when an item from the description-entry-completion-menu is selected. This method sets the selected text in the description-entry.
@@ -394,13 +526,13 @@ class Openall_time_applet::Gui::Win_main
394
526
  added = {}
395
527
  @descr_ec.model.clear
396
528
 
397
- @args[:oata].ob.list(:Worktime, "orderby" => "timestamp") do |worktime|
529
+ @ob.list(:Worktime, "orderby" => [["timestamp", "desc"]]) do |worktime|
398
530
  next if added.key?(worktime[:comment])
399
531
  added[worktime[:comment]] = true
400
532
  @descr_ec.model.append[0] = worktime[:comment]
401
533
  end
402
534
 
403
- @args[:oata].ob.list(:Timelog, "orderby" => "descr") do |timelog|
535
+ @ob.list(:Timelog, "orderby" => [["timestamp", "desc"]]) do |timelog|
404
536
  next if added.key?(timelog[:descr])
405
537
  added[timelog[:descr]] = true
406
538
  @descr_ec.model.append[0] = timelog[:descr]
@@ -408,57 +540,63 @@ class Openall_time_applet::Gui::Win_main
408
540
  end
409
541
 
410
542
  def reload_timelogs_preparetransfer
411
- return nil if @dont_reload_sync or @gui["tvTimelogsPrepareTransfer"].destroyed?
412
- @gui["tvTimelogsPrepareTransfer"].model.clear
413
- @timelogs_sync_count = 0
414
- tnow_str = Time.now.strftime("%Y %m %d")
543
+ return nil if @dont_reload_sync or @tvpt.destroyed?
544
+ @dont_reload_sync = true
415
545
 
416
- @args[:oata].ob.list(:Timelog, "task_id_not" => ["", 0], "orderby" => "timestamp") do |timelog|
417
- #Read time and transport from timelog.
418
- time = timelog[:time].to_i
419
- transport = timelog[:time_transport].to_i
420
-
421
- #If transport is logged, then the work if offsite. It should be rounded up by 30 min. Else 15 min. round up.
422
- if transport > 0
423
- roundup = 1800
424
- else
425
- roundup = 900
426
- end
427
-
428
- #Do the actual counting.
429
- count_rounded_time = 0
430
- loop do
431
- break if count_rounded_time >= time
432
- count_rounded_time += roundup
433
- end
434
-
435
- #Set sync-time on timelog.
436
- timelog[:time_sync] = count_rounded_time
437
-
438
- tstamp = timelog.timestamp
546
+ begin
547
+ @tvpt.model.clear
548
+ @timelogs_sync_count = 0
549
+ tnow_str = Time.now.strftime("%Y %m %d")
439
550
 
440
- if tstamp.strftime("%Y %m %d") == tnow_str
441
- tstamp_str = tstamp.strftime("%H:%M")
442
- else
443
- tstamp_str = tstamp.strftime("%d/%m")
551
+ @ob.list(:Timelog, "task_id_not" => ["", 0], "orderby" => [["timestamp", "desc"]]) do |timelog|
552
+ #Read time and transport from timelog.
553
+ time = timelog[:time].to_i
554
+ transport = timelog[:time_transport].to_i
555
+
556
+ #If transport is logged, then the work if offsite. It should be rounded up by 30 min. Else 15 min. round up.
557
+ if transport > 0
558
+ roundup = 1800
559
+ else
560
+ roundup = 900
561
+ end
562
+
563
+ #Do the actual counting.
564
+ count_rounded_time = 0
565
+ loop do
566
+ break if count_rounded_time >= time
567
+ count_rounded_time += roundup
568
+ end
569
+
570
+ #Set sync-time on timelog.
571
+ timelog[:time_sync] = count_rounded_time
572
+
573
+ tstamp = timelog.timestamp
574
+
575
+ if tstamp.strftime("%Y %m %d") == tnow_str
576
+ tstamp_str = tstamp.strftime("%H:%M")
577
+ else
578
+ tstamp_str = tstamp.strftime("%d/%m")
579
+ end
580
+
581
+ @tv_settings_pt.append(
582
+ :id => timelog.id,
583
+ :descr => timelog[:descr],
584
+ :timestamp => tstamp_str,
585
+ :time => timelog.time_as_human,
586
+ :ttime => timelog.time_transport_as_human,
587
+ :tkm => Knj::Locales.number_out(timelog[:transportlength], 0),
588
+ :tdescr => timelog.transport_descr_short,
589
+ :cost => Knj::Locales.number_out(timelog[:transportcosts], 2),
590
+ :fixed => Knj::Strings.yn_str(timelog[:travelfixed], true, false),
591
+ :internal => Knj::Strings.yn_str(timelog[:workinternal], true, false),
592
+ :skip => Knj::Strings.yn_str(timelog[:sync_need], false, true),
593
+ :task => timelog.task_name,
594
+ :sync_time => Knj::Strings.secs_to_human_time_str(count_rounded_time, :secs => false)
595
+ )
596
+ @timelogs_sync_count += 1
444
597
  end
445
-
446
- @tv_settings_pt.append(
447
- :id => timelog.id,
448
- :descr => timelog[:descr],
449
- :timestamp => tstamp_str,
450
- :time => timelog.time_as_human,
451
- :ttime => timelog.time_transport_as_human,
452
- :tkm => Knj::Locales.number_out(timelog[:transportlength], 0),
453
- :tdescr => timelog.transport_descr_short,
454
- :cost => Knj::Locales.number_out(timelog[:transportcosts], 2),
455
- :fixed => Knj::Strings.yn_str(timelog[:travelfixed], true, false),
456
- :internal => Knj::Strings.yn_str(timelog[:workinternal], true, false),
457
- :skip => Knj::Strings.yn_str(timelog[:sync_need], false, true),
458
- :task => timelog.task_name,
459
- :sync_time => Knj::Strings.secs_to_human_time_str(count_rounded_time, :secs => false)
460
- )
461
- @timelogs_sync_count += 1
598
+ ensure
599
+ @dont_reload_sync = nil
462
600
  end
463
601
  end
464
602
 
@@ -470,7 +608,7 @@ class Openall_time_applet::Gui::Win_main
470
608
  Knj::Gtk2.msgbox("msg" => _("There is nothing to sync at this time."), "run" => false)
471
609
  else
472
610
  #...else show the window.
473
- @gui["expOverview"].hide
611
+ @expander.hide
474
612
  self.update_sync_totals
475
613
  @gui["vboxPrepareTransfer"].show
476
614
  end
@@ -479,13 +617,18 @@ class Openall_time_applet::Gui::Win_main
479
617
  def update_sync_totals
480
618
  total_secs = 0
481
619
 
482
- @gui["tvTimelogsPrepareTransfer"].model.each do |model, path, iter|
483
- time_val = @gui["tvTimelogsPrepareTransfer"].model.get_value(iter, 12)
620
+ @tvpt.model.each do |model, path, iter|
621
+ time_val = @tvpt.model.get_value(iter, 12)
484
622
 
485
- begin
486
- total_secs += Knj::Strings.human_time_str_to_secs(time_val)
487
- rescue
488
- #ignore - user is properly entering stuff.
623
+ col_no_skip = @tv_settings_pt.col_orig_no_for_id(:skip)
624
+ skip_val = @tvpt.model.get_value(iter, col_no_skip)
625
+
626
+ if skip_val != 1
627
+ begin
628
+ total_secs += Knj::Strings.human_time_str_to_secs(time_val)
629
+ rescue
630
+ #ignore - user is properly entering stuff.
631
+ end
489
632
  end
490
633
  end
491
634
 
@@ -493,16 +636,16 @@ class Openall_time_applet::Gui::Win_main
493
636
  end
494
637
 
495
638
  def on_btnCancelPrepareTransfer_clicked
496
- @gui["expOverview"].show
639
+ @expander.show
497
640
  @gui["vboxPrepareTransfer"].hide
498
641
  end
499
642
 
500
643
  #This method is called, when editting starts in a description-, time- or task-cell. If it is the active timelog, then editting is canceled.
501
644
  def on_cell_editingStarted(renderer, editable, path, col_title)
502
- iter = @gui["tvTimelogs"].model.get_iter(path)
503
- timelog_id = @gui["tvTimelogs"].model.get_value(iter, 0).to_i
645
+ iter = @tv.model.get_iter(path)
646
+ timelog_id = @tv.model.get_value(iter, 0).to_i
504
647
 
505
- if tlog = @args[:oata].timelog_active and tlog.id.to_i == timelog_id
648
+ if tlog = @oata.timelog_active and tlog.id.to_i == timelog_id
506
649
  renderer.stop_editing(true)
507
650
  Knj::Gtk2.msgbox(_("You cannot edit this on the active timelog."))
508
651
  end
@@ -522,16 +665,19 @@ class Openall_time_applet::Gui::Win_main
522
665
  def reload_timelogs
523
666
  self.reload_descr_completion
524
667
 
525
- return nil if @dont_reload or @gui["tvTimelogs"].destroyed?
668
+ return nil if @dont_reload or @tv.destroyed?
669
+ @log.debug("Reloading main treeview.")
670
+
526
671
  tnow_str = Time.now.strftime("%Y %m %d")
527
- @gui["tvTimelogs"].model.clear
672
+ @tv.model.clear
528
673
 
529
674
  #Create date-parent elements that the timelogs will be appended under.
530
675
  dates = {}
531
676
  now_year = Time.now.year
532
677
 
533
- @args[:oata].ob.list(:Timelog, "orderby" => "timestamp") do |tlog|
678
+ @ob.list(:Timelog, "orderby" => [["timestamp", "desc"]]) do |tlog|
534
679
  tstamp = Datet.in(tlog[:timestamp])
680
+ next if !tstamp
535
681
  str = tstamp.out(:time => false)
536
682
 
537
683
  if !dates.key?(str)
@@ -554,17 +700,19 @@ class Openall_time_applet::Gui::Win_main
554
700
  end
555
701
 
556
702
  #Append the timelogs to the parent dates.
557
- @args[:oata].ob.list(:Timelog, "orderby" => ["task_id", "descr", "timestamp"]) do |timelog|
703
+ @ob.list(:Timelog, "orderby" => ["task_id", "descr", "timestamp"]) do |timelog|
558
704
  begin
559
705
  tstamp_str = timelog.timestamp_str
560
706
  rescue => e
561
707
  tstamp_str = "[#{_("error")}: #{e.message}"
562
708
  end
563
709
 
564
- tstamp = timelog.timestamp
565
- tstamp_str = tstamp.strftime("%H:%M")
566
-
567
- parent = dates[tstamp.out(:time => false)][:iter]
710
+ if tstamp = timelog.timestamp
711
+ tstamp_str = tstamp.strftime("%H:%M")
712
+ parent = dates[tstamp.out(:time => false)][:iter]
713
+ else
714
+ parent = nil
715
+ end
568
716
 
569
717
  @tv_settings.append_adv(
570
718
  :parent => parent,
@@ -575,6 +723,7 @@ class Openall_time_applet::Gui::Win_main
575
723
  :descr => Knj::Web.html(timelog[:descr]),
576
724
  :ttime => timelog.time_transport_as_human,
577
725
  :tkm => Knj::Locales.number_out(timelog[:transportlength], 0),
726
+ :ttype => @time_types[timelog[:timetype]],
578
727
  :tdescr => Knj::Web.html(timelog[:transportdescription]),
579
728
  :cost => Knj::Locales.number_out(timelog[:transportcosts], 2),
580
729
  :fixed => Knj::Strings.yn_str(timelog[:travelfixed], true, false),
@@ -585,14 +734,14 @@ class Openall_time_applet::Gui::Win_main
585
734
  end
586
735
 
587
736
  #Make all dates expand their content (timelogs).
588
- @gui["tvTimelogs"].expand_all
737
+ @tv.expand_all
589
738
 
590
739
  #Reset cache of which rows are set to bold.
591
740
  @bold_rows = {}
592
741
  end
593
742
 
594
743
  def timelog_tv_data_by_timelog(timelog)
595
- @gui["tvTimelogs"].model.each do |model, path, iter|
744
+ @tv.model.each do |model, path, iter|
596
745
  timelog_i_id = iter[0].to_i
597
746
 
598
747
  if timelog.id.to_i == timelog_i_id
@@ -604,14 +753,14 @@ class Openall_time_applet::Gui::Win_main
604
753
  end
605
754
  end
606
755
 
607
- raise sprintf(_("Could not find timelog in treeview: '%s'."), timelog.id)
756
+ raise Errno::ENOENT, sprintf(_("Could not find timelog in treeview: '%s'."), timelog.id)
608
757
  end
609
758
 
610
759
  def date_tv_data_by_datet(datet, args = nil)
611
760
  datet_str = datet.out(:time => false)
612
761
  add_after = nil
613
762
 
614
- @gui["tvTimelogs"].model.each do |model, path, iter|
763
+ @tv.model.each do |model, path, iter|
615
764
  #Skip the iter's that are timelogs (we look for a parent-date).
616
765
  timelog_id = iter[0].to_i
617
766
  next if timelog_id != 0
@@ -633,7 +782,7 @@ class Openall_time_applet::Gui::Win_main
633
782
  end
634
783
 
635
784
  if args and args[:add]
636
- iter = @gui["tvTimelogs"].insert_after(nil, add_after)
785
+ iter = @tv.insert_after(nil, add_after)
637
786
  iter[1] = "<b>#{Knj::Web.html(datet.out(:time => false))}</b>"
638
787
 
639
788
  return {
@@ -654,18 +803,26 @@ class Openall_time_applet::Gui::Win_main
654
803
  parent_date = Php4r.strip_tags(parent_iter[1])
655
804
 
656
805
  #Get the date from the selected timelog.
657
- tlog_date_str = timelog.timestamp.out(:time => false)
806
+ tlog_date_str = timelog.timestamp.out(:time => false, :year => false)
658
807
 
659
808
  #The date of the timelog has been updated, and the timelog has to be moved elsewhere in the treeview.
660
809
  if parent_date != tlog_date_str
661
810
  #Wait 5 miliseconds so the 'dont_reload' variable wont be 'true' any more.
662
811
  Gtk.timeout_add(5) do
812
+ @log.debug("Timestamps wasnt the same - reload treeview (parent: #{parent_date} vs tlog: #{tlog_date_str}).")
813
+
663
814
  #Reload timelogs to make the changed timelog appear under the right date.
664
815
  self.reload_timelogs
665
816
 
666
817
  #Re-select the timelog in the treeview.
667
- tlog_data = self.timelog_tv_data_by_timelog(timelog)
668
- @gui["tvTimelogs"].selection.select_iter(tlog_data[:iter])
818
+ begin
819
+ if !timelog.deleted?
820
+ tlog_data = self.timelog_tv_data_by_timelog(timelog)
821
+ @tv.selection.select_iter(tlog_data[:iter])
822
+ end
823
+ rescue Errno::ENOENT
824
+ #Ignore - it has been deleted.
825
+ end
669
826
 
670
827
  #Return false for the timeout, so it wont be called again.
671
828
  false
@@ -673,13 +830,38 @@ class Openall_time_applet::Gui::Win_main
673
830
  end
674
831
  end
675
832
 
833
+ #This method handels events for when a timelog is deleted. It removes the timelog from various treeviews, so it isnt necessary to do a complete reload of them, which takes a lot longer.
834
+ def on_timelog_delete(timelog)
835
+ del_id = timelog.id.to_i
836
+
837
+ @tv.model.each do |model, path, iter|
838
+ timelog_id = model.get_value(iter, 0).to_i
839
+ next if timelog_id <= 0
840
+
841
+ if timelog_id == del_id
842
+ model.remove(iter)
843
+ break
844
+ end
845
+ end
846
+
847
+ @tvpt.model.each do |model, path, iter|
848
+ timelog_id = model.get_value(iter, 0).to_i
849
+ next if timelog_id <= 0
850
+
851
+ if timelog_id == del_id
852
+ model.remove(iter)
853
+ break
854
+ end
855
+ end
856
+ end
857
+
676
858
  #Called when the quit-menu-item is activated.
677
859
  def on_imiQuit_activate
678
860
  #Check if a timelog needs to be synced. If so the user needs to confirm he really wants to quit.
679
861
  timelog_found = nil
680
862
  do_destroy = true
681
863
 
682
- @args[:oata].ob.list(:Timelog, "task_id_not" => ["", 0]) do |timelog|
864
+ @ob.list(:Timelog, "task_id_not" => ["", 0]) do |timelog|
683
865
  if timelog.time_total > 0 or timelog.time_total(:transport => true) > 0 or timelog[:sync_need].to_i == 1
684
866
  timelog_found = timelog
685
867
  break
@@ -692,55 +874,54 @@ class Openall_time_applet::Gui::Win_main
692
874
  end
693
875
  end
694
876
 
695
- @args[:oata].destroy if do_destroy
877
+ @oata.destroy if do_destroy
696
878
  end
697
879
 
698
880
  def on_imiPreferences_activate
699
- @args[:oata].show_preferences
881
+ @oata.show_preferences
700
882
  end
701
883
 
702
884
  def on_imiWeekview_activate
703
- @args[:oata].show_worktime_overview
885
+ @oata.show_worktime_overview
704
886
  end
705
887
 
706
888
  def on_window_destroy
707
889
  #Unconnect reload-event. Else it will crash on call to destroyed object. Also frees up various ressources.
708
- @args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id)
709
- @args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id_update)
710
- @args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_preparetransfer_id)
890
+ @ob.unconnect("object" => :Timelog, "conn_ids" => [@reload_id, @reload_id_update, @reload_id_delete, @reload_preparetransfer_id])
891
+ @ob.unconnect("object" => :Task, "conn_ids" => [@reload_tasks_id])
711
892
 
712
- @args[:oata].events.disconnect(:timelog_active_changed, @event_timelog_active_changed) if @event_timelog_active_changed
893
+ @oata.events.disconnect(:timelog_active_changed, @event_timelog_active_changed) if @event_timelog_active_changed
713
894
  @event_timelog_active_changed = nil
714
895
  Gtk.timeout_remove(@timeout_id)
715
896
  end
716
897
 
717
898
  def on_expOverview_activate(expander)
718
899
  if expander.expanded?
719
- @gui["window"].resize(@gui["window"].size[0], 480)
900
+ self.window_resize_height_enable
901
+ @log.debug("Current window-size: #{@window.size}")
902
+ size = @window.size
903
+ @window.resize(size[0], 480) if size[1] < 480
720
904
  self.timelog_info_trigger
721
905
  else
722
- Gtk.timeout_add(200) do
723
- self.timelog_info_trigger
724
- @gui["window"].resize(@gui["window"].size[0], 1)
725
- false
726
- end
906
+ self.window_resize_height_disable
907
+ self.timelog_info_trigger
727
908
  end
728
909
  end
729
910
 
730
911
  #This method handles the "Timelog info"-frame. Hides, shows and updates the info in it.
731
912
  def timelog_info_trigger
732
- if tlog = @args[:oata].timelog_active
733
- time_tracked = @args[:oata].timelog_active_time_tracked + tlog.time_total
913
+ if tlog = @oata.timelog_active
914
+ time_tracked = @oata.timelog_active_time_tracked + tlog.time_total
734
915
  @gui["btnSwitch"].label = "#{_("Stop")} (#{Knj::Strings.secs_to_human_short_time(time_tracked)})"
735
916
 
736
917
  #Update icon every second while showing main-window, so it looks like stop-button and tray-icon-time is in sync (else tray will only update every 30 sec. which will make it look out of sync, even though it wont be).
737
- @args[:oata].ti.update_icon
918
+ @oata.ti.update_icon
738
919
  end
739
920
  end
740
921
 
741
922
  def on_btnSwitch_clicked
742
- if @args[:oata].timelog_active
743
- @args[:oata].timelog_stop_tracking
923
+ if @oata.timelog_active
924
+ @oata.timelog_stop_tracking
744
925
  @gui["txtDescr"].grab_focus
745
926
  else
746
927
  descr = @gui["txtDescr"].text.to_s.strip
@@ -752,20 +933,20 @@ class Openall_time_applet::Gui::Win_main
752
933
  task_id = task.id
753
934
  end
754
935
 
755
- timelog = @args[:oata].ob.get_by(:Timelog, {
936
+ timelog = @ob.get_by(:Timelog, {
756
937
  "task_id" => task_id,
757
938
  "descr_lower" => descr,
758
939
  "timestamp_day" => Time.now
759
940
  })
760
941
 
761
942
  if !timelog
762
- timelog = @args[:oata].ob.add(:Timelog, {
943
+ timelog = @ob.add(:Timelog, {
763
944
  :task_id => task_id,
764
945
  :descr => descr
765
946
  })
766
947
  end
767
948
 
768
- @args[:oata].timelog_active = timelog
949
+ @oata.timelog_active = timelog
769
950
  @gui["txtDescr"].text = ""
770
951
  @gui["cbTask"].sel = _("Choose:")
771
952
  end
@@ -776,7 +957,7 @@ class Openall_time_applet::Gui::Win_main
776
957
  #This method updates the switch button to start or stop, based on the if a timelog is tracked or not.
777
958
  def update_switch_button
778
959
  but = @gui["btnSwitch"]
779
- tlog_act = @args[:oata].timelog_active
960
+ tlog_act = @oata.timelog_active
780
961
 
781
962
  if tlog_act
782
963
  but.image = Gtk::Image.new(Gtk::Stock::MEDIA_STOP, Gtk::IconSize::BUTTON)
@@ -805,7 +986,7 @@ class Openall_time_applet::Gui::Win_main
805
986
  #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.
806
987
  def check_rows
807
988
  return nil if @tv_editting
808
- act_timelog = @args[:oata].timelog_active
989
+ act_timelog = @oata.timelog_active
809
990
 
810
991
  if act_timelog
811
992
  act_timelog_id = act_timelog.id
@@ -813,40 +994,59 @@ class Openall_time_applet::Gui::Win_main
813
994
  act_timelog_id = nil
814
995
  end
815
996
 
816
- rows_bold = [:timestamp, :time, :descr, :ttime, :tkm, :tdescr, :cost, :task]
997
+ col_no_track = @tv_settings.col_orig_no_for_id(:track)
817
998
 
818
- @gui["tvTimelogs"].model.each do |model, path, iter|
999
+ @tv.model.each do |model, path, iter|
819
1000
  timelog_id = model.get_value(iter, 0).to_i
820
1001
  bold = false
821
- iter_id = iter.to_s.to_i
822
1002
 
823
1003
  #Update time tracked.
824
1004
  if timelog_id == act_timelog_id
825
- secs = act_timelog.time_total + @args[:oata].timelog_active_time_tracked
1005
+ secs = act_timelog.time_total + @oata.timelog_active_time_tracked
826
1006
  col_no = @tv_settings.col_orig_no_for_id(:time)
827
1007
  iter[col_no] = "<b>#{Knj::Strings.secs_to_human_time_str(secs, :secs => false)}</b>"
828
1008
  bold = true
1009
+
1010
+ @log.debug("Setting track-check for timelog '#{timelog_id}'.")
1011
+ iter[col_no_track] = 1
1012
+ else
1013
+ if iter[col_no_track] == 1
1014
+ #Track-check-value is set for a timelog that isnt being tracked - remove it.
1015
+ @log.debug("Remove track-check for timelog '#{timelog_id}'.")
1016
+ iter[col_no_track] = 0
1017
+ end
1018
+
1019
+ if @bold_rows[timelog_id] == true
1020
+ #Remove bold text if timelog is not being tracked.
1021
+ ROWS_BOLD.each do |row_no|
1022
+ col_no = @tv_settings.col_orig_no_for_id(row_no)
1023
+ cur_val = model.get_value(iter, col_no)
1024
+ iter[col_no] = cur_val.gsub(/^<b>/i, "").gsub(/<\/b>$/i, "")
1025
+ end
1026
+
1027
+ @bold_rows.delete(timelog_id)
1028
+ end
829
1029
  end
830
1030
 
831
1031
  #Set all columns to bold if not already set.
832
- if bold and !@bold_rows.key?(iter_id)
833
- rows_bold.each do |row_no|
1032
+ if bold and !@bold_rows.key?(timelog_id)
1033
+ ROWS_BOLD.each do |row_no|
834
1034
  col_no = @tv_settings.col_orig_no_for_id(row_no)
835
1035
  iter[col_no] = "<b>#{model.get_value(iter, col_no)}</b>"
836
1036
  end
837
1037
 
838
- @bold_rows[iter_id] = true
1038
+ @bold_rows[timelog_id] = true
839
1039
  end
840
1040
  end
841
1041
  end
842
1042
 
843
1043
  def on_miSyncStatic_activate
844
- @args[:oata].sync_static("transient_for" => @gui["window"])
1044
+ @oata.sync_static("transient_for" => @window)
845
1045
  end
846
1046
 
847
1047
  def on_btnMinus_clicked
848
- sel = @gui["tvTimelogs"].sel
849
- tlog = @args[:oata].ob.get(:Timelog, sel[0]) if sel
1048
+ sel = @tv.sel
1049
+ tlog = @ob.get(:Timelog, sel[0]) if sel
850
1050
 
851
1051
  if !sel or !tlog
852
1052
  Knj::Gtk2.msgbox(_("Please choose a timelog to delete."), "warning")
@@ -855,7 +1055,8 @@ class Openall_time_applet::Gui::Win_main
855
1055
 
856
1056
  return nil if Knj::Gtk2.msgbox(_("Do you want to remove this timelog?"), "yesno") != "yes"
857
1057
  begin
858
- @args[:oata].ob.delete(tlog)
1058
+ @log.debug("Deleting timelog from pressing minus and confirming: '#{tlog.id}'.")
1059
+ @ob.delete(tlog)
859
1060
  rescue => e
860
1061
  Knj::Gtk2.msgbox(sprintf(_("Could not delete the timelog: %s"), e.message))
861
1062
  end
@@ -863,18 +1064,20 @@ class Openall_time_applet::Gui::Win_main
863
1064
 
864
1065
  def on_btnPlus_clicked
865
1066
  #Add new timelog to database.
866
- timelog = @args[:oata].ob.add(:Timelog)
1067
+ timelog = @ob.add(:Timelog)
1068
+ @log.debug("Timelog added from pressing plus-button: '#{timelog.to_hash}'.")
867
1069
 
868
1070
  #Focus new timelog in treeview and open the in-line-editting for the description.
869
1071
  added_id = timelog.id.to_i
870
1072
 
871
- @gui["tvTimelogs"].model.each do |model, path, iter|
1073
+ @tv.model.each do |model, path, iter|
872
1074
  col_no = @tv_settings.col_no_for_id(:id)
873
1075
  timelog_id = model.get_value(iter, col_no).to_i
874
1076
 
875
1077
  if timelog_id == added_id
876
- col = @gui["tvTimelogs"].columns[1]
877
- @gui["tvTimelogs"].set_cursor(path, col, true)
1078
+ @log.debug("Setting focus to added timelog.")
1079
+ col = @tv.columns[1]
1080
+ @tv.set_cursor(path, col, true)
878
1081
  break
879
1082
  end
880
1083
  end
@@ -888,10 +1091,21 @@ class Openall_time_applet::Gui::Win_main
888
1091
  def on_btnSyncPrepareTransfer_clicked
889
1092
  begin
890
1093
  #Destroy this window and start syncing for real.
891
- @gui["window"].destroy
892
- @args[:oata].sync_real
1094
+ @window.destroy
1095
+ @oata.sync_real
893
1096
  rescue => e
894
1097
  Knj::Gtk2.msgbox(Knj::Errors.error_str(e))
895
1098
  end
896
1099
  end
1100
+
1101
+ #Enables or disables the minus-button based on what is selected in the treeview.
1102
+ def validate_minus_active(*args)
1103
+ sel = @tv.sel
1104
+
1105
+ if sel and sel[0].to_i > 0
1106
+ @gui["btnMinus"].sensitive = true
1107
+ else
1108
+ @gui["btnMinus"].sensitive = false
1109
+ end
1110
+ end
897
1111
  end