new preferences
[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, DEFAULT_REPORTS_WINDOW_WIDTH
23 from Actions import \
24         APPLICATION_QUIT, FLOW_GRAPH_KILL, \
25         FLOW_GRAPH_SAVE, get_accel_group
26 import pygtk
27 pygtk.require('2.0')
28 import gtk
29 import Bars
30 from BlockTreeWindow import BlockTreeWindow
31 from Dialogs import TextDisplay, MessageDialogHelper
32 from DrawingArea import DrawingArea
33 from NotebookPage import NotebookPage
34 import Preferences
35 import Messages
36 import os
37
38 ############################################################
39 # Main window
40 ############################################################
41
42 class MainWindow(gtk.Window):
43         """The topmost window with menus, the tool bar, and other major windows."""
44
45         def __init__(self, handle_states, platform):
46                 """
47                 MainWindow contructor.
48                 @param handle_states the callback function
49                 """
50                 self._platform = platform
51                 #setup window
52                 self.handle_states = handle_states
53                 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
54                 vbox = gtk.VBox()
55                 self.hpaned = gtk.HPaned()
56                 self.add(vbox)
57                 #create the menu bar and toolbar
58                 self.add_accel_group(get_accel_group())
59                 vbox.pack_start(Bars.MenuBar(), False)
60                 vbox.pack_start(Bars.Toolbar(), False)
61                 vbox.pack_start(self.hpaned)
62                 #setup scrolled window
63                 self.scrolled_window = gtk.ScrolledWindow()
64                 self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
65                 self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
66                 self.drawing_area = DrawingArea(self)
67                 self.scrolled_window.add_with_viewport(self.drawing_area)
68                 #create the notebook
69                 self.notebook = gtk.Notebook()
70                 self.page_to_be_closed = None
71                 self.current_page = None
72                 self.notebook.set_show_border(False)
73                 self.notebook.set_scrollable(True) #scroll arrows for page tabs
74                 self.notebook.connect('switch-page', self._handle_page_change)
75                 #setup containers
76                 flow_graph_box = gtk.VBox(False, 0)
77                 self.flow_graph_vpaned = gtk.VPaned()
78                 flow_graph_box.pack_start(self.notebook, False, False, 0)
79                 flow_graph_box.pack_start(self.scrolled_window)
80                 self.flow_graph_vpaned.pack1(flow_graph_box)
81                 self.hpaned.pack1(self.flow_graph_vpaned)
82                 self.hpaned.pack2(BlockTreeWindow(platform, self.get_flow_graph), False) #dont allow resize
83                 #create the reports window
84                 self.text_display = TextDisplay()
85                 #house the reports in a scrolled window
86                 self.reports_scrolled_window = gtk.ScrolledWindow()
87                 self.reports_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
88                 self.reports_scrolled_window.add_with_viewport(self.text_display)
89                 self.reports_scrolled_window.set_size_request(-1, DEFAULT_REPORTS_WINDOW_WIDTH)
90                 self.flow_graph_vpaned.pack2(self.reports_scrolled_window, False) #dont allow resize
91                 #load preferences and show the main window
92                 Preferences.load(platform)
93                 self.resize(*Preferences.main_window_size())
94                 self.flow_graph_vpaned.set_position(Preferences.reports_window_position())
95                 self.hpaned.set_position(Preferences.blocks_window_position())
96                 self.show_all()
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         ############################################################
140         # Pages: create and close
141         ############################################################
142
143         def new_page(self, file_path='', show=False):
144                 """
145                 Create a new notebook page.
146                 Set the tab to be selected.
147                 @param file_path optional file to load into the flow graph
148                 @param show true if the page should be shown after loading
149                 """
150                 #if the file is already open, show the open page and return
151                 if file_path and file_path in self._get_files(): #already open
152                         page = self.notebook.get_nth_page(self._get_files().index(file_path))
153                         self._set_page(page)
154                         return
155                 try: #try to load from file
156                         if file_path: Messages.send_start_load(file_path)
157                         flow_graph = self._platform.get_new_flow_graph()
158                         #inject drawing area and handle states into flow graph
159                         flow_graph.drawing_area = self.drawing_area
160                         flow_graph.handle_states = self.handle_states
161                         page = NotebookPage(
162                                 self,
163                                 flow_graph=flow_graph,
164                                 file_path=file_path,
165                         )
166                         if file_path: Messages.send_end_load()
167                 except Exception, e: #return on failure
168                         Messages.send_fail_load(e)
169                         return
170                 #add this page to the notebook
171                 self.notebook.append_page(page, page.get_tab())
172                 try: self.notebook.set_tab_reorderable(page, True)
173                 except: pass #gtk too old
174                 self.notebook.set_tab_label_packing(page, False, False, gtk.PACK_START)
175                 #only show if blank or manual
176                 if not file_path or show: self._set_page(page)
177
178         def close_pages(self):
179                 """
180                 Close all the pages in this notebook.
181                 @return true if all closed
182                 """
183                 open_files = filter(lambda file: file, self._get_files()) #filter blank files
184                 open_file = self.get_page().get_file_path()
185                 #close each page
186                 for page in self._get_pages():
187                         self.page_to_be_closed = page
188                         self.close_page(False)
189                 if self.notebook.get_n_pages(): return False
190                 #save state before closing
191                 Preferences.files_open(open_files)
192                 Preferences.file_open(open_file)
193                 Preferences.main_window_size(self.get_size())
194                 Preferences.reports_window_position(self.flow_graph_vpaned.get_position())
195                 Preferences.blocks_window_position(self.hpaned.get_position())
196                 Preferences.save()
197                 return True
198
199         def close_page(self, ensure=True):
200                 """
201                 Close the current page.
202                 If the notebook becomes empty, and ensure is true,
203                 call new page upon exit to ensure that at least one page exists.
204                 @param ensure boolean
205                 """
206                 if not self.page_to_be_closed: self.page_to_be_closed = self.get_page()
207                 #show the page if it has an executing flow graph or is unsaved
208                 if self.page_to_be_closed.get_pid() or not self.page_to_be_closed.get_saved():
209                         self._set_page(self.page_to_be_closed)
210                 #unsaved? ask the user
211                 if not self.page_to_be_closed.get_saved() and self._save_changes():
212                         self.handle_states(FLOW_GRAPH_SAVE) #try to save
213                         if not self.page_to_be_closed.get_saved(): #still unsaved?
214                                 self.page_to_be_closed = None #set the page to be closed back to None
215                                 return
216                 #stop the flow graph if executing
217                 if self.page_to_be_closed.get_pid(): self.handle_states(FLOW_GRAPH_KILL)
218                 #remove the page
219                 self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed))
220                 if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one
221                 self.page_to_be_closed = None #set the page to be closed back to None
222
223         ############################################################
224         # Misc
225         ############################################################
226
227         def update(self):
228                 """
229                 Set the title of the main window.
230                 Set the titles on the page tabs.
231                 Show/hide the reports window.
232                 @param title the window title
233                 """
234                 title = ''.join((
235                                 self._platform.get_name(),
236                                 ' - Editing: ',
237                                 (self.get_page().get_file_path() or NEW_FLOGRAPH_TITLE),
238                                 (self.get_page().get_saved() and ' ' or '*'), #blank must be non empty
239                                 (self.get_page().get_read_only() and ' (read-only)' or ''),
240                         )
241                 )
242                 gtk.Window.set_title(self, title)
243                 #set tab titles
244                 for page in self._get_pages():
245                         #get filename and strip out file extension
246                         title = os.path.splitext(os.path.basename(page.get_file_path()))[0]
247                         page.set_text(''.join((
248                                                 (title or NEW_FLOGRAPH_TITLE),
249                                                 (page.get_saved() and ' ' or '*'), #blank must be non empty
250                                                 (page.get_read_only() and ' (ro)' or ''),
251                                         )
252                                 )
253                         )
254                 #show/hide notebook tabs
255                 if len(self._get_pages()) > 1: self.notebook.show()
256                 else: self.notebook.hide()
257
258         def get_page(self):
259                 """
260                 Get the selected page.
261                 @return the selected page
262                 """
263                 return self.current_page
264
265         def get_flow_graph(self):
266                 """
267                 Get the selected flow graph.
268                 @return the selected flow graph
269                 """
270                 return self.get_page().get_flow_graph()
271
272         ############################################################
273         # Helpers
274         ############################################################
275
276         def _set_page(self, page):
277                 """
278                 Set the current page.
279                 @param page the page widget
280                 """
281                 self.current_page = page
282                 self.notebook.set_current_page(self.notebook.page_num(self.current_page))
283
284         def _save_changes(self):
285                 """
286                 Save changes to flow graph?
287                 @return true if yes
288                 """
289                 return MessageDialogHelper(
290                         gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Unsaved Changes!',
291                         'Would you like to save changes before closing?'
292                 ) == gtk.RESPONSE_YES
293
294         def _get_files(self):
295                 """
296                 Get the file names for all the pages, in order.
297                 @return list of file paths
298                 """
299                 return map(lambda page: page.get_file_path(), self._get_pages())
300
301         def _get_pages(self):
302                 """
303                 Get a list of all pages in the notebook.
304                 @return list of pages
305                 """
306                 return [self.notebook.get_nth_page(page_num) for page_num in range(self.notebook.get_n_pages())]