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