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