fixed error for non root install in ticket:298, moved install task for freedesktop...
[debian/gnuradio] / grc / src / gui / MainWindow.py
1 """
2 Copyright 2008 Free Software Foundation, Inc.
3 This file is part of GNU Radio
4
5 GNU Radio Companion is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 GNU Radio Companion 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, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18 """
19
20 from Constants import \
21         MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, \
22         NEW_FLOGRAPH_TITLE, REPORTS_WINDOW_HEIGHT, \
23         FLOW_GRAPH_FILE_EXTENSION
24 from Actions import APPLICATION_QUIT, FLOW_GRAPH_KILL
25 import pygtk
26 pygtk.require('2.0')
27 import gtk
28 import Bars
29 from BlockTreeWindow import BlockTreeWindow
30 from Dialogs import TextDisplay, MessageDialogHelper
31 from DrawingArea import DrawingArea
32 from NotebookPage import NotebookPage
33 import Preferences
34 import Messages
35 import os
36
37 ############################################################
38 # Main window
39 ############################################################
40
41 class MainWindow(gtk.Window):
42         """The topmost window with menus, the tool bar, and other major windows."""
43
44         def __init__(self, handle_states, platform):
45                 """
46                 MainWindow contructor.
47                 @param handle_states the callback function
48                 """
49                 self._platform = platform
50                 #setup window
51                 self.handle_states = handle_states
52                 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
53                 vbox = gtk.VBox()
54                 hbox = gtk.HBox()
55                 self.add(vbox)
56                 #create the menu bar and toolbar
57                 vbox.pack_start(Bars.MenuBar(), False)
58                 vbox.pack_start(Bars.Toolbar(), False)
59                 #setup scrolled window
60                 self.scrolled_window = gtk.ScrolledWindow()
61                 self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
62                 self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
63                 self.drawing_area = DrawingArea(self)
64                 self.scrolled_window.add_with_viewport(self.drawing_area)
65                 #create the notebook
66                 self.notebook = gtk.Notebook()
67                 self.page_to_be_closed = None
68                 self.current_page = None
69                 self.notebook.set_show_border(False)
70                 self.notebook.set_scrollable(True) #scroll arrows for page tabs
71                 self.notebook.connect('switch-page', self._handle_page_change)
72                 fg_and_report_box = gtk.VBox(False, 0)
73                 fg_and_report_box.pack_start(self.notebook, False, False, 0)
74                 fg_and_report_box.pack_start(self.scrolled_window)
75                 hbox.pack_start(fg_and_report_box)
76                 vbox.pack_start(hbox)
77                 #create the side windows
78                 side_box = gtk.VBox()
79                 hbox.pack_start(side_box, False)
80                 side_box.pack_start(BlockTreeWindow(platform, self.get_flow_graph)) #allow resize, selection window can have more space
81                 #create the reports window
82                 self.text_display = TextDisplay()
83                 #house the reports in a scrolled window
84                 self.reports_scrolled_window = gtk.ScrolledWindow()
85                 self.reports_scrolled_window.set_size_request(-1, REPORTS_WINDOW_HEIGHT)
86                 self.reports_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
87                 self.reports_scrolled_window.add_with_viewport(self.text_display)
88                 fg_and_report_box.pack_end(self.reports_scrolled_window, False) #dont allow resize, fg should get all the space
89                 #show all but the main window container and the reports window
90                 vbox.show_all()
91                 self.notebook.hide()
92                 self._show_reports_window(False)
93                 # load preferences and show the main window
94                 Preferences.load(platform)
95                 self.resize(*Preferences.window_size())
96                 self.show()#show after resize in preferences
97
98         ############################################################
99         # Event Handlers
100         ############################################################
101
102         def _quit(self, window, event):
103                 """
104                 Handle the delete event from the main window.
105                 Generated by pressing X to close, alt+f4, or right click+close.
106                 This method in turns calls the state handler to quit.
107                 @return true
108                 """
109                 self.handle_states(APPLICATION_QUIT)
110                 return True
111
112         def _handle_page_change(self, notebook, page, page_num):
113                 """
114                 Handle a page change. When the user clicks on a new tab,
115                 reload the flow graph to update the vars window and
116                 call handle states (select nothing) to update the buttons.
117                 @param notebook the notebook
118                 @param page new page
119                 @param page_num new page number
120                 """
121                 self.current_page = self.notebook.get_nth_page(page_num)
122                 Messages.send_page_switch(self.current_page.get_file_path())
123                 self.handle_states()
124
125         ############################################################
126         # Report Window
127         ############################################################
128
129         def add_report_line(self, line):
130                 """
131                 Place line at the end of the text buffer, then scroll its window all the way down.
132                 @param line the new text
133                 """
134                 self.text_display.insert(line)
135                 vadj = self.reports_scrolled_window.get_vadjustment()
136                 vadj.set_value(vadj.upper)
137                 vadj.emit('changed')
138
139         def _show_reports_window(self, show):
140                 """
141                 Show the reports window when show is True.
142                 Hide the reports window when show is False.
143                 @param show boolean flag
144                 """
145                 if show: self.reports_scrolled_window.show()
146                 else: self.reports_scrolled_window.hide()
147
148         ############################################################
149         # Pages: create and close
150         ############################################################
151
152         def new_page(self, file_path='', show=False):
153                 """
154                 Create a new notebook page.
155                 Set the tab to be selected.
156                 @param file_path optional file to load into the flow graph
157                 @param show true if the page should be shown after loading
158                 """
159                 #if the file is already open, show the open page and return
160                 if file_path and file_path in self._get_files(): #already open
161                         page = self.notebook.get_nth_page(self._get_files().index(file_path))
162                         self._set_page(page)
163                         return
164                 try: #try to load from file
165                         if file_path: Messages.send_start_load(file_path)
166                         flow_graph = self._platform.get_new_flow_graph()
167                         #inject drawing area and handle states into flow graph
168                         flow_graph.drawing_area = self.drawing_area
169                         flow_graph.handle_states = self.handle_states
170                         page = NotebookPage(
171                                 self,
172                                 flow_graph=flow_graph,
173                                 file_path=file_path,
174                         )
175                         if file_path: Messages.send_end_load()
176                 except Exception, e: #return on failure
177                         Messages.send_fail_load(e)
178                         return
179                 #add this page to the notebook
180                 self.notebook.append_page(page, page.get_tab())
181                 try: self.notebook.set_tab_reorderable(page, True)
182                 except: pass #gtk too old
183                 self.notebook.set_tab_label_packing(page, False, False, gtk.PACK_START)
184                 #only show if blank or manual
185                 if not file_path or show: self._set_page(page)
186
187         def close_pages(self):
188                 """
189                 Close all the pages in this notebook.
190                 @return true if all closed
191                 """
192                 open_files = filter(lambda file: file, self._get_files()) #filter blank files
193                 open_file = self.get_page().get_file_path()
194                 #close each page
195                 for page in self._get_pages():
196                         self.page_to_be_closed = page
197                         self.close_page(False)
198                 if self.notebook.get_n_pages(): return False
199                 #save state before closing
200                 Preferences.files_open(open_files)
201                 Preferences.file_open(open_file)
202                 Preferences.window_size(self.get_size())
203                 Preferences.save()
204                 return True
205
206         def close_page(self, ensure=True):
207                 """
208                 Close the current page.
209                 If the notebook becomes empty, and ensure is true,
210                 call new page upon exit to ensure that at least one page exists.
211                 @param ensure boolean
212                 """
213                 if not self.page_to_be_closed: self.page_to_be_closed = self.get_page()
214                 #show the page if it has an executing flow graph or is unsaved
215                 if self.page_to_be_closed.get_pid() or not self.page_to_be_closed.get_saved():
216                         self._set_page(self.page_to_be_closed)
217                 #unsaved? ask the user
218                 if not self.page_to_be_closed.get_saved() and self._save_changes():
219                         self.handle_states(FLOW_GRAPH_SAVE) #try to save
220                         if not self.page_to_be_closed.get_saved(): #still unsaved?
221                                 self.page_to_be_closed = None #set the page to be closed back to None
222                                 return
223                 #stop the flow graph if executing
224                 if self.page_to_be_closed.get_pid(): self.handle_states(FLOW_GRAPH_KILL)
225                 #remove the page
226                 self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed))
227                 if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one
228                 self.page_to_be_closed = None #set the page to be closed back to None
229
230         ############################################################
231         # Misc
232         ############################################################
233
234         def update(self):
235                 """
236                 Set the title of the main window.
237                 Set the titles on the page tabs.
238                 Show/hide the reports window.
239                 @param title the window title
240                 """
241                 if self.get_page():
242                         title = ''.join((
243                                         Preferences.window_prefix(),
244                                         ' - Editing: ',
245                                         (self.get_page().get_file_path() or NEW_FLOGRAPH_TITLE),
246                                         (self.get_page().get_saved() and ' ' or '*'), #blank must be non empty
247                                 )
248                         )
249                 else: title = MAIN_WINDOW_PREFIX + ' - Editor '
250                 gtk.Window.set_title(self, title)
251                 #set tab titles
252                 for page in self._get_pages():
253                         title = os.path.basename(page.get_file_path())
254                         #strip file extension #TEMP
255                         if title.endswith('.xml'):
256                                 title = title[0:-len('.xml')]
257                         #strip file extension
258                         if title.endswith(FLOW_GRAPH_FILE_EXTENSION):
259                                 title = title[0:-len(FLOW_GRAPH_FILE_EXTENSION)]
260                         page.set_text(''.join((
261                                                 (title or NEW_FLOGRAPH_TITLE),
262                                                 (page.get_saved() and ' ' or '*'), #blank must be non empty
263                                         )
264                                 )
265                         )
266                 #reports window
267                 self._show_reports_window(Preferences.show_reports_window())
268                 #show/hide notebook tabs
269                 if len(self._get_pages()) > 1: self.notebook.show()
270                 else: self.notebook.hide()
271
272         def get_page(self):
273                 """
274                 Get the selected page.
275                 @return the selected page
276                 """
277                 return self.current_page
278
279         def get_flow_graph(self):
280                 """
281                 Get the selected flow graph.
282                 @return the selected flow graph
283                 """
284                 return self.get_page().get_flow_graph()
285
286         ############################################################
287         # Helpers
288         ############################################################
289
290         def _set_page(self, page):
291                 """
292                 Set the current page.
293                 @param page the page widget
294                 """
295                 self.current_page = page
296                 self.notebook.set_current_page(self.notebook.page_num(self.current_page))
297
298         def _save_changes(self):
299                 """
300                 Save changes to flow graph?
301                 @return true if yes
302                 """
303                 return MessageDialogHelper(
304                         gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Unsaved Changes!',
305                         'Would you like to save changes before closing?'
306                 ) == gtk.RESPONSE_YES
307
308         def _get_files(self):
309                 """
310                 Get the file names for all the pages, in order.
311                 @return list of file paths
312                 """
313                 return map(lambda page: page.get_file_path(), self._get_pages())
314
315         def _get_pages(self):
316                 """
317                 Get a list of all pages in the notebook.
318                 @return list of pages
319                 """
320                 return [self.notebook.get_nth_page(page_num) for page_num in range(self.notebook.get_n_pages())]