better fix for #5785
[mevemon] / package / src / ui / fremantle / gui.py
1 #
2 # mEveMon - A character monitor for EVE Online
3 # Copyright (c) 2010  Ryan and Danny Campbell, and the mEveMon Team
4 #
5 # mEveMon is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # mEveMon is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 import sys, time
20
21 import gtk
22 import hildon
23 import gobject
24
25 import glib
26
27 from ui import models
28 import validation
29 import util
30
31 class BaseUI():
32     menu_items = ("Settings", "About", "Refresh")
33
34     def create_menu(self, window):
35         menu = hildon.AppMenu()
36
37         for command in self.menu_items:
38             # Create menu entries
39             button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
40             button.set_label(command)
41
42             if command == "About":
43                 button.connect("clicked", self.about_clicked)
44             elif command == "Settings":
45                 button.connect("clicked", self.settings_clicked, window)
46             elif command == "Refresh":
47                 button.connect("clicked", self.refresh_clicked)
48             else:
49                 assert False, command
50
51             # Add entry to the view menu
52             menu.append(button)
53
54         menu.show_all()
55
56         return menu
57
58     def settings_clicked(self, button, window):
59
60         RESPONSE_NEW, RESPONSE_EDIT, RESPONSE_DELETE = range(3)
61
62         dialog = gtk.Dialog()
63         dialog.set_transient_for(window)
64         dialog.set_title("Settings")
65
66         pannable_area = hildon.PannableArea()
67
68         dialog_vbox = dialog.vbox
69
70         vbox = gtk.VBox(False, 1)
71
72         self.accounts_model = models.AccountsModel(self.controller)
73         
74         accounts_treeview = hildon.GtkTreeView(gtk.HILDON_UI_MODE_NORMAL)
75         accounts_treeview.set_model(self.accounts_model)
76         accounts_treeview.set_headers_visible(True)
77         self.add_columns_to_accounts(accounts_treeview)
78         vbox.pack_start(accounts_treeview, False, False, 1)
79
80         # all stock responses are negative, so we can use any positive value
81         new_button = dialog.add_button("New", RESPONSE_NEW)
82         #TODO: get edit button working
83         #edit_button = dialog.add_button("Edit", RESPONSE_EDIT)
84         delete_button = dialog.add_button("Delete", RESPONSE_DELETE)
85         ok_button = dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
86         
87         pannable_area.add_with_viewport(vbox)
88         
89         dialog_vbox.pack_start(pannable_area, True, True, 1)
90       
91       
92         dialog.show_all()
93
94         result = dialog.run()
95
96         while(result != gtk.RESPONSE_DELETE_EVENT):
97             if result == RESPONSE_NEW:
98                 self.new_account_clicked(window)
99             #elif result == RESPONSE_EDIT:
100             #    # get the selected treeview item and pop up the account_box
101             #    self.edit_account(accounts_treeview)
102             elif result == RESPONSE_DELETE:
103                 # get the selected treeview item, and delete the gconf keys
104                 self.delete_account(accounts_treeview)
105             elif result == gtk.RESPONSE_OK:
106                 self.char_model.get_characters()
107                 break
108         
109             result = dialog.run()
110
111         dialog.destroy()
112
113
114
115     def get_selected_item(self, treeview, column):
116         selection = treeview.get_selection()
117         model, miter = selection.get_selected()
118
119         value = model.get_value(miter, column)
120
121         return value
122
123     def edit_account(self, treeview):
124         uid = self.get_selected_item(treeview, 0)
125         # pop up the account dialog
126
127         self.accounts_model.get_accounts()
128
129     def delete_account(self, treeview):
130         uid = self.get_selected_item(treeview, 0)
131         self.controller.remove_account(uid)
132         # refresh model
133         self.accounts_model.get_accounts()
134
135
136     def add_columns_to_accounts(self, treeview):
137         #Column 0 for the treeview
138         renderer = gtk.CellRendererText()
139         column = gtk.TreeViewColumn('User ID', renderer, 
140                 text=models.AccountsModel.C_UID)
141         column.set_property("expand", True)
142         treeview.append_column(column)
143
144         #Column 2 (characters) for the treeview
145         column = gtk.TreeViewColumn('Characters', renderer, 
146                 markup=models.AccountsModel.C_CHARS)
147         column.set_property("expand", True)
148         treeview.append_column(column)
149
150
151     def new_account_clicked(self, window):
152         dialog = gtk.Dialog()
153     
154         #get the vbox to pack all the settings into
155         vbox = dialog.vbox
156     
157         dialog.set_transient_for(window)
158         dialog.set_title("New Account")
159
160         uidLabel = gtk.Label("User ID:")
161         uidLabel.set_justify(gtk.JUSTIFY_LEFT)
162         vbox.add(uidLabel)
163         
164         uidEntry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
165         uidEntry.set_placeholder("User ID")
166         uidEntry.set_property('is_focus', False)
167         
168         vbox.add(uidEntry)
169
170         apiLabel = gtk.Label("API key:")
171         apiLabel.set_justify(gtk.JUSTIFY_LEFT)
172         vbox.add(apiLabel)
173         
174         apiEntry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
175         apiEntry.set_placeholder("API Key")
176         apiEntry.set_property('is_focus', False)
177
178         vbox.add(apiEntry)
179        
180         ok_button = dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
181
182         dialog.show_all()
183         result = dialog.run()
184         
185         valid_credentials = False
186
187         while not valid_credentials:
188             if result == gtk.RESPONSE_OK:
189                 uid = uidEntry.get_text()
190                 api_key = apiEntry.get_text()
191             
192                 try:
193                     validation.uid(uid)
194                     validation.api_key(api_key)
195                 except validation.ValidationError, e:
196                     self.report_error(e.message)
197                     result = dialog.run()
198                 else:
199                     valid_credentials = True
200                     self.controller.add_account(uid, api_key)
201                     self.accounts_model.get_accounts()
202             else:
203                 break
204
205         dialog.destroy()
206
207
208     def report_error(self, error):
209         hildon.hildon_banner_show_information(self.win, '', error)
210     
211     def about_clicked(self, button):
212         dialog = gtk.AboutDialog()
213         dialog.set_website(self.controller.about_website)
214         dialog.set_website_label(self.controller.about_website)
215         dialog.set_name(self.controller.about_name)
216         dialog.set_authors(self.controller.about_authors)
217         dialog.set_comments(self.controller.about_text)
218         dialog.set_version(self.controller.app_version)
219         dialog.run()
220         dialog.destroy()
221
222     def add_label(self, text, box, markup=True, align="left", padding=1):
223         label = gtk.Label(text)
224         if markup:
225             label.set_use_markup(True)
226         if align == "left":
227             label.set_alignment(0, 0.5)
228
229         box.pack_start(label, False, False, padding)
230
231         return label
232
233 class mEveMonUI(BaseUI):
234
235     def __init__(self, controller):
236         self.controller = controller
237         gtk.set_application_name("mEveMon")
238
239         # create the main window
240         self.win = hildon.StackableWindow()
241         self.win.connect("destroy", self.controller.quit)
242         self.win.show_all()
243         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
244
245         # Create menu
246         menu = self.create_menu(self.win)
247         # Attach menu to the window
248         self.win.set_app_menu(menu)
249
250         pannable_area = hildon.PannableArea()
251
252         character_win = CharacterSheetUI(self.controller)
253
254         # gtk.HILDON_UI_MODE_NORMAL -> not selection in the treeview
255         # gtk.HILDON_UI_MODE_EDIT -> selection in the treeview
256         treeview = hildon.GtkTreeView(gtk.HILDON_UI_MODE_NORMAL)
257         treeview.connect('row-activated', character_win.build_window)
258
259         self.char_model = models.CharacterListModel(self.controller)
260         treeview.set_model(self.char_model)
261         self.add_columns_to_treeview(treeview)
262
263         pannable_area.add(treeview)
264
265         self.win.add(pannable_area);
266         
267         self.win.show_all()
268
269         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
270
271     def add_columns_to_treeview(self, treeview):
272         #Column 0 for the treeview
273         renderer = gtk.CellRendererPixbuf()
274         column = gtk.TreeViewColumn()
275         column.pack_start(renderer, True)
276         column.add_attribute(renderer, "pixbuf", 
277                 models.CharacterListModel.C_PORTRAIT)
278         treeview.append_column(column)
279
280         #Column 1 for the treeview
281         renderer = gtk.CellRendererText()
282         column = gtk.TreeViewColumn('Character Name', renderer, 
283                 text=models.CharacterListModel.C_NAME)
284         column.set_property("expand", True)
285         treeview.append_column(column)
286  
287     def refresh_clicked(self, button):
288         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
289         self.char_model.get_characters()
290         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
291     
292
293 class CharacterSheetUI(BaseUI):
294     UPDATE_INTERVAL = 1
295
296     def __init__(self, controller):
297         self.controller = controller
298         self.sheet = None
299         self.char_id = None
300         self.skills_model = None
301
302
303     def build_window(self, treeview, path, view_column):
304         # TODO: this is a really long and ugly function, split it up somehow
305
306         self.win = hildon.StackableWindow()
307         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
308         self.win.show_all() 
309
310         # Create menu
311         # NOTE: we probably want a window-specific menu for this page, but the
312         # main appmenu works for now
313         menu = self.create_menu(self.win)
314         # Attach menu to the window
315         self.win.set_app_menu(menu)
316
317         pannable_area = hildon.PannableArea()
318
319         model = treeview.get_model()
320         miter = model.get_iter(path)
321         
322         # column 0 is the portrait, column 1 is name
323         char_name = model.get_value(miter, 1)
324         self.uid = model.get_value(miter, 2)
325         self.char_id = self.controller.char_name2id(char_name)
326
327         self.sheet = self.controller.get_char_sheet(self.uid, self.char_id)
328
329         self.win.set_title(char_name)
330
331
332         hbox = gtk.HBox(False, 0)
333         info_vbox = gtk.VBox(False, 0)
334
335         portrait = gtk.Image()
336         portrait.set_from_file(self.controller.get_portrait(char_name, 256))
337         portrait.show()
338
339         hbox.pack_start(portrait, False, False, 10)
340         hbox.pack_start(info_vbox, False, False, 5)
341
342         vbox = gtk.VBox(False, 0)
343         pannable_area.add_with_viewport(vbox)
344
345         vbox.pack_start(hbox, False, False, 0)
346
347         self.fill_info(info_vbox)
348         self.fill_stats(info_vbox)
349
350         separator = gtk.HSeparator()
351         vbox.pack_start(separator, False, False, 5)
352         separator.show()
353
354         
355         self.add_label("<big>Skill in Training:</big>", vbox, align="normal")
356
357         self.display_skill_in_training(vbox)
358
359         separator = gtk.HSeparator()
360         vbox.pack_start(separator, False, False, 0)
361         separator.show()
362         
363         self.add_label("<big>Skills:</big>", vbox, align="normal")
364
365         skills_treeview = hildon.GtkTreeView(gtk.HILDON_UI_MODE_NORMAL)
366         self.skills_model = models.CharacterSkillsModel(self.controller, self.char_id)
367         skills_treeview.set_model(self.skills_model)
368         self.add_columns_to_skills_view(skills_treeview)
369         vbox.pack_start(skills_treeview, False, False, 1)
370
371         self.win.add(pannable_area)
372         self.win.show_all()
373
374         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
375         
376         # if we start the timer too early, get_is_topmost() returns False
377         self.timer = glib.timeout_add_seconds(self.UPDATE_INTERVAL, self.update_live_sp)
378
379         self.win.connect("destroy", self.back)
380
381     def back(self, widget):
382         glib.source_remove(self.timer)
383         gtk.Window.destroy(self.win)
384
385
386     def display_skill_in_training(self, vbox):
387         skill = self.controller.get_skill_in_training(self.uid, self.char_id)
388         
389         if skill.skillInTraining:
390
391             skilltree = self.controller.get_skill_tree()
392             
393             # I'm assuming that we will always find a skill with the skill ID
394             for group in skilltree.skillGroups:
395                 found_skill = group.skills.Get(skill.trainingTypeID, False)
396                 if found_skill:
397                     skill_name = found_skill.typeName
398                     break
399                 
400             self.add_label("%s <small>(Level %d)</small>" % (skill_name, skill.trainingToLevel),
401                     vbox, align="normal")
402             self.add_label("<small>start time: %s\t\tend time: %s</small>" 
403                     %(time.ctime(skill.trainingStartTime),
404                 time.ctime(skill.trainingEndTime)), vbox, align="normal")
405
406             progressbar = gtk.ProgressBar()
407             fraction_completed = (time.time() - skill.trainingStartTime) / \
408                     (skill.trainingEndTime - skill.trainingStartTime)
409
410             progressbar.set_fraction(fraction_completed)
411             align = gtk.Alignment(0.5, 0.5, 0.5, 0)
412             vbox.pack_start(align, False, False, 5)
413             align.show()
414             align.add(progressbar)
415             progressbar.show()
416         else:
417             self.add_label("<small>No skills are currently being trained</small>",
418                     vbox, align="normal")
419
420
421
422     def fill_info(self, box):
423         self.add_label("<big><big>%s</big></big>" % self.sheet.name, box)
424         self.add_label("<small>%s %s %s</small>" % (self.sheet.gender, 
425             self.sheet.race, self.sheet.bloodLine), box)
426         self.add_label("", box, markup=False)
427         self.add_label("<small><b>Corp:</b> %s</small>" % self.sheet.corporationName, box)
428         self.add_label("<small><b>Balance:</b> %s ISK</small>" % 
429                 util.comma(self.sheet.balance), box)
430
431         self.live_sp_val = self.controller.get_sp(self.uid, self.char_id)
432         self.live_sp = self.add_label("<small><b>Total SP:</b> %s</small>" %
433                 util.comma(int(self.live_sp_val)), box)
434         
435         self.spps = self.controller.get_spps(self.uid, self.char_id)[0]
436
437
438     def fill_stats(self, box):
439
440         atr = self.sheet.attributes
441
442         self.add_label("<small><b>I: </b>%d  <b>M: </b>%d  <b>C: </b>%d  " \
443                 "<b>P: </b>%d  <b>W: </b>%d</small>" % (atr.intelligence,
444                     atr.memory, atr.charisma, atr.perception, atr.willpower), box)
445
446
447     def add_columns_to_skills_view(self, treeview):
448         #Column 0 for the treeview
449         renderer = gtk.CellRendererText()
450         column = gtk.TreeViewColumn('Skill Name', renderer, 
451                 markup=models.CharacterSkillsModel.C_NAME)
452         column.set_property("expand", True)
453         treeview.append_column(column)
454         
455         #Column 1 for the treeview
456         column = gtk.TreeViewColumn('Rank', renderer, 
457                 markup=models.CharacterSkillsModel.C_RANK)
458         column.set_property("expand", True)
459         treeview.append_column(column)
460
461         #Column 2
462         column = gtk.TreeViewColumn('Points', renderer,
463                 markup=models.CharacterSkillsModel.C_SKILLPOINTS)
464         column.set_property("expand", True)
465         treeview.append_column(column)
466
467         #Column 3
468         column = gtk.TreeViewColumn('Level', renderer, 
469                 markup=models.CharacterSkillsModel.C_LEVEL)
470         column.set_property("expand", True)
471         treeview.append_column(column)
472
473
474     def refresh_clicked(self, button):
475         hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
476         self.skills_model.get_skills()
477         hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
478
479
480     def update_live_sp(self):
481         print 'tick'
482         self.live_sp_val = self.live_sp_val + self.spps * self.UPDATE_INTERVAL
483         self.live_sp.set_label("<small><b>Total SP:</b> %s</small>" %
484                                 util.comma(int(self.live_sp_val)))
485
486         return True
487
488
489 if __name__ == "__main__":
490     main()
491