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