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