[03fe3a]: koha-offline-circulation-client / koc.phpw  Maximize  Restore  History

Download this file

987 lines (781 with data), 31.7 kB

#!/usr/bin/php
<?php
require_once( 'functions.php' );
require_once( 'prompt.class.php' );
//require_once( 'borrowerSearch.class.php' );

define( "SQLITE_DB_PATH", "./borrowers.db" );

$koc = new KohaOfflineCirculation();

class KohaOfflineCirculation {
  protected $debug = false;
  protected $glade;

  protected $issuesCurrent;
  protected $returnsCurrent;
  protected $history = array();

  protected $filename;
  protected $filehandle;

  protected $os;

  protected $sqlite;

  protected $noFileWarning = 'Circulation Data Is Not Being Saved To A File!';

  public function __construct() {
    if ( file_exists( "c:\windows" ) ) {
      $this->os = "windows";
    } else {
      $this->os = "linux";
    }

    $this->sqlite = sqlite_open( SQLITE_DB_PATH );

    $this->glade = new GladeXML('koc.glade');

    $this->glade->signal_autoconnect_instance( $this );

    $this->setupIssuesTreeview();
    $this->setupCurrentIssuesTreeview();
    $this->setupReturnsTreeview();
    $this->setupCircHistoryTreeview();
    $this->setupBsResultsListTreeview();

    $kocWin = $this->glade->get_widget( 'kocWin' );
    $kocWin->maximize();

    $borrowerAddressTextview = $this->glade->get_widget( 'borrowerAddressTextview' );
    $buffer = new GtkTextBuffer();
    $buffer->set_text( '' );
    $borrowerAddressTextview->set_buffer( $buffer );

    $bc = $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' );
    $bc->grab_focus();

    $this->setStatusBar( $this->noFileWarning );

    $this->statusbarTimeoutHandlerId = Gtk::timeout_add( 1000 * 3, array( &$this, 'onStatusbarTimeout' ) );

    $this->alertSaveOpen();

    Gtk::main();
  }

  public function on_kocWin_destroy() {
    if ( ( ! $this->filename ) && $this->isAnyCircs() ) {
        if( $this->alertYesNo( "You Have Not Saved Your Data To A File!\nDo You Want To Save It Now?" ) ) {
          $this->on_save_as_activate();
        }
    }
  
    Gtk::main_quit();
  }
  
  
  ## Issues ##
  
  # Buttons #
  
  public function on_issuesBorrowerCardnumberButton_clicked() {
    if ( $this->debug ) echo "on_issuesBorrowerCardnumberButton_clicked()\n";
    $this->on_issuesBorrowerCardnumberTextentry_activate();
  }

  public function on_searchButton_clicked() {
    if ( $this->debug ) echo "on_searchButton_clicked()\n";

    $bsFirstNameEntry = $this->glade->get_widget( 'bsFirstNameEntry' );
    $bsLastNameEntry = $this->glade->get_widget( 'bsLastNameEntry' );

    $bsFirstNameEntry->set_text('');
    $bsLastNameEntry->set_text('');
    $this->bsResultsList->clear();

    $bsWin = $this->glade->get_widget( 'bsWin' );
    $bsWin->show();

    $bsLastNameEntry->grab_focus();
  }
  
  public function on_issuesItemBarcodeButton_clicked() {
    if ( $this->debug ) echo "on_issuesItemBarcodeButton_clicked()\n";
    $this->on_issuesItemBarcodeTextentry_activate();
  }
  
  public function on_issuesCancelButton_clicked() {
    if ( $this->debug ) echo "on_issuesCancelButton_clicked()\n";
    $bc = $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' );
    $bc->set_editable( true );
    $bc->set_text('');    
    $this->issuesBarcodesList->clear();
    
    $this->updateBorrowerData();
    
    unset( $this->issuesCurrent );
    
    $bc->grab_focus();
  }
  
  public function on_issuesFinishButton_clicked() {
    if ( $this->debug ) echo "on_issuesFinishButton_clicked()\n";
    $cn = $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' );
    $cardnumber = $cn->get_text();
    
    foreach( $this->issuesCurrent as $issue ) {
      $issue['cardnumber'] = $cardnumber;
      $this->insertCircHistory( $issue );
    }
    
    $this->updateBorrowerData();
    
    unset( $this->issuesCurrent );

    $cn->set_editable( true );
    $cn->set_text('');    
    $this->issuesBarcodesList->clear();
    $cn->grab_focus();    
    
    $this->saveFile();
  }
  
  public function on_issuesDeleteSelectedBarcodeButton_clicked() {
    if ( $this->debug ) echo "on_issuesDeleteSelectedBarcodeButton_clicked()\n";
    $tree = $this->glade->get_widget( 'issuesTreeview' );
    $selection = $tree->get_selection();
    list( $model, $iter ) = $selection->get_selected();

    $value = $model->get_value( $iter, 0 );
    $this->removeCirc( $field = 'barcode', $value, $this->issuesCurrent );

    $this->issuesBarcodesList->remove( $iter );

    $ib = $this->glade->get_widget( 'issuesItemBarcodeTextentry' );
    $ib->grab_focus();
    
  }

  public function on_payFinesButton_clicked() {
    ## Get the fine payment amount
    $borrowerFinesEntry = $this->glade->get_widget( 'borrowerFinesEntry' );

    $prompt = new Prompt( "Enter Amount of Fine Payment. $", $borrowerFinesEntry->get_text()  );
    $payment = $prompt->entry->get_text();
    
    if ( $payment > 0 ) {
	    ## Process the fine payment.
	    $bc = $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' );
	    $cardnumber = $bc->get_text();

	    $finePayment = array();
	    $finePayment['type'] = 'payment';
	    $finePayment['cardnumber'] = $cardnumber;
	    $finePayment['barcode'] = $payment;
	    $finePayment['date'] = $this->getDate();
	    $this->insertCircHistory( $finePayment );
    }

    $this->saveFile();
  }
  
  # Text Fields #
  
  public function on_issuesBorrowerCardnumberTextentry_activate() {
    if ( $this->debug ) echo "on_issuesBorrowerCardnumberTextentry_activate()\n";
    $bc = $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' );
    $cardnumber = $bc->get_text();
    
    if ( ! empty( $cardnumber ) ) {
      $bc->set_editable( false );
      
      if ( ! is_array( $this->issuesCurrent ) ) {
        $this->issuesCurrent = array();
      }  
      
      $ib = $this->glade->get_widget( 'issuesItemBarcodeTextentry' );
      $ib->grab_focus();    
    } else {
      $bc->grab_focus();
    }
    
    $this->updateBorrowerData( $cardnumber );
  }
  
  public function on_issuesItemBarcodeTextentry_activate() {
    if ( $this->debug ) echo "on_issuesItemBarcodeTextentry_activate()\n";
    $ib = $this->glade->get_widget( 'issuesItemBarcodeTextentry' );   
    $barcode = $ib->get_text();
    if ( $this->debug ) echo "Barcode value is $barcode\n";

    if ( empty ( $barcode ) ) {
      $this->on_issuesFinishButton_clicked();
    } else {
      if ( ! in_array( $barcode, $this->issuesCurrent ) ) {
        $this->issuesBarcodesList->prepend( array( $barcode ) );
        $this->issuesCurrent[] = array( 
          'barcode' => $barcode, 
          'date' => $this->getDate(),
          'type' => 'issue',
        );
      }
      $ib->set_text('');
      $ib->grab_focus();
    }
    
    
  }

  ## Returns ##
  
  # Buttons #

  public function on_returnsItemBarcodeButton_clicked() {
    if ( $this->debug ) echo "on_returnsItemBarcodeButton_clicked()\n";
    $this->on_returnsItemBarcodeTextentry_activate();
  
  }

  public function on_returnsDeleteSelectedBarcodeButton_clicked() {
    if ( $this->debug ) echo "on_returnsDeleteSelectedBarcodeButton_clicked()\n";
    $tree = $this->glade->get_widget( 'returnsTreeview' );
    $selection = $tree->get_selection();
    list( $model, $iter ) = $selection->get_selected();

    $value = $model->get_value( $iter, 0 );
    if ( $this->debug ) echo "Removing value $value\n";
    $this->removeCirc( $field = 'barcode', $value, $this->returnsCurrent );

    $this->returnsBarcodesList->remove( $iter );
    
    $ib = $this->glade->get_widget( 'returnsItemBarcodeTextentry' );
    $ib->grab_focus();
  }
  
  public function on_returnsFinishButton_clicked() {
    if ( $this->debug ) echo "on_returnsFinishButton_clicked()\n";
    $ib = $this->glade->get_widget( 'returnsItemBarcodeTextentry' );

    foreach( $this->returnsCurrent as $return ) {
      $this->insertCircHistory( $return );
    }
    
    unset( $this->returnsCurrent );

    $this->returnsBarcodesList->clear();
    
    $ib->grab_focus();
    
    $this->saveFile();
  }
  
  public function on_returnsCancelButton_clicked() {
    if ( $this->debug ) echo "on_returnsCancelButton_clicked()\n";
    $bc = $this->glade->get_widget( 'returnsItemBarcodeTextentry' );
    $bc->set_text('');    
    $this->returnsBarcodesList->clear();
    
    unset( $this->returnsCurrent );
    
    $bc->grab_focus();
  }

  
  # Textfields #

  public function on_returnsItemBarcodeTextentry_activate() {
    if ( $this->debug ) echo "on_returnsItemBarcodeTextentry_activate()\n";

    if ( ! is_array( $this->returnsCurrent ) ) {
      $this->returnsCurrent = array();
    }

    $ib = $this->glade->get_widget( 'returnsItemBarcodeTextentry' );   
    $barcode = $ib->get_text();
    if ( $this->debug ) echo "Barcode value is $barcode\n";

    if ( empty ( $barcode ) ) {
      $this->on_returnsFinishButton_clicked();
    } else {
      if ( ! in_array( $barcode, $this->returnsCurrent ) ) {
        $this->returnsBarcodesList->prepend( array( $barcode ) );
        $this->returnsCurrent[] = array( 
          'barcode' => $barcode, 
          'date' => $this->getDate(),
          'type' => 'return'
         );
      }
      $ib->set_text('');
      $ib->grab_focus();
    }  
  }  

  ## Circulation  History ##
  
  public function on_deleteSelectedCircButton_clicked() {
    if ( $this->debug ) echo "on_deleteSelectedCircButton_clicked()\n";
    $tree = $this->glade->get_widget( 'circHistoryTreeview' );
    $selection = $tree->get_selection();
    list( $model, $iter ) = $selection->get_selected();

    $date = $model->get_value( $iter, 3 );
    $this->removeCirc( $field = 'date', $date, $this->history );
    $this->circHistoryList->remove( $iter );
  }

  ## Menu Commands ##
  public function on_new_activate() {
    if ( ( ! $this->isAnyCircs() ) || $this->alertYesNo( "Start A New Circulation Files?", "New File" ) ) {
      if ( $this->filename ) $this->saveOpenCircs();
      if ( ( ! $this->isAnyCircs() ) || $this->filename || $this->alertYesNo( 
            "You Are About To Erase All Current Data.\nIf you have not saved to a file, all data will be lost.\nAre you sure you do not want to do this?"
            , "Warning" )
      ) {
        unset( $this->history );
        $this->history = array();
        $this->rebuildHistoryTreeview();
      
        $this->on_issuesCancelButton_clicked();
        $this->on_returnsCancelButton_clicked();
        
        $this->on_save_as_activate();
      }
    }
  }
  
  public function on_open_activate() {
    if ( ( ! $this->filename ) || $this->alertYesNo( "Open A Different Circulation File?", "New File" ) ) {
      if ( $this->filename ) $this->saveOpenCircs();
      if ( ( ! $this->isAnyCircs() ) || $this->filename || $this->alertYesNo( 
            "You Are About To Erase All Current Data.\nIf you have not saved to a file, all data will be lost.\nAre you sure you do not want to do this?"
            , "Warning" )
      ) {

        $dialog = new GtkFileChooserDialog(
          "Open Data File", 
          null, 
          Gtk::FILE_CHOOSER_ACTION_OPEN,
          array( Gtk::STOCK_OK, Gtk::RESPONSE_OK ),
          null );
            
	##FIXME: Move this to an ini file pref or something.
        $dialog->set_current_folder( "/tmp" );

        $filter = $this->getFileFilter();
        $dialog->add_filter( $filter );
        $dialog->set_filter( $filter );
          
        $dialog->show_all();
          
        if ( $dialog->run() == Gtk::RESPONSE_OK ) {
          $filename = $dialog->get_filename();
          if ( ! is_readable( $filename ) ) {
            self::alert( 'Unable To Read File!' );
            $dialog->destroy();
            $this->on_open_activate();
          } elseif ( ! is_writable( $filename ) ) {
            self::alert( 'Unable To Write To File!' );
            $dialog->destroy();
            $this->on_open_activate();
          } else {
            $this->loadFile( $filename );
          }
        }
    
        $dialog->destroy();
      }
    }
  }  
  
  public function on_save_as_activate() {
    $this->saveOpenCircs();
     
    $dialog = new GtkFileChooserDialog(
      "Save Data", 
      null, 
      Gtk::FILE_CHOOSER_ACTION_SAVE,
      array( Gtk::STOCK_OK, Gtk::RESPONSE_OK ),
      null );

    $filter = $this->getFileFilter();
    $dialog->add_filter( $filter );
    $dialog->set_filter( $filter );

    $dialog->set_current_folder( "/tmp" );

    $dialog->show_all();

    if ( $dialog->run() == Gtk::RESPONSE_OK ) {
      $filename = $dialog->get_filename();
      
      if ( ! $this->endsWith( $filename, '.koc' ) ) {
        $filename .= '.koc';
      }
      
      if ( ! $this->saveFile( $filename ) ) {
        self::alert( 'Unable To Write To File!' );
        $dialog->destroy();
        $this->on_save_as_activate();
      }
    }
    
    $dialog->destroy(); 
  }
  
  public function on_quit_activate() {
    $this->on_kocWin_destroy();
  }
  
  public function on_about_activate() {
    $about = new GtkAboutDialog();
    $about->set_logo( GdkPixbuf::new_from_file('koha-logo.png') );
    $about->set_name( 'Koha Offline Circulation' );
    $about->set_copyright( '2008 Kyle Hall' );
    $about->set_license( 'Release Under the GNU GPL V2' );
    $about->set_website( 'http://www.kylehall.info' );
    $about->set_version( '0.1' );
    $about->set_comments( 'Written for the Crawford County Federated Library System <ccfls.org>' );
    $about->show_all();
  }
  
  ## Status Bar ##
  
  public function setStatusBar( $message ) {
    $sb = $this->glade->get_widget( 'statusbar' );
    $sb->pop( $sb->get_context_id( 'message' ) );
    $sb->push( $sb->get_context_id( 'message' ), $message );
  }

  ## Borrowers ##
  private function updateBorrowerData( $cardnumber = null ) {
    if ( $cardnumber ) {
      $results = sqlite_query( $this->sqlite, "SELECT * FROM borrowers WHERE cardnumber = '$cardnumber'" );
      $borrower = sqlite_fetch_array( $results, SQLITE_ASSOC );
    }
  
    $borrowerNameEntry = $this->glade->get_widget( 'borrowerNameEntry' );
    $borrowerAddressTextview = $this->glade->get_widget( 'borrowerAddressTextview' );
    $borrowerPhoneEntry = $this->glade->get_widget( 'borrowerPhoneEntry' );
    $borrowerBirthdateEntry = $this->glade->get_widget( 'borrowerBirthdateEntry' );
    $borrowerFinesEntry = $this->glade->get_widget( 'borrowerFinesEntry' );
    
    if ( $cardnumber && $borrower ) { ## Barcode was given and borrower was found
 
      $borrowerNameEntry->set_text( $borrower['surname'] . ', ' . $borrower['firstname'] );
      
      $buffer = $borrowerAddressTextview->get_buffer();
      $buffer->set_text( $borrower['streetaddress'] . "\n" . $borrower['city'] . ', ' . $borrower['state'] . "\n" . $borrower['zipcode'] );
      $borrowerAddressTextview->set_buffer( $buffer );
      
      $borrowerPhoneEntry->set_text( $borrower['phone'] );
      $borrowerBirthdateEntry->set_text( $borrower['dateofbirth'] );
      $borrowerFinesEntry->set_text( number_format( $borrower['total_fines'], 2, '.', '' ) );
 
      ## List current issues
      $this->updateBorrowerCurrentIssues( $borrower['borrowernumber'] );
    } elseif ( $cardnumber ) { ## Barcode was given and borrower was *not* found
 
      $borrowerNameEntry->set_text( 'Card Number Not Found' );
      
      $buffer = $borrowerAddressTextview->get_buffer();
      $buffer->set_text( '' );
      $borrowerAddressTextview->set_buffer( $buffer );
      
      $borrowerPhoneEntry->set_text( '' );
      $borrowerBirthdateEntry->set_text( '' );
      $borrowerFinesEntry->set_text( '0.00' );

      $this->currentIssuesList->clear();
    } else {  ## Barcode was *not* given
 
      $borrowerNameEntry->set_text( '' );
      
      $buffer = $borrowerAddressTextview->get_buffer();
      $buffer->set_text( '' );
      $borrowerAddressTextview->set_buffer( $buffer );
      
      $borrowerPhoneEntry->set_text( '' );
      $borrowerBirthdateEntry->set_text( '' );
      $borrowerFinesEntry->set_text( '0.00' );
    
      $this->currentIssuesList->clear();
    }
  }

  public function updateBorrowerCurrentIssues( $borrowernumber = null ) {
    if ( $borrowernumber ) {
      $results = sqlite_query( $this->sqlite, "SELECT * FROM issues WHERE borrowernumber LIKE '$borrowernumber' ORDER BY date_due DESC" );

      while ( $issue = sqlite_fetch_array( $results, SQLITE_ASSOC ) ) {
	$due = $issue['date_due'];
	$callnumber = $issue['itemcallnumber'];
	$title = $issue['title'];
	$itemtype = $issue['itemtype'];
        $this->currentIssuesList->prepend( array( $due, $callnumber, $itemtype, $title ) );
      }
    } else {
      $this->currentIssuesList->clear();
    }
  }
  
  ## Timeouts ##
  
  public function onStatusbarTimeout() {
    if ( ! $this->filename ) {
      $this->setStatusBar( $this->noFileWarning );
    } else {
      $this->setStatusBar( 'Saving to file ' . $this->filename );
    }
    return true;
  }  

  ## Other Methods ##
  
  private function loadFile( $filename ) {
    $this->filename = $filename;
  
    $filehandle = fopen( $filename, 'r' );
    $data = fread( $filehandle, filesize( $filename ) );
    fclose( $filehandle );

    $fileArray = array();

    $lines = explode( "\n", $data );
    
    $header = explode( "\t", array_shift( $lines ) );
    foreach ( $lines as $line ) {
      $parts = explode( "\t", $line );
      $h = array();
      
      $h['date'] = $parts[0];
      $h['type'] = $parts[1];

      switch( $h['type'] ) {
	case 'issue':
 		$h['cardnumber'] = $parts[2];
		$h['barcode'] = $parts[3];
        	break;
	case 'return':
		$h['barcode'] = $parts[2];
		break;
	case 'payment':
		$h['cardnumber'] = $parts[2];
		$h['barcode'] = $parts[3]; # The amount of a payment is stored in 'barcode'
		break;
	}

	$fileArray[] = $h;
    }   

    array_reverse( $fileArray );

    $this->putFileArray( $fileArray );
  }
  
  private function saveFile( $filename = null ) {
    if ( $filename ) $this->filename = $filename;
    
    $filehandle = fopen( $this->filename, 'w' );
    
    if ( ! $filehandle ) return false;

    $history = $this->getFileArray();

    ## Write some header data
    fwrite( $filehandle, "Version=1.0\tGenerator=kocPHP\tGeneratorVersion=0.1\n" );
## Deprecated    fwrite( $filehandle, "type\tcardnumber\tbarcode\tdate\n" );
    foreach ( $history as $h ) {
	$line = '';

	$type = $h['type'];
	if ( $type == 'issue' ) {
	      $cardnumber = $h["cardnumber"];
	      $barcode = $h["barcode"];
	      $date = $h["date"];
	      $line = "$date\t$type\t$cardnumber\t$barcode\n";
	} elseif ( $type == 'return' ) {
	      $barcode = $h["barcode"];
	      $date = $h["date"];
	      $line = "$date\t$type\t$barcode\n";
	} elseif ( $type =='payment' ) {
	      $cardnumber = $h['cardnumber'];
	      $amount = $h["barcode"];
	      $date = $h["date"];
	      $line = "$date\t$type\t$cardnumber\t$amount\n";
	}

	fwrite( $filehandle, $line );
    }
    
    fclose( $filehandle );
    return true;
  }
  
  private function getFileArray() {
    return $this->history;
  }
  
  private function putFileArray( $array ) {
    $this->history = $array;
    $this->rebuildHistoryTreeview();
  }
  
  private static function encodeData( $data ) {
    return base64_encode( serialize( $data ) );
  }
  
  private static function decodeData( $data ) {
    return unserialize( base64_decode( $data ) );
  }

  private function saveOpenCircs() {
   if ( $this->isOpenCircs() && $this->alertYesNo( 
          "Would You Like To Commit Current Open Issues & Returns Before Saving?\nIf You Don't, The Data May Be Lost."
          , "Warning" )
    ) {
      $this->on_issuesFinishButton_clicked();
      $this->on_returnsFinishButton_clicked();
    }
  }
  
  private function insertCircHistory( $circ ) {
    if ( $this->debug ) echo "insertCircHistory( $circ )\n";
    $c = array(
      $circ['type'], 
      $circ['cardnumber'], 
      $circ['barcode'], 
      $circ['date'] 
    );
    $this->history[] = $circ;
    $this->circHistoryList->prepend( $c );
  }

  private function rebuildHistoryTreeview() {
    $this->circHistoryList->clear();
    
    foreach( $this->history as $h ) {
      $c = array(
        $h['type'], 
        $h['cardnumber'], 
        $h['barcode'], 
        $h['date'] 
      );
      $this->circHistoryList->prepend( $c );
    }
  }
  
  public function removeCirc( $field, $value, &$array ) {
    if ( $this->debug ) echo "removeCurrentCirc( $field, $value, $array )\n";
    $found = false;
    for( $i = 0; $found == false; $i++ ) {
      if ( $array[$i][$field] == $value ) {
        $found = true;
      }
    }

    $i--; ## Last for loop increments $i before breaking;
    
    if ( $found ) {
      $array = array_merge( 
        array_slice( $array, 0, $i ), 
        array_slice( $array, $i+1) 
      );
    }
    return $found;
  }

  public function isOpenCircs() {
    return( count( $this->issuesCurrent ) || count( $this->returnsCurrent ) );
  }
  
  public function isAnyCircs() {
    return( $this->isOpenCircs() || count( $this->history ) );
  }
  
  public function getDate() {
	if (version_compare(PHP_VERSION, '5.2.2', '>=')) { # Milliseconds only supported in version 5.2.2 or higher.
		return date("Y-m-d H:i:s u");	    
	} else {
		list($usec, $sec) = explode(" ",microtime());
		$milliseconds = round( $usec * 1000 );
		return date("Y-m-d H:i:s") . " " . $milliseconds;
	}

  }
  
  public function endsWith( $str, $sub ) {
     return ( substr( $str, strlen( $str ) - strlen( $sub ) ) === $sub );
  }

  ## Initial Setup Methods ##
  
  private function setupIssuesTreeview() {
    $tree = $this->glade->get_widget( 'issuesTreeview' );
    $renderer = new GtkCellRendererText();

    $col = new GtkTreeViewColumn( 'Barcode', $renderer, 'text', 0 );
    if ( $this->os == "windows" ) {
      $this->issuesBarcodesList = new GtkListStore( Gtk::TYPE_STRING );
    } else {
      $this->issuesBarcodesList = new GtkListStore( GObject::TYPE_STRING );
    }
    $tree->set_model( $this->issuesBarcodesList );
    $tree->append_column( $col );
  }

  ## Current Issues are items already on loan to a borrower
  private function setupCurrentIssuesTreeview() {
    $tree = $this->glade->get_widget( 'currentIssuesTreeview' );

    $colRend = new GtkCellRendererText();

    $colDue = new GtkTreeviewColumn( 'Due', $colRend, 'text', 0 );
    $colDue->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colCallnumber = new GtkTreeviewColumn( 'Callnumber', $colRend, 'text', 1 );
    $colCallnumber->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colType = new GtkTreeViewColumn( 'Type', $colRend, 'text', 2 );
    $colType->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
   $colTitle = new GtkTreeViewColumn( 'Title', $colRend, 'text', 3 );
    $colTitle->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
 
    if ( $this->os == "windows" ) {
      $this->currentIssuesList = new GtkListStore(
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING,
	Gtk::TYPE_STRING
       );
    } else {
      $this->currentIssuesList = new GtkListStore(
        GObject::TYPE_STRING,
        GObject::TYPE_STRING,
        GObject::TYPE_STRING,
	GObject::TYPE_STRING
       );
    }
    $tree->set_model( $this->currentIssuesList );

    $tree->append_column( $colDue );
    $tree->append_column( $colCallnumber );
    $tree->append_column( $colType );
    $tree->append_column( $colTitle );
  }

  private function setupReturnsTreeview() {
    $tree = $this->glade->get_widget( 'returnsTreeview' );
    $renderer = new GtkCellRendererText();
    $col = new GtkTreeViewColumn( 'Barcode', $renderer, 'text', 0 );
    if ( $this->os == "windows" ) {
      $this->returnsBarcodesList = new GtkListStore( Gtk::TYPE_STRING );
    } else {
      $this->returnsBarcodesList = new GtkListStore( GObject::TYPE_STRING );    
    }
    $tree->set_model( $this->returnsBarcodesList );
    $tree->append_column( $col );
  }
  
  private function setupCircHistoryTreeview() {
    $tree = $this->glade->get_widget( 'circHistoryTreeview' );
    
    $colRend = new GtkCellRendererText();

    $colType = new GtkTreeviewColumn( 'Type', $colRend, 'text', 0 );
    $colType->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colCardnumber = new GtkTreeviewColumn( 'Cardnumber', $colRend, 'text', 1 );
    $colCardnumber->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colBarcode = new GtkTreeViewColumn( 'Barcode', $colRend, 'text', 2 );
    $colBarcode->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colDate = new GtkTreeViewColumn( 'Date (Y-M-D H:M:S)', $colRend, 'text', 3 );
    $colDate->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    
    if ( $this->os == "windows" ) {
      $this->circHistoryList = new GtkListStore( 
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING
       );
    } else {
      $this->circHistoryList = new GtkListStore( 
        GObject::TYPE_STRING,
        GObject::TYPE_STRING,
        GObject::TYPE_STRING,
        GObject::TYPE_STRING
       );
    }
    $tree->set_model( $this->circHistoryList );
    
    $tree->append_column( $colType );
    $tree->append_column( $colCardnumber );
    $tree->append_column( $colBarcode );
    $tree->append_column( $colDate ); 
  }
  
  private function setupBsResultsListTreeview() {
    $tree = $this->glade->get_widget( 'bsResultsListTreeview' );
    
    $colRend = new GtkCellRendererText();

    $colLast = new GtkTreeviewColumn( 'Last', $colRend, 'text', 0 );
    $colLast->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colFirst = new GtkTreeviewColumn( 'First', $colRend, 'text', 1 );
    $colFirst->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colBirthdate = new GtkTreeViewColumn( 'Birthdate', $colRend, 'text', 2 );
    $colBirthdate->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colAddress = new GtkTreeViewColumn( 'Address', $colRend, 'text', 3 );
    $colAddress->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    $colCardnumber = new GtkTreeviewColumn( 'Cardnumber', $colRend, 'text', 1 );
    $colCardnumber->set_cell_data_func( $colRend, array( &$this, 'format_column' ) );
    
    if ( $this->os == "windows" ) {
      $this->bsResultsList = new GtkListStore( 
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING,
        Gtk::TYPE_STRING
       );
    } else {
      $this->bsResultsList = new GtkListStore( 
        GObject::TYPE_STRING,
        GObject::TYPE_STRING,
        GObject::TYPE_STRING,
        GObject::TYPE_STRING,
        GObject::TYPE_STRING
       );
    }
    $tree->set_model( $this->bsResultsList );

    $tree->append_column( $colLast );
    $tree->append_column( $colFirst );
    $tree->append_column( $colBirthdate );
    $tree->append_column( $colAddress ); 
  }

  ## Gtk Functions ##
  protected function alertSaveOpen() {
    $dialog = new GtkDialog( 'Create New File or Open Existing', null, Gtk::DIALOG_MODAL );
    $top_area = $dialog->vbox;
    $top_area->pack_start( $hbox = new GtkHBox() );
    $stock = GtkImage::new_from_stock( Gtk::STOCK_DIALOG_WARNING, Gtk::ICON_SIZE_DIALOG );
    $hbox->pack_start( $stock, 0, 0 );
    $hbox->pack_start( new GtkLabel( "Would You Like To Create A New Circulation File\nOr Open An Existing File?" ) );
    $dialog->add_button( Gtk::STOCK_OPEN, Gtk::RESPONSE_YES );
    $dialog->add_button( Gtk::STOCK_NEW, Gtk::RESPONSE_NO );
    $dialog->set_has_separator( false );
    $dialog->show_all();
        
    $response = $dialog->run();
    if ( $response === Gtk::RESPONSE_YES ) {
      $dialog->destroy();
      $this->on_open_activate();
    } elseif ( $response === Gtk::RESPONSE_NO ) {
      $dialog->destroy();  
      $this->on_new_activate();
    }
  }
  
  protected function alertYesNo( $message, $title = 'Alert' ) {
    $dialog = new GtkDialog( $title, null, Gtk::DIALOG_MODAL );
    $dialog->set_position( Gtk::WIN_POS_CENTER_ALWAYS );
    $top_area = $dialog->vbox;
    $top_area->pack_start( $hbox = new GtkHBox() );
    $stock = GtkImage::new_from_stock( Gtk::STOCK_DIALOG_WARNING, Gtk::ICON_SIZE_DIALOG );
    $hbox->pack_start( $stock, 0, 0 );
    $hbox->pack_start( new GtkLabel( $message ) );
    $dialog->add_button( Gtk::STOCK_NO, Gtk::RESPONSE_NO );
    $dialog->add_button( Gtk::STOCK_YES, Gtk::RESPONSE_YES );
    $dialog->set_has_separator( false );
    $dialog->show_all();
    
    if ( $dialog->run() !== Gtk::RESPONSE_YES ) {
      $dialog->destroy();
      return false;
    } else {
      $dialog->destroy();  
      return true;
    }
  }
  
  protected function alert( $message, $title = 'Alert' ) {
    $dialog = new GtkDialog( $title, null, Gtk::DIALOG_MODAL );
    $dialog->set_position( Gtk::WIN_POS_CENTER_ALWAYS );
    $top_area = $dialog->vbox;
    $top_area->pack_start( $hbox = new GtkHBox() );
    $stock = GtkImage::new_from_stock( Gtk::STOCK_DIALOG_WARNING, Gtk::ICON_SIZE_DIALOG );
    $hbox->pack_start( $stock, 0, 0 );
    $hbox->pack_start( new GtkLabel( $message ) );
    $dialog->add_button( Gtk::STOCK_OK, Gtk::RESPONSE_OK );
    $dialog->set_has_separator( false );
    $dialog->show_all();
    $dialog->run();
    $dialog->destroy();  
  }
  
  protected function getFileFilter() {
    $filter = new GtkFileFilter();
    $filter->add_pattern( "*.koc" );
    $filter->set_name( "Koha Offline Circ File" );
    return $filter;
  }
  
  ## function to alternate row color for treeviews
  public function format_column( $column, $cell, $model, $iter ) {
    $path = $model->get_path( $iter );
    $row_num = $path[0];
    $row_color = ( $row_num % 2 == 1 ) ? '#dddddd' : '#ffffff';
    $cell->set_property( 'cell-background', $row_color );
  }
  

## bsWin functions
  public function on_bsSearchButton_clicked() {
    $bsFirstNameEntry = $this->glade->get_widget( 'bsFirstNameEntry' );
    $bsLastNameEntry = $this->glade->get_widget( 'bsLastNameEntry' );
    $bsResultsListTreeview = $this->glade->get_widget( 'bsResultsListTreeview' );

    $this->bsResultsList->clear();

    $firstName = $bsFirstNameEntry->get_text();
    $lastName = $bsLastNameEntry->get_text();

    $results = sqlite_query( $this->sqlite, "SELECT * FROM borrowers WHERE firstname LIKE '$firstName%' AND surname LIKE '$lastName%'" );

    while ( $borrower = sqlite_fetch_array( $results, SQLITE_ASSOC ) ) {
      $b = array(
        $borrower['surname'], 
        $borrower['firstname'], 
        $borrower['dateofbirth'], 
        $borrower['streetaddress'] . ", " . $borrower['city'] . ", " . $borrower['state'] . ", " . $borrower['zipcode'],
        $borrower['cardnumber']
      );
      $this->bsResultsList->prepend( $b );
    }
  }

  public function on_bsSelectButton_clicked() {
    $tree = $this->glade->get_widget( 'bsResultsListTreeview' );
    $selection = $tree->get_selection();
    list($model, $iter) = $selection->get_selected();
    $cardnumber = $model->get_value( $iter, 4 );

    $cn = $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' );
    $cn->set_text( $cardnumber );

    $bsWin = $this->glade->get_widget( 'bsWin' );
    $bsWin->hide();

    $this->on_issuesBorrowerCardnumberTextentry_activate();
  }

  public function on_bsCancelButton_clicked() {
    $bsWin = $this->glade->get_widget( 'bsWin' );
    $bsWin->hide();

    $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' )->grab_focus();
  }

  public function on_bsWin_delete_event() {
    $bsWin = $this->glade->get_widget( 'bsWin' );
    $bsWin->hide();

    $this->glade->get_widget( 'issuesBorrowerCardnumberTextentry' )->grab_focus();
  }
}
?>