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