1 /* gobby - A GTKmm driven libobby client
2 * Copyright (C) 2005 0x539 dev group
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <stdexcept>
20 #include <gtkmm/stock.h>
21 #include <gtkmm/main.h>
22 #include "common.hpp"
23 #include "progressdialog.hpp"
24
Thread(Glib::Dispatcher & done_disp,Glib::Dispatcher & work_disp)25 Gobby::ProgressDialog::Thread::Thread(Glib::Dispatcher& done_disp,
26 Glib::Dispatcher& work_disp):
27 m_thread(NULL), m_disp_done(done_disp),
28 m_disp_work(work_disp), m_quit(false)
29 {
30 }
31
~Thread()32 Gobby::ProgressDialog::Thread::~Thread()
33 {
34 }
35
launch(const entry_slot & entry_func)36 void Gobby::ProgressDialog::Thread::launch(const entry_slot& entry_func)
37 {
38 // TODO: sigc::bind to on_thread_entry
39 m_entry_func = entry_func;
40
41 lock();
42 m_thread = Glib::Thread::create(
43 sigc::mem_fun(*this, &Thread::on_thread_entry),
44 false
45 );
46 unlock();
47 }
48
quit()49 void Gobby::ProgressDialog::Thread::quit()
50 {
51 lock();
52 m_quit = true;
53 unlock();
54 }
55
quitting()56 bool Gobby::ProgressDialog::Thread::quitting()
57 {
58 return m_quit;
59 }
60
assert_running() const61 void Gobby::ProgressDialog::Thread::assert_running() const
62 {
63 if(Glib::Thread::self() != m_thread)
64 {
65 throw std::logic_error(
66 "Gobby::ProgressDialog::Thread::assert_running"
67 );
68 }
69 }
70
lock()71 void Gobby::ProgressDialog::Thread::lock()
72 {
73 m_mutex.lock();
74 }
75
unlock()76 void Gobby::ProgressDialog::Thread::unlock()
77 {
78 m_mutex.unlock();
79 }
80
done_event()81 Glib::Dispatcher& Gobby::ProgressDialog::Thread::done_event()
82 {
83 return m_disp_done;
84 }
85
work_event()86 Glib::Dispatcher& Gobby::ProgressDialog::Thread::work_event()
87 {
88 return m_disp_work;
89 }
90
on_thread_entry()91 void Gobby::ProgressDialog::Thread::on_thread_entry()
92 {
93 try
94 {
95 lock();
96 unlock();
97
98 // Call working function
99 m_entry_func(*this);
100 }
101 catch(Glib::Thread::Exit& e)
102 {
103 // No need to throw e futher, the thread exits anyhow
104 }
105
106 // If the caller told us to quit we remove us silently, otherwise we
107 // tell the caller that we have done what we were supposed to.
108 if(m_quit)
109 {
110 // TODO: The dispatcher's destructor writes an odd message to
111 // stdout, how to supress it?
112 // - armin, 10-04-2005
113 delete this;
114 }
115 else
116 {
117 m_thread = NULL; // ???
118 m_disp_done.emit();
119 }
120 }
121
ProgressDialog(const Glib::ustring & title,Gtk::Window & parent)122 Gobby::ProgressDialog::ProgressDialog(const Glib::ustring& title,
123 Gtk::Window& parent)
124 : Gtk::Dialog(title, parent, true, true),
125 m_lbl_state("", Gtk::ALIGN_CENTER), m_thread(NULL),
126 m_parent(parent)
127 {
128 get_vbox()->pack_start(m_lbl_state, Gtk::PACK_SHRINK);
129 get_vbox()->pack_start(m_progress, Gtk::PACK_SHRINK);
130 get_vbox()->set_spacing(5);
131
132 add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
133
134 set_border_width(10);
135 set_resizable(false);
136
137 Glib::signal_idle().connect(
138 sigc::mem_fun(*this, &ProgressDialog::on_idle) );
139
140 show_all();
141 }
142
~ProgressDialog()143 Gobby::ProgressDialog::~ProgressDialog()
144 {
145 // Tell the thread to terminate itself when the dialog has been closed
146 // before the thread has finsihed.
147 if(m_thread != NULL)
148 {
149 m_thread->quit();
150
151 m_conn_done.disconnect();
152 m_conn_work.disconnect();
153 }
154 }
155
set_status_text(const Glib::ustring & text)156 void Gobby::ProgressDialog::set_status_text(const Glib::ustring& text)
157 {
158 m_lbl_state.set_text(text);
159 }
160
set_progress_fraction(double progress)161 void Gobby::ProgressDialog::set_progress_fraction(double progress)
162 {
163 m_progress.set_fraction(progress);
164 }
165
progress_pulse()166 void Gobby::ProgressDialog::progress_pulse()
167 {
168 m_progress.pulse();
169 }
170
work(Thread & thread)171 void Gobby::ProgressDialog::work(Thread& thread)
172 {
173 // Make sure that the calling thread is the worker thread
174 thread.assert_running();
175 thread.work_event().emit();
176 }
177
lock(Thread & thread)178 void Gobby::ProgressDialog::lock(Thread& thread)
179 {
180 // Make sure that the calling thread is the worker thread, the main
181 // thread does not have to call this function because it is used by
182 // the worker thread before querieng data from the dialog. The only
183 // time the main thread is locking the mutex is when the user closes
184 // the dialog.
185 thread.assert_running();
186
187 thread.lock();
188 if(thread.quitting() )
189 {
190 // Exit from the thread if the dialog has been destroys, the
191 // user is no more interested in what we have done (otherwise,
192 // he would not have closed the dialog).
193 thread.unlock();
194 throw Glib::Thread::Exit();
195 }
196 }
197
unlock(Thread & thread)198 void Gobby::ProgressDialog::unlock(Thread& thread)
199 {
200 // Make sure that the calling thread is the worker thread.
201 thread.assert_running();
202 thread.unlock();
203 }
204
on_work()205 void Gobby::ProgressDialog::on_work()
206 {
207 }
208
on_done()209 void Gobby::ProgressDialog::on_done()
210 {
211 m_conn_done.disconnect();
212 m_conn_work.disconnect();
213
214 // Delete thread on termination
215 delete m_thread;
216 m_thread = NULL;
217 }
218
on_response(int response_id)219 void Gobby::ProgressDialog::on_response(int response_id)
220 {
221 // Tell the thread to terminate itself when the dialog has been closed
222 // before the thread has finsihed
223 /* if(m_thread != NULL)
224 {
225 m_thread->quit();
226 m_thread = NULL;
227 }*/
228
229 // Response
230 Gtk::Dialog::on_response(response_id);
231 }
232
on_idle()233 bool Gobby::ProgressDialog::on_idle()
234 {
235 // Create the worker thread
236 m_thread = new Thread(m_disp_done, m_disp_work);
237
238 // Connect dispatchers
239 m_conn_done = m_thread->done_event().connect(
240 sigc::mem_fun(*this, &ProgressDialog::on_done) );
241 m_conn_work = m_thread->work_event().connect(
242 sigc::mem_fun(*this, &ProgressDialog::on_work) );
243
244 // Launch the thread
245 m_thread->launch(sigc::mem_fun(*this, &ProgressDialog::on_thread) );
246 return false;
247 }
248
249