1   /**
2    * JHylaFax - A java client for HylaFAX.
3    *
4    * Copyright (C) 2005 by Steffen Pingel <steffenp@gmx.de>
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation; either version 2 of the License, or
9    * (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with this program; if not, write to the Free Software
18   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19   */
20  package net.sf.jhylafax;
21  
22  import static net.sf.jhylafax.JHylaFAX.i18n;
23  import java.awt.event.ActionEvent;
24  import java.io.File;
25  import java.util.ArrayList;
26  import java.util.Date;
27  import java.util.List;
28  import javax.swing.Action;
29  import javax.swing.JOptionPane;
30  import javax.swing.table.AbstractTableModel;
31  import javax.swing.table.TableModel;
32  import net.sf.jhylafax.DetailsDialog.Property;
33  import net.sf.jhylafax.JobHelper.FileStat;
34  import net.sf.jhylafax.fax.FaxJob;
35  import org.xnap.commons.gui.Builder;
36  import org.xnap.commons.gui.Dialogs;
37  import org.xnap.commons.gui.action.AbstractXNapAction;
38  import org.xnap.commons.gui.util.DoubleClickListener;
39  
40  /**
41   * A panel that displays a list of jobs. Used for "sendq", "pollq" and "doneq".
42   *  
43   * @author Steffen Pingel
44   */
45  public class JobQueuePanel extends AbstractQueuePanel {
46  	
47  	private DetailsAction detailsAction;
48  	private EditJobAction editAction;
49  	private JobTableModel jobTableModel;
50  	private RemoveJobAction removeAction;
51  	private ResumeJobAction resumeAction;
52  	private RetryJobAction retryAction;
53  	private SuspendJobAction suspendAction;
54  	private ViewJobAction viewAction;
55  	
56  	public JobQueuePanel(String queueName) {
57  		super(queueName);
58  		
59  		removeAction = new RemoveJobAction();
60  		suspendAction = new SuspendJobAction();
61  		resumeAction = new ResumeJobAction();
62  		retryAction = new RetryJobAction();
63  		detailsAction = new DetailsAction();
64  		editAction = new EditJobAction();
65  		viewAction = new ViewJobAction();
66  		
67  		// TODO: should not hard code queue names here
68  		if (queueName.equals("doneq")) {
69  			getButtonPanel().add(Builder.createButton(viewAction));
70  			getButtonPanel().add(Builder.createButton(removeAction));
71  
72  			getTablePopupMenu().add(Builder.createMenuItem(viewAction));
73  			getTablePopupMenu().add(Builder.createMenuItem(detailsAction));
74  
75  			getTable().addMouseListener(new DoubleClickListener(viewAction));
76  		}
77  		else { // "sendq" / "pollq" (?)
78  			getButtonPanel().add(Builder.createButton(editAction));
79  			getButtonPanel().add(Builder.createButton(removeAction));
80  			getButtonPanel().add(Builder.createButton(suspendAction));
81  			getButtonPanel().add(Builder.createButton(resumeAction));
82  			getButtonPanel().add(Builder.createButton(viewAction));
83  			
84  			getTablePopupMenu().add(Builder.createMenuItem(editAction));
85  			getTablePopupMenu().add(Builder.createMenuItem(removeAction));
86  			getTablePopupMenu().add(Builder.createMenuItem(detailsAction));
87  			getTablePopupMenu().addSeparator();
88  			getTablePopupMenu().add(Builder.createMenuItem(retryAction));
89  			getTablePopupMenu().addSeparator();
90  			getTablePopupMenu().add(Builder.createMenuItem(suspendAction));
91  			getTablePopupMenu().add(Builder.createMenuItem(resumeAction));
92  			getTablePopupMenu().addSeparator();
93  			getTablePopupMenu().add(Builder.createMenuItem(viewAction));		
94  
95  			getTable().addMouseListener(new DoubleClickListener(editAction));
96  		}
97  		
98  		updateLabels();
99  		updateActions();
100 	}
101 	
102 	@Override
103 	public FileStat getSelectedFile()
104 	{
105 		return null;
106 	}
107 
108 	public FaxJob getSelectedJob()
109 	{
110 		int row = getSelectedRow();
111 		return (row == -1) ? null : jobTableModel.getJob(row); 
112 	}
113 	
114 	@Override
115 	protected TableModel getTableModel()
116 	{
117 		if (jobTableModel == null) {
118 			jobTableModel = new JobTableModel();
119 		}
120 		return jobTableModel;
121 	}
122 	
123 	protected void initializeTableLayout() {
124 		getTableLayout().setColumnProperties(0, "id", 20);
125 		getTableLayout().setColumnProperties(1, "priority", 20);
126 		getTableLayout().setColumnProperties(2, "result", 20);
127 		getTableLayout().setColumnProperties(3, "permissions", 40);
128 		getTableLayout().setColumnProperties(4, "owner", 40);
129 		getTableLayout().setColumnProperties(5, "sender", 80);
130 		getTableLayout().setColumnProperties(6, "clientMachine", 80);
131 		getTableLayout().setColumnProperties(7, "resolution", 40);
132 		getTableLayout().setColumnProperties(8, "number", 80);
133 		getTableLayout().setColumnProperties(9, "time", 60);
134 		getTableLayout().setColumnProperties(10, "pages", 20);
135 		getTableLayout().setColumnProperties(11, "dials", 20);
136 		getTableLayout().setColumnProperties(12, "error", 100);
137 		getTableLayout().setColumnProperties(13, "state", 18);
138 		getTableLayout().setColumnProperties(14, "cid", 40);
139 		getTableLayout().setColumnProperties(15, "tag", 40);
140 	}
141 
142 	public void setData(List<FaxJob> data) {
143 		jobTableModel.setData(data);
144 	}
145 
146 	@Override
147 	public void updateActions() {
148 		FaxJob job = getSelectedJob();
149 		viewAction.setEnabled(job != null);
150 		boolean isEditable = (job != null) && job.getID() != -1;
151 		editAction.setEnabled(isEditable);
152 		removeAction.setEnabled(isEditable);
153 		suspendAction.setEnabled(job != null && job.getState() != FaxJob.State.SUSPENDED);
154 		resumeAction.setEnabled(job != null && job.getState() == FaxJob.State.SUSPENDED);
155 		retryAction.setEnabled(job != null);
156 	}
157 
158 	@Override
159 	public void updateLabels() {
160 		super.updateLabels();
161 		
162 		removeAction.updateLabels();
163 		suspendAction.updateLabels();
164 		resumeAction.updateLabels();
165 		retryAction.updateLabels();
166 		detailsAction.updateLabels();
167 		editAction.updateLabels();
168 		viewAction.updateLabels();
169 		
170 		getTableLayout().setColumnNames(new String[] {
171 				i18n.tr("ID"),
172 				i18n.tr("Priority"), 
173 				i18n.tr("Result"),
174 				i18n.tr("Permission"),
175 				i18n.tr("Owner"),
176 				i18n.tr("Sender"), 
177 				i18n.tr("Client Machine"), 
178 				i18n.tr("Resolution"),
179 				i18n.tr("Number"),
180 				i18n.tr("Time"),
181 				i18n.tr("Pages"),
182 				i18n.tr("Dials"),				
183 				i18n.tr("Last Error"),
184 				i18n.tr("State"),
185 				i18n.tr("CID"),
186 				i18n.tr("Tag"),}); 
187 	}
188 
189 	private class EditJobAction extends AbstractXNapAction {
190 		
191 		public EditJobAction() {
192 			putValue(ICON_FILENAME, "edit.png");
193 		}
194 
195 		public void actionPerformed(ActionEvent e)
196 		{
197 			// a double click could be triggered despite of the disabled state
198 			if (!isEnabled()) {
199 				return;
200 			}
201 
202 			FaxJob job = getSelectedJob();
203 			EditDialog dialog = new EditDialog(JHylaFAX.getInstance(), job);
204 			dialog.setLocationRelativeTo(JHylaFAX.getInstance());
205 			dialog.setVisible(true);
206 		}
207 
208 		public void updateLabels() {
209 			putValue(Action.NAME, i18n.tr("Edit..."));
210 		}
211 
212 	}
213 
214 	private class DetailsAction extends AbstractXNapAction {
215 		
216 		public DetailsAction() {
217 			//putValue(ICON_FILENAME, "redo.png");
218 		}
219 
220 		public void actionPerformed(ActionEvent e)
221 		{
222 			FaxJob job = getSelectedJob();
223 			List<Property> data = new ArrayList<Property>();
224 			data.add(new Property(i18n.tr("Assigned modem"), job.getAssignedModem()));
225 			data.add(new Property(i18n.tr("Client-specefied dial string"), job.getClientDialString()));
226 			data.add(new Property(i18n.tr("Client machine name"), job.getClientMachineName()));
227 			data.add(new Property(i18n.tr("Scheduling priority"), job.getClientSchedulingPriority()));
228 			data.add(new Property(i18n.tr("Communication identifier"), job.getCommunicationIdentifier()));
229 			data.add(new Property(i18n.tr("Page chopping threshold"), job.getChoppingThreshold()));
230 			data.add(new Property(i18n.tr("Client-specified minimum signalling rate"), job.getClientMinimumSignallingRate()));
231 			data.add(new Property(i18n.tr("Client-specified tag"), job.getTag()));
232 			data.add(new Property(i18n.tr("Client-specified tagline format"), job.getTaglineFormat()));
233 			data.add(new Property(i18n.tr("# of consecutive failed dials"), job.getConsecutiveFailedDials()));
234 			data.add(new Property(i18n.tr("# of consecutive failed tries"), job.getConsecutiveFailedTries()));
235 			data.add(new Property(i18n.tr("Desired data format"), job.getDesiredDataFormat()));
236 			data.add(new Property(i18n.tr("Desired use of ECM"), job.getDesiredECM()));
237 			data.add(new Property(i18n.tr("Desired minimum scanline time"), job.getDesiredMinScanline()));
238 			data.add(new Property(i18n.tr("Desired signalling rate"), job.getDesiredSignallingRate()));
239 			data.add(new Property(i18n.tr("Destination company name"), job.getDestinationCompanyName()));
240 			data.add(new Property(i18n.tr("Destination geographic location"), job.getDestinationLocation()));
241 			data.add(new Property(i18n.tr("Destination password"), job.getDestinationPassword()));
242 			data.add(new Property(i18n.tr("Destination sub-address"), job.getDestinationSubAddress()));
243 			data.add(new Property(i18n.tr("# of attempted dials"), job.getDialsAttempted()));
244 			data.add(new Property(i18n.tr("Group identifier"), job.getGroupID()));
245 			data.add(new Property(i18n.tr("Horizontal resolution"), job.getHorizontalResolution()));
246 			data.add(new Property(i18n.tr("ID"), job.getID()));
247 			data.add(new Property(i18n.tr("Job done operation"), job.getJobDoneOperation()));
248 			data.add(new Property(i18n.tr("Job type"), job.getJobType()));
249 			data.add(new Property(i18n.tr("Kill time"), job.getKillTime()));
250 			data.add(new Property(i18n.tr("Last error"), job.getLastError()));
251 			data.add(new Property(i18n.tr("Total # of dials"), job.getMaxDials()));
252 			data.add(new Property(i18n.tr("Total # of tries"), job.getMaxTries()));
253 			data.add(new Property(i18n.tr("Notify"), job.getNotify()));
254 			data.add(new Property(i18n.tr("Notify address"), job.getNotifyAdress()));
255 			data.add(new Property(i18n.tr("Number"), job.getNumber()));
256 			data.add(new Property(i18n.tr("Owner"), job.getOwner()));
257 			data.add(new Property(i18n.tr("Page chopping"), job.getPageChopping()));
258 			data.add(new Property(i18n.tr("Permissions"), job.getPermissions()));
259 			data.add(new Property(i18n.tr("Total # of pages"), job.getPageCount()));
260 			data.add(new Property(i18n.tr("Page length"), job.getPageLength()));
261 			data.add(new Property(i18n.tr("# of transmitted pages"), job.getPagesTransmitted()));
262 			data.add(new Property(i18n.tr("Page width"), job.getPageWidth()));
263 			data.add(new Property(i18n.tr("Priority"), job.getPriority()));
264 			data.add(new Property(i18n.tr("Receiver"), job.getReceiver()));
265 			data.add(new Property(i18n.tr("Result"), job.getResult()));
266 			data.add(new Property(i18n.tr("Retry time"), job.getRetryTime()));
267 			data.add(new Property(i18n.tr("Sender"), job.getSender()));
268 			data.add(new Property(i18n.tr("Send time"), job.getSendTime()));
269 			data.add(new Property(i18n.tr("State"), job.getState()));
270 			data.add(new Property(i18n.tr("Vertical resolution"), job.getVerticalResolution()));
271 			DetailsDialog dialog = new DetailsDialog(JHylaFAX.getInstance(), data);
272 			dialog.setLocationRelativeTo(JHylaFAX.getInstance());
273 			dialog.setVisible(true);
274 		}
275 		
276 		public void updateLabels() {
277 			putValue(Action.NAME, i18n.tr("Details"));
278 		}
279 	}
280 
281 	private static class JobTableModel extends AbstractTableModel {
282 
283 		private static final Class[] columnClasses= {
284 			Integer.class,
285 			Integer.class,
286 			String.class, 
287 			String.class,
288 			String.class,
289 			String.class,
290 			String.class,
291 			Integer.class,
292 			String.class,
293 			Date.class,
294 			Integer.class, 
295 			String.class,
296 			String.class,
297 			FaxJob.State.class,
298 			String.class,
299 			String.class,
300 		};
301 		
302 		private List<FaxJob> data = new ArrayList<FaxJob>();
303 		
304 		public JobTableModel() 
305 		{
306 		}
307 		
308 		public Class<?> getColumnClass(int column) {
309 	        return columnClasses[column];
310 	    }
311 	
312 		public int getColumnCount()
313 		{
314 			return columnClasses.length;
315 		}
316 
317 		public FaxJob getJob(int row) {
318 			return data.get(row);
319 		}
320 
321 		public int getRowCount()
322 		{
323 			return data.size();
324 		}
325 		
326 	    public Object getValueAt(int row, int column) {
327 	    	FaxJob job = data.get(row);
328 			switch (column) {
329 			case 0:
330 				return job.getID();
331 			case 1:
332 				return job.getPriority();
333 			case 2:
334 				return job.getResult();
335 			case 3:
336 				return job.getPermissions();
337 			case 4:
338 				return job.getOwner();
339 			case 5:
340 				return job.getSender();
341 			case 6:
342 				return job.getClientMachineName();
343 			case 7:
344 				return job.getVerticalResolution();
345 			case 8:
346 				return job.getNumber();
347 			case 9:
348 				return job.getSendTime();
349 			case 10:
350 				return job.getPageCount();
351 			case 11:
352 				return job.getDialsAttempted() + "/" + job.getMaxDials();
353 			case 12:
354 				return job.getLastError();
355 			case 13:
356 				return job.getState();
357 			case 14:
358 				return job.getCommunicationIdentifier();
359 			case 15:
360 				return job.getTaglineFormat(); // FIME display tag
361 			default:
362 				return null;
363 			}
364 		}
365 		
366 		public void setData(List<FaxJob> data)
367 		{
368 			this.data = data;
369 			fireTableDataChanged();
370 		}
371 
372 	}
373 
374 	private class RemoveJobAction extends AbstractXNapAction {
375 		
376 		public RemoveJobAction() {
377 			putValue(ICON_FILENAME, "editdelete.png");
378 		}
379 
380 		public void actionPerformed(ActionEvent event) {
381 			FaxJob job = getSelectedJob();
382 			if (Dialogs.showConfirmDialog(JHylaFAX.getInstance(), 
383 					i18n.tr("Do you really want to delete the job with id {0}?", job.getID()),
384 					i18n.tr("Remove Job"), 
385 					JOptionPane.YES_NO_OPTION, 
386 					Settings.CONFIRM_DELETE) == JOptionPane.YES_OPTION) {
387 				JobHelper.removeJob(job.getID());
388 			}
389 		}
390 		
391 		public void updateLabels() {
392 			putValue(Action.NAME, i18n.tr("Remove"));
393 		}
394 	}
395 
396 	private class ResumeJobAction extends AbstractXNapAction {
397 		
398 		public ResumeJobAction() {
399 			putValue(ICON_FILENAME, "player_play.png");
400 		}
401 
402 		public void actionPerformed(ActionEvent event)
403 		{
404 			FaxJob job = getSelectedJob();
405 			JobHelper.resumeJob(job.getID());
406 		}
407 
408 		public void updateLabels() {
409 			putValue(Action.NAME, i18n.tr("Resume"));
410 		}
411 
412 	}
413 
414 	private class RetryJobAction extends AbstractXNapAction {
415 		
416 		public RetryJobAction() {
417 			putValue(ICON_FILENAME, "redo.png");
418 		}
419 
420 		public void actionPerformed(ActionEvent e)
421 		{
422 			FaxJob job = getSelectedJob();
423 			JobHelper.retryJob(job.getID());
424 		}
425 		
426 		public void updateLabels() {
427 			putValue(Action.NAME, i18n.tr("Send Now"));
428 		}
429 	}
430 
431 	private class SuspendJobAction extends AbstractXNapAction {
432 		
433 		public SuspendJobAction() {
434 			putValue(ICON_FILENAME, "player_pause.png");
435 		}
436 
437 		public void actionPerformed(ActionEvent event)
438 		{
439 			FaxJob job = getSelectedJob();
440 			JobHelper.suspendJob(job.getID());
441 		}
442 		
443 		public void updateLabels() {
444 			putValue(Action.NAME, i18n.tr("Suspend"));
445 		}
446 	}
447 
448 	private class ViewJobAction extends AbstractXNapAction {
449 		
450 		public ViewJobAction() {
451 			putValue(ICON_FILENAME, "viewmag.png");
452 		}
453 
454 		public void actionPerformed(ActionEvent event)
455 		{
456 			FaxJob job = getSelectedJob();
457 			if (job == null) {
458 				return;
459 			}
460 			
461 			// TODO should find a better way to determine viewer
462 			String viewerPath = JHylaFAXHelper.getViewerPath(getQueueName());
463 			if (viewerPath != null) {
464 				FileStat[] files = JobHelper.retrieveJobFilenames(job.getID());
465 				if (files != null) {
466 					if (files.length == 0) {
467 						Dialogs.showInfo(JHylaFAX.getInstance(), 
468 								i18n.tr("The job does not contain documents"), 
469 								i18n.tr("JHylaFAX Information"));
470 					}
471 					else {
472 						File[] tempFiles = new File[files.length];
473 						for (int i = 0; i < files.length; i++) {
474 							tempFiles[i] = createTempFile(files[i].filename);
475 							if (tempFiles[i] == null) {
476 								return;
477 							}
478 							if (!JobHelper.save(tempFiles[i], files[i].filename, files[i].filesize)) {
479 								// user abort or error
480 								return;
481 							}
482 						}
483 						JHylaFAXHelper.view(viewerPath, tempFiles);
484 					}
485 				}
486 			}
487 		}
488 
489 		public void updateLabels() {
490 			putValue(Action.NAME, i18n.tr("View"));
491 		}
492 		
493 	}
494 
495 }