openall_time_applet 0.0.11 → 0.0.12
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/README.rdoc +2 -1
- data/VERSION +1 -1
- data/bin/openall_time_applet.rb +1 -1
- data/classes/unix_socket.rb +32 -0
- data/glade/win_main.glade +302 -0
- data/gui/trayicon.rb +5 -21
- data/gui/win_main.rb +209 -0
- data/gui/win_overview.rb +0 -135
- data/lib/openall_time_applet.rb +71 -18
- data/openall_time_applet.gemspec +5 -2
- metadata +6 -3
data/README.rdoc
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.12
|
data/bin/openall_time_applet.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
class Openall_time_applet::Unix_socket
|
2
|
+
def initialize(args)
|
3
|
+
@args = args
|
4
|
+
|
5
|
+
#Remove the sock-file if it already exists.
|
6
|
+
File.unlink(Openall_time_applet::CONFIG[:sock_path]) if File.exists?(Openall_time_applet::CONFIG[:sock_path])
|
7
|
+
|
8
|
+
#Start Unix-socket.
|
9
|
+
require "socket"
|
10
|
+
@usock = UNIXServer.new(Openall_time_applet::CONFIG[:sock_path])
|
11
|
+
|
12
|
+
#Remove the sock-file after this process is done.
|
13
|
+
Kernel.at_exit do
|
14
|
+
File.unlink(Openall_time_applet::CONFIG[:sock_path]) if File.exists?(Openall_time_applet::CONFIG[:sock_path])
|
15
|
+
end
|
16
|
+
|
17
|
+
#Start thread that listens for connections through the Unix-socket.
|
18
|
+
Knj::Thread.new do
|
19
|
+
while client = @usock.accept
|
20
|
+
client.each_line do |line|
|
21
|
+
line = line.strip
|
22
|
+
|
23
|
+
if line.strip == "open_main_window"
|
24
|
+
@args[:oata].show_main
|
25
|
+
else
|
26
|
+
print "Unknown line: #{line}\n"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<interface>
|
3
|
+
<requires lib="gtk+" version="2.24"/>
|
4
|
+
<!-- interface-naming-policy project-wide -->
|
5
|
+
<object class="GtkImage" id="image1">
|
6
|
+
<property name="visible">True</property>
|
7
|
+
<property name="can_focus">False</property>
|
8
|
+
<property name="stock">gtk-missing-image</property>
|
9
|
+
</object>
|
10
|
+
<object class="GtkImage" id="image2">
|
11
|
+
<property name="visible">True</property>
|
12
|
+
<property name="can_focus">False</property>
|
13
|
+
<property name="stock">gtk-missing-image</property>
|
14
|
+
</object>
|
15
|
+
<object class="GtkWindow" id="window">
|
16
|
+
<property name="can_focus">False</property>
|
17
|
+
<property name="window_position">center</property>
|
18
|
+
<property name="default_width">640</property>
|
19
|
+
<signal name="destroy" handler="on_window_destroy" swapped="no"/>
|
20
|
+
<child>
|
21
|
+
<object class="GtkVBox" id="vbox1">
|
22
|
+
<property name="visible">True</property>
|
23
|
+
<property name="can_focus">False</property>
|
24
|
+
<property name="spacing">5</property>
|
25
|
+
<child>
|
26
|
+
<object class="GtkMenuBar" id="menubar1">
|
27
|
+
<property name="visible">True</property>
|
28
|
+
<property name="can_focus">False</property>
|
29
|
+
<property name="ubuntu_local">True</property>
|
30
|
+
<child>
|
31
|
+
<object class="GtkMenuItem" id="menuitem1">
|
32
|
+
<property name="visible">True</property>
|
33
|
+
<property name="can_focus">False</property>
|
34
|
+
<property name="use_action_appearance">False</property>
|
35
|
+
<property name="label" translatable="yes">_Tracker</property>
|
36
|
+
<property name="use_underline">True</property>
|
37
|
+
<child type="submenu">
|
38
|
+
<object class="GtkMenu" id="menu1">
|
39
|
+
<property name="visible">True</property>
|
40
|
+
<property name="can_focus">False</property>
|
41
|
+
<property name="ubuntu_local">True</property>
|
42
|
+
<child>
|
43
|
+
<object class="GtkImageMenuItem" id="imiWeekview">
|
44
|
+
<property name="label" translatable="yes">Week view</property>
|
45
|
+
<property name="visible">True</property>
|
46
|
+
<property name="can_focus">False</property>
|
47
|
+
<property name="use_action_appearance">False</property>
|
48
|
+
<property name="image">image1</property>
|
49
|
+
<property name="use_stock">False</property>
|
50
|
+
<signal name="activate" handler="on_imiWeekview_activate" swapped="no"/>
|
51
|
+
</object>
|
52
|
+
</child>
|
53
|
+
<child>
|
54
|
+
<object class="GtkImageMenuItem" id="imiPreferences">
|
55
|
+
<property name="label" translatable="yes">Preferences</property>
|
56
|
+
<property name="visible">True</property>
|
57
|
+
<property name="can_focus">False</property>
|
58
|
+
<property name="use_action_appearance">False</property>
|
59
|
+
<property name="image">image2</property>
|
60
|
+
<property name="use_stock">False</property>
|
61
|
+
<signal name="activate" handler="on_imiPreferences_activate" swapped="no"/>
|
62
|
+
</object>
|
63
|
+
</child>
|
64
|
+
<child>
|
65
|
+
<object class="GtkSeparatorMenuItem" id="separatormenuitem1">
|
66
|
+
<property name="visible">True</property>
|
67
|
+
<property name="can_focus">False</property>
|
68
|
+
<property name="use_action_appearance">False</property>
|
69
|
+
</object>
|
70
|
+
</child>
|
71
|
+
<child>
|
72
|
+
<object class="GtkImageMenuItem" id="imiQuit">
|
73
|
+
<property name="label">gtk-quit</property>
|
74
|
+
<property name="visible">True</property>
|
75
|
+
<property name="can_focus">False</property>
|
76
|
+
<property name="use_action_appearance">False</property>
|
77
|
+
<property name="use_underline">True</property>
|
78
|
+
<property name="use_stock">True</property>
|
79
|
+
<signal name="activate" handler="on_imiQuit_activate" swapped="no"/>
|
80
|
+
</object>
|
81
|
+
</child>
|
82
|
+
</object>
|
83
|
+
</child>
|
84
|
+
</object>
|
85
|
+
</child>
|
86
|
+
</object>
|
87
|
+
<packing>
|
88
|
+
<property name="expand">False</property>
|
89
|
+
<property name="fill">True</property>
|
90
|
+
<property name="position">0</property>
|
91
|
+
</packing>
|
92
|
+
</child>
|
93
|
+
<child>
|
94
|
+
<object class="GtkFrame" id="frame1">
|
95
|
+
<property name="visible">True</property>
|
96
|
+
<property name="can_focus">False</property>
|
97
|
+
<property name="label_xalign">0</property>
|
98
|
+
<property name="shadow_type">none</property>
|
99
|
+
<child>
|
100
|
+
<object class="GtkAlignment" id="alignment1">
|
101
|
+
<property name="visible">True</property>
|
102
|
+
<property name="can_focus">False</property>
|
103
|
+
<property name="left_padding">12</property>
|
104
|
+
<child>
|
105
|
+
<object class="GtkHBox" id="hbox1">
|
106
|
+
<property name="visible">True</property>
|
107
|
+
<property name="can_focus">False</property>
|
108
|
+
<property name="spacing">6</property>
|
109
|
+
<child>
|
110
|
+
<object class="GtkEntry" id="txtDescr">
|
111
|
+
<property name="visible">True</property>
|
112
|
+
<property name="can_focus">True</property>
|
113
|
+
<property name="invisible_char">•</property>
|
114
|
+
<property name="primary_icon_activatable">False</property>
|
115
|
+
<property name="secondary_icon_activatable">False</property>
|
116
|
+
<property name="primary_icon_sensitive">True</property>
|
117
|
+
<property name="secondary_icon_sensitive">True</property>
|
118
|
+
<signal name="activate" handler="on_txtDescr_activate" swapped="no"/>
|
119
|
+
</object>
|
120
|
+
<packing>
|
121
|
+
<property name="expand">True</property>
|
122
|
+
<property name="fill">True</property>
|
123
|
+
<property name="position">0</property>
|
124
|
+
</packing>
|
125
|
+
</child>
|
126
|
+
<child>
|
127
|
+
<object class="GtkComboBox" id="cbTask">
|
128
|
+
<property name="visible">True</property>
|
129
|
+
<property name="can_focus">False</property>
|
130
|
+
</object>
|
131
|
+
<packing>
|
132
|
+
<property name="expand">False</property>
|
133
|
+
<property name="fill">True</property>
|
134
|
+
<property name="position">1</property>
|
135
|
+
</packing>
|
136
|
+
</child>
|
137
|
+
<child>
|
138
|
+
<object class="GtkButton" id="btnSwitch">
|
139
|
+
<property name="label" translatable="yes">Switch</property>
|
140
|
+
<property name="visible">True</property>
|
141
|
+
<property name="can_focus">True</property>
|
142
|
+
<property name="receives_default">True</property>
|
143
|
+
<property name="use_action_appearance">False</property>
|
144
|
+
<signal name="clicked" handler="on_btnSwitch_clicked" swapped="no"/>
|
145
|
+
</object>
|
146
|
+
<packing>
|
147
|
+
<property name="expand">False</property>
|
148
|
+
<property name="fill">True</property>
|
149
|
+
<property name="position">2</property>
|
150
|
+
</packing>
|
151
|
+
</child>
|
152
|
+
</object>
|
153
|
+
</child>
|
154
|
+
</object>
|
155
|
+
</child>
|
156
|
+
<child type="label">
|
157
|
+
<object class="GtkLabel" id="label1">
|
158
|
+
<property name="visible">True</property>
|
159
|
+
<property name="can_focus">False</property>
|
160
|
+
<property name="label" translatable="yes"><b>Description</b></property>
|
161
|
+
<property name="use_markup">True</property>
|
162
|
+
</object>
|
163
|
+
</child>
|
164
|
+
</object>
|
165
|
+
<packing>
|
166
|
+
<property name="expand">False</property>
|
167
|
+
<property name="fill">True</property>
|
168
|
+
<property name="position">1</property>
|
169
|
+
</packing>
|
170
|
+
</child>
|
171
|
+
<child>
|
172
|
+
<object class="GtkExpander" id="expOverview">
|
173
|
+
<property name="visible">True</property>
|
174
|
+
<property name="can_focus">True</property>
|
175
|
+
<signal name="activate" handler="on_expOverview_activate" swapped="no"/>
|
176
|
+
<child>
|
177
|
+
<object class="GtkVBox" id="vbox2">
|
178
|
+
<property name="visible">True</property>
|
179
|
+
<property name="can_focus">False</property>
|
180
|
+
<property name="spacing">5</property>
|
181
|
+
<child>
|
182
|
+
<object class="GtkAlignment" id="alignment2">
|
183
|
+
<property name="visible">True</property>
|
184
|
+
<property name="can_focus">False</property>
|
185
|
+
<property name="left_padding">12</property>
|
186
|
+
<child>
|
187
|
+
<object class="GtkViewport" id="viewport1">
|
188
|
+
<property name="visible">True</property>
|
189
|
+
<property name="can_focus">False</property>
|
190
|
+
<child>
|
191
|
+
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
192
|
+
<property name="visible">True</property>
|
193
|
+
<property name="can_focus">True</property>
|
194
|
+
<property name="hscrollbar_policy">automatic</property>
|
195
|
+
<property name="vscrollbar_policy">automatic</property>
|
196
|
+
<child>
|
197
|
+
<object class="GtkTreeView" id="tvTimelogs">
|
198
|
+
<property name="visible">True</property>
|
199
|
+
<property name="can_focus">True</property>
|
200
|
+
</object>
|
201
|
+
</child>
|
202
|
+
</object>
|
203
|
+
</child>
|
204
|
+
</object>
|
205
|
+
</child>
|
206
|
+
</object>
|
207
|
+
<packing>
|
208
|
+
<property name="expand">True</property>
|
209
|
+
<property name="fill">True</property>
|
210
|
+
<property name="position">0</property>
|
211
|
+
</packing>
|
212
|
+
</child>
|
213
|
+
<child>
|
214
|
+
<object class="GtkHButtonBox" id="hbuttonbox2">
|
215
|
+
<property name="visible">True</property>
|
216
|
+
<property name="can_focus">False</property>
|
217
|
+
<property name="spacing">5</property>
|
218
|
+
<property name="layout_style">start</property>
|
219
|
+
<child>
|
220
|
+
<object class="GtkButton" id="btnPlus">
|
221
|
+
<property name="label" translatable="yes">+</property>
|
222
|
+
<property name="visible">True</property>
|
223
|
+
<property name="can_focus">True</property>
|
224
|
+
<property name="receives_default">True</property>
|
225
|
+
<property name="use_action_appearance">False</property>
|
226
|
+
<signal name="clicked" handler="on_btnPlus_clicked" swapped="no"/>
|
227
|
+
</object>
|
228
|
+
<packing>
|
229
|
+
<property name="expand">False</property>
|
230
|
+
<property name="fill">False</property>
|
231
|
+
<property name="position">0</property>
|
232
|
+
</packing>
|
233
|
+
</child>
|
234
|
+
<child>
|
235
|
+
<object class="GtkButton" id="btnMinus">
|
236
|
+
<property name="label" translatable="yes">-</property>
|
237
|
+
<property name="visible">True</property>
|
238
|
+
<property name="can_focus">True</property>
|
239
|
+
<property name="receives_default">True</property>
|
240
|
+
<property name="use_action_appearance">False</property>
|
241
|
+
<signal name="clicked" handler="on_btnMinus_clicked" swapped="no"/>
|
242
|
+
</object>
|
243
|
+
<packing>
|
244
|
+
<property name="expand">False</property>
|
245
|
+
<property name="fill">False</property>
|
246
|
+
<property name="position">1</property>
|
247
|
+
</packing>
|
248
|
+
</child>
|
249
|
+
</object>
|
250
|
+
<packing>
|
251
|
+
<property name="expand">False</property>
|
252
|
+
<property name="fill">True</property>
|
253
|
+
<property name="position">1</property>
|
254
|
+
</packing>
|
255
|
+
</child>
|
256
|
+
</object>
|
257
|
+
</child>
|
258
|
+
<child type="label">
|
259
|
+
<object class="GtkLabel" id="label2">
|
260
|
+
<property name="visible">True</property>
|
261
|
+
<property name="can_focus">False</property>
|
262
|
+
<property name="label" translatable="yes">Overview</property>
|
263
|
+
</object>
|
264
|
+
</child>
|
265
|
+
</object>
|
266
|
+
<packing>
|
267
|
+
<property name="expand">True</property>
|
268
|
+
<property name="fill">True</property>
|
269
|
+
<property name="position">2</property>
|
270
|
+
</packing>
|
271
|
+
</child>
|
272
|
+
<child>
|
273
|
+
<object class="GtkHButtonBox" id="hbuttonbox1">
|
274
|
+
<property name="visible">True</property>
|
275
|
+
<property name="can_focus">False</property>
|
276
|
+
<property name="layout_style">end</property>
|
277
|
+
<child>
|
278
|
+
<object class="GtkButton" id="btnSync">
|
279
|
+
<property name="label" translatable="yes">Sync</property>
|
280
|
+
<property name="visible">True</property>
|
281
|
+
<property name="can_focus">True</property>
|
282
|
+
<property name="receives_default">True</property>
|
283
|
+
<property name="use_action_appearance">False</property>
|
284
|
+
<signal name="clicked" handler="on_btnSync_clicked" swapped="no"/>
|
285
|
+
</object>
|
286
|
+
<packing>
|
287
|
+
<property name="expand">False</property>
|
288
|
+
<property name="fill">False</property>
|
289
|
+
<property name="position">0</property>
|
290
|
+
</packing>
|
291
|
+
</child>
|
292
|
+
</object>
|
293
|
+
<packing>
|
294
|
+
<property name="expand">False</property>
|
295
|
+
<property name="fill">True</property>
|
296
|
+
<property name="position">3</property>
|
297
|
+
</packing>
|
298
|
+
</child>
|
299
|
+
</object>
|
300
|
+
</child>
|
301
|
+
</object>
|
302
|
+
</interface>
|
data/gui/trayicon.rb
CHANGED
@@ -4,6 +4,7 @@ class Openall_time_applet::Gui::Trayicon
|
|
4
4
|
|
5
5
|
def initialize(args)
|
6
6
|
@args = args
|
7
|
+
@debug = @args[:oata].debug
|
7
8
|
|
8
9
|
@ti = Gtk::StatusIcon.new
|
9
10
|
@ti.signal_connect("popup-menu", &self.method(:on_statusicon_rightclick))
|
@@ -26,6 +27,8 @@ class Openall_time_applet::Gui::Trayicon
|
|
26
27
|
|
27
28
|
#This updates the icon in the system-tray. It draws seconds on the icon, if a timelog is being tracked.
|
28
29
|
def update_icon
|
30
|
+
print "Updating icon.\n" if @debug
|
31
|
+
|
29
32
|
color = Knj::Opts.get("tray_text_color")
|
30
33
|
color = "black" if color.to_s.strip.length <= 0
|
31
34
|
|
@@ -82,15 +85,6 @@ class Openall_time_applet::Gui::Trayicon
|
|
82
85
|
end
|
83
86
|
|
84
87
|
def on_statusicon_rightclick(tray, button, time)
|
85
|
-
#Build rightclick-menu for tray-icon.
|
86
|
-
timelog_new = Gtk::ImageMenuItem.new(Gtk::Stock::NEW)
|
87
|
-
timelog_new.label = _("New timelog")
|
88
|
-
timelog_new.signal_connect("activate", &self.method(:on_timelogNew_activate))
|
89
|
-
|
90
|
-
overview = Gtk::ImageMenuItem.new(Gtk::Stock::EDIT)
|
91
|
-
overview.label = _("Timelog list")
|
92
|
-
overview.signal_connect("activate", &self.method(:on_overview_activate))
|
93
|
-
|
94
88
|
worktime_overview = Gtk::ImageMenuItem.new(Gtk::Stock::HOME)
|
95
89
|
worktime_overview.label = _("Week view")
|
96
90
|
worktime_overview.signal_connect("activate", &self.method(:on_worktimeOverview_activate))
|
@@ -106,8 +100,6 @@ class Openall_time_applet::Gui::Trayicon
|
|
106
100
|
sync.signal_connect("activate", &self.method(:on_sync_activate))
|
107
101
|
|
108
102
|
menu = Gtk::Menu.new
|
109
|
-
menu.append(timelog_new)
|
110
|
-
menu.append(overview)
|
111
103
|
menu.append(worktime_overview)
|
112
104
|
menu.append(Gtk::SeparatorMenuItem.new)
|
113
105
|
menu.append(pref)
|
@@ -158,19 +150,11 @@ class Openall_time_applet::Gui::Trayicon
|
|
158
150
|
end
|
159
151
|
|
160
152
|
def on_statusicon_leftclick(*args)
|
161
|
-
@args[:oata].
|
153
|
+
@args[:oata].show_main
|
162
154
|
end
|
163
155
|
|
164
156
|
def on_preferences_activate(*args)
|
165
|
-
@args[:oata].
|
166
|
-
end
|
167
|
-
|
168
|
-
def on_timelogNew_activate(*args)
|
169
|
-
@args[:oata].show_timelog_new
|
170
|
-
end
|
171
|
-
|
172
|
-
def on_overview_activate(*args)
|
173
|
-
@args[:oata].show_overview
|
157
|
+
@args[:oata].show_main
|
174
158
|
end
|
175
159
|
|
176
160
|
def on_worktimeOverview_activate(*args)
|
data/gui/win_main.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
class Openall_time_applet::Gui::Win_main
|
2
|
+
attr_reader :args, :gui
|
3
|
+
|
4
|
+
def initialize(args)
|
5
|
+
@args = args
|
6
|
+
|
7
|
+
@gui = Gtk::Builder.new.add("../glade/win_main.glade")
|
8
|
+
@gui.translate
|
9
|
+
@gui.connect_signals{|h| method(h)}
|
10
|
+
|
11
|
+
|
12
|
+
#Generate list-store containing tasks for the task-column.
|
13
|
+
task_ls = Gtk::ListStore.new(String, String)
|
14
|
+
iter = task_ls.append
|
15
|
+
iter[0] = _("None")
|
16
|
+
iter[1] = 0.to_s
|
17
|
+
|
18
|
+
tasks = [_("Choose:")]
|
19
|
+
@args[:oata].ob.list(:Task, {"orderby" => "title"}) do |task|
|
20
|
+
iter = task_ls.append
|
21
|
+
iter[0] = task[:title]
|
22
|
+
iter[1] = task.id.to_s
|
23
|
+
tasks << task
|
24
|
+
end
|
25
|
+
|
26
|
+
#Add the tasks to the combo-box.
|
27
|
+
@gui["cbTask"].init(tasks)
|
28
|
+
|
29
|
+
init_data = @gui["tvTimelogs"].init([
|
30
|
+
_("ID"),
|
31
|
+
_("Description"),
|
32
|
+
_("Timestamp"),
|
33
|
+
_("Time"),
|
34
|
+
_("Transport"),
|
35
|
+
_("Length"),
|
36
|
+
_("Transport descr."),
|
37
|
+
_("Transport costs"),
|
38
|
+
{
|
39
|
+
:title => _("Fixed travel"),
|
40
|
+
:type => :toggle
|
41
|
+
},
|
42
|
+
{
|
43
|
+
:title => _("Int. work"),
|
44
|
+
:type => :toggle
|
45
|
+
},
|
46
|
+
{
|
47
|
+
:title => _("Sync?"),
|
48
|
+
:type => :toggle
|
49
|
+
},
|
50
|
+
{
|
51
|
+
:title => _("Task"),
|
52
|
+
:type => :combo,
|
53
|
+
:model => task_ls,
|
54
|
+
:has_entry => false
|
55
|
+
}
|
56
|
+
])
|
57
|
+
|
58
|
+
Knj::Gtk2::Tv.editable_text_renderers_to_model(
|
59
|
+
:ob => @args[:oata].ob,
|
60
|
+
:tv => @gui["tvTimelogs"],
|
61
|
+
:model_class => :Timelog,
|
62
|
+
:renderers => init_data[:renderers],
|
63
|
+
:change_before => proc{ @dont_reload = true },
|
64
|
+
:change_after => proc{ @dont_reload = false },
|
65
|
+
:cols => {
|
66
|
+
1 => :descr,
|
67
|
+
2 => {:col => :timestamp, :type => :datetime},
|
68
|
+
3 => {:col => :time, :type => :time_as_sec},
|
69
|
+
4 => {:col => :time_transport, :type => :time_as_sec},
|
70
|
+
5 => {:col => :transportlength, :type => :int},
|
71
|
+
6 => {:col => :transportdescription},
|
72
|
+
7 => {:col => :transportcosts, :type => :human_number, :decimals => 2},
|
73
|
+
8 => {:col => :travelfixed},
|
74
|
+
9 => {:col => :workinternal},
|
75
|
+
10 => {:col => :sync_need},
|
76
|
+
11 => {
|
77
|
+
:col => :task_id,
|
78
|
+
:value_callback => lambda{ |data|
|
79
|
+
task = @args[:oata].ob.get_by(:Task, {"title" => data[:value]})
|
80
|
+
|
81
|
+
if !task
|
82
|
+
return 0
|
83
|
+
else
|
84
|
+
return task.id
|
85
|
+
end
|
86
|
+
},
|
87
|
+
:value_set_callback => proc{ |data| data[:model].task_name }
|
88
|
+
}
|
89
|
+
}
|
90
|
+
)
|
91
|
+
|
92
|
+
@gui["tvTimelogs"].columns[0].visible = false
|
93
|
+
self.reload_timelogs
|
94
|
+
|
95
|
+
#Reload the treeview if something happened to a timelog.
|
96
|
+
@reload_id = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["add", "update", "delete"], &self.method(:reload_timelogs))
|
97
|
+
|
98
|
+
@gui["window"].show_all
|
99
|
+
end
|
100
|
+
|
101
|
+
def dont_reload
|
102
|
+
@dont_reload = true
|
103
|
+
begin
|
104
|
+
yield
|
105
|
+
ensure
|
106
|
+
@dont_reload = false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def reload_timelogs
|
111
|
+
return nil if @dont_reload or @gui["tvTimelogs"].destroyed?
|
112
|
+
@gui["tvTimelogs"].model.clear
|
113
|
+
@args[:oata].ob.list(:Timelog, {"orderby" => "id"}) do |timelog|
|
114
|
+
@gui["tvTimelogs"].append([
|
115
|
+
timelog.id,
|
116
|
+
timelog[:descr],
|
117
|
+
timelog.timestamp_str,
|
118
|
+
timelog.time_as_human,
|
119
|
+
timelog.time_transport_as_human,
|
120
|
+
Knj::Locales.number_out(timelog[:transportlength], 0),
|
121
|
+
timelog.transport_descr_short,
|
122
|
+
Knj::Locales.number_out(timelog[:transportcosts], 2),
|
123
|
+
Knj::Strings.yn_str(timelog[:travelfixed], true, false),
|
124
|
+
Knj::Strings.yn_str(timelog[:workinternal], true, false),
|
125
|
+
Knj::Strings.yn_str(timelog[:sync_need], true, false),
|
126
|
+
timelog.task_name
|
127
|
+
])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def on_tvTimelogs_row_activated(*args)
|
132
|
+
row = @gui["tvTimelogs"].sel
|
133
|
+
return nil if !row
|
134
|
+
|
135
|
+
timelog = @args[:oata].ob.get(:Timelog, row[0])
|
136
|
+
win_timelog_edit = @args[:oata].show_timelog_edit(timelog)
|
137
|
+
win_timelog_edit.gui["window"].modal = @gui["window"]
|
138
|
+
win_timelog_edit.gui["window"].transient_for = @gui["window"]
|
139
|
+
end
|
140
|
+
|
141
|
+
def on_imiQuit_activate
|
142
|
+
@gui["window"].destroy
|
143
|
+
end
|
144
|
+
|
145
|
+
def on_imiPreferences_activate
|
146
|
+
@args[:oata].show_preferences
|
147
|
+
end
|
148
|
+
|
149
|
+
def on_imiWeekview_activate
|
150
|
+
@args[:oata].show_worktime_overview
|
151
|
+
end
|
152
|
+
|
153
|
+
def on_window_destroy
|
154
|
+
#Unconnect reload-event. Else it will crash on call to destroyed object. Also frees up various ressources.
|
155
|
+
@args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id)
|
156
|
+
end
|
157
|
+
|
158
|
+
def on_expOverview_activate(expander)
|
159
|
+
if !expander.expanded?
|
160
|
+
@gui["window"].set_size_request(-1, 480)
|
161
|
+
else
|
162
|
+
@gui["window"].set_size_request(-1, -1)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def on_btnSwitch_clicked
|
167
|
+
task = @gui["cbTask"].sel
|
168
|
+
if !task.is_a?(Knj::Datarow)
|
169
|
+
Knj::Gtk2.msgbox(_("Please choose a task."), "warning")
|
170
|
+
return nil
|
171
|
+
end
|
172
|
+
|
173
|
+
@timelog = @args[:oata].ob.add(:Timelog, {
|
174
|
+
:task_id => task.id,
|
175
|
+
:descr => @gui["txtDescr"].text
|
176
|
+
})
|
177
|
+
@args[:oata].timelog_active = @timelog
|
178
|
+
@gui["txtDescr"].text = ""
|
179
|
+
@gui["cbTask"].sel = _("Choose:")
|
180
|
+
end
|
181
|
+
|
182
|
+
def on_btnSync_clicked
|
183
|
+
@args[:oata].sync_static do
|
184
|
+
@args[:oata].sync
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def on_btnMinus_clicked
|
189
|
+
sel = @gui["tvTimelogs"].sel
|
190
|
+
tlog = @args[:oata].ob.get(:Timelog, sel[0]) if sel
|
191
|
+
|
192
|
+
if !sel or !tlog
|
193
|
+
Knj::Gtk2.msgbox(_("Please choose a timelog to delete."), "warning")
|
194
|
+
return nil
|
195
|
+
end
|
196
|
+
|
197
|
+
return nil if Knj::Gtk2.msgbox(_("Do you want to remove this timelog?"), "yesno") != "yes"
|
198
|
+
@args[:oata].ob.delete(tlog)
|
199
|
+
end
|
200
|
+
|
201
|
+
def on_btnPlus_clicked
|
202
|
+
@args[:oata].ob.add(:Timelog)
|
203
|
+
end
|
204
|
+
|
205
|
+
#Redirects 'enter'-events to 'switch'-click-event.
|
206
|
+
def on_txtDescr_activate(*args)
|
207
|
+
self.on_btnSwitch_clicked
|
208
|
+
end
|
209
|
+
end
|
data/gui/win_overview.rb
CHANGED
@@ -1,140 +1,5 @@
|
|
1
1
|
class Openall_time_applet::Gui::Win_overview
|
2
|
-
attr_reader :args, :gui
|
3
|
-
|
4
2
|
def initialize(args)
|
5
|
-
@args = args
|
6
3
|
|
7
|
-
@gui = Gtk::Builder.new.add("../glade/win_overview.glade")
|
8
|
-
@gui.translate
|
9
|
-
@gui.connect_signals{|h| method(h)}
|
10
|
-
|
11
|
-
|
12
|
-
#Generate list-store containing tasks for the task-column.
|
13
|
-
task_ls = Gtk::ListStore.new(String, String)
|
14
|
-
iter = task_ls.append
|
15
|
-
iter[0] = _("None")
|
16
|
-
iter[1] = 0.to_s
|
17
|
-
|
18
|
-
@args[:oata].ob.list(:Task, {"orderby" => "title"}) do |task|
|
19
|
-
iter = task_ls.append
|
20
|
-
iter[0] = task[:title]
|
21
|
-
iter[1] = task.id.to_s
|
22
|
-
end
|
23
|
-
|
24
|
-
init_data = @gui["tvTimelogs"].init([
|
25
|
-
_("ID"),
|
26
|
-
_("Description"),
|
27
|
-
_("Timestamp"),
|
28
|
-
_("Time"),
|
29
|
-
_("Transport"),
|
30
|
-
_("Length"),
|
31
|
-
_("Transport descr."),
|
32
|
-
_("Transport costs"),
|
33
|
-
{
|
34
|
-
:title => _("Fixed travel"),
|
35
|
-
:type => :toggle
|
36
|
-
},
|
37
|
-
{
|
38
|
-
:title => _("Int. work"),
|
39
|
-
:type => :toggle
|
40
|
-
},
|
41
|
-
{
|
42
|
-
:title => _("Sync?"),
|
43
|
-
:type => :toggle
|
44
|
-
},
|
45
|
-
{
|
46
|
-
:title => _("Task"),
|
47
|
-
:type => :combo,
|
48
|
-
:model => task_ls,
|
49
|
-
:has_entry => false
|
50
|
-
}
|
51
|
-
])
|
52
|
-
|
53
|
-
Knj::Gtk2::Tv.editable_text_renderers_to_model(
|
54
|
-
:ob => @args[:oata].ob,
|
55
|
-
:tv => @gui["tvTimelogs"],
|
56
|
-
:model_class => :Timelog,
|
57
|
-
:renderers => init_data[:renderers],
|
58
|
-
:change_before => proc{ @dont_reload = true },
|
59
|
-
:change_after => proc{ @dont_reload = false },
|
60
|
-
:cols => {
|
61
|
-
1 => :descr,
|
62
|
-
2 => {:col => :timestamp, :type => :datetime},
|
63
|
-
3 => {:col => :time, :type => :time_as_sec},
|
64
|
-
4 => {:col => :time_transport, :type => :time_as_sec},
|
65
|
-
5 => {:col => :transportlength, :type => :int},
|
66
|
-
6 => {:col => :transportdescription},
|
67
|
-
7 => {:col => :transportcosts, :type => :human_number, :decimals => 2},
|
68
|
-
8 => {:col => :travelfixed},
|
69
|
-
9 => {:col => :workinternal},
|
70
|
-
10 => {:col => :sync_need},
|
71
|
-
11 => {
|
72
|
-
:col => :task_id,
|
73
|
-
:value_callback => lambda{ |data|
|
74
|
-
task = @args[:oata].ob.get_by(:Task, {"title" => data[:value]})
|
75
|
-
|
76
|
-
if !task
|
77
|
-
return 0
|
78
|
-
else
|
79
|
-
return task.id
|
80
|
-
end
|
81
|
-
},
|
82
|
-
:value_set_callback => proc{ |data| data[:model].task_name }
|
83
|
-
}
|
84
|
-
}
|
85
|
-
)
|
86
|
-
|
87
|
-
@gui["tvTimelogs"].columns[0].visible = false
|
88
|
-
self.reload_timelogs
|
89
|
-
|
90
|
-
#Reload the treeview if something happened to a timelog.
|
91
|
-
@reload_id = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["add", "update", "delete"], &self.method(:reload_timelogs))
|
92
|
-
|
93
|
-
@gui["window"].show_all
|
94
|
-
end
|
95
|
-
|
96
|
-
def dont_reload
|
97
|
-
@dont_reload = true
|
98
|
-
begin
|
99
|
-
yield
|
100
|
-
ensure
|
101
|
-
@dont_reload = false
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def reload_timelogs
|
106
|
-
return nil if @dont_reload
|
107
|
-
@gui["tvTimelogs"].model.clear
|
108
|
-
@args[:oata].ob.list(:Timelog, {"orderby" => "id"}) do |timelog|
|
109
|
-
@gui["tvTimelogs"].append([
|
110
|
-
timelog.id,
|
111
|
-
timelog.descr_short,
|
112
|
-
timelog.timestamp_str,
|
113
|
-
timelog.time_as_human,
|
114
|
-
timelog.time_transport_as_human,
|
115
|
-
Knj::Locales.number_out(timelog[:transportlength], 0),
|
116
|
-
timelog.transport_descr_short,
|
117
|
-
Knj::Locales.number_out(timelog[:transportcosts], 2),
|
118
|
-
Knj::Strings.yn_str(timelog[:travelfixed], true, false),
|
119
|
-
Knj::Strings.yn_str(timelog[:workinternal], true, false),
|
120
|
-
Knj::Strings.yn_str(timelog[:sync_need], true, false),
|
121
|
-
timelog.task_name
|
122
|
-
])
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def on_tvTimelogs_row_activated(*args)
|
127
|
-
row = @gui["tvTimelogs"].sel
|
128
|
-
return nil if !row
|
129
|
-
|
130
|
-
timelog = @args[:oata].ob.get(:Timelog, row[0])
|
131
|
-
win_timelog_edit = @args[:oata].show_timelog_edit(timelog)
|
132
|
-
win_timelog_edit.gui["window"].modal = @gui["window"]
|
133
|
-
win_timelog_edit.gui["window"].transient_for = @gui["window"]
|
134
|
-
end
|
135
|
-
|
136
|
-
def on_window_destroy
|
137
|
-
#Unconnect reload-event. Else it will crash on call to destroyed object. Also frees up various ressources.
|
138
|
-
@args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id)
|
139
4
|
end
|
140
5
|
end
|
data/lib/openall_time_applet.rb
CHANGED
@@ -9,23 +9,10 @@ else
|
|
9
9
|
require "knjrbfw"
|
10
10
|
end
|
11
11
|
|
12
|
-
require "gtk2"
|
13
12
|
require "sqlite3"
|
14
13
|
require "gettext"
|
15
14
|
require "base64"
|
16
15
|
|
17
|
-
#For msgbox and translation of windows.
|
18
|
-
require "knj/gtk2"
|
19
|
-
|
20
|
-
#For easy initialization, getting and settings of values on comboboxes.
|
21
|
-
require "knj/gtk2_cb"
|
22
|
-
|
23
|
-
#For easy initialization, getting and settings of values on treeviews.
|
24
|
-
require "knj/gtk2_tv"
|
25
|
-
|
26
|
-
#For easy making status-windows with progressbar.
|
27
|
-
require "knj/gtk2_statuswindow"
|
28
|
-
|
29
16
|
#The base class of the applet. Spawns all windows, holds subclasses for models and gui, holds models objects and holds database-objects.
|
30
17
|
class Openall_time_applet
|
31
18
|
#Shortcut to start the application. Used by the Ubuntu-package.
|
@@ -68,17 +55,23 @@ class Openall_time_applet
|
|
68
55
|
end
|
69
56
|
|
70
57
|
#Various readable variables.
|
71
|
-
attr_reader :db, :ob, :ti, :timelog_active, :timelog_active_time
|
58
|
+
attr_reader :db, :debug, :ob, :ti, :timelog_active, :timelog_active_time
|
72
59
|
attr_accessor :reminder_next
|
73
60
|
|
74
61
|
#Config controlling paths and more.
|
75
62
|
CONFIG = {
|
76
63
|
:settings_path => "#{Knj::Os.homedir}/.openall_time_applet",
|
77
|
-
:
|
64
|
+
:run_path => "#{Knj::Os.homedir}/.openall_time_applet/run",
|
65
|
+
:db_path => "#{Knj::Os.homedir}/.openall_time_applet/openall_time_applet.sqlite3",
|
66
|
+
:sock_path => "#{Knj::Os.homedir}/.openall_time_applet/sock"
|
78
67
|
}
|
79
68
|
|
80
69
|
#Initializes config-dir and database.
|
81
70
|
def initialize(args = {})
|
71
|
+
self.check_runfile_and_cmds
|
72
|
+
self.require_gtk2
|
73
|
+
|
74
|
+
@debug = args[:debug]
|
82
75
|
Dir.mkdir(CONFIG[:settings_path]) if !File.exists?(CONFIG[:settings_path])
|
83
76
|
|
84
77
|
#Database-connection.
|
@@ -118,6 +111,59 @@ class Openall_time_applet
|
|
118
111
|
|
119
112
|
#Start reminder.
|
120
113
|
self.reminding
|
114
|
+
|
115
|
+
#Start unix-socket that listens for remote control.
|
116
|
+
@unix_socket = Openall_time_applet::Unix_socket.new(:oata => self)
|
117
|
+
end
|
118
|
+
|
119
|
+
#Creates a runfile or sending a command to the running OpenAll-Time-Applet through the Unix-socket.
|
120
|
+
def check_runfile_and_cmds
|
121
|
+
if File.exists?(CONFIG[:run_path])
|
122
|
+
cmd = nil
|
123
|
+
ARGV.each do |val|
|
124
|
+
if match = val.match(/^--cmd=(.+)$/)
|
125
|
+
cmd = match[1]
|
126
|
+
break
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
if cmd
|
131
|
+
print "Executing command through sock: #{cmd}\n"
|
132
|
+
|
133
|
+
require "socket"
|
134
|
+
UNIXSocket.open(CONFIG[:sock_path]) do |sock|
|
135
|
+
sock.puts(cmd)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
puts "Already running"
|
140
|
+
exit
|
141
|
+
end
|
142
|
+
|
143
|
+
File.open(CONFIG[:run_path], "w") do |fp|
|
144
|
+
fp.write(Process.pid)
|
145
|
+
end
|
146
|
+
|
147
|
+
Kernel.at_exit do
|
148
|
+
File.unlink(CONFIG[:run_path]) if File.exists?(CONFIG[:run_path]) and File.read(CONFIG[:run_path]).to_i == Process.pid
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
#Requires the heavy Gtk-stuff.
|
153
|
+
def require_gtk2
|
154
|
+
require "gtk2"
|
155
|
+
|
156
|
+
#For msgbox and translation of windows.
|
157
|
+
require "knj/gtk2"
|
158
|
+
|
159
|
+
#For easy initialization, getting and settings of values on comboboxes.
|
160
|
+
require "knj/gtk2_cb"
|
161
|
+
|
162
|
+
#For easy initialization, getting and settings of values on treeviews.
|
163
|
+
require "knj/gtk2_tv"
|
164
|
+
|
165
|
+
#For easy making status-windows with progressbar.
|
166
|
+
require "knj/gtk2_statuswindow"
|
121
167
|
end
|
122
168
|
|
123
169
|
#Updates the database according to the db-schema.
|
@@ -179,6 +225,12 @@ class Openall_time_applet
|
|
179
225
|
@ti = Openall_time_applet::Gui::Trayicon.new(:oata => self)
|
180
226
|
end
|
181
227
|
|
228
|
+
def show_main
|
229
|
+
Knj::Gtk2::Window.unique!("main") do
|
230
|
+
Openall_time_applet::Gui::Win_main.new(:oata => self)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
182
234
|
#Spawns the preference-window.
|
183
235
|
def show_preferences
|
184
236
|
Knj::Gtk2::Window.unique!("preferences") do
|
@@ -230,10 +282,10 @@ class Openall_time_applet
|
|
230
282
|
end
|
231
283
|
|
232
284
|
#Synchronizes organisations, tasks and worktimes.
|
233
|
-
def sync_static
|
285
|
+
def sync_static(args = nil)
|
234
286
|
sw = Knj::Gtk2::StatusWindow.new
|
235
287
|
|
236
|
-
Knj::Thread.new do
|
288
|
+
return Knj::Thread.new do
|
237
289
|
begin
|
238
290
|
sw.label = _("Updating organisation-cache.")
|
239
291
|
self.update_organisation_cache
|
@@ -247,7 +299,8 @@ class Openall_time_applet
|
|
247
299
|
self.update_worktime_cache
|
248
300
|
sw.percent = 1
|
249
301
|
|
250
|
-
sleep 1
|
302
|
+
sleep 1 if !block_given?
|
303
|
+
yield if block_given?
|
251
304
|
rescue => e
|
252
305
|
Knj::Gtk2.msgbox("msg" => Knj::Errors.error_str(e), "type" => "warning", "title" => _("Error"), "run" => false)
|
253
306
|
ensure
|
data/openall_time_applet.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{openall_time_applet}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.12"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kasper Johansen"]
|
12
|
-
s.date = %q{2012-06-
|
12
|
+
s.date = %q{2012-06-18}
|
13
13
|
s.default_executable = %q{openall_time_applet.rb}
|
14
14
|
s.description = %q{Off-line time-tracking for OpenAll with syncing when online.}
|
15
15
|
s.email = %q{k@spernj.org}
|
@@ -30,17 +30,20 @@ Gem::Specification.new do |s|
|
|
30
30
|
"bin/openall_time_applet.rb",
|
31
31
|
"classes/connection.rb",
|
32
32
|
"classes/translations.rb",
|
33
|
+
"classes/unix_socket.rb",
|
33
34
|
"conf/db_schema.rb",
|
34
35
|
"gfx/icon_time_black.png",
|
35
36
|
"gfx/icon_time_green_casalogic.png",
|
36
37
|
"gfx/icon_time_orig.png",
|
37
38
|
"gfx/icon_time_white.png",
|
39
|
+
"glade/win_main.glade",
|
38
40
|
"glade/win_overview.glade",
|
39
41
|
"glade/win_preferences.glade",
|
40
42
|
"glade/win_sync_overview.glade",
|
41
43
|
"glade/win_timelog_edit.glade",
|
42
44
|
"glade/win_worktime_overview.glade",
|
43
45
|
"gui/trayicon.rb",
|
46
|
+
"gui/win_main.rb",
|
44
47
|
"gui/win_overview.rb",
|
45
48
|
"gui/win_preferences.rb",
|
46
49
|
"gui/win_sync_overview.rb",
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: openall_time_applet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.12
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Kasper Johansen
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-06-
|
13
|
+
date: 2012-06-18 00:00:00 +02:00
|
14
14
|
default_executable: openall_time_applet.rb
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -155,17 +155,20 @@ files:
|
|
155
155
|
- bin/openall_time_applet.rb
|
156
156
|
- classes/connection.rb
|
157
157
|
- classes/translations.rb
|
158
|
+
- classes/unix_socket.rb
|
158
159
|
- conf/db_schema.rb
|
159
160
|
- gfx/icon_time_black.png
|
160
161
|
- gfx/icon_time_green_casalogic.png
|
161
162
|
- gfx/icon_time_orig.png
|
162
163
|
- gfx/icon_time_white.png
|
164
|
+
- glade/win_main.glade
|
163
165
|
- glade/win_overview.glade
|
164
166
|
- glade/win_preferences.glade
|
165
167
|
- glade/win_sync_overview.glade
|
166
168
|
- glade/win_timelog_edit.glade
|
167
169
|
- glade/win_worktime_overview.glade
|
168
170
|
- gui/trayicon.rb
|
171
|
+
- gui/win_main.rb
|
169
172
|
- gui/win_overview.rb
|
170
173
|
- gui/win_preferences.rb
|
171
174
|
- gui/win_sync_overview.rb
|
@@ -195,7 +198,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
195
198
|
requirements:
|
196
199
|
- - ">="
|
197
200
|
- !ruby/object:Gem::Version
|
198
|
-
hash:
|
201
|
+
hash: 2134031448071051365
|
199
202
|
segments:
|
200
203
|
- 0
|
201
204
|
version: "0"
|