openall_time_applet 0.0.38 → 0.0.40

Sign up to get free protection for your applications and to get access to all the features.
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