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