a0b21317568bee5a703319e795a70e78aa4e0dad
[mevemon] / package / src / ui / diablo / 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 = gtk.Menu()
36
37         for command in self.menu_items:
38             # Create menu entries
39             button = gtk.MenuItem(command)
40
41             if command == "About":
42                 button.connect("activate", self.about_clicked)
43             elif command == "Settings":
44                 button.connect("activate", self.settings_clicked, window)
45             elif command == "Refresh":
46                 button.connect("activate", self.refresh_clicked, window)
47             else:
48                 assert False, command
49
50             # Add entry to the view menu
51             menu.append(button)
52
53         menu.show_all()
54
55         return menu
56
57     def settings_clicked(self, button, window):
58
59         RESPONSE_NEW, RESPONSE_EDIT, RESPONSE_DELETE = range(3)
60
61         dialog = gtk.Dialog()
62         dialog.set_transient_for(window)
63         dialog.set_title("Settings")
64
65
66
67         vbox = dialog.vbox
68
69         acctsLabel = gtk.Label("Accounts:")
70         acctsLabel.set_justify(gtk.JUSTIFY_LEFT)
71
72         vbox.pack_start(acctsLabel, False, False, 1)
73
74         self.accounts_model = models.AccountsModel(self.controller)
75
76         accounts_treeview = gtk.TreeView(model = self.accounts_model)
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         cancel_button = dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
87
88         #TODO: for some reason the scrollbar shows up in the middle of the
89         # dialog. Why?
90         #scrollbar = gtk.VScrollbar()
91         
92         dialog.show_all()
93
94         result = dialog.run()
95
96         while(result != gtk.RESPONSE_CANCEL):
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 = gtk.Entry()
165         uidEntry.set_property('is_focus', False)
166         
167         vbox.add(uidEntry)
168
169         apiLabel = gtk.Label("API key:")
170         apiLabel.set_justify(gtk.JUSTIFY_LEFT)
171         vbox.add(apiLabel)
172         
173         apiEntry = gtk.Entry()
174         apiEntry.set_property('is_focus', False)
175
176         vbox.add(apiEntry)
177        
178         ok_button = dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
179
180         dialog.show_all()
181         result = dialog.run()
182         
183         valid_credentials = False
184
185         while not valid_credentials:
186             if result == gtk.RESPONSE_OK:
187                 uid = uidEntry.get_text()
188                 api_key = apiEntry.get_text()
189             
190                 try:
191                     validation.uid(uid)
192                     validation.api_key(api_key)
193                 except validation.ValidationError, e:
194                     self.report_error(e.message)
195                     result = dialog.run()
196                 else:
197                     valid_credentials = True
198                     self.controller.add_account(uid, api_key)
199                     self.accounts_model.get_accounts()
200             else:
201                 break
202
203         dialog.destroy()
204
205
206     def report_error(self, error):
207         hildon.hildon_banner_show_information(self.win, '', error)
208     
209     def about_clicked(self, button):
210         dialog = gtk.AboutDialog()
211         dialog.set_website(self.controller.about_website)
212         dialog.set_website_label(self.controller.about_website)
213         dialog.set_name(self.controller.about_name)
214         dialog.set_authors(self.controller.about_authors)
215         dialog.set_comments(self.controller.about_text)
216         dialog.set_version(self.controller.app_version)
217         dialog.run()
218         dialog.destroy()
219
220     def add_label(self, text, box, markup=True, align="left", padding=1):
221         label = gtk.Label(text)
222         if markup:
223             label.set_use_markup(True)
224         if align == "left":
225             label.set_alignment(0, 0.5)
226
227         box.pack_start(label, False, False, padding)
228
229         return label
230
231 class mEveMonUI(BaseUI):
232
233     def __init__(self, controller):
234         self.controller = controller
235         gtk.set_application_name("mEveMon")
236
237         # create the main window
238         self.win = hildon.Window()
239         self.win.connect("destroy", self.controller.quit)
240         self.win.show_all()
241         progress_bar = hildon.hildon_banner_show_progress(self.win, None, "Loading overview...")
242         progress_bar.set_fraction(0.4)
243
244         # Create menu
245         menu = self.create_menu(self.win)
246
247         # Attach menu to the window
248         self.win.set_menu(menu)
249
250         character_win = CharacterSheetUI(self.controller)
251
252         # create the treeview --danny
253         self.char_model = models.CharacterListModel(self.controller)
254         treeview = gtk.TreeView(model = self.char_model)
255         treeview.connect('row-activated', character_win.build_window)
256         treeview.set_model(self.char_model)
257         self.add_columns_to_treeview(treeview)
258
259         # add the treeview with scrollbar --danny
260         self.win.add_with_scrollbar(treeview)
261         self.win.show_all()
262
263         progress_bar.set_fraction(1)
264         progress_bar.destroy()
265
266     def add_columns_to_treeview(self, treeview):
267         #Column 0 for the treeview
268         renderer = gtk.CellRendererPixbuf()
269         column = gtk.TreeViewColumn()
270         column.pack_start(renderer, True)
271         column.add_attribute(renderer, "pixbuf", 
272                 models.CharacterListModel.C_PORTRAIT)
273         treeview.append_column(column)
274
275         #Column 1 for the treeview
276         renderer = gtk.CellRendererText()
277         column = gtk.TreeViewColumn('Character Name', renderer, 
278                 text=models.CharacterListModel.C_NAME)
279         column.set_property("expand", True)
280         treeview.append_column(column)
281  
282     def refresh_clicked(self, button):
283         progress_bar = hildon.hildon_banner_show_progress(self.win, None, "Loading characters...")
284         progress_bar.set_fraction(1)
285         self.char_model.get_characters()
286         progress_bar.destroy()
287
288 class CharacterSheetUI(BaseUI):
289     UPDATE_INTERVAL = 1
290
291     def __init__(self, controller):
292         self.controller = controller
293         self.sheet = None
294         self.char_id = None
295         self.skills_model = None
296
297
298     def build_window(self, treeview, path, view_column):
299         # TODO: this is a really long and ugly function, split it up somehow
300
301         self.win = hildon.Window()
302
303         progress_bar = hildon.hildon_banner_show_progress(self.win, None, "Loading character sheet...")
304
305         self.win.show_all() 
306         progress_bar.set_fraction(0.4)
307
308         # Create menu
309         # NOTE: we probably want a window-specific menu for this page, but the
310         # main appmenu works for now
311         menu = self.create_menu(self.win)
312         # Attach menu to the window
313         self.win.set_menu(menu)
314
315         model = treeview.get_model()
316         miter = model.get_iter(path)
317         
318         # column 0 is the portrait, column 1 is name
319         char_name = model.get_value(miter, 1)
320         self.uid = model.get_value(miter, 2)
321         self.char_id = self.controller.char_name2id(char_name)
322
323         self.sheet = self.controller.get_char_sheet(self.uid, self.char_id)
324
325         self.win.set_title(char_name)
326
327
328         hbox = gtk.HBox(False, 0)
329         info_vbox = gtk.VBox(False, 0)
330
331         portrait = gtk.Image()
332         portrait.set_from_file(self.controller.get_portrait(char_name, 256))
333         portrait.show()
334
335         hbox.pack_start(portrait, False, False, 10)
336         hbox.pack_start(info_vbox, False, False, 5)
337
338         vbox = gtk.VBox(False, 0)
339
340         vbox.pack_start(hbox, False, False, 0)
341
342         self.fill_info(info_vbox)
343         self.fill_stats(info_vbox)
344
345         separator = gtk.HSeparator()
346         vbox.pack_start(separator, False, False, 5)
347         separator.show()
348
349         
350         self.add_label("<big>Skill in Training:</big>", vbox, align="normal")
351
352         self.display_skill_in_training(vbox)
353
354         separator = gtk.HSeparator()
355         vbox.pack_start(separator, False, False, 0)
356         separator.show()
357         
358         self.add_label("<big>Skills:</big>", vbox, align="normal")
359
360
361         self.skills_model = models.CharacterSkillsModel(self.controller, self.char_id)
362         skills_treeview = gtk.TreeView(model = skills_model)
363         skills_treeview.set_model(self.skills_model)
364         self.add_columns_to_skills_view(skills_treeview)
365
366         vbox.pack_start(skills_treeview, False, False, 0)
367
368         self.win.add_with_scrollbar(vbox)
369         self.win.show_all()
370
371         progress_bar.set_fraction(1)
372         progress_bar.destroy()
373
374     def display_skill_in_training(self, vbox):
375         skill = self.controller.get_skill_in_training(self.uid, self.char_id)
376         
377         if skill.skillInTraining:
378
379             skilltree = self.controller.get_skill_tree()
380             
381             # I'm assuming that we will always find a skill with the skill ID
382             for group in skilltree.skillGroups:
383                 found_skill = group.skills.Get(skill.trainingTypeID, False)
384                 if found_skill:
385                     skill_name = found_skill.typeName
386                     break
387                 
388             self.add_label("%s <small>(Level %d)</small>" % (skill_name, skill.trainingToLevel),
389                     vbox, align="normal")
390             self.add_label("<small>start time: %s\t\tend time: %s</small>" 
391                     %(time.ctime(skill.trainingStartTime),
392                 time.ctime(skill.trainingEndTime)), vbox, align="normal")
393
394             progressbar = gtk.ProgressBar()
395             fraction_completed = (time.time() - skill.trainingStartTime) / \
396                     (skill.trainingEndTime - skill.trainingStartTime)
397
398             progressbar.set_fraction(fraction_completed)
399             align = gtk.Alignment(0.5, 0.5, 0.5, 0)
400             vbox.pack_start(align, False, False, 5)
401             align.show()
402             align.add(progressbar)
403             progressbar.show()
404         else:
405             self.add_label("<small>No skills are currently being trained</small>",
406                     vbox, align="normal")
407
408
409
410     def fill_info(self, box):
411         self.add_label("<big><big>%s</big></big>" % self.sheet.name, box)
412         self.add_label("<small>%s %s %s</small>" % (self.sheet.gender, 
413             self.sheet.race, self.sheet.bloodLine), box)
414         self.add_label("", box, markup=False)
415         self.add_label("<small><b>Corp:</b> %s</small>" % self.sheet.corporationName, box)
416         self.add_label("<small><b>Balance:</b> %s ISK</small>" % 
417                 util.comma(self.sheet.balance), box)
418
419         self.live_sp_val = self.controller.get_sp(self.uid, self.char_id)
420         self.live_sp = self.add_label("<small><b>Total SP:</b> %s</small>" %
421                 util.comma(int(self.live_sp_val)), box)
422         
423         self.spps = self.controller.get_spps(self.uid, self.char_id)[0]
424
425         glib.timeout_add_seconds(self.UPDATE_INTERVAL, self.update_live_sp)
426
427     def fill_stats(self, box):
428
429         atr = self.sheet.attributes
430
431         self.add_label("<small><b>I: </b>%d  <b>M: </b>%d  <b>C: </b>%d  " \
432                 "<b>P: </b>%d  <b>W: </b>%d</small>" % (atr.intelligence,
433                     atr.memory, atr.charisma, atr.perception, atr.willpower), box)
434
435
436     def add_columns_to_skills_view(self, treeview):
437         #Column 0 for the treeview
438         renderer = gtk.CellRendererText()
439         column = gtk.TreeViewColumn('Skill Name', renderer, 
440                 markup=models.CharacterSkillsModel.C_NAME)
441         column.set_property("expand", True)
442         treeview.append_column(column)
443         
444         #Column 1 for the treeview
445         column = gtk.TreeViewColumn('Rank', renderer, 
446                 markup=models.CharacterSkillsModel.C_RANK)
447         column.set_property("expand", True)
448         treeview.append_column(column)
449
450         #Column 2
451         column = gtk.TreeViewColumn('Points', renderer,
452                 markup=models.CharacterSkillsModel.C_SKILLPOINTS)
453         column.set_property("expand", True)
454         treeview.append_column(column)
455
456         #Column 3
457         column = gtk.TreeViewColumn('Level', renderer, 
458                 markup=models.CharacterSkillsModel.C_LEVEL)
459         column.set_property("expand", True)
460         treeview.append_column(column)
461
462
463     def refresh_clicked(self, button):
464         progress_bar = hildon.hildon_banner_show_progress(self.win, None, "Loading overview...")
465         progress_bar.set_fraction(1)
466         self.skills_model.get_skills()
467         progress_bar.destroy()
468
469
470     def update_live_sp(self):
471         # we don't want to keep the timer running in the background
472         # when this callback returns False, the timer destorys itself
473        
474         # TODO: figure out why this doesn't work on the real device
475         #
476         #if not self.win.get_is_topmost():
477         #    return False
478         
479         self.live_sp_val = self.live_sp_val + self.spps * self.UPDATE_INTERVAL
480         self.live_sp.set_label("<small><b>Total SP:</b> %s</small>" %
481                                 util.comma(int(self.live_sp_val)))
482
483         return True
484
485
486 if __name__ == "__main__":
487     main()
488