366710baab4bf546c0c6cb2d2e21452e58ff3486
[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         self.timer = glib.timeout_add_seconds(self.UPDATE_INTERVAL, self.update_live_sp)
375         self.win.connect("destroy", self.back)
376
377         def back(self, widget):
378             glib.source_remove(self.timer)
379             gtk.Window.destroy(self.win)
380
381     def display_skill_in_training(self, vbox):
382         skill = self.controller.get_skill_in_training(self.uid, self.char_id)
383         
384         if skill.skillInTraining:
385
386             skilltree = self.controller.get_skill_tree()
387             
388             # I'm assuming that we will always find a skill with the skill ID
389             for group in skilltree.skillGroups:
390                 found_skill = group.skills.Get(skill.trainingTypeID, False)
391                 if found_skill:
392                     skill_name = found_skill.typeName
393                     break
394                 
395             self.add_label("%s <small>(Level %d)</small>" % (skill_name, skill.trainingToLevel),
396                     vbox, align="normal")
397             self.add_label("<small>start time: %s\t\tend time: %s</small>" 
398                     %(time.ctime(skill.trainingStartTime),
399                 time.ctime(skill.trainingEndTime)), vbox, align="normal")
400
401             progressbar = gtk.ProgressBar()
402             fraction_completed = (time.time() - skill.trainingStartTime) / \
403                     (skill.trainingEndTime - skill.trainingStartTime)
404
405             progressbar.set_fraction(fraction_completed)
406             align = gtk.Alignment(0.5, 0.5, 0.5, 0)
407             vbox.pack_start(align, False, False, 5)
408             align.show()
409             align.add(progressbar)
410             progressbar.show()
411         else:
412             self.add_label("<small>No skills are currently being trained</small>",
413                     vbox, align="normal")
414
415
416
417     def fill_info(self, box):
418         self.add_label("<big><big>%s</big></big>" % self.sheet.name, box)
419         self.add_label("<small>%s %s %s</small>" % (self.sheet.gender, 
420             self.sheet.race, self.sheet.bloodLine), box)
421         self.add_label("", box, markup=False)
422         self.add_label("<small><b>Corp:</b> %s</small>" % self.sheet.corporationName, box)
423         self.add_label("<small><b>Balance:</b> %s ISK</small>" % 
424                 util.comma(self.sheet.balance), box)
425
426         self.live_sp_val = self.controller.get_sp(self.uid, self.char_id)
427         self.live_sp = self.add_label("<small><b>Total SP:</b> %s</small>" %
428                 util.comma(int(self.live_sp_val)), box)
429         
430         self.spps = self.controller.get_spps(self.uid, self.char_id)[0]
431
432
433     def fill_stats(self, box):
434
435         atr = self.sheet.attributes
436
437         self.add_label("<small><b>I: </b>%d  <b>M: </b>%d  <b>C: </b>%d  " \
438                 "<b>P: </b>%d  <b>W: </b>%d</small>" % (atr.intelligence,
439                     atr.memory, atr.charisma, atr.perception, atr.willpower), box)
440
441
442     def add_columns_to_skills_view(self, treeview):
443         #Column 0 for the treeview
444         renderer = gtk.CellRendererText()
445         column = gtk.TreeViewColumn('Skill Name', renderer, 
446                 markup=models.CharacterSkillsModel.C_NAME)
447         column.set_property("expand", True)
448         treeview.append_column(column)
449         
450         #Column 1 for the treeview
451         column = gtk.TreeViewColumn('Rank', renderer, 
452                 markup=models.CharacterSkillsModel.C_RANK)
453         column.set_property("expand", True)
454         treeview.append_column(column)
455
456         #Column 2
457         column = gtk.TreeViewColumn('Points', renderer,
458                 markup=models.CharacterSkillsModel.C_SKILLPOINTS)
459         column.set_property("expand", True)
460         treeview.append_column(column)
461
462         #Column 3
463         column = gtk.TreeViewColumn('Level', renderer, 
464                 markup=models.CharacterSkillsModel.C_LEVEL)
465         column.set_property("expand", True)
466         treeview.append_column(column)
467
468
469     def refresh_clicked(self, button):
470         progress_bar = hildon.hildon_banner_show_progress(self.win, None, "Loading overview...")
471         progress_bar.set_fraction(1)
472         self.skills_model.get_skills()
473         progress_bar.destroy()
474
475
476     def update_live_sp(self):
477         self.live_sp_val = self.live_sp_val + self.spps * self.UPDATE_INTERVAL
478         self.live_sp.set_label("<small><b>Total SP:</b> %s</small>" %
479                                 util.comma(int(self.live_sp_val)))
480
481         return True
482
483
484 if __name__ == "__main__":
485     main()
486