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