By default java swing will send error messages to the java console. This would be ok if the java console would pop up from a runnable java application, but it does not and have not found a way to do so. If otherwise instructed, java will hide all messages (even fatal ones) from the user during runtime. This is not acceptable behavior, so I have found a solution. Route the messages to JOptionPanes like a popup.
During the main(String[] args) procedure, insert the following code:
public static void main(String[] args) {
System.out.println(SwingUtilities.isEventDispatchThread());
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.println(SwingUtilities.isEventDispatchThread());
try {
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
// TODO Auto-generated method stub
JOptionPane.showMessageDialog(null,
e.toString(),
"Error",
JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
});
****START CODE HERE****
System.out.println(SwingUtilities.isEventDispatchThread());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
Tuesday, July 22, 2014
Create dynamically loading java swing objects
I gotta say, java swing isn't the greatest framework for creating interfaces with objects that need to be updated depending on selections. I came across this issue when I trying to create a panel that would dynamically allocate objects depending on the class that this panel was inheriting by the users choice from a radio button selection. When a panel was created with the allocated objects, the containing panel would not recognize those changes even after repainting and revalidating. The trick is knowing how the event driven swing framework works. Since the objects were not originally created by the event dispatch thread a.k.a. AWT-EventQueue, they will not take effect until an EventQueue owned object "affects" it. So how would the EventQueue thread "own" the interface objects that were not created in the first place?
In comes the SwingWorker. This is an abstract class that allows dynamic background processes to update the object being modified even if the process is a concurrent running process. The SwingWorker can publish the object at any time even in the middle of a process since it publishes itself to the even dispatch thread which will then take ownership of the object in need of updating. Now loading bars and live dynamic object allocation are possible. I've created a class below that extends the SwingWorker class, but it can take any Swing parent component (the one that contains the object needed to be modified) and a method associated with a child Swing component. Depending on what the child method does, this will update the interface of the parent that will be containing the childs actions.
For example, I have a Jpanel that contains another Jpanel that will update some JComboBoxes with different values. The kicker is that depending on the child class, there can different quantities of JComboBoxes.
Sample Code:
public class DocTypeContainPanel extends JPanel{
...
SwingObjectWorker temp1 = null;
SwingObjectWorker temp2 = null;
temp1 = new SwingObjectWorker(pnlSearchPropsContainer, pnlSearchProps.getPropsPanel());
temp1.execute();
temp2 = new SwingObjectWorker(pnlDocPropsContainer, pnlDocProps.getPropsPanel());
temp2.execute();
revalidate();
repaint();
...
}
class SwingObjectWorker extends SwingWorker<JComponent, Void> {
private JComponent parentComp;
private JComponent childComp;
public SwingObjectWorker (JComponent inparentComp, JComponent inchildComp){
parentComp = inparentComp;
childComp = inchildComp;
final SwingObjectWorker temp = this;
this.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent arg0) {
if (StateValue.DONE == temp.getState()) {
try {
parentComp.removeAll();
parentComp.add(get(), "cell 0 0,grow");
parentComp.setVisible(true);
parentComp.revalidate();
parentComp.repaint();
System.out.println("Swing Worker Done!");
// TODO: insert code to run on the EDT after move determined
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
}
@Override
public JComponent doInBackground() {
return childComp;
}
@Override
public void done() {
}
}
In comes the SwingWorker. This is an abstract class that allows dynamic background processes to update the object being modified even if the process is a concurrent running process. The SwingWorker can publish the object at any time even in the middle of a process since it publishes itself to the even dispatch thread which will then take ownership of the object in need of updating. Now loading bars and live dynamic object allocation are possible. I've created a class below that extends the SwingWorker class, but it can take any Swing parent component (the one that contains the object needed to be modified) and a method associated with a child Swing component. Depending on what the child method does, this will update the interface of the parent that will be containing the childs actions.
For example, I have a Jpanel that contains another Jpanel that will update some JComboBoxes with different values. The kicker is that depending on the child class, there can different quantities of JComboBoxes.
Sample Code:
public class DocTypeContainPanel extends JPanel{
...
SwingObjectWorker temp1 = null;
SwingObjectWorker temp2 = null;
temp1 = new SwingObjectWorker(pnlSearchPropsContainer, pnlSearchProps.getPropsPanel());
temp1.execute();
temp2 = new SwingObjectWorker(pnlDocPropsContainer, pnlDocProps.getPropsPanel());
temp2.execute();
revalidate();
repaint();
...
}
class SwingObjectWorker extends SwingWorker<JComponent, Void> {
private JComponent parentComp;
private JComponent childComp;
public SwingObjectWorker (JComponent inparentComp, JComponent inchildComp){
parentComp = inparentComp;
childComp = inchildComp;
final SwingObjectWorker temp = this;
this.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent arg0) {
if (StateValue.DONE == temp.getState()) {
try {
parentComp.removeAll();
parentComp.add(get(), "cell 0 0,grow");
parentComp.setVisible(true);
parentComp.revalidate();
parentComp.repaint();
System.out.println("Swing Worker Done!");
// TODO: insert code to run on the EDT after move determined
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
}
@Override
public JComponent doInBackground() {
return childComp;
}
@Override
public void done() {
}
}
Friday, July 11, 2014
Using a TP-Link TL-WN725N USB Wifi Adapter on a Raspberry Pi
For those that purchased a TP-Link TL-WN725N because it was really cheap and didn't bother to check the linux driver support for it... oops
Well here's your fix.
Well here's your fix.
This is been tested on raspbian on the rpi (raspberry pi), your mileage may vary on other operating systems and architectures.
First, in order to compile drivers for linux, we need the linux headers. Running rpi-update does not provide the headers in with the update since the kernel is precompiled. What we have to do is find the headers for the current commit of the kernel installed. We do this by running an awesome tool called rpi-source which downloads the correct headers for your specific kernel version that is currently installed on your rpi.
run in a directory of your choice:
sudo wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/bin/rpi-source && sudo chmod +x /usr/bin/rpi-source && /usr/bin/rpi-source -q --tag-update
If you get a message that states "gcc version check: mismatch between gcc (4.6.3) and /proc/version (4.7.2) Skip this check with --skip-gcc", you can simply ignore it by running "rpi-source --skip-gcc"
What this will do is create and install the headers and modules in your /lib/modules/ folder. Now that you have the kernel headers installed, we can move on to compiling the driver. The following comes straight from the examples used for the "rpi-source" repository on github.
You may read and follow the directions here:
https://github.com/notro/rpi-source/wiki/Examples-on-how-to-build-various-modules#tp-link-tl-wn725n-version-2-lwfinger
Otherwise just follow the instructions below which is copied and pasted from the repository:
______________________________________________________________________________
$ uname -a
Linux raspberrypi 3.12.21+ #1 PREEMPT Sat Jun 14 13:44:18 CEST 2014 armv6l GNU/Linux
$ git clone https://github.com/lwfinger/rtl8188eu.git
$ cd rtl8188eu
$ make all
$ sudo make install
$ sudo depmod
$ sudo modprobe 8188eu
$ lsmod
Module Size Used by
8188eu 796381 0
______________________________________________________________________________
After that, you should have a working TL-WN725N wifi adapter.
Major props goes to the developer of rpi-source and for the open source 8188eu driver for the wifi adapter.
Enjoy!
Wednesday, June 25, 2014
Resize partitions for SoC computers and other linux devices (raspberry pi, beaglebone black, etc...)
This only uses fdisk and resize2fs. I ran out of space on the beaglebone black so I could not use parted for partition editing but the following works just as well. The same procedure below applies to raspberry pi's too. The bbb is running debian wheezy burned on a 32GB micro sd card. Raspi-config simplifies the following with one step, but it also used parted for it's file recreation. We don't have that package at our disposal so here is the alternative.
enter: df -h
root@beaglebone:/media/Angstrom# df -h
Filesystem Size Used Avail Use% Mounted on
rootfs 1.6G 1.6G 0 100% /
udev 10M 0 10M 0% /dev
tmpfs 100M 3.1M 97M 4% /run
/dev/mmcblk0p2 1.6G 1.6G 0 100% /
tmpfs 249M 0 249M 0% /dev/shm
tmpfs 249M 0 249M 0% /sys/fs/cgroup
tmpfs 100M 0 100M 0% /run/user
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/mmcblk0p1 96M 70M 27M 73% /boot/uboot
/dev/mmcblk1p2 1.7G 1.1G 519M 68% /media/Angstrom
/dev/mmcblk1p1 70M 54M 16M 78% /media/
*************************************************************************
Clearly the rootfs has no more space available. We want to find the partition that the rootfs is referencing by looking at the filesystems that start with "/dev/" and are mounted on the same location as the rootfs. In this case (and it most cases with SoC computers) it appears here as /dev/mmcblk0p2.
*************************************************************************
enter: fdisk -l
root@beaglebone:~# fdisk -l
Disk /dev/mmcblk0: 31.9 GB, 31914983424 bytes
4 heads, 16 sectors/track, 973968 cylinders, total 62333952 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 * 2048 198655 98304 e W95 FAT16 (LBA)
/dev/mmcblk0p2 198656 62333951 31067648 83 Linux
Disk /dev/mmcblk1: 1920 MB, 1920991232 bytes
255 heads, 63 sectors/track, 233 cylinders, total 3751936 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
/dev/mmcblk1p1 * 63 144584 72261 c W95 FAT32 (LBA)
/dev/mmcblk1p2 144585 3743144 1799280 83 Linux
Disk /dev/mmcblk1boot1: 1 MB, 1048576 bytes
4 heads, 16 sectors/track, 32 cylinders, total 2048 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Disk /dev/mmcblk1boot1 doesn't contain a valid partition table
Disk /dev/mmcblk1boot0: 1 MB, 1048576 bytes
4 heads, 16 sectors/track, 32 cylinders, total 2048 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Disk /dev/mmcblk1boot0 doesn't contain a valid partition table
*************************************************************************
This will tell us where the /dev/mmcblk0p2 partition is located. It appears under the disk /dev/mmcblk0 which makes sense because it has 31.9 GB of total usable space. We have made a direct correlation with the disk needed to be modified and the 32GB card installed in the bbb.
*************************************************************************
enter: fdisk /dev/mmcblk0
root@beaglebone:/media/Angstrom# fdisk /dev/mmcblk0
Command (m for help): p
Disk /dev/mmcblk0: 31.9 GB, 31914983424 bytes
4 heads, 16 sectors/track, 973968 cylinders, total 62333952 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 * 2048 198655 98304 e W95 FAT16 (LBA)
/dev/mmcblk0p2 198656 3481599 1641472 83 Linux
Command (m for help): d
Partition number (1-4): 2
Command (m for help): p
Disk /dev/mmcblk0: 31.9 GB, 31914983424 bytes
4 heads, 16 sectors/track, 973968 cylinders, total 62333952 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 * 2048 198655 98304 e W95 FAT16 (LBA)
Command (m for help): n
Partition type:
p primary (1 primary, 0 extended, 3 free)
e extended
Select (default p): p
Partition number (1-4, default 2): 2
First sector (198656-62333951, default 198656): (press enter here)
Using default value 198656
Last sector, +sectors or +size{K,M,G} (198656-62333951, default 62333951): (press enter here)
Using default value 62333951
Command (m for help): p
Disk /dev/mmcblk0: 31.9 GB, 31914983424 bytes
4 heads, 16 sectors/track, 973968 cylinders, total 62333952 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 * 2048 198655 98304 e W95 FAT16 (LBA)
/dev/mmcblk0p2 198656 62333951 31067648 83 Linux
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.
root@beaglebone:/media/Angstrom# reboot
Broadcast message from root@beaglebone (pts/0) (Wed Jun 25 15:37:07 2014):
The system is going down for reboot NOW!
*************************************************************************
What we did was delete the partition from the partition table, not the actual partition itself so the data still remains intact when we create a new partition. After the second partition has been deleted and recreated, we reboot.
*************************************************************************
log in again and reenter: df -h
root@beaglebone:~# df -h
Filesystem Size Used Avail Use% Mounted on
rootfs 1.6G 1.6G 0 100% /
udev 10M 0 10M 0% /dev
tmpfs 100M 540K 99M 1% /run
/dev/mmcblk0p2 1.6G 1.6G 0 100% /
tmpfs 249M 0 249M 0% /dev/shm
tmpfs 249M 0 249M 0% /sys/fs/cgroup
tmpfs 100M 0 100M 0% /run/user
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/mmcblk0p1 96M 70M 27M 73% /boot/uboot
*************************************************************************
We have free space! ... wait.... huh?
The available space is still 0???
... remember, we just modified the partition table, not the partition itself. Now we have resize the actual partition in accordance with the partition table.
*************************************************************************
enter: resize2fs /dev/mmcblk0p2
root@beaglebone:~# resize2fs /dev/mmcblk0p2
resize2fs 1.42.5 (29-Jul-2012)
Filesystem at /dev/mmcblk0p2 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 2
The filesystem on /dev/mmcblk0p2 is now 7766912 blocks long.
root@beaglebone:~# reboot
*************************************************************************
From the previous output of "df -h", we want to modify the partition itself which would be /dev/mmcblk0p2 since that is the rootfs described in the first step. Now the physical partition is resized.
*************************************************************************
enter: df -h
root@beaglebone:~# df -h
Filesystem Size Used Avail Use% Mounted on
rootfs 30G 1.6G 27G 6% /
udev 10M 0 10M 0% /dev
tmpfs 100M 540K 99M 1% /run
/dev/mmcblk0p2 30G 1.6G 27G 6% /
tmpfs 249M 0 249M 0% /dev/shm
tmpfs 249M 0 249M 0% /sys/fs/cgroup
tmpfs 100M 0 100M 0% /run/user
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/mmcblk0p1 96M 70M 27M 73% /boot/uboot
*************************************************************************
Bam! We have copious amounts of free space. Enjoy!
*************************************************************************
Tuesday, May 20, 2014
Installing Microsoft Office 2013 with many licenses
Leave it to Microsoft to screw up a working system...
New for Office 2013 is a brand new method for installing office.
Whether you have created a installation DVD or install through a web browser, the installation procedure follows the same basic really detoured guideline.
During the installation process after you enter the product key, Office 2013 installation utility (again, doesn't matter how you install, web or DVD) retrieves the newly entered product key and populates the product on the screen without any distinguishing identification of that product. Assuming you only have one product, this process is very simple. If you have more than one product key, say 29 product keys like I do, this process is a very annoying game of russian roulette.
The list that populates with your newely entered product key displays the name of the office product over and over again depending on how many instances of office you have registered under your account. You have to guess to see which office key is the most recent office key you have just entered.
If you have installed a microsoft office product that has been registered and activated on one machine and you mistakenly clicked on the wrong office line, you now have the possibility of an office install that would not be activated and you will have to uninstall and reinstall to get the new key working.
I have a very long solution that may help in this matter.
The following is followed after the first installation and going onto multiples which now involves guessing.
Make a table that looks like this:
Initial Product Key New Product Key Computer Identifier Line Number
While keeping track of which product key goes to which computer, go to https://office.microsoft.com/en-us/MyAccount.aspx
and click "Install from a disc" under one of the products, click "I have a disc", then click view your product key.
This key is NOT going to be the same key you have entered in from the office installation. It appears they create a whole new product key that stems from the original. Write this product key as the replacement for the old product key that was used in the office installation and will be called the New Product key in our table.
In your records, find the line# (literally count the instances) in your "My Account page" that does not contain a previously recorded product key.
Using the following line in command prompt to record the last 5 characters of your product key.
For 32 bit Windows:
cscript "C:\Program Files\Microsoft Office\Office15\OSPP.VBS" /dstatus
For 64 bit Windows (assuming you are using 32 bit Office):
cscript "C:\Program Files (x86)\Microsoft Office\Office15\OSPP.VBS" /dstatus
Record this product key with the computer identifier so you have a link from that computer to the product key.
Using this method there will still be guessing, but a lot less of it.
When you need to change the product key, here are the commands for that
For 32 bit Windows:
cscript "C:\Program Files\Microsoft Office\Office15\OSPP.VBS" /inpkey:yourkeygoeshere
For 64 bit Windows (assuming you are using 32 bit Office):
cscript "C:\Program Files (x86)\Microsoft Office\Office15\OSPP.VBS" /inpkey:yourkeygoeshere
If you are reading this and have not bought 2013 but are thinking of doing so, please don't.
Buy or download anything else, MS Office 2010 or lower, Libre office, Open office...
New for Office 2013 is a brand new method for installing office.
Whether you have created a installation DVD or install through a web browser, the installation procedure follows the same basic really detoured guideline.
During the installation process after you enter the product key, Office 2013 installation utility (again, doesn't matter how you install, web or DVD) retrieves the newly entered product key and populates the product on the screen without any distinguishing identification of that product. Assuming you only have one product, this process is very simple. If you have more than one product key, say 29 product keys like I do, this process is a very annoying game of russian roulette.
The list that populates with your newely entered product key displays the name of the office product over and over again depending on how many instances of office you have registered under your account. You have to guess to see which office key is the most recent office key you have just entered.
If you have installed a microsoft office product that has been registered and activated on one machine and you mistakenly clicked on the wrong office line, you now have the possibility of an office install that would not be activated and you will have to uninstall and reinstall to get the new key working.
I have a very long solution that may help in this matter.
The following is followed after the first installation and going onto multiples which now involves guessing.
Make a table that looks like this:
Initial Product Key New Product Key Computer Identifier Line Number
While keeping track of which product key goes to which computer, go to https://office.microsoft.com/en-us/MyAccount.aspx
and click "Install from a disc" under one of the products, click "I have a disc", then click view your product key.
This key is NOT going to be the same key you have entered in from the office installation. It appears they create a whole new product key that stems from the original. Write this product key as the replacement for the old product key that was used in the office installation and will be called the New Product key in our table.
In your records, find the line# (literally count the instances) in your "My Account page" that does not contain a previously recorded product key.
Using the following line in command prompt to record the last 5 characters of your product key.
For 32 bit Windows:
cscript "C:\Program Files\Microsoft Office\Office15\OSPP.VBS" /dstatus
For 64 bit Windows (assuming you are using 32 bit Office):
cscript "C:\Program Files (x86)\Microsoft Office\Office15\OSPP.VBS" /dstatus
Record this product key with the computer identifier so you have a link from that computer to the product key.
Using this method there will still be guessing, but a lot less of it.
When you need to change the product key, here are the commands for that
For 32 bit Windows:
cscript "C:\Program Files\Microsoft Office\Office15\OSPP.VBS" /inpkey:yourkeygoeshere
For 64 bit Windows (assuming you are using 32 bit Office):
cscript "C:\Program Files (x86)\Microsoft Office\Office15\OSPP.VBS" /inpkey:yourkeygoeshere
If you are reading this and have not bought 2013 but are thinking of doing so, please don't.
Buy or download anything else, MS Office 2010 or lower, Libre office, Open office...
Wednesday, May 7, 2014
Install Node.js from git
node.js did not work properly for me through apt-get so I installed it from source.
git clone https://github.com/joyent/node.git
./configure
make
make install
npm is already included with node so there is not need to install.
To solve the error:
This is because "npm install" needs node's source for binary compilation. Make sure that directory is permanent. I have mine so that I can git pull a newer version right in that directory or change versions without any other major modifications. The first line represents the current user's npm config file and the second represents the global config file.
git clone https://github.com/joyent/node.git
./configure
make
make install
npm is already included with node so there is not need to install.
To solve the error:
Error: "pre" versions of node cannot be installed, use the --nodedir flag instead
use the following commands:npm config set nodedir /directory/to/node
npm config set nodedir /directory/to/node --global
This is because "npm install" needs node's source for binary compilation. Make sure that directory is permanent. I have mine so that I can git pull a newer version right in that directory or change versions without any other major modifications. The first line represents the current user's npm config file and the second represents the global config file.
Monday, May 5, 2014
Macbook Pro 5,5 Brightness Control in Ubuntu
I have verified that the latest installment of Ubuntu 14.04 with the latest Nvidia proprietary drivers fixed the brightness controls
All you have to do is add
Option "RegistryDwords" "EnableBrightnessControl=1"
to /etc/X11/xorg.conf under the device section.
OR
If you do not see a valid xorg.conf, maybe something that looks like this "xorg.conf~", more than likely the new method for Xorg configurations files is being used which involves a conglomeration of "conf" files inside of the xorg.conf.d folder. Make and add the following:
mkdir /usr/share/X11/xorg.conf.d/20-nvidia.conf
vim 20-nvidia.conf
paste the following:
Section "Device"
Identifier "NVIDIA"
Driver "nvidia"
Option "NoLogo" "True"
Option "RegistryDwords" "EnableBrightnessControl=1"
EndSection
save it by hitting "esc" then ":wq" and finally "enter".
Reboot, then try the f1 and f2 keys. Your brightness should be adjusting accordingly.
All you have to do is add
Option "RegistryDwords" "EnableBrightnessControl=1"
to /etc/X11/xorg.conf under the device section.
OR
If you do not see a valid xorg.conf, maybe something that looks like this "xorg.conf~", more than likely the new method for Xorg configurations files is being used which involves a conglomeration of "conf" files inside of the xorg.conf.d folder. Make and add the following:
mkdir /usr/share/X11/xorg.conf.d/20-nvidia.conf
vim 20-nvidia.conf
paste the following:
Section "Device"
Identifier "NVIDIA"
Driver "nvidia"
Option "NoLogo" "True"
Option "RegistryDwords" "EnableBrightnessControl=1"
EndSection
save it by hitting "esc" then ":wq" and finally "enter".
Reboot, then try the f1 and f2 keys. Your brightness should be adjusting accordingly.
Subscribe to:
Posts (Atom)