xref: /haiku/src/apps/terminal/TermWindow.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 /*
2  * Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net>
3  * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
4  * Copyright (c) 2004 Daniel Furrer <assimil8or@users.sourceforge.net>
5  *
6  * Distributed unter the terms of the MIT license.
7  */
8 #include <Alert.h>
9 #include <Application.h>
10 #include <Menu.h>
11 #include <MenuBar.h>
12 #include <MenuItem.h>
13 #include <Path.h>
14 #include <PrintJob.h>
15 #include <PropertyInfo.h>
16 #include <ScrollBar.h>
17 #include <TextControl.h>
18 #include <WindowScreen.h>
19 #include <float.h>
20 #include <stdio.h>
21 #include <string>
22 #include <sys/time.h>
23 #include <unistd.h>
24 
25 #include "CodeConv.h"
26 #include "ColorWindow.h"
27 #include "MenuUtil.h"
28 #include "PrefDlg.h"
29 #include "PrefView.h"
30 #include "PrefHandler.h"
31 #include "TermApp.h"
32 #include "TermBaseView.h"
33 #include "TermBuffer.h"
34 #include "TermParse.h"
35 #include "TermView.h"
36 #include "TermWindow.h"
37 #include "TermConst.h"
38 #include "spawn.h"
39 
40 
41 // Global Preference Handler
42 extern PrefHandler *gTermPref;
43 //
44 // help and GPL URL
45 //
46 //#define URL_PREFIX  "file:///boot/home/config/settings/MuTerminal/help/"
47 //#define INDEX_FILE  "/index.html"
48 //#define GPL_FILE  "/gpl.html"
49 //#define CHLP_FILE   "file:///boot/beos/documentation/Shell%20Tools/index.html"
50 
51 extern int gNowCoding;  /* defined TermParce.cpp */
52 
53 void SetCoding(int);
54 
55 
56 TermWindow::TermWindow(BRect frame, const char* title)
57 	: BWindow(frame, title, B_DOCUMENT_WINDOW, B_CURRENT_WORKSPACE)
58 {
59 	InitWindow();
60 
61 	fPrintSettings = NULL;
62 	fPrefWindow = NULL;
63 	fFindPanel = NULL;
64 }
65 
66 
67 TermWindow::~TermWindow()
68 {
69 	delete fWindowUpdate;
70 }
71 
72 
73 //	#pragma mark - public methods
74 
75 
76 /** Initialize Window object. */
77 
78 void
79 TermWindow::InitWindow(void)
80 {
81 	// make menu bar
82 	SetupMenu();
83 
84 	// Setup font.
85 
86 	const char *family = gTermPref->getString(PREF_HALF_FONT_FAMILY);
87 
88 	BFont halfFont;
89 	halfFont.SetFamilyAndStyle(family, NULL);
90 	float size = gTermPref->getFloat(PREF_HALF_FONT_SIZE);
91 	if (size < 6.0f)
92 		size = 6.0f;
93 	halfFont.SetSize(size);
94 	halfFont.SetSpacing(B_FIXED_SPACING);
95 
96 	family = gTermPref->getString(PREF_FULL_FONT_FAMILY);
97 
98 	BFont fullFont;
99 	fullFont.SetFamilyAndStyle(family, NULL);
100 	size = gTermPref->getFloat(PREF_FULL_FONT_SIZE);
101 	if (size < 6.0f)
102 		size = 6.0f;
103 	fullFont.SetSize(size);
104 	fullFont.SetSpacing(B_FIXED_SPACING);
105 
106 	// Make Terminal text view.
107 
108 	BRect textframe = Bounds();
109 	textframe.top = fMenubar->Bounds().bottom + 1.0;
110 
111 	fCodeConv = new CodeConv();
112 	fTermView = new TermView(Bounds(), fCodeConv);
113 
114 	/*
115 	 * MuTerm has two views. BaseView is window base view.
116 	 * TermView is character Terminal view on BaseView. It has paste
117 	 * on BaseView shift as VIEW_OFFSET.
118 	 */
119 	fBaseView = new TermBaseView(textframe, fTermView);
120 
121 	// Initialize TermView. (font, size and color)
122 
123 	fTermView->SetTermFont(&halfFont, &fullFont);
124 	BRect rect = fTermView->SetTermSize(gTermPref->getInt32(PREF_ROWS),
125 		gTermPref->getInt32(PREF_COLS), 1);
126 
127 	int width, height;
128 
129 	fTermView->GetFontSize(&width, &height);
130 	SetSizeLimits(MIN_COLS * width, MAX_COLS * width,
131 		MIN_COLS * height, MAX_COLS * height);
132 
133 	fTermView->SetTermColor();
134 	fBaseView->SetViewColor(gTermPref->getRGB(PREF_TEXT_BACK_COLOR));
135 
136 	// Add offset to baseview.
137 	rect.InsetBy(-VIEW_OFFSET, -VIEW_OFFSET);
138 
139 	// Resize Window
140 
141 	ResizeTo(rect.Width()+ B_V_SCROLL_BAR_WIDTH,
142 		rect.Height() + fMenubar->Bounds().Height());
143 
144 	fBaseView->ResizeTo(rect.Width(), rect.Height());
145 	fBaseView->AddChild(fTermView);
146 	fTermView->MoveBy(VIEW_OFFSET, VIEW_OFFSET);
147 
148 	// Make Scroll Bar.
149 
150 	BRect scrollRect(0, 0, B_V_SCROLL_BAR_WIDTH,
151 		rect.Height() - B_H_SCROLL_BAR_HEIGHT + 1);
152 
153 	scrollRect.OffsetBy(rect.Width() + 1, fMenubar->Bounds().Height());
154 
155 	BScrollBar *scrollBar = new BScrollBar(scrollRect, "scrollbar",
156 		fTermView, 0, 0, B_VERTICAL);
157 	fTermView->SetScrollBar(scrollBar);
158 
159 	AddChild(scrollBar);
160 	AddChild(fBaseView);
161 
162 	// Set fEditmenu's target to fTermView. (Oh!...)
163 	fEditmenu->SetTargetForItems(fTermView);
164 
165 	// Initialize TermParse
166 
167 	gNowCoding = longname2op(gTermPref->getString(PREF_TEXT_ENCODING));
168 	fTermParse = new TermParse();
169 	fTermParse->InitPtyReader(this);
170 	fTermParse->InitTermParse(fTermView, fCodeConv);
171 
172 	// Set Coding.
173 
174 	// Initialize MessageRunner.
175 	fWindowUpdate = new BMessageRunner(BMessenger(this),
176 		new BMessage (MSGRUN_WINDOW), 500000);
177 }
178 
179 
180 void
181 TermWindow::MenusBeginning(void)
182 {
183 	// Syncronize Encode Menu Pop-up menu and Preference.
184 	(fEncodingmenu->FindItem(op2longname(gNowCoding)))->SetMarked(true);
185 	BWindow::MenusBeginning();
186 }
187 
188 
189 void
190 TermWindow::SetupMenu(void)
191 {
192   PrefHandler menuText;
193 
194   LoadLocaleFile (&menuText);
195 
196   // Menu bar object.
197   fMenubar = new BMenuBar(Bounds(), "mbar");
198 
199   /*
200    * Make Fiile Menu.
201    */
202   fFilemenu = new BMenu("Terminal");
203   fFilemenu->AddItem(new BMenuItem("Switch Terminals", new BMessage(MENU_SWITCH_TERM),'G'));
204   fFilemenu->AddItem(new BMenuItem("Start New Terminal", new BMessage(MENU_NEW_TREM), 'N'));
205   fFilemenu->AddSeparatorItem();
206   fFilemenu->AddItem(new BMenuItem("Page Setup...", new BMessage(MENU_PAGE_SETUP)));
207   fFilemenu->AddItem(new BMenuItem("Print", new BMessage(MENU_PRINT),'P'));
208   fFilemenu->AddSeparatorItem();
209   fFilemenu->AddItem(new BMenuItem("About Terminal...", new BMessage(B_ABOUT_REQUESTED)));
210   fFilemenu->AddSeparatorItem();
211   fFilemenu->AddItem(new BMenuItem("Quit", new BMessage(MENU_FILE_QUIT), 'Q'));
212   fMenubar->AddItem(fFilemenu);
213 
214   /*
215    * Make Edit Menu.
216    */
217   fEditmenu = new BMenu ("Edit");
218   fEditmenu->AddItem (new BMenuItem ("Copy", new BMessage (B_COPY),'C'));
219   fEditmenu->AddItem (new BMenuItem ("Paste", new BMessage (B_PASTE),'V'));
220   fEditmenu->AddSeparatorItem ();
221   fEditmenu->AddItem (new BMenuItem ("Select All", new BMessage (B_SELECT_ALL), 'A'));
222   fEditmenu->AddItem (new BMenuItem ("Clear All", new BMessage (MENU_CLEAR_ALL), 'L'));
223 
224 /*
225   // TODO: Implement Finding
226   fEditmenu->AddSeparatorItem ();
227   fEditmenu->AddItem (new BMenuItem ("Find", new BMessage (MENU_FIND_STRING),'F'));
228   fEditmenu->AddItem (new BMenuItem ("Find Again", new BMessage (MENU_FIND_AGAIN), ']'));
229 */
230 	fMenubar->AddItem (fEditmenu);
231 
232 
233   /*
234    * Make Help Menu.
235    */
236 	fHelpmenu = new BMenu("Settings");
237 	fWindowSizeMenu = new BMenu("Window Size");
238   	fWindowSizeMenu->AddItem(new BMenuItem("80x24", new BMessage(EIGHTYTWENTYFOUR)));
239   	fWindowSizeMenu->AddItem(new BMenuItem("80x25", new BMessage(EIGHTYTWENTYFIVE)));
240    	fWindowSizeMenu->AddItem(new BMenuItem("80x40", new BMessage(EIGHTYFORTY)));
241  	fWindowSizeMenu->AddItem(new BMenuItem("132x24", new BMessage(ONETHREETWOTWENTYFOUR)));
242  	fWindowSizeMenu->AddItem(new BMenuItem("132x25", new BMessage(ONETHREETWOTWENTYFIVE)));
243 
244  	// Considering we have this in the preferences window, this menu is not
245  	// needed and should not be shown if we are to not confuse the user
246 /*  fNewFontMenu = new BMenu("Font");
247 	fNewFontMenu->SetRadioMode(true);
248 		int32 numFamilies1 = count_font_families();
249 		for ( int32 i = 0; i < numFamilies1; i++ ) {
250 			font_family family;
251 			uint32 flags;
252 			if ( get_font_family(i, &family, &flags) == B_OK ) {
253 				fNewFontMenu->AddItem(item = new BMenuItem(family, new BMessage(MSG_FONT_CHANGED)));
254 			//	if (0 ==i) item->SetMarked(true);
255 			}
256 		}
257   fNewFontMenu->FindItem (gTermPref->getString(PREF_HALF_FONT_FAMILY))->SetMarked(true);
258 */
259 
260 	fEncodingmenu = new BMenu("Font Encoding");
261 	fEncodingmenu->SetRadioMode(true);
262 	MakeEncodingMenu(fEncodingmenu, gNowCoding, true);
263 	fHelpmenu->AddItem(fWindowSizeMenu);
264 	fHelpmenu->AddItem(fEncodingmenu);
265 //  fHelpmenu->AddItem(fNewFontMenu);
266 	fHelpmenu->AddSeparatorItem();
267 	fHelpmenu->AddItem(new BMenuItem("Preferences", new BMessage(MENU_PREF_OPEN)));
268 	fHelpmenu->AddSeparatorItem();
269 	fHelpmenu->AddItem(new BMenuItem("Save as default", new BMessage(SAVE_AS_DEFAULT)));
270 	fMenubar->AddItem(fHelpmenu);
271 
272 	AddChild(fMenubar);
273 }
274 
275 
276 void
277 TermWindow::MessageReceived(BMessage *message)
278 {
279   int32 coding_id;
280   BRect r;
281   BFont halfFont;
282   BFont fullFont;
283 
284   switch (message->what) {
285 
286   case MENU_SWITCH_TERM:
287     be_app->PostMessage(MENU_SWITCH_TERM);
288     break;
289 
290   case MENU_NEW_TREM:
291     be_app->PostMessage(MENU_NEW_TREM);
292     break;
293 
294   case MENU_PREF_OPEN:
295     if (!fPrefWindow){
296       fPrefWindow = new PrefDlg(this);
297     }else{
298       fPrefWindow->Activate();
299     }
300     break;
301 
302   case MSG_PREF_CLOSED:
303     fPrefWindow = NULL;
304     break;
305 
306   case MENU_FILE_QUIT:
307     be_app->PostMessage(B_QUIT_REQUESTED);
308     break;
309   case MENU_ENCODING:
310     message->FindInt32 ("op", &coding_id);
311     gNowCoding = coding_id;
312     SetCoding(coding_id);
313     break;
314 
315     /*
316      * Extended B_SET_PROPERTY. Dispatch this message,
317      * Set coding ID.
318      */
319   case B_SET_PROPERTY:
320   {
321     int32 i;
322     BMessage spe;
323     message->GetCurrentSpecifier(&i, &spe);
324     if (!strcmp("encode", spe.FindString("property", i))){
325       message->FindInt32 ("data",  &coding_id);
326       gNowCoding = coding_id;
327       SetCoding (coding_id);
328 
329       message->SendReply(B_REPLY);
330     }else{
331       BWindow::MessageReceived(message);
332     }
333     break;
334   }
335 
336     /*
337      * Extended B_GET_PROPERTY. Dispatch this message,
338      * reply now coding ID.
339      */
340   case B_GET_PROPERTY:
341   {
342     int32 i;
343     BMessage spe;
344     message->GetCurrentSpecifier(&i, &spe);
345     if (!strcmp("encode", spe.FindString("property", i))){
346       BMessage reply(B_REPLY);
347       reply.AddInt32("result", gNowCoding);
348       message->SendReply(&reply);
349     }else if (!strcmp("tty", spe.FindString("property", i))){
350       BMessage reply(B_REPLY);
351       reply.AddString("result", &tty_name[8]);
352       message->SendReply(&reply);
353     }else{
354       BWindow::MessageReceived(message);
355     }
356     break;
357   }
358 
359   /*
360    * Message from Preference panel.
361    */
362   case MSG_ROWS_CHANGED:
363   case MSG_COLS_CHANGED:
364     r = fTermView->SetTermSize (gTermPref->getInt32 (PREF_ROWS),
365               gTermPref->getInt32 (PREF_COLS),
366               0);
367 
368     ResizeTo (r.Width()+ B_V_SCROLL_BAR_WIDTH + VIEW_OFFSET * 2,
369         r.Height()+fMenubar->Bounds().Height() + VIEW_OFFSET *2);
370     break;
371 
372   case MSG_HALF_FONT_CHANGED:
373   case MSG_FULL_FONT_CHANGED:
374   case MSG_HALF_SIZE_CHANGED:
375   case MSG_FULL_SIZE_CHANGED:
376 
377     halfFont.SetFamilyAndStyle (gTermPref->getString(PREF_HALF_FONT_FAMILY),
378         NULL);
379     halfFont.SetSize (gTermPref->getFloat(PREF_HALF_FONT_SIZE));
380     halfFont.SetSpacing (B_FIXED_SPACING);
381 
382     fullFont.SetFamilyAndStyle (gTermPref->getString(PREF_FULL_FONT_FAMILY),
383         NULL);
384     fullFont.SetSize (gTermPref->getFloat(PREF_FULL_FONT_SIZE));
385     fullFont.SetSpacing (B_FIXED_SPACING);
386 
387     fTermView->SetTermFont (&halfFont, &fullFont);
388     r = fTermView->SetTermSize (0, 0, 0);
389 
390     int width, height;
391 
392     fTermView->GetFontSize (&width, &height);
393 
394     SetSizeLimits (MIN_COLS * width, MAX_COLS * width,
395 		   MIN_COLS * height, MAX_COLS * height);
396 
397 
398 
399     ResizeTo (r.Width()+ B_V_SCROLL_BAR_WIDTH + VIEW_OFFSET * 2,
400         r.Height()+fMenubar->Bounds().Height() + VIEW_OFFSET * 2);
401 
402     fTermView->Invalidate();
403     break;
404 
405 	case EIGHTYTWENTYFOUR:
406 		gTermPref->setString(PREF_COLS, "80");
407 		gTermPref->setString(PREF_ROWS, "24");
408 	   	this->PostMessage (MSG_ROWS_CHANGED);
409 		this->PostMessage (MSG_COLS_CHANGED);
410 	break;
411 
412 	case EIGHTYTWENTYFIVE:
413 		gTermPref->setString(PREF_COLS, "80");
414 		gTermPref->setString(PREF_ROWS, "25");
415 	   	this->PostMessage (MSG_ROWS_CHANGED);
416 	   	this->PostMessage (MSG_COLS_CHANGED);
417 	break;
418 
419 	case EIGHTYFORTY:
420 		gTermPref->setString(PREF_COLS, "80");
421 		gTermPref->setString(PREF_ROWS, "40");
422 	   	this->PostMessage (MSG_ROWS_CHANGED);
423 	   	this->PostMessage (MSG_COLS_CHANGED);
424 	break;
425 
426 	case ONETHREETWOTWENTYFOUR:
427 		gTermPref->setString(PREF_COLS, "132");
428 		gTermPref->setString(PREF_ROWS, "24");
429 	   	this->PostMessage (MSG_ROWS_CHANGED);
430 	   	this->PostMessage (MSG_COLS_CHANGED);
431 	break;
432 
433 	case ONETHREETWOTWENTYFIVE:
434 		gTermPref->setString(PREF_COLS, "132");
435 		gTermPref->setString(PREF_ROWS, "25");
436 	   	this->PostMessage (MSG_ROWS_CHANGED);
437 	   	this->PostMessage (MSG_COLS_CHANGED);
438 	break;
439 
440 	case MSG_FONT_CHANGED:
441     	gTermPref->setString (PREF_HALF_FONT_FAMILY, fNewFontMenu->FindMarked()->Label());
442     	this->PostMessage (MSG_HALF_FONT_CHANGED);
443     break;
444 
445 
446 	case MSG_COLOR_CHANGED:
447 		fBaseView->SetViewColor (gTermPref->getRGB (PREF_TEXT_BACK_COLOR));
448 		fTermView->SetTermColor ();
449 		fBaseView->Invalidate();
450 		fTermView->Invalidate();
451 		break;
452 	case SAVE_AS_DEFAULT:
453 		{
454 			BPath path;
455 			if (PrefHandler::GetDefaultPath(path) == B_OK)
456 				gTermPref->SaveAsText(path.Path(), PREFFILE_MIMETYPE);
457 		}
458 		break;
459 	case MENU_PAGE_SETUP:
460 		DoPageSetup ();
461 		break;
462 	case MENU_PRINT:
463 		DoPrint ();
464 		break;
465 
466   case MSGRUN_WINDOW:
467     fTermView->UpdateSIGWINCH ();
468     break;
469 
470 	case B_ABOUT_REQUESTED:
471 		be_app->PostMessage(B_ABOUT_REQUESTED);
472 		break;
473 
474   default:
475     BWindow::MessageReceived(message);
476     break;
477   }
478 }
479 ////////////////////////////////////////////////////////////////////////////
480 // WindowActivated (bool)
481 //  Dispatch Mesasge.
482 ////////////////////////////////////////////////////////////////////////////
483 void
484 TermWindow::WindowActivated (bool )
485 {
486 
487 }
488 
489 ////////////////////////////////////////////////////////////////////////////
490 // Quit (void)
491 //  Quit Application.
492 ////////////////////////////////////////////////////////////////////////////
493 //void
494 //TermWindow::colRequested() {
495 //	colWindow *colW=new colWindow("Colours for Terminal");
496 //	colW->Show();
497 //	}
498 
499 
500 void
501 TermWindow::Quit(void)
502 {
503   delete fTermParse;
504   delete fCodeConv;
505   if(fPrefWindow) fPrefWindow->PostMessage (B_QUIT_REQUESTED);
506     be_app->PostMessage (B_QUIT_REQUESTED, be_app);
507   BWindow::Quit ();
508 }
509 
510 
511 bool
512 TermWindow::QuitRequested(void)
513 {
514 
515   return true;
516 }
517 ////////////////////////////////////////////////////////////////////////////
518 // int GetTimeZone (void)
519 //  Get Machine Timezone.
520 ////////////////////////////////////////////////////////////////////////////
521 int
522 TermWindow::GetTimeZone ()
523 {
524   struct timeval tv;
525   struct timezone tm;
526 
527   gettimeofday (&tv, &tm);
528 
529   return -tm.tz_minuteswest / 60;
530 }
531 
532 
533 #include "spawn.h"
534 
535 
536 void
537 TermWindow::TermWinActivate()
538 {
539 	Activate();
540 
541 #ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST
542 	if (focus_follows_mouse()) {
543 		BPoint aMouseLoc = Frame().LeftTop();
544 		set_mouse_position(int32(aMouseLoc.x + 16), int32(aMouseLoc.y + 2));
545 		be_app->SetCursor(B_HAND_CURSOR);
546 	}
547 #endif
548 }
549 
550 
551 status_t
552 TermWindow::GetSupportedSuites(BMessage *msg)
553 {
554   static property_info prop_list[] = {
555      { "encode",
556        {B_GET_PROPERTY, 0},
557        {B_DIRECT_SPECIFIER, 0},
558        "get muterminal encode"},
559      { "encode",
560        {B_SET_PROPERTY, 0},
561        {B_DIRECT_SPECIFIER, 0},
562        "set muterminal encode"},
563      { "tty",
564        {B_GET_PROPERTY, 0},
565        {B_DIRECT_SPECIFIER, 0},
566        "get tty_name."},
567      { 0  }
568 
569   };
570   msg->AddString("suites", "suite/vnd.naan-termwindow");
571   BPropertyInfo prop_info(prop_list);
572   msg->AddFlat("messages", &prop_info);
573   return BWindow::GetSupportedSuites(msg);
574 }
575 ////////////////////////////////////////////////////////////////////////////
576 // ResolveSpecifier
577 //
578 ////////////////////////////////////////////////////////////////////////////
579 BHandler*
580 TermWindow::ResolveSpecifier(BMessage *msg, int32 index,
581            BMessage *specifier, int32 form,
582            const char *property)
583 {
584   if ( (strcmp(property, "encode") == 0)
585     && ((msg->what == B_SET_PROPERTY) || (msg->what == B_GET_PROPERTY) ))
586       return this;
587   else if ( (strcmp(property, "tty") == 0)
588     &&  (msg->what == B_GET_PROPERTY) )
589       return this;
590 
591   return BWindow::ResolveSpecifier(msg, index, specifier, form, property);
592 }
593 
594 ////////////////////////////////////////////////////////////////////////////
595 // SetCoding
596 //  Set coding utility functions.
597 ////////////////////////////////////////////////////////////////////////////
598 void SetCoding (int coding)
599 {
600   const etable *p = encoding_table;
601   p += coding;
602 
603   gNowCoding = coding;
604 
605   return;
606 }
607 ////////////////////////////////////////////////////////////////////////////
608 // DoPageSetUp ()
609 //
610 ////////////////////////////////////////////////////////////////////////////
611 status_t
612 TermWindow::DoPageSetup()
613 {
614   status_t rv;
615   BPrintJob job("PageSetup");
616 
617   /* display the page configure panel */
618   rv = job.ConfigPage();
619 
620   /* save a pointer to the settings */
621   fPrintSettings = job.Settings();
622 
623   return rv;
624 }
625 
626 ////////////////////////////////////////////////////////////////////////////
627 // DoPrint ()
628 //
629 ////////////////////////////////////////////////////////////////////////////
630 void
631 TermWindow::DoPrint()
632 {
633 //#if B_BEOS_VERSION < 0x0460
634   BPrintJob job("Print");
635 
636   if((! fPrintSettings) || (DoPageSetup() != B_NO_ERROR)) {
637     (new BAlert("Cancel", "Print cancelled.", "OK"))->Go();
638   return;
639   }
640 
641   job.SetSettings(new BMessage(*fPrintSettings));
642 
643   BRect pageRect = job.PrintableRect();
644   BRect curPageRect = pageRect;
645 
646   int pHeight = (int)pageRect.Height();
647   int pWidth = (int)pageRect.Width();
648   float w,h;
649   fTermView->GetFrameSize (&w, &h);
650   int xPages = (int)ceil(w / pWidth);
651   int yPages = (int)ceil(h / pHeight);
652 
653    /* engage the print server */
654   job.BeginJob();
655 
656   /* loop through and draw each page, and write to spool */
657   for(int x = 0; x < xPages; x++)
658     for(int y = 0; y < yPages; y++){
659       curPageRect.OffsetTo(x * pWidth, y * pHeight);
660       job.DrawView(fTermView, curPageRect, BPoint(0, 0));
661       job.SpoolPage();
662 
663       if(!job.CanContinue()){
664       	  // It is likely that the only way that the job was cancelled is
665       	  // because the user hit 'Cancel' in the page setup window, in which
666       	  // case, the user does *not* need to be told that it was cancelled.
667       	  // He/she will simply expect that it was done.
668 //        (new BAlert("Cancel", "Print job cancelled", "OK"))->Go();
669         return;
670       }
671   }
672 
673   /* commit the job, send the spool file */
674   job.CommitJob();
675 //#endif
676 }
677